视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
酒精浓度测试仪设计
2025-09-25 14:25:25 责编:小OO
文档
酒精浓度测试仪设计报告

                                      目   录

酒精浓度测试仪设计报告    1

一、 设计意义    3

二、 硬件设计    3

1、设计框图    3

2、乙醇信号检测及调理电路    4

3、单片机电路    7

4、显示电路    8

5、供电及程序下载电路    9

三、 Protel硬件开发软件    10

1.Protel软件组成    10

2.PCB板设计    11

四、 软件编程    13

1、软件流程图    13

2、主程序    14

五、 下载与调试    20

1、 USB转串口驱动安装    20

2、 下载程序    21

参考文献    22

程序    22

一、设计意义

自《刑法修正案(八)》和修改后的《道路交通安全法》正式实施,“醉酒驾驶”正式入刑。不仅交警部门,而且很多车主都期盼能够有便携仪器方便地测量气体酒精浓度,为安全驾驶提供保障,有效减少重大交通事故的发生。

本研究设计的酒精浓度测试仪是一款实用性强、安全可靠的气体乙醇浓度检测工具,采用高精度MQ-3乙醇气体传感器对空气中的乙醇浓度进行检测,利用宏晶公司高性能低成本单片机STCC52对检测信号进行A/D转换和处理,最后通过液晶屏显示输出。本研究设计的酒精浓度测试仪还具有醉酒阈值设定功能,可以根据法律法规或用户需要设定修改醉酒阈值,并进行保存。

二、硬件设计

1、设计框图

本研究设计的酒精浓度测试仪框图如图1所示。MQ-3乙醇气体传感器输出信号经信号调理电路处理,输出随乙醇浓度变化的电压信号,该电压信号送入单片机系统,经AD转换,与设定的醉酒阈值进行比较,并显示或报警。

图1 酒精浓度测试仪方框图

2、乙醇信号检测及调理电路

MQ-3乙醇气体传感器可以应用用于机动车驾驶人员及其他严禁酒后作业人员的现场检测,也用于其他场所乙醇蒸汽的检测。其技术特点为:

●对乙醇蒸汽有很高的灵敏度和良好的选择性

●快速的响应恢复特性

●长期的寿命和可靠的稳定性

●简单的驱动回路

主要技术指标:

MQ-3乙醇气体传感器灵敏度曲线如图2所示,其传感原理为气敏电阻的输出阻值随乙醇气体等浓度变化而变化。

图2 MQ-3乙醇气体传感器灵敏度曲线

MQ-3乙醇气体传感器管脚与测试电路如图3所示。

    (a) 管脚图                   (b) 测试电路

图3 MQ-3乙醇气体传感器管脚及测试电路

    MQ-3乙醇气体传感器及其调理电路原理如图4所示。其外形如图5所示。经过调理,检测信号由电阻值转变成电压值,便于后续电路进行A/D转换和处理。

图4 传感器及调理模块原理图

  该传感器模块具有如下特点,方便与单片机系统接口组成检测仪器。

●具有信号输出指示。

●双路信号输出(模拟量输出及TTL电平输出)

●TTL输出有效信号为低电平。

(当输出低电平时信号灯亮,可直接接单片机)                 

●模拟量输出0~5V电压,浓度越高电压越高。

3、单片机电路

本设计选用宏晶公司高性能单片机STCC52,其管脚如图6所示。

图6  STCC52单片机管脚图

该芯片为52内核8位单片机,适用于常用检测电路。由STCC52组成的单片机系统原理图如图7所示。图中AOUT为MQ-3传感器模块输出的检测电压信号,送入ADC0832采集芯片端口进行处理,该信号可以根据乙醇气体浓度直接输出报警信号,报警阈值通过模块上的电位器进行调节。

图7 单片机系统原理图

   图7中,按键K2和K3为醉酒阈值调整键,其中K2为“增加”,K3为“减小”按键。L2和L3为报警指示灯,分别可以进行酒后和醉酒两级报警。

4、显示电路

显示部分采用SMC 1602液晶屏进行数据显示,其主要技术参数为:

