资讯动态

基于STM32的实验室消防预警系统:原理图、仿真与代码全开源

发布时间:2026/9/25 3:50:58 来源:尧图企业网站定制
1. 为什么我要用STM32做一套实验室消防预警系统实验室这个地方跟普通办公室完全不是一个概念。普通办公室最坏的情况是电脑烧了、文件丢了但实验室里可能放着乙醇、丙酮、氢气瓶、锂电池组甚至一些自配的有机溶剂。一旦起火前三十秒的响应速度基本决定了损失是“一台设备”还是“整间实验室”。市面上成套的消防报警主机不是不好用而是太贵、太封闭一个烟感探头加主机随随便便上千块而且你想改个报警阈值、加个联动逻辑基本没戏。我前后做过三版实验室安全监测的小东西从最早的STC89C52RC方案一路换到STM32F103C8T6最后沉淀下来的就是这套实验室消防预警控制系统。它做的事情很朴素实时采集温度、烟雾浓度、火焰红外三个维度的数据本地声光报警同时把状态通过串口打出来方便上位机记录。整套东西的硬件成本控制在八十块以内代码、原理图、仿真我全部开源适合电子类专业的学生做课程设计、毕业设计也适合实验室管理员自己焊一套挂在墙角当低成本补充预警。关键词里提到的STM32、消防预警控制系统、原理图、仿真、代码这五个词其实就是这套项目的全部骨架。我会从选型逻辑讲到原理图每一部分为什么这么画再到代码里几个容易翻车的地方最后把仿真怎么跑通也一并说清楚。你如果是刚接触STM32的新手照着做能跑通如果你已经做过几个项目这里面关于传感器时序、ADC采样滤波、报警状态机的处理方式应该也能给你省点调试时间。需要提前说明的是这套系统定位是辅助预警不是替代正规消防设施。它的价值在于低成本、可定制、可记录适合作为实验室安全管理的补充手段而不是唯一手段。这个定位想清楚了后面的设计取舍就都顺了。2. 系统整体架构与核心器件选型逻辑2.1 主控为什么锁定STM32F103C8T6选主控这件事我踩过的坑比选传感器还多。最早用STC89C52RC51内核跑起来确实简单但问题很快就暴露了ADC只有8位、没有硬件I2C、串口只有一个、定时器资源紧张。消防预警系统需要同时处理三路模拟量采集、一路串口输出、一路蜂鸣器PWM、还有状态机轮询51跑起来明显吃力采样周期一拉长响应就迟钝。换成STM32F103C8T6之后情况完全不一样。这颗芯片是Cortex-M3内核72MHz主频64KB Flash、20KB SRAM关键是它有三个12位ADC、多个16位定时器、硬件I2C和USART。对于这套系统来说资源绰绰有余。更重要的是它的开发资料极其丰富Keil5、STM32CubeMX、标准外设库、HAL库都能用网上随便一搜就有大量参考代码。价格上一片原装F103C8T6现在也就十块钱出头最小系统板更便宜性价比没有对手。这里有个选型细节值得说很多人会纠结用F103C8T6还是F103C6T6。C6T6的Flash只有32KBSRAM只有10KB如果你后面想加OLED显示、SD卡存储、甚至简单的RTOS空间会非常紧张。C8T6多出来的那点成本几乎可以忽略但余量完全不是一个级别。我的建议是直接上C8T6别在这上面省。2.2 三路传感器的分工与选型理由消防预警的核心是“早发现”而单一传感器很容易误报或漏报。我的方案是温度、烟雾、火焰三路并行任何一路触发阈值就进入预警状态两路以上触发就升级为报警状态。这种多传感器融合的思路能显著降低误报率。温度采集我用的是DS18B20。它是单总线数字传感器不需要额外的ADC直接一个GPIO就能读精度0.5摄氏度测量范围-55到125度实验室环境完全够用。相比热敏电阻加ADC的方案DS18B20省去了标定环节一致性也好很多。缺点是单总线时序比较严格代码里延时必须准这个后面会专门讲。烟雾浓度我用的是MQ-2。这是半导体式可燃气体传感器对液化气、丙烷、氢气、烟雾都有响应模拟输出接STM32的ADC引脚。它的优点是便宜、灵敏度高缺点是输出受温湿度影响大需要预热而且没有绝对浓度标定。所以我在代码里没有去换算具体ppm值而是用相对阈值加滑动滤波来判断趋势这个思路更实用。火焰检测我用的是火焰红外接收管本质上是一个对特定波长红外敏感的光电二极管配合比较器输出数字信号。火焰燃烧时会辐射大量红外线这个传感器能在几米范围内检测到。它的响应速度比温度传感器快得多是整套系统里最先能感知到明火的通道。2.3 报警与交互部分的设计取舍报警输出分两级一级是有源蜂鸣器直接GPIO驱动三极管开关二级是高亮LED红色表示报警、黄色表示预警、绿色表示正常。有源蜂鸣器比无源的好驱动不需要PWM给高电平就响代码简单。如果你想要不同频率的报警音那就得用无源蜂鸣器加定时器PWM复杂度会上一个台阶这套系统里没必要。交互部分我只留了一个按键用来手动消音和复位。按键用外部中断或者轮询都行我用的是轮询加20ms消抖简单可靠。没有做LCD显示因为实验室场景下报警声和灯光比屏幕更直接屏幕反而增加成本和故障点。状态数据通过USART1以9600波特率输出格式是简单的文本行方便上位机或者串口助手直接看。供电部分我用的是AMS1117-3.3把5V降到3.3V给STM32和传感器供电输入5V可以从USB口或者DC头进来。这里要注意MQ-2的加热丝电流比较大大概150mA左右AMS1117要加散热片或者选大封装否则长时间工作会烫手。我在原理图里给MQ-2单独留了一路5V供电不和3.3V混在一起这个细节后面原理图部分会展开。3. 原理图设计从电源到传感器的每一处细节3.1 电源部分为什么AMS1117要这么接电源是整个系统最容易被忽视、但出问题最多的部分。我的输入是5V通过AMS1117-3.3降到3.3V。原理图上输入端我放了两个电容一个100uF的电解电容和一个0.1uF的陶瓷电容。100uF负责滤低频纹波0.1uF负责滤高频噪声这两个不能省。输出端同样是一个10uF加一个0.1uF的组合。很多人画原理图的时候只放一个0.1uF觉得够了实际上AMS1117在负载突变的时候输出会有明显波动尤其是MQ-2加热丝周期性工作时电流变化会让3.3V轨产生纹波进而影响ADC采样精度。我实测过不加输出电容的时候ADC读数的跳动能达到±30个LSB加上之后就稳定在±5以内。这个差别在调试阶段非常明显。另外AMS1117的散热问题必须重视。它的压差是5V减3.3V等于1.7V如果系统总电流按200mA算功耗就是0.34W。SOT-223封装的热阻大概在60度每瓦左右温升就是20度环境温度30度的话芯片表面能到50度以上。所以我在PCB上给AMS1117的散热焊盘铺了一大块铜原理图上虽然看不出来但画PCB的时候一定要做。3.2 STM32最小系统晶振、复位、启动模式STM32F103C8T6最小系统的三要素是晶振、复位电路、启动模式配置。晶振我用的是8MHz无源晶振配合两个22pF的负载电容。这里有个经验负载电容的值不是随便选的要根据晶振规格书上的负载电容来算。常见8MHz晶振的负载电容是20pF那么两个匹配电容就应该是2乘以20减去引脚寄生电容约5pF大概30pF左右。我用22pF是因为手头只有这个值实测起振没问题但严格来说应该按规格书来。复位电路是经典的10K上拉加100nF电容到地再加一个复位按键。这个电路简单但有效上电时电容充电产生复位脉冲按下按键时手动拉低复位。启动模式我全部拉低也就是从主Flash启动这是最常用的配置。BOOT0和BOOT1各接一个10K下拉电阻不要悬空悬空会导致启动模式不确定。调试接口我留了SWD四线3.3V、GND、SWDIO、SWCLK。SWD比JTAG省引脚而且ST-Link Utility和Keil都支持得很好。这里提醒一句SWDIO和SWCLK不要接其他外设也不要在旁边走高频信号线否则下载容易失败。我遇到过因为SWCLK旁边走了一根PWM线导致下载时好时坏的情况后来把线挪开就稳定了。3.3 传感器接口DS18B20、MQ-2、火焰管的接法DS18B20的数据脚接在PA0上同时接一个4.7K上拉电阻到3.3V。这个上拉电阻是必须的因为DS18B20是开漏输出没有上拉就没有高电平。4.7K是常用值如果你总线拉得比较长可以降到2.2K增强驱动能力。DS18B20的VDD接3.3VGND接地注意不要接反接反会直接烧掉。MQ-2有四个引脚VCC、GND、DO、AO。VCC接5VGND接地AO接STM32的PA1ADC1的通道1DO我悬空了因为数字输出的阈值调节比较粗糙不如直接用ADC读模拟量灵活。MQ-2的负载电阻RL我用的10K这个值影响灵敏度RL越大灵敏度越高但响应越慢10K是个折中。火焰传感器我用的模块自带比较器输出数字信号接在PA2上。模块上有电位器可以调灵敏度我一般调到刚好能检测到打火机火焰的距离。注意火焰传感器对光线敏感阳光或者强白炽灯可能会误触发所以安装位置要避开直射光。3.4 报警输出与串口蜂鸣器驱动和USART1蜂鸣器我用的是S8050三极管驱动基极串一个1K电阻接PA3集电极接蜂鸣器负极蜂鸣器正极接5V发射极接地。这里要注意蜂鸣器是感性负载关断时会产生反向电动势所以我在蜂鸣器两端并联了一个1N4148续流二极管。不加这个二极管三极管用不了多久就会击穿。LED部分红黄绿三个LED分别接PA4、PA5、PA6每个LED串一个1K限流电阻到地。STM32的GPIO灌电流能力比拉电流强所以用低电平点亮的方式更合理也就是LED正极接3.3V负极通过电阻接GPIOGPIO输出低电平时点亮。USART1用的是PA9TX和PA10RX波特率96008位数据位1位停止位无校验。TX接USB转串口模块的RXRX接模块的TXGND对接。如果你要用ST-Link的虚拟串口那就接对应的引脚具体看你的下载器型号。4. 代码实现从ADC采样到报警状态机4.1 DS18B20单总线时序延时不准一切白搭DS18B20的代码网上有很多但能稳定跑的不多核心问题就出在延时上。单总线协议对时间要求非常严格比如复位脉冲要求拉低至少480us然后释放等待15到60us后检测存在脉冲。如果你用HAL_Delay最小单位是1ms根本不够用。必须用微秒级延时。我的做法是用SysTick或者定时器做一个微秒延时函数。如果用SysTick注意不要和HAL库的1ms滴答冲突。我一般用TIM4做一个1us计数的定时器专门给DS18B20和后续可能的其他时序用。具体实现是定时器预分频到1MHz也就是每计数一次1us然后写一个delay_us函数传入要延时的微秒数循环等待计数。读时序和写时序也要注意。写1是拉低1到15us然后释放写0是拉低60us然后释放。读时序是主机拉低1us然后释放在15us内采样总线电平。这些时间窗口都有余量但不能差太多。我建议你写完代码后用逻辑分析仪或者示波器抓一下波形确认时序在规格范围内。没有逻辑分析仪的话至少用示波器看一下复位脉冲的宽度。还有一个坑DS18B20的温度转换需要时间12位精度下最长750ms。如果你在转换期间去读读到的就是上一次的结果或者85度默认值。我的做法是发起转换后用状态机等待或者干脆延时800ms再读。在消防预警系统里温度采样周期本来就是秒级所以延时完全不影响。4.2 MQ-2的ADC采样与滑动滤波MQ-2的输出是模拟电压接STM32的ADC1通道1。STM32的ADC是12位的参考电压3.3V所以分辨率是3.3除以4096大概0.8mV。MQ-2在洁净空气中的输出电压大概在0.5V到1V之间随着烟雾浓度升高输出电压会上升。直接读ADC值会有跳动这是正常的因为传感器本身有噪声电源也有纹波。我的处理方式是做滑动平均滤波开一个长度为10的数组每次采样把新值放进去然后算平均值。这样能把随机噪声压下去同时保留趋势变化。窗口长度不要太大10到20比较合适太大响应会迟钝。这里有个细节MQ-2需要预热。刚上电的时候加热丝还没到工作温度输出会偏高大概需要预热20到30秒才能稳定。所以我在代码里加了一个预热标志上电后前30秒不判断烟雾报警只采集不动作。这个逻辑很重要否则每次上电都会误报一次。阈值设定上我没有用绝对电压值而是用相对变化。具体做法是上电预热完成后记录一个基准值然后判断当前值是否超过基准值的1.5倍或者2倍。这样能适应不同环境下的基线漂移。当然如果你想更精确可以加温湿度补偿但那会增加复杂度这套系统里没必要。4.3 报警状态机的设计正常、预警、报警三态切换状态机是这套代码的灵魂。我用三个状态STATE_NORMAL、STATE_WARNING、STATE_ALARM。切换逻辑是这样的三路传感器中任意一路超过阈值进入WARNING两路及以上超过阈值或者温度超过更高阈值进入ALARM。从ALARM回到NORMAL需要所有传感器都恢复正常并且持续一段时间防止抖动。具体实现上我用一个结构体保存每个传感器的状态和计数然后在主循环里每100ms执行一次状态判断。为什么是100ms因为传感器响应时间都在秒级100ms足够快又不会让CPU太累。状态切换的时候我会记录切换时间用于消音和复位逻辑。消音按键的处理也要注意。按下按键后蜂鸣器停止但LED状态保持直到传感器恢复正常。这个逻辑符合实际使用习惯你消音了但问题还在灯光提醒你。如果问题解决了状态自动回到NORMALLED变绿。串口输出我用的是printf重定向把fputc改成往USART1发数据。输出格式是类似“T:25.3 S:1200 F:0 STATE:WARNING”这样的文本行方便解析。注意printf会占用不少Flash如果空间紧张可以用sprintf加自定义发送函数。4.4 串口输出与上位机对接的实用技巧串口输出看起来简单但实际用起来有几个坑。第一个是波特率误差。STM32的USART波特率是从APB时钟分频来的如果时钟配置不对误差会累积。9600波特率下误差要控制在2%以内才能稳定通信。我一般用72MHz的PCLK2分频系数算出来是468.75取整469误差很小没问题。第二个是发送阻塞。如果你用阻塞方式发送每发一个字节都要等TXE标志数据量大的时候会拖慢主循环。我的做法是发少量数据用阻塞如果以后要发大量数据就改成DMA或者中断发送。这套系统里数据量很小阻塞完全够用。第三个是上位机对接。我用Python写了一个简单的接收脚本用pyserial读串口然后解析文本行画实时曲线。如果你不想写代码直接用串口助手也能看只是没有曲线。这里提醒一句串口助手的自动换行和编码要设对否则中文会乱码。我输出的是纯ASCII所以不存在这个问题。5. 仿真验证没有硬件也能跑通逻辑5.1 Proteus仿真里STM32模型的局限性Proteus能仿真STM32但它的模型和真实芯片有差距。最大的问题是外设行为不完全一致比如ADC的参考电压、定时器的精度、中断的响应时间都和实物有偏差。所以仿真能验证逻辑但不能验证时序精度。DS18B20这种对时序敏感的器件在Proteus里经常跑不通因为仿真时间不是实时的。我的建议是仿真主要用来验证状态机逻辑、串口输出格式、LED和蜂鸣器的控制逻辑。传感器部分可以用信号发生器或者手动改变输入来模拟。比如MQ-2的模拟输出在Proteus里可以用一个电位器分压来模拟你手动调电位器看状态机是否正确切换。5.2 仿真工程的搭建步骤与元件替换搭建仿真工程的步骤大概是这样的新建Proteus工程放置STM32F103C8T6放置DS18B20、电位器模拟MQ-2、按键、LED、蜂鸣器、虚拟串口终端。然后加载编译好的hex文件设置晶振频率为8MHz运行仿真。元件替换方面如果Proteus里找不到某个型号可以用功能相近的替代。比如火焰传感器可以用一个开关加电阻来模拟数字输出。虚拟串口终端用COMPIM或者Virtual Terminal都行注意波特率要设成9600。仿真里最容易出问题的是电源和地。Proteus不会自动连接电源你需要手动放POWER和GROUND符号并且确保所有器件的电源引脚都连上了。我见过很多人仿真跑不起来就是因为忘了连电源。5.3 仿真与实物差异带来的调试启示仿真跑通不代表实物能跑通这个心理准备要有。我在仿真里状态机切换很流畅但实物上因为DS18B20时序问题温度一直读85度。后来用示波器一看延时函数差了十几微秒调整之后就好了。反过来实物上能跑的逻辑仿真里不一定能跑因为仿真模型可能不支持某些特性。所以我的做法是先在实物上验证核心功能再用仿真验证逻辑分支。两者互补不要偏废。6. 调试过程中踩过的坑与排查思路6.1 DS18B20读回85度的三种可能原因85度是DS18B20的默认上电值读回85度基本意味着通信失败。我遇到过三种情况第一种是延时不准复位脉冲宽度不够传感器没响应第二种是上拉电阻没接或者阻值太大总线拉不高第三种是引脚配置错了比如配成了模拟输入而不是开漏输出。排查顺序是先用示波器看复位脉冲和存在脉冲确认通信有没有建立然后检查上拉电阻用万用表量总线在空闲时是不是高电平最后检查GPIO配置开漏输出加上拉是标准做法。这三个都排除了基本就能读到正确温度。6.2 MQ-2预热期误报的处理方式前面提过预热问题这里展开说。MQ-2上电后加热丝需要时间升温输出会从高慢慢降下来。如果你上电就开始判断肯定会误报。我的处理是加一个30秒的预热计时器期间LED黄灯闪烁表示预热中不触发报警。预热结束后记录基准值再进入正常判断。这个逻辑在代码里就是一个状态变量加一个计数器很简单但效果很好。如果你不做这个每次上电都报警用起来会非常烦。6.3 串口乱码与波特率误差的排查链路串口乱码最常见的原因是波特率不匹配。先确认代码里的波特率和串口助手设的是不是一样然后确认系统时钟配置对不对。如果时钟是72MHz但代码里按8MHz算分频那波特率就会差很多。排查方法是用示波器量TX引脚发一个已知字节比如0x55看波形宽度算出实际波特率。如果和设定值差太多就是时钟配置问题。另外USB转串口模块的质量也有影响劣质模块在高速率下容易出错9600这种低速一般没问题。6.4 蜂鸣器误响与GPIO初始化的顺序问题蜂鸣器上电瞬间响一下这个问题很常见。原因是GPIO初始化之前引脚处于浮空状态可能被外部干扰拉高。解决办法是在初始化的时候先把引脚设成低电平再配置成输出模式。或者用三极管驱动的时候基极加下拉电阻确保默认关断。我在代码里的做法是在GPIO_Init之前先调用GPIO_ResetBits把引脚拉低然后再初始化。这样上电就不会误响了。这个细节很小但不注意的话用户体验很差。7. 开源资料的组织方式与复现建议7.1 代码目录结构与移植注意事项代码我按功能分目录Core放main和系统初始化Drivers放STM32外设驱动Devices放DS18B20、MQ-2、火焰传感器的驱动App放状态机和业务逻辑。这样分层的好处是移植方便换芯片只需要改Drivers层。移植的时候注意几点时钟配置要根据目标芯片改GPIO引脚要根据原理图改延时函数要根据主频调整。如果你从F103换到F407主频从72M变到168M延时函数的预分频值要重新算。7.2 原理图与PCB的对应关系检查清单画完原理图之后导出网表然后画PCB。画PCB之前先检查这几项电源和地的连接是否完整去耦电容是否每个电源引脚都有晶振是否靠近芯片SWD接口是否引出传感器接口是否标注清楚。这些检查做完能避免大部分低级错误。PCB布局上AMS1117和MQ-2要远离晶振和模拟信号线蜂鸣器和LED可以放在板边。走线宽度上电源线要加粗至少20mil信号线10mil够了。7.3 从这套系统延伸出去的几个改造方向这套系统是个基础框架你可以往上加很多东西。比如加一个ESP8266模块把报警信息推到手机加一个SD卡模块记录历史数据加一个继电器输出联动排风扇或者电磁阀。这些扩展都不难因为主控资源还有余量代码结构也留了接口。我个人觉得最实用的扩展是加一个RTC时钟给每条报警记录打时间戳。这样事后查的时候能知道具体是什么时候出的问题。DS1302或者STM32内部的RTC都行成本增加很少但数据价值提升很大。最后说一句这套东西我做出来之后在实验室挂了大半年中间因为隔壁房间做实验飘过来一点烟雾触发过一次预警虽然最后是虚惊但至少说明传感器是工作的。这种低成本预警系统的意义就在这儿它不能保证万无一失但能让你在关键时刻多几十秒的反应时间。这几十秒有时候就是一台设备和一间实验室的区别。

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

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

免费获取报价 →
↑