简介:基于51单片机的烟雾检测报警系统完整设计资源,适合电子类课程设计、毕业设计及单片机爱好者。系统以51单片机为核心,通过ADC0833采集烟雾浓度,LCD1602显示浓度与阈值,支持按键调节阈值。浓度超标时,蜂鸣器报警、风扇启动,同时串口向手机发送报警信息;数据每分钟存入AT24C02,按键可读取历史数据回传。资源共70个文件,压缩包仅6.65MB,含Keil工程源码、原理图、演示视频和说明文档,代码模块化,适合学习外设驱动、I2C及串口通信。已有567人学习。下载可获得完整工程文件、电路原理图和演示视频,方便复现与二次开发。
1. 烟雾检测报警系统到底在检测什么:51 单片机的分工与边界
楼道、宿舍、配电机房这类场所,最怕的不是明火,而是“先冒烟再起火”的那几分钟。基于51单片机的烟雾检测报警系统,做的就是在这几分钟里把浓度变化转成可执行的声光提醒:烟雾传感器输出随浓度变化的电压,51 单片机负责采样、比较和驱动,最后落到蜂鸣器、LED 和 LCD 显示上。很多人以为核心是“传感器读到超标值就报警”,实际做下来会发现,信号链路怎么接、阈值放代码里还是模块上、报警怎么防抖,比读值本身更花时间。
下面的内容按“传感器选型 → 硬件电路 → 51 固件 → 仿真与标定 → 防误报细节”一条线展开,面向正在做 51 单片机课程设计、电子竞赛作品,或者想把烟雾报警原型做到能演示、能答辩、能交付的工程师。读完你能得到一个可直接复现的最小系统:MQ-2 输出模拟电压,ADC0832 采样,阈值在固件里可调,声光报警带防抖。
2. 烟雾传感器选型与信号输出:MQ-2 为什么最常见
2.1 MQ-2 的气敏电阻原理与输出特性
MQ-2 属于半导体气敏元件,内部有加热丝和二氧化锡敏感层。加热丝把敏感层加热到工作温度,烟雾或可燃气体分子接触敏感层表面后,会改变材料的电导率。洁净空气中电导率低,浓度升高时电导率随之上升。这个电阻变化没法直接给单片机读,需要在外面接一颗负载电阻,把“电阻变化”换成“电压变化”,这就是 MQ-2 模块上那颗可调电位器的用途。
MQ-2 的典型工作参数是加热电压 5V、回路电压 5V,检测对象以液化气、丙烷、氢气和烟雾为主,检测浓度范围在 300~10000ppm 量级。它不是精密浓度计,给到单片机的是一个相对电压值:同一块传感器在不同温湿度、不同老化阶段下,基准电压都会漂。所以烟雾检测报警系统里的“阈值”必须做成可调,不能烧死一个固定值。
2.2 模拟输出与数字输出的取舍:ADC 还是比较器
市面上的 MQ-2 模块多数同时引出两个输出脚。DO 是数字量,模块上已经有一颗 LM393 比较器,电位器转到哪,跳变点就在哪;AO 是模拟量,电压随浓度连续变化。两种接法都能做报警,但差别很大。
用 DO 的话,51 单片机只需把引脚配置成输入,读到电平跳变就报警,代码最少。问题在于阈值被模块电位器固定,用户想改报警浓度只能去拧螺丝,固件里完全看不到当前浓度。课程演示够用,但要做成“按键调阈值、LCD 显示当前值”的完整系统,就得走模拟量。
常见做法是 AO 接 ADC。51 单片机本身不带 ADC,STC 系列部分型号内置,AT89C51 这种经典型号没有,所以一般外接 ADC0832(8 位、两路、串行)或 ADC0809(8 位、八路、并行)。烟雾浓度变化本来就慢,8 位分辨率够用。数字量接法的代码很简单:
sbit smoke_do = P3^2; // MQ-2 模块数字输出,低电平表示超过模块阈值 void check_do(void) { if (smoke_do == 0) // 模块内部比较器翻转 alarm_on = 1; // 触发报警 }这段代码对应的是依赖模块电位器定阈值的简化方案,优点是省一个 ADC 器件,缺点是阈值不可在固件里调,而且 LM393 在临界点附近会抖动,后面还要加软件消抖。我一般只在快速验证传感器有没有反应时用 DO,正式系统一律用 AO。
2.3 常见烟雾传感器的参数对比与选型建议
和火灾报警系统里用的离子式或光电式感烟探测器不一样,51 单片机课程设计里常见的烟雾传感器都是半导体气敏类型。选型时最容易碰到 MQ-2、MQ-5、MQ-135 这三颗:
| 型号 | 主要检测对象 | 典型检测范围 | 加热电压 | 对烟雾响应 |
|---|---|---|---|---|
| MQ-2 | 液化气、丙烷、氢气、烟雾 | 300~10000ppm | 5V | 明显 |
| MQ-5 | 液化气、天然气 | 300~10000ppm | 5V | 一般 |
| MQ-135 | 空气质量、氨气、烟雾 | 10~1000ppm | 5V | 中等 |
从仿真到实物的便利程度看,MQ-2 是 Proteus 里最容易找到模型、网上资料最多、模块价格也最低的选择。缺点是对酒精、油烟也有响应,装在厨房里容易误报。如果场景偏天然气泄漏,可以换 MQ-5;偏空气质量检测,可以换 MQ-135。后面讲的 ADC 采样、阈值比较逻辑完全不变,换传感器只需要重新标定基准电压。
3. 烟雾报警硬件电路:从传感器信号到 51 单片机引脚
3.1 信号输入链路:分压、取样与 ADC 通道
用模块还是裸传感器,信号链路接法不一样。如果买的是成品 MQ-2 模块,AO 输出已经做了分压,直接把 AO 接到 ADC0832 的 CH0 即可。如果只买到传感器本体,需要自己搭分压取样电路:5V 电源给传感器加热端,信号端串一颗 10kΩ 可调电阻到地,取样点放在信号端和电阻之间,接到 ADC 输入。这个取样点的电压会随烟雾浓度升高而升高。
取样电阻的取值影响灵敏度和线性段跨度。取值偏小,洁净空气时电压低,浓度上升时爬升快,但高浓度段容易饱和;取值偏大,低浓度段变化更明显,但容易把噪声一起放大。对 MQ-2 来说,4.7kΩ~10kΩ 是比较常用的区间。Proteus 仿真里可以直接用滑动变阻器代替这颗取样电阻,调到中间位置就能得到一个典型分压点。
MQ-2 模块上那颗电位器是调节比较器参考电压的。走 AO 方案时它的位置会影响输出基准,我一般把它拧到中位附近,让洁净空气时的 AO 电压在 0.3V~0.5V 之间,具体数值以实物测量为准。ADC 输入范围是 0~5V,要保证洁净空气时取样电压不低于 0.1V,满量程时不超过 4.8V,避免超出 ADC 参考电压。
3.2 蜂鸣器与 LED 报警输出的三极管驱动
51 单片机的 P1/P2 口输出电流只有毫安级,直接驱动有源蜂鸣器要么声音小,要么把引脚拉到异常状态。常见做法是用一颗 PNP 三极管做开关:发射极接 5V,基极经 4.7kΩ 电阻接单片机引脚,集电极接蜂鸣器负极,蜂鸣器正极接 5V。引脚输出低电平时三极管导通,蜂鸣器响;输出高电平时关断。LED 报警指示灯则简单得多,引脚串一颗 220Ω~470Ω 限流电阻到 LED 正极,负极接地即可。
给一张报警输出状态表,方便接线后对照调试:
| 器件 | 控制引脚 | 触发电平 | 备注 |
|---|---|---|---|
| 有源蜂鸣器 | P2.0 | 低电平响 | 8550 PNP 驱动 |
| 报警 LED | P2.1 | 高电平亮 | 330Ω 限流电阻 |
| ADC0832 CS | P1.0 | 低电平有效 | 片选 |
| ADC0832 CLK | P1.1 | 上升沿采样 | 软件产生时钟 |
| ADC0832 DI/DO | P1.2/P1.3 | 分时复用 | 数据线 |
接好线之后先不写业务逻辑,跑一段硬件自检,确认蜂鸣器和 LED 的驱动极性没接反:
sbit BEEP = P2^0; sbit LED = P2^1; void hw_self_check(void) { BEEP = 1; LED = 0; DelayMs(200); // 全关 BEEP = 0; LED = 1; DelayMs(200); // 全开 BEEP = 1; LED = 0; DelayMs(200); // 全关 }如果 BEEP=0 时蜂鸣器不响,大概率是选成了 NPN 三极管或者蜂鸣器正负极接反。有源蜂鸣器直接接 5V 和地之间应该能持续发声,先单独给器件通电,再查驱动电路,能省很多时间。
3.3 电源去耦与最小接线清单
软件上容易忽略的一条是:蜂鸣器动作瞬间的电流变化会在 5V 上产生尖峰,这个尖峰如果串进 ADC 参考电压,会直接表现为读数跳变。常见做法是系统入口放 100μF 电解电容加 0.1μF 陶瓷电容,单片机电源引脚旁边再放一颗 0.1μF;传感器加热回路和单片机电源别共用长地线。实际搭板时,按这个清单核对:
| 器件 | 数量 | 关键参数 |
|---|---|---|
| MQ-2 烟雾传感器模块 | 1 | AO/DO 输出,5V 供电 |
| ADC0832 | 1 | 8 位,双通道,串行 |
| AT89C51 / STC89C52 | 1 | 12MHz 晶振 |
| LCD1602 | 1 | 字符液晶,4 线接法 |
| 有源蜂鸣器 | 1 | 5V,低电平触发 |
| LED | 1 | 红色,报警指示 |
| 8550 PNP 三极管 | 1 | 蜂鸣器驱动 |
| 电阻电容若干 | - | 10k、4.7k、330Ω、0.1μF、100μF |
4. 51 单片机固件实现:ADC 采样、阈值比较与报警控制
4.1 ADC0832 采样时序与读取函数
ADC0832 是 8 位逐次逼近型 ADC,两路输入,通过四根线挂在 51 单片机 IO 上:CS 片选、CLK 时钟、DI 数据输入、DO 数据输出。配置位一共三段:起始位、通道模式位、通道选择位,全部由单片机 IO 模拟产生。关键是每个 CLK 上升沿发送一个配置位,之后在每个下降沿从 DO 读一位数据。读取函数如下:
#include <reg52.h> #include <intrins.h> sbit ADC_CS = P1^0; sbit ADC_CLK = P1^1; sbit ADC_DI = P1^2; sbit ADC_DO = P1^3; unsigned char read_adc0832(unsigned char ch) { unsigned char i, dat = 0; ADC_CS = 0; // 片选拉低,启动一次转换 ADC_DI = 1; ADC_CLK = 1; _nop_(); ADC_CLK = 0; // 起始位 ADC_DI = 1; ADC_CLK = 1; _nop_(); ADC_CLK = 0; // SGL=1,单通道模式 ADC_DI = ch; // 通道选择:0 或 1 ADC_CLK = 1; _nop_(); ADC_CLK = 0; ADC_DI = 1; // 释放 DI,后续读取 DO for (i = 0; i < 8; i++) { ADC_CLK = 1; _nop_(); ADC_CLK = 0; _nop_(); if (ADC_DO) dat |= (0x80 >> i); } ADC_CS = 1; // 片选拉高,结束转换 return dat; }这段代码的要点是配置位的顺序不能颠倒。第一拍发起始位,第二拍发 SGL 位,第三拍发通道号,之后芯片才把转换结果放到 DO 上。返回值 0~255 对应 0~Vref,Vref 一般就是 5V,想显示电压值就用value * 5000 / 255换算成毫伏。CLK 频率要控制,12MHz 晶振下用_nop_()延时足够;如果换到 24MHz 晶振,建议在 CLK 翻转之间再加一条空语句,否则读数容易跳变。
4.2 阈值比较与软件防抖的报警逻辑
报警逻辑的关键是消除临界抖动。烟雾浓度在阈值附近波动时,单次采样值的微小变化就会让蜂鸣器反复通断。常见做法是在阈值比较前加两个机制:连续超阈值计数和回差比较。连续计数解决瞬时尖峰,回差解决阈值附近振荡。
#define TH_HIGH 30 // 报警阈值,ADC 码 #define TH_LOW 22 // 解除阈值,低于此值才停止报警 #define ALARM_CNT_MAX 3 // 连续超阈值 3 次才报警 unsigned char smoke_adc; unsigned char alarm_cnt = 0; bit alarm_on = 0; void check_smoke(void) { smoke_adc = read_adc0832(0); if (smoke_adc > TH_HIGH) { if (alarm_cnt < ALARM_CNT_MAX) alarm_cnt++; if (alarm_cnt >= ALARM_CNT_MAX) alarm_on = 1; } else if (smoke_adc < TH_LOW) { alarm_cnt = 0; alarm_on = 0; } }这里 TH_HIGH 和 TH_LOW 故意留了 8 个码的区间。浓度升到 30 启动报警后,要跌回 22 以下才解除,这个回差避免蜂鸣器在阈值附近乱响。ALARM_CNT_MAX 设为 3 表示连续三次采样都超阈值才报警,配合 200ms 的采样周期,大约 0.6 秒延时,足以滤掉常见干扰。三种状态对应输出:
| 采样值区间 | 状态 | 输出 |
|---|---|---|
| ≤ TH_LOW | 正常 | 蜂鸣器关、LED 灭 |
| TH_LOW~TH_HIGH | 保持上次状态 | 状态不变 |
| ≥ TH_HIGH 连续 3 次 | 报警 | 蜂鸣器开、LED 亮 |
4.3 LCD1602 显示与按键调阈值
一个完整的系统光报警还不够,至少要能显示当前浓度码和当前阈值,并且支持按键调阈值。LCD1602 用 4 线接法(RS、RW、EN 加 D4-D7),核心是把阈值做成全局变量,供按键修改。按键采用扫描加延时消抖,K1 加阈值、K2 减阈值、K3 恢复默认值。
#define KEY_ADD 0 #define KEY_DEC 1 #define KEY_DEF 2 unsigned char smoke_th = 30; // 当前阈值,默认 30 void key_scan(void) { if (key_pressed(KEY_ADD)) { if (smoke_th < 200) smoke_th++; } if (key_pressed(KEY_DEC)) { if (smoke_th > 10) smoke_th--; } }注意smoke_th在按键后要立即写回 LCD,并用它替换 4.2 节里的 TH_HIGH。TH_LOW 可以写成smoke_th - 8,让回差宽度自动跟随阈值变化,不用在按键逻辑里单独改两个宏。阈值调整范围限制在 10~200,防止误操作把阈值调到 0 造成一直报警。
5. Proteus 仿真与烟雾浓度标定:从调通到可靠
5.1 Proteus 里搭最小仿真电路:用滑动变阻器模拟烟雾电压
写好的固件先别急着买器件,Proteus 里能完成绝大部分验证。新建工程后,从元件库拉取 AT89C51、ADC0832、LED、BUZZER、LM016L 和滑动变阻器 POT-HG。POT-HG 的中端接 ADC0832 的 CH0,两端分别接 5V 和地,调节阻值就等于模拟 MQ-2 输出烟雾浓度电压。
接线顺序建议按照信号流向:先连 ADC0832 到单片机,再连 LCD1602,最后连蜂鸣器和 LED。ADC0832 的 CLK、CS、DI/DO 分别接 P1.1、P1.0、P1.2/P1.3,程序里已经定义好,仿真里保持一致。晶振设为 12MHz,给 AT89C51 加上电源和地。
| 网络名 | 起点 | 终点 |
|---|---|---|
| POT_VO | POT-HG 中端 | ADC0832 CH0 |
| ADC_CS | P1.0 | ADC0832 CS |
| ADC_CLK | P1.1 | ADC0832 CLK |
| ADC_DIO | P1.2 | ADC0832 DI/DO |
| BEEP_CTL | P2.0 | 蜂鸣器驱动基极 |
| LED_CTL | P2.1 | LED 正极 |
滑动变阻器初始位置要放在靠近地的一端,对应洁净空气的低浓度。如果一上电就在报警,检查 POT-HG 中端电压是不是已经高于 TH_HIGH,或者蜂鸣器驱动电路接反了。
5.2 仿真联调:观察阈值翻转和回差生效
加载 hex 文件后运行,把 POT-HG 调到最低位置,此时 LCD 显示的 ADC 值应接近 0 或几。然后缓慢往上调,观察两个动作点:第一,ADC 值超过 TH_HIGH 之后,蜂鸣器是否持续鸣叫而不是抖动;第二,回调阻值让 ADC 值降到 TH_LOW 以下,报警是否解除。
如果在阈值附近来回调节时蜂鸣器“咔嗒咔嗒”响,说明回差区间在起作用,这是正常现象。但要注意这种抖动只能出现在回差区间内,如果低浓度区就频繁触发,多半是有源蜂鸣器驱动极性反了,或者 ADC0832 的 DO 数据线在仿真里没接对。Proteus 里看不到真实的烟,但能看到完整的“浓度电压 → ADC 码 → 阈值比较 → 输出驱动”链路,这条链路通了,实物才敢通电。
5.3 实物标定:预热 10 分钟再谈基准
传感器出厂参数只是典型值,同一批 MQ-2 的洁净空气电压可能差 0.2V 以上,所以实物必须现场标定。常见做法是上电后先别接报警逻辑,用串口把原始 ADC 码持续打印出来,等传感器预热 10~15 分钟,记录平稳后的读数作为基准。MQ-2 内部有加热丝,冷启动时表面温度没起来,电导率还在漂,直接设阈值很可能把预热漂移当烟雾。
void uart_send_adc(void) { unsigned char v = read_adc0832(0); SBUF = v; // 串口发送一个字节 while (!TI); TI = 0; DelayMs(200); // 每 200ms 发一次 }用打火机气体(丁烷)离传感器进气孔 5~10cm 短喷一下,观察 ADC 值爬升高度和回落时间,能大致测出该器件的响应范围。记录一张标定表:
| 测量点 | ADC 码 | 换算电压 | 处理动作 |
|---|---|---|---|
| 预热后洁净空气 | 约 10 | 约 0.2V | 作为基准 |
| 丁烷气体 10cm | 60~90 | 1.2~1.8V | 应触发报警 |
| 气体移开后 30s | 回落至基准 | 回落 | 报警应解除 |
如果基准值本身就高于 20,说明采样电路分压不合适,回查 3.1 节的取样电阻;如果气体测试时 ADC 爬升很慢,检查传感器是否长时间没通电、表面是否积灰。8 位 ADC 对 5V 电压分辨约 20mV,现场调试时直接改代码里的 TH_HIGH 宏比拧模块电位器直观得多。
6. 防误报的三个工程细节:预热禁报、迟滞回差与均值滤波
6.1 开机预热期内禁止报警
MQ-2 冷启动后电导率需要几分钟才能进入平稳状态,这段时间的 ADC 读数会缓慢下降或上升,如果直接启用报警逻辑,很可能在刚通电时误报一次。常见做法是加一个预热计时变量,系统上电后前 120 秒强制alarm_on保持 0,主循环里用定时器中断每秒递减一次预热计数。预热状态可以用 LED 闪烁提示,方便演示时看出系统正在启动阶段。
6.2 迟滞回差的具体调试方法
第 4 章的 TH_HIGH 和 TH_LOW 就是迟滞回差,但具体差值取多少要看现场。差值太小,回差几乎不起作用;差值太大,报警解除点会明显低于启动点,气体散尽后要等很久才停。我的调试方法很直接:在仿真里把滑动变阻器缓慢推过阈值区,观察蜂鸣器动作区域宽度,再把 TH_LOW 从TH_HIGH - 2逐步加大到TH_HIGH - 10,找到既不抖又不会“粘住”不回位的那个差值。实物场景下优先从 8~10 个 ADC 码开始试。
6.3 滑动窗口均值滤波压制尖峰干扰
烟雾传感器输出本身不是理想直流,加上蜂鸣器动作、LCD 刷新这些干扰源,单次采样值偶尔会冒出一个尖峰。给采样加一个简单可靠的滑动窗口滤波:连续读 8 次,去掉最大值和最小值,剩下 6 次取平均。这个算法比单纯求平均更能压制尖峰,也不需要复杂的环形缓冲区。
unsigned char smoke_filter(void) { unsigned char i, max_v = 0, min_v = 255, sum = 0, cur; for (i = 0; i < 8; i++) { cur = read_adc0832(0); if (cur > max_v) max_v = cur; if (cur < min_v) min_v = cur; sum += cur; } return (sum - max_v - min_v) / 6; }注意返回值至少是 6 次采样的均值,实际采样周期会变成 8 倍,所以要重新算总响应时间。每 30ms 读一次,滤波后约 240ms 出一组结果,配合 200ms 的主循环调度,整体响应在 1 秒以内,对烟雾报警足够。
把预热禁报、迟滞回差、均值滤波做成三个独立函数,联调时逐个注释掉其中一个,就能分清现场误报到底来自传感器预热漂移、阈值附近振荡还是采样尖峰。
本文还有配套的精品资源,点击获取