复位后须立即读取RCC_CSR寄存器,因LPWRRSTF、WWDGRSTF、IWDGRSTF等标志仅在复位退出后短暂有效,一旦执行RCC_ClearFlag()或HAL_RCC_GetResetSource()等操作即被清零;正确做法是在startup文件跳转main前、SystemInit开头或HAL_Init之前完成读取,并用__attribute__((section(".noinit")))持久化保存原始值。

复位后第一时间读取 RCC_CSR 寄存器,别等 main() 才开始
系统异常复位(如看门狗超时、软件复位、电源掉电)发生后,RCC 的复位标志位(RCC_CSR 寄存器中 RCC_CSR_LPWRRSTF、RCC_CSR_WWDGRSTF、RCC_CSR_IWDGRSTF 等)只在复位退出后短暂有效——一旦执行了 RCC_ClearFlag() 或任意复位相关初始化(比如 HAL_RCC_OscConfig()),这些标志就会被清零。很多开发者在 main() 里才去查,结果读到全是 0。
正确做法是:把读取逻辑放在启动文件(如 startup_stm32f4xx.s)跳转到 main() 之前,或在 SystemInit() 开头强制插入。如果你用的是 HAL 库,务必在 HAL_Init() 和 SystemClock_Config() 之前完成读取。
-
RCC->CSR是只读寄存器,直接读取即可,无需使能时钟 - 不要调用
__HAL_RCC_CLEAR_RESET_FLAGS()或HAL_RCC_GetResetSource()—— 它们内部会清标志 - 若使用 CMSIS,可直接用
RCC->CSR & RCC_CSR_IWDGRSTF判断,避免宏展开引入隐式清除
区分 IWDG/WWDG/LPWRRSTF,别只看一个 bit
不同复位源对应不同标志位,但它们可能共存(比如低功耗唤醒失败同时触发 LPWRRSTF 和 IWDGRSTF)。单独检查 RCC_CSR_IWDGRSTF 不足以确认是独立看门狗导致的死机——得排除其他干扰。
典型组合判断逻辑如下(复位后立即执行):
立即学习“C++免费学习笔记(深入)”;
uint32_t csr = RCC->CSR;
if (csr & RCC_CSR_IWDGRSTF) {
// 可能是 IWDG 超时,但需先确认没被其他复位覆盖
if (!(csr & (RCC_CSR_SFTRSTF | RCC_CSR_PORRSTF | RCC_CSR_PINRSTF))) {
// 排除软复位、上电、引脚复位干扰,才较可信
}
}
if (csr & RCC_CSR_WWDGRSTF) {
// WWDG 早于窗口下限触发,通常意味着中断未及时喂狗或中断被屏蔽
}
if (csr & RCC_CSR_LPWRRSTF) {
// 深度睡眠唤醒失败或 VDDA 电压跌落,注意和 PVD 标志配合看
}
-
RCC_CSR_PINRSTF表示 NRST 引脚手动复位,不是死机证据 -
RCC_CSR_SFTRSTF是NVIC_SystemReset()触发的,属于可控复位 -
RCC_CSR_PORRSTF上电复位,一般出现在首次上电,但掉电重启也可能出现
HAL_RCC_GetResetSource() 为什么不能直接用
HAL_RCC_GetResetSource() 看起来方便,但它内部第一行就是 __HAL_RCC_CLEAR_RESET_FLAGS(),也就是读完立刻清空所有标志。你拿到返回值的同时,硬件状态已丢失——下次再想查就只能靠日志或调试器回溯。
更麻烦的是,它把多个标志“或”在一起返回枚举值(如 RCC_RESET_FLAG_IWDG),掩盖了多标志共存的可能性。实际死机分析中,共存标志往往才是关键线索(比如 IWDGRSTF + LPWRRSTF 暗示低功耗模式下 IWDG 喂狗失败,可能因时钟配置错误导致 IWDG 频率异常)。
- 自己读
RCC->CSR并保存原始值到 RAM(如__attribute__((section(".noinit")))变量),比依赖 HAL 封装更可靠 - 如果必须用 HAL,至少把它拆开:先手动读寄存器,再调用 HAL 函数做辅助解析,但绝不让它清标志
- 注意某些芯片(如 STM32H7)RCC_CSR 分成两个寄存器(
RCC_CSR和RCC_SR),HAL 可能未完全适配
复位标志读取后怎么持久化,避免被覆盖
MCU 复位后 SRAM 默认不清零(除非启用 __HAL_RCC_CLEAR_FLAG(RCC_FLAG_SFTRST) 类似操作或调用了 memset 初始化),但标准启动流程(如 system_stm32f4xx.c 中的 SystemInit())之后,C 运行环境会执行 .data/.bss 段初始化,可能覆盖你存的标志值。
- 用
__attribute__((section(".backupram"), used))把变量放到备份域(如 RTC_BKP_DRx),但需提前使能 LSE 和备份域写入 - 更简单:定义为
static __attribute__((section(".noinit"))) uint32_t g_rcc_csr_backup;,并在启动代码最开头(C 环境初始化前)赋值 - 调试阶段可直接用
printf输出到串口(确保 UART 时钟已由 HSI 提供,不依赖 PLL),但注意有些调试器复位后串口引脚状态不稳定
真正容易被忽略的是:JTAG/SWD 在复位过程中可能拉低 NRST 或干扰 SWCLK,导致你看到的“复位标志”其实是调试器触发的软复位,而非真实死机。验证前先断开调试器,用独立电源+按键复位观察。


















