ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于AT89C51的烟雾温度火灾报警器:从传感器选型到实物联调全解析

基于AT89C51的烟雾温度火灾报警器:从传感器选型到实物联调全解析 简介基于AT89C51单片机的温度烟雾火灾报警系统设计项目适合单片机学习者、电子竞赛参赛者及课程设计人员可用于完整掌握火灾报警功能的软硬件实现。方案覆盖MQ-2烟雾传感器与ADC0832模数转换、DS18B20温度采集、数码管实时显示、蜂鸣器和LED声光报警并设置按键调节报警阈值与手动报警按键兼顾火情探测、灯光报警和异常告警。整体采用模块化设计用户可根据不同防火场所灵活选用温度、烟雾等探测方式便于硬件搭建、程序调试与二次扩展具有较高的工程实用价值。压缩包约4.93MB内容便于下载后直接参考电路连接、传感器选型、控制逻辑及调试方法。目前已有354人学习适合作为课程设计或毕业设计的参考资料。1. 从「仿真通过」到「实物能报警」AT89C51烟雾温度火灾报警器到底卡在哪Proteus 里把烟雾传感器换成电位器仿真跑得再好实物焊出来也未必响——这是温度烟雾火灾报警设计最真实的起点。AT89C51 这颗老芯片没有内置 ADC没有 I2C 硬件外设连 DS18B20 的单总线时序都得靠延时函数一点点凑但恰恰因为电路简单、资料多、成本低它依然是单片机课设和毕设里做实物最容易出成果的方案之一。这篇文章不讲仿真只讲实物链路从 AT89C51 最小系统、MQ-2 烟雾传感器、DS18B20 温度传感器、ADC0831 采样芯片到数码管显示和两级报警判定把每一根接线、每一条指令、每个需要调的参数说透。适合正在做单片机课程设计的人也适合手里有 51 开发板、想接真实传感器做小项目的工程师。仿真里看不到的电源噪声、传感器预热漂移、蜂鸣器驱动电流不足才是实物能不能稳定工作的关键。2. 系统拆解与器件选型AT89C51、DS18B20、MQ-2 和 ADC0831 怎么搭才合理2.1 为什么还用 AT89C51Flash、引脚和任务匹配度AT89C51 是 Atmel 时代的经典 51 内核单片机4KB Flash、128B RAM、32 个 IO 口、两个定时器主频常见 12MHz 或 11.0592MHz。放到今天它的运算能力连 STM32 的零头都比不上但这个报警器任务的核心是「周期采样 阈值比较 数码管显示 按键输入」全是简单的位操作和字节比较4KB Flash 写一个 800 行以内的 C51 程序完全够用。胜在引脚直插、焊接容易、最小系统电路成熟出了问题上搜索引擎能查到大量同款原理图。选型上要注意区分 AT89C51 和 AT89S51AT89S51 支持 ISP 在线下载AT89C51 需要编程器烧录。实物制作优先选 AT89S51或者直接选 STC 系列的 STC89C52RC引脚兼容下载方便。下文代码按标准 51 内核编写默认晶振 12MHz用 AT89C51 或兼容芯片都能直接编译烧录。2.2 温度烟雾火灾报警的输入输出选型表这个设计里输入有两路物理量输出有三路执行器件。先列一张选型表把每个器件的接口类型、供电要求和关键参数说清楚后面接线和写代码都围绕这张表展开。器件作用接口类型供电关键参数AT89C51主控DIP40P0-P3 四组 IO5V约 10-20mA4KB Flash12MHzDS18B20温度采集单总线 1-Wire仅需 1 根数据线3.0-5.5V-55°C 到 125°C默认 12 位分辨率MQ-2烟雾/可燃气体检测模拟电压输出0-5V 随浓度升高加热丝 5V约 150mA预热 5 分钟以上输出才稳定ADC08318 位串行 ADC3 线 SPI 兼容CS、CLK、DO5V参考电压 Vref5V 时分辨率约 19.6mV/LSB有源蜂鸣器声音报警IO 口驱动三极管5V约 20-30mA有源自带振荡IO 高电平即响红色 LED光报警IO 口串联限流电阻5V约 5-10mA限流电阻 220Ω-1kΩ两位共阴数码管显示温度或烟雾值8 段 2 位选 10 个 IO5V段电流约 5-10mA动态扫描刷新选择 ADC0831 而不是 ADC0804是因为 ADC0831 是串行输出只占 3 个 IO 口而 ADC0804 的 8 位并行输出会占用整个 P1 口。在 51 单片机上IO 资源比什么都金贵省下来的引脚可以留给数码管段选和按键。如果手头只有 ADC0804也能做但布线复杂度会上升一个等级。2.3 MQ-2 的测量原理气敏电阻变化如何变成单片机认识的数字MQ-2 内部有一个加热丝和一个氧化锡半导体气敏层。加热丝通电后把敏感层加热到 200°C 左右此时洁净空气中的氧分子会吸附在敏感层表面夺取电子使材料电阻升高。当还原性气体如烟雾、液化气、丁烷出现时气体分子与吸附氧发生反应释放电子材料电阻下降。MQ-2 模块通常已经集成了一个分压比较电路输出两种信号AO 模拟电压和 DO 数字电平。做温度烟雾火灾报警用 AO 口输出电压电压随气体浓度升高而上升接 ADC0831 的输入端。整个测量链路是气体浓度 → MQ-2 内部电阻下降 → 分压点电压上升 → ADC0831 数字化 → AT89C51 读寄存器数值 → 与阈值比较。这条链路里最容易忽略的是 MQ-2 的加热特性。刚上电时加热丝温度没到工作点输出会从接近 0V 缓慢爬升这个过程短则几十秒长则几分钟。如果程序一上电就按固定阈值判断很可能连续误报。解决方法是上电后先做阈值自学习取前 30 秒的平均值作为基线后面单独讲实现。3. 实物电路搭建AT89C51 最小系统、传感器接口与数码管动态显示的接线细节3.1 AT89C51 最小系统晶振、复位与电源去耦实物搭建从最小系统开始。AT89C51 的第 18、19 脚接 12MHz 晶振两个 30pF 负载电容分别接地。第 9 脚是复位引脚典型的 RC 上电复位电路用 10μF 电解电容串联 10kΩ 电阻到地电源上升瞬间 RST 保持高电平约 100ms足以让单片机完成内部复位。电源去耦是实物最容易翻车的地方。单片机工作时 IO 口翻转会产生高频电流尖峰如果 VCC 和 GND 之间没有就近放置去耦电容轻则数码管显示闪烁重则程序跑飞。我的习惯是在 AT89C51 的 VCC 引脚旁边放一个 10μF 钽电容和一个小容量的 104 陶瓷电容两个电容并联分别吸收低频纹波和高频噪声。MQ-2 的加热丝是阻性负载工作时电流波动大不能和单片机共用同一路稳压输出否则加热丝的电流变化会直接污染单片机的电源。3.2 MQ-2 信号通道分压电阻与 ADC0831 的接线MQ-2 模块的 AO 输出接到 ADC0831 的第 2 脚 VINVIN- 接地。ADC0831 的 Vref 接 5V 稳压电源的干净输出端VCC 同样接 5V。数据线 DO 接 AT89C51 的 P1.0时钟线 CLK 接 P1.1片选 CS 接 P1.2。如果 MQ-2 模块是裸传感器而不是集成模块需要在传感器输出端和地之间接一个 4.7kΩ 分压电阻电阻中点作为信号输出接 ADC0831。这里有一个参考电压的细节。ADC0831 的参考电压 Vref 决定了测量量程Vref 越高量程越大但分辨率越低。Vref 接 5V 时8 位 ADC 的每个 LSB 对应 5V/255 ≈ 19.6mV。MQ-2 在洁净空气中的输出大约是 0.1-0.5V在有烟雾时能升到 2V 以上在 5V 量程下完全够用。如果接的是 2.5V 基准电压源分辨率能提高到 9.8mV/LSB但量程只有 2.5V对 MQ-2 这种输出上探到 3V 以上的传感器不太合适。3.3 数码管动态扫描与声光报警驱动电路两位共阴数码管的段选 a-g、dp 分别接 AT89C51 的 P2.0-P2.7每位段选之间串联 220Ω 限流电阻。位选端通过 PNP 三极管接 VCCP3.4 控制十位P3.5 控制个位。动态扫描的原理是轮流点亮十位和个位刷新频率在 50Hz 以上人眼看到的就是稳定的数字。注意位选不能直接接 IO 口驱动共阴数码管一位全亮时电流可达 40mA 以上超过 AT89C51 单个 IO 口的灌电流能力必须用三极管扩流。蜂鸣器驱动同理。有源蜂鸣器工作电流约 20-30mA直接接 P1.7 会拉低电平、声音发闷甚至烧毁 IO 口。用一颗 NPN 三极管如 S8050做开关基极串联 1kΩ 电阻接 P1.7发射极接地集电极接蜂鸣器负极蜂鸣器正极接 VCC。这样 IO 口只提供约 3mA 的基极电流蜂鸣器由电源直接供电。LED 报警灯串 470Ω 电阻后接 P1.6低电平点亮。3.4 完整接线总表与实物装配顺序下面这个接线总表可以直接照着在洞洞板上操作基本覆盖了设计里除按键外的所有信号连接。信号从端到端说明晶振XTAL1/XTAL212MHz 晶振两端各接 30pF 电容到地复位RST10μF 电容 10kΩ 电阻上电自动复位温度数据P1.3DS18B20 的 DQ 引脚DQ 同时接 4.7kΩ 上拉到 VCCADC 数据P1.0ADC0831 的 DO串行数据输出ADC 时钟P1.1ADC0831 的 CLK无数据时保持低电平ADC 片选P1.2ADC0831 的 CS低电平启动转换烟雾输入ADC0831 VINMQ-2 的 AO共用 GND蜂鸣器P1.7S8050 基极串 1kΩ集电极接蜂鸣器负极报警 LEDP1.6LED 负极串 470ΩLED 正极接 VCC段选P2.0-P2.7数码管 a-g、dp各串 220Ω位选P3.4/P3.5十位/个位 PNP 基极经 1kΩ 电阻装配顺序上先焊最小系统并烧写一个点亮 LED 的测试程序验证单片机活着再接数码管验证显示再接 DS18B20 看温度读数最后接 MQ-2 和 ADC0831。每接一个模块就单独测试这样出了问题能快速定位是一块硬件的问题还是整体逻辑的问题。如果一上来全部焊完再调试蜂鸣器乱响、显示乱跳时根本不知道从哪查起。4. 软件实现温度读取、烟雾电压采样与两级报警判定的 C51 代码4.1 DS18B20 的 1-Wire 时序拆解与 C51 驱动DS18B20 的数据读取完全靠一根 DQ 线上的时序控制代码的核心是三个函数复位、写字节、读字节。每次读写前都要先关中断因为 1-Wire 对时序要求严格定时器中断一进来延时被打断数据就可能读错。这里给出在 12MHz 晶振下的标准 C51 驱动。sbit DQ P1^3; // 复位脉冲拉低 480us释放检测应答脉冲 bit ds18b20_reset(void) { bit presence; DQ 0; // 主机拉低总线 delay_us(480); // 复位脉冲宽度datasheet 要求 480-960us DQ 1; // 释放总线 delay_us(60); // 等待 DS18B20 拉低应答 presence DQ; // 读到 0 表示有设备应答 delay_us(420); // 等待复位周期结束 return presence; // 返回 1 表示无应答返回 0 表示正常 } // 写一个字节每 bit 拉低 2us 后写数据持续 60us void ds18b20_write_byte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DQ 0; delay_us(2); DQ dat 0x01; // 数据位从低到高发送 delay_us(60); // 保持电平完成写入 DQ 1; dat 1; } } // 读一个字节每 bit 拉低 2us 后释放采样 unsigned char ds18b20_read_byte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { DQ 0; delay_us(2); DQ 1; // 释放总线让 DS18B20 驱动 delay_us(2); dat 1; if (DQ) dat | 0x80; // 采样点高电平写 1 delay_us(60); } return dat; } // 读取温度值返回带符号整数单位 0.0625°C int ds18b20_read_temperature(void) { unsigned char low, high; int temp; ds18b20_reset(); ds18b20_write_byte(0xCC); // 跳过 ROM单设备不用读序列号 ds18b20_write_byte(0x44); // 启动温度转换 delay_ms(600); // 12 位分辨率转换时间约 750ms这里留余量 ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); // 读暂存器 low ds18b20_read_byte(); // 先读低字节 high ds18b20_read_byte(); // 再读高字节 ds18b20_reset(); // 结束本次读取 temp (high 8) | low; if (temp 0x8000) { // 负温度时高 3 位全是 1 temp temp - 65536; } return temp; // 实际温度 temp * 0.0625 }代码里有几个参数在实物调试时必须确认。复位脉冲的 480μs 不是随便写的AT89C51 主频 12MHz 时delay_us(1) 大约是 12 个机器周期这个延时函数要用单片机的定时器校准或者用逻辑分析仪看时序否则可能读出来一直是 0xFFFF。跳过 ROM 指令 0xCC 的前提是总线上只有一个 DS18B20如果实物上接了多个温度传感器就要改成读 ROM 并匹配序列号。转换时间 600ms 是为了保险实际用 750ms 是上限如果想要显示更流畅可以在等待期间做按键扫描。4.2 ADC0831 的 8 位串行读取与烟雾电压换算ADC0831 的数据读取比 DS18B20 简单得多只要片选拉低后在时钟下降沿逐位读取输出即可高位在前。这个时序不要求微秒级精确定时普通延时就能满足但当片选拉低、开始读取时转换是边沿触发的时钟频率不要超过芯片上限。sbit AD_DO P1^0; // 数据输出 sbit AD_CLK P1^1; // 时钟 sbit AD_CS P1^2; // 片选 // 读取一次 ADC0831 转换结果8 位高位在前 unsigned char adc0831_read(void) { unsigned char i, dat 0; AD_CS 0; // 片选拉低启动转换 delay_us(10); // 等待内部采样保持稳定 for (i 0; i 8; i) { AD_CLK 1; // 时钟上升沿ADC 开始输出当前位 delay_us(2); // 建立时间 AD_CLK 0; // 时钟下降沿数据稳定在 DO 引脚 delay_us(2); if (AD_DO) { dat | (0x80 i); // 高位在前依次填入 } } AD_CS 1; // 片选拉高结束读取 return dat; }ADC0831 是 8 位分辨率输出范围 0-255对应 0-Vref 的电压。如果 Vref 接 5V那么读取到的数值 dat 换算成电压的公式是V dat * 5.0 / 255.0。但在实际阈值判断时不用真的计算浮点电压直接把 ADC 值和预设的阈值整数比较就行。比如想设定烟雾报警阈值为 2.5V对应的 ADC 阈值就是2.5 * 255 / 5 127.5取 128 即可。这样写代码省去浮点运算在 51 单片机上执行速度更快。4.3 两级阈值报警主循环与按键调阈值报警逻辑分两级预报警和强制报警。预报警是温度超过 50°C 或烟雾浓度达到阈值时触发蜂鸣器慢速鸣叫LED 点亮强制报警是温度超过 75°C 或烟雾继续升高蜂鸣器快速鸣叫触发更急促的节拍。这个分段的意义是给现场人员区分「注意」和「立即撤离」两种状态。#define TEMP_WARN 800 // 预报警温度阈值 50°C (50/0.0625) #define TEMP_ALARM 1200 // 强制报警温度阈值 75°C #define SMOKE_WARN 128 // 预报警烟雾阈值对应约 2.5V #define SMOKE_ALARM 200 // 强制报警烟雾阈值对应约 3.9V unsigned char smoke_value; int temperature; unsigned char warn_flag, alarm_flag; void alarm_process(void) { if (alarm_flag) { // 强制报警蜂鸣器急速鸣叫LED 常亮 BEEP ~BEEP; LED 0; } else if (warn_flag) { // 预报警蜂鸣器慢速鸣叫间隔 200ms if (beep_tick 20) { // 10ms 定时器累计 200ms BEEP ~BEEP; beep_tick 0; } LED 0; } else { BEEP 1; // 无报警时蜂鸣器关闭 LED 1; // LED 熄灭 } } void main(void) { unsigned char key_state 0; timer0_init(); // 定时器 0 产生 10ms 时基 while (1) { // 每 300ms 轮询一次传感器 if (poll_tick 30) { poll_tick 0; smoke_value adc0831_read(); temperature ds18b20_read_temperature(); // 篡改阈值按键KEY1 切换阈值模式KEY2 增加KEY3 减少 key_scan(); // 两级判断 if (temperature TEMP_ALARM || smoke_value SMOKE_ALARM) { alarm_flag 1; warn_flag 0; } else if (temperature TEMP_WARN || smoke_value SMOKE_WARN) { alarm_flag 0; warn_flag 1; } else { alarm_flag 0; warn_flag 0; } } // 数码管动态刷新时间片轮换显示温度或烟雾值 display_refresh(smoke_value, temperature); alarm_process(); } }这段代码的主循环是「轮询 判断 输出」三种操作但有个容易忽略的细节DS18B20 的读取函数内部有几百毫秒的转换等待这段等待期间不能去做数码管刷新否则显示会闪烁。解决办法是把 DS18B20 的转换启动和读取分成两次先发 0x44 启动转换利用等待时间去刷新显示再回来读温度。上面的代码为便于理解写成同步方式实际做实物时建议把ds18b20_read_temperature()拆成ds18b20_start_conversion()和ds18b20_get_temperature()两个函数中间穿插显示刷新。4.4 显示刷新、按键消抖与代码组织说明显示刷新用定时器中断驱动每 10ms 中断一次在主循环里切换当前显示的是温度还是烟雾值。判定「显示温度还是烟雾」用一个状态位按键按下时翻转按键再按时保持。按键消抖的做法是不在按键中断里处理而是在主循环里轮询检测到电平变化后延时 20ms 再读一次两次一致才认为有效这是 51 单片机最稳妥的软件消抖方式。代码文件建议拆分成main.c、ds18b20.c、adc0831.c、display.c四个模块各模块用头文件声明接口函数。这样后面调整阈值、换传感器型号时不用到到处找代码。整个程序去掉注释大约 400-500 行 C 语言烧录到 AT89C51 的 4KB Flash 里还有余量可以再塞入报警历史记录、串口打印调试信息等功能。5. 实物联调与可靠性处理传感器预热、阈值自学习与误报抑制的三个技巧5.1 上电先量供电电压不对后面全是幻觉实物联调的第一步不是看报警而是用万用表量三个关键点的电压AT89C51 的 VCC 与 GND 之间是否稳定在 4.8-5.2VMQ-2 加热丝两端是否有 5VADC0831 的 Vref 与 GND 之间是否干净。我在调试时遇到过一次数码管乱跳、温度读数忽高忽低的情况查了半天发现是 MQ-2 和单片机共用了同一个 7805 稳压输出加热丝启动瞬间把电压拉到了 4.2V单片机直接复位。把 MQ-2 改接独立电源后问题消失。5.2 阈值自学习克服 MQ-2 上电漂移MQ-2 上电后的输出不是立即稳定在洁净空气值会从 0.1V 缓慢爬到 0.5V 再回落到 0.3V 左右整个过程约 3-10 分钟。如果程序用固定阈值报警这段时间内很容易误报。常见做法是上电后做 30 秒基线采集每秒读一次 ADC 值去掉最大最小值后取平均得到基线值 base_ad之后的报警阈值动态计算为base_ad 50预报警和base_ad 120强制报警。这样换一个环境、换一个传感器模块都能自动适配不用手工改代码里的阈值常量。unsigned int base_ad 0; // 上电后调用采集 30 次取平均作为烟雾基线 void smoke_baseline_calibration(void) { unsigned char i; unsigned int sum 0; for (i 0; i 30; i) { sum adc0831_read(); delay_ms(1000); // 每 1 秒采一次共 30 秒 } base_ad sum / 30; SMOKE_WARN base_ad 50; SMOKE_ALARM base_ad 120; }5.3 滑动平均与连续判定误报与漏报的折中烟雾传感器有一个特性检测到气体后输出上升不是瞬时的响应时间约 10 秒但抖动是瞬时的一颗烟头飘过就可能让 ADC 值突然跳高几十个单位。应对方法是软件滤波——连续读 4 次去掉最大值和最小值取中间两次的平均值。这个滑动平均窗口可以放在 ADC 读取函数和报警判断之间不影响主循环的其他操作。报警判定上再加一条「连续 3 次超过阈值才触发」的约束这样虽然会延迟约 1-2 秒报警但能大幅减少瞬时干扰。真正的火灾烟雾浓度是持续上升的1 秒的延迟换来的是不误报在宿舍、实验室这类环境里更实用。调整参数时记住一条经验预报警阈值设得低、次数要求少强制报警阈值设得高、次数要求多让系统对小烟雾敏感、对大火灾果断。本文还有配套的精品资源点击获取
返回列表