STM32消防预警系统设计与Proteus仿真实战:从传感器采集到报警状态机
做实验室消防预警控制系统主题绕不开单片机采集、报警判断和可靠性验证这几件事。我这里整理的开源版本把代码、原理图和仿真三条线一起放了出来主控平台选了STM32F103C8T6整体流程覆盖温湿度、烟雾浓度、火焰信号的采集再到声光报警、串口输出和继电器动作属于很典型但很有教学价值的STM32项目。如果你正在做嵌入式方向的项目练手、毕业设计或者想把实验室安全监控做个低成本方案这套东西可以直接拿过去改。很多朋友看到“消防预警”第一反应是传感器越多越好实际上系统能不能用拼的是采集稳定性和报警判据。这篇文章不聊虚的我按设计思路、原理图、代码实现、仿真复现、常见坑位、开源发布顺序讲尽量把每一步背后的取舍说清楚这样你拿到代码和原理图之后能自己改而不是只会烧录。1. 方案选型与整体设计思路1.1 这个项目的价值在哪儿实验室、机房、仓库这类场景环境参数一旦异常早发现几秒钟可能就避免了设备损失。相比商场那种完整消防主机实验室内部更需要低成本、可定制、能联网上报的小系统。STM32F103C8T6之所以被大家反复拿来用一是价格便宜二是资料极多三是外设足够覆盖大多数传感器即便是做基于STM32的毕业设计也远比一个裸机跑马灯更能体现完整工程能力。我开源这个项目核心想让大家看到三件事传感器不是挂上去就完事电气连接和驱动时序要单独设计报警模块不能一个if打天下状态机加滤波才靠谱先用Proteus仿真把逻辑跑通再上实物能省大量调试时间。这套东西真正的应用场景不只是抄作业把阈值、传感器型号替换成自己的现场需求它就能变成机房温度监控、厨房燃气报警、宿舍烟雾检测的底子。项目开放性就在这里框架比具体实现值钱。1.2 数据链路和执行链路整个系统的数据流并不复杂但每个环节都有讲究。环境参数经过传感器变成电压或电平信号进入STM32的GPIO、ADC或者定时器输入捕获STM32内部做滤波、阈值判断和状态迁移状态结果驱动蜂鸣器、LED、OLED显示、继电器模块同时通过串口把调试信息发到上位机。这里最关键的一点是采集和响应不能互相卡死否则传感器延时读取时报警输出会被拖慢。所以我在代码里把传感器读取做成周期任务把报警输出放在定时器回调里保证任何时间点出现危险信号蜂鸣器都能立刻响应。从工程化角度看消防预警系统最忌讳的就是“采集到了才报警”而是要在主循环中不断轮询关键输入在中断中快速响应紧急事件。STM32的定时器中断频率我设成1kHz每1ms扫描一次火焰信号和紧急按键这种硬实时设计在后期仿真里非常容易验证。1.3 器件选型是怎么定的核心器件列表如下每个的选择我都说下理由模块选型选择理由主控STM32F103C8T672MHz主频足够用64KB Flash承载工程和驱动绰绰有余温湿度DHT11便宜、库多、驱动经典适合教学演示烟雾检测MQ-2模拟量输出能体验ADC采样和阈值滤波火焰检测红外火焰传感器数字/模拟双输出响应快适合作为紧急信号显示0.96英寸OLED SSD1306I2C接口只占两根线信息直观预警执行有源蜂鸣器、LED、继电器蜂鸣器用于声报警继电器可控制排风扇或电磁阀按键独立按键主要有两个用途测试自检、解除报警有人可能会问为什么不用ESP32或者更高级的芯片Fire alarm系统本身以可靠性优先STM32F103在裸机环境下完全可控不引入操作系统复杂度也方便观测每一个外设的寄存器行为。对学习和移植来说这是最舒服的平台。DHT11精度虽然一般但作为消防辅助温湿度监测已经够了真要高精度环境检测建议换SHT30代码改I2C即可。2. 硬件原理图设计要点2.1 最小系统和电源部分MCU最小系统看着简单但看原理图时往往有一堆新人的坑。STM32F103C8T6需要8MHz外部晶振两个20pF左右的负载电容3.3V电源引脚全部接上100nF去耦电容。复位引脚用10kΩ上拉到3.3V再并联一个100nF电容稳定复位。BOOT0直接通过10kΩ下拉到GND保证从Flash启动。你喜欢用内部HSI也可以省掉晶振但这里我希望时钟源稳定尤其后面DHT11时序和串口波特率都依赖系统时钟外部晶振更靠谱。电源部分我用USB的5V输入经过LM1117-3.3稳压到3.3V给MCU和传感器供电。注意不要混乱DHT11数据口和OLED、FLASH、按键这些外设都在3.3V域工作而MQ-2的加热电阻和继电器模块的线圈可能用到5V所以原理图里至少需要5V和3.3V两个电源网络。画图时要习惯用电源符号和网络标签而不是把导线从板子一端拉到另一端否则电源扇出会把图面塞成一团DRC检查也不好查。2.2 DHT11传感器接入电路DHT11是单总线器件数据线空闲时被上拉到高电平主机发送起始信号DHT11拉低响应后开始传输40位数据。它的数据引脚内部没有强上拉所以外部必须在数据线和VCC之间加一个4.7kΩ上拉电阻。有些模块板载了上拉电阻但如果你自己打样板必须预留。原理图绘制时容易犯的错是把GPIO直接当成主机推挽输出又在同一根线上挂另一个I2C器件。DHT11不是标准I2C它的时序更多是微秒级电平翻转我在代码里会临时切IO模式原理图设计上保证这根线只连DHT11和单片机的PA0不共享总线。如果条件允许在数据线上预留一个100Ω串联电阻和一个小电容能吸收长导线带来的反射噪声。仿真里看不到这种细节实物调试时很有用。2.3 MQ-2烟雾传感器电路MQ-2传感器内部是一个气敏加热电阻随着可燃气体浓度变化其输出电阻也会变化常见模块会输出模拟电压A0和数字电压DO。数字输出是模块上板载比较器生成的阈值固定没法微调所以我更建议把A0信号引入STM32的ADC通道通过软件动态设置报警阈值这样在现场校准时非常方便。MQ-2加热部分通常要求5V供电模拟输出信号在模块工作电压5V时最高接近5V超过STM32引脚耐压范围。实际上模块厂商多做了一个电位器分压加上LM393比较器A0输出一般在0-5V之间浮动。稳妥起见原理图上A0经过电阻分压网络缩到3.3V以内再进PA1。常用的做法是10kΩ串联、10kΩ下拉在ADC引脚并联一个100nF电容做低通滤波。这样即使用5V供电模块采样电压也不会超过3.3V。如果你买的模块本身已经是3.3V供电版本直接连接即可。2.4 蜂鸣器、继电器和按键电路报警输出这一类大功率器件千万不能直接把GPIO接到蜂鸣器和继电器线圈。STM32引脚是弱驱动最多输出几毫安到十几毫安推有源蜂鸣器偶尔还行但继电器线圈的驱动电流都几十毫安直接接会损坏GPIO。我的原理图里蜂鸣器使用NPN三极管S8050驱动基极通过1kΩ限流电阻接PA12发射极接地集电极接蜂鸣器负极蜂鸣器正极接5V。继电器则使用NPN或ULN2003驱动线圈两端必须并联一个反向续流二极管1N4007否则断电瞬间反向电动势容易打坏三极管。这一段我特别想强调消防设备的安全性和稳定性优先驱动电路留足够余量上电自检时继电器多吸合几次也是测试重点。按键采用独立上拉输入模式按下引脚接地软件里用HAL库读取GPIO电平做去抖。报警解除按键可以直接接到外部中断输入也可以在扫描循环里轮询。我推荐外部中断方式因为这属于“紧急操作”中断响应更及时且不受DHT11读取阻塞影响。2.5 嘉立创EDA画原理图的实操习惯用嘉立创EDA画这套图时有几个习惯值得从一开始就建立起来。一是所有网络都需要有意义的名字比如5V、3V3、SMOKE_ADC、FIRE_IN、BEEP_CTL不要只靠连线判断。二是放置器件后马上写位号不要等画完再统一标不然很容易漏。三是熟练使用“电气规则检查”DRC会在你删除电源引脚、悬空输入、短路网络时报错及时处理。嘉立创EDA里DHT11的符号有多个版本注意选带VCC、DATA、GND、NC四脚的标准封装自建符号时把电源引脚勾选为“电源”类型会更方便统一布线。原理图画好之后我习惯导出一份PDF示意和一份BOM表BOM表里的封装一定要与实际元件库对应这样打样后焊接效率会高很多。3. 代码逻辑与核心模块实现3.1 工程初始化和传感器驱动写法代码工程基于STM32CubeMX生成初始化时钟配置为外部8MHz晶振倍频到72MHzADC1、USART1、I2C1、定时器和GPIO外设在Cubemx里勾选后自动生成。有人对Cubemx生成的一堆代码有抵触但工程实践里生成代码是常态你要改的是业务逻辑层而不是寄存器初始化效率高很多。DHT11读取是这套代码里最容易让人翻车的点。它的通信协议是单总线总线上只有一根数据线主机要把GPIO模式从输出切到输入并且时序精确到微秒级。先给一个基本代码片段#define DHT11_PORT GPIOA #define DHT11_PIN GPIO_PIN_0 void DHT11_Start(void) { GPIO_InitTypeDef io; __HAL_RCC_GPIOA_CLK_ENABLE(); io.Pin DHT11_PIN; io.Mode GPIO_MODE_OUTPUT_PP; io.Speed GPIO_SPEED_HIGH; HAL_GPIO_Init(DHT11_PORT, io); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); delay_ms(20); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); delay_us(40); io.Mode GPIO_MODE_INPUT; io.Pull GPIO_NOPULL; HAL_GPIO_Init(DHT11_PORT, io); } uint8_t DHT11_ReadBit(void) { uint32_t cnt 0; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET); while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET cnt 100); if (cnt 50) { return 0; } while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET); return 1; }为什么ReadBit要数高电平持续长度这正是STM32测频法在单总线时序里的变体和超声波测距里测回波高电平宽度的思路一样。DHT11用高电平持续26-28微秒表示逻辑070微秒左右表示逻辑1测量高电平宽度比盲delay更可靠。实际的OTDR如果你手头有示波器会看到心率波动单靠delay_us很容易因为中断打断读出错误数据计时方式容错性更强。读取完整40位并校验数据格式也很重要。40位分别是湿度整数、湿度小数、温度整数、温度小数、校验和如果前四个字节累加和等于最后一个字节才认为这次采集有效。要注意DHT11的模块质量差异极大有些廉价模块在初次上电前几百毫秒响应不稳定代码里要连续读取失败多次后保留上一次有效值不能把乱码直接送进显示。3.2 报警状态机与消抖滤波报警逻辑如果只是“温度高于50度就报警”系统会敏感到没法用。实验室里的电炉、消毒设备都可能瞬时产生高温或异常烟雾一两个误报就会让人对系统失去信任。我的处理方法是设计一个三态报警状态机正常、预警、报警。预警状态是连续两次采样超阈值才进入报警状态是连续三次超阈值并且持续时间超过500ms才触发。温度、烟雾、火焰三种参数分开计数互相取或但每种参数都要有自己的触发次数。代码里可以这样实现typedef enum { STATE_NORMAL, STATE_WARN, STATE_ALARM } AlarmState; AlarmState smoke_state STATE_NORMAL; uint8_t smoke_over_count 0; if (adc_value SMOKE_HIGH_THRESHOLD) { smoke_over_count; if (smoke_over_count 3) smoke_state STATE_ALARM; else smoke_state STATE_WARN; } else { smoke_over_count 0; smoke_state STATE_NORMAL; }这个“连续N次确认”的方案虽然简单但在消防这类场景足够务实现场烟雾浓度会波动不会像软件开关一样瞬间突变。如果你还想更平滑可以对ADC值做滑动平均滤波保留最近10次采样每次算平均值和当前值差差大于阈值才认为真的变化了。我建议在真实硬件上做一次土味标定用打火机气体短暂触碰MQ-2观察串口打印的原始ADC值然后按照“正常值的三倍”设阈值这个比例比拍脑袋定一个固定数值可靠。火焰传感器的处理比较特殊它反应极快一旦检测到红外火焰信号不能走“连续三次确认”的慢路径要立即报警。实践中的处理方式是把火焰信号接到外部中断中断服务函数里置一个紧急报警标志主循环检测到这个标志后立刻拉响蜂鸣器并点亮红灯。这个设计让我在Proteus仿真时看得很清楚普通烟雾报警有几百毫秒延迟但火焰中断几乎是立竿见影的。3.3 OLED显示和串口打印系统里有一块0.96寸OLED走I2C接口只占用PB6和PB7两个引脚。因为分辨率和刷新能力有限我不建议每次刷新全屏更合理的做法是把画面分成固定区域温度湿度区域、烟雾状态区域、报警状态区域每秒刷新一次。OLED驱动的字体库会占不少Flash选一个3232的小图标和1616英文字体就够用了没必要把整个中文字库塞进去否则64KB Flash会吃紧。串口调试是这套系统里最好用的工具。初始化USART1波特率1152008N1PA9接TXPA10接RX。我把HAL库的printf重定向到串口每两秒打印一帧结构化数据比如[SYSTEM] Temp26.1C Hum45% SmokeAdc198 FireOK StateNORMAL很多人在仿真阶段喜欢看虚拟终端但一搬到实物就看不到内容原因往往是串口打印里包含了浮点格式的%f而Keil MDK默认的微库没办法正确输出浮点需要在Keil的配置里勾选“Use MicroLIB”或者自己写整数定点格式。把浮点乘10转成整数再打印是我的习惯做法temp_x10 (uint16_t)(temp * 10);用整数格式打印既稳定又方便上位机解析。3.4 按键交互和非阻塞延时设计主循环里不能写一个大delay_ms(5000)之类的阻塞延时否则报警响应会完全失灵。STM32项目里除非你在初始化过程中需要等待上电稳定其他地方尽量使用非阻塞式设计。按键消抖用定时器扫描的方式每10ms读一次按键状态连续两次读到不同状态才认为有效。报警解除按键送入外部中断中断里设置标志位由主循环决定什么时候关掉蜂鸣器和继电器而不是中断里直接操作GPIO这样可以避免函数重入问题。有人会把所有事情都堆在主循环里DHT11阻塞230ms读一次OLED刷新又要100ms串口打印再来几十毫秒整个循环周期可能超过400ms。这个循环周期对消防预警来说太慢了。我的代码结构里主循环拆成三个任务第一优先级检查紧急标志第二优先级更新传感器数据和报警状态第三优先级刷新显示和串口。任务之间通过共享变量和标志位传递数据核心思想是紧急路径永远不被普通路径卡死。4. 用Proteus把整套系统仿真跑起来4.1 为什么要先在Proteus里做仿真这次开源包里的仿真文件用的是Proteus 8.x工程。仿真不是摆设它能让你在没有实物元件、没有示波器的情况下提前验证STM32代码逻辑是否合理。尤其是DHT11这种带严格时序的传感器网上资料容易抄错在仿真里跑不过去时多半是代码问题实物跑不过去时可能是硬件问题。先把代码问题在仿真里滤掉后面焊板子的成功率会明显更高。另外仿真文件对跨平台协作也有意义。导师或同事不一定有完整硬件环境只要装了Proteus就能打开工程看整体电路、加载HEX文件一键看到运行效果。所以我认为一个开源项目只要包含原理图就应该尽量附带对应的仿真工程这是嵌入式开源项目管理里很加分的一项。4.2 仿真搭建步骤如果你想自己搭一遍仿真过程大致是这样的先在Proteus里新建一个工程从元件库搜索STM32F103R6或STM32F103C8放置到原理图区。接着放置DHT11模型、OLED模型、两个LED、电阻、按键、一个虚拟终端和一对电源端子。DHT11的模型在部分Proteus版本中可能不在默认元件库如果搜不到需要从外部导入一个第三方模型库文件或者用DS18B20这类类似单总线传感器替换验证“读传感器”流程但最终代码要回到DHT11。连接电路时注意DHT11的信号线同样需要上拉电阻。打开MCU属性指定Program File为你用Keil编译生成的HEX文件并把Clock Frequency设置为8MHz这和代码里的外部晶振参数保持一致。运行仿真后虚拟终端里如果能看到串口打印的环境数据逻辑就基本通了。OLED模型在Proteus里不一定会显示中文只显示英文是正常的不影响判断程序流程是否正常。4.3 仿真中的DHT11时序问题很多同学第一次在Proteus里跑单总线传感器发现数据全是FF或者在“data busy”期间卡死。这是因为仿真模型的微秒级行为和你代码里的延时并不完全等价主频配置不对时更是差之毫厘谬以千里。比较有效的调试方式是在代码里加一个调试脚每次DHT11时序变化时翻转一个GPIO然后在Proteus通过虚拟示波器抓这个脚的电平波形。因为没有示波器时逻辑分析仪也是一种办法。把DHT11的DATA引脚和调试引脚一起接到示波器通道一眼就能看出起始信号的反转是否完整、40位数据波形是不是基本平整。仿真中暴露出来的问题90%出在超时判断和GPIO方向切换这两个地方不一定是硬件连接错误。还要提醒一句仿真环境对传感器的响应速度和时序都是理想化模拟它只能证明你“程序逻辑”是通的不能替代真机上MQ-2的飘移校准。仿真里你把烟雾阈值设成150能触发实物上MQ-2的ADC值可能稳定在200以上所以拿到硬件后必须重新标定阈值。4.4 仿真看门狗和边界情况测试推荐大家在仿真阶段专门做几组边界测试而不只是让系统正常跑。第一组是模拟传感器断线比如把DHT11数据线断开看系统会不会死循环卡住第二组是模拟烟雾超限调整烟雾传感器的模拟输入电压让它从正常值缓慢升上阈值确认报警状态机进入警报第三组是同时给火焰信号和烟雾信号确认紧急通道优先响应。这些边界测试在开源项目里尤其重要因为看代码的人最担心的就是这个系统在异常工况下会不会完全瘫痪。我的代码里给DHT11读取函数加了重试上限如果连续读取6次都失败系统会进入传感器故障状态蜂鸣器按特定节奏响三短一长用来区分传感器故障和真实火灾这个细节实践性很强。5. 常见问题与排错实录5.1 STM32无法识别USB设备实物调试阶段最扫兴的问题就是用ST-LINK连接开发板时电脑提示无法识别USB设备。出现这个现象先别急着怀疑硬件坏了。大多数ST-LINK无法识别的根源是驱动没装好或者接触不良。STM32开发板上常见的ST-LINK/V2使用的是专用驱动在Win10/Win11上偶尔会掉到蓝牙或通用USB设备类别里。处理办法是插上ST-LINK进入设备管理器右键更新驱动手动指向STM32 ST-LINK Utility安装目录下的驱动文件。还有一种情况是板子由外部额外供电但ST-LINK的SWD接口没有先接地线。ST-LINK与目标板之间至少需要SWDIO、SWCLK、GND这三条线有些偷懒只接两根线系统识别IDCODE不稳定看起来就是不识别。如果USB插上后设备管理器有感叹号卸载设备后重插一般能恢复。另外如果你用串口ISP烧录而不是ST-LINK烧录前要把BOOT0拉高烧完再拉回来否则复位后进不了用户程序。5.2 Keil MDK环境兼容问题Keil MDK安装时最容易被坑的是版本路径和包管理器。可能有人的电脑之前装了Keil C51后装MDK后两个环境混在一起导致工程打开后找不到器件型号或者编译时报找不到头文件。这个问题的本质是C51和MDK是两套不同的工具链放在同一个目录会造成组件混乱。建议分开安装到不同目录或者用不同的IDE版本工具链管理。新建STM32工程时一定要在Pack Installer里安装对应的STM32F1系列Device Pack否则连器件型号都选不出来。还有一种很典型的绿色安装问题打开Keil或STM32CubeProgrammer提示“由于找不到msvcp140.dll无法继续执行代码”。这不是项目工程的问题而是系统缺少Visual C 2015-2022运行库。STM32CubeMX、Keil、STM32CubeProgrammer很多基于Qt工具链都需要这个运行库。下载对应版本安装后重启这个问题一般马上消失。如果你用的是绿色版Keil尤其容易在别人电脑上遇到这种缺库问题我会在开源仓库的README里明确写上这一步。5.3 DHT11读不到数据的实战排查DHT11读不到数据往往不是一种原因。我的排查路径是这样的先确认GPIO方向切换有没有生效很多代码里用了开漏模式但没有及时打开外部上拉导致引脚浮空数据一直是0xFF检查数据线电压正常情况下总线上空闲高电平是3.3V如果示波器测量只有零点几伏大概率是上拉电阻缺失或板载模块本身有问题检查时序里有没有被中断打断DHT11要求微秒级精度如果系统开启了一堆定时器中断且优先级设置不当读取过程中会夹进中断导致位宽错乱确认DHT11供电不是稳压器刚启动的不稳定阶段传感器上电后需要至少1秒稳定如果主程序上电立即读取成功率很低。调试时最有效的办法是把40位原始数据通过串口打出来而不是只打印最终温湿度。当你能看到checksum error或者zero byte时至少说明时序通道是通的问题只在校验或者引脚模式。加一个测频计数也可以辅助分析比如在两次读位之间用定时器计数统计高电平持续了多长时间串口打印后对照标准的26-70微秒数据这比反复猜测可靠得多。5.4 仿真中的配置细节坑Proteus仿真跑不起来或者波形不对大部分是配置细节疏忽。第一MCU属性里的Crystal Frequency要填实际值默认1MHz会让你代码里的延时完全不在正常量级。第二要确认工程没有放在带中文或空格的路径下老版本Proteus对路径中文支持不好。第三虚拟终端的波特率设置要与代码一致很多工程师在串口助手看到乱码第一反应是代码问题结果只是虚拟终端波特率错了。还有一个常见问题是仿真里放置了多个LED但有些LED接地方式不对。Proteus里LED的属性包含正向导通压降如果直接接3.3V且没有限流电阻仿真会报警甚至不亮。你说“系统能跑但灯不亮”先看LED是不是反了。STM32 GPIO默认推挽输出驱动LED时电源正极接MCU引脚、负极经过电阻接地是没什么问题的但那种嵌在模块原理图里的LED低电平点亮方式在仿真里很容易忘记引脚状态。5.5 传感器误报警怎么压下来报警系统的核心指标是“该报的时候一定要报不该报的时候别乱报”。误报警通常有几个来源MQ-2在刚上电预热阶段会有一个短暂高输出如果立即参与判断基本都会误报一次继电器吸合瞬间产生的电磁干扰也可能让ADC抖动实验室通风橱抽风时烟雾浓度大幅波动连续几次超阈值也不一定代表火灾。我的建议是代码里加两个机制预热屏蔽和第二阈值迟滞。上电前30秒内不参与烟雾报警一旦进入报警后要让数值低于阈值的80%才解除报警而不是下降一丁点就恢复这样可以避免蜂鸣器在阈值附近反复开关。把这两个机制写进状态机里实际测试时误报数量会明显下降。这些也是我在代码注释里写得最详细的部分因为它们在现象上看不见但直接影响用户信任度。6. 开源发布方式与仓库结构建议6.1 仓库里需要放哪些文件既然标题带了“开源”代码只是仓库的一半原理图和仿真文件同样重要。我建议的仓库结构是这样的stm32-fire-alarm/ ├─ firmware/ │ ├─ Core/ │ ├─ Drivers/ │ ├─ MDK-ARM/ │ └─ README.md ├─ hardware/ │ ├─ schematics_v1.0/ │ ├─ bom/ │ └─ 原理图说明.md ├─ simulation/ │ └─ proteus/ ├─ docs/ │ ├─ 接线说明.md │ ├─ 校准说明.md │ └─ 常见问题.md └─ README.mdfirmware目录放完整的Keil工程不要只丢一个.hex文件因为别人要改功能就必须有源码和库文件。Hardware目录放原理图源文件。因为我用嘉立创EDA画图会导出一份PDF和一份工程源文件PDF方便直接看连线BOM表格标好每个器件的型号、数量、封装。Simulation目录放Proteus工程文件尽量附带使用说明告诉别人要用哪个版本打开以及如何加载hex。6.2 README必须写清楚的事README不是摆设它是开源项目的第一道门槛。我见过太多好项目因为没有清晰的README别人克隆下来根本跑不起来。README里至少要把环境版本写完整Keil MDK版本、STM32CubeMX版本、HAL库版本、Proteus版本、STM32F1Pack版本。不要只写“使用Keil打开”版本不匹配时启动会直接卡住或者编译报错。接线表必须和原理图完全对应。我画一个简单的接线表就可以避免大部分新手插错线模块MCU引脚说明DHT11 DATAPA0单总线数据MQ-2 A0PA1ADC1通道1火焰信号PA2外部中断输入OLED SCLPB6I2C1时钟OLED SDAPB7I2C1数据蜂鸣器控制PA12三极管驱动继电器输入PA11低电平有效请留意另一个很容易被忽略的是烧录方式。README里写明三行命令或操作步骤安装ST-LINK驱动配置Keil Debug选项为ST-Link然后烧录任何人按条执行基本不会错。如果你用的是串口ISP请额外标注BOOT0操作。开源项目想要有人用、有人反馈降低复现门槛比代码本身更重要。6.3 开源项目的维护心得把开源项目放出来后续要持续维护的不是功能新增而是让运行说明和实际代码不脱节。每次改动代码至少要同步更新README和仿真工程否则过两个月你自己再看都记不清当时做了哪些调整。我习惯在每次提交里带上“测试记录”比如什么环境下验证通过、测量到的ADC基值范围是多少。这样其他人在自己硬件上跑如果数值出入很大他能更快判断是硬件电源问题还是标定差异。有朋友问这个项目后面还能怎么扩展方向其实很多。温度传感器换SHT30通信加ESP8266走局域网通知或者把串口改成485总线接入宿舍管理平台。这些扩展点我和代码里的模块划分都预留了位置。控制逻辑和硬件耦合不强大可以沿着自己的实验室环境继续改。最后说点个人体会。这套系统我从原理图到代码到仿真前后迭代了几轮最大的收获不是逻辑怎么顺畅而是“预警系统的价值不在报警那一瞬间而在不误报、不误动的基础上必要时刻一定报”。不要小看状态机加连续确认这种朴素的方案它在嵌入式系统里永远比华而不实的花活可靠。动手焊板之前先花半小时把仿真和代码调顺后面能少走很多弯路。