本文简要介绍了基于微控制器的酒精报警器的设计。
论文首先介绍了酒精传感器的基本原理及相关的微控制器理论。然后,给出自己的设计,它基于8位微控制器ATS52。
本设计中使用的是流行的TP-3C型半导体酒精气敏传感器;ATS52与模数转换器ADS1110通过I2C总线接口。
关键词:微控制器;气敏传感器;报警器;A/D转换;I2C总线;SPI总线;TP-3C;ADS1110;ATS52
Abstract
This paper introduces the design of alcohol alarm briefly based on Microcontroller unit (MCU).
In the first place, this paper introduces the basic principles of alcohol sensors and basic theories of MCU that are related. Then, put forward the design based on ATS52 which is a 8-bit MCU designed by myself.
The widely used TP-3C which is a air-sensitive semiconductor alcohol sensor is used in this design ; ATS52 and A/D convertor ADS1110 are connected by the I2C bus.
Key Words:Microcontroller Unit;Gas Sensor;Alarm;A/D Converter;I2C Bus;SPI Bus;TP-3C;ADS1110;ATs52
摘要.............................................................................................................................................I
Abstract.....................................................................................................................................II
目录..........................................................................................................................................III
引言.............................................................................................................................................1
第一章酒精传感器的基本原理...............................................................................................2
第二章硬件电路设计
2.1核心器件简介.........................................................................................................4
2.1.1关与ATS52............................................................................................. 4
2.1.2关于ADS1110...............................................................................................9
2.1.3关于ISD2560...............................................................................................11
2.2硬件电路设计.......................................................................................................12
2.2.1概述..............................................................................................................12
2.2.2电路设计原理总图与PCB设计................................................................ 13
2.2.3电源与处理器子系统................................................................................. 14
2.2.4酒精检测子系统..........................................................................................16
2.2.5键盘与LCD显示子系统............................................................................18
2.2.6语音报警子系统........................................................................................ 19
第三章 软件设计
3.1概述.......................................................................................................................19
3.2软件设计流程图...................................................................................................20
3.3代码实现与实验验证...........................................................................................21 结束语.......................................................................................................................................24
致谢...........................................................................................................................................25
参考文献...................................................................................................................................26
附录..........................................................................................................................................27
引 言
如今,气敏传感器已在很多领域广泛使用。特别是在有害气体安全检测中,它们充当着重要角色,为人们安全生产和正常生活做出了卓越贡献,酒精气敏传感器就是其中的一种。
本文主要讨论的是基于微控制器的酒精传感器的应用,即便携式酒精报警器,它在交通系统及特定工业现场应用广泛。论文在对目前流行的酒精传感器的原理作简要分析的基础上,针对便携式应用,提出一些设计思想并予实现;同时为了概念清楚,还将介绍一些单片机方面的内容。
目前常用的酒精传感器有电化学酒精传感器和半导体酒精传感器,大多数人都认为电化学酒精传感器比半导体酒精传感器稳定可靠。这部分内容将在有关章节简要介绍。本设计为原理性验证,使用的相对便宜的半导体酒精气敏传感器TP-3C。
在酒精传感器的应用领域里,可有多种实现方案。对半导体酒精传感器的应用设计方案中多采用采集传感器两端的电压与标准参考电压作对比的方法,然后再做后续处理,因为半导体酒精气敏传感器是根据酒精浓度敏感而改变自身体电阻的,相应内容请参考TP-3C的datasheet,讲述TP-3C的工作原理时也有简要介绍。
本设计采用的是优化的设计方案采用可编程模放大倍数的模数转换芯片ADS1110,它支持I2C总线,传感器模块的设计是基于I2C总线的。ADS1110具有很多优点,具有片内基准电压,片内可编程增益放大器PGA,片内振荡器,可编程转换速率,具有16 位分辨率,相关内容请参见ADS1110的datasheet,相应章节也有简要介绍。
第一章
酒精传感器的基本原理
1.1酒精传感器的基本原理
1.1.1引言
气敏传感器是一种能够感知环境中某种气体及其浓度的一种敏感元件,它能将气体成分、浓度等有关信息转换成电信号,以便于检测、监控、报警;通过与计算机接口,可组成检测、控制和报警系统 。它可分为识别系统与传导系统;识别系统选择性的与待测物发生反映,将所测得的化学参数转化为传导系统可以产生相应的信号;传导系统接收响应信号,通过电极、光或质量敏感元件将响应信号以电压、电流或光强度等变化形式,传送到电子系统进行放大或输出,使响应信号成为人们能分析的信号,达到检测的目的。本设计中采用的是TP系列的气敏传感器——TP-3C,它采用二氧化锡作为制作材料,能有效检测现场酒精浓度,且灵敏度高,电路简单。TP-3C显然是一种半导体型气敏传感器,还有一种常见的气敏传感器是电化学型气敏传感器。
酒精气敏传感器在日常生活中日见广泛。传统上,人们利用其进行测量有关参数停留在手工阶段,给操作带来了许多不便。因此,开发使用一种高效智能化的测试系统非常必要。本设计中,采用基于微控制器ATS52的语音报警系统,能直接有效的反映检测现场状态,给操作人员带来了极大方便。
1.1.2半导体型气敏传感器的基本原理
半导体型气敏传感器多采用半导体氧化物作为制作材料,其气—电转换原理是:在不同气体中,半导体氧化物材料吸附气体发生氧化还原反应,引起材料电导的不同变化,使传感器分辨出被检测气体。
本设计中采用的TP-3C是用二氧化锡材料制成的酒精气敏传感器,它利用二氧化锡薄膜表面吸附乙醇气体后电阻的变化来间接测试乙醇浓度的。其原理是当N型的二氧化锡膜被加热带一定温度后,其空穴中的电子将吸附表面的氧气分子变成带电状态使其薄膜体电阻发生变化,此过程为:
O2(gas)+2e-→2O-
经此过程后,二氧化锡薄膜才会对被测气体具有还原性,通常改过程称之为敏感化过程;此后,二氧化锡若接触到乙醇气体时,乙醇气体分子与SnO2表面带电的氧离子发生反应:
C2H5OH(gas)+O-→2CH3CHO(gas)+2H2O
从上式可以看出,反应结束后电子重新回到SnO2表面的空穴当中,于是二氧化锡薄膜的表面电阻再次变化。
TP-3C的测试电路如图1:
图1 TP-3C型气敏传感器测试电路
第二章硬件电路设计
2.1 核心器件简介
2.1.1 关于ATS52
2.1.1.1 引言
ATS52是ATMAL公司的兼容51内核的8位低功耗、高性能单片机,具有8K内部FLASH存储器。ATS5X系列单片机具有独特的优势,可进行10000次程序反复写入且支持在系统编程,不需专门使用编程器进行编程,只需通过SPI串行总线接口和简单的软硬件支持就可现场将程序下载至FLASH(使用脚P1.5-P1.7,RST),也能实现“在系统”仿真和“在线”远程升级,因此在系统开发过程中可以十分容易的将程序进行修改,反复进行试验,大大缩短系统开发周期,同时可以保证用户的系统设计可以达到最优。
2.1.1.2 ATS52的并行输入/输出口
51系列单片机有四个8为并行输入/输出接口:P0,P1,P2,P3。这四个口既可以并行进行输入或输出8位数据,也可以按位使用,即每一位均能作输入或输出口。
P0口是一个三态双向口,可作为地址/数据分时复用口,也可作为通用I/O接口。其字节地址为80H,位地址为80H-87H。P0口电路逻辑主要包括:
●一个数据输出锁存器,用于进行数据位的锁存。
●两个三态输出缓冲器,分别用于锁存器数据和引脚数据的输入缓冲。
●一个多路转接开关MUX,在控制信号作用下由其实现锁存器输出和地址/数据线之间的连通转接。
●数据输出的驱动可控制电路,由两只FET组成,上面的那个FET构成上拉电路。
需要注意的是P0口作为输出口使用时必须外接上拉电阻才有高电平输出,其输出电路为漏极开路电路。作为输入口使用时应区分读引脚与读端口的区别,所谓独引脚就是读芯片引脚上的数据,也就是直接读取外部数据;而读端口则是通过锁存器上方的缓冲器把锁存器的Q端状态读进来。
P1口的输出不是三态的,所以它是准三态口,它只作为通用I/O口使用,内部也无需多路转接开关MUX,其输出电路中有上拉电阻,所以作为输出口时外电路也无需接入上拉电阻。
P1口的字节地址为90H,位地址为90H-97H。要注意的是,P1口作为输入口使用时,应先向其锁存器写1,使输出驱动电路的FET截止。另外对于52子系列单片机,还具有第二功能,这通常与定时器/计数器2及SPI总线有关。
P2口也为准三态口,其字节地址为0A0H,位地址为0A0H-0A7H。除可用于通用I/O口外,它还可用于为系统提供高地址位。
P3口的字节地址为0B0H,位地址为0B0H-0B7H。虽其可作为通用I/O口使用,但在实际中其第二功能信号更为重要。不管是作为输入口使用还是第二功能信号输入,输出电路中的锁存器输出和“第二功能输出信号”线都应保持高电平。
2.1.1.3 ATS52的定时与中断系统
在实际应用中,定时与中断联系异常紧密,学好它们是学好单片机的核心技术。
中断技术实质上是一种资源共享技术,在不使用操作系统时,中断技术可使有限的资源进行充分的应用。在单片机系统中,中断技术主要用于实时控制。ATS52中三类共有六个中断源:外部中断、定时中断、串行中断。
●外部中断:由外部信号引起,共两个中断源——外部中断“0”和外部中断“1”。它们的中断请求信号由引脚/INT0(P3.2)和/INT1(P3.3)引入。它有两种信号请求方式:电平方式和脉冲方式。电平方式的中断请求低电平有效,脉冲方式的中断请求则是脉冲的后延跳变有效。此种情况下,请求信号的高低电平都应维持至少一个机器周期。
●定时中断:为满足定时或计数需要而设定,从应用角度讲,此过程无需过问。
●串行中断:为串行数据传送的需要而设置的。每当串口接收或发送完一组串行数据时,就产生一个串行请求。和定时中断一样,请求信号时单片机芯片内部自动产生的,不需在芯片上设置引入端。
在本设计中使用了定时器,下面简要介绍与定时器相关的中断技术。
ATS52有三个计数器/定时器,其中计数器/定时器0和1和51系列单片机兼容,而定时器/计数器2为16位,通过设置SFR T2CON可有三种工作模式:捕获、自动加载和波特率发生器。请见下表:
表1:ATS52的SFR T2CON的工作模式设置
和其有关的模式控制寄存器T2MOD的相关信息如下:
表2:计数器2的模式控制寄存器T2MOD
对于定时器0和1最起码要用到的和中断相关的寄存器如下:
●定时器控制寄存器(TCON),寄存器地址88H,位地址8FH-88H。
●中断允许控制寄存器(IE),寄存器地址0A8H,位地址0AFH-0A8H。
当然,如果用到串行口应该要用到串行口控制寄存器(SCON);若需要对中断分级,中断优先级控制寄存器(IP)应该用到。对于计数器/定时器,再设置工作方式寄存器(TMOD),就能设定两个计数器/定时器的工作状态和中断处理方式。这在以后的软件编程中可以得到体现。注意,IE.5是定时器2使能位。
2.1.1.4 ATS52的串行通信及SPI总线技术、I2C总线技术
通信分串行和并行两种。并行通信是构成一组数据的各位同时进行传输,其特点是传送速度快,但当距离较远、位数有多时,会导致通信线路复杂且成本高。串行通信是数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现通信,从而降低了成本,特别适用于远距离通信,其缺点是传送速度慢。
串行通信又可分为异步传送和同步传送两种方式。
异步传送的特点是数据在线路上的传送不连续。传送时,数据是以一个字符(又称为一帧)为单位进行传送的。一帧信息由起始位、数据位、奇偶校验位和停止位四个部分组成,见下图:
图4 异步通信的数据格式
同步通信时要建立发送方始终对接受方时钟的直接控制,使双方达到完全同步,因此在数据块开头处要用字符SYN来加以指示。此时,传送数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间缝,即保持位同步关系,也保持字符同步关系。发送方对接收方的同步可以通过外同步和自同步方式实现。面向位得同步格式和面向字符的同步格式见下图:
图5 面向位/字符的同步格式
要说的一点是波特率(Baud rate),计算如下:
波特率=一个字符的二进制编码位数*字符/秒
在本设计中,用到的I2C、SPI总线均属同步串行总线,其它的单总线(1-wire)、Microwire等也属此类。下面介绍I2C和SPI总线。
I2C总线是Philips公司推出的一种基于两线制的同步串行总线,其全称为芯片间总线(Inter IC Bus)。两线是数据线SDA和时钟线SCL,数据传送采用主从方式,即主器件寻址从器件,启动总线,产生时钟传送数据及结束数据的传送。接入I2C的器件都应有I2C总线接口,所有器件都通过总线寻址,且所有SDA/SCL同名端相连。按照I2C总线标准规范,总线传输中将所有的状态都生成相应的状态码,主器件能够依照这些状态码自动进行总线管理。
器件地址由7位组成,它与一位方向位共同构成了I2C总线器件的寻址字节(SLA)。寻址字节的格式如下:
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
器件地址 | 引脚地址 | 方向位 | |||||
DA3 | DA2 | DA1 | DA0 | A2 | A1 | A0 | R/W非 |
器件地址(DA3,DA2,DA1,DA1)是I2C总线外围器件的固有地址编码,器件出厂时就已经给定。引脚地址(A2,A1,A0)是由I2C总线外围器件引脚所指定的地址端口,根据其连接状态形成地址码。
I2C总线上数据传送和数据传送格式见在软件部分描述。这里先给出其过程流图,见下图。需要说明的是ATS52没有I2C接口,我们才送软件模拟的方法实现I2C接口。
图6 I2C总线数据传送过程
SPI(Serial Peripheral Interface,串行外围设备接口)是由Motorala公司提出的一种基于四线制的同步串行总线。SPI总线接口在速度要求不高,低功耗,需保存少量数据的应用场合中广泛应用。本设计中采用的是单主系统,它基于主从配置,它有四个信号线:
●MOSI:主器件数据输出,从器件数据输入。
●MISO:主器件数据输入,从器件数据输出。
●SCLK:时钟信号,有主器件产生。
●/SS:从器件使能信号,由主器件控制。
由它可方便的构成主-从分布式系统。单片机与外围器件连接时,SCK,MOSI,MISO均为同名端相连,/SS用做从属片选择端,扩展多个SPI外围器件时可通过相应I/O口分时选通外围器件。SPI系统可工作在全双工方式下,主SPI的时钟信号(SCK)使传输同步,移位寄存器的数据位在SCK下降沿从输出引脚(MOSI)输出;在SCK上升沿从输入引脚(MISO)接收的数据逐位移到移位寄存器,发送一个字节后,从另一个外围器件接收的字节数据进入移位寄存器中。同样,在软件设计中,将详细分析此实现过程。
2.1.2 关于ADS1110
单片机系统经常使用A/D转换器。虽然并行A/D转换器速度高、转换通道多,但其价格高,占用单片机接口资源比串行A/D转换器多。故在工业检测控制及智能化仪器仪表中经常采用串行A/D转换器。ADS1110是一种精密、可连续自校准的串行A/D转换器,带有差分输入和高达16位的分辨率,其串行接口为I2C总线。本设计中通过软件模拟I2C总线实现单片机ATS52与ADS1110之间的通信。
AD1110的特点是具有完整的数据采集系统。具有片内基准电压;片内可编程放大器PGA;片内振荡器;16位分辨率;可编程的转换速率可实现15次/秒-240次/秒;I2C总线接口;电源电压2.7V-5.5V。具体见下图: |
图7 ADS1110的内部结构 ADS1110的A/D转换器内核是由差分开关电容△—∑调节器和数字滤波器组成。调节器测量正模拟输入和负模拟输入的压差,并将其与基准电压相比较。数字滤波器接收高速流并输出代码,该代码是一个与输入电压成比例的数字,即A/D转换后的数据。ADS1110片内基准电压是2.048V,它只能采用内部电压基准该基准,不能测量,也不能用于外部电路。ADS1110的内部集成时钟振荡器用于驱动△—∑调节器和数字滤波器,输入阻抗在差分输入时为2.8M。ADS1110为I2C总线设备,由前面关于I2C总线接口知识可知,两个I/O口最多可挂接8个设备,完全可满足在本设计中的要求。ADS1110的I2C地址为1001000,单片机通过此地址建立和它的联系。ADS1110的输出寄存器为A/D转换后的结果,配置寄存器用于设置其工作模式。 •ADS1110共有两个寄存器,Configuration Register和Output Register 。设计中Configuration Register采用默认配置:PGA=1,DATARATE=15SPS,SC=1。 •Output Register为两个字节,其值取决于以下公式: •Output Code= −1*Min Code *PGA *(Vin+-Vin-)/2.048v •注:15sps下,Min Code=-32768. 单片机对ADS1110的读写过程见下图: |
图8 ADS1110的读写过程
同样,更多内容将在软件部分介绍。
2.1.3 关于ISD2560
ISD系列语音芯片具有其独特的优势,它采用模拟数据在半导体存储器中直接存储的专利技术,将模拟语音数据直接存储在单个存储单元,不需经过A/D或D/A转换,较好的真实再现语音的自然效果,避免了一般固体语音因为量化和压缩所造成的量化噪声和失真现象。另外,芯片功能强大,即录即放、语音可掉电保存、10万次的擦写寿命、手动操作和CPU控制兼容、可多片级联、无需开发系统等优点给单片机应用设计人员提供了单片的解决方案。
本设计中要是使用的ISD2560特征是:语音长度60S,采样频率8KHZ,工作电压4.5V-5.5V,工作电流30mA。它内部包括了前置放大器、内部时钟、定时器、采样时钟、滤波器、自动增益控制、逻辑控制、模拟收发器、解码器和480KB的EEPROM。在实际验证时用其替代产品ISD1760代替。具体请参阅下图:
图9 ISD2560的内部电路
ISD语音芯片完全可以手动完成语音任务,使用单片机更加智能化的完成一些功能,例如自动播音,最大程度减轻人的劳动强度。同样,具体内容在软硬件设计时具体论述。
2.2 硬件电路设计
2.2.1概述
传统的酒精报警器电路设计有的太简单,这虽减少了生产成本但却达不到精确地测定酒精浓度的目的,而且需要人手工操作,一些场合下,给人们带来了一些繁重的得操作;有的设计没有语音报警系统,在一些需要语音报警的场合和需要直观显示酒精浓度甚至记录需要记录报警的场合,传统的电路设计已达不到人们的需要。在本设计中,LCD及模数转换芯片ADS1110和语音芯片ISD2560的应用,完美解决了上述问题。
下面是硬件电路的原理框图:
图10 原理框图
2.2.2电路设计原理总图与PCB设计
总的电路原理图设计如下,各子模块将分别介绍。
图11 总电路原理图
使用PROTEL设计的已覆铜的PCB板如下图:
图12 PCB成品板
2.2.3电源与处理器子系统
需要说明的是在本系统设计中,需要使用ATS52的SPI总线接口用来作为程序下载接口,使用专门的软件通过ISP方式就可将程序轻松地下载至FLASH,不需将单片机反复拿下使用编程器烧录,即节省了时间有减少了成本,在软件设计中,将会论述到。在本设计中使用的USB-ISP编程软件如下图:
图13 USB-ISP编程软件
在此设计中,USB接口作为供电电源,具体电路见下图,需要注意的是SPI的接口定义,这已在串行通信相关部分论述过,另外,设计中,只要正常供电红色发光二极管就会被点亮。
图14 电源子系统
下图为晶振和复位子系统。要说的是复位子系统,此系统能实现上电复位和按键复位。注意TN4148在此处的作用,这也是此系统改进的一个地方,即能够降低复位引脚的对地阻抗,可以显著增强单片机复位电路的抗干扰能力;二极管可以实现快速释放电容电量的功能,满足短时间复位的要求。
图15 晶振与复位子系统
2.2.3酒精检测子系统
酒精检测子系统如下图:
图16 酒精检测子系统
在第一章中已说明过酒精传感器的测试电路,在电路正确使用前必须加电预热一段时间(使酒精传感器敏感化),在是由传感器本身的特点决定的,常用的传感器的加热电压在3-5V左右,MQ-3工作电压为+5v,TP-3C为+3v为了兼容使用酒精气敏传感器MQ-3及调节传感器端电压设置了可调电阻RV3。TP-3C与20k的RV2并接,调节RV2可设置想要设置的报警酒精浓度,相应算法在软件部分实现。
前面已讲过,酒精传感器的原理是二氧化锡吸附乙醇气体而本身体电阻发生变化,我们通过检测因传感器本身的电阻变化而带来的端电压变化来检测空气中的浓度。在此设计中,采用ADS1110来进行采样。ADS1110在此类应用中非常合适,因为ADS1110具有片内参考电压,通过比较传感器端与基准参考电压得到的一个值存在输出寄存器接口中(ADS1110为计数型的反馈比较型的ADC),单片机通过I2C接口与ADS1110建立联系后取走数据,单片机通过读OUTPUT寄存器与软件设置的寄存器进行比较,而决定是不是要语音报警。不得不说,可调电阻RV2在此处的作用是校正酒精传感器精确度。I2C总线设备与单片机连接时,必须接上拉电阻,因为ADS1110的SCL和SDA是漏极开路的。
有必要对ADS1110做些更详尽的说明,本设计中使用的ADS1110的PACKET MARKING为ED0,I2C总线地址为1001000,对于输出代码的大小值,是根据两个模拟输入的差值决定的一串码值,输出码值的方法具体如下:
输出码值=-1*最小码*PGA*((Vin+)-(Vin-))/2.048V
其中PGA的大小可有编程实现改变其大小,可为1、2、4、8;最小码的值可由下表得出:
表4 最大与最小码值
本设计使用默认配置设置,PGA=1,DATARATE=15SPS,SC=1。
2.2.4键盘与LCD显示子系统
对于键盘,我们使用非编码矩阵键盘,对于键盘我们采用软件去抖,这将在软件部分介绍。下图为用到的矩阵键盘:
图17 矩阵键盘子系统
LCD子系统相对复杂,但使用了液晶显示模块(LCM),使用变得简单的多,它已自带驱动与字符集,可与单片机直接接口。它能显示16*2个字符,在本设计中已足够满足要求。下图为LCM子系统接口电路:
图18 LCD子系统
需要说明的是LCD子系统和下面要论述的语音报警系统共用P0口(与实验验证时不同),我们通过使用8-D锁存器74LS573实现八路二选一功能,要注意,在本设计中加入了时钟模块,LCD显示时,要求时钟显示,具体见总原理图。
2.2.5语音报警子系统
语音报警系统的核心器件为ISD2560,已在以前讲述过,我们可以采用4KHZ,5.3KHZ,6.4KHZ和8KHZ进行录音,当然录音频率越高,存储的内容越少,音质越高;对此模块,难的是软件设计,硬件电路我们参考相关DATA-SHEET就已经足够了。
图19 语音报警子系
第三章 软件设计
3.1 概述
在单片机应用系统开发中,应用程序设计是整个系统设计的主要工作。过去,单片机应用程序设计都采用汇编语言,它能直接操纵硬件,能编写出高质量的代码。然而,使用汇编语言编写复杂的数值计算却非常困难,可移植性和维护都远不如高级语言。如今,利用C语言开发单片机应用程序是主流趋势。
用C语言编写的程序必须经过单片机C语言编译器(简称C51)转换成单片机可执行的代码程序。我们使用KEIL公司集成开发环境(IDE)自带的C51编译器。KEIL C51对标准C进行了扩展,在此不做介绍,本设计中不使用专用的操作系统RTX51。需要注意的是,对于特出功能寄存器(SFR),C51提供了sfr,sfr16,sbit等几个与标准C语言不兼容的关键字来自主定义;对于扩展I/O地址要使用#include 3.2 软件设计流程图 因为未使用操作系统,主程序应设计为大循环的形式。注意酒精传感器在使用前要预热,在此不考虑与软件设计无关的一些问题。软件流程图如下: Y Y N Y N N Y 图20 程序流程图 整个程序的结构如下(在KEIL uVision4中): 图21 程序结构 子函数模块都定义为头文件的形式,主函数将他们包含进去。代码实现将在附录中给出。 3.3代码实现与实验验证 代码实现见附录,下面是实验验证。 未接酒精传感器模块和语音报警模块效果图(开机画面): 图22 开机画面 未接酒精传感器模块和语音报警模块效果图(设置好时间): 图23 调整好时间 接入酒精传感器模块和语音报警模块效果图: 图24 报警后的LCD显示 开发板和酒精传感器及语音报警部分的连接: 图25 模块连接 结束语 本设计是基于微控制器ATS52的便携式酒精报警器。与传统的简单基于微控制器的设计不同,本设计中采用的语音报警并使用高亮LCD显示酒精浓度结果和实时时钟,特别是采用模数转换器ADS1110能精确转换酒精传感器采集到的数据。本设计可进一步优化。 使用ATS52可使用SPI接口在线升级软件,无需将其从电路板上拿下,节省了时间与成本。 本设计中,有许多不足之处,有些可以进一步完善。 不足之处: ●键盘扫描程序不能防连击。 ●器件ADS1110,价格较贵,可以使用其它方式代替以减少成本。 ●可以使用自带SPI及I2C总线的微控制器/微处理器,避免用软件实现的方式进行模拟,简化程序设计。 ●因酒精传感器需要一定时间检测气体浓度且程序规模较小,使用操作系统不太合适,但为了以后进一步优化设计、增加特设功能(如:实时记录报警时的时间和酒精浓度)、简化代码复杂度使用操作系统也有必要。 总的来说,学到了不少东西。例如ISD系列芯片是SPI总线器件,怎样使用SPI总线器件;ADS1110是I2C总线器件,使用它们对这两种总线应很熟悉(使用软件模拟);另外还有向LCD写数据显示的方法。 致 谢 在本毕业论文设计过程中,始终得到了张保平老师的悉心指导,并在相关特定方面进行了探讨。张保平老师工作认真负责,治学严谨,对老师能在紧张的教学节奏里抽出宝贵时间来进行指导表示诚挚的谢意;同时对在本设计过程中提供过帮助的同学、朋友表示感谢。另外,感谢养育和培育我多年的父母并致以崇高的敬意。 参 考 文 献 1 普通图书 [1]单片机原理及应用(第二版) 李建忠 主编.西安:西安电子科技大学出版社,2008. [2]单片机基础(修订本) 李广弟 朱月秀 王秀山 编著.北京:北京航空航天大学出版社2001. [3]51单片机C语言常用模块与综合系统设计实例精讲(第二版)于勇等编 北京:电子工业出版社2008. [4]嵌入式系统 张大波 编著 北京:电子工业出版设 2008 [5]传感器技术大全(中册) 张洪润 主编 北京:北京航空航天大学出版社 2007. [6]电子技术基础 模拟部分(第五版)康华光 主编 北京:高等教育出版社 2006. [7]数字电子技术基础(第五版)阎石 主编 北京:高等教育出版社 2006. 2 期刊中析出的文献 [1]气敏传感器及其应用 尤克,倪景秀 仪表技术与传感器 2007年第7期 [2]一种便携式TiO2薄膜乙醇气敏传感器的研制 何平,赵红东,潘国峰 传感技术学报 2007年7月第7期 3电子文献 [1] ISD2560_Datasheet www.21ic.com [2] ISD1760_Datasheet www.21ic.com [3] AtS52 _Datasheet www.21ic.com [4] Ads1110_Datasheet www.21ic.com 附录 代码实现 主函数:FirstPMAIN.C /************************************************ 不使用RTX51Tiny OS 使用ATS52 Vesion:1.0 designed by GDB *************************************************/ #include #include #include #include #include #include #include #define uchar unsigned char #define uint unsigned int void delay(uint x) { uint y=100; for(x;x<0;x--) for(y;y<0;y--); } init() { lcd_handle(); //开机画面 realtime(); //设置时间,实时时钟 } void main() { init(); while(1) { ads_det(); }; } LCD初始化部分子函数:lcd_handle.h #include #include sbit LcdRs = P3^5; sbit LcdRw = P3^7; sbit LcdEn = P3^4; sbit dula = P2^6; sbit wela = P2^7; #define LCD_COMMAND 0 // Command #define LCD_DATA 1 // Data #define uchar unsigned char #define uint unsigned int uchar code table0[]="Alcoholalarm!!!"; //开机画面 uchar code table1[]="2010-05-27 4 "; //初始值 uchar code table2[]="00:00:00"; uchar code table3[]="c:000ppm"; uchar ads_bai,ads_shi,ads_ge; Lcd_delay1(uint x) { uint y=100; for(y;y!=0;y--) for(x;x!=0;x--); } Lcd_delay2(uint z) { for(z;z!=0;z--) Lcd_delay1(1000); } void LCD_Write(bit style, uchar input) //向LCD写数据或命令 { LcdRs=style; LcdRw=0; P0=input; Lcd_delay1(10); LcdEn=1; Lcd_delay1(10); LcdEn=0; } void LCD_Init() //开机画面,初始化 { uchar num; dula=0; wela=0; LcdEn=0; LCD_Write(LCD_COMMAND,0x38); //8位数据端口,2行显示,5*7点阵 LCD_Write(LCD_COMMAND,0x0c); LCD_Write(LCD_COMMAND,0x06); LCD_Write(LCD_COMMAND,0x80); //写第一行 for(num=0;num<15;num++) { LCD_Write(LCD_DATA,table0[num]); Lcd_delay1(5); }; Lcd_delay2(3000); //延时 LCD_Write(LCD_COMMAND,0x80); //写第一行 for(num=0;num<15;num++) { LCD_Write(LCD_DATA,table1[num]); Lcd_delay1(5); }; LCD_Write(LCD_COMMAND,0x80+0x40); //写第二行 for(num=0;num<8;num++) { LCD_Write(LCD_DATA,table2[num]); Lcd_delay1(5); }; LCD_Write(LCD_COMMAND,0x80+0x40+0x08); for(num=0;num<8;num++) { LCD_Write(LCD_DATA,table3[num]); Lcd_delay1(5); }; } void lcd_handle() { LCD_Init(); } 实时时钟子模块:realtime.h #include #include #define uint unsigned int #define uchar unsigned char //sbit voice=P2^3; sbit K1=P3^0; sbit K2=P3^1; sbit K3=P3^2; sbit rd=P3^7; uchar count,s1num,shi,ge; char sec,min,hour; void delay_realtime(uint z) { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } void init_realtime() { TMOD=0x01; TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; //开中断 ET0=1; TR0=1; //定时开始 } void write_time(uchar addr,uchar date) //写入调整后的时间数据 { shi=date/10; //分离数据十位和个位 ge=date%10; LCD_Write(LCD_COMMAND,0x80+0x40+addr); LCD_Write(LCD_DATA,0x30+shi); //写数据,0在字符表的位置为0x30 LCD_Write(LCD_DATA,0x30+ge); } void keyscan() //简单的键盘扫描,去抖,识别 { rd=0; if(K1==0) //进入调整模式 { delay_realtime(5); if(K1==0) //确定写入数据位置 { s1num++; while(!K1); if(s1num==1) { TR0=0; LCD_Write(LCD_COMMAND,0x80+0x40);//首先调整时,在第二行第一位 LCD_Write(LCD_COMMAND,0x0f); } if(s1num==2) { LCD_Write(LCD_COMMAND,0x80+0x40+0x03); } if(s1num==3) { LCD_Write(LCD_COMMAND,0x80+0x40+0x06); } if(s1num==4) { s1num=0; LCD_Write(LCD_COMMAND,0x0c); TR0=1; } } } if(s1num!=0) //时间调整算法 { if(K2==0) //加法调整 { delay_realtime(5); if(K2==0) { while(!K2); //松手检测 if(s1num==1) //调整时,加 { hour++; if(hour==24) hour=0; write_time(0,hour); //格式为:地址、数据 LCD_Write(LCD_COMMAND,0x80+0x40); } if(s1num==2) //调整分,加 { min++; if(min==60) min=0; write_time(3,min); LCD_Write(LCD_COMMAND,0x80+0x40+3); } if(s1num==3) //调整秒,加 { sec++; if(sec==60) sec=0; write_time(6,sec); LCD_Write(LCD_COMMAND,0x80+0x40+6); } } } if(K3==0) //减法调整 { delay_realtime(5); if(K3==0) { while(!K3); if(s1num==1) //调整时,减 { hour--; if(hour==-1) sec=23; write_time(0,hour); LCD_Write(LCD_COMMAND,0x80+0x40); } if(s1num==2) //调整分,减 { min--; if(min==-1) min=59; write_time(3,min); LCD_Write(LCD_COMMAND,0x80+0x40+3); } if(s1num==3) //调整秒,减 { sec--; if(sec==-1) sec=59; write_time(6,sec); LCD_Write(LCD_COMMAND,0x80+0x40+6); } } } } } void timer0() interrupt 1 //定时中断处理程序 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; count++; if(count==18) { count=0; sec++; if(sec==60) { sec=0; min++; if(min==60) { min=0; hour++; if(hour==24) { hour=0; } write_time(0,hour); } write_time(3,min); } write_time(6,sec); } } void realtime() { dula=0; wela=0; init_realtime(); while(K1) { keyscan(); }; } 酒精检测子模块:ads_det.h #include #define ADS1110_SDA P3_0 #define ADS1110_SCL P3_1 #define uchar unsigned char #define uint unsigned int sbit ADS1110_SCL = P3^0; sbit ADS1110_SDA = P3^1; uchar TMR1H, TMR1L,TMR2; uchar ads_count,value; delay_nus(uint n) //延时Nus { uint i=0; for (i=0;i } void delay_1ms(void) //延时1ms { uint i; for (i=0;i<1000;i++); } void delay_nms(unsigned int n) //延时Nms { unsigned int i=0; for (i=0;i } void IIC_Start(void)//启动I2C总线 { ADS1110_SDA = 1; delay_nus(5); ADS1110_SCL = 1; delay_nus(5); ADS1110_SDA = 0; delay_nus(5); } void IIC_Stop(void)//停止I2C总线 { ADS1110_SDA = 0; delay_nus(5); ADS1110_SCL = 1; delay_nus(5); ADS1110_SDA = 1; delay_nus(5); } unsigned char IIC_Ask(bit i) //应答信号 { bit ask; if (i) ADS1110_SDA = 1; else ADS1110_SDA = 0; ADS1110_SCL = 1; ask = ADS1110_SDA; ADS1110_SCL = 0; return ask; } void SendByte(unsigned char DATA) //发送字节数据 { unsigned char i; for(i=8;i!=0;i--) { ADS1110_SDA=(DATA&0x80);//0x80=1000 0000 DATARATE为15SPS ADS1110_SCL=1; DATA<<=1; ADS1110_SCL=0; } } unsigned char ReadByte(void) //读字节数据 { unsigned char i,DATA; DATA = 0; ADS1110_SDA = 1; for(i=8;i!=0;i--) { ADS1110_SCL = 1; ADS1110_SCL = 1; DATA <<= 1; if(ADS1110_SDA) { DATA++; } ADS1110_SCL = 0; } return DATA; } void ADS110_Write(unsigned char Outdata)//写操作 { IIC_Start(); //启动I2C总线 SendByte(0x90); // ED0的I2C总线地址为90H while(IIC_Ask(1)&&(delay_nus(20))); //等待应答信号 SendByte(Outdata); while(IIC_Ask(1)&&(delay_nus(20))); //等待应答信号 IIC_Stop(); //停止I2C总线 } void ADS110_Read(void)//读操作 { IIC_Start(); SendByte(0x91); while(IIC_Ask(0)); //等待应答 TMR1H=ReadByte(); // 读高8位 IIC_Ask(0); //连续读 TMR1L=ReadByte(); //读低8位 IIC_Ask(0); //连续读 TMR2=ReadByte(); //读配置字 IIC_Ask(1); //停止读 IIC_Stop(); } void write_ads_date(uchar addr,uchar date) //写入调整后的时间数据 { ads_bai=date/100; //分离数据百位,十位和个位 ads_shi=date%100/10; ads_ge=date%10; LCD_Write(LCD_COMMAND,0x80+0x40+addr); //写数据,0在字符表的位置为0x30 LCD_Write(LCD_DATA,0x30+ads_bai); LCD_Write(LCD_DATA,0x30+ads_shi); LCD_Write(LCD_DATA,0x30+ads_ge); } ADS110_Valuehandle() //处理从ADS1110读出的数值,决定语音报警 { value=(0x9c-TMR2)/(0x9c-0x2e); write_ads_date(10,value); if(TMR2<0x9c) //独处的值取决于以下公式: { //Vaule=32768*(V+)/2.048v(v+初始值为2.5v) //alarm(); //Vaule初始值为40000(0x9c40) } TR1=1; //LCD显示数据后,再次开始定时 } ads_init() { TMOD=0x10; TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; //开中断 ET1=1; TR1=1; //定时开始 } void timer1() interrupt 3 //定时中断处理程序 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; ads_count++; if(ads_count==36) { TR1=0; //停止定时,读ADS1110数据,处理 ADS110_Read(); ADS110_Valuehandle(); }; } void ads_det() { ads_init(); //初始化 ADS110_Write(0x8c);//ADS1110写操作 } 语音报警子模块:alarm.h #include #include #define uchar unsigned char #define uint unsigned int #define PU 0X11 //SPI模式指令集 #define STOP 0x12 #define RESET 0x03 #define CLI_INT 0X04 #define RD_STATUS 0X15 #define RD_PLAY_PTR 0X06 #define RD 0X17 #define RD_REC_PTR 0X08 #define RD_DEVID 0X19 #define G_ERASE 0X43 #define RD_APC 0X44 #define WR_APC1 0X45 #define WR_APC2 0X65 #define WR_NVCFG 0X46 #define CHK_MEM 0X49 #define SET_PLAY 0X80 #define SET_REC 0X81 #define SET_EARSE 0X82 sbit SS=P1^0; sbit SCLK=P1^1; sbit MOSI=P1^2; sbit MISO=P1^3; uchar SR0_L; uchar SR0_H; uchar SR1; uchar PORTB; uchar PD; int delay_100nus(int x) { int a,b; for(a=0;a } int delay_nms(int x) { int a,b; for(a=0;a } uchar ISD_SendData(uchar BUF_ISD)//模拟SPI模式传送一个数据 { uchar i,dat=BUF_ISD; SCLK=1; SS=0; for(i=0;i<8;i++) { SCLK=0; delay_100nus(1); if(dat&0x01) { MOSI=1; } else { MOSI=0; } dat>>=1; if(MISO) { dat|=0x80; } SCLK=1; delay_100nus(1); } MOSI=0; return(dat); } void ISD_ERASE(void) //擦除当前段 { ISD_SendData(0x52); ISD_SendData(0x00); SS=1; delay_nms(10); } void ISD_FWD(void) //快进 { ISD_SendData(0x58); ISD_SendData(0x00); SS=1; delay_nms(10); } void ISD_STOP(void) //停止当前操作 { ISD_SendData(STOP); ISD_SendData(0X00); SS=1; delay_nms(20); } void RdAPC(void) //读取APC { uchar APCH,APCL; ISD_SendData(RD_APC); ISD_SendData(0X00); ISD_SendData(0X00); ISD_SendData(0X00); SS=1; delay_nms(10); SR0_L=ISD_SendData(RD_APC); SR0_H=ISD_SendData(0X00); APCL=ISD_SendData(0X00); APCH=ISD_SendData(0X00); SS=1; delay_nms(10); } uchar RD_DevID(void) //读取ID { uchar ID; ISD_SendData(RD_DEVID); ISD_SendData(0X00); ISD_SendData(0X00); SS=1; delay_nms(10); SR0_L=ISD_SendData(RD_DEVID); SR0_H=ISD_SendData(0x00); ID=ISD_SendData(0x00); SS=1; delay_nms(10); return(ID); } void ISD_Reset(void) //复位 { ISD_SendData(0x13); ISD_SendData(0x00); SS=1; delay_nms(50); } void ISD_PLAY(void) //播放当前段 { ISD_SendData(0x40); ISD_SendData(0x00); PORTB=0X03; delay_nms(10); } void ISD_PU(void) { ISD_SendData(0x11); ISD_SendData(0x00); SS=1; delay_nms(50); } void ISD_PD(void) //掉电 { ISD_SendData(PD); ISD_SendData(0x00); SS=1; } void ISD_WR_NVCFG(void) //永久写入寄存器 { ISD_SendData(WR_NVCFG); ISD_SendData(0x00); SS=1; delay_nms(10); } void RdStatus(void) { ISD_SendData(RD_STATUS); ISD_SendData(0x00); ISD_SendData(0x00); SS=1; delay_nms(10); SR0_L=ISD_SendData(RD_STATUS); SR0_H=ISD_SendData(0X00); SR1=ISD_SendData(0X00); SS=1; delay_nms(10); } void ClrInt(void) //清除中断 { ISD_SendData(CLI_INT); ISD_SendData(0x00); SS=1; delay_nms(10); } void ISD_WR_APC2(void) //写APC { ISD_SendData(0x75); ISD_SendData(0x47); ISD_SendData(0x04); SS=1; delay_nms(20); ISD_WR_NVCFG(); } void ISD_init(void) //初始化 { ISD_Reset(); do{ ISD_PU(); RdStatus(); } while((SR0_L&0X01)||(!(SR1&0X01))); RD_DevID(); ClrInt(); ISD_WR_APC2(); RdAPC(); } void SetREC(void) //定点录音 { uchar Add_ST_H,Add_ST_L,Add_ED_H,Add_ED_L; do{ RdStatus(); } while((SR0_L&0X01)||(!(SR1&0X01))); ClrInt(); Add_ST_L=0x00; Add_ED_L=0xFF; Add_ST_H=0x00; Add_ED_H=0x00; ISD_SendData(0x91); ISD_SendData(Add_ST_L); //S7:S0开始地址 ISD_SendData(Add_ST_H); //S10:S8 ISD_SendData(Add_ED_L); //E7:E0 结束地址 ISD_SendData(Add_ED_H); //E10:E8 ISD_SendData(0x00); SS=1; delay_nms(10); do{ RdStatus(); } while((SR0_L&0X01)||(SR1&0X04)); } void ISD_RD_PLAY_PTR(void) //读取状态寄存器,当前地址和播放录音 { uchar APCH,APCL; ISD_SendData(0x16); ISD_SendData(0x00); ISD_SendData(0x00); ISD_SendData(0x00); SS=1; } void alarm() //使用P10-P13模拟SPI接口/c/c. { ISD_PU(); delay_nms(50); //ISD_STOP(); //ISD_REC(); //delay_nms(10000); ISD_PLAY(); delay_nms(3000); while(1); }