` 本帖最后由 吴鉴鹰 于 2014-5-19 14:36 编辑
吴鉴鹰单片机项目详细解析系列连载之基于单片机的便携燃气报警器设计 QQ群号:332164870 欢迎大家加入,交流,沟通,咨询!
今天来讲第三个项目,有兄弟说我的帖子内容不够细,今后一定做到模块化,硬件软件模块化,对软硬件难点、重点内容作详细解析。 一提到燃报警器,鹰哥我就有一种独特的情绪,因为这是我的毕业设计做的实物,遥想要想当年毕业答辩那会,直接拿着实物去答辩,省去了不少口舌,事实胜于雄辩嘛,老师也不多问,直接让我演示了一下,结果效果还不错,得到了老师的肯定,说做的不错,还问我有没有意向推广从那以后我对 电子的热情更加强了。 这是鹰哥当时做的实物图片:
这也是鹰哥自己一手负责软件、硬件做成的一个完整的产品,从原理图到 PCB板,再到程序,都是鹰哥一人搞定的,现在想想那感觉还是很好的,虽然很辛苦,但是乐在其中。
鹰哥的这篇文章主要论述了一种便携式瓦斯检测仪的工作原理,并给出了系统硬件原理 电路设计与软件系统流程。该系统主要由 电源模块、催化燃烧式气体传感器模块、单片机控制模块、键盘、LED显示器和声光报警模块等部分构成。该装置将单片机的适时控制及数据处理功能与传感技术相结合,不仅可精确检测井下空气中的瓦斯浓度,还能根据瓦斯浓度的大小及时发出声光报警信号,而且可通过数码管显示装置显示出瓦斯的浓度。
有需要硬件原理图的请点击下载:
首先来介绍硬件部分
一、介绍最前端的传感器 这个项目采用MJC4/3.0L瓦斯检测传感器,当时在网上买了一个,价格在40块钱左右,虽然价钱不是太高,可是对那时的鹰哥来说也不是一笔小钱,能买10瓶营养快线,当时鹰哥一咬牙就买了一个回来。 MJC4/3.0L原理图
刚买回来,也没有说明书,就到网上到处找,打电话问客服,也说不是做技术的,后来又联系工程师,才终于弄明白个大概。
传感器原理:
载体催化 元件一般由一个带催化剂的敏感元件(俗称黑元件)和一个不带催化剂的补偿元件(俗称白元件)构成。其检测电路如图所示。黑元件是载体催化燃烧式元件,当甲烷气体在元件表面与氧气产生无焰燃烧时,电桥失去平衡,输出一个电压信号,白元件是补偿元件,基本结构和技术参数与黑元件相同,但表面不涂镀催化剂,所以它不掺入低温燃烧。但它处在与黑元件相同的工作环境中,所以对非甲烷浓度变化引起的催化元件阻值变化起补偿作用,从而提高仪器零点稳定性和抗干扰能力。
二、由OP07组成的双运放高共模抑制比放大电路 OP07介绍:放大器OP07参数及引脚说明,大家只需要知道芯片怎么用就行了,没有必要详细研究,毕竟咱们不学搞微电子的 OP07参数如下:
1、低的输入噪声电压幅度:0.35μVP-P (0.1Hz -10Hz) 2、极低的输入失调电压:10μV 3、极低的输入失调电压温漂:0.2μV/ ℃ 4、具有长期的稳定性:0.2μV/MO 5、低的输入偏置电流:±1nA 6、高的共模抑制比:126dB 7、宽的共模输入电压范围:±14V 8、宽的电源电压范围:±3V-±22V 1和8脚为偏置平衡(调零端),2脚为反向输入端,3脚为正向输入端,5脚为空脚 ,6脚为输出端,7和4分别接电源的正负极。
电路分析计算: 由电路可得: Uo1 = (1 + R4/R3)×Ui (1) (Uo1 - Ui2)/R5 = (Ui2 - Uo)/R6 (2)
所以由式(1)和式(2)得: Uo = ((1 + R6/R5)×Ui2 - (1 + R4/R3)×R6Ui)/R5 (3) 又因为输入共模电压Uic = (Ui1 + Ui2)/2,输入差模电压Uid = Ui1 - Ui2,将它们带入式(3)可得: Uo =( (1 - R4R6/R3R5)×Uic + 1 )/(2(1+2×R6/R5+R4R6/R3R5)×Uid) (4)
为了获得零共模增益,式(3-4)右边第一项必须为零,可取此时,电路得差动闭环增益为: 因为从传感器输出得电压信号为0-90mv,为了取得较大的A/D转换器输入信号,需要对其放大,取R3=R6=5000Ω,R4= R5=100Ω,放大倍数为51倍。
感兴趣的读者可以自己推导一下,这也是有的公司笔试喜欢考的题目类型,其实模电研究一下也挺有意思的。鹰哥当时自己可是认认真真的推导过得的哦。
三、AD采样模块的简单介绍
A/D转换模块设计
在检测系统中,传感器将采集到的信号(例如:我们这个项目中,信号就是瓦斯的浓度),这些信号都是模拟信号,将这些模拟信号进行处理,放大、滤波、补偿、以及信号的变换等方式,然后在案通过某些芯片将模拟信号变成数字信号,这里我们用ADC0809将传感器转化的电压进行数字化,然后再传递给MCU,也就是51单片机。一定会有人问我,为什么采用这么低档次的51单片机,精确度如此低的ADC0809,鹰哥告诉你,一个好的项目并不是采用的芯片越高档就越好,而且对于初学者,档次越低的芯片,学到的东西越多,所以大家不要嫌弃51单片机低级,也不要嫌弃ADC0809芯片不够先进。 相信鹰哥,只要你能把ADC0809用好,别的AD转换芯片也没有问题。 鹰哥来给大家简单介绍一下ADC0809芯片。 ADC0809芯片简单介绍 当时鹰哥采用的是ADC0809是采样分辨率为8位的、以逐次逼近原理进行模—数转换的器件,。其内部有一个8通道多路开关,它可以根据地址码锁存译码后的信号,只选通8路模拟输入信号中的一个进行A/D转换。芯片的引脚图如图所示: 1、ADC0809芯片有28条引脚,采用双列直插式封装,如图所示。下面说明各引脚功能。 (1) IN0-IN7:8路模拟量输入端; (2) D0-D7:8位数字量输出端; (3) ADD-A、ADD-B、ADD-C:3位地址输入线,用于选通8路模拟输入中的一路; (4) ALE:地址锁存允许信号输入端,高电平有效; (5) START:A/D转换启动脉冲输入端,输入一个正脉冲(至少100ns宽)使其启动(脉冲上升沿使ADC0809复位,下降沿启动A/D转换); (6) EOC:A/D转换结束信号,输出,当A/D转换结束时,此端输出一个高电平(转换期间一直为低电平); (7) OE:数据输出允许信号,输入,高电平有效。当A/D转换结束时,此端输入一个高电平,才能打开输出三态门,输出数字量; (8) CLK:时钟脉冲输入端。要求时钟频率不高于640KHZ; (9) REF(+)、REF(-):基准电压; (10) Vcc:电源,单一+5V; (11) GND:接地。 2、ADC0809的主要特性有: (1) 8路输入通道,8位A/D转换器,即分辨率为8位; (2) 具有转换起停控制端; (3) 转换时间为100μs; (4) 单个+5V电源供电; (5) 模拟输入电压范围0~+5V,不需零点和满刻度校准; (6) 工作温度范围为-40~+85摄氏度; (7) 低功耗,约15mW。 3、ADC0809的工作过程: 首先输入3位地址,并使ALE=1,将地址存入地址锁存器中。此地址经译码选通8路模拟输入之一到比较器。START上升沿将逐次逼近寄存器复位。下降沿启动 A/D转换,之后EOC输出信号变低,指示转换正在进行。直到A/D转换完成,EOC变为高电平,指示A/D转换结束,结果数据已存入锁存器,这个信号可用作中断申请。当OE输入高电平时,输出三态门打开,转换结果的数字量输出到数据总线上。
对于AD采样的模块就到这了,后面内容更加精彩。
四、数码管显示电路设计 1、显示器结构与原理以及显示方式 LED显示块是由发光二极管显示字段的显示器件,也称为数码管。在单片机中通常使用的是7段LED。这种显示块有共阳极和共阴极两种,共阳极LED显示块的发光二极管阳极并联(系统中接驱动电源)。当某个发光二极管阴极为低电平时,发光二极管点亮;共阴极LED显示块的发光二极管阴极共地。当某个发光二极管的阳极为高电平时,发光二极管点亮。通常使用的7段显示块有8个二极管组成,故也称8段显示器。其中7个发光二极管构成7笔字型“8”;一个发光二极管构成小数点“.”。7段发光二极管,再加上一个小数点,共计8段,这样提供给LED显示器的字形数据正好一个字节。LED显示块与单片机接口非常容易,只要将一个8位并行输出口与显示块的发光二极管引脚相连即可。8位并行输出口输出不同的字节数据可显示不同的数字或字符。7段LED显示器如图所示。
7段LED显示器图
4位LED显示器有4根位选线和32根段选线。根据显示方式不同,位选线与段选线的连接方法不同。段选线控制要显示什么样的字符,而位选线则控制要在哪—位上显示这个字符。LED显示有静态显示和动态显示两种方式。本设计采用4位LED动态显示方式。 4位LED动态显示电路只需要两个4位I/O口。其中一个控制段选码,另一个控制位选。由于所有位的段选码皆由一个4位I/O口控制,因此,在每个瞬间,4位LED可能显示相同的字符。要想每位显示不同的字符,必须采用扫描显示方式,即在每一瞬间只使某一位显示相应字符。在此瞬间,段选码由控制I/O口输出相应字符电平,位选I/O口输出位选码(共阴极送低电平、阳极送高电平)以保证位显示相应字符。如此轮流,使每位显示该位应显示字符,并延时一段时间,以造成视觉暂留效果。
2 显示驱动器MAX7219 MAX7219是串行输入/输出共阴极显示驱动器。它采用了3线串行接口,传送速率达到10M数据,能驱动8位七段数字型的LED或条形显示器或64只独立的 LED。MAX7219内置BCD码译码器、多路扫描电路、段和数字驱动器和存储每一位的8×8静态RAM。能方便的用模拟或数字方法控制段电流的大小,改变显示器的亮度;能对每一位进行译码或不译码控制;能选择驱动显示器的数量;能进入低功耗的关断模式(仅消耗150uA电流,数据保留);而且其最重要的一点是,每个显示位都能个别寻址和刷新,而不需要重写其他的显示位,这使得软件编程十分简单且灵活。 MAX7219的引脚排列如图所示。 图 3-14 MAX7219引脚图
MAX7219采用串行寻址方式,在传送的串行数据中包含有内部RAM的地址。加在 DIN脚上的串行数据,必须在LOAD信号为高电平的前提下,以每两个字节为一个数据包在CLK信号上升沿移入16位的移位寄存器。然后在LOAD信号的上升沿锁存进数字或控制寄存器中。LOAD信号必须在第16个CLK信号上升沿的同时,或在下一个 CLK信号上升沿之前升高,否则,数据会丢失。DIN脚上的串行数据在16.5个CLK信号后出现在 DOUT脚上,以便级连应用时传到下一个MAX7219上。
MAX7219和单片机的接口电路 如图3-15所示为AT89C51单片机和MAX7219的连接图。用单片机AT89C51的P口中的P2.0、P2.1、P2.2,分别和MAX7219的DIN、CLK、LOAD相连,MAX7219的段输出A-DP,分别和共阴极数码管的A-DP相连。位驱动DIG0和数码管的地端COM1脚相连,位驱动DIG1和数码管的地端COM2脚相连,位驱动DIG2和数码管的地端COM3脚相连,位驱动DIG3和数码管的地端COM4脚相连。
五、电源模块的简单介绍 电源是一个好产品的心脏,所以只有电源做好了,才能做出好的产品,鹰哥来给大家简单阐述一下这个项目的电源。虽然鹰哥主攻软件,但是对硬件也是有点小研究的。
+3V稳压源的设计 +3V电压的实现需要LM317芯片。LM317作为输出电压可变的集成三端稳压块,是一种使用方便、应用广泛的集成稳压块。输出电压范围为1.2V到37V之间可调节;内部有热过载保护;不随温度变化的内部短路电流限制;输入至少要比输出高2V,否则不能调压;输出电流最好不超过1A;可调整输出电压可低到1.2V。 用LM317实现+3V稳压电源,提供传感器工作电压。其稳压示意图如图所示。
图3-18 LM317稳压示意图
+5V稳压源的设计 该稳压电源的设计需要芯片MAX756。MAX756是CMOS升压DC-DC开关小型,低输入电压或电池供电的系统监管芯片。MAX756可接受一个输入电压低至0.7V,并将其转换为更高3.3V或5V可选的输出电压。典型的全负荷的MAX756效率大于87%。 其关键特性是,输入电压为0.7V;20mA的效率为87%;静态电流为60µA;20µA的关闭模式;温度误差±1.5%;低电池最检波器 (LBI/LBO);8-Pin SO封装。用MAX756实现+5V的稳压电路如图所示。 图3-19 MAX756稳压电路图
-5V稳压源的设计 该稳压电源的设计采用芯片ICL7660来实现。LCL7660有8个引脚。其中的NC是空脚;CAP+是储能电容正极;CAP-是储能电容负极;LV是输入低电压控制端,输入电压低于3.5V时,该脚接地,输入电压高于5V时该脚必须悬空;OSC是作时钟输入端。采用ICL7660芯片实现-5V稳压电源的电路如图所示。
电源部分也讲解完了,现在要讲解鹰哥最擅长的软件部分了,不要走开,精彩马上到来。
六、程序设计
#include // 引用标准库的头文件
#include
#include
#define uchar unsigned char
#define uint unsigned int
***it DIN = P2^0; // 串行数据输入
***it CLK = P2^1; // 串行时钟
***it LOAD = P2^2; // 显示数据锁存控制
***it SOUND = P1^5; //用来进行蜂鸣器的报警
***it ST=P3^6; //启动信号
***it EOC=P3^3; //转换结束信号,连到外部中断1口,转换结束后进入外部中断
***it OE=P3^7; //输出使能
#define NoOp 0x00 // 空操作寄存器
#define Digit0 0x01 // 数码管1寄存器
#define Digit1 0x02 // 数码管2寄存器
#define Digit2 0x03 // 数码管3寄存器
#define Digit3 0x04 // 数码管4寄存器
#define Digit4 0x05 // 数码管5寄存器
#define Digit5 0x06 // 数码管6寄存器
#define Digit6 0x07 // 数码管7寄存器
#define Digit7 0x08 // 数码管8寄存器
#define DecodeMode 0x09 // 译码模式寄存器
#define Intensity 0x0a // 亮度寄存器
#define ScanLimit 0x0b // 扫描位数寄存器
#define ShutDown 0x0c // 低功耗模式寄存器
//#define DisplayTest 0x0f // 显示测试寄存器
#define ShutdownMode 0x00 // 低功耗方式
#define NormalOperation 0x01 // 正常操作方式
#define ScanDigit 0x07 // 扫描位数设置,显示8位数码管
#define DecodeDigit 0xff // 译码设置,8位均为BCD码
#define CommonDigit 0x00 // 译码设置,8位均为非译码方式
#define IntensityGrade 0x0a // 亮度级别设置
#define TestMode 0x01 // 显示测试模式
#define TextEnd 0x00 // 显示测试结束,恢复正常工作模式
float result; //这是AD采样采得的数据
uchar MAX,MIN; //这是报警极的最低值和不报警的最大值
uint f;
/*********************************************************************************************************
** 函数名称: SendChar
** 功能描述: 向MAX7219写入字节(8位)
** 输 入: ch :要送入的数据
**
**
** 输 出:
**
** 全局变量:
** 调用模块: 延时函数_nop_
**
** 作 者: 吴鉴鹰
** 日 期: 14.02.22
**-------------------------------------------------------------------------------------------------------
** 修改人:吴鉴鹰
** 日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
void SendChar(uchar ch)
{
uchar i,temp;
_nop_(); //短暂的延时
for (i=0;i<8;i++)
{
temp=ch&0x80;
ch=ch<<1;
if(temp) //当该位为1时写入
{
DIN=1;
CLK=0;
CLK=1;
}
else //当该位为0时写入
{
DIN=0;
CLK=0;
CLK=1;
}
}
}
/*********************************************************************************************************
** 函数名称: changestyle
** 功能描述: 译码功能,将需要显示的数字转成相应的七段译码表
如要显示的字符为“0”,则为7e ---0111 1111,就中间的一个LED不亮
** 输 入: p:要转换的数据
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 14.02.22
**-------------------------------------------------------------------------------------------------------
** 修改人:吴鉴鹰
** 日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
uchar changestyle(uint p)
{
uchar c;
if(p==0) c = 0x7e;
if(p==1) c = 0x30;
if(p==2) c = 0x6d;
if(p==3) c = 0x79;
if(p==4) c = 0x33;
if(p==5) c = 0x5b;
if(p==6) c = 0x5f;
if(p==7) c = 0x70;
if(p==8) c = 0x7f;
if(p==9) c = 0x7b;
return(c);
}
/*********************************************************************************************************
** 函数名称: delayms
** 功能描述: 译码功能,将需要显示的数字转成相应的七段译码表
如要显示的字符为“0”,则为7e ---0111 1111,就中间的一个LED不亮
** 输 入: ms:需要延时的长度
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 14.02.22
**-------------------------------------------------------------------------------------------------------
** 修改人:吴鉴鹰
** 日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
void delayms(uint ms)
{
uchar i;
while(ms--) for(i=0;i<120;i++);
}
/*********************************************************************************************************
** 函数名称: WriteWord
** 功能描述: 向MAX7219写入字(16位)
** 输 入: addr:需要延时的长度
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 14.02.22
**-------------------------------------------------------------------------------------------------------
** 修改人:吴鉴鹰
** 日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
/* 向MAX7219写入字(16位)*/
void WriteWord (uchar addr,uchar num)
{
LOAD=0;
_nop_();
SendChar(addr);
_nop_();
SendChar(num);
_nop_();
LOAD=1; // 锁存进相应寄存器
}
/* MAX7219初始化 */
void InitDisplay ()
{
WriteWord (ScanLimit,ScanDigit); // 设置扫描界限
WriteWord (DecodeMode,CommonDigit); // 设置译码模式为非译码模式
WriteWord (ShutDown,NormalOperation); // 设置为正常工作模式
}
/*********************************************************************************************************
** 函数名称: show
** 功能描述: 显示函数,其中Fdata为状态为第一位,Sdata状态为第二位,Tdata为状态为第三位。
Fdata为状态为第四位。degdata为显示的具体数值degdata 为4位 例如23.21(要求degdata输入格式)
** 输 入: Fdata Sdata Ddata Hdata
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 14.02.22
**-------------------------------------------------------------------------------------------------------
** 修改人:吴鉴鹰
** 日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
void show(float degdata,uint Fdata,Sdata,Ddata,Hdata)
{
uint a,b,c,d; //a为10位值,b为个位值,c为小数点后1位值,d为小数点后2位值,e为临时数据
InitDisplay (); // MAX7219初始化
_nop_(); //延时一小会
a=degdata/10;
b=(degdata-a*10)/1;
c=(degdata-a*10-b)*10;
d=(degdata-a*10-b-c*0.1)*100;
a=changestyle(a);
b=changestyle(b);
c=changestyle(c);
d=changestyle(d);
b=b|0x80;
WriteWord(Digit0,a);
WriteWord(Digit1,b);
WriteWord(Digit2,c);
WriteWord(Digit3,d);
WriteWord(Digit4,Fdata);
WriteWord(Digit5,Sdata);
WriteWord(Digit6,Ddata);
WriteWord(Digit7,Hdata);
}
/*********************************************************************************************************
** 函数名称: KEY
** 功能描述: 按键检测程序,实现检测标准的修改
** 输 入:
**
**
** 输 出: MAX , MIN
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 14.02.22
**-------------------------------------------------------------------------------------------------------
** 修改人:吴鉴鹰
** 日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
void KEY()
{
if(P0&0X01)
{
EA = 0;
delayms(30);
if(P0&0X01)
{
MAX ++;
}
EA = 1;
}
else if(P0&0X02)
{
EA = 0;
delayms(30);
if(P0&0X02)
{
MAX--;
}
EA = 1;
}
else if(P0&0X04)
{
delayms(30);
if(P0&0X04)
{
MIN++;
}
}
else if(P0&0X08)
{ EA = 0;
delayms(30);
if(P0&0X08)
{
MIN--;
}
EA = 1;
}
}
/*********************************************************************************************************
** 函数名称: shutshow
** 功能描述: 用来熄灭所有的数码管
** 输 入:
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 14.02.22
**-------------------------------------------------------------------------------------------------------
** 修改人:吴鉴鹰
** 日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
void shutshow() //数码管不显示函数
{
WriteWord(0x0c,0x00); //向低功耗模式寄存器(地址为0X0c)写入全1值(上电状态)用来熄灭显示器
}
main()
{
TMOD=0x02; //T1工作模式2
TH0=0x14; //定时器初始值高位
TL0=0x00;
IE=0x82; //IE.7位为1表示CPU开放中断,IE.1位也为1,表示允许定时器T0溢出中断
TR0=1; //使能定时器
P1=0x00; //选择ADC0809的通道1(000)(P1.0~P1.2)
// IT1=0; //低电平触发
IT1=1; //下降沿触发
EX1=1; //使能外部中断1
EA=1; //允许全局中断
WriteWord(0x0f,0x00); //退出测试模式
WriteWord(0x0a,0x03); //调解亮度,16级可选择,用于设置LED的显示亮度,从0xX0~0xXF
delayms(1000);
show(88.89,0x01,0x01,0x01,0x01);
delayms(1000);
while(1)
{
ST=0;ST=1;ST=0; //启动A/D转换
while(EOC==0); //等待转换完成
OE=1;
if(f==1)
{
delayms(1000); // 延时1s
if(result > MAX) SOUND = 0; //这里的60是设定的报警极限值,当超过该值时开始报警
if(result < MIN) SOUND = 1; //没达到报警值,无动作
show(result,0x01,0x01,0x01,0x01); //显示ad转换后的电压值
delayms(1000);
}
else
{ //这是鹰哥的风格,不管几句话都加上大括号,这样看起不会乱
show(0x01,0x01,0x01,0x01,55.55); //如果初始化未成功则显示55.55
}
//Display_Result(P3);
OE=0;
KEY();
}
}
/*********************************************************************************************************
** 函数名称: counter1
** 功能描述: 当数据采集完毕,出发单片机的外部中断进行数据的存储显示以及报警
** 输 入:
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 14.02.22
**-------------------------------------------------------------------------------------------------------
** 修改人:吴鉴鹰
** 日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
void counter1(void) interrupt 2 using 2
{
EX1=0;
result = P0;
EX1=1;
}
QQ群号:332164870 欢迎大家加入,交流,沟通,咨询!
`
|