
简介这份面向51单片机毕业设计与课程实践的火灾检测资料以STC89C52为核心结合电阻式烟雾、温度、火焰等传感器构建可实时采集烟雾浓度、可燃气浓度、温度与火焰信号并超阈值触发声光报警的智能报警系统。压缩包内仅含1个doc文件约1.27MB内容按毕业设计论文结构展开涵盖绪论、总体方案论证、硬件设计、最小系统、1602液晶显示、烟雾检测模块及软件程序思路等模块便于直接参考选题背景、系统框图和模块电路。资源已吸引104人学习适合需要完成单片机报警器设计、撰写论文或搭建实物原型的读者可从中获取传感器选型、阈值判断、声光报警与软硬件联调的整体方案并理解结构简单、成本低廉的公共场所火灾预警实现路径。1. 从一次误报到一次可靠报警火灾检测到底在测什么厨房里一锅开水的水蒸气能让离子式烟感直接跳报警梅雨季的机房又能被机柜排风烘到五十度触发第二类误报。这两个现场把「基于单片机的火灾检测设计」里最要命的一环摆到桌面上只测一个物理量、只比一个固定阈值抗干扰能力几乎为零。真实火灾会同时产生烟雾颗粒、温升、特征气体和火焰辐射把多个特征量交叉判断才能把误报压下去。这套方案适合做单片机课程设计、毕业设计和自建小型监测节点的人一颗 51 或 STC 单片机挂上烟雾、温度、火焰传感器跑一条带持续时间确认的判据再用声光报警和串口把状态送出去。硬件不复杂难点在传感器选型、阈值标定和去抖参数。2. 火灾检测的传感器选型与单片机资源分配选型决定后面固件好不好写。传感器输出是模拟量还是数字量、需要几根 IO、要不要 ADC、供电是 3.3V 还是 5V这四件事没想清楚画出来的板子大概率要飞线重做。2.1 四类火灾特征量与传感器对照火灾早期最值得测的量有四个烟雾颗粒浓度、环境温升速率、CO 等特征气体、火焰紫外/红外辐射。不同传感器对应的响应时间和适用范围差别很大组合使用比单独堆高精度更有效。特征量常用器件输出形式响应时间典型误报源烟雾MQ-2、MQ-135模拟电压10~30s水蒸气、油烟、酒精温度DS18B20、NTC 热敏数字单总线 / 电阻1~5s阳光直射、机柜排风火焰红外火焰探头数字电平1s白炽灯、电焊弧光特征气体MQ-7、MQ-9模拟电压30~60s烹饪燃气、汽车尾气经验做法是「烟雾 温度」做主体判据火焰探头做快速触发通道。火焰探头响应快但视野窄只对着一个方向放在角落基本测不到整个房间一般装在设备正前方一米内。2.2 51 单片机与 STM32 的资源对比标题里的「单片机」在课程设计里八成指 STC89C52RC 这类 51 内核器件但现在用 STM32 做的也不少。两者的取舍点在于 ADC、串口数量和中断响应。项目STC89C52RC51STM32F103C8T6工作电压5V3.3V内置 ADC无需外挂 ADC083212 位多通道定时器2 个 16 位4 个以上串口1 路 UART3 路 USART适合场景教学、单节点、成本敏感多传感器、要联网上报用 51 做的话ADC0832 是常见搭配两通道差分输入、8 位分辨率读 MQ-2 足够了。如果只测 DS18B20 这类数字传感器51 的 IO 资源完全够用不需要额外 ADC。用 STM32 的话直接开 ADC1 的规则通道配 DMA 循环采样省掉外挂芯片代价是要处理 3.3V 与 5V 传感器的电平匹配。2.3 最小系统与外设的引脚分配引脚分配先写死成宏后面改板子只改这一处。下面是一份 51 平台的典型分配ADC0832 走软件模拟时序DS18B20 占一根 IO蜂鸣器和 LED 各占一根。#include reg52.h /* ---- 传感器接口 ---- */ sbit ADC_CS P1^0; // ADC0832 片选 sbit ADC_CLK P1^1; // ADC0832 时钟 sbit ADC_DO P1^2; // ADC0832 数据输出 - 单片机输入 sbit ADC_DI P1^3; // ADC0832 数据输入 - 单片机输出 sbit DQ P3^7; // DS18B20 单总线外接 4.7k 上拉 sbit FLAME P3^2; // 火焰探头数字输出低电平有效 /* ---- 输出接口 ---- */ sbit BUZZER P2^0; // 蜂鸣器经三极管驱动 sbit LED_RED P2^1; // 报警指示 sbit LED_GRN P2^2; // 正常指示 /* ---- 阈值参数现场标定后改这里 ---- */ #define SMOKE_WARN 90 // MQ-2 数字化后的预警值 0~255 #define TEMP_WARN 55 // 摄氏度 #define CONFIRM_CNT 20 // 连续命中次数配合 100ms 周期等于 2s这段宏定义把「采样周期 × 确认次数」这组耦合参数集中在一处后面调灵敏度只动CONFIRM_CNT和阈值不用翻遍整个工程。FLAME走外部中断最省事火焰信号持续时间短靠主循环轮询容易漏掉。提示MQ-2 的加热丝功耗接近 150mA5V 供电下不要直接从单片机 IO 取电走独立支路并加 100μF 电解电容否则采样值会随加热丝的开关周期性抖动。3. 用 51 单片机跑通双判据报警的最小固件固件结构分三层底层驱动负责从传感器拿到干净的数字量判据层负责比较阈值和持续时间输出层负责蜂鸣器、LED 和串口。三层分开写调传感器的时候不用动报警逻辑。3.1 ADC0832 读 MQ-2 的软件时序ADC0832 是 SPI 类时序但只用一个数据脚分时复用配置阶段单片机写、转换阶段单片机读。时序里最容易错的是三个配置位的顺序和时钟极性的配合。#include intrins.h /* 读 ADC0832 单端通道 ch(0/1)返回 0~255 */ unsigned char adc0832_read(unsigned char ch) { unsigned char i, dat 0; ADC_CS 0; // 片选拉低开始一次转换 ADC_CLK 0; ADC_DI 1; // 起始位固定为 1 _nop_(); ADC_CLK 1; _nop_(); ADC_CLK 0; ADC_DI 1; // SGL/DIF 1单端输入模式 _nop_(); ADC_CLK 1; _nop_(); ADC_CLK 0; ADC_DI ch; // ODD/SIGN选通道 0 或 1 _nop_(); ADC_CLK 1; _nop_(); ADC_CLK 0; ADC_DI 1; // 释放数据线切换为输入 for (i 0; i 8; i) { // MSB first 读回 8 位结果 ADC_CLK 1; _nop_(); dat (dat 1) | ADC_DO; ADC_CLK 0; _nop_(); } ADC_CS 1; // 拉高片选结束转换 return dat; }三个配置位分别是起始位、单端/差分选择位和通道选择位任何一位顺序错位都会读出恒定的 0 或 255。_nop_()是为了在 11.0592MHz 晶振下保证时钟高电平宽度换到 24MHz 晶振要额外插入更多空指令或改用软件延时。3.2 DS18B20 温度读取与单总线上拉DS18B20 的关键是时隙宽度复位要拉低至少 480μs读写时隙都在 60μs 量级。晶振频率变了delay_us必须跟着重算。void delay_us(unsigned int us) { while (us--) ; } void ds18b20_reset(void) { DQ 0; delay_us(480); // 主机拉低 480us DQ 1; delay_us(60); // 释放总线等待存在脉冲 delay_us(420); // 等存在脉冲结束 } 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; delay_us(2); dat 1; } } /* 返回整数摄氏度12 位分辨率下按高字节取整 */ unsigned char ds18b20_read_temp(void) { unsigned char th; ds18b20_reset(); ds18b20_write_byte(0xCC); // SKIP ROM单器件在线时省去匹配 ds18b20_write_byte(0x44); // CONVERT T启动转换 delay_ms(800); // 12 位转换最长 750ms ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); // READ SCRATCHPAD ds18b20_read_byte(); // 低字节整数度用不上 th ds18b20_read_byte(); // 高字节 return th; }delay_ms(800)是阻塞等待会让主循环卡住近一秒。真要做得体面应该把「启动转换」和「读结果」拆到定时器状态机里两次间隔用定时器计数这也是 STC 单片机做多任务时的常规写法。0xCC跳过 ROM 匹配总线上只挂一颗时是标准做法挂多颗必须用0x33读 ROM 后逐颗匹配。3.3 主循环里的双判据与持续时间确认判据层的核心是计数器而不是瞬时比较。单次超阈值只给预警连续命中 N 次才升级为报警N × 采样周期就是确认时间。unsigned char cnt_smoke 0, cnt_temp 0; void fire_scan(void) /* 由 100ms 定时器中断调用 */ { unsigned char smoke adc0832_read(0); unsigned char temp ds18b20_read_temp(); if (smoke SMOKE_WARN) cnt_smoke; else cnt_smoke 0; if (temp TEMP_WARN ) cnt_temp; else cnt_temp 0; if (FLAME 0) { /* 火焰探头最快直接触发 */ alarm_on(); } else if (cnt_smoke CONFIRM_CNT || cnt_temp CONFIRM_CNT) { alarm_on(); /* 任一路持续命中确认火警 */ } else if (smoke SMOKE_WARN || temp TEMP_WARN) { pre_alarm(); /* 单次超标先预警不报警 */ } else { alarm_off(); LED_GRN 0; } }用「或」而不是「与」是刻意的真实火灾里烟雾先到、温度后到的情况很常见用与逻辑会明显拖慢响应。代价是误报概率上升所以用确认时间和传感器组合来补偿。else分支里必须把计数器清零漏掉这一句会出现「报警后无法复位」的经典 bug。3.4 定时器与串口上报的参数配置定时器 0 负责产生 100ms 节拍串口用 9600 波特率把状态送出去。51 的串口波特率依赖定时器 1两者不能共用同一个定时器。参数取值说明晶振11.0592MHz保证 9600 波特率无误差定时器 0 初值0x4C0050ms 溢出累计两次进入 fire_scan定时器 1 模式模式 28 位自动重装做波特率发生器TH10xFD9600bps 11.0592MHz串口模式模式 110 位异步收发void timer0_init(void) { TMOD 0xF0; TMOD | 0x01; // 定时器0模式116位 TH0 0x4C; // 50ms 11.0592MHz TL0 0x00; ET0 1; // 开定时器0中断 EA 1; TR0 1; // 启动 } void uart_send_byte(unsigned char dat) { SBUF dat; while (!TI); // 等发送完成 TI 0; // 软件清标志 }中断服务函数里只置一个标志位把fire_scan()放主循环执行避免在中断里跑 ADC 和单总线时序——这两者都对时间敏感被其他中断打断会直接读错。4. 火灾检测的阈值、确认时间与误报排查同一套代码在不同房间表现完全不同问题几乎都出在阈值和确认时间没按现场标定。4.1 烟雾与温度阈值的标定方法MQ-2 的输出随温湿度漂移出厂值不可直接使用。标定要在目标环境里做而不是在实验室桌面。步骤操作判据1上电预热 60s串口打印原始值读数稳定波动 ±52记录洁净空气下 10 分钟均值记为基准值 B3用少量烟雾靠近探头记录上升曲线观察从 B 涨到峰值的时间4取 B × 1.4 作为 SMOKE_WARN兼顾灵敏度与误报5温度阈值取环境最高温 15℃机房可取 55℃厨房需上调温度阈值设得太低是厨房场景误报的主因。灶台附近环境温度能到 45℃把阈值设成 50℃ 基本天天报警。这种情况更合理的做法是把温度通道的权重降低改成「温度持续超过 10℃ 且 30 分钟内单调上升」才计入判据。4.2 确认次数与采样周期的组合选择确认时间和采样周期是一组耦合参数单独调哪一个都得不到想要的效果。采样周期CONFIRM_CNT确认时间适用场景50ms201s火焰、燃气泄漏要求快100ms202s常规房间默认值100ms606s厨房、机房抗误报优先500ms4020s长周期值守省电调参时有个容易忽略的点确认时间越长火灾真正发生时的报警延迟越大。一般民用场景控制在 3s 以内比较合理超过 10s 就失去早期预警的意义了。4.3 报警不触发或常触发的排查路径下面这张表覆盖了实际调试中八成以上的故障。现象可能原因排查动作上电立即报警MQ-2 未预热输出阻抗未稳定加 60s 预热延时再使能判据烟雾值恒为 0 或 255ADC 时序错位或 DI/DO 接反用示波器看 CLK 与 DO核对三配置位温度恒为 85单总线无应答检查 4.7k 上拉与 DQ 焊接温度恒为 0复位时隙不足或被中断打断读温度期间临时关中断报警后无法复位计数器未清零检查else分支串口乱码波特率误差或晶振非 11.0592MHz换晶振核对 TH1 初值数值周期性抖动蜂鸣器电流冲击供电蜂鸣器单独供电加去耦电容注意单片机复位后 IO 默认高电平蜂鸣器如果是低电平触发上电瞬间会响一声。把驱动三极管的基极接成上拉结构或者复位后立刻写高电平。5. 让火灾检测节点可维护参数存 EEPROM 与串口自检设备装到墙上以后再想改阈值就得拆下来重烧固件维护成本很高。把阈值和确认次数放进外部 EEPROM现场通过串口下发指令修改节点才真正具备可维护性。5.1 用 AT24C02 保存阈值参数AT24C02 是 51 平台上最常见的 I²C EEPROM2Kbit 容量存几个阈值绰绰有余。#define CFG_ADDR_SMOKE 0x00 #define CFG_ADDR_TEMP 0x01 #define CFG_ADDR_CNT 0x02 void cfg_save(unsigned char smoke_th, unsigned char temp_th, unsigned char confirm_cnt) { at24c02_write(CFG_ADDR_SMOKE, smoke_th); delay_ms(10); // 页写周期典型 5ms留足余量 at24c02_write(CFG_ADDR_TEMP, temp_th); delay_ms(10); at24c02_write(CFG_ADDR_CNT, confirm_cnt); delay_ms(10); } void cfg_load(unsigned char *smoke_th, unsigned char *temp_th, unsigned char *confirm_cnt) { *smoke_th at24c02_read(CFG_ADDR_SMOKE); *temp_th at24c02_read(CFG_ADDR_TEMP); *confirm_cnt at24c02_read(CFG_ADDR_CNT); /* 首次上电 EEPROM 为 0xFF回落到默认值 */ if (*smoke_th 0xFF) *smoke_th 90; if (*temp_th 0xFF) *temp_th 55; if (*confirm_cnt 0xFF) *confirm_cnt 20; }每次写字节之间留 10ms 是必须的AT24C02 的页写周期最长 5ms连续写不等会丢数据。加载时做 0xFF 兜底判断能避免首次上电读到空片导致阈值变成 255、设备彻底不报警。5.2 串口自检帧与现场阈值下发用一条简单的文本协议就够了上位机发S90,T55,N20节点回一帧当前状态方便确认。字段格式示例含义帧头$$起始标识烟雾值SS88当前 ADC 读数 0~255温度值TT31当前摄氏度状态RR00 正常 / 1 预警 / 2 报警解析时按字符逐位判断遇到$开始缓存遇到换行结束并解析中间不做阻塞等待。这样主循环不会被串口拖住火灾判据的 100ms 节拍不会因为收到一帧数据而抖动。现场调试时把这一帧接进串口助手既能看实时值也能验证阈值改完是否生效——比起反复烧录这是排查「为什么一直误报」最快的路径。本文还有配套的精品资源点击获取