STM32火灾报警系统毕业设计全攻略:从选型到调试避坑
简介:面向嵌入式毕设学生与开发人员的STM32火灾报警系统资料包,覆盖从硬件选型、PCB原理图设计到STM32源码、流程图及报告的完整链路。资源共1445个文件,压缩包大小56.55MB,以C/H源码、Keil工程文件(uvproj/uvopt)、PDF文档、原理图PNG、固件HEX/BIN为主,并包含温度、烟雾传感器模块资料,目录按硬件、软件、文档分类,便于检索和二次开发。代码含报警输出与按键配置逻辑,可帮助读者快速理解工程实现。目前已有3579人学习下载。核心内容包括STM32F103主控选型与电路设计、ADC采集与阈值报警逻辑、传感器驱动及中断服务程序、流程图与软件分析,并附硬件选型对比和PCB布线注意事项,可指导读者完成火灾报警系统原型搭建与调试;配套的毕设答辩技巧和教学文章链接,也为课程设计或毕业设计提供了实用参考。
1. 为什么一套「看起来简单」的火灾报警系统,能让 80% 的毕业设计翻车
一套基于 stm32 的火灾报警系统,原理上就是「传感器检测 → 单片机判断 → 蜂鸣器报警」三件事,但每年基于 stm32 的毕业设计里,翻车最多的恰恰是这种看似人畜无害的项目。烟雾传感器上电就误报、ADC 读数像心电图、PCB 画完焊上去主控不启动——这些问题跟代码量无关,而是硬件选型、PCB原理图、源码和流程图之间没有形成闭环。我会把做这类系统真正要过的关卡拆开讲:硬件选型怎么定、最小系统与 PCB原理图怎么读怎么画、源码骨架怎么搭、调试时哪些坑必须先知道。适合正在做毕业设计,或者第一次用 STM32 做完整小系统的工程师照着走一遍。
2. 硬件选型:主控、传感器、通信和电源的搭配逻辑
2.1 主控选型:为什么 F103C8T6 是「够用且好抄」的均衡解
火灾报警系统对主控的需求其实很低:一路 ADC 采烟雾、一路 GPIO 读火焰、一路单总线读温度、一路 GPIO 控制蜂鸣器、一路 I2C 接 OLED、一路串口留作调试或上报。算下来 72MHz 主频、64KB Flash、20KB RAM 的 STM32F103C8T6 刚好覆盖,而且价格最低、参考设计最多,是最不会在选型上被答辩老师挑刺的答案。
F103C8T6 有 37 个 GPIO、2 个 12 位 ADC(10 通道)、3 个 USART、2 个 SPI、2 个 I2C、4 个 16 位定时器,做这套系统只用掉不到一半资源。对比 F103RCT6 多出来的引脚在这里纯属浪费;F407 的 DSP 和硬件 FPU 在烟雾阈值判断这类算法里完全用不上,反而贵一倍。常见做法是直接买最小系统板起步,后期再把最小系统电路抄进自己的 PCB。
这里要给刚入门的人一个判断标准:报警系统的软实时性要求不高,真正的瓶颈不在算力,而在 ADC 采样稳定性和电源完整性。所以选型时优先看「外设接口够不够、资料多不多、有没有人踩过坑」,而不是堆主频。
2.2 传感器三件套:烟雾、火焰、温度各自的选型与接口
火灾报警不能只靠一路传感器。最稳的组合是烟雾检测为主判据,温度和火焰作为佐证,能过滤掉大量误报。
烟雾传感器选 MQ-2 最普及:半导体式,对丙烷、丁烷、烟雾都有响应,输出有数字量 DO 和模拟量 AO 两路,供电 5V,加热丝需要预热。AO 输出范围约 0~5V,这个电压不能直接进 STM32 的 ADC,原因后面 PCB 章会展开。想检测一氧化碳可以换 MQ-7,但 MQ-7 对烟雾响应弱,火灾报警场景还是 MQ-2 更合适。
火焰传感器模块一般带 LM393 比较器,输出数字电平,检测波长 760~1100nm 的红外光,供电 3.3V 或 5V 均可,输出 TTL 电平可以直接接 GPIO。模块上的电位器用来调灵敏度,实测时顺时针拧到底最灵敏,但也会把太阳光和灯光误判成火焰,调参建议配合明火测试。
温度传感器用 DS18B20,单总线协议,只占一个 GPIO,测量范围 -55°C~+125°C,0.5°C 精度足够判断火灾温升。选择它的另一个理由是:单总线协议在毕业设计里可以单独写一节,报告内容更饱满。器件接口对比如下。
| 模块 | 检测对象 | 输出信号 | 供电 | STM32 接口 | 注意事项 |
|---|---|---|---|---|---|
| MQ-2 烟雾 | 可燃气体/烟雾 | AO 模拟 0~5V | 5V | ADC(需分压) | 加热丝预热 30s 再参与判断 |
| 火焰传感器 | 红外光 760~1100nm | DO 数字 TTL | 3.3V/5V | GPIO | 灵敏度电位器需实测标定 |
| DS18B20 | 温度 | 单总线数字 | 3.3V | GPIO + 4.7kΩ 上拉 | 时序敏感,读取失败返回 85°C |
| OLED 0.96'' | 显示 | I2C | 3.3V | I2C1 PB6/PB7 | 部分模块自带 4.7kΩ 上拉 |
| 有源蜂鸣器 | 报警 | 电平驱动 | 5V | GPIO + 三极管 | 感性负载需续流二极管 |
2.3 报警输出、人机交互与通信上报的选型
蜂鸣器是这套系统最容易被忽略的坑。有源蜂鸣器内置振荡电路,给高电平就响,比无源蜂鸣器省一个定时器;但它的瞬态电流可达 30mA,GPIO 直接驱动会拉低电平、甚至让 MCU 复位。常见做法是 S8050 NPN 三极管做开关,集电极驱动蜂鸣器负极,正极接 5V,蜂鸣器两端反向并联 1N4148 续流二极管。
人机交互部分,一块 0.96 寸 OLED 显示烟雾 ADC 值、温度和状态字,两个轻触按键分别做消音和复位。OLED 选 SSD1306 驱动,I2C 接口只占 4 根线,比 8080 并口屏少焊一堆线。
通信上报看项目定位。做教学演示,串口打印足够;想接入物联网,常见做法是串口外挂 ESP8266 模块,用 AT 指令把报警帧推到局域网或云平台,代码里只需要多写一个 USART 收发状态机。要注意 ESP8266 供电峰值接近 300mA,不能直接从 3.3V LDO 拉,要和 MQ-2 的加热丝一样从 5V 输入处取电。整板供电入口建议放一颗 100uF 电解电容,给这路大口径供电做缓冲。
2.4 模块资料归档:每个外设需要哪四样东西
标题里带「模块资料」,这个要件在工程里很容易被做成「买完模块往抽屉一扔」。我一般会给每个模块建独立文件夹,放四样东西:数据手册里最关键的那几页(引脚定义、电气参数、时序图)、模块自己的原理图或接线图、厂商提供的参考例程、自己实测的三组数据。参考例程要核对芯片型号和固件版本,DS18B20 的例程尤其多,有的用 ROM 全扫描、有的用跳过 ROM,代码混用就会读到 85°C。
这份模块资料有三个用途:改板时核对引脚定义、调代码时查时序参数、写报告时直接引用实测数据。建议在硬件调试阶段就把每路信号用万用表量一遍、记录电压范围,后面写测试章节会轻松很多。资料命名别用「新建文件夹 (3)」,按「模块名_芯片型号_接口_状态」编号,一个月后回看才知道里面是什么。另外记得把模块实物照片也放进去,插拔线之前拍一张,比接线图更直观。
3. PCB 原理图设计:从最小系统到整板布局的关键细节
3.1 STM32 最小系统板原理图:复位、晶振、BOOT、下载接口的常规画法
STM32 最小系统板原理图是这套 PCB 的地基,网上能找到大量参考,但抄之前要先看懂六个固定部分:电源、复位、时钟、BOOT、下载接口、去耦电容。逐个对着 F103C8T6 的芯片手册核对参数,比直接照搬网图靠谱得多。
时钟部分,8MHz 晶振接 OSC_IN/OSC_OUT(PD0/PD1),两个 20pF 负载电容接地。这个 20pF 不是随便写的,要和晶振手册标称的负载电容匹配,用 22pF 也常见,偏差太大会导致不起振或频率漂移。复位电路是 NRST 引脚接 10kΩ 上拉到 3.3V,再对地并联 100nF 电容,可选加一个按键到地做手动复位。
BOOT 和下载接口是新手抄原理图最容易抄错的地方。BOOT0 和 BOOT1 各接 10kΩ 下拉到地,保证默认从 Flash 启动;BOOT0 跳线拉高时进入 ISP 下载模式。下载口用 4pin SWD:SWDIO、SWCLK、3.3V、GND,配上 ST-Link 就能烧录,比 JTAG 省 4 个引脚。注意 SWDIO 和 SWCLK 两个引脚在程序初始化里不要配置成普通 GPIO,否则下载器连不上,后面避坑章会单独讲。
电源去耦是「原理图看着一样、板子表现差很多」的分水岭。每个 VDD 引脚就近放一颗 100nF 陶瓷电容,VDDA 与 VDD 之间串 10Ω 电阻加一组 10uF+100nF 滤波。这颗 10Ω 电阻在芯片手册里有明确建议,很多人省略,ADC 采样跳动时才会想起来补。
3.2 报警系统外围电路原理图:传感器接口、蜂鸣器驱动、OLED 排针
外围电路第一优先级是解决电平匹配。MQ-2 模块的 AO 输出是 0~5V,而 STM32 ADC 采样范围是 0~3.3V,直接接会把 ADC 打满甚至损坏引脚。常见做法是两只电阻分压:10kΩ 串在信号路径、20kΩ 到地,5V 分压后约 3.33V,留一点余量。分压电阻用 1% 精度,分压后的信号再串一颗 100Ω 进 ADC 引脚,配合 ADC 内部采样电容形成低通滤波。
提示:电阻分压后的带载能力有限,只能接高阻输入的 ADC 引脚,不能同时并联其他负载。
火焰传感器模块的 DO 输出在 3.3V 供电下可以直接接 GPIO,如果模块被设成 5V 供电就要加电平转换,最简单的办法是串联 1kΩ 限流电阻加 3.3V 稳压管钳位,或者干脆把模块供电改成 3.3V。
DS18B20 的数据线 DQ 必须接 4.7kΩ 上拉到 3.3V,这是手册规定的,缺了这个电阻单总线复位永远失败。有源蜂鸣器驱动电路按前面选型章的方案画:PC13 → 1kΩ 电阻 → S8050 基极,发射极接地,集电极接蜂鸣器负极,蜂鸣器正极接 5V,两端反向并联 1N4148。注意 PC13 是 RTC 引脚且驱动能力偏弱,串的基极电阻不能太小,1kΩ 起步。
OLED 的 I2C 走 PB6/PB7,如果买的是不带电平转换的模块,就只接 3.3V。两个按键一端接地、一端分别接 PA0 和 PB3,软件开内部上拉,省两个外部上拉电阻。所有模块的接口统一用 2.54mm 排针引出,方便先插接调试、再决定要不要做板载。
3.3 PCB 布局走线与板级检查:拿到原理图先看哪六处
分层布局上,我把板子划成三个区:5V 电源输入与降压、MCU 最小系统、传感器与蜂鸣器接口。蜂鸣器必须远离晶振,铁芯的磁滞振荡会干扰 8MHz 晶振导致时钟抖动;MQ-2 的加热丝是热源,也要和温度传感器保持距离,否则 DS18B20 测的是烟感温度而不是环境温度。
走线规则我一般是 5V 电源线 20mil、3.3V 15mil、信号线 10mil,双面板底层大面积铺地。晶振两根走线尽量短且对称,下方不要走数字信号线;ADC 分压电阻靠近 ADC 引脚放置,分压后的模拟走线远离蜂鸣器驱动线和 PWM 线。排针接口放在板边,下载口放板子一角,方便调试时夹 ST-Link。
拿到别人的 PCB 原理图做分析,我最先核对六个点:晶振负载电容是否匹配、VDDA 滤波是否完整、复位上拉是否忘了、BOOT 是否被悬空(悬空等于电平不确定)、SWD 引脚是否被复用、DC 座和 5V 入口有没有大电解电容。这六处如果能全对上,这版原理图大概率不是随手画的。
画 PCB 的工具用立创 EDA 或 KiCad 都行,元件库封装要逐个核对,尤其是蜂鸣器、排针这类机械尺寸敏感的元件,封装错了焊完就得飞线。打样回来先不焊芯片,用万用表量一遍 3.3V 和地之间有没有短路,再焊 MCU 和最小外围,最后插接传感器模块——这个顺序能避免一次焊完整板找不到故障点。
4. 源码落地:Keil5 工程、时钟树与三路采集的代码骨架
4.1 工程环境与库的选择:标准库、HAL 库和我为什么选标准库
Keil5 装好之后先确认 Device Pack 选的是 STM32F1 系列。网上常说的「keil5 兼容 c51 和 stm32 安装」坑在于:C51 和 ARM 两个工具链要装不同目录,且 Pack 要单独导入,装完在 Manage Run-Time Environment 里确认能看到 STM32F103C8 型号再开工。
代码库用哪套是个经典问题。HAL 库适合用 CubeMX 生成立即跑的工程,代码封装度高、带超时判断,但做这种小项目会有大量冗余初始化,改一个时钟配置要翻好几层结构体。标准外设库(大家口中的库函数)把所有外设初始化拆成 RCC 开时钟、GPIO_Init、ADC_Init 这样的函数,逻辑直白,每行都能在芯片手册里找到对应寄存器,最适合把「为什么这么配」讲清楚。寄存器版自由度最高,但可读性差,不推荐第一次做完整系统的人用。
我的结论是:毕业设计和入门工程选标准库。它和 HAL 库并不冲突——先用标准库把外设时序吃透,再去看 CubeMX 生成的代码,会更容易理解 HAL 为什么那样封装。
4.2 时钟树配置:从 8MHz 晶振到 72MHz 主频的 6 行代码
STM32 的时钟树是很多新手第一次卡住的地方。F103C8 的外部晶振 8MHz 作为 HSE,经过 PLL 锁相环 9 倍频得到 72MHz 系统时钟,AHB 不分频,APB1 分频到 36MHz,APB2 保持 72MHz。ADC 挂在 APB2 上,但 ADC 时钟不能超过 14MHz,所以还要单独给 ADC 分频。
void SystemClock_Config(void) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); // 打开外部8MHz晶振 while (RCC_WaitForHSEStartUp() != SUCCESS); // 等待晶振起振 FLASH_SetLatency(FLASH_Latency_2); // 72MHz下Flash需2个等待周期 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); // 8MHz * 9 = 72MHz RCC_PLLCmd(ENABLE); // 启动PLL while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // 等待PLL锁定 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // 系统时钟切换至PLL while (RCC_GetSYSCLKSource() != 0x08); // 0x08表示PLL已作为系统时钟 RCC_HCLKConfig(RCC_SYSCLK_Div1); // AHB = 72MHz RCC_PCLK1Config(RCC_HCLK_Div2); // APB1 = 36MHz RCC_PCLK2Config(RCC_HCLK_Div1); // APB2 = 72MHz RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC时钟 = 72/6 = 12MHz }这段代码里 FLASH_SetLatency 是第一个容易漏的点:Flash 在 72MHz 下没有 2 个等待周期,取指就会出错,表现为主频一拉高就跑飞。RCC_PLLMul_9 是按 8MHz 晶振算的,如果你手里是 12MHz 晶振,这里必须改乘数,比如 RCC_PLLMul_6 同样是 72MHz,否则超频。
APB1 分频成 36MHz 之后,挂在 APB1 上的 TIM2/TIM3/TIM4 如果想用 72MHz 计数,时钟树会自动给它们倍频到 72MHz,不用手动配。但很多人不知道 APB1 上的串口 USART2/3 是直接用 36MHz 的,算波特率分频时不要按 72MHz 算,这是串口乱码的一个隐蔽来源。
4.3 ADC 采集烟雾:滑动滤波与基线校准
ADC 初始化部分,PA1 作为模拟输入通道,ADC1 选择软件触发、右对齐、12 位分辨率。采样周期我习惯配成最大采样周期 239.5 周期,虽然采样率低了几十 kHz,但对烟雾这种缓变信号来说,抗噪声能力比采样速度重要得多。
uint16_t GetSmokeADC(void) { uint16_t val, sum = 0, min = 4095, max = 0; for (int i = 0; i < ADC_SAMPLE_NUM; i++) // ADC_SAMPLE_NUM = 10 { ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 启动一次软件转换 while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); // 等转换结束 val = ADC_GetConversionValue(ADC1); // 读取12位结果 sum += val; if (val < min) min = val; if (val > max) max = val; } return (uint16_t)((sum - min - max) / (ADC_SAMPLE_NUM - 2)); // 去掉最大最小求平均 }去掉最大最小值这一手,过滤的是蜂鸣器、继电器这类开关器件带来的单次脉冲干扰。参数上,ADC_SAMPLE_NUM 取 10,去掉两个极值后剩 8 个样本平均;如果烟雾变化很快需要提高响应速度,可以降到 5,但抗脉冲能力会差一些。注意 EOC 标志必须在读取前等待,很多刚开始写的人直接读转换结果寄存器,读到的还是上一次数据。
烟雾报警不能拿 ADC 绝对值当阈值。MQ-2 的基线会随环境温湿度和老化漂移,正确做法是上电预热 30 秒后连续采 50 次求平均,记为 baseline_smoke,报警阈值设为 baseline_smoke + SMOKE_TH,SMOKE_TH 常见做法是取 15%~20% 满量程,也就是 600~800 个 LSB。这样换一个传感器模块也不用改代码,重新校准一次阈值就能工作,这是避免误报的「后悔药」。
4.4 状态机:消抖、分级报警与蜂鸣器/OLED/串口联动
报警逻辑不要写成 if 套 if,用一个状态机管理整个系统生命周期,比散落的条件分支好维护得多。状态划分成 INIT、PREHEAT、MONITOR、ALARM、SILENCE 五态,每次任务循环 100ms,阈值判断在 MONITOR 状态里执行。
void Alarm_Task(void) { uint16_t smoke = GetSmokeADC(); int16_t temp = ReadDS18B20(); uint8_t flame = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4); switch (state) { case SYS_INIT: Buzzer_Off(); OLED_Clear(); OLED_ShowStr(0, 0, "SYSTEM INIT"); state = SYS_PREHEAT; preheat_tick = 0; break; case SYS_PREHEAT: preheat_tick++; if (preheat_tick >= 300) // 100ms * 300 = 30s预热 { baseline_smoke = Smoke_GetBaseline(); state = SYS_MONITOR; } break; case SYS_MONITOR: OLED_ShowNum(0, 2, smoke); OLED_ShowNum(4, 2, temp); /* 双条件判据:烟雾超标且温度超标才进报警确认 */ if ((smoke > baseline_smoke + SMOKE_TH) && (temp >= TEMP_TH)) { alarm_cnt++; if (alarm_cnt >= 5) // 连续5个周期确认,滤掉瞬时尖峰 { state = SYS_ALARM; alarm_cnt = 0; } } else { alarm_cnt = 0; } break; case SYS_ALARM: Buzzer_On(); OLED_ShowStr(0, 3, "** FIRE **"); USART_SendString(USART1, "FIRE ALARM\r\n"); if (Key_Scan() == KEY_PRESS_LONG) // 长按2s进入消音 { state = SYS_SILENCE; } break; case SYS_SILENCE: Buzzer_Off(); if ((smoke < baseline_smoke + SMOKE_TH) && (temp < TEMP_TH)) { state = SYS_MONITOR; // 火情解除,自动恢复监测 } break; } }报警逻辑里的关键全在阈值和计数上。SMOKE_TH 是基线偏移量而不是绝对 ADC 值,解决了换传感器要改数字的问题;TEMP_TH 默认给 55°C,模拟正常室内环境不会误触发。连续 5 次确认等于 500ms 持续判据,油烟瞬时冒一下不会触发,真着火半秒足够反应。火焰传感器在这个状态机里做辅助:如果火焰拉低且烟雾超标,alarm_cnt 可以改成 3 次快速确认,响应更快;单纯火焰拉低先不报,防止灯光干扰误报。
串口上报留一个 USART1 的打印重定向,调试期打印「smoke=xxx temp=xx state=xx」,加 ESP8266 时只需要在报警分支把字符串换成 AT 指令发送,不影响状态机主流程。
5. 调试避坑:五个高频翻车点与排查方法
5.1 传感器侧的三个「看起来正常、实际有毒」的坑
坑一:烟雾传感器上电就报警,还没点火蜂鸣器先响。
现象是板子上电不到五秒,报警状态自动进入,蜂鸣器狂响,而且断电重上电复现。很多人第一反应是代码写错,其实大概率是 MQ-2 的预热特性导致的。MQ-2 的加热丝冷态电阻小,上电瞬间流过较大电流,同时内部敏感层在这个阶段电阻剧烈变化,AO 输出接近满量程,软件如果在这段时间做阈值判断,必然误报。
原因是 MQ-2 需要 30 秒左右的预热时间让加热丝达到稳定温度,这个预热不是可选项。解决方法是把系统启动流程改成 INIT → PREHEAT 30 秒倒计时 → 采集基线 → 进入 MONITOR,预热期间 OLED 显示倒计时、切断报警输出。基线也要在预热完成后采集,而不是上电时采一次,这样同一块板子换到不同环境,第一次上电都能自适应。
坑二:ADC 读数像心电图,烟雾没变数值上下跳几百个 LSB。
现象是 OLED 上显示的 smoke 数值在 800 到 1200 之间乱跳,烟雾浓度没有任何变化。排查时先量 VDDA 纹波,如果接近甚至超过 100mV,问题就出在电源和布局上。
原因是 ADC 的参考电压跟随 VDDA,VDDA 上的纹波直接变成采样噪声;蜂鸣器这类开关器件切换瞬间会通过地线干扰模拟信号;MQ-2 加热丝的电流变化也会周期性影响 5V 和地平面。解决分三层:硬件在 VDDA 与 VDD 之间加 10Ω 电阻加 10uF 和 100nF 滤波,蜂鸣器加上续流二极管;布局把模拟走线远离 PWM 线和蜂鸣器驱动线;软件把 ADC 采样周期设长、做去极值平均滤波。三层都做完还有跳动,再用示波器看各点的信号波形。
坑三:DS18B20 永远读回 85°C,换芯片也一样。
现象是温度显示稳定在 85°C,无论热风枪吹还是手捂,纹丝不动。85°C 是 DS18B20 内部寄存器上电默认值,能读出这个数说明单总线通信的复位和 ROM 命令其实没成功,芯片根本没开始温度转换。
原因通常是单总线时序不对。DS18B20 的时序要求 us 级精确,读写时间槽的采样窗口很窄,用 for 循环空转的延时在编译器优化下时间完全不同,O0 和 O2 优化后的延时可以从 20us 变成 2us,时序直接被打乱。解决方法是把 us 级延时改用定时器或者寄存器精确计时,也可以用标准库的延时函数但先确认实现基于 SysTick 还是 DWT;同时检查 DQ 上拉电阻,4.7kΩ 不能省。
5.2 电源、时钟和下载器的两个「翻车」现场
坑四:stm32 延时函数 delay 卡死,加一个延时就进 HardFault。
现象是代码在某处调用延时后程序不再运行,调试器直接跳进 HardFault_Handler,把延时删掉又正常。我排查过的最常见原因是 SysTick 的配置被时钟重设覆盖了:SystemClock_Config 里调 RCC_DeInit 会复位 SysTick 所在的外设时钟,而延时函数依赖 SysTick 计数,两者执行顺序颠倒时,延时的计数值全错。
解决方法是统一时钟初始化顺序:先 SystemClock_Config,再 SysTick_Config(SystemCoreClock / 1000) 重新配置 SysTick 中断周期,最后 NVIC_PriorityGroup_Config 设置分组,把 SysTick 中断优先级设为最低,避免和串口、ADC 中断抢占时嵌套死锁。另一个原则是中断服务函数里不要调用长延时,改用状态变量和计数器的模式,消音这种交互逻辑放在主循环处理。
坑五:ST-Link 下载失败,提示 No target connected,但板子灯是亮的。
现象是代码已经下载过一次,第二次连接 ST-Link 识别不到目标,报连接超时。排查顺序先看 BOOT0 跳线,如果被拉高,芯片上电进 ISP 引导,SWD 接口不响应,把 BOOT0 跳回低电平再试;再看程序里引脚重映射配置有没有碰过 SWD 引脚复用,PA13/PA14(SWDIO/SWCLK)如果被配置成普通 GPIO 并拉低,等于把下载口自己关死了。
解决方法是程序里保留 SWD 初始化不动,或者下载时按住目标板复位键点 Connect Under Reset。真遇到代码把下载口锁死的情况,用 ST-Link Utility 的 Connect Under Reset 模式全片擦除,这是最后的后悔药,擦完重新烧程序即可。
6. 流程图怎么画、报告怎么写,外加一个联调验证的土办法
6.1 一张让答辩老师 30 秒看懂的流程图
报告里的流程图不要照着状态机代码逐行翻译。我一般给三张图:系统整体框图表示数据流(烟雾/火焰/温度 → STM32 → 蜂鸣器/OLED/串口);主程序流程图按功能块画到「传感器采集 → 滤波 → 阈值判断 → 报警」粒度;再加一张状态转换图,把预热、监测、报警、消音四态之间的转移条件标清楚。状态转换图是这套系统的加分项,因为它直接对应代码里的 state 枚举,老师追问任何一态怎么进入、怎么退出,你都能指着图回答。
6.2 联调验证顺序和一个「土办法」测试矩阵
整机联调按从内到外验证:先跑最小系统加串口打印确认时钟和延时正常,再接传感器看裸数据,最后接蜂鸣器和 OLED 测报警逻辑。烟雾测试我用一个土办法:打火机不点火,把丁烷气体缓慢喷向 MQ-2 约一秒,正常应在 2~3 秒内进入报警;火焰测试用打火机明火靠近火焰传感器 10cm 以内;温度测试用吹风机热风挡吹 DS18B20。全程通风、周围不放可燃物。
| 测试项 | 输入方法 | 预期结果 | 实测记录 |
|---|---|---|---|
| 烟雾报警 | 丁烷喷 MQ-2 1s | 3s 内报警,蜂鸣器响 | 2.4s 报警 |
| 火焰检测 | 明火 10cm 内 | 火焰状态位置位 | 8cm 稳定触发 |
| 温度上报 | 热风枪 60°C | OLED 温度读数爬升 | 正常 |
| 消音复位 | 长按按键 2s | 蜂鸣器停止,烟气散尽后恢复监测 | 正常 |
这套流程我自己至少改过三版才收敛,最后发现最值钱的不是源码,而是那张阈值标定和测试记录表,每次改版快速归零问题全靠它。进阶可以做的方向很多:把固定 SMOKE_TH 改成移动平均基线、加入火焰快速确认通道、用 ESP8266 把报警帧推到云平台。先让本地板子稳定跑一周不误报,再谈远程,这是我最想留给你的一条习惯,希望帮到你。
本文还有配套的精品资源,点击获取