
1. 为什么我决定开源这套STM32安防项目先说说这个项目的来由。前阵子帮朋友做一个店铺安防改造要求很直接店铺关门后一旦有人闯入要能报警后厨燃气泄漏要能第一时间知道而且成本不能高维护要简单。市面上成品方案不是太贵就是功能不匹配干脆用STM32自己搭了一套做完以后发现这套东西的复用价值很高——原理图、代码、仿真全都齐活正好整理出来开源给大家。这个项目叫“智能安防与燃气监测系统”核心控制器是STM32F103C8T6就是大家常说的“蓝丸”核心板属于性价比极高、资料最全的入门级ARM芯片。系统做的事情其实不复杂通过多种传感器感知环境状态——有人闯入、有明火、有燃气泄漏然后根据预设逻辑驱动报警器、继电器、显示屏做出响应。整体就是一个典型的“传感器采集 → MCU逻辑判断 → 执行器响应”嵌入式闭环但它把安防和燃气监测两个场景整合到了一起工程价值比单一功能的小demo高不少。我开源的内容包括三块完整的Keil工程代码HAL库编写注释齐全、PDF版原理图、Proteus仿真工程。也就是说哪怕你手里暂时没有实物硬件也能先把系统跑起来看效果——这一点对初学者特别友好。这套东西适合谁三类人第一类是正在准备毕设的电子信息类学生直接拿来当毕设题目完全够深度加上扩展空间也大第二类是刚入门STM32、想找一个“完整项目”来练手的学习者因为代码不是流水账式教学代码而是工程化组织方式第三类是真正有低成本安防需求的小店主、租房党自己焊一套成本也就五六十块。接下来我按项目的实际拆解顺序把整体设计思路、硬件原理图要点、代码实现逻辑、仿真搭建过程、常见坑位一条条讲清楚。2. 系统整体设计与方案选型思路2.1 功能需求拆解这个系统到底要做什么动手画原理图之前先把需求列出清单。这套系统的核心功能点如下布防/撤防模式切换白天在店里不需要安防报警晚上关门后开启布防有人闯入才报警。人体闯入检测采用被动红外传感器检测范围内有人体移动即触发。火焰检测检测火焰光谱中的红外特征用于早期火情预警。燃气泄漏检测采用半导体气敏传感器对天然气、液化石油气等可燃气体敏感浓度超标触发报警。本地声光报警蜂鸣器急促鸣叫LED闪烁提示同时OLED屏幕显示当前状态。继电器联动输出可外接电磁阀燃气泄漏时自动切断气源或大功率警号、排风扇。状态可视化OLED屏实时显示各传感器状态、系统模式、报警标志。整体工作逻辑一句话就能说清系统上电后默认处于撤防状态通过按键可以切换布防/撤防布防状态下人体红外触发立即报警不论布防与否火焰和燃气触发就立即报警并联动继电器。这个逻辑在仿真和实物里都必须百分之百复现所以代码里我用一个全局结构体来管理状态机后续扩展远程上报功能也方便。2.2 主控选型为什么是STM32F103C8T6主控选STM32F103C8T6核心原因有三个。第一是性能足够。72MHz主频、64KB Flash、20KB RAM跑这套逻辑富余得很。哪怕后面加个ESP8266做联网上报资源也够用。第二是生态成熟。Keil MDK下配置HAL库开发网上资料铺天盖地寄存器手册、例程、FAQ一搜一大把遇到问题基本都能搜到答案。F103系列也是绝大多数高校嵌入式课程的主讲型号学生上手门槛最低。第三是成本极低。核心板批量拿货不到十块钱自己画最小系统板更是几块钱搞定。对于“低成本安防”这个需求来说主控成本不能喧宾夺主。有人可能会问用ESP32、树莓派Pico不也行吗行但没必要。ESP32优势在联网这套系统本地逻辑为主联网属于可扩展项而不是必需品F103做打底以后真需要上云串口挂个ESP8266模块就是最省事的加法。2.3 传感器选型与工作原理传感器是整个系统的“眼睛和鼻子”选型时我一共用了三颗人体红外传感器选的是HC-SR501。它的核心是热释电红外探头靠检测人体发出的8~14μm红外辐射变化来触发输出。模块上自带两个可调电位器一个调灵敏度检测距离约3~7米一个调延时输出高电平持续时间从0.5秒到200秒可调。这里我要特别提醒作为安防报警输入延时电位器一定要调到最小否则传感器输出一直拉高你没法区分“一直有人”还是“有人进入后又离开”。火焰传感器用的是普通四针火焰检测模块核心是一个对红外光敏感的NPN型光电三极管检测波长范围大约在760nm~1100nm这个波段正好覆盖火焰燃烧产生的红外特征。模块上带LM393比较器旋转电位器可以调节触发阈值检测到火焰时DO引脚输出低电平。需要注意的是太阳光、白炽灯在这个波段也有较强的红外辐射所以这个传感器容易出现误报我建议的安装位置是背对窗户、朝向来人方向。燃气传感器选的是MQ-2它对液化气、丙烷、氢气等可燃气体灵敏度较高响应时间在10秒以内。MQ系列传感器属于气敏电阻型内部是一个加热丝加一个二氧化锡半导体气敏层接触到还原性气体时电导率上升通过分压电路可以把气体浓度变成电压信号再传给STM32的ADC引脚。MQ-2模块上同样有一个电位器用来调节报警阈值。这里必须说清楚模拟量输出和数字量输出都要接到MCU上因为我们需要知道“浓度涨到多少了”而不仅仅是“超没超”后面做联动控制和状态上报时模拟量才是真正有用的数据。2.4 执行器与交互模块执行器选了三个有源蜂鸣器、继电器模块、OLED屏外加三个按键和两颗指示灯。蜂鸣器用有源蜂鸣器优点是不需要MCU给PWM波只要给高电平就响代码里控制就是GPIO置位/复位的事。用有源蜂鸣器虽然音色单一但它作为报警器要的就是声音尖锐刺耳218Hz左右的自带振荡频率正好合适。继电器模块是5V低电平触发一路常开常闭触点。在仿真里我拿它控制一个LED灯模拟电磁阀通断实物场景中常开触点串联在燃气电磁阀供电回路里报警时继电器吸合电磁阀断电关闭气源。选继电器而不是直接驱动电磁阀是因为电磁阀线圈电流大必须隔离。OLED用的是0.96寸I2C接口的SSD1306四根线VCC、GND、SCL、SDA接上就能用显示汉字需要字库。我把所有状态文本都做成中英混合显示中文字模用取模软件生成代码里直接调用。按键一共三个一个布防/撤防切换一个“报警清除”在布防状态下触发过报警后需要密码解锁思路这里简化成长按3秒清除一个系统复位兼“测试按键”模拟传感器触发。三颗LED分别指示电源、布防状态、报警状态。3. 原理图设计与核心电路解析3.1 最小系统板电路一套可以直接抄的设计我在开源包里放了两份原理图一份是STM32F103C8T6最小系统板原理图另一份是包含全部外设的系统总原理图。前者适合想自己打板的人后者适合直接把外设模块叠在核心板上用。F103C8T6最小系统看起来简单但有几个细节极易翻车。首先是供电电路芯片需要3.3V供电但市面上的传感器模块基本都吃5V于是系统里必须同时存在5V和3.3V两条电源轨。AMS1117-3.3低压差稳压器是主力方案输入5V输出3.3V输入输出各加一个10μF电解电容和一个0.1μF瓷片电容做滤波。其次是晶振电路我用的是8MHz无源晶振两个20pF负载电容。特别注意负载电容太小会导致振荡电路起振困难太大则会拉偏频率20pF是针对这种贴片晶振的通用值。如果板子上画了晶振但下载程序时识别不到芯片八成是晶振没起振用示波器量X1引脚有没有正弦波就能确认。第三是复位电路——一个10kΩ上拉电阻加一个0.1μF电容到地复位引脚NRST平时被拉高按键按下接地触发复位。ST-Link下载器也能控制这根复位线所以画原理图时要把复位引脚引出来。3.2 传感器接口电路上拉电阻和滤波电容别省HC-SR501的输出引脚是开漏输出必须外接10kΩ上拉电阻到3.3V否则输出高电平不是3.3V而是浮空态。很多同学直接杜邦线把DO引脚怼到MCU的GPIO上也能跑那是因为模块内部有一个1kΩ的上拉但为了信号稳定我习惯在原理图上再画一个。MQ-2模块的AO引脚输出的电压摆幅在0~5V而STM32的ADC最大只能承受3.3V直接接会烧引脚。这个必须在原理图上解决。方案是电阻分压AO输出串联一个10kΩ电阻然后并联一个10kΩ电阻到地中间抽头进ADC引脚电压被砍半0~5V映射成0~2.5V安全又线性。如果你的模块输出本身可调范围小也可以改用精密电位器分压这样还能顺带做软件校准。我在原理图里画的就是分压电阻网络加一个0.1μF滤波电容。继电器模块的输入控制脚是从MCU GPIO直接驱动的吗不是。GPIO高电平能力有限直接驱动继电器线圈不现实所以我用了NPN三极管S8050做开关GPIO输出高电平 → 三极管导通 → 继电器线圈通电吸合。同时在继电器线圈两端反向并联一个1N4007续流二极管防止断电瞬间线圈产生的反向电动势打坏三极管。这个续流二极管是很多自画板子容易漏掉的细节画的时候务必加上。3.3 供电设计与功耗估算这个系统在工作状态下的电流构成大致如下STM32核心板约50mAHC-SR501约60mA模块上稳压芯片自身就要吃50mAMQ-2加热丝是耗电大头约150mA~180mAOLED屏约30mA蜂鸣器约25mA继电器线圈约70mA合计下来峰值电流大约400mA左右。这意味着用USB口5V/500mA的供电勉强能带但留的余量太小。我实测过如果同时触发报警蜂鸣器继电器吸合再加上液晶刷新电流接近430mAUSB口有压降就可能造成MCU复位。所以设计供电时我建议两个方向一是使用5V/2A的手机充电头供电USB线尽可能短粗二是把MQ-2的加热丝供电独立出来用一个MOS管控制检测间隙可以断电让传感器休息省电又能降低模块老化速度。后者做低功耗版本时可以上常规版本不用这么麻烦。4. 代码实现与核心逻辑详解4.1 工程结构与模块划分代码用STM32CubeMX生成底层初始化Keil MDK下编译。整个工程分为几个核心模块不搞“一个main.c写天下”的坏习惯Core/ ├── Inc/ │ ├── main.h │ ├── gpio.h │ ├── adc.h │ ├── i2c.h │ └── tim.h ├── Src/ │ ├── main.c │ ├── gpio.c │ ├── adc.c │ ├── i2c.c │ └── tim.c APP/ ├── app_system.h // 系统状态结构体、宏定义 ├── app_system.c // 状态机主逻辑 ├── app_sensor.h // 传感器读取接口 ├── app_sensor.c // 传感器数据采集与滤波 ├── app_display.h // OLED显示接口 ├── app_display.c // OLED显示内容函数 ├── app_alarm.h // 报警控制接口 ├── app_alarm.c // 蜂鸣器、继电器控制 ├── app_key.c // 按键扫描与消抖这个分法的核心思路是“底层驱动和业务逻辑分离”。HAL库生成的代码尽量不改自己写的业务代码全部放在APP层。后面如果想要把系统改成RTOS版本或加联网功能只需要改APP层底层不用动。4.2 系统状态机设计多事件并发场景下如果全部用if-else堆叠代码会非常难维护。这个系统的逻辑虽然不算庞大但“布防/撤防”、“报警触发”、“报警清除”几个状态存在时序关系所以我直接上了状态机。typedef enum { SYS_DISARMED 0, // 撤防 SYS_ARMED, // 布防 SYS_ALARM // 报警 } SystemState_t; typedef struct { SystemState_t state; uint8_t fire_flag; uint8_t gas_flag; uint8_t pir_flag; uint16_t gas_adc_value; uint8_t alarm_cleared; } SystemStatus_t;状态迁移规则在app_system.c里用一个switch-case管理void System_Task(void) { switch (sys.status.state) { case SYS_DISARMED: Display_ShowDisarmed(); if (key_arm_pressed) { sys.status.state SYS_ARMED; Alarm_LEDOn(); } break; case SYS_ARMED: if (sys.status.pir_flag) { sys.status.state SYS_ALARM; } break; case SYS_ALARM: Alarm_Trigger(); if (key_clear_pressed) { sys.status.alarm_cleared 1; sys.status.state SYS_DISARMED; } break; } }这里需要注意火焰和燃气报警不能受布防状态约束——燃气泄漏发生时不管你设没设防都要报警这跟红外防闯入是两码事。所以在主循环里燃气和火焰标志位的判断是独立于状态机之外的一旦置位立即执行报警动作。4.3 燃气浓度采集与滤波别让ADC裸奔MQ-2输出是模拟信号STM32通过ADC1通道采集。直接读原始ADC值肯定不行——传感器本身有噪声环境温湿度也会让读数漂移。我在这里做了两层处理第一层是硬件滤波电路上加了RC低通滤波截止频率大约160Hz能滤掉大部分工频和高频噪声。第二层是软件滤波。我用了中位值平均滤波法连续采集10次去掉一个最大值和一个最小值剩下8个值求平均。这个滤波算法在慢变信号场景下效果很好计算量也小uint16_t Gas_GetFilteredADC(void) { uint16_t buf[10]; uint16_t temp; uint32_t sum 0; for (int i 0; i 10; i) { buf[i] HAL_ADC_GetValue(hadc1); HAL_Delay(10); } // 冒泡排序取中段 for (int i 0; i 9; i) { for (int j 0; j 9 - i; j) { if (buf[j] buf[j 1]) { temp buf[j]; buf[j] buf[j 1]; buf[j 1] temp; } } } for (int i 1; i 9; i) { sum buf[i]; } return (uint16_t)(sum / 8); }阈值怎么定我实测环境下MQ-2在清洁空气中ADC读数约在500~700之间分压后0~2.5V12位ADC满量程4096对应2.5V则600对应约0.37V。打火机气体靠近时读数能冲到2500以上。所以我设了两级阈值一级报警阈值1600二级联动阈值2200。一级只亮灯鸣叫二级才触发继电器切断气源。这么做的好处是避免浓度刚到临界值就误切断电磁阀毕竟切断后重新开阀需要人工干预代价不低。4.4 按键扫描与消抖延时消抖比什么都稳按键扫描我采用10ms定时器轮询方式在TIM2的中断回调里置一个标志位主循环里每10ms调用一次按键扫描函数。消抖逻辑用经典的两次采样确认第一次读到按下延时10ms后再读一次如果还是按下才认为是有效按键。void Key_Scan(void) { static uint8_t last_key_state 0; uint8_t current_state; current_state (HAL_GPIO_ReadPin(KEY_ARM_GPIO_Port, KEY_ARM_Pin) GPIO_PIN_RESET); if (current_state 1 last_key_state 0) { HAL_Delay(10); if (HAL_GPIO_ReadPin(KEY_ARM_GPIO_Port, KEY_ARM_Pin) GPIO_PIN_RESET) { key_arm_pressed 1; } } last_key_state current_state; }长按3秒清除报警的实现在按键扫描函数里加计数值连续检测到按键按下超过300次10ms一次就置长按标志。这个逻辑看着简单但实际写代码时很容易忘掉“释放后才处理短按事件”的细节导致长按和短按同时触发。我的处理方式是短按标志只在按键释放沿置位长按标志在持续计时满3000ms时置位二者互斥。4.5 OLED显示内容布局0.96寸OLED屏幕128x64像素一屏能显示4行16点阵汉字每行8个16x16字。我的显示布局分两种界面布防界面显示三行第一行系统状态布防/撤防/报警第二行燃气ADC数值实时刷新第三行火焰 正常/检测到第四行人体 无/有人报警界面强制覆盖显示第一行!! 报警 !!第二行类型燃气/火焰/入侵第三行ADC数值第四行按清除键复位OLED刷新频率不要太高我实测5Hz刷新率最合适——低了感觉卡高了浪费CPU且会有闪烁感。每次刷新前先清屏再重绘清屏用SSD1306_Fill(Black)重绘用SSD1306_DrawString这套SSD1306驱动库网上到处都有移植方便。4.6 报警策略与继电器联动报警策略我做了优先级设计燃气 火焰 入侵。优先级高的报警源触发时OLED优先显示高优先级来源蜂鸣器发出不同长度的鸣叫节奏以示区分。燃气报警蜂鸣器连续鸣叫500ms停200ms循环火焰报警蜂鸣器连续鸣叫300ms停100ms循环入侵报警蜂鸣器连续鸣叫200ms停200ms循环继电器动作逻辑是燃气二级报警时继电器吸合切断电磁阀同时锁定——即使燃气浓度降下来继电器也要保持吸合状态人工确认安全后长按复位键才能恢复。这个叫“故障锁存”是工业安防的常见设计要求防止自动复位导致危险反复出现。代码实现上在app_alarm.c里维护一个锁存标志没有用户干预不自动清除。5. 仿真搭建没买硬件也能跑起来的完整方案5.1 Proteus仿真工程配置要点开源包里带的Proteus仿真工程可以直接用Proteus 8.9以上版本打开。如果打开后元件显示不全多半是库版本问题按F2打开库管理器重新加载一下就行。Proteus里搭建这套系统的关键点是STM32F103C8T6的模型自带调试接口程序通过“Program File”选项加载编译生成的hex文件。加载后点运行MCU就真的在仿真环境里跑起来了。三个传感器在Proteus里都需要用等效电路替代这里要重点说明HC-SR501在Proteus库中没有现成模型我用的是一个开关加一个上拉电阻模拟。开关闭合时模拟人体触发输出高电平给MCU。火焰传感器同理用一个按键开关模拟DO输出低电平。MQ-2用的是一个滑动变阻器POT-HG代替滑动变阻器输出电压范围0~5V经过原理图中的分压网络后进ADC。仿真时拨动滑动变阻器改变ADC读数就能看到燃气浓度数值变化和报警逻辑响应。5.2 仿真到实物的偏差点仿真能跑通和实物能跑通是两回事这一点必须给初学者打好预防针。最大的坑是时序不真实。Proteus仿真的时钟频率和代码里配置的时序是软件模拟的ADC采集瞬间跳变不带上电噪声蜂鸣器也不存在驱动力矩问题。所以仿真里能正常工作的延时参数、滤波参数拿到实物上可能要调。第二个坑是Proteus的ADC模型非常理想化。真实MCU的ADC有一个采样时间和转换时间的配置如果CubeMX里配置不当采集出来的数值会偏大或者跳动剧烈。仿真是直接从ADC外设模型拿到数值完全规避了这个问题。出现“仿真正常实物不准”的情况优先检查ADC采样时间是不是太短我一般配置为55.5个采样周期。第三个坑是供电模型。仿真里电源是理想电压源不存在电流不足导致电压跌落的问题所以如果实物上用USB供电出现随机重启仿真里根本复现不出来。遇到这类问题先量一下MCU供电脚电压在报警瞬间有没有下跌超过100mV。5.3 仿真调试技巧看变量比看现象更重要Proteus仿真有一个特别好用的功能——可以在ST Co-Simulation模式下运行这个时候MCU程序里定义的变量可以直接在Proteus的“Debug Menu”里看实时值配合断点调试找逻辑bug的效率比看硬件现象高得多。我的习惯是先在仿真里跑一遍核心场景矩阵测试场景操作预期结果撤防状态模拟人体触发无报警OLED显示撤防布防状态模拟人体触发蜂鸣器鸣叫OLED显示入侵任意状态调高燃气浓度模拟量蜂鸣器鸣叫显示燃气报警任意状态模拟火焰触发蜂鸣器鸣叫显示火焰报警报警状态长按清除键3秒复位到撤防状态把这张表过一遍逻辑基本就稳了。实物测试也照这个矩阵来只不过每个场景都要真实触发传感器。6. 常见问题与排查技巧实录6.1 程序烧录进芯片后没反应这个是最常见的问题多数原因有三个第一BOOT0引脚电平不对。STM32F103C8T6的BOOT0引脚通过10kΩ电阻下拉到地如果悬空或高电平芯片会进入系统存储器引导模式程序跑不起来。检查BOOT0是否接地。第二ST-Link接线错误。SWD接口只用到SWDIO、SWCLK、GND、3.3V四根线其中SWDIO接PA13SWCLK接PA14。有些盗版ST-Link的接口定义不标准接反是常事。如果下载时报“No target connected”先拿万用表量引脚电压SWDIO和SWCLK应该在3.3V附近。第三复位电路问题。我刚才说过如果NRST引脚上无上拉电阻或者电容过大导致复位时间过长也会造成烧录失败。排查时可以用镊子短按NRST到地再点下载看能不能捡漏。6.2 MQ-2数值漂移严重、报警阈值天天变MQ-2传感器上电初期有一个“预热漂移”刚通电那会加热丝温度没稳定气敏层电阻一直在缓慢变化读数可能从400飘到900持续5~10分钟才稳定。解决方法是软件上做开机预热逻辑。系统上电后前30秒不判断燃气报警只在OLED上显示“预热中…”30秒后再开始ADC采样和阈值判断。这30秒是给传感器留出稳定时间代码简单却非常管用。另一个漂移原因是MQ-2标定不准。MQ-2的灵敏度特性会随使用时间衰减出厂标定的阈值在几个月后会明显偏移。建议每个月做一次“清洁空气校准”——在确认无燃气的环境中读取当前ADC均值把这个值作为基准值存到Flash里报警阈值改成“基准值固定增量”的方式这样传感器老化后报警阈值也会自动跟随。6.3 红外传感器频繁误报警HC-SR501误报警大部分是安装位置不对导致的。我踩过的坑是把它安装在空调出风口旁边空调风吹动窗帘就触发一次一晚上报警七八回。被动红外传感器对温度变化敏感热风直吹等于持续制造红外扰动。安装位置建议离地2.2~2.5米朝向人员必经路径避开空调、暖气、窗户和玻璃幕墙。灵敏度旋钮从最小慢慢调大调到“有人经过才触发静止不触发”的位置就停不要一味追求高灵敏度。模块上还有一个“重复触发模式”跳线H/LH模式是持续有人就一直输出高电平L模式是只输出一个脉冲。做防闯入报警应该用L模式H模式会导致人在房间里一直保持报警状态没法通过“离开后自动复位”来恢复。6.4 继电器吸合时MCU复位这个故障100%是电源问题。继电器线圈吸合瞬间电流冲击大如果电源带载能力弱电压瞬间跌落超过MCU工作电压下限F103是2.0V~3.6VMCU就复位了。排查分两步第一步确认供电电流余量。用5V/2A的电源代替原有电源如果故障消失说明原来的电源余量不够。第二步检查继电器驱动电路。确认续流二极管是否接反或漏装二极管方向应该是负极接线圈正极、正极接线圈负极。续流二极管的作用是在三极管关断瞬间提供回路没有它线圈产生的反向电动势直接冲击电源轨MCU复位就是家常便饭。还有一种可能继电器供电和MCU供电共用一条线且走线太长导致继电器工作时地线压降过大。建议把继电器模块电源独立走线从总电源处直接取电不要串在MCU的供电回路上。6.5 OLED显示花屏或白屏SSD1306驱动OLED常见三个问题I2C地址不对。SSD1306默认地址是0x787位地址0x3C但有些模块的地址选择电阻焊上了地址会变成0x7A。代码里检查I2C_Addr定义。初始化时序不匹配。SSD1306初始化需要按厂家数据手册的序列发送命令如果初始化命令序列里少了“电荷泵开启”的命令0x8D、0x14屏幕就一直白屏不发亮。网上很多精简版驱动库省略了这条命令要注意检查。I2C速率过快。SSD1306的标准I2C速率是100kHz快速模式下可以到400kHz但有些盗版模块的I2C粘合能力较弱400kHz下通信容易出错导致花屏。把I2C时钟从400kHz改到100kHz能解决大部分花屏问题。6.6 燃气报警值一直为0或满量程ADC读数为0检查GPIO是否配置成模拟模式。在CubeMX中ADC输入引脚必须配置为Analog模式如果配成GPIO_INPUTADC外设读到的值永远是0。ADC读数满量程4095大概率是引脚悬空或分压电阻焊接问题。分压网络的下拉电阻脱落会导致引脚电平直接被上拉到3.3V读数自然就是满量程。用万用表量一下ADC引脚对地电压如果接近3.3V但模块AO输出正常检查分压电阻。7. 开源代码的工程规范与二次开发建议7.1 代码组织方式为什么不用寄存器开发开源代码我选了HAL库而不是寄存器或标准库理由是这套代码的受众大概率是学生或刚入门的人HAL库的可读性和可移植性最好。用CubeMX生成初始化代码后业务逻辑和底层封装分离哪怕不熟悉芯片寄存器细节也能改出自己想要的行为。但我也必须说一句公道话HAL库的抽象层有性能损耗比如GPIO翻转速度比寄存器直接操作慢不少。在这套安防系统里所有外设的响应时间都不是微秒级要求性能损耗完全无所谓。如果你的二次开发方向是高速数据采集这类场景可以针对热点函数改用寄存器操作我是把HAL_GPIO_WritePin这类高频调用宏重定义过的方便替换。7.2 合理的扩展方向从本地报警到远程告警这套系统最大的扩展空间在于联网。目前它是一套本地安防系统报警只体现在本地声光上。下一步最自然的升级是加ESP8266模块通过串口与STM32通信把报警事件、传感器数值推送到云平台或微信小程序。代码层面我已经预留了扩展点app_system.c里的事件回调逻辑是全量输出到串口的只要你把串口重定向到ESP8266的AT指令通道再写一个简单的HTTP POST或MQTT协议就可以上报。我这套代码里串口1默认就是调试输出波特率115200格式是JSON——这个设计就是给后续联网做的。如果你想做低功耗版本可以把MCU切到STOP模式通过RTC定时唤醒或外部中断唤醒再配合MQ-2加热丝的MOS管控制整体功耗可以降到20mA以下用18650电池供电能撑很久。7.3 PCB打板建议与元件选型清单如果你的目标是把它做成一块紧凑的成品板而不只是核心板加模块堆叠我整理一份关键元件选型参考元件推荐型号/参数备注MCUSTM32F103C8T6 LQFP48可替换为STM32F103CBT6稳压芯片AMS1117-3.3输入5V耗散功率注意别超晶振8MHz 无源贴片负载电容20pF蜂鸣器有源5V蜂鸣器额定电流≤30mA继电器SRD-05VDC-SL-C5V线圈10A触点三极管S8050驱动继电器线圈续流二极管1N4007必装OLED0.96寸I2C SSD1306高度1.3mm适合贴装打板时建议四层板信号完整性强很多成本其实只多几十块。两层板也能跑但要注意模拟地和数字地分开布线在电源入口单点汇合继电器驱动部分和传感器模拟部分要拉开距离继电器触点通断大电流时的电磁干扰会影响ADC精度。8. 最终使用体验与改进方向整套系统我从画原理图到调试完成前后花了大概一个周末。最花时间的环节不是写代码反而是传感器阈值整定——MQ-2的基准值受温湿度影响很大同一颗传感器在室内和室外、夏天和冬天读数能差出300多这就逼着我加了开机预热和动态基准校准的逻辑。这个坑对任何做气敏检测的同学都有参考价值。还有一个我特别想强调的细节报警逻辑一定要做到“宁可误报两次不可漏报一次”。安防系统的核心价值不是精确而是可靠。我在火焰检测阈值上故意调得偏灵敏虽然偶尔会因为阳光直射误报几次但绝对不会出现明火在眼前还不报警的情况。对于燃气检测也一样一级报警阈值偏低只要浓度趋势往上走就先通知你具体是真泄漏还是炒菜油烟靠的是二级联动阈值来区分。这套开源资料我已经把代码、原理图、仿真工程整理好放在开源包里了Keil工程、Proteus仿真、PDF原理图、元件清单都在下载后按博文里的步骤走一遍从仿真到实物基本不会卡壳。最后再分享一个小经验自己第一次焊这套板子的时候一定要先把电源焊好上电测试确认3.3V稳定输出后再焊MCU和其他芯片一旦先焊完所有元件再上电短路了根本没法定位问题。按部就班来这项目就能顺利跑起来。