简介:这是一份面向51单片机初学者与嵌入式系统爱好者的燃气检测Proteus仿真设计资源,以智能气表LCD流量控制场景为切入点,完整演示了从传感器信号采集、ADC转换、浓度判断到LED/蜂鸣器报警输出的典型流程,也适合电子竞赛或课程设计参考。压缩包共33个文件,约289KB,主要包含Proteus仿真工程(dsn)、C语言源程序(c/h)、编译生成的hex、调试辅助的lst/m51/obj及Keil工程文件(uvproj/uvopt),基本覆盖了从源代码编写、编译烧录到仿真验证的完整工具链,目录中还可看到“LCD流量控制”“脉冲”“智能气表”等设计模块。目前已有173人学习下载,适合希望快速理解51单片机燃气检测系统工作原理、掌握Proteus硬件仿真调试方法的读者。通过查看源程序与仿真文件,能够直观学习传感器数据采集逻辑、阈值比较与报警控制实现,并结合示例工程练习Keil与Proteus的联调操作,对提升嵌入式系统设计能力有明显帮助。
1. 燃气检测仿真卡在哪:Proteus模型和单片机程序的配对
做燃气检测类课程设计时,大部分人不是被C语言难住的,而是被“仿真做不出来”卡住。标题里的四个要素要分开看:51单片机是控制核心,燃气检测是传感端需求,Proteus是交付载体,源程序和仿真文件是评分直接看的对象。这类设计放到Proteus里跑,真正的技术难点在于传感器模型缺失、AD转换时序对不上、报警逻辑没法直观验证。网上能搜到的教程大多只给一份源码,但实际把hex文件烧进仿真图后,数码管乱码、EOC不翻转、读数恒为0xFF的情况比比皆是。这篇文章按一个可复现的完整方案来讲:MQ-2传感器怎么等效进Proteus,ADC0809怎么和51单片机握手,报警阈值怎么调,最后怎么把源程序与仿真文件整理成一套能交差的工程。
2. 燃气检测与51单片机之间为什么要插一个ADC0809
2.1 MQ-2气敏传感器的输出特性曲线
燃气检测最常见的传感器型号是MQ-2。它的核心是二氧化锡半导体材料,内部带一根加热丝,通电后把敏感层加热到工作温度。清洁空气中,敏感层表面吸附氧分子,载流子浓度低,等效电阻很高;当甲烷、丙烷、液化气等还原性气体出现在传感器表面时,氧解吸释放电子,材料电导率上升,等效电阻RS明显下降。典型接法是把传感器和负载电阻RL串联分压:
Uout = VCC x RL / (RS + RL)洁净空气中RS约为几十千欧,Uout接近0V;浓度升高时RS降到几千欧,Uout上升。这个输出是模拟电压,范围大概在0到4V之间,跟浓度是非线性曲线。51单片机内部没有ADC,所以要么外接ADC芯片采样电压值,要么用比较器把它变成高低电平触发报警。做分级浓度显示时,ADC方案明显更合适。
2.2 三种信号接入51单片机的方式对比
- 比较器方案:用LM393把Uout和电位器设定的阈值电压比较,输出数字电平直接进IO口。成本最低,但只能告诉你“有没有超标”,给不出浓度趋势。
- ADC0832方案:串行接口ADC,占用2根IO,速度快,但Proteus里调试SPI时序对新手不友好。
- ADC0809方案:8路模拟输入,8位并行输出,接口直观,Proteus元件库里自带模型,网上可参考的例程最多。
本设计选ADC0809,理由很简单:Proteus仿真里并行接口的引脚状态一目了然,适合边仿真边理解转换时序。8位分辨率在5V基准下每个LSB约19.5mV,对应MQ-2的输出范围,做三档浓度划分完全够用。
2.3 MQ-2在Proteus元件库里的等效替代
在Proteus元件库里搜“MQ-2”,通常找不到可直接用的气体传感器模型。这不是你的库缺元件,而是这类半导体气敏传感器本身没有仿真模型。常见的等效做法是用电位器POT-HG充当可变分压器,手动调节输出电压模拟气体浓度变化。更接近真实特性一点的做法是保留分压结构:把一个普通电阻作为RL,另一个电位器作为RS,电位器调小时Uout升高,和MQ-2的电阻下降趋势一致。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 加热电压VH | 5V直流 | 仿真中直接接电源 |
| 检测回路电压VC | 5V直流 | 与单片机共地 |
| 负载电阻RL | 4.7k到10k可调 | 决定Uout静态工作点 |
| 电位器(模拟RS) | 10k | 调节范围覆盖清洁空气到泄漏状态 |
| ADC参考电压 | VCC=5V | VREF+接VCC,VREF-接地 |
实际做硬件时RL的选择要参考MQ-2数据手册里的灵敏度曲线,仿真阶段用电位器手调浓度,不需要精确匹配电阻值。这里要注意电位器三个引脚别接错,中间抽头输出到ADC0809的IN0,其余两端分别接VCC和GND。
2.4 ADC0809和51单片机的引脚对接规划
我比较常用的分配方式是把P1口整个作为ADC0809的数据总线,通道选择用P2的低三位,控制信号走P3口:
| 信号 | 接单片机引脚 |
|---|---|
| D0-D7 | P1.0-P1.7 |
| A/B/C(通道选择) | P2.0-P2.2 |
| START | P3.0 |
| OE | P3.1 |
| EOC | P3.2 |
| 蜂鸣器 | P3.3 |
| LCD数据口 | P0.0-P0.7 |
| LCD控制口 | P2.3-P2.5 |
| LED指示 | P3.5-P3.7 |
这样分配的好处是数据总线独占一个口,写代码时直接用P1 = ADC_DATA读取,时序不容易错位。ADC0809的CLK时钟信号可以用Proteus里的DCLOCK信号源直接供给,也可以让51单片机的定时器T0产生方波,两种方式都行,细节放在下一章程序里说。
3. 51单片机源程序怎么写:ADC0809驱动与三级报警逻辑
3.1 用定时器T0给ADC0809提供CLK
ADC0809工作时必须有时钟信号,典型范围10kHz到1.2MHz。Proteus仿真里最简单的方式是放一个DCLOCK时钟源,频率设为500kHz,输出端接到ADC0809的CLK引脚。如果手上没有DCLOCK或者老师要求“纯单片机电路”,就用定时器T0工作在方式2,把机器周期分频后从P3.4引脚送出去。以12MHz晶振为例,机器周期是1us,T0输出500kHz方波的初始化代码如下:
void Timer0_Init(void) { TMOD &= 0xF0; // 保留T1的配置 TMOD |= 0x02; // T0方式2:8位自动重装 TH0 = 0xFE; // 初值,产生500kHz方波 TL0 = 0xFE; ET0 = 1; // 开T0中断 TR0 = 1; // 启动T0 } void Timer0_ISR(void) interrupt 1 { // 方式2自动重装,不需要重新赋初值 // 对P3.4取反即可得到方波 T0_PIN = !T0_PIN; }使用定时器输出时钟时,ADC0809的CLK引脚直接连P3.4,不需要外部信号源。但要注意:方式2重装后第一次进中断的时间比后续周期多一个机器周期,仿真中这个误差可以忽略。我一般更推荐DCLOCK方案,程序少一个中断源,后续调EOC时序时变量更少。
3.2 ADC0809转换时序的代码实现
ADC0809的转换流程是:拉低START,设置通道地址,拉高START,再拉低START触发转换,等待EOC由低变高表示转换完成,最后拉高OE读取数据。驱动函数如下:
unsigned char ADC0809_Read(unsigned char ch) { unsigned char val; ADC_A = ch & 0x01; // 通道选择,低三位有效 ADC_B = (ch >> 1) & 0x01; ADC_C = (ch >> 2) & 0x01; START = 0; _nop_(); START = 1; // 启动脉冲上升沿 _nop_(); START = 0; // 下降沿开始转换 while (EOC == 0); // 等待转换结束,EOC变高 OE = 1; // 打开输出使能 _nop_(); val = ADC_DATA; // P1口读取8位转换结果 OE = 0; // 关闭输出使能 return val; }这里有个关键细节:EOC信号在转换过程中保持低电平,转换完成后变为高电平,所以while(EOC == 0)可以可靠等待。读取数据时OE为高电平期间,ADC0809把内部8位锁存器推送到数据总线上,单片机直接从P1口取数。最后一次OE必须拉低,否则下一次转换时数据总线一直被占用。
3.3 连续采样加均值滤波
燃气传感器输出有波动,单片机上电瞬间ADC读数也会跳变,直接拿单次采样值做阈值判断容易出现误报警。我一般做8次连续采样取平均值:
unsigned char ADC_Read_Filter(void) { unsigned char i; unsigned int sum = 0; for (i = 0; i < 8; i++) { sum += ADC0809_Read(0); // 固定采样IN0通道 } return (unsigned char)(sum >> 3); // 右移3位相当于除以8 }滤波之后再做阈值判断,稳定性和源程序的健壮性都会好一些。这里选择的IN0通道对应电位器抽头,Proteus里调节电位器时,ADC值会连续变化。
3.4 三档报警阈值与执行动作
我一般把8位ADC值按浓度分成三档,正常状态绿灯常亮,预警状态黄灯慢闪、蜂鸣器间歇鸣叫,报警状态红灯快闪、蜂鸣器连续响。阈值变量用宏定义,方便课程设计答辩时临时改参数:
#define TH_WARN 80 // 0.8V左右,根据VREF=5V换算 #define TH_ALARM 160 // 1.6V左右,视为泄漏报警 void Alarm_Handle(unsigned char conc) { if (conc < TH_WARN) // 安全区 { LED_SAFE = 1; LED_WARN = 0; LED_ALARM = 0; BEEP = 0; } else if (conc < TH_ALARM) // 预警区 { LED_SAFE = 0; LED_WARN = !LED_WARN; // 慢闪效果 LED_ALARM = 0; BEEP = (millis % 500 < 100); // 每500ms响100ms } else // 报警区 { LED_SAFE = 0; LED_WARN = 0; LED_ALARM = 1; BEEP = 1; // 持续报警 } }这里LED指示灯用了LED_WARN = !LED_WARN产生闪烁效果,实际源程序里要配合延时或定时器中断让主循环周期稳定。阈值换算逻辑是:5V对应ADC值255,所以0.8V对应2550.8/5=40? 不对,80对应0.8V? 阈值向下取整。我这段代码里的注释“0.8V左右”如果按2550.8/5=40.8,其实TH_WARN=80对应1.57V。需要修正。
重新计算:VREF=5V,8位ADC,LSB=19.6mV。浓度分级对应电压:
- 正常:Uout < 0.6V,对应ADC值<31
- 预警:0.6V-1.5V,对应ADC值31-76
- 报警:>1.5V,对应ADC值>76
为了演示效果好,把阈值放宽一点:TH_WARN=60(约1.18V),TH_ALARM=120(约2.35V)。这个分级在仿真里配合电位器好操作,不至于滑几度就报警。实际产品阈值要按MQ-2在目标气体浓度下的输出电压标定,仿真阶段阈值的主要作用是演示逻辑。
主循环里每50ms读一次浓度并刷新状态:
void main(void) { unsigned char concentration; LCD_Init(); // 1602初始化 Timer0_Init(); // 若用定时器输出CLK,则调用 while (1) { concentration = ADC_Read_Filter(); // 8次均值 LCD_Show_Concentration(concentration); Alarm_Handle(concentration); Delay50ms(); } }LCD1602的驱动代码属于标准外设,源程序里直接复用平时写好的显示驱动函数即可,本节不再重复贴完整代码,重点在ADC和报警逻辑的配合。
4. Proteus仿真文件搭建:元件摆放、连线和hex加载
4.1 元器件清单与Proteus元件库搜索关键词
Proteus 8 Professional的元件库里,搜索关键词和实际元件不完全同名,列一份常用对照表方便直接查:
| 元件 | Proteus里的搜索关键词 | 数量 |
|---|---|---|
| 单片机 | AT89C51 或 AT89C52 | 1 |
| ADC转换器 | ADC0809 | 1 |
| 气敏传感器替代 | POT-HG(电位器) | 1 |
| 液晶屏 | LM016L(字符液晶) | 1 |
| 排阻 | RESPACK-8 | 1 |
| 蜂鸣器 | BUZZER | 1 |
| LED | LED-RED / LED-YELLOW / LED-GREEN | 各1 |
| 晶振 | CRYSTAL,12MHz | 1 |
| 电容 | CAP 30pF x2,CAP-ELEC 10uF | 各2、1 |
| 电阻 | RES 100R / 10k | 若干 |
POT-HG在Proteus元件库里的名称就是POT-HG,也可以用RES可变电阻替代。注意数码管在这个设计里不用,显示功能交给LM016L液晶,评分时显示实时浓度更直观。
4.2 仿真原理图连线顺序
先把AT89C51放中间,P1口整体向右引出8根线给ADC0809的D0-D7。ADC0809的IN0接电位器中间抽头,电位器两端分别接5V和GND,这样旋转电位器就能模拟燃气浓度从低到高。ADC0809的VREF+接5V,VREF-接地,CLK引脚接一个DCLOCK信号源,频率设500kHz。
单片机P0口接RESPACK-8排阻,排阻另一端接VCC,用于上拉P0口。P0口再接LM016L的D0-D7,P2.3接RS,P2.4接RW,P2.5接E。蜂鸣器一端接VCC,另一端串一个100欧限流电阻接P3.3。三个LED分别接P3.5、P3.6、P3.7,注意LED的正极接单片机引脚,负极接GND,这样引脚输出高电平时LED点亮,逻辑和代码一致。
STOP,我上面3.4里LED_SAFE=1点亮,那LED接法要匹配:LED正极接P3.5,负极接GND,输出1时点亮。代码里正常状态下LED_SAFE=1,符合。
4.3 Keil源程序编译与hex烧录步骤
Keil uVision5里建立新工程时,芯片型号选Atmel下的AT89C52,勾选生成hex文件。编译通过后,在Proteus里双击AT89C51元件,弹窗里找到Program File项,点击文件夹图标选择Keil Output目录下的.hex文件。晶振频率要同步设置:如果原理图上用的是12MHz晶振,单片机属性里的Clock Frequency默认是12MHz,这里能对应上就正常。
仿真文件组织我习惯分三个目录:“Proteus工程”放.pdsprj文件,“Source”放Keil工程和.c文件,“Doc”放课程设计说明书。答辩时老师要源程序和仿真文件,直接整目录拷走就能跑,比单独丢一个hex文件规范得多。
4.4 初次仿真验证的观察点
点击右下角的Play按钮后,第一眼应该看ADC0809的EOC引脚波形。EOC从低到高跳变一次,代表一次转换完成。如果EOC纹丝不动,优先检查CLK信号。然后在LCD屏上应该看到字符显示“GAS: 001”之类的浓度值,调节电位器POT-HG时数值连续变化,超过阈值后LED和蜂鸣器状态切换。
拧电位器的时候,一只手按住键盘上的A键可以快速增加阻值,按住D键减小,每次按一下变化1%。要快速滑到报警区间,直接按住A键不放,几秒钟就能看到浓度从20跳到140,报警灯亮起。这个操作手感比拿鼠标拖电位器方便很多。
5. 仿真调试:ADC0809的EOC不翻转和读数恒定怎么办
5.1 EOC一直为低电平的检查路径
EOC是ADC0809的转换结束信号,仿真中这个引脚如果始终不跳高,按下面顺序排查:
- 检查CLK引脚是否有方波输入。用示波器探针点CLK,没有波形说明DCLOCK频率设置不对或者引脚没连上。
- 检查START脉冲是否真的送达。单片机的P3.0如果初始化时默认输出高电平,需要先在程序里给START=0,再进入转换流程。我的源程序里每次读ADC前都会把START拉低,避免悬空电平干扰。
- 检查程序是否跑起来了:在Keil里全速运行,Proteus里看P1.0引脚有没有电平变化。如果整个程序卡在某个while循环里,EOC自然不会有反应。
5.2 读数恒为0xFF或0x00的典型原因
读数恒为0xFF,大概率是P1口外部没有拉低信号的驱动能力问题。Proteus里P0口是开漏结构,但P1口有内部上拉,如果数据总线悬空,读回的值是所有位为1,也就是0xFF。把ADC0809的D0-D7全部接到P1口,如果某根线没连上,该位就保持高电平。另一种情况是OE没拉高就开始读数据,数据总线上是高阻状态。
读数恒为0,通常是ADC0809的VREF上下限问题。VREF+和VREF-不能悬空,VREF+接VCC后电位器抽头电压才能被正确量化。还有检查IN0通道选择:A/B/C三根线都接低电平,选择IN0,如果C引脚悬空被上拉成高电平,实际采样的可能就是IN4通道,而IN4没接任何东西,读数要么0要么跳变。
5.3 用Proteus虚拟示波器看ADC时序
Proteus里有简易示波器,点右边的工具栏找到“VSINE”?不是,我指的是Virtual Instruments面板里的“OSCILLOSCOPE”。把示波器的A通道接EOC,B通道接CLK,运行仿真后可以看到两路波形。正常情况是CLK连续方波,转换期间EOC为低,转换结束后EOC拉高,形成一个周期性的负脉冲。这个波形图上能直接判断START脉冲宽度是否太短。如果EOC的负脉冲宽度远远大于一个CLK周期,说明START没有正确触发转换,程序里需要在START=1之后加几个_nop_(),让脉冲宽度不小于2个CLK周期。
5.4 仿真专属的“浓度”调节技巧
用鼠标左键点一下POT-HG,然后按键盘A或D键微调阻值。A增加,D减少,每按一次变化1%。想快速跳变就长按A不放。如果老师演示时要求看到“浓度上升导致报警”的连贯过程,先把电位器调到10%,再演示长按A键,画面里液晶数值连续上涨,这是答辩时最直观的效果。POT-HG两端的接法决定旋转方向对应浓度增大还是减小,如果反过来,把电位器上下两个固定端交换即可。
6. 进阶:串口上报浓度和继电器联动排气扇
上面的方案已经能覆盖燃气检测报警的全部流程,但课程设计要想拉开分差,通常加两个功能:一是串口把浓度值发到Proteus虚拟终端,二是继电器联动排风扇。先说串口,51单片机的UART引脚是P3.0和P3.1,波特率用4800或9600,初始化时把SCON置为0x50,定时器T1做波特率发生器:
void UART_Init(void) { SCON = 0x50; // 串口方式1,允许接收 TMOD &= 0x0F; TMOD |= 0x20; // T1方式2 TH1 = 0xFA; // 9600波特率,12MHz晶振 TL1 = 0xFA; TR1 = 1; TI = 0; } void UART_SendByte(unsigned char dat) { SBUF = dat; while (TI == 0); TI = 0; }Proteus里放置VIRTUAL TERMINAL,RX引脚接单片机的TXD引脚,TX接RXD,两个设备交叉相连并共地。程序里在每次更新浓度值后,把ADC值转换成十六进制字符发送,虚拟终端窗口里就能看到数据滚动输出。
继电器联动排气扇的可做性更强:在Proteus元件库里放一个RELAY模型,线圈一端接VCC,另一端接NPN三极管的集电极,发射极接地,基极串电阻接P2.6。代码里阈值越过TH_ALARM时给P2.6置1,继电器吸合,常开触点接一个直流电机模拟排风扇转动。注意如果在Proteus里直接放RELAY,线圈两端要并联一个反向二极管,否则断电瞬间的感生电动势会让模型报错。很多教程原型图里没画这个二极管,仿真也能跑,但实际硬件电路里必须有。
最后给你一个答辩现场的演示脚本:开机先展示安全状态,LCD显示浓度值小于60,绿色LED亮;按住A键把电位器阻值拉高,LCD数值跳到130以上,红色LED亮,蜂鸣器持续响,继电器吸合,电机开始转;松开A键慢慢调回去,系统自动恢复到安全状态。整个过程不需要重新烧写程序,单靠源程序里的阈值宏定义和Proteus工程文件就能完整展示闭环逻辑。把这套源程序和仿真文件按第四章的分类文件夹整理好,拷到U盘里就能直接打开运行。
本文还有配套的精品资源,点击获取