ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于STM32的实验室消防预警系统设计与实现

基于STM32的实验室消防预警系统设计与实现 1. 项目缘起与整体设计思路1.1 为什么选择STM32做消防预警控制器实验室这个场景对消防预警的需求其实挺特殊的。它不像商场、写字楼那种大面积开放式空间实验室往往是密闭的、设备密集、易燃试剂多、人员流动性低一旦起火早期发现的价值远大于事后报警。市面上成套的消防报警主机价格不低而且大多是面向楼宇级别设计的动辄几百个点位用在几十平米的实验室里属于杀鸡用牛刀。更关键的是成品主机基本是黑盒你想加点自定义逻辑——比如联动排风扇、自动断电、微信推送——几乎不可能。STM32这条路线就灵活多了。一颗STM32F103C8T6七十二兆主频六十四K Flash二十K RAM价格不到十块钱外设资源足够撑起一个多点位的预警系统。温度、烟雾、火焰、可燃气体这几路传感器全部走ADC或者GPIO继电器输出控制声光报警和排风串口留出来接无线模块做远程通知。整个方案的成本能压到百元以内而且每一行代码、每一根走线你都能自己掌控。这个项目我前后做了两版第一版用的是最小系统板加杜邦线验证逻辑第二版直接画了PCB把传感器接口、继电器驱动、电源管理全部集成到一块板子上。开源出来的内容包括三部分完整的Keil工程代码、Altium Designer画的原理图和PCB、以及Proteus仿真工程。这样不管你是想直接复现还是想在此基础上改都有完整的参考。1.2 系统架构与模块划分整个系统的架构可以拆成四层来看从下往上依次是感知层、控制层、执行层和交互层。感知层负责采集环境数据我选了四种传感器DHT11测温湿度MQ-2测烟雾和可燃气体火焰传感器检测明火还有一个DS18B20做温度冗余。为什么温度要用两路因为DHT11的响应速度偏慢采样周期最快也要一秒一次而DS18B20可以做到七百五十毫秒两者互为补充避免单一传感器失效导致漏报。控制层就是STM32F103C8T6核心板跑的是裸机程序没有上RTOS。原因很简单这个系统的任务数量不多主要就是定时采样、阈值判断、报警输出、串口通信这几件事用SysTick做时间片轮询完全够用上RTOS反而增加复杂度和调试难度。主循环里用状态机的方式管理各个任务每个任务有自己的时间片互不阻塞。执行层包括三部分一是声光报警用蜂鸣器加红色LED二是继电器输出可以控制排风扇或者切断实验台电源三是串口输出通过HC-05蓝牙模块或者ESP8266把报警信息推到手机端。交互层就是几个按键和OLED显示屏用来设置阈值、查看当前状态、手动消音。按键用了三个设置键、加键、减键配合OLED的菜单做参数配置。OLED用的是0.96寸的SSD1306I2C接口占两个IO口就够了。1.3 方案选型中的几个关键取舍传感器选型上我纠结过一阵。烟雾检测有离子式和光电式两种离子式对明火烟雾响应快但对黑烟不敏感光电式对阴燃烟雾更灵敏。实验室场景里有机溶剂起火往往是明火而纸张、布料阴燃的概率也不低所以最后选了MQ-2这种半导体式传感器它对多种可燃气体和烟雾都有响应虽然精度不如专业光电式但胜在便宜、接口简单、响应速度也够用。火焰传感器我选的是五路红外接收管的那种模块检测角度大概六十度有效距离一米左右。这里有个坑火焰传感器的输出是模拟量但模块上带了比较器可以直接输出数字信号。我一开始用的是数字输出结果发现阈值电位器调起来很麻烦环境光一变就误报。后来改成读模拟量在代码里做滑动平均滤波稳定性好了很多。通信方式上蓝牙和WiFi我都试过。HC-05蓝牙配对简单手机装个串口助手就能收数据但传输距离有限穿墙能力差。ESP8266可以连WiFi推送到服务器但配网过程对不熟悉的人不太友好。最后开源版本里两种都保留了代码里用宏定义切换你想用哪个就开哪个。2. 硬件设计细节与原理图解析2.1 核心板与最小系统电路STM32F103C8T6的最小系统其实很成熟了但有几个地方容易出错我重点说一下。首先是复位电路我用的是十K上拉电阻加一百纳法电容的标准配置复位按键并联在电容两端。这里注意电容不能太大否则复位时间过长芯片可能无法正常启动。一百纳法对应的时间常数大概是十毫秒量级足够覆盖复位脉冲宽度。晶振电路用的是八兆无源晶振两个二十皮法的负载电容。这里有个经验负载电容的值要根据晶振规格书来选不是随便拿两个二十皮法就完事。我遇到过一批晶振规格书上写的是十二点五皮法负载电容结果用二十皮法起振困难换成十二皮法就正常了。所以画板子的时候晶振旁边的电容位置最好留两个焊盘方便调试时更换。启动模式选择也很关键。BOOT0和BOOT1两个引脚决定了芯片从哪启动。正常运行时BOOT0接地从Flash启动。如果要串口下载程序BOOT0接高电平BOOT1接地复位后进入系统存储器启动模式。我在板子上留了一个跳线帽切换起来方便。注意BOOT0不能悬空必须明确接高或接低否则启动行为不确定。2.2 传感器接口电路设计DHT11的接口很简单但有个细节数据线需要四K七到十K的上拉电阻。我一开始忘了加结果读出来的数据全是零。加上十K上拉之后通信就正常了。DHT11的供电范围是三到五点五伏我直接用的五伏因为它的数据线是开漏输出上拉到五伏后STM32的IO口也能识别但为了保险我在数据线和单片机之间串了一个一K电阻限流。MQ-2的接口稍微复杂一点。它需要五伏供电加热丝电流大概一百五十毫安所以电源走线要够宽。模拟输出接到STM32的ADC引脚我选的是PA0对应ADC1的通道零。这里注意MQ-2的输出电压范围是零到五伏而STM32的ADC参考电压是三点三伏直接接会烧引脚。所以我在中间加了一个分压电路两个十K电阻串联取中间点接到ADC这样五伏就被分压到二点五伏在安全范围内。火焰传感器我用了四路分别朝向四个方向减少检测盲区。每路输出接一个ADC通道代码里轮流采样。这里有个技巧火焰的闪烁频率大概在五到三十赫兹之间单纯读瞬时值容易受环境光干扰。我在代码里做了一个简单的一阶低通滤波公式是filtered filtered * 0.8 raw * 0.2效果比滑动平均还好计算量也小。2.3 继电器驱动与电源管理继电器我用的是五伏的SRD-05VDC-SL-C线圈电流大概七十毫安。STM32的IO口最大输出电流是二十毫安直接驱动肯定不行所以加了一个S8050三极管做开关。基极串一个一K电阻发射极接地集电极接继电器线圈的一端线圈另一端接五伏。继电器线圈两端要反向并联一个续流二极管我用的是1N4148防止断电时线圈产生的反向电动势击穿三极管。电源部分我用了两种方案。调试阶段直接用USB供电五伏进来后经过一个AMS1117-3.3降到三点三伏给STM32。但AMS1117的压差大概是一点一伏五伏降到三点三伏没问题但发热量不小尤其是继电器吸合的时候。后来做PCB版本的时候我换成了MP1584这种开关电源芯片效率高很多发热也小。输入电容用了一百微法电解加一百纳法陶瓷输出电容用了二十二微法电解加一百纳法陶瓷纹波控制在五十毫伏以内。这里有个坑要提醒继电器吸合的瞬间电流突变会在电源线上产生尖峰可能导致STM32复位。我在继电器电源和单片机电源之间加了一个磁珠再并一个一百微法的电解电容问题就解决了。如果你也遇到继电器一吸合单片机就重启的情况先查电源。2.4 原理图绘制中的注意事项画原理图的时候网络标号一定要规范。我习惯把电源网络标成VCC_5V和VCC_3V3地网络统一用GND不要混用AGND和DGND除非你确实做了模拟地和数字地的分割。这个项目里模拟部分只有MQ-2和火焰传感器精度要求不高所以模拟地和数字地统一用GND单点接地就行。元件封装要提前确认好。我第一版画板的时候DHT11的封装画错了引脚间距搞成了二点五四毫米实际的是二点零毫米结果板子回来焊不上。后来重新画了封装才解决。所以画原理图的时候最好把实物拿在手里比对一下或者去立创商城下载现成的封装库比自己画靠谱。ERC检查一定要跑。我遇到过好几次原理图上看起来连对了但ERC一跑就报错仔细一看是电源引脚没接或者输出引脚和输出引脚短接了。ERC能帮你抓出大部分低级错误别嫌麻烦。3. 软件架构与核心代码实现3.1 主循环状态机设计裸机程序最怕的就是阻塞。你要是用delay_ms(1000)这种函数整个系统在这一秒内什么都干不了传感器数据读不了按键也响应不了。所以我的主循环用的是时间片轮询加状态机的结构。具体做法是这样SysTick配置成一毫秒中断一次在中断里给每个任务的时间计数器加一。主循环里不断检查各个任务的计数器是否到达预设值到了就执行对应的任务函数执行完把计数器清零。这样每个任务都有自己的执行周期互不干扰。// 任务时间片定义 #define TASK_DHT11_PERIOD 2000 // 2秒 #define TASK_MQ2_PERIOD 500 // 500毫秒 #define TASK_FLAME_PERIOD 100 // 100毫秒 #define TASK_KEY_PERIOD 20 // 20毫秒 #define TASK_DISPLAY_PERIOD 200 // 200毫秒 volatile uint32_t task_tick[TASK_MAX] {0}; void SysTick_Handler(void) { for (int i 0; i TASK_MAX; i) { task_tick[i]; } } int main(void) { SystemInit(); SysTick_Config(SystemCoreClock / 1000); // 外设初始化... while (1) { if (task_tick[TASK_DHT11] TASK_DHT11_PERIOD) { task_tick[TASK_DHT11] 0; DHT11_Read(); } if (task_tick[TASK_MQ2] TASK_MQ2_PERIOD) { task_tick[TASK_MQ2] 0; MQ2_Read(); } // 其他任务... } }这个结构的好处是每个任务的执行周期一目了然改起来也方便。比如你觉得火焰检测需要更快响应把TASK_FLAME_PERIOD从一百改成五十就行不用动其他代码。3.2 传感器数据采集与滤波DHT11的时序要求比较严格起始信号拉低至少十八毫秒然后拉高二十到四十微秒接着释放总线。DHT11响应后会拉低八十微秒再拉高八十微秒然后开始传输四十位数据。每一位数据都以五十微秒的低电平开始高电平的持续时间决定数据是零还是一。二十六到二十八微秒是高电平表示零七十微秒表示一。uint8_t DHT11_ReadByte(void) { uint8_t data 0; for (int i 0; i 8; i) { while (!DHT11_DATA_IN); // 等待低电平结束 delay_us(40); data 1; if (DHT11_DATA_IN) { data | 1; while (DHT11_DATA_IN); // 等待高电平结束 } } return data; }这里有个细节delay_us(40)这个延时很关键。如果延时太短可能在高电平还没结束的时候就采样导致误判。四十微秒正好在二十六到七十微秒之间能正确区分零和一。但如果你用的晶振频率不是八兆这个延时需要重新计算。MQ-2的ADC采样我做了十次平均去掉最大值和最小值剩下的八次求平均。这样能有效抑制尖峰噪声。火焰传感器用的是前面提到的一阶低通滤波系数零点八是试出来的太小了响应慢太大了滤波效果不好。3.3 报警逻辑与阈值管理报警逻辑我设计了三档正常、预警、报警。预警是某个传感器超过阈值但还没到危险程度这时候OLED显示黄色提示蜂鸣器不响。报警是超过危险阈值蜂鸣器响继电器动作串口发送报警信息。阈值不是固定的而是存在Flash里可以通过按键修改。我用了STM32内部的Flash模拟EEPROM把阈值参数存在最后一页。每次上电的时候读出来修改的时候写回去。注意Flash写之前要先擦除而且擦除是按页擦除的一页一K字节。我定义了一个结构体来存参数typedef struct { uint16_t temp_warn; uint16_t temp_alarm; uint16_t smoke_warn; uint16_t smoke_alarm; uint16_t flame_threshold; uint8_t magic; // 用于判断是否已初始化 } Config_t; Config_t config;magic这个字段很有用。第一次上电的时候Flash里全是0xFF读出来的magic不是预设值这时候就加载默认参数并写入Flash。之后每次上电magic匹配就直接读参数不匹配就重新初始化。3.4 串口通信与远程通知串口我配置的是九六零零波特率八位数据位一位停止位无校验。为什么用九六零零而不是一一五二零零因为HC-05蓝牙模块默认就是九六零零改波特率虽然可以但多一事不如少一事。而且这个系统传输的数据量很小九六零零完全够用。发送的数据格式我用了简单的JSON字符串方便手机端解析void Send_Alarm_Message(uint8_t type, uint16_t value) { char buf[64]; sprintf(buf, {\type\:%d,\value\:%d}\r\n, type, value); UART_SendString(buf); }type表示报警类型一是温度二是烟雾三是火焰。value是当前值。手机端用串口助手收到后一眼就能看出是什么报警。如果你用ESP8266可以把数据推到MQTT服务器然后手机装个MQTT客户端就能远程接收。代码里我留了接口把UART_SendString换成ESP8266_SendToServer就行。4. 仿真验证与实物调试4.1 Proteus仿真工程搭建Proteus仿真最大的好处是你不用等PCB打样回来就能验证逻辑。我搭仿真的时候STM32模型用的是Proteus自带的STM32F103C8传感器用滑动变阻器模拟模拟量输出DHT11用了一个自定义的仿真模型。这里有个坑Proteus自带的STM32模型不支持某些外设比如ADC的注入通道。我一开始想用注入通道做火焰传感器的快速采样结果仿真跑不起来。后来改成规则通道加DMA仿真就正常了。所以如果你仿真跑不通先查查是不是用了模型不支持的外设。仿真里我设置了几个测试场景一是温度缓慢上升看预警和报警是否按预期触发二是烟雾浓度突变看响应速度三是火焰信号闪烁看滤波效果。每个场景都跑了至少十次确认逻辑稳定。4.2 实物调试中的典型问题实物调试遇到的问题比仿真多得多。第一个问题是DHT11读不出来示波器抓波形发现STM32拉低总线的时间不够十八毫秒。我用的延时函数是基于SysTick的但SysTick中断优先级设得太高导致延时被其他中断打断。后来把SysTick优先级降到最低问题解决。第二个问题是继电器吸合导致OLED花屏。OLED用的是I2C接口继电器吸合时电源波动导致I2C时序错乱。我在OLED的电源引脚并了一个一百微法的电解电容花屏现象就消失了。所以I2C设备旁边一定要放去耦电容别省这几个元件。第三个问题是火焰传感器误报。实验室的日光灯有五十赫兹的闪烁火焰传感器能检测到导致误报。我在代码里加了一个判断如果火焰信号的频率在四十五到五十五赫兹之间就认为是环境光干扰不触发报警。这个频率判断用定时器输入捕获实现准确率很高。4.3 常见问题速查表问题现象可能原因排查方法解决方案DHT11读数全零上拉电阻缺失万用表测数据线电压加四K七到十K上拉电阻继电器吸合时单片机复位电源尖峰示波器看电源纹波加磁珠和一百微法电容OLED花屏I2C时序错乱示波器看SCL/SDA波形电源引脚加去耦电容火焰传感器误报环境光干扰示波器看输出频率加频率过滤逻辑ADC读数跳动大参考电压不稳万用表测VREF加一百纳法和十微法电容串口乱码波特率不匹配示波器测位宽检查时钟配置和波特率按键不响应消抖时间不当逻辑分析仪看波形调整消抖时间为二十毫秒Flash参数丢失擦除前未解锁读Flash状态寄存器先解锁再擦除再写入4.4 调试工具与技巧调试STM32ST-Link Utility是个好工具可以看内存、看寄存器、单步执行。我习惯在报警逻辑的关键位置设断点比如阈值判断那几行然后手动改传感器值看程序是否按预期跳转。逻辑分析仪我用的是Saleae的八通道版本抓I2C、SPI、串口波形很方便。DHT11的单总线时序用逻辑分析仪抓出来一目了然比示波器还直观。如果你没有逻辑分析仪用示波器也行但触发设置要调好不然抓不到完整的时序。串口打印是个笨办法但很有效。我在代码里加了一个调试宏定义DEBUG_ENABLE的时候才编译打印语句发布版本关掉不占Flash空间。#ifdef DEBUG_ENABLE #define DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define DEBUG_PRINT(fmt, ...) #endif5. 开源资料说明与复现建议5.1 开源内容清单这次开源的内容包括三部分。代码部分是完整的Keil MDK工程芯片型号STM32F103C8T6用了标准外设库没有用HAL库。为什么不用HAL因为标准库代码量小执行效率高而且网上资料多遇到问题好查。工程里包含了所有驱动代码、应用逻辑、配置文件编译直接能过。原理图和PCB是Altium Designer格式的包含了原理图、PCB布局、BOM表。原理图里每个元件都标了参数和封装PCB是双层板尺寸是八十毫米乘六十毫米嘉立创打样的话五块钱就能搞定。仿真工程是Proteus 8.9版本的包含了STM32模型、传感器模型、显示模型。仿真里预设了几个测试场景打开就能跑。5.2 复现步骤与注意事项复现的第一步是打样PCB。把Gerber文件发给嘉立创选最便宜的双层板工艺就行。注意板厚选一点六毫米沉金工艺比喷锡好焊。打样回来先检查电源和地有没有短路用万用表蜂鸣档测一下VCC和GND不响再上电。第二步是焊接。先焊电源部分焊完测电压三点三伏和五伏都正常了再焊单片机。单片机焊完先烧个点灯程序确认最小系统能跑。然后再焊传感器接口和继电器驱动每焊一部分测一部分别一次性全焊完出了问题不好定位。第三步是烧录程序。用ST-Link接SWD接口四根线VCC、GND、SWDIO、SWCLK。Keil里选ST-Link Debugger能识别到芯片就能下载。如果识别不到检查接线和供电有时候是SWD引脚被占用了需要在代码里禁用JTAG保留SWD。第四步是调试传感器。先调DHT11读出来温湿度正常了再调MQ-2。MQ-2需要预热上电后等三到五分钟读数才稳定。火焰传感器用打火机测试距离十厘米左右应该能触发。5.3 后续扩展方向这个项目的基础框架搭好之后扩展空间很大。你可以加一个ESP8266模块把数据推到云平台手机远程查看。也可以加一个SD卡模块把历史数据存下来方便事后分析。还可以加一个人体红外传感器检测实验室有没有人没人的时候提高报警阈值减少误报。如果你想让系统更可靠可以加一个看门狗。STM32内部有独立看门狗和窗口看门狗独立看门狗用内部低速时钟即使主时钟挂了也能复位。窗口看门狗要求喂狗时间在特定窗口内能检测程序跑飞。我建议用独立看门狗配置简单可靠性也够。代码结构上如果你想移植到其他STM32型号主要改三个地方一是启动文件二是时钟配置三是外设引脚定义。应用逻辑基本不用动因为我是按分层架构写的驱动层和应用层分离换芯片只需要改驱动层。我在实际调试这个系统的时候最大的体会是传感器的可靠性比精度更重要。DHT11的精度是正负二摄氏度不算高但它稳定读一万次也不会突然跳变。MQ-2的精度受环境影响大但通过滤波和预热也能做到可用。所以选传感器的时候别光看参数表实际跑一段时间看看数据稳不稳定这才是关键。
返回列表