相关咨询

单片机酒精浓度测试仪设计报告 含源码与电路原理图

作者:酒精检测仪  来源:酒精测试仪日期:2019-07-17 18:50:32
单片机酒精浓度测试仪设计报告 含源码与电路原理图
一、设计意义
二、硬件设计
1、设计框图
2、乙醇信号检测及调理电路
3、单片机电路
4、显示电路
5、供电及程序下载电路
三、Protel硬件开发软件
1.Protel软件组成
2.PCB板设计
四、软件编程
1、软件流程图
2、主程序
五、下载与调试
1、USB转串口驱动安装
2、下载程序
参考文献
程序
一、设计意义
自《道路交通安全法》正式实施,“醉酒驾驶”正式入刑。不仅交警部门,而且很多车主都期盼能够有便携仪器方便地测量气体酒精浓度,为安全驾驶提供保障,有效减少重大交通事故的发生。
本研究设计的酒精浓度测试仪是一款实用性强、安全可靠的气体乙醇浓度检测工具,采用高精度MQ-3乙醇气体传感器对空气中的乙醇浓度进行检测,利用宏晶公司高性能低成本单片机STC89C52对检测信号进行A/D转换和处理,最后通过液晶屏显示输出。本研究设计的酒精浓度测试仪还具有醉酒阈值设定功能,可以根据法律法规或用户需要设定修改醉酒阈值,并进行保存。
二、硬件设计
1、设计框图
本研究设计的酒精浓度测试仪框图如图1所示。MQ-3乙醇气体传感器输出信号经信号调理电路处理,输出随乙醇浓度变化的电压信号,该电压信号送入单片机系统,经AD转换,与设定的醉酒阈值进行比较,并显示或报警。
图1酒精浓度测试仪方框图
2、乙醇信号检测及调理电路
MQ-3乙醇气体传感器可以应用用于机动车驾驶人员及其他严禁酒后作业人员的现场检测,也用于其他场所乙醇蒸汽的检测。其技术特点为:
对乙醇蒸汽有很高的灵敏度和良好的选择性
快速的响应恢复特性
长期的寿命和可靠的稳定性
简单的驱动回路
主要技术指标:
MQ-3乙醇气体传感器灵敏度曲线如图2所示,其传感原理为气敏电阻的输出阻值随乙醇气体等浓度变化而变化。
图2MQ-3乙醇气体传感器灵敏度曲线
MQ-3乙醇气体传感器管脚与测试电路如图3所示。
(a)管脚图(b)测试电路
图3MQ-3乙醇气体传感器管脚及测试电路
MQ-3乙醇气体传感器及其调理电路原理如图4所示。其外形如图5所示。经过调理,检测信号由电阻值转变成电压值,便于后续电路进行A/D转换和处理。
图4传感器及调理模块原理图
该传感器模块具有如下特点,方便与单片机系统接口组成检测仪器。
具有信号输出指示。
双路信号输出(模拟量输出及TTL电平输出)
TTL输出有效信号为低电平。
(当输出低电平时信号灯亮,可直接接单片机)
模拟量输出0~5V电压,浓度越高电压越高。
3、单片机电路
本设计选用宏晶公司高性能单片机STC89C52,其管脚如图6所示。
图6STC89C52单片机管脚图
该芯片为52内核8位单片机,适用于常用检测电路。由STC89C52组成的单片机系统原理图如图7所示。图中AOUT为MQ-3传感器模块输出的检测电压信号,送入ADC0832采集芯片端口进行处理,该信号可以根据乙醇气体浓度直接输出报警信号,报警阈值通过模块上的电位器进行调节。
图7单片机系统原理图
图7中,按键K2和K3为醉酒阈值调整键,其中K2为“增加”,K3为“减小”按键。L2和L3为报警指示灯,分别可以进行酒后和醉酒两级报警。
4、显示电路
显示部分采用SMC1602液晶屏进行数据显示,其主要技术参数为:
表1液晶屏技术指标
接口信号说明如表2所示。
表2液晶屏接口信号说明
与单片机接口电路如图8所示。其中J2的3脚为背光引脚,R9和R10电阻用于调节背光亮度。J2的4、5、6引脚分别接液晶的RS、E/W和E控制引脚,J2的7—14引脚为数据引脚。
图8LCD与单片机接口电路
5、供电及程序下载电路
本设计采用电池盒接口供电,电源电压5V。同时,其电路原理如图10所示。
图10供电及程序下载电路
三、Protel硬件开发软件
Protel是目前国内最流行的通用EDA软件,它是将电路原理图设计、PCB板图设计、电路仿真和PLD设计等多个实用工具软件组合后构成的EDA工作平台,是第一个将EDA软件设计成基于Windows的普及型产品。它集成了软件界面、仿真功能和PLD设计和信号完整性分析,在此基础上Protel99SE又增加了一些新的功能,用户使用更加方便灵活。Protel的功能十分强大,在电子电路设计领域占有极其重要的地位。它以其强大功能和实用性,逐渐获得广大硬件设计人员的青睐,是目前众多EDA设计软件中用户最多的产品之一。
1.Protel软件组成
Protel软件主要由电路原理图设计模块、印制电路板设计模块(PCB设计模块)、电路信号仿真模块和PLD逻辑器件设计模块等组成,各模块具有强大的功能,可以很好的实现电路设计与分析。
(1)原理图设计模块(Schematic模块)
电路原理图是表示电气产品或电路工作原理的重要技术文件,电路原理图主要由代表各种电子器件的图形符号、线路和结点组成。图4.1所示为一张电路原理图。该原理图是由Schematic模块设计完成的。Schematic模块具有如下功能:丰富而灵活的编辑功能、在线库编辑及完善的库管理功能、强大的设计自动化功能、支持层次化设计功能等。
(2)印制电路板设计模块(PCB设计模块)
印制电路板(PCB)制板图是由电路原理图到制作电路板的桥梁。设计了电路原理图后,需要根据原理图生设计成印制电路板的制板图,然后在根据制板图制作具体的电路板。印制电路板设计模块具有如下主要功能和特点:可完成复杂印制电路板(PCB)的设计;方便而又灵活的编辑功能;强大的设计自动化功能;在线式库编辑及完善的库管理;完备的输出系统等。
(3)电路信号仿真模块
电路信号仿真模块是一个功能强大的数字/模拟混合信号电路仿真器,能提供连续的模拟信号和离散的数字信号仿真。它运行在Protel的EDA/Client集成环境下,与ProtelAdvancedSchematic原理图输入程序协同工作,作为AdvancedSchematic的扩展,为用户提供了一个完整的从设计到验证仿真设计环境。
在Protel中进行仿真,只需从仿真用元器件库中放置所需的元器件,连接好原理图,加上激励源,然后单击防真按钮即可自动开始。
2.PCB板设计
(1)定元件的封装
①打开网络表(可以利用一些编辑器辅助编辑),将所有封装浏览一遍,确保所有元件的封装都正确无误并且元件库中包含所有元件的封装,网络表中所有信息全部大写,一面载入出问题,或PCBBOM不连续。
②标准元件全部采用公司统一元件库中的封装。
③④⑥⑤元件库中不存在的封装,应自己建立元器件库。
(2)建立PCB板框
①根据PCB结构图,或相应的模板建立PCB文件,包括安装孔、禁布区等相关信息。
②尺寸标注。在钻孔层中应标明PCB的精确结构,且不可以形成封闭尺寸标注。
(3)载入网络表
①载入网表并排除所有载入问题,具体请看《PROTEL技术大全》。其他软件载入问题有很多相似之处,可以借鉴。
②如果使用PROTEL,网表须载入两次以上(没有任何提示信息)才可以确认载入无误。
(4)布局
①首先要确定参考点。
一般参考点都设置在左边和底边的边框线的交点(或延长线的交点)上或印制板的插件的第一个焊盘。
②一但参考点确定以后,元件布局、布线均以此参考点为准。布局推荐使用25MIL网格。
③根据要求先将所有有定位要求的元件固定并锁定。
④布局的基本原则
A.遵循先难后易、先大后小的原则。
B.布局可以参考硬件工程师提供的原理图和大致的布局,根据信号流向规律放置主要原器件。
C.总的连线尽可能的短,关键信号线最短。
D.强信号、弱信号、高电压信号和弱电压信号要完全分开。
E.高频元件间隔要充分。
F.模拟信号、数字信号分开。
⑤相同结构电路部分应尽可能采取对称布局。
⑥按照均匀分布、重心平衡、版面美观的标准来优化布局。
(5)PCB设计遵循的规则
①地线回路规则:
图11地线回路规则
环路最小规则,即信号线与其回路构成的环面积要尽可能小,环面积要尽可能小,环面积越小,对外的辐射越少,接收外界的干扰也越小。针对这一规则,在地平面分割时,要考虑到地平面与重要信号走线的分布,防止由于地平面开槽等带来的问题;在双层板设计中,在为电源留下足够空间的情况下,应该将留下的部分用参考地填充,且增加一些必要的过孔,将双面信号有效连接起来,对一些关键信号尽量采用地线隔离,对一些频率较高的设计,需特别考虑其地平面信号回路问题,建议采用多层板为宜。
②窜扰控制
窜扰(CrossTalk)是指PCB上不同网络之间因较长的平行布线引起的相互干扰,主要是由于平行线间的分布电容和分布电感的作用。克服窜扰的主要措施是:
A.加大平行布线的间距,遵循3W规则。
B.在平行线间插入接地的隔离线。
C.减少布线层与地平面的距离
③屏蔽保护
图12屏蔽保护
对应地线回路规则,实际上也是为了尽量减小信号的回路面积,多用于一些比较重要的信号,如时钟信号,同步信号;对一些特别重要,频率特别高的信号,应该考虑采用铜轴电缆屏蔽结构设计,即将所布的线上下左右用地线隔离,而且还要考虑好如何有效的让屏蔽地与实际地平面有效结合。
④走线方向控制规则
相邻层的走线方向成正交结构,避免将不同的信号线在相邻层走成同一方向,以减少不必要的层间窜扰;当由于板结构限制(如某些背板)难以避免出现该情况,特别是信号速率较高时,应考虑用地平面隔离各布线层,用地信号线隔离各信号线。
⑤电源与地线层的完整性规则
对于导通孔密集的区域,要注意避免孔在电源和地层的挖空区域相互连接,形成对平面层的分割,从而破坏平面层的完整性,并进而导致信号线在地层的回路面积增大。
四、软件编程1、软件流程图
本设计软件主程序流程图如图13所示。
图13主程序流程图
2、主程序
下面介绍main.c主程序编写,其他程序略。
头文件和一些宏定义
五、下载与调试
当程序在uVision环境下编写完成,并编译生成.hex文件后,就可以下载并进行调试了。
1、USB转串口驱动安装
打开USB驱动文件夹下的PL2303_Prolific_DriverInstaller_v130.exe安装文件,按提示安装USB转串口驱动程序。安装完成后,插入USB下载线后,在[开始]-[控制面板]-[打印机和其他硬件]-[设备管理器],在“端口”分支下有(ProlificUSB-to-SerialCommPort(COMX)。X表示串口号,如果没有说明USB转串口驱动没有安装,须重新安装。记住括号里的COM口号。
图14成功安装USB转串口驱动示意图
2、下载程序
打开STC单片机下载软件文件夹,点击运行STC_ISP_V481.exe程序,出现如下界面。
图15下载软件
正确选择MCU类型,COM口(与刚才安装的COM号一致),最高波特率和最低波特率都选2400bps或者1200bps(下载线内PL2303芯片所限,没办法!),并打开正确的.hex数据文件。
点击“Download/下载”按纽,窗口出现提示:
Chinese:正在尝试与MCU/单片机握手连接...
Connectionisfailure.Youcantry:
1.GiveyourMCUPowerOnReset.
2.Stopoperation,thenre-selectCOMPort.
3.BecausePLCC-DIP/PQFP-DIPSockettracetoolong.
4.UpdatetheSTCISP.exeversion.
5.Ifstillerror,yourMCUFirmwareiserror
ornull.
Chinese:连接失败,请尝试以下操作:
1.在单片机停电状态下,点下载按钮,再给单片机上电
2.停止下载,重新选择RS-232串口,接好电缆
3.可能需要先将P1.0/P1.1短接到地
4.可能外部时钟未接
5.因PLCC、PQFP转换座引线过长而引起时钟不振荡,请
调整参数
6.可能要升级电脑端的STCISP.exe软件
7.若仍然不成功,可能MCU/单片机内无ISP系统引导码,
或需退回升级,或MCU已损坏
8.若使用USB转RS-232串口线下载,可能会遇到不兼容
的问题,可以让我们帮助购买兼容的USB转RS-232
串口线
仍在连接中,请给MCU上电...
按下电路板上的电源按纽,保证其有个失电至上电的过程,则窗口显示开始烧录芯片。
芯片烧录成功后,程序开始运行,酒精浓度测试仪正常工作。

单片机源码:
  1. #include                        //调用单片机头文件
  2. #define uchar unsigned char  //无符号字符型 宏定义              变量范围0~255
  3. #define uint  unsigned int              //无符号整型 宏定义              变量范围0~65535
  4.  
  5. #include
  6. //#include "lcd1602.h"
  7.  
  8. sbit CS=P2^4;                            //CS定义为P2口的第4位脚,连接ADC0832CS脚
  9. sbit SCL=P2^3;                            //SCL定义为P2口的第3位脚,连接ADC0832SCL脚
  10. sbit DO=P2^2;                            //DO定义为P2口的第4位脚,连接ADC0832DO脚
  11.  
  12. sbit beep = P3^2;   //蜂鸣器IO口定义
  13. long dengji,s_dengji = 50;     //酒精等级
  14.  
  15.  
  16. bit flag_300ms ;
  17. uchar key_can;                            //按键值的变量
  18. uchar menu_1;        //菜单设计的变量
  19. uchar flag_clock;
  20. #include "eeprom52.h"
  21.  
  22. #include "lcd1602.h"
  23.  
  24.  
  25.  
  26. /***********************1ms延时函数*****************************/
  27. void delay_1ms(uint q)
  28. {
  29.               uint i,j;
  30.               for(i=0;i
  31.                             for(j=0;j<120;j++);
  32. }
  33.  
  34.  
  35. /******************把数据保存到单片机内部eeprom中******************/
  36. void write_eeprom()
  37. {
  38.               SectorErase(0x2000);
  39. //              byte_write(0x2000, s_dengji);
  40.               byte_write(0x2001, s_dengji);
  41.               byte_write(0x2060, a_a);             
  42. }
  43.  
  44. /******************把数据从单片机内部eeprom中读出来*****************/
  45. void read_eeprom()
  46. {
  47. //              s_dengji   = byte_read(0x2000);
  48.               s_dengji = byte_read(0x2001);
  49.               a_a      = byte_read(0x2060);
  50. }
  51.  
  52. /**************开机自检eeprom初始化*****************/
  53. void init_eeprom()
  54. {
  55.               read_eeprom();                            //先读
  56.               if(a_a != 2)                            //新的单片机初始单片机内问eeprom
  57.               {
  58.                             s_dengji = 80;
  59.                             a_a = 2;
  60.                             write_eeprom();
  61.               }             
  62. }
  63.  
  64. /***********读数模转换数据********************************************************/             
  65. //请先了解ADC0832模数转换的串行协议,再来读本函数,主要是对应时序图来理解,本函数是模拟0832的串行协议进行的
  66.                                                                                     //  1  0  0 通道
  67.                                                                                     //  1  1  1 通道
  68. unsigned char ad0832read(bit SGL,bit ODD)
  69. {
  70.               unsigned char i=0,value=0,value1=0;                           
  71.                             SCL=0;
  72.                             DO=1;
  73.                             CS=0;                            //开始
  74.                             SCL=1;                            //第一个上升沿             
  75.                             SCL=0;
  76.                             DO=SGL;
  77.                             SCL=1;                //第二个上升沿
  78.                             SCL=0;
  79.                             DO=ODD;
  80.                             SCL=1;                  //第三个上升沿
  81.                             SCL=0;                  //第三个下降沿
  82.                             DO=1;
  83.                             for(i=0;i<8;i++)
  84.                             {
  85.                                           SCL=1;
  86.                                           SCL=0; //开始从第四个下降沿接收数据
  87.                                           value<<=1;
  88.                                           if(DO)
  89.                                                         value++;                                                                                   
  90.                             }
  91.                             for(i=0;i<8;i++)
  92.                             {                                          //接收校验数据
  93.                                           value1>>=1;
  94.                                           if(DO)
  95.                                                         value1+=0x80;
  96.                                           SCL=1;
  97.                                           SCL=0;
  98.                             }
  99.                             CS=1;
  100.                             SCL=1;             
  101.                             if(value==value1)                                                        //与校验数据比较,正确就返回数据,否则返回0             
  102.                                           return value;
  103.               return 0;
  104. }
  105.  
  106.  
  107. /*************定时器0初始化程序***************/
  108. void time_init()               
  109. {
  110.               EA   = 1;                              //开总中断
  111.               TMOD = 0X01;                //定时器0、定时器1工作方式1
  112.               ET0  = 1;                              //开定时器0中断
  113.               TR0  = 1;                              //允许定时器0定时
  114. }
  115.  
  116. /****************按键处理显示函数***************/
  117. void key_with()
  118. {
  119.               if(key_can == 1)
  120.               {
  121.                             s_dengji ++ ;                                //酒精浓度设置数加1
  122.                             if(s_dengji > 999)
  123.                                           s_dengji = 999;
  124.               }
  125.               if(key_can == 2)
  126.               {
  127.                                           s_dengji -= 1;              //酒精浓度设置数减1
  128.                             if(s_dengji <= 1)
  129.                                           s_dengji = 1 ;
  130.               }
  131.               write_sfm2(2,9,s_dengji);                               //显示酒精等级
  132.               write_eeprom();       //保存数据                                                                     
  133.                                          
  134.  
  135. /********************独立按键程序*****************/
  136. uchar key_can;              //按键值
  137.  
  138. void key()              //独立按键程序
  139. {
  140.               static uchar key_new;
  141.               key_can = 20;                   //按键值还原
  142.               P1 |= 0x0f;
  143.               if((P1 & 0x0f) != 0x0f)                            //按键按下
  144.               {
  145.                             delay_1ms(1);                                 //按键消抖动
  146.                             if(((P1 & 0x0f) != 0x0f) && (key_new == 1))
  147.                             {                                                                                    //确认是按键按下
  148.                                           key_new = 0;
  149.                                           switch(P1 & 0x0f)
  150.                                           {
  151.                                                         case 0x0b: key_can = 2; break;                 //得到k3键值
  152.                                                         case 0x07: key_can = 1; break;                 //得到k4键值
  153.                                           }                           
  154.                             }                                         
  155.               }
  156.               else
  157.                             key_new = 1;             
  158. }
  159.  
  160. /****************报警函数***************/
  161. void clock_h_l()
  162. {
  163.               static uchar value;
  164.               if(dengji >= s_dengji )                            //报警
  165.               {
  166.                             value ++;
  167.                             if(value >= 2)
  168.                             {
  169.                                           value = 10;
  170.                                           beep = ~beep;                //蜂鸣器报警
  171.                             }
  172.               }else
  173.               {
  174.                             if(dengji < s_dengji)                //取消报警
  175.                             {
  176.                                           value = 0;
  177.                                           beep = 1;
  178.                             }             
  179.               }
  180. }
  181.  
  182. /***************主函数*****************/
  183. void main()
  184. {
  185.               beep = 0;                                            //开机叫一声  
  186.               delay_1ms(150);
  187.               P0 = P1 = P2 = P3 = 0xff;                            //单片机IO口初始化为1             
  188.               init_eeprom();                                                //读eeprom数据
  189.               time_init();                                                        //初始化定时器
  190.               init_1602();
  191.               while(1)
  192.               {
  193.                             key();                                                                      //独立按键程序
  194.                             if(key_can < 10)
  195.                             {
  196.                                           key_with();                                          //按键按下要执行的程序
  197.                             }
  198.                             if(flag_300ms == 1)
  199.                             {                           
  200.                                           flag_300ms = 0;
  201.                                           clock_h_l();                 //报警函数
  202.                                           dengji = ad0832read(1,0);             
  203. ……………………
  204.  
  205. …………限于本文篇幅 余下代码请从51黑下载附件…………
  206.  
复制代码

 
(以上内容来自互联网)
0
首页
电话
短信
联系