尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于STM32的分布式消防预警控制系统设计与实现

基于STM32的分布式消防预警控制系统设计与实现 1. 项目缘起与整体设计思路实验室安全这件事没出事的时候谁都不在意一旦出事就是大事。我所在的实验室有十几台大功率设备电烙铁、热风枪、老化测试架常年不断电夏天室温三十五六度的时候心里总是不踏实。市面上成品的消防预警系统要么是工业级的动辄上万块要么是家用烟感功能太单一没法接入实验室这种多点位、多参量的场景。所以干脆自己动手用STM32做了一套分布式的消防预警控制系统从硬件原理图到嵌入式代码再到仿真验证全部走了一遍。这套系统核心解决三个问题第一多点位实时监测实验室不同角落的温度、烟雾浓度、明火情况都要能采集到第二分级预警不是一有异常就拉满警报而是根据阈值分预警、报警、紧急三个等级第三联动控制报警触发后自动切断非关键设备电源、启动排风、推送通知。适合有STM32基础、想做完整嵌入式项目的同学参考也适合实验室管理员直接拿去改改用。整个方案的主控选型上我用了STM32F103C8T6也就是大家常说的蓝板那颗芯片。为什么不用F4或者G系列因为这个项目对算力要求不高主要跑的是传感器轮询、阈值判断、状态机逻辑和串口通信F103的72MHz主频、64KB Flash、20KB RAM完全够用而且价格便宜、资料多、社区支持好出了问题搜一下基本都有答案。传感器方面温度用DS18B20单总线数字输出精度0.5度布线简单烟雾用MQ-2模拟输出便宜且灵敏度可调明火检测用红外火焰传感器数字输出响应快。人机交互用0.96寸OLEDI2C接口省引脚。报警输出用有源蜂鸣器加LED指示灯联动控制通过继电器模块实现。通信架构上我设计的是一主多从的分布式结构。每个监测节点用一块STM32最小系统板负责本地传感器采集和初步判断通过RS485总线或者CAN总线把数据汇总到主控节点。主控节点负责全局状态判断、OLED显示、报警输出和继电器联动。为什么不用无线实验室环境里2.4G频段太拥挤WiFi模块又增加成本和复杂度有线总线在固定安装场景下更稳定可靠。RS485用MAX485芯片半双工两根线搞定传输距离上千米实验室几十米的布线完全没问题。注意选型阶段最容易犯的错误是过度设计。我一开始想用STM32F407加FreeRTOS加LWIP网络通信后来发现根本没必要项目复杂度上去了调试时间成倍增加最后砍到F103加裸机状态机反而跑得很稳。2. 硬件原理图设计与关键电路解析原理图这块我用的是嘉立创EDA免费且元件库全画完直接可以导出BOM和PCB文件。整个系统分三块板子主控板、传感器节点板、继电器驱动板。主控板和节点板的核心电路其实差不多都是STM32最小系统加外设接口区别在于主控板多了OLED接口和RS485收发电路。2.1 STM32最小系统电路最小系统是任何STM32项目的地基这部分画错了后面全白搭。核心包括电源电路、晶振电路、复位电路、启动模式配置和下载接口。电源部分输入用7-12V直流经过AMS1117-3.3降到3.3V给STM32供电。这里有个细节AMS1117的输入输出都要加滤波电容输入端加100uF电解电容加0.1uF陶瓷电容输出端加10uF钽电容加0.1uF陶瓷电容。为什么这么配大电容滤低频纹波小电容滤高频噪声两者配合才能保证电源干净。我实测过如果只加一个0.1uFADC采集MQ-2模拟量的时候跳变能到±50加上大电容后稳定在±5以内。晶振电路用8MHz无源晶振加两个22pF负载电容配合STM32内部的PLL锁相环倍频到72MHz。这里注意负载电容的选择要根据晶振规格书来一般22pF是标配但如果晶振要求12pF你焊22pF就会导致起振困难或者频率偏移。我踩过一次坑用了一批便宜晶振负载电容没改结果板子有一半概率起不来换了匹配电容后问题消失。复位电路用10K上拉电阻加100nF电容再加一个复位按键。启动模式BOOT0和BOOT1都通过10K电阻下拉到地默认从Flash启动。下载接口用SWD模式只需要SWDIO、SWCLK、GND、3.3V四根线比JTAG省地方。这里强烈建议把SWD接口做成标准4Pin排针调试的时候用ST-Link Utility直接连比串口下载方便太多。2.2 传感器接口电路DS18B20的接口很简单数据线接STM32的GPIO同时接一个4.7K上拉电阻到3.3V。为什么是4.7K因为DS18B20是单总线协议靠拉低总线来通信上拉电阻太小功耗大太大上升沿变缓导致通信失败。4.7K是经过验证的经典值实测在3米线长下通信稳定。MQ-2烟雾传感器模块有四个引脚VCC、GND、DO、AO。DO是数字输出通过电位器调节阈值AO是模拟输出接STM32的ADC引脚。我建议用AO输出因为可以软件设置多级阈值比硬件电位器灵活。MQ-2的加热丝需要5V供电预热时间至少24小时才能稳定刚上电的时候读数漂移很大这个在代码里要做延时预热处理。火焰传感器模块也是四引脚DO输出直接接GPIO检测到火焰时输出低电平。注意这个传感器检测的是特定波长的红外线对日光灯和太阳光也有一定响应安装时要避免直射光源干扰。我在实验室测试的时候把传感器对着窗户下午阳光斜射进来就误报了后来加了个遮光罩才解决。2.3 RS485通信电路RS485收发用MAX485芯片RO接STM32的RXDI接TXRE和DE短接后接一个GPIO做方向控制。发送时拉高DE接收时拉低。总线上加120欧姆终端电阻只在总线两端各加一个中间节点不加。A接AB接B千万别接反接反了通信不上但芯片不会烧排查的时候容易懵。实操心得RS485布线一定要用双绞线而且尽量远离强电线路。我一开始和220V电源线捆在一起走通信误码率很高分开走线后误码率降到几乎为零。另外每个节点的GND最好共地否则共模电压可能超出MAX485的承受范围。3. 嵌入式代码架构与核心逻辑实现代码部分我用Keil MDK5开发标准库和HAL库都试过最后选了HAL库因为ST的CubeMX工具可以自动生成初始化代码省去大量配置时间。整个工程分五个模块系统初始化、传感器驱动、数据处理、报警状态机、通信协议。3.1 系统初始化与时钟配置系统时钟配置是第一步用CubeMX配置外部8MHz晶振PLL倍频到72MHzAHB不分频APB1分频系数2得到36MHzAPB2不分频得到72MHz。为什么APB1要分频因为APB1挂载的外设最高只能到36MHz超频会导致外设工作异常。ADC时钟也要注意ADCCLK最大14MHz所以ADC预分频器要选6分频72除以6等于12MHz在安全范围内。GPIO初始化根据原理图来DS18B20的数据引脚配置为开漏输出加内部上拉MQ-2的AO引脚配置为模拟输入火焰传感器DO配置为浮空输入OLED的I2C引脚配置为复用开漏RS485方向控制引脚配置为推挽输出。每个引脚的模式都要和硬件电路匹配配置错了轻则不工作重则烧芯片。3.2 DS18B20温度采集代码DS18B20的时序要求很严格微秒级的延时必须准确。我用SysTick定时器做微秒延时比空循环精确得多。初始化序列是拉低总线至少480us然后释放等待15-60us如果存在脉冲到来则初始化成功。读写时序里写1是拉低1-15us后释放写0是拉低60us后释放读操作是拉低1us后释放在15us内采样总线电平。// DS18B20初始化 uint8_t DS18B20_Init(void) { uint8_t presence; DS18B20_Output_Mode(); DS18B20_Low(); delay_us(480); DS18B20_High(); delay_us(60); DS18B20_Input_Mode(); presence DS18B20_Read_Pin(); delay_us(420); return presence; }温度读取流程是复位、跳过ROM、启动转换、等待750ms、复位、跳过ROM、读取暂存器、计算温度值。注意温度值是16位有符号数低4位是小数部分精度0.0625度。代码里要做符号扩展和浮点转换。3.3 MQ-2烟雾浓度采集与滤波MQ-2的模拟输出接ADC1的通道0用DMA方式连续采样每次采16个点做均值滤波。为什么用DMA因为ADC连续转换模式下CPU不用干预DMA自动把数据搬到内存效率高。均值滤波能有效抑制随机噪声但要注意去掉最大最小值再平均效果更好。// ADC均值滤波 uint16_t Get_ADC_Average(uint8_t times) { uint32_t sum 0; uint16_t max 0, min 4095, temp; for(uint8_t i 0; i times; i) { temp ADC_GetValue(); sum temp; if(temp max) max temp; if(temp min) min temp; } sum sum - max - min; return sum / (times - 2); }烟雾浓度的阈值设定要结合实测。我在实验室密闭空间里点了一根烟做测试ADC读数从基线800跳到2200左右所以预警阈值设1500报警阈值设2000紧急阈值设2500。这些值要根据实际环境调整不能照搬。3.4 报警状态机设计状态机是整个系统的核心逻辑我用四个状态正常、预警、报警、紧急。状态转移条件基于传感器读数和持续时间。比如温度超过45度持续10秒进入预警超过55度持续5秒进入报警超过65度立即进入紧急。为什么要加持续时间因为传感器偶尔会有尖峰噪声单次超阈值就报警会导致误报频繁。typedef enum { STATE_NORMAL, STATE_WARNING, STATE_ALARM, STATE_EMERGENCY } SystemState; void StateMachine_Task(void) { static SystemState current_state STATE_NORMAL; switch(current_state) { case STATE_NORMAL: if(temp 45 smoke 1500) current_state STATE_WARNING; break; case STATE_WARNING: if(temp 55 || smoke 2000) current_state STATE_ALARM; else if(temp 40 smoke 1200) current_state STATE_NORMAL; break; // ... 其他状态 } }状态机的好处是逻辑清晰每个状态下的行为独立定义不会出现if-else嵌套地狱。而且状态转移条件集中管理后期调整阈值很方便。3.5 RS485通信协议设计主从节点之间的通信协议我自定义了一个简单帧格式帧头0xAA 0x55地址码1字节命令码1字节数据长度1字节数据N字节校验和1字节。校验和用累加和取低8位简单但够用。主节点轮询各个从节点从节点收到自己的地址才回复避免总线冲突。typedef struct { uint8_t head[2]; uint8_t addr; uint8_t cmd; uint8_t len; uint8_t data[32]; uint8_t checksum; } RS485_Frame;轮询周期设200ms每个节点回复超时时间50ms超时三次判定节点离线。这个机制能及时发现传感器节点故障避免因为一个节点坏了导致整个系统盲区。4. 仿真验证与实物调试仿真这块我用的是Proteus 8.9虽然Proteus对STM32的支持不如对51单片机那么完善但跑基本的GPIO、ADC、串口仿真还是可以的。仿真目的是验证逻辑正确性不能替代实物调试但能提前发现代码里的低级错误。4.1 Proteus仿真电路搭建在Proteus里搭建电路时STM32F103C8T6的模型要选对我用的是STM32F103C8这个元件。DS18B20用DS18B20模型MQ-2没有现成模型用滑动变阻器模拟模拟输出火焰传感器用按键模拟数字输出。OLED用OLED12864I2C模型蜂鸣器用BUZZER模型。仿真电路搭建好后把Keil编译生成的hex文件加载到STM32模型里设置晶振频率为8MHz点击运行。这时候可以在Proteus里看到OLED显示温度值调节滑动变阻器模拟烟雾浓度变化观察状态机是否按预期转移。注意Proteus仿真STM32的ADC有时候不准确读数跳动很大这是模型本身的问题不代表实际代码有问题。仿真主要看逻辑流程不要纠结具体数值。4.2 实物调试步骤实物调试我按分模块验证、再联调的思路来。第一步只焊电源部分用万用表测3.3V输出是否正常纹波是否在可接受范围。第二步焊STM32最小系统用ST-Link Utility连接看能否识别芯片下载一个LED闪烁程序验证GPIO正常。第三步逐个焊接传感器模块每焊一个就写测试代码验证读数。DS18B20调试的时候如果读出来一直是85度说明初始化失败或者时序不对。85度是DS18B20的上电默认值出现这个值基本就是通信没成功。检查上拉电阻是否焊了延时是否准确引脚配置是否正确。我遇到过因为SysTick优先级配置错误导致延时不准的情况调了半天才发现是中断优先级的问题。MQ-2调试的时候刚上电读数会从高到低缓慢下降这是加热丝升温的正常现象等24小时后再校准基线。校准方法是在洁净空气中读取ADC值作为基线然后根据灵敏度曲线计算不同浓度对应的ADC值。4.3 常见问题与排查技巧问题现象可能原因排查方法解决方案STM32无法识别驱动未安装/芯片锁死设备管理器查看/ST-Link Utility连接安装驱动/用ST-Link Utility解锁DS18B20读数85初始化失败/时序错误示波器看总线波形检查上拉电阻和延时MQ-2读数漂移预热不足/电源纹波大测量电源纹波/等待预热加滤波电容/预热24小时RS485通信失败A/B接反/终端电阻缺失万用表测差分电压调换A/B/加120欧姆电阻OLED不显示I2C地址错误/引脚配置错用逻辑分析仪抓I2C波形确认地址0x78/检查引脚模式继电器不动作驱动电流不足/续流二极管缺失测量GPIO输出电压加三极管驱动/加续流二极管继电器驱动这里要特别说一下STM32的GPIO输出电流最大20mA而继电器线圈需要几十mA直接驱动会烧引脚。正确做法是用S8050三极管做驱动基极串1K电阻继电器线圈两端反并联一个1N4148续流二极管防止断电时反向电动势击穿三极管。5. 系统优化与扩展方向基础功能跑通之后我做了几项优化。第一加了看门狗用独立看门狗IWDG超时时间设2秒防止程序跑飞。第二加了Flash存储把报警记录和阈值参数存在Flash里掉电不丢失。第三加了OTA升级功能通过RS485总线给从节点远程更新固件不用拆机烧录。OTA升级的实现思路是主节点把新固件分包通过RS485发给从节点从节点收到后写入Flash的备份区校验通过后跳转到备份区执行。这里要注意STM32的Flash擦写寿命是1万次左右不能频繁写所以升级不是天天做只在必要时执行。后续还可以扩展的方向接入ESP8266做WiFi上传把数据推到云平台加SD卡做本地数据记录加人体红外传感器判断实验室是否有人无人时降低报警灵敏度。这些扩展都不难核心框架已经搭好了加模块就是加驱动和状态机分支的事。最后分享一个小技巧调试多节点系统的时候给每个节点加一个唯一的LED闪烁模式比如节点1闪1次停1秒节点2闪2次停1秒这样一眼就能看出哪个节点在工作、哪个节点挂了比看串口打印快得多。
返回列表