资讯动态

基于51单片机的防盗防火报警系统设计:传感器采集与状态机实现

发布时间:2026/10/21 17:31:11 来源:尧图企业网站定制
1. 需求拆解与方案定调防盗和防火的逻辑为什么能共用一块511.1 防盗与防火的需求差异和整合切入点先把这个项目的需求本质拆开看防盗和防火其实是两套完全不同的感知逻辑。防盗关注的是“有没有人非法进入”核心信号是人体活动防火关注的是“环境有没有异常升温、燃烧产生的烟雾”核心信号是温度和颗粒浓度。这两套逻辑在传感器层面完全不同但在处理层面又有一个共同点——都需要一个主控对多路信号做实时采集、判断、显示和报警。我的做法是把感知层拆成三路温度信号模拟量需要ADC采集烟雾信号模拟量也需要ADC采集红外人体信号数字量直接读引脚电平三路信号汇总到51单片机后由主控统一调度。这样做的最大好处是硬件电路不复杂主控只负责判断和显示逻辑清晰也方便后续扩展比如改成联网上报、短信报警等。你说它高端吗谈不上。但作为一套完整的传感检测系统它的架构是合理的而且是能够稳定跑起来的。这里要强调一个设计思路不要把防盗和防火做成两个独立的主控系统。否则供电、时钟、复位、显示电路全都要做两套成本翻倍联调也更麻烦。共享一个51核心通过分时采集和状态机切换完全能兼顾两套逻辑。这是这个项目最核心的取舍。1.2 方案框定为什么是“511602ADC0832”这套组合不少人在搭这类系统时第一反应是换STM32、用自带ADC觉得51太老、太弱。但在这个项目里51完全够用系统只需要轮流采集两路模拟量、读一路数字量、刷新1602液晶、控制蜂鸣器这些操作连51一半的性能都用不到。更现实的原因是51的生态资料极其丰富无论是初学者还是做毕设上手成本低遇到问题也容易查到参考方案。然后说ADC0832。它不是性能最好的ADC8位精度、两路输入指标在今天看确实不起眼。但在这个项目里8位分辨率完全够用。为什么因为温度传感器LM35输出是10mV/℃即使按5V参考电压算ADC0832的每一档电压分辨率大约是19.5mV对应温度分辨率约1.95℃。对火灾报警这种需要“超过阈值触发”的场景1.95℃的精度绰绰有余。烟雾传感器也一样我们关注的是浓度有没有超过警戒线而不是精确到个位数的浓度值。那为什么不直接用带ADC的单片机这要分情况。STC89C52这类芯片没有内置多路ADC就算有其采样稳定性和参考电压处理也未必比专用ADC芯片更可控。ADC0832的用法非常标准学习价值也高还能帮助你理解SPI类时序的基本原理。1602液晶就更不用说了显示温度、烟雾值、报警状态这类字符信息16列2行足够接口成熟驱动代码遍地都是。这套组合最大的优点是可复制性强。任何一个人手上有51开发板、1602液晶、ADC0832模块和几只传感器都能照着搭出来。这也是我最终放弃更花哨方案的原因。2. 传感器选型与信号链路温度、烟雾、红外分别走什么通路2.1 温度检测LM35的电压输出与ADC参考电压的匹配温度传感器我选了LM35理由只有一个线性输出不用调理电路。它的输出电压和摄氏温度成线性比例0℃时输出0V每升高1℃输出增加10mV。比如室温25℃时输出约250mV。LM35的输出接ADC0832的CH0通道。这里有一个值得注意的点LM35的模拟输出比较微弱如果传感器和主控板之间用很长的导线连接导线电阻和电磁干扰都会影响读数。我在项目中把LM35放在防火检测区域的顶部距离主控板大约30厘米使用双绞线连接信号端对地并联了一个0.1μF电容效果明显比裸线稳定。关于ADC参考电压的匹配这里分开说。如果ADC0832的Vref直接接5V那么温度分辨率大约是1.95℃如果你手头有TL431之类的精密基准源把Vref降到2.5V分辨率能提升到约0.98℃显示温度就能出现小数点后一位。我的实际做法是初期先用5V参考跑通逻辑稳定后再增加TL431做2.5V参考。两种接法在程序上的区别只有一个换算系数的差异完全不影响整体设计。2.2 烟雾检测MQ-2的浓度-电压特性与负载电阻烟雾传感器我选用MQ-2。它的核心是一个二氧化锡半导体气敏元件当空气中可燃气体、烟雾浓度升高时电导率会发生变化从而改变输出电压。MQ-2模块通常有两种输出数字量输出板载比较器和模拟量输出。这里必须用模拟量输出因为我们要通过ADC读取浓度对应的电压值再用阈值去判断而不是依赖模块上那个固定的可调电位器。MQ-2需要加热这是很多人忽略的一点。它的内部有一个加热丝工作电流不小典型工作电流在150mA级别。如果整块板子只用USB口供电一旦蜂鸣器、1602背光、传感器同时工作电流很容易超过500mA的限制。我在项目里使用了外部12V适配器加7805稳压的方案7805输出端加了100μF电解电容和0.1μF陶瓷电容做去耦。调试时我还特意测过整机工作电流蜂鸣器不响时约200mA报警时接近300mA电源余量完全够。MQ-2的模拟输出和烟雾浓度不是线性关系而近似对数关系。这意味着在低浓度区域电压变化明显在高浓度区域电压变化趋于平缓。不过在报警系统里我们只需要一个阈值判断所以无需做对数矫正直接读ADC值比对阈值即可。阈值怎么标定看6.1章节。2.3 红外检测被动式人体红外模块直接作为开关量红外部分我用的是被动式热释电红外模块也就是PIR模块型号最常见的是HC-SR501。它的工作原理是检测人体发出的红外线。人和环境温度有温差当人进入检测区域时热释电传感器输出的信号变化会经过板载放大电路处理最终输出一个高电平脉冲。PIR模块不经过ADC0832而是直接接到单片机的数字输入引脚。我在项目里分配了外部中断引脚P3.2作为红外输入这样设备的防盗响应可以依靠中断来实现而不需要主循环每轮都去轮询这个引脚。PIR模块本身有三个引脚VCC、GND、OUT供电用5V输出电平是TTL电平和51单片机直接兼容。PIR模块有三个调试点需要注意板上两个可调电位器一个调灵敏度一个调输出保持时间。灵敏度调的过高会误触发调得过低又会漏报需要实测调整。模块上电后有约30到60秒的预热稳定期这段时间输出信号会乱跳不能作为有效检测依据。PIR对正对着它的快速温度变化特别敏感比如空调出风口、暖气片附近这些位置不能安装。3. 电路搭建与引脚分配1602和ADC0832的低引脚占用的接线方案3.1 最小系统的供电、复位和晶振以及电源余量51最小系统包括电源、复位电路、晶振电路三部分。晶振我用11.0592MHz因为后续如果要扩展串口通信这个频率产生的波特率没有累积误差调试时少一个变量。复位电路就是经典的10μF电容加10kΩ电阻上电自动复位。供电部分前面提过这里再强调引脚分配上的一个细节模拟部分和数字部分要适当隔离。我的板子上把LM35和MQ-2的供电线从5V电源节点独立引出并分别并联电容不和1602背光、蜂鸣器共用同一条电源走线。虽然这块板子不算高频系统但这种做法能显著减少ADC读数的抖动。接线表如下器件引脚接单片机的引脚备注LCD1602VSS/VDD/VOGND、5V、对比度电位器VO对地接10k电位器LCD1602RS/RW/EP2.0/P2.1/P2.24线模式LCD1602DB4~DB7P2.4~P2.7数据口ADC0832CSP1.0片选ADC0832CLKP1.1时钟ADC0832DIP1.2配置位输入ADC0832DOP1.3转换结果输出LM35VOUTADC0832的CH0温度模拟量MQ-2AOUTADC0832的CH1烟雾模拟量PIROUTP3.2外部中断0蜂鸣器正极P2.3经三极管驱动报警LED正极P1.7通过限流电阻3.2 LCD1602采用4位数据模式接线LCD1602标准接口有16个引脚如果采用8位数据模式要占用8个IO口加上RS、RW、E一共11个口对51来说IO压力太大。我采用4位数据模式只接DB4到DB7配合RS、RW、E三个控制口总共7个IO口搞定显示。数据分两次传输先高4位后低4位速度慢一点但对人眼显示没有影响。1602有一个特别容易踩的坑第三脚VO是对比度调节如果不接电位器或者接成固定电平屏幕可能显示不出来表现为背光亮但无字符或满屏方块。这个电位器一般用10kΩ电位器两端接5V和GND中间抽头接VO调到屏幕上字符清晰、不重影为止。另外RW引脚很多人习惯直接接地表示只写不读。这样确实能省下一个IO口。但如果想读取忙标志再判断写命令时机就必须让RW可控。我的做法是RW接地通过延时函数控制读写节奏。优点是省引脚缺点是需要足够的延时否则会出现字符错乱。项目里我的延时参数是实测调出来的用12MHz晶振时写命令后延时2ms以上写数据后延时200μs以上稳定运行没有问题。3.3 ADC0832与51的软件SPI接线ADC0832和单片机之间是类SPI的串行接口由CS、CLK、DI、DO四根线组成。其中DI是配置输入DO是数据输出。大多数应用下这两根线可以共用一根IO因为配置阶段结束后ADC0832的DO引脚才会开始输出数据这个顺序是固定的。但在我的接线方案里为了让代码更直观DI和DO分别接P1.2和P1.3两个引脚这样读数据时不需要频繁切换IO方向代码的可读性更好。ADC0832的Vref引脚需要注意。它决定转换的满量程电压如果从VCC取参考电压那么VCC的波动会直接影响转换结果。所以我不仅在Vref引脚对地并联了0.1μF电容还在靠近Vref处加了一个10μF电容相当于给它做了一级局部稳压。这种方式不能和专门的参考电压源比但对这个项目的精度要求来说已经完全足够。4. ADC0832驱动与双通道采样8位精度下怎么把温度烟雾读稳4.1 ADC0832的协议与时序ADC0832的协议不难但时序细节需要理解。片选CS拉低后单片机通过DI引脚发送3位配置信息分别是启动位、单端/差分模式选择位、通道选择位。发送完成后ADC0832开始转换单片机再通过CLK时钟读取DO引脚上的8位转换结果先出的是高位。我用的是单端模式两路输入分别是CH0和CH1。配置位具体是启动位固定为1单端模式位固定为1通道选择位0代表CH01代表CH1。这里有一个容易搞混的地方很多资料会把DI和DO分开解释但在控制阶段单片机只需要关心DI到了读取阶段才需要关心DO。你只要记住它们在不同时刻工作就不会乱。延时方面ADC0832的时钟频率一般控制在几百kHz以内。我的做法是在51的11.0592MHz晶振下用普通IO口模拟时钟每个时钟周期插入几个空指令延时。实际测试下来CLK频率大约几十kHz远低于芯片最高工作频率稳定可靠。4.2 双通道轮流采样的驱动函数直接给出我调试通过的ADC0832驱动代码。这里用到了KEIL C51的标准头文件空指令用宏_nop_()实现#include reg51.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 adc0832_read(unsigned char channel) { unsigned char i; unsigned char dat 0; ADC_CS 1; ADC_CLK 0; ADC_CS 0; // 片选拉低启动一次转换 // 写入3位配置启动位单端模式通道选择 ADC_DI 1; // 启动位 ADC_CLK 1; _nop_(); ADC_CLK 0; _nop_(); ADC_DI 1; // 单端模式 ADC_CLK 1; _nop_(); ADC_CLK 0; _nop_(); ADC_DI channel ? 1 : 0; // 通道选择 ADC_CLK 1; _nop_(); ADC_CLK 0; _nop_(); // 读取8位转换结果高位在前 for (i 0; i 8; i) { ADC_CLK 1; _nop_(); dat 1; if (ADC_DO) dat | 0x01; ADC_CLK 0; _nop_(); } ADC_CS 1; // 结束转换 return dat; }调用时adc0832_read(0)读CH0即温度通道adc0832_read(1)读CH1即烟雾通道。4.3 数据滤波与换算ADC0832是8位ADC意味着结果范围是0到255。单次读数的抖动比较大特别是在烟雾电压波动、电源纹波存在的时候。我采用的办法很简单连续采样4次去掉最大值和最小值再对中间两次求平均。这个也叫中值平均滤波对付毛刺很有效。unsigned int filter_adc(unsigned char channel) { unsigned char i; unsigned int sum 0; unsigned char max 0; unsigned char min 255; for (i 0; i 4; i) { unsigned char val adc0832_read(channel); sum val; if (val max) max val; if (val min) min val; } sum - max; sum - min; return sum 1; // 剩余两个值的平均 }换算温度时假设Vref5VADC值换算成电压的公式是电压 ADC值 × 5 / 256。再除以LM35的灵敏度10mV/℃得到摄氏温度。整数运算可以写成// 温度 (adc * 50) / 256单位是0.1℃ unsigned int temp_tenths (filter_adc(0) * 50) / 256;这里乘50是为了保留小数点后一位。显示时temp_tenths / 100显示整数部分temp_tenths % 100显示小数部分。注意如果Vref改成2.5V这个系数要相应调整所以建议把参考电压的选择写成一个宏方便切换。烟雾通道就不需要换算成具体浓度了直接把ADC滤波值用于阈值比较。项目里我设定的是滤波值小于80视为正常80到120提示注意大于120判定为烟雾报警。具体阈值要结合MQ-2的预热情况和实测环境来标定而不是照搬网上的数字。5. 主控程序与联动状态机如何编排防盗、防误报、报警输出5.1 状态划分与转移条件主程序的核心是一个状态机。我划分了五个状态状态含义进入条件IDLE正常监控温度、烟雾、红外均正常TEMP_WARN温度偏高温度超过阈值烟雾正常SMOKE_WARN烟雾超标烟雾超过阈值温度正常FIRE_ALARM火灾报警温度、烟雾同时超标TAMPER_ALARM防盗报警布防状态下红外触发为什么要把温度异常和烟雾异常拆成两个独立的预警状态因为在实际场景中夏天室温高但无烟或者厨房炒菜有油烟但温度不高都可能触发单一指标。单独预警只是提示不驱动强报警。只有两个条件同时满足才断定火灾这样可以显著降低误报率。防盗报警和防火报警是两条独立的线互不干扰。红外触发时不管温度、烟雾状态如何都进入防盗报警直到人为撤防或复位。这样设计是为了避免逻辑混淆防盗和防火的处置方式不同报警信号要能区分。5.2 主循环显示与报警输出主循环固定做四件事滤波采集两路ADC、判断状态机、刷新LCD、控制蜂鸣器和LED。#define TEMP_TH (80) // 约40℃(5V参考时)实测按环境调整 #define SMOKE_TH (120) void main(void) { unsigned int temp; unsigned int smoke; unsigned char key; LCD_Init(); LCD_Clear(); while (1) { temp filter_adc(0); smoke filter_adc(1); // 状态判断 if (temp TEMP_TH smoke SMOKE_TH) { state FIRE_ALARM; } else if (temp TEMP_TH) { state TEMP_WARN; } else if (smoke SMOKE_TH) { state SMOKE_WARN; } else { state IDLE; } // 防盗判断 if (armed PIR_STATE) { if (pir_confirm_count 20) // 连续20个周期触发才确认 { state TAMPER_ALARM; } } else { pir_confirm_count 0; } // 报警输出 if (state FIRE_ALARM || state TAMPER_ALARM) { BUZZER 1; ALARM_LED !ALARM_LED; } else { BUZZER 0; ALARM_LED 0; } LCD_Update(temp, smoke, state); delay_ms(100); } }主循环周期我控制在100ms左右。这样LCD刷新不会闪烁红外触发确认的速度也在合理范围。如果你把延时调得太小比如1msLCD会因为频繁写入而出现显示不全的问题调得太大则反应迟钝实测150ms以上时按撤销按钮会有明显延迟感。5.3 防误报与联动确认逻辑这里提一个容易被忽略的优化温度、烟雾的阈值判断要带回滞。什么叫回滞就是报警阈值和解除阈值不能是同一个值。比如烟雾阈值设为120那么进入报警后要等烟雾值降到100以下才解除报警。这样做的目的是防止阈值附近的轻微波动导致报警和解除反复切换蜂鸣器一开一关非常吵。我的实现比较简单if (state SMOKE_WARN || state FIRE_ALARM) { if (smoke SMOKE_TH - 20) state IDLE; }同理温度报警的解除阈值也要低于报警阈值。加上回滞之后系统的报警行为会“稳”很多这在现场调试时感受特别明显。防盗的防误报逻辑是通过连续周期确认实现的。PIR模块输出高电平的持续时间比主循环周期长得多所以我在主循环中连续检测到20次高电平才确认报警相当于在2秒内红外信号一直处于触发状态。如果只是瞬间有飞鸟或小动物经过PIR输出高电平时间短不会达到20次就不会误报警。当然PIR的位置和灵敏度调节仍然是关键前提。5.4 1602显示驱动的关键片段LCD的完整驱动代码很长我只写关键部分。使用4位模式初始化时序必须严格void LCD_Init(void) { delay_ms(15); // 等待内部复位 LCD_WriteCmd(0x30); // 8位模式命令 delay_ms(5); LCD_WriteCmd(0x30); delay_ms(5); LCD_WriteCmd(0x30); delay_ms(5); LCD_WriteCmd(0x20); // 切换到4位模式 LCD_WriteCmd(0x28); // 4位模式2行显示 LCD_WriteCmd(0x06); // 光标右移 LCD_WriteCmd(0x0C); // 显示开光标关 LCD_WriteCmd(0x01); // 清屏 }之所以要把0x30重复三次是因为1602在上电后可能还没有稳定到8位模式只有重复发送才能确保它正确识别。这也是初学阶段最容易卡住的点很多人直接发0x28结果屏幕永远没反应。显示更新时我第一行显示温度和烟雾状态第二行显示系统状态。比如火灾报警时第二行显示FIRE! Temp78C防盗报警时显示ALARM! INTRUDER。字符闪烁和提示音配合现场效果比较直观。6. 实测调参与踩坑记录预热漂移、阈值回滞、参考电压抖动6.1 MQ-2预热漂移与阈值标定MQ-2上电后加热丝需要时间让气敏元件达到工作温度。我在第一次上电连续记录烟雾ADC值发现刚开机时读数是60左右之后5分钟内逐渐爬升到80到90稳定过程大约持续10分钟。如果一上电就进入监控状态用这个未稳定的数据做阈值判断很可能刚通电就误报烟雾超标。解决方案是在程序启动后加一个预热状态前60秒只显示读数不启动报警判断。更稳妥的做法是记录预热结束时的稳定值作为基准阈值等于基准加固定偏移。比如预热后基准值是85那么烟雾报警阈值设为120偏移量35。这样做的好处是每块MQ-2的个体差异可以被自动抵消不需要每块板子手工调电位器。这个“自动基准校准”功能在很多低成本气体报警产品里都有原理并不复杂就是初始化阶段把空环境的输出平均值存下来后面所有判断都基于这个值做相对比较。6.2 红外误报的触发时间过滤PIR模块安装位置不当会导致频繁误报。我调试时遇到过一个典型情况模块朝向过道一侧的空调出风口空调一启动热风改变局部温度场PIR马上就报。这是热释电传感器的工作特性决定的它检测的本来就是温度变化快速气流也会引起敏感元件表面温度变化。解决方式并不只有程序过滤硬件安装要前置。PIR尽量安装在墙角面朝人体主要活动路径避免正对窗户、空调、暖气。如果条件限制必须安装在风口附近把灵敏度电位器调到最低配合程序里2秒的连续触发确认基本就能把误报压下来。另外PIR模块供电不建议和蜂鸣器共用同一路。蜂鸣器工作时电流波动较大可能造成PIR模块上的比较器误触发。我的板子上给PIR单独加了一个100μF电容报警声音响起时PIR输出信号毛刺明显减少。6.3 ADC参考电压稳定性与读数跳变ADC0832的读数跳变问题我在调试时排查了很久。现象是温度读数稳定时输出值在上下3到5个档位之间跳动蜂鸣器只要一响跳变范围更大。最后定位到两个原因一是蜂鸣器工作电流波动引起5V电源纹波上升二是ADC0832的Vref直接取自VCC纹波通过Vref影响到转换结果。处理办法有三步在ADC0832的Vref引脚对地并联0.1μF电容必要的话串联一个小电阻。蜂鸣器改用三极管驱动供电从主电源独立取电并在蜂鸣器两端并联一个续流二极管。主程序里PID滤波器改为中值平均滤波抵消随机跳变。这三步做完后读数抖动控制在正负1个档位以内这个效果对8位ADC来说已经算很理想了。另外还有一个细节ADC0832的CLK信号线不要和1602的数据线捆扎在一起。LCD在刷新时数据线上的电平变化会通过线间耦合干扰到CLK线我曾经在面包板上遇到这个问题把两根线分开之后读数瞬间稳定下来。飞线整理不只是美观问题对模拟信号采集的影响是实打实的。6.4 扩展空间从基础系统到实用产品还差什么这套系统做完之后我实际遇到的最大不便是报警信息只能靠蜂鸣器和屏幕显示人不在现场就完全管不了。所以如果要往实用产品方向走第一个值得扩展的是通信模块比如用蓝牙或者串口WiFi模块把报警信息发到手机。51单片机只要留一个串口扩展这类功能非常容易驱动代码也有大量现成参考。第二个值得扩展的是联动控制。现在系统只做到了“感知和报警”但真正的智能安防应该做到“报警后自动响应”。比如检测到火灾时自动切断电源、打开排烟阀检测到入侵时自动启动摄像头录像。这些都需要在输出端增加继电器控制电路逻辑层还是在51里加几个条件判断难度不大。还有一点如果对数据精度有更高要求可以考虑在LM35和ADC0832之间加一级运放把0到100℃的电压范围放大到接近ADC的满量程。但这样一来电路的零点和增益都要校准复杂度上升不少。对多数防盗防火场景来说8位ADC加合理阈值已经足够没有必要过度设计。最后说点实际体会这个项目的坑我基本都踩了一遍。最后想提醒的是做这类传感检测系统不要一上来就堆代码。先把硬件的电源、地线、参考电压整明白再用最简单的程序验证每一个传感器能不能读到正确的信号最后才是写状态机和报警逻辑。顺序反了问题会混在一起极难排查。另外阈值参数一定不要照搬网上教程里的数值。每块传感器的个体差异、安装环境、供电质量都不一样必须实际测量、记录、修正。我虽然给了参考值但那只是针对我这套板子和环境的数据你手里的板子跑起来数值肯定会有偏移这是正常的。如果你正准备做类似的单片机综合系统先从本文的电源去耦、预热处理、回滞判断这几件事抓起能帮你少走很多弯路。做完之后你也会发现51虽然老但把逻辑理清楚了照样能构建一套完整可靠的防盗防火报警系统。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