表1  液晶屏技术指标

接口信号说明如表2所示。

表2  液晶屏接口信号说明

与单片机接口电路如图8所示。其中J2的3脚为背光引脚,R9和R10电阻用于调节背光亮度。J2的4、5、6引脚分别接液晶的RS、E/W和E控制引脚,J2的7—14引脚为数据引脚。

图8  LCD与单片机接口电路

5、供电及程序下载电路

本设计采用电池盒接口供电,电源电压5V。同时,其电路原理如图10所示。

图10  供电及程序下载电路

三、Protel硬件开发软件

Protel是目前国内最流行的通用EDA软件,它是将电路原理图设计、PCB板图设计、电路仿真和PLD设计等多个实用工具软件组合后构成的EDA工作平台,是第一个将EDA软件设计成基于Windows的普及型产品。它集成了软件界面、仿真功能和PLD设计和信号完整性分析,在此基础上Protel 99SE又增加了一些新的功能,用户使用更加方便灵活。Protel的功能十分强大,在电子电路设计领域占有极其重要的地位。它以其强大功能和实用性,逐渐获得广大硬件设计人员的青睐,是目前众多EDA设计软件中用户最多的产品之一。

1.Protel软件组成

Protel软件主要由电路原理图设计模块、印制电路板设计模块(PCB设计模块)、电路信号仿真模块和PLD逻辑器件设计模块等组成,各模块具有强大的功能,可以很好的实现电路设计与分析。

(1)  原理图设计模块(Schematic模块)

电路原理图是表示电气产品或电路工作原理的重要技术文件,电路原理图主要由代表各种电子器件的图形符号、线路和结点组成。图4.1所示为一张电路原理图。该原理图是由Schematic模块设计完成的。Schematic模块具有如下功能:丰富而灵活的编辑功能、在线库编辑及完善的库管理功能、强大的设计自动化功能、支持层次化设计功能等。  

(2) 印制电路板设计模块(PCB设计模块)

印制电路板(PCB)制板图是由电路原理图到制作电路板的桥梁。设计了电路原理图后,需要根据原理图生设计成印制电路板的制板图,然后在根据制板图制作具体的电路板。印制电路板设计模块具有如下主要功能和特点:可完成复杂印制电路板(PCB)的设计;方便而又灵活的编辑功能;强大的设计自动化功能;在线式库编辑及完善的库管理;完备的输出系统等。

(3) 电路信号仿真模块

电路信号仿真模块是一个功能强大的数字/模拟混合信号电路仿真器,能提供连续的模拟信号和离散的数字信号仿真。它运行在Protel的EDA/Client集成环境下,与Protel Advanced Schematic原理图输入程序协同工作,作为Advanced Schematic的扩展,为用户提供了一个完整的从设计到验证仿真设计环境。

在Protel中进行仿真,只需从仿真用元器件库中放置所需的元器件,连接好原理图,加上激励源,然后单击防真按钮即可自动开始。

2.PCB板设计

(1)  定元件的封装

① 打开网络表(可以利用一些编辑器辅助编辑),将所有封装浏览一遍,确保所有元件的封装都正确无误并且元件库中包含所有元件的封装,网络表中所有信息全部大写,一面载入出问题,或PCB BOM不连续。

② 标准元件全部采用公司统一元件库中的封装。

③ ④⑥⑤元件库中不存在的封装,应自己建立元器件库。

