你的产品在客户那里跑了几天突然重启了,日志里就一条"系统初始化",至于为什么重启的——不知道。问硬件说没掉电,问软件说没主动复位,那就剩下一个迷之问题:到底是谁触发了复位?
单片机复位的原因其实不算多,但每一种的后果都一样——程序从头跑,之前的运行状态全丢。如果软件分不清复位来源,排查就只能靠猜,效率很低。
常见的复位来源
不同厂家的MCU复位源略有差异,但核心就这几类:
| 上电复位(POR) | VDD从0上升到门槛电压 | 设备开机、电池插入 |
| 欠压复位(BOR) | VDD降到阈值以下 | 电源波动、电池电量不足 |
| 外部复位 | RESET引脚被拉低 | 复位按键、看门狗芯片触发 |
| 独立看门狗复位(IWDG) | 计数器溢出未被喂狗 | 程序死循环、中断风暴 |
| 窗口看门狗复位(WWDG) | 在窗口外喂狗或超时 | 程序跑飞、时序异常 |
| 软件复位 | 执行复位指令 | OTA升级、严重错误主动重启 |
| 低功耗模式唤醒复位 | 从Standby模式唤醒 | 深度休眠后的恢复 |
这里面最让人头疼的是欠压复位和看门狗复位,因为它们往往间歇出现,复现困难,而且都可能表现为"跑着跑着就重启了"。
软件怎么区分——寄存器是关键绝大多数MCU在复位后会自动把复位原因写入一个状态寄存器。比如STM32的RCC_CSR,GD32的RCTL_RST,ESP32的RTC_CNTL_RST_STATE——虽然名字不一样,但思路都一样:硬件在复位时置位对应的标志位,软件启动后去读这个寄存器就能知道刚才发生了什么。
以STM32为例,RCC_CSR里就有这些标志位:
| BORRSTF | 欠压复位标志 |
| PINRSTF | 引脚复位标志(外部RESET引脚) |
| PORRSTF | 上电复位标志 |
| IWDGRSTF | 独立看门狗复位标志 |
| WWDGRSTF | 窗口看门狗复位标志 |
| SFTRSTF | 软件复位标志 |
【注意】复位标志位不会自动清零。如果你不在启动代码里主动清除,下次复位后读到的会是所有历史标志的叠加。所以正确的做法是:读完标志后立即写1清零,这样下次读到的才是最新一次的复位原因。
实战代码思路在main()的最开头(或者系统初始化函数的第一行),把复位标志读出来、存好、清掉:
读出来的值可以存到非易失性存储器里(比如内部Flash的一个备用区域,或者EEPROM),这样即使连续复位,你也能追溯每次的复位原因。实际项目里,我习惯保留最近5次的复位记录,包括复位类型和时间戳,排查问题的时候效率很高。
小技巧:如果你用了FreeRTOS或其他RTOS,可以在任务调度器启动之前就读取并保存复位标志,避免系统运行过程中被其他操作干扰。有些RTOS自身的看门狗超时也会触发复位,这类复位需要和硬件看门狗区分开。
容易踩的几个坑1. 只看一个标志就下结论。有些复位会同时置多个标志。比如上电复位时,PORRSTF和PINRSTF可能同时为1;欠压复位时,BORRSTF和PORRSTF也可能同时置位。所以要按优先级来判断:先看是不是上电复位(POR),再看是不是欠压(BOR),然后才是看门狗、软件复位等。不能看到一个标志就认定是那个原因。
2. 调试器连接导致误判。用J-Link或ST-Link调试时,连接和断开都会触发复位,这些复位会设置PINRSTF或SFTRSTF。如果你在调试阶段观察复位标志,很容易被调试器干扰。建议用日志输出到串口的方式观察,而不是依赖在线调试。
3. Standby唤醒后标志丢失。从Standby模式唤醒时,有些MCU的复位标志寄存器会被清零(因为本质上是一次新的上电过程)。如果你需要追踪这种情况,就得在进入Standby之前把关键状态提前写到备份寄存器里。
说在最后分清除复位来源,是可靠嵌入式系统的基本功。别等出了问题才发现自己一片空白——在项目初期就把复位标志的读取和存储写进代码框架里,后续不管是排查偶发重启还是做产品可靠性分析,都能省下大量时间。一行读寄存器的代码,比十行猜测的排查都管用。

扫码关注










































