简介:基于单片机的火灾预警系统设计与实现是一份完整的本科毕业设计文档,面向电子信息、自动化及相关专业学生,用于学习单片机火灾报警系统从方案设计到软硬件实现的完整流程。文档以AT89C51为主控芯片,搭配MQ-2烟雾传感器与DS18B20温度传感器,介绍了多传感器数据采集、非线性与线性时间序列分析预测算法,以及液晶显示、声光报警等功能模块的设计思路,覆盖硬件电路选型与布置、控制器单元设计、软件编程与系统调试等关键环节。资源包含1个docx文档,压缩包大小2.23MB,内容紧凑,便于阅读和打印,适合作为课程设计或毕业设计的参考模板。已有111人学习下载。文档不仅给出了整体系统框架与具体功能实现,还详细分析了操作人员管理、探测器管理、预报警、火警处理等模块,并对系统的性价比、结构简单、使用方便等特点作了说明,能够帮助读者快速理解火灾预警系统的设计方法。对于后续开展类似项目研发、完善报警算法或撰写论文,都具有较好的参考价值。
1. 从传感器到声光报警:火灾预警系统到底在检测什么
火灾预警系统听起来是个大工程,拆到单片机这一层,核心其实只有三件事:把烟雾浓度、温度这类物理量变成电信号,用一个判据决定“该不该响”,再驱动蜂鸣器、LED 和继电器去执行报警与联动。很多课程设计和毕业设计卡住的地方不在硬件连接,而在判据——传感器原始值跳来跳去,直接和固定阈值比较必然误报。以 51 内核为中心的方案之所以在单片机火灾预警系统设计里最常见,是因为 STC89C52 这类芯片便宜、外设资料全,哪怕不带 ADC 也能用电压比较器或外接 PCF8591 把模拟量读进来。这篇博文按“硬件选型 → 检测原理 → 软件状态机 → 阈值标定 → 无线升级”的顺序,把一套能跑通、能演示、能答辩的系统讲清楚,适合做课程设计或入门嵌入式预警产品的读者。
2. 主控、传感器与执行机构的选型逻辑
2.1 为什么 51 内核仍是这类设计的主流选择
选主控先问需求:要采集几路模拟量、要驱动什么外设、要不要联网。对于火灾预警,核心外设是烟雾传感器、温度传感器、蜂鸣器、LED 和显示模块,实时性和运算量要求都不高,STC89C52、STC12C5608 这类 51 内核芯片完全够用。它们的优势在 I/O 充足、5V 供电和传感器模块直接兼容,以及 Proteus 仿真资料丰富,做课程设计的人能快速验证逻辑。
需要注意一个细节:AT89C52 原生不带 ADC,而 MQ-2 烟雾传感器输出的是模拟电压。这个矛盾的常见解法有三种,我在实际项目里按成本从低到高排序:
- LM393 电压比较器方案:把 MQ-2 的输出和一个用电位器设定的阈值比较,输出高低电平直接进单片机 I/O。电路最简单,缺点是阈值只能在硬件上调,改判据要动螺丝刀。
- 外接 PCF8591 芯片:用 I2C 总线扩展 4 路 ADC,价格两块左右,代码也不复杂,能把烟雾浓度做成数值。
- 直接换带 ADC 的型号:比如 STC15W408AS 内部集成 10 位 ADC,少一块外围芯片,但很多人的开发板是 89C52,所以这个方案往往出现在自己画板时。
我一般推荐第二种。它保留了 89C52 的通用性,又能把“数据采集→数值处理→阈值判断”这个完整链路在代码里体现出来,答辩时也更有讲头。
2.2 火灾预警系统的模块划分和供电设计
系统按功能拆成六个单元:传感采集、主控、报警执行、显示与交互、电源。各模块选型如下表。
| 模块 | 推荐器件 | 用途 | 关键参数 |
|---|---|---|---|
| 烟雾浓度检测 | MQ-2 半导体传感器 | 检测可燃气体和烟雾 | 加热电压 5V,模拟输出 0~5V 或 TTL 电平 |
| 温度检测 | DS18B20 | 检测环境温度,参与联动判据 | 单总线,-55~125℃,12 位分辨率 |
| 主控 | STC89C52 / STC12C5608 | 数据采集、判定、报警控制 | 5V 供电,双 16 位定时器 |
| 模拟量采集 | PCF8591 | 将 MQ-2 模拟电压转为数字量 | 4 路 ADC,8 位精度,I2C 接口 |
| 声光报警 | 有源蜂鸣器、LED | 现场声光提示 | 蜂鸣器工作电流约 30mA,需三极管驱动 |
| 显示 | LCD1602 | 显示烟雾值、温度、报警状态 | 4/8 位并口,5V 供电 |
| 联动执行 | 5V 继电器 | 驱动排风扇或电磁阀 | 继电器线圈电流约 70mA,需三极管驱动 |
供电是新手最容易翻车的地方。单片机、PCF8591 和传感器都要 5V,而继电器线圈瞬间启动电流可能拉到上百毫安,如果全部从同一个 LDO 取电,电压跌落会导致单片机复位。常见做法是用 USB 5V 输入,经过 10uF 电解电容和 104 陶瓷电容并联退耦后给整个系统供电,继电器单独接电源正端,由 I/O 通过三极管控制通断,避免线圈反峰电压串到主控电源线上。
2.3 电平匹配和驱动能力:一个不能跳过的细节
51 芯片的 P0 口是开漏输出,外部必须接 4.7k~10k 上拉电阻才能输出高电平,P1~P3 内部有上拉但驱动能力有限。驱动蜂鸣器和继电器时,绝对不能用 I/O 直接串负载,因为 I/O 输出电流通常只有 10~20mA,带不动线圈,而且感性负载关断时会产生反向电动势,容易打坏引脚。标准接法是三极管或 ULN2003 做驱动级。
+5V | 继电器线圈 | P2.0---[1k]---C B极 | NPN 8050 | E极---GND这个电路里基极串 1k 限流电阻,I/O 输出高电平导通,继电器吸合;输出低电平时截止。线圈两端建议反向并联一个 1N4007 二极管,方向是负极接 +5V 正极接集电极,否则关断瞬间的反峰电压最高能到几十伏。蜂鸣器同理,有源蜂鸣器是正极接电源、负极接三极管集电极,这样单片机只用提供一个开关信号。
3. 传感器接口电路与信号调理:从 MQ-2 电压到可比较的 ADC 数值
3.1 MQ-2 的检测原理和分压电路计算
MQ-2 属于半导体气敏传感器,内部有一个加热电阻和一块二氧化锡气敏材料。加热电阻通电后让敏感材料处于高温状态,空气中还原性气体浓度升高时,材料表面电导率上升,等效电阻 Rs 下降。传感器模块上的模拟输出本质是一个分压电路:
Vcc(5V) ---- Rs(传感器) ---- A点 ---- RL(负载电阻) ---- GND | 模拟输出A 点电压 Vout = Vcc × RL / (Rs + RL)。洁净空气下 Rs 很大,Vout 很低;烟雾浓度升高,Rs 下降,Vout 上升。注意这个关系不是线性的,MQ-2 的 Rs 和浓度在对数坐标上才接近直线,所以代码里如果要换算成 ppm 需要查手册里的灵敏度特性曲线和做指数拟合。对预警系统来说,绝大多数应用不需要精确浓度,只需要一个“趋势 + 阈值”判据,所以直接用 ADC 数值做比较即可,这也是大多数单片机火灾预警设计里的实际做法。
3.2 PCF8591 的接线方式和 I2C 时序要点
PCF8591 是 8 位 ADC,I2C 地址由 A0/A1/A2 引脚决定,默认全接地时地址是 0x48。和 STC89C52 连接时,SCL 接 P2.1,SDA 接 P2.2,两根线都要接 4.7k 上拉电阻到 VCC。MQ-2 的模拟输出 AOUT 接 PCF8591 的 AIN0 通道。
读取时序上有一个常被忽略的控制字问题:I2C 写地址后传输的第一个字节是控制字,默认值 0x00 表示允许模拟输出、选 AIN0 单端输入,但如果上一轮程序改了控制字,下一轮读之前必须重新写一遍,否则会读错通道。我在调试时遇到过数据一直是 0 的情况,就是这个原因。代码里把配置和读取分成两个函数能避免这类隐性问题。
// I2C 起始信号 void I2C_Start(void) { SDA = 1; SCL = 1; SDA = 0; SCL = 0; } // 从 PCF8591 的 AIN0 通道读取 8 位数据 unsigned char PCF8591_Read_AIN0(void) { unsigned char value; I2C_Start(); I2C_SendByte(0x48 << 1); // 器件地址 + 写位 I2C_SendByte(0x40); // 控制字:使能模拟输出,选 AIN0 单端 I2C_Start(); I2C_SendByte((0x48 << 1) | 1); // 器件地址 + 读位 value = I2C_RecvByte(); I2C_Stop(); return value; }逻辑说明:先发一个 Start 信号,再发“地址 + 写位”通知 PCF8591 接下来要写控制字;控制字 0x40 的含义是 bit6 置 1 使能模拟输出,bit0~bit1 为 00 选中 AIN0 通道。写完控制字后再次发 Start 和“地址 + 读位”,此时从机在 SCL 时钟驱动下把转换结果放在 SDA 上,主机接收一个字节后发 Stop。整个过程主时钟频率不要超过 100kHz,51 直接用软件延时模拟即可。
3.3 温度传感器 DS18B20 的接入与寄生供电问题
DS18B20 适合做联动判据的第二路输入,它的特点是严格遵循单总线协议,时序窗口要求比较高。我在代码里一般把它的延时函数写成循环等待,并且调好晶振后就不再改动。硬件上,数据引脚要接一个 4.7k 上拉电阻到 VCC,这是保证总线空闲为高电平的前提。
有一个容易踩的坑是寄生供电模式:DS18B20 只有两根线(GND 和数据)时靠数据线上的高电平给内部电容充电,转换温度时会从总线汲取瞬时大电流,如果总线上拉电阻太大或线太长,转换结果会读出 85℃(上电默认值),代码逻辑上又不好排查。我自己做板时都直接接 VCC 和 GND 三根线,避开这个坑。温度读取代码使用经典的单总线时序,初始化时先拉低总线 480us 再释放,等待 60~240us 的应答脉冲;写 1 时拉低 1~15us 后拉高,写 0 时拉低 60~120us;读时隙拉低 1us 后释放,在 15us 内采样引脚电平。
4. 软件状态机与采样滤波:报警不误报的关键实现
4.1 主程序结构:轮询加定时中断的例行做法
火灾预警软件不需要上实时操作系统,前台轮询加一个定时中断就够了。主循环负责读传感器、跑判定状态机、刷新 LCD,定时器 0 每 200ms 中断一次,给状态机提供一个节拍基准。这种架构的好处是逻辑直观,不容易出现资源竞争。
void Timer0_ISR(void) interrupt 1 { static unsigned char tick; TH0 = 0x3C; // 重装初值, 50ms @12MHz TL0 = 0xB0; tick++; if (tick >= 4) { // 累计 200ms flag_200ms = 1; tick = 0; } } void main(void) { Timer0_Init(); // 定时器0, 模式1, 16位 LCD1602_Init(); display_string(0, 0, "FIRE ALARM"); while (1) { if (flag_200ms) { flag_200ms = 0; smoke_adc_raw = PCF8591_Read_AIN0(); temperature = DS18B20_ReadTemp(); process_data(); // 滤波、判据、状态机 update_display(); // LCD刷新 } } }这里没有把所有逻辑都塞进中断,只在中断里置一个标志位。标志位放在主循环,可以避免在中断里调用 I2C 读数和单总线时序这种耗时操作,否则中断时间过长会破坏单总线的时序窗口。参数上,200ms 的采样间隔对烟雾扩散速度来说已经足够,太快反而让后续数字滤波难以去除尖峰。
4.2 滑动平均滤波与连续触发消抖:两道滤网的作用不同
烟雾传感器的原始输出波动很大,集成式传感器模块内部虽然有比较器和电容滤波,模拟输出仍带有环境扰动。软件里我用两层处理:一层是滑动平均,削平随机噪声;一层是连续触发计数,防止瞬时尖峰直接触发火警。
#define FILTER_WINDOW 10 #define TRIGGER_COUNT 5 #define SMOKE_THRESHOLD 150 #define TEMP_THRESHOLD 50 unsigned char adc_buffer[FILTER_WINDOW]; unsigned char buffer_index = 0; unsigned char trigger_counter = 0; bit alarm_flag = 0; unsigned int get_smooth_value(void) { unsigned int sum = 0; unsigned char i; adc_buffer[buffer_index] = smoke_adc_raw; buffer_index = (buffer_index + 1) % FILTER_WINDOW; for (i = 0; i < FILTER_WINDOW; i++) { sum += adc_buffer[i]; } return sum / FILTER_WINDOW; } void fire_detect(void) { unsigned int smooth; unsigned int temp; smooth = get_smooth_value(); temp = temperature; if (smooth > SMOKE_THRESHOLD && temp > TEMP_THRESHOLD) { trigger_counter++; if (trigger_counter >= TRIGGER_COUNT) { alarm_flag = 1; } } else { trigger_counter = 0; } }这段代码的关键参数在宏定义里:窗口 10 次、连续 5 次触发。窗口越长平滑效果越强,但真实烟雾到达时的响应也会变慢;连续触发次数的作用是剔除偶发尖峰。这里的阈值 150 是针对 8 位 ADC(0~255 范围)的示例值,实际值必须根据传感器在洁净环境下的基线来定,不能照抄。注意 get_smooth_value 里用了取余运算,在 51 上取余会消耗较多指令周期,更高效的写法是让 buffer_index 到上限时归零,再配合一个计数变量累加和,这一步留给读者自己优化。
4.3 状态机:待警、预警、报警三态比简单 if 更可控
不写状态机直接 if 判断阈值也能跑,但会出现一个问题:报警后蜂鸣器需要持续响 30 秒,期间如果烟雾浓度暂时低于阈值,主循环又把它关了,现场听到的就是断断续续的响声。更规范的做法是把系统状态分为 STANDBY(待警)、PRE_ALARM(预警)、ALARM(报警)三态。
- STANDBY:滑动平均值低于阈值,蜂鸣器关,LED 慢闪表示系统正常。
- PRE_ALARM:连续 3 次采样超阈值,蜂鸣器短鸣提示“可疑”,LED 加快闪烁。
- ALARM:从 PRE_ALARM 升级,或烟雾和温度同时超阈值,蜂鸣器长鸣,继电器吸合启动排风设备。此状态持续 30 秒,期间即使浓度下降也不立即撤销,防止抖动。
状态迁移只发生在主循环的判定函数里,每次执行时读取当前状态和采样结果决定下一个状态。这套逻辑比散落的 if/else 好排查,答题时也能清楚讲“为什么要消抖,为什么要状态保持”。状态保持时长用一个递减计数器实现,就在定时器中断里 tick 的基础上去累加秒数。
5. 阈值标定的三次实测法与防误报调参
5.1 先测基线再设阈值:直接抄示例值的后果
网上很多例程里写成if(smoke > 150) alarm,这个数字在不同传感器、不同供电电压下的含义完全不同。MQ-2 模块上的灵敏度电位器改变的是比较器参考电压,不影响模拟输出;但模拟输出本身的基线电压受 VCC 波动影响,5V 电源有 0.3V 纹波时 ADC 读数就能差十几个数。所以必须实测环境基线,步骤如下:
- 给系统上电,让 MQ-2 预热至少 30 分钟,因为敏感材料需要加热到工作温度才能稳定。
- 保持周围环境通风良好且没有烟雾,连续读取 50 次 ADC 原始值,求平均。
- 记录平均值和最大值,阈值初始值设为“平均值 + 最大波动幅度的 2 倍”。
- 用真实烟雾源测试,如果触发太灵敏就增加阈值,太迟钝就降低阈值,每次调整幅度控制在 5% 以内。
实际测量中我常用的模拟烟雾源是几根点燃的香烟放在距离传感器 10cm 处吹气,测试时注意串口打印 ADC 原始值,观察从基线到报警值的时间差。如果这个时间小于 1 秒,说明阈值离基线太近,正常炒菜时油烟就可能误报。
5.2 双判据融合:用什么参数组合降低误报
单靠烟雾阈值误报率高,尤其是厨房、楼道这类环境。常见做法是引入温度判据作为确认条件:只有烟雾浓度超过阈值的连续采样次数达到 3 次,且 DS18B20 读到的温度同时高出环境温度 8℃ 以上,才进入 ALARM 状态。这个“烟雾 + 温度”的逻辑和很多商用探测器一致,具体参数参考下表。
| 参数 | 建议范围 | 作用 | 调大的代价 | 调小的代价 |
|---|---|---|---|---|
| FILTER_WINDOW | 8~16 | 平滑采样噪声 | 响应变慢 | 抗干扰差 |
| TRIGGER_COUNT | 3~8 | 连续触发确认 | 漏报风险 | 误报风险 |
| SMOKE_THRESHOLD | 基线+波动2倍 | 烟雾判定 | 漏报 | 误报 |
| 温度偏差量 | 5~10℃ | 联动确认 | 延迟报警 | 高温环境误报 |
| 报警保持时间 | 20~60s | 防止同源抖动 | 复位慢 | 间歇鸣响 |
我处理过的一个典型案例是:有人在预警器旁边给主板做了助焊剂清洗,挥发的异丙醇蒸汽瞬间让 ADC 数值冲到几乎满量程,但因为环境温度没变化,双判据拦截住了这次误报。这个案例说明单传感器阈值是必要条件而非充分条件,融合判据的成本在代码里只有几行,收益却在实用性上体现得最明显。
5.3 调试期必备:把传感器原始值和状态同时输出到串口
标定调参时看不到内部数据等于盲调,建议在系统初始化时设置串口通信并输出结构化信息。一根 USB 转 TTL 线就能把 STC89C52 的 P3.1(TXD)接到电脑上,数据格式建议做成逗号分隔,方便直接导入串口助手或表格。
void send_report(void) { unsigned char buf[32]; sprintf(buf, "smoke:%d,temp:%d,state:%d\r\n", smooth_value, temperature, current_state); UART_SendString(buf); }在状态机每次迁移时顺便调用这个函数,记录“从待警到预警、再升级到报警”的全过程,能看到触发时间点是否合理。这段代码不做复杂的处理,关键是把 smooth_value 和原始值区分开,避免调参时分不清当前温度是滤波前还是滤波后的值。
6. 模块化扩展:给系统预留串口协议并接上 Wi-Fi 上报
预警系统的价值在“人不在现场时也能收到通知”,给裸机系统加无线模块是最常见的进阶路线。模块选型上,ESP8266 系列使用 AT 指令、成本低,适合作为从设备挂在单片机串口上。接线注意电平匹配:ESP8266 的串口是 3.3V TTL,而 51 的 P3.0/P3.1 输出高电平为 5V,直接连接可能损伤模块。常见做法是在 RX 端串接一个 1k 电阻分压,或者用两个电阻组成分压网络降到 3.3V,TX 端因为模块输出本身是 3.3V、能被 51 识别为高电平,但保险起见也可以加电阻限流。
软件上给单片机设计一个精简的上报帧,不要裸发字符串。推荐格式是固定帧头加长度加数据和校验:
FF 55 04 0A 2C 01 8F其中FF 55是帧头,04表示后面数据长度 4 字节,0A 2C是烟雾 ADC 值(高字节在前),01是状态字节(0 待警、1 预警、2 报警),最后8F是校验和,做法是所有数据字节相加取低 8 位。上位机或服务端收到后先校验,数据错乱直接丢弃,避免脏数据导致误报。
给 ESP8266 配置透传模式时,提前用 USB 转串口设好 Wi-Fi 参数,单片机代码里只保留一条 AT 指令序列。系统上电后依次发送AT+CWMODE=1连接路由、AT+CIPSTART="TCP","服务器IP",8080建立 TCP 连接、AT+CIPMODE=1进入透传模式,之后所有 send_report 内容直接成为 TCP 负载。注意透传模式下不能再发 AT 指令,否则会被当成数据发送,这也是一个常见坑。验证方式用电脑上的网络调试助手开一个 TCP 服务端,观察收到的帧是否和串口助手里的一致。如果上报延迟超过 2 秒,排查方向在 Wi-Fi 信号强度和 TCP 心跳体,而不在单片机代码本身。
本文还有配套的精品资源,点击获取