(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主程序编写,其他程序略。

(1)  头文件和一些宏定义

#include              //调用单片机头文件

#define uchar unsigned char  //无符号字符型 宏定义    变量范围0~255

#define uint  unsigned int     //无符号整型 宏定义    变量范围0~65535

#include

//#include "lcd1602.h"

sbit CS=P2^4;        //CS定义为P2口的第4位脚,连接ADC0832CS脚

sbit SCL=P2^3;        //SCL定义为P2口的第3位脚,连接ADC0832SCL脚

sbit DO=P2^2;        //DO定义为P2口的第4位脚,连接ADC0832DO脚

sbit beep = P3^2;   //蜂鸣器IO口定义

long dengji,s_dengji = 50;     //酒精等级

bit flag_300ms ;

uchar key_can;         //按键值的变量

uchar menu_1;        //菜单设计的变量

uchar flag_clock;

#include "eeprom52.h"

#include "lcd1602.h"

/***********************1ms延时函数*****************************/

void delay_1ms(uint q)

{

    uint i,j;

for(i=0;i     for(j=0;j<120;j++);

}

/******************把数据保存到单片机内部eeprom中******************/

void write_eeprom()

{

    SectorErase(0x2000);

//    byte_write(0x2000, s_dengji);

    byte_write(0x2001, s_dengji);

    byte_write(0x2060, a_a);    

}

/******************把数据从单片机内部eeprom中读出来*****************/

void read_eeprom()

{

//    s_dengji   = byte_read(0x2000);

    s_dengji = byte_read(0x2001);

    a_a      = byte_read(0x2060);

}

/**************开机自检eeprom初始化*****************/

void init_eeprom()

{

    read_eeprom();        //先读

    if(a_a != 2)        //新的单片机初始单片机内问eeprom

    {

        s_dengji = 80;

        a_a = 2;

        write_eeprom();

    }    

}

/***********读数模转换数据********************************************************/    

//请先了解ADC0832模数转换的串行协议,再来读本函数,主要是对应时序图来理解,本函数是模拟0832的串行协议进行的

                        //  1  0  0 通道

                        //  1  1  1 通道 

unsigned char ad0832read(bit SGL,bit ODD)

{

    unsigned char i=0,value=0,value1=0;        

        SCL=0;

        DO=1;

        CS=0;        //开始

        SCL=1;        //第一个上升沿    

        SCL=0;

        DO=SGL;

        SCL=1;      //第二个上升沿

        SCL=0;

        DO=ODD;

        SCL=1;        //第三个上升沿

        SCL=0;        //第三个下降沿

        DO=1;

     for(i=0;i<8;i++)

        {

            SCL=1;

            SCL=0; //开始从第四个下降沿接收数据

         value<<=1;

            if(DO)

                value++;                        

        }

     for(i=0;i<8;i++)

        {            //接收校验数据

         value1>>=1;

            if(DO)

                value1+=0x80;

            SCL=1;

            SCL=0;

        }

        CS=1;

        SCL=1;    

        if(value==value1)                //与校验数据比较,正确就返回数据,否则返回0    

            return value;

    return 0;

}

/*************定时器0初始化程序***************/

void time_init()      

{

    EA   = 1;           //开总中断

    TMOD = 0X01;      //定时器0、定时器1工作方式1

    ET0  = 1;          //开定时器0中断 

    TR0  = 1;          //允许定时器0定时

}

/****************按键处理显示函数***************/

void key_with()

{

    if(key_can == 1)

    {

        s_dengji ++ ;            //酒精浓度设置数加1

     if(s_dengji > 999)

            s_dengji = 999;

    }

    if(key_can == 2)

    {

            s_dengji -= 1;    //酒精浓度设置数减1

     if(s_dengji <= 1)

            s_dengji = 1 ;

    }

    write_sfm2(2,9,s_dengji);           //显示酒精等级

    write_eeprom();       //保存数据                    

            

}  

/********************按键程序*****************/

uchar key_can;     //按键值

void key()     //按键程序

{

    static uchar key_new;

    key_can = 20;                   //按键值还原

    P1 |= 0x0f;

    if((P1 & 0x0f) != 0x0f)        //按键按下

    {

        delay_1ms(1);             //按键消抖动

        if(((P1 & 0x0f) != 0x0f) && (key_new == 1))

        {                        //确认是按键按下

            key_new = 0;

            switch(P1 & 0x0f)

            {

                case 0x0b: key_can = 2; break;       //得到k3键值

                case 0x07: key_can = 1; break;       //得到k4键值

            }        

        }            

    }

    else 

        key_new = 1;    

}

/****************报警函数***************/

void clock_h_l()

{

    static uchar value;

if(dengji >= s_dengji )        //报警

    {

        value ++;

     if(value >= 2)

        {

            value = 10;

            beep = ~beep;      //蜂鸣器报警

        }

    }else 

    {

     if(dengji < s_dengji)      //取消报警

        {

            value = 0;

            beep = 1;

        }    

    }

}

/***************主函数*****************/

void main()

{

    beep = 0;                        //开机叫一声   

    delay_1ms(150);

    P0 = P1 = P2 = P3 = 0xff;        //单片机IO口初始化为1    

    init_eeprom();                  //读eeprom数据

    time_init();                //初始化定时器 

    init_1602();

    while(1)

    {

        key();                    //按键程序

     if(key_can < 10)

        {

            key_with();            //按键按下要执行的程序

        }

        if(flag_300ms == 1)

        {        

            flag_300ms = 0;

            clock_h_l();       //报警函数

            dengji = ad0832read(1,0);    

            dengji = dengji * 450 / 255.0;

            dengji = dengji - 130;              //首先减去零点漂移,一般是1V

         if(dengji < 0)

                dengji = 0;    

            dengji = dengji * 2;             //将mV转变成mg/L,系数需要校准   

                                  //电压每升高0.1V,实际被测气体的浓度增加20ppm 

                                  //1ppm=1mg/kg=1mg/L=1×10-6 常用来表示气体浓度,或者溶液浓度。       

            write_sfm2(1,9,dengji);          //显示酒精浓度

        } 

    }

}

/*************定时器0中断服务程序***************/

void time0_int() interrupt 1

{    

    static uchar value;

    TH0 = 0x3c;

    TL0 = 0xb0;     // 50ms

    value ++;     

    if(value % 6 == 0)

    {

        flag_300ms = 1;       //300ms

        value = 0;

    }

}

五、下载与调试

当程序在uVision环境下编写完成,并编译生成.hex文件后,就可以下载并进行调试了。

1、USB转串口驱动安装

打开USB驱动文件夹下的PL2303_Prolific_DriverInstaller_v130.exe安装文件,按提示安装USB转串口驱动程序。安装完成后,插入USB下载线后,在[开始]-[控制面板]-[打印机和其他硬件]-[设备管理器],在“端口”分支下有(Prolific USB-to-Serial Comm Port(COMX)。X表示串口号,如果没有说明USB转串口驱动没有安装,须重新安装。记住括号里的COM口号。

图14 成功安装USB转串口驱动示意图

2、下载程序

打开STC单片机下载软件文件夹,点击运行STC_ISP_V481.exe程序,出现如下界面。

图15 下载软件

正确选择MCU 类型,COM口(与刚才安装的COM号一致),最高波特率和最低波特率都选2400bps或者1200bps (下载线内PL2303芯片所限,没办法!),并打开正确的.hex数据文件。

点击“Download/下载”按纽,窗口出现提示:

Chinese:正在尝试与 MCU/单片机 握手连接 ... 

Connection is failure. You can try:

 1.Give your MCU Power On Reset.

 2.Stop operation, then re-select COM Port.

 3.Because PLCC-DIP/PQFP-DIP Socket trace too long.

 4.Update the STC ISP.exe version.

 5.If still error, your MCU Firmware is error

   or null.

Chinese:连接失败,请尝试以下操作:

 1.在单片机停电状态下,点下载按钮,再给单片机上电

 2.停止下载,重新选择 RS-232 串口, 接好电缆

 3.可能需要先将 P1.0/P1.1 短接到地

 4.可能外部时钟未接

 5.因 PLCC、PQFP 转换座引线过长而引起时钟不振荡,请

   调整参数

 6.可能要升级电脑端的 STC ISP.exe 软件

 7.若仍然不成功,可能 MCU/单片机内无 ISP 系统引导码,

   或需退回升级,或 MCU 已损坏

 8.若使用 USB 转 RS-232 串口线下载,可能会遇到不兼容

   的问题,可以让我们帮助购买兼容的 USB 转 RS-232 

   串口线

 仍在连接中, 请给 MCU 上电...

按下电路板上的电源按纽,保证其有个失电至上电的过程,则窗口显示开始烧录芯片。

芯片烧录成功后,程序开始运行,酒精浓度测试仪正常工作。

参考文献

[1] 陈明荧.8051单片机课程设计实训教材[M]. 北京:清华大学出版社,2003

[2] 徐新艳.单片机原理、应用与实践[M]. 北京:高等教育出版社,2005

[3] 吴金戌,沈庆阳,郭庭吉.8051单片机实践与应用[M]. 北京:清华大学出版社,2002

[4] 王千.实用电子电路大全[M]. 电子工业出版社,2001

[5] 冯博琴.微型计算机原理与接口技术[M]. 北京:清华大学出版社,2004

[6] 张毅刚.MCS-51单片机应用设计[M]. 哈尔滨工业大学出版社,2004

[7] 张淑清,姜万录等.单片微型计算机接口技术及应用[M]. 国防工业出版社,2003

[8] 吴金戌,沈庆阳,郭庭吉.8051单片机实践与应用[M]. 北京:清华大学出版社,2001

[9] 赵亮,侯国锐.单片机C语言编程与实例[M]. 北京:人民邮电出版社,2003

[10] 王振红,李洋,郝承祥.WT588D语音芯片的工作原理及其在智能控制系统中的应用[J]. 电子器件2002年3月第25卷第1期

[11] Ingle-Chip Voice Record/Playback Devices 4-, 5-, 6-, and 8-Minute Durations,1990

[12] ANALOG DEVICES.The technology of ATC51[EB/OL].White Paper,Spe.28,2000

程序

#include              //调用单片机头文件

#define uchar unsigned char  //无符号字符型 宏定义    变量范围0~255

#define uint  unsigned int     //无符号整型 宏定义    变量范围0~65535

#include

//#include "lcd1602.h"

sbit CS=P2^4;        //CS定义为P2口的第4位脚,连接ADC0832CS脚

sbit SCL=P2^3;        //SCL定义为P2口的第3位脚,连接ADC0832SCL脚

sbit DO=P2^2;        //DO定义为P2口的第4位脚,连接ADC0832DO脚

sbit beep = P3^2;   //蜂鸣器IO口定义

long dengji,s_dengji = 50;     //酒精等级

bit flag_300ms ;

uchar key_can;         //按键值的变量

uchar menu_1;        //菜单设计的变量

uchar flag_clock;

#include "eeprom52.h"

#include "lcd1602.h"

/***********************1ms延时函数*****************************/

void delay_1ms(uint q)

{

    uint i,j;

for(i=0;i     for(j=0;j<120;j++);

}

/******************把数据保存到单片机内部eeprom中******************/

void write_eeprom()

{

    SectorErase(0x2000);

//    byte_write(0x2000, s_dengji);

    byte_write(0x2001, s_dengji);

    byte_write(0x2060, a_a);    

}

/******************把数据从单片机内部eeprom中读出来*****************/

void read_eeprom()

{

//    s_dengji   = byte_read(0x2000);

    s_dengji = byte_read(0x2001);

    a_a      = byte_read(0x2060);

}

/**************开机自检eeprom初始化*****************/

void init_eeprom()

{

    read_eeprom();        //先读

    if(a_a != 2)        //新的单片机初始单片机内问eeprom

    {

        s_dengji = 80;

        a_a = 2;

        write_eeprom();

    }    

}

/***********读数模转换数据********************************************************/    

//请先了解ADC0832模数转换的串行协议,再来读本函数,主要是对应时序图来理解,本函数是模拟0832的串行协议进行的

                        //  1  0  0 通道

                        //  1  1  1 通道 

unsigned char ad0832read(bit SGL,bit ODD)

{

    unsigned char i=0,value=0,value1=0;        

        SCL=0;

        DO=1;

        CS=0;        //开始

        SCL=1;        //第一个上升沿    

        SCL=0;

        DO=SGL;

        SCL=1;      //第二个上升沿

        SCL=0;

        DO=ODD;

        SCL=1;        //第三个上升沿

        SCL=0;        //第三个下降沿

        DO=1;

     for(i=0;i<8;i++)

        {

            SCL=1;

            SCL=0; //开始从第四个下降沿接收数据

         value<<=1;

            if(DO)

                value++;                        

        }

     for(i=0;i<8;i++)

        {            //接收校验数据

         value1>>=1;

            if(DO)

                value1+=0x80;

            SCL=1;

            SCL=0;

        }

        CS=1;

        SCL=1;    

        if(value==value1)                //与校验数据比较,正确就返回数据,否则返回0    

            return value;

    return 0;

}

/*************定时器0初始化程序***************/

void time_init()      

{

    EA   = 1;           //开总中断

    TMOD = 0X01;      //定时器0、定时器1工作方式1

    ET0  = 1;          //开定时器0中断 

    TR0  = 1;          //允许定时器0定时

}

/****************按键处理显示函数***************/

void key_with()

{

    if(key_can == 1)

    {

        s_dengji ++ ;            //酒精浓度设置数加1

     if(s_dengji > 999)

            s_dengji = 999;

    }

    if(key_can == 2)

    {

            s_dengji -= 1;    //酒精浓度设置数减1

     if(s_dengji <= 1)

            s_dengji = 1 ;

    }

    write_sfm2(2,9,s_dengji);           //显示酒精等级

    write_eeprom();       //保存数据                    

            

}  

/********************按键程序*****************/

uchar key_can;     //按键值

void key()     //按键程序

{

    static uchar key_new;

    key_can = 20;                   //按键值还原

    P1 |= 0x0f;

    if((P1 & 0x0f) != 0x0f)        //按键按下

    {

        delay_1ms(1);             //按键消抖动

        if(((P1 & 0x0f) != 0x0f) && (key_new == 1))

        {                        //确认是按键按下

            key_new = 0;

            switch(P1 & 0x0f)

            {

                case 0x0b: key_can = 2; break;       //得到k3键值

                case 0x07: key_can = 1; break;       //得到k4键值

            }        

        }            

    }

    else 

        key_new = 1;    

}

/****************报警函数***************/

void clock_h_l()

{

    static uchar value;

if(dengji >= s_dengji )        //报警

    {

        value ++;

     if(value >= 2)

        {

            value = 10;

            beep = ~beep;      //蜂鸣器报警

        }

    }else 

    {

     if(dengji < s_dengji)      //取消报警

        {

            value = 0;

            beep = 1;

        }    

    }

}

/***************主函数*****************/

void main()

{

    beep = 0;                        //开机叫一声   

    delay_1ms(150);

    P0 = P1 = P2 = P3 = 0xff;        //单片机IO口初始化为1    

    init_eeprom();                  //读eeprom数据

    time_init();                //初始化定时器 

    init_1602();

    while(1)

    {

        key();                    //按键程序

     if(key_can < 10)

        {

            key_with();            //按键按下要执行的程序

        }

        if(flag_300ms == 1)

        {        

            flag_300ms = 0;

            clock_h_l();       //报警函数

            dengji = ad0832read(1,0);    

            dengji = dengji * 450 / 255.0;

            dengji = dengji - 130;              //首先减去零点漂移,一般是1V

         if(dengji < 0)

                dengji = 0;    

            dengji = dengji * 2;             //将mV转变成mg/L,系数需要校准   

                                  //电压每升高0.1V,实际被测气体的浓度增加20ppm 

                                  //1ppm=1mg/kg=1mg/L=1×10-6 常用来表示气体浓度,或者溶液浓度。       

            write_sfm2(1,9,dengji);          //显示酒精浓度

        } 

    }

}

/*************定时器0中断服务程序***************/

void time0_int() interrupt 1

{    

    static uchar value;

    TH0 = 0x3c;

    TL0 = 0xb0;     // 50ms

    value ++;     

    if(value % 6 == 0)

    {

        flag_300ms = 1;       //300ms

        value = 0;

    }

}下载本文

显示全文
专题