基于51单片机的烟雾检测报警系统:从传感器选型到仿真标定
简介基于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检测对象以液化气、丙烷、氢气和烟雾为主检测浓度范围在 30010000ppm 量级。它不是精密浓度计给到单片机的是一个相对电压值同一块传感器在不同温湿度、不同老化阶段下基准电压都会漂。所以烟雾检测报警系统里的“阈值”必须做成可调不能烧死一个固定值。2.2 模拟输出与数字输出的取舍ADC 还是比较器市面上的 MQ-2 模块多数同时引出两个输出脚。DO 是数字量模块上已经有一颗 LM393 比较器电位器转到哪跳变点就在哪AO 是模拟量电压随浓度连续变化。两种接法都能做报警但差别很大。用 DO 的话51 单片机只需把引脚配置成输入读到电平跳变就报警代码最少。问题在于阈值被模块电位器固定用户想改报警浓度只能去拧螺丝固件里完全看不到当前浓度。课程演示够用但要做成“按键调阈值、LCD 显示当前值”的完整系统就得走模拟量。常见做法是 AO 接 ADC。51 单片机本身不带 ADCSTC 系列部分型号内置AT89C51 这种经典型号没有所以一般外接 ADC08328 位、两路、串行或 ADC08098 位、八路、并行。烟雾浓度变化本来就慢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液化气、丙烷、氢气、烟雾30010000ppm5V明显MQ-5液化气、天然气30010000ppm5V一般MQ-135空气质量、氨气、烟雾101000ppm5V中等从仿真到实物的便利程度看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.3V0.5V 之间具体数值以实物测量为准。ADC 输入范围是 05V要保证洁净空气时取样电压不低于 0.1V满量程时不超过 4.8V避免超出 ADC 参考电压。3.2 蜂鸣器与 LED 报警输出的三极管驱动51 单片机的 P1/P2 口输出电流只有毫安级直接驱动有源蜂鸣器要么声音小要么把引脚拉到异常状态。常见做法是用一颗 PNP 三极管做开关发射极接 5V基极经 4.7kΩ 电阻接单片机引脚集电极接蜂鸣器负极蜂鸣器正极接 5V。引脚输出低电平时三极管导通蜂鸣器响输出高电平时关断。LED 报警指示灯则简单得多引脚串一颗 220Ω470Ω 限流电阻到 LED 正极负极接地即可。给一张报警输出状态表方便接线后对照调试器件控制引脚触发电平备注有源蜂鸣器P2.0低电平响8550 PNP 驱动报警 LEDP2.1高电平亮330Ω 限流电阻ADC0832 CSP1.0低电平有效片选ADC0832 CLKP1.1上升沿采样软件产生时钟ADC0832 DI/DOP1.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); // 全关 }如果 BEEP0 时蜂鸣器不响大概率是选成了 NPN 三极管或者蜂鸣器正负极接反。有源蜂鸣器直接接 5V 和地之间应该能持续发声先单独给器件通电再查驱动电路能省很多时间。3.3 电源去耦与最小接线清单软件上容易忽略的一条是蜂鸣器动作瞬间的电流变化会在 5V 上产生尖峰这个尖峰如果串进 ADC 参考电压会直接表现为读数跳变。常见做法是系统入口放 100μF 电解电容加 0.1μF 陶瓷电容单片机电源引脚旁边再放一颗 0.1μF传感器加热回路和单片机电源别共用长地线。实际搭板时按这个清单核对器件数量关键参数MQ-2 烟雾传感器模块1AO/DO 输出5V 供电ADC083218 位双通道串行AT89C51 / STC89C52112MHz 晶振LCD16021字符液晶4 线接法有源蜂鸣器15V低电平触发LED1红色报警指示8550 PNP 三极管1蜂鸣器驱动电阻电容若干-10k、4.7k、330Ω、0.1μF、100μF4. 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; // SGL1单通道模式 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 上。返回值 0255 对应 0VrefVref 一般就是 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_LOWTH_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让回差宽度自动跟随阈值变化不用在按键逻辑里单独改两个宏。阈值调整范围限制在 10200防止误操作把阈值调到 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_VOPOT-HG 中端ADC0832 CH0ADC_CSP1.0ADC0832 CSADC_CLKP1.1ADC0832 CLKADC_DIOP1.2ADC0832 DI/DOBEEP_CTLP2.0蜂鸣器驱动基极LED_CTLP2.1LED 正极滑动变阻器初始位置要放在靠近地的一端对应洁净空气的低浓度。如果一上电就在报警检查 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 码持续打印出来等传感器预热 1015 分钟记录平稳后的读数作为基准。MQ-2 内部有加热丝冷启动时表面温度没起来电导率还在漂直接设阈值很可能把预热漂移当烟雾。void uart_send_adc(void) { unsigned char v read_adc0832(0); SBUF v; // 串口发送一个字节 while (!TI); TI 0; DelayMs(200); // 每 200ms 发一次 }用打火机气体丁烷离传感器进气孔 510cm 短喷一下观察 ADC 值爬升高度和回落时间能大致测出该器件的响应范围。记录一张标定表测量点ADC 码换算电压处理动作预热后洁净空气约 10约 0.2V作为基准丁烷气体 10cm60901.21.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找到既不抖又不会“粘住”不回位的那个差值。实物场景下优先从 810 个 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 秒以内对烟雾报警足够。把预热禁报、迟滞回差、均值滤波做成三个独立函数联调时逐个注释掉其中一个就能分清现场误报到底来自传感器预热漂移、阈值附近振荡还是采样尖峰。本文还有配套的精品资源点击获取