
简介这是一份基于AT89C51单片机的温度烟雾火灾报警系统设计方案面向单片机初学者与电子设计竞赛参赛者。系统具备火情探测、灯光报警、蜂鸣器报警和阈值调节等核心功能。烟雾采集选用MQ-2传感器输出模拟信号经ADC0832转换后送单片机温度采集用DS18B20通过单总线与单片机相连。显示设备采用数码管实时显示烟雾浓度值和温度值。报警模块由蜂鸣器和LED组成检测元素超标即鸣叫。此外系统提供三个按键调节报警阈值并增加一个手动报警按键。压缩包约4.93MB包含设计相关文件便于对照原理图与代码理解流程。目前已有354人学习浏览覆盖传感器选型、数据转换、显示与报警联动等关键环节对掌握单片机火灾监测应用有参考价值。1. AT89C51 双路传感器的火警数据链路解析当年实验室的防火监控需要在可见烟雾出现前就预警温度探头和烟雾传感器往往很难在同一个 MCU 上有序工作。MQ-2 的模拟输出需要外接 ADC而 DS18B20 又是一条完全不同的单总线协议再加上数码管轮流刷新很多团队在调试时节拍会乱——显示一卡温度跳变报警又误触发。这套系统把 AT89C51 作为主控让 ADC0832 承担烟雾信号的模数转换DS18B20 直接上报温度数码管显示实时状态蜂鸣器和 LED 负责多级警报。在入门 51 平台上这种组合能充分利用端口资源是性价比最高的经典做法。对于正在做课设、嵌入式入门或者是想把实物完整跑起来的人来说这套线路覆盖了传感器采集、总线时序、按键阈值校准和报警联动四个核心环节值得从实物可复现的角度逐步拆解。2. 硬件连接AT89C51 怎么同时挂上 ADC0832 和 DS18B20在入门 51 上同时使用 ADC0832 和 DS18B20 最大的问题是端口资源占用。多数人设计时会先拨 P0 做数码管段选P2 做位选再把 P1 口的一部分划给传感器。常见做法是 P1.0 接 MQ-2 的模拟输出到 ADC0832 的 CH0P1.1 接 DS18B20 的数据线另外 P1.3~P1.5 接三个按键P2.7 接蜂鸣器P2.0、P2.1 接 LED。这种安排保证了 ADC0832 时钟和数据线不会与数码管动态扫描互相干扰。2.1 MQ-2 的模拟输出与 ADC0832 的采样电路MQ-2 的敏感体是二氧化锡还原性气体浓度升高时导电性变强输出到负载电阻的电压随之抬高。这个输出电压一般在 0~5V 之间摆动普通 51 内部没有 ADC 无法直接读取所以要把信号送到 ADC0832 的 CH0 通道。ADC0832 是 8 位串行逐次逼近型 ADC256 级分辨率对 MQ-2 这种趋势性信号完全够用烟雾浓度从 200ppm 升到 1000ppm 时电压差异远大于一个 LSB。转换时钟由单片机机器周期分频得到一般取 250kHz 上下比较稳妥。实际接线时有两个细节容易出问题。第一MQ-2 的 DO 引脚是内部比较器输出的数字信号很多设计者误把 DO 直接接单片机结果烟雾浓度线性区域表现很差正确做法是取 AO 模拟输出接 ADC0832 的 CH0。第二ADC0832 的 CS 要拉低才能启动转换片选若和数码管位选共用一个端口扫描瞬间会产生采集噪声改到独立 IO 后基本能消除。下面给出一份建议接线信号MCU 端口说明ADC0832.DI/DOP1.0数据输入输出复用脚ADC0832.CLKP1.1串行时钟输入ADC0832.CSP1.2片选低电平有效DS18B20.DQP3.7单总线数据线需接 4.7k 上拉蜂鸣器P2.7经 NPN 三极管驱动LED1烟雾报警P2.0烟雾超阈值点亮LED2温度报警P2.1温度超阈值点亮按键 S1~S3P1.3~P1.5阈值加减与确认表里把 ADC0832 和 DS18B20 的端口彻底分开数码管又占用 P0 和 P2 的高位这种分配在后续调试时可以避免总线驱动电平竞争。2.2 DS18B20 单总线接法在强电环境下要注意什么DS18B20 的一线总线协议全靠单根线上的时序高低电平传递上拉电阻必须选 4.7k。报警器周围如果存在继电器或其他感性负载模块线上毛刺很容易把复位脉冲误判成数据帧所以布局里要尽量把传感器线缩短并在 VCC 和 GND 之间加一个 0.1uF 去耦电容。我做过一次实物调试时踩过这个坑扎带把 DS18B20 数据线与继电器驱动线绑在一起断电瞬间温度读数直接变成 -55 或 125 两个极端值。而 AT89C51 的 P3.7 上电默认状态是准双向口读引脚前要先置 1这个特性在代码里必须时刻记住后面时序部分会再展开。如果打板而不是洞洞板飞线建议把 DS18B20 用 3PIN 排针引出地线和信号线间距至少 2mm。传感器离主板超过 30cm 时屏蔽线情况下总线电容应控制在 200pF 以内否则上升沿变缓程序可能一直卡在转换等待。注意DS18B20 的 VCC 不要从 MQ-2 的加热回路取电加热丝瞬间压降会导致温度芯片复位。2.3 显示、按键和报警执行电路参数推荐数码管选用 4 位共阴即可P0 口经 200Ω 排阻限流做段选P2.3~P2.6 做位选通过 8550 三极管驱动公共端。蜂鸣器选择 5V 有源蜂鸣器经 S8050 接到 P2.7严禁直接接 IO 口AT89C51 拉电流上限约 20mA直接驱动会拉低端口电平甚至烧毁引脚。按键部分若用板上微动直接接 10k 上拉并配 100nF 电容滤波程序内再补一次消抖。这套设计的供电能力完全依托 51 基本盘展开P0 段选 P2 位选 P1 传感器输入各外设之间没有共用同一组总线的尴尬。若使用外部电源适配器建议 7805 输入侧留一个 470uF 电解电容。MQ-2 加热丝启动瞬间电流可达 150mA 以上电压跌落会造成单片机复位这是实物调试中非常隐蔽的偶发故障。3. 固件实现AD 值、温度值到稳定显示的转换过程硬件链路搭建完成后固件核心分为三块ADC0832 的时序读取、DS18B20 的时序读取、数码管动态扫描刷新。三者放在同一个主循环里时要特别注意时序占用因为 DS18B20 转换期间要求数据线保持稳定而 ADC0832 的串行时钟又不能被中断拖得太久。以下是我在 12MHz 晶振下的实现思路。3.1 ADC0832 驱动与连续采样消抖#include reg52.h #include intrins.h sbit ADC_CS P1^2; sbit ADC_CLK P1^1; sbit ADC_DIO P1^0; /* 读取 ADC0832 指定通道的 8 位转换结果 */ unsigned char read_adc0832(unsigned char channel) { unsigned char i, dat1 0, dat2 0; ADC_CS 1; ADC_CLK 0; ADC_CS 0; /* 片选拉低启动转换 */ ADC_DIO 1; ADC_CLK 1; _nop_(); ADC_CLK 0; /* 第一个下降沿写入起始位 */ ADC_DIO 1; ADC_CLK 1; _nop_(); ADC_CLK 0; /* 第二个下降沿写入 SGL/DIFF 位 */ ADC_DIO channel; /* 0 选 CH01 选 CH1 */ ADC_CLK 1; _nop_(); ADC_CLK 0; /* 第三个下降沿写入通道号 */ ADC_DIO 1; /* 释放数据线准备读取 */ ADC_CLK 1; _nop_(); ADC_CLK 0; for (i 0; i 8; i) { ADC_CLK 1; ADC_CLK 0; if (ADC_DIO) dat1 | (0x80 i); /* 高位在前收集数据 */ } for (i 0; i 8; i) /* 读取后半段校验数据 */ { dat2 1; if (ADC_DIO) dat2 | 0x80; ADC_CLK 1; ADC_CLK 0; } ADC_CS 1; return dat1; }这段代码的核心时序是CS 拉低后连续送四个时钟依次写入起始位、通道选择位、单端输入标志位随后方向从 DI 切换为 DO在后续 8 个时钟的下降沿逐位读出结果。dat1 保存第一次读到的 8 位数据dat2 保存第二组读出的数据两者若相差超过 1说明通信线上有干扰我一般会丢弃本次结果并重新触发采样。ADC0832 是 8 位精度返回 0~255 对应 0~5V 电压程序里做阈值比较时直接使用这个原始 AD 值即可。配合 MQ-2 使用有个常见问题传感器上电前 30 秒输出电压会先冲高到接近 5V然后缓慢回落到静态值。如果单片机上电后立刻采样容易误判为高浓度烟雾触发报警。解决办法是上电后先延时 60 秒再进入采样循环或者连续读 10 次去掉最大最小值取平均。3.2 DS18B20 单总线时序驱动sbit DQ P3^7; void ds18b20_reset(void) { DQ 0; delay_us(480); /* 主机拉低 480us 以上产生复位脉冲 */ DQ 1; delay_us(60); /* 释放总线等待器件应答 */ } unsigned char ds18b20_read_bit(void) { unsigned char bit; DQ 0; _nop_(); /* 拉低 1us 后释放 */ DQ 1; _nop_(); bit DQ; /* 在 15us 窗口内采样 */ delay_us(50); return bit; } unsigned char ds18b20_read_byte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; if (ds18b20_read_bit()) dat | 0x80; } return dat; } void ds18b20_write_byte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DQ 0; _nop_(); DQ dat 0x01; /* 数据位从低位开始写入 */ delay_us(60); DQ 1; dat 1; } } unsigned char get_temperature(void) { unsigned char low, high; ds18b20_reset(); ds18b20_write_byte(0xCC); /* 跳过 ROM单点测温 */ ds18b20_write_byte(0x44); /* 启动温度转换 */ while (!DQ); /* 转换完成 DQ 被释放拉高 */ ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); /* 读取暂存器数据 */ low ds18b20_read_byte(); high ds18b20_read_byte(); /* 12 位分辨率下拼接整数部分忽略小数位 */ return (high 4) | (low 4); }DS18B20 的通信分为初始化、ROM 命令、功能命令三个层次。rst后通过拉低 480us 产生复位脉冲器件会回一个 60~240us 的低电平应答接着写 0xCC 跳过 ROM 寻址0x44 启动转换等待 DQ 引脚释放后重新复位再发 0xBE 读取暂存器前两个字节。返回的是带符号补码比如 0x0032 代表 50°C0xFFCE 代表 -50°C判断温度超限时直接与阈值比较。提示DS18B20 的延时函数不要用带中断的 delay建议用_nop_()组合或关闭中断的定时查询否则中断服务函数会破坏单总线时序。3.3 数码管 4 位动态扫描与数据缓冲unsigned char code seg_code[10] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F }; unsigned char disp_buf[4]; unsigned char disp_pos 0; void timer0_isr(void) interrupt 1 { TH0 0xFC; TL0 0x18; /* 12MHz 下定时 2ms */ P0 0x00; /* 消隐防止拖影 */ P2 0x0F; P0 seg_code[disp_buf[disp_pos]]; P2 | (0x10 disp_pos); /* 选中当前位 */ if (disp_pos 4) disp_pos 0; }显示部分放在定时器中断里每 2ms 切换一位数码管四位数码管 8ms 完成一轮刷新人眼看不到闪烁。主循环只需维护 disp_buf 数组显示温度时disp_buf[0] 和 disp_buf[1] 放温度十位和个位disp_buf[2] 放 0x40 作为分隔符号显示 AD 值时把 0~255 映射成两位十进制。P2 高四位用作位选切换前先 P00 消隐。这样做的收益是显示刷新与采样时序解耦DS18B20 转换期间定时器中断照常发生但因为中断服务函数只有显示逻辑不会碰到 DQ 引脚所以不会破坏单总线时序。3.4 按键消抖与长按连续调阈值按键 S1 加一、S2 减一、S3 确认是这类系统最常见的交互方式。阈值初始化时烟雾阈值设为 150温度阈值设为 50°C。按键消抖不做软件延时死等而是利用主循环扫描周期连续两次读到同一电平才认为稳定10ms 后再进入分支。长按 S1 或 S2 超过 60 次扫描后把计数器阈值设为 3实现连续快速加减不需要一次次按。主循环骨架如下while (1) { smoke_val read_adc0832(0); temp_val get_temperature(); /* ADC 值在 0~255 之间与阈值直接比较 */ if (smoke_val smoke_limit) led_smoke_on(); if (temp_val temp_limit) led_temp_on(); if (smoke_val smoke_limit || temp_val temp_limit) buzzer_on(); else buzzer_off(); scan_key(); /* 消抖 长按检测 */ update_display_mode(); /* 3 秒交替显示 */ }主循环中不建议把 ADC 和 DS18B20 采样放进中断。尤其是 DS18B20 的 while 等待一旦中断优先级配置不当很可能导致传感器应答位丢失。更好的做法是把采样放在主循环顺序执行数码管刷新放在定时器中断里两者互不打扰。4. 阈值调节与手动报警从按键到报警状态的联动阈值按键和手动报警按键是这套设计里最能体现交互细节的部分。如果按键逻辑写得太简单会出现设置状态下误触报警、手动报警无法撤销等问题。这一章把这些交互边界逐一理清。4.1 阈值设置的进入、修改与保存阈值调整不应在正常监控模式下直接响应 S1/S2否则现场人员擦拭面板就会误改参数。常见做法是同时按住 S3 超过 1 秒进入阈值设置模式数码管第一位显示 S 代表烟雾阈值后两位显示当前 AD 阈值再按 S3 切换到 T 代表温度阈值S1/S2 修改数值长按 S3 退出并写入保存。如果用 24C02 EEPROM 保存阈值写操作约 5ms期间按键扫描会被阻塞所以保存动作应在退出设置时统一执行而不是每按一次 S1 写一次 EEPROM。阈值修改后要检查合法性烟雾阈值范围限制在 30~250温度阈值限制在 30~85°C。低于下限会导致开机瞬间误报警高于上限则报警功能形同虚设。不少课设同学把阈值随意设在 0 或 255恰好踩中 MQ-2 上电脉冲的峰值这种问题在初版代码里非常常见。4.2 手动报警与自动报警的优先级设计手动报警按钮的存在意义是系统阈值未超标但人已闻到焦味时提供一个强制的报警入口。单片机读取到该按键拉低后应立即进入独立报警分支不依赖任何阈值判断。S3 必须长按 2 秒才能消除避免误触或小孩乱按后无法复位。报警状态的真值表如下场景蜂鸣器LED 状态温度超限鸣叫红色 LED 亮烟雾超限鸣叫黄色 LED 亮温度和烟雾同时超限鸣叫双 LED 亮手动报警鸣叫不依赖阈值双 LED 闪烁无异常关闭绿色 LED 慢闪手动报警分支的优先级最高自动报警判断在其之后执行。消除手动报警的唯一入口是 S3 长按 2 秒期间数码管显示H提示手动报警状态。这个设计比简单的一键消除更符合实际安防产品习惯——消除操作需要刻意且明确。4.3 显示模式自动轮换与缓冲区写入显示内容分为温度模式和烟雾模式主循环通过一个 3 秒计数器切换。在阈值设置界面下显示缓冲区强制写入设置值而非实时采样值避免用户调阈值时看到数字不停跳动。计数器声明时必须加 volatile否则 keil 优化后可能出现判断条件被内联掉导致显示永不切换的情况。实际实现中我用一个 16 位计数器每次主循环自增达到阈值就取反显示标志并清零。显示缓冲区只在主循环中写入定时器中断只读取这样不会出现数据竞争。5. 实物调试的关键动作从时序到阈值漂移的排错顺序5.1 上电先量静态电平再抓总线时序拿到实物板子后不要急着烧录程序。先测三组电压DS18B20 的 DQ 引脚静态电平应接近 5V低于 2V 说明传感器未接好或已经短路MQ-2 的 AO 引脚静态电压应在 0.3~1V 之间刚上电会偏高等 60 秒再量蜂鸣器驱动三极管基极电压应为 0V 或用万用表二极管档确认无击穿。之后用示波器观察 DQ 线上的复位脉冲正常能看到 480us 低电平和紧随的 80us 应答脉冲。没有示波器时在 ds18b20_reset 后读 DQ读到 1 直接返回 0xFF 错误码通过串口区分通信断裂与传感器断开。5.2 MQ-2 预热漂移与基准校准MQ-2 数据手册给出的预热稳定时间是 24 小时实际做实物时不可能等那么久。我一般在上电 2 分钟后连续采样 10 次取平均作为基准值比如静态 AD 值 45就把报警阈值设为基准值加固定增量 80也就是 125。增量的大小按环境调整厨房油烟大放 100机房要求灵敏放 60。基准值每 30 秒做一次滑动平均更新以消除凌晨和中午温湿度不同带来的基线漂移。5.3 三条高频故障路径和对应解法整理实物调试中最常见的问题。第一ADC0832 的 CS 与数码管位选共用引脚扫描瞬间转换结果跳变表现为烟雾值每隔几百毫秒出现一个尖峰解决办法是把 CS 挪到独立空闲 IO。第二DS18B20 数据线过长或走线宽带不足温度读数隔几次跳变一次这是上升沿变缓导致采样窗口错位缩短线长或换双绞线可解决。第三蜂鸣器启动瞬间电源跌落导致单片机复位现象是报警一响数码管熄灭又重启检查 7805 输出端是否放置 100uF 电解电容没有的话补上并把蜂鸣器供电单独加 10uF 去耦。最后做一次完整的报警链路验证用打火机气体靠近 MQ-23~5 秒内蜂鸣器应鸣叫黄色 LED 点亮用电烙铁靠近 DS18B20 加热温度超过设定值时红色 LED 点亮蜂鸣器再次鸣叫按下手动报警键无论当前采样值高低都应立即进入报警状态。整个过程中数码管上的 ADC 值和温度值实时变化这正是这套基于 AT89C51 的设计最直接的验收标准。本文还有配套的精品资源点击获取