#endif /* __RESET_CAUSE_H__ */
3.2 核心实现
/* reset_cause.c */
#include "reset_cause.h"
/* 备份寄存器操作宏(STM32G0系列为例,注意:RTC基地址可能因芯片而异) */
/* 在G0系列上,备份寄存器由RTC模块控制,地址为RTC_BASE + 0x50 */
#define BKP_REG_ADDR(reg) (RTC_BASE + 0x50 + (reg * 4))
#define BKP_REG_WRITE(reg, val) (*(__IO uint32_t *)(BKP_REG_ADDR(reg)) = (val))
#define BKP_REG_READ(reg) (*(__IO uint32_t *)(BKP_REG_ADDR(reg)))
/* 复位原因字符串表 */
static const char* reset_cause_strings = {
"Unknown", /* 0 */
"POR/PDR Reset", /* 1 */
"BOR Reset", /* 2 */
"External Reset", /* 3 */
"IWDG Reset", /* 4 */
"WWDG Reset", /* 5 */
"Software Reset", /* 6 */
"Low Power Reset", /* 7 */
};
/**
* @brief 初始化复位原因检测
* @note 必须在系统启动后尽早调用,并在调用前确保RCC时钟(RCC外设)已使能。
* 此函数会读取并保存复位原因,然后清除复位标志位。
*/
void ResetCause_Init(void)
uint32_t csr_value;
ResetCause_t cause = RESET_CAUSE_UNKNOWN;
/* 1. 读取RCC_CSR寄存器 */
csr_value = RCC->CSR;
/* 2. 解析复位原因,注意优先级:通常按从高优先级到低优先级顺序判断,
* 以避免多个标志同时置位时产生误判。
* PORRSTF 优先级最高,LPWRRSTF 优先级最低。
*/
if (csr_value & RCC_CSR_PORRSTF) {
cause = RESET_CAUSE_POR_PDR;
} else if (csr_value & RCC_CSR_BORRSTF) {
cause = RESET_CAUSE_BOR;
} else if (csr_value & RCC_CSR_PINRSTF) {
cause = RESET_CAUSE_EXTERNAL;
} else if (csr_value & RCC_CSR_SFTRSTF) {
cause = RESET_CAUSE_SOFTWARE;
} else if (csr_value & RCC_CSR_IWDGRSTF) {
cause = RESET_CAUSE_IWDG;
} else if (csr_value & RCC_CSR_WWDGRSTF) {
cause = RESET_CAUSE_WWDG;
} else if (csr_value & RCC_CSR_LPWRRSTF) {
cause = RESET_CAUSE_LOW_POWER;
/* 3. 保存到备份寄存器 */
BKP_REG_WRITE(BKP_REG_RESET_CAUSE, (uint32_t)cause);
/* 4. 清除复位标志(写1清除RMVF位)
* 必须在所有读取和处理完成后执行,以防止后续代码修改标志位。
* 注意:RMVF位只清除复位标志,不影响其他配置。
*/
RCC->CSR |= RCC_CSR_RMVF;
/**
* @brief 获取上次复位原因
* @retval 复位原因枚举值
*/
ResetCause_t ResetCause_Get(void)
return (ResetCause_t)BKP_REG_READ(BKP_REG_RESET_CAUSE);
/**
* @brief 获取复位原因字符串
* @param cause 复位原因枚举值
* @retval 字符串指针
*/
const char* ResetCause_GetString(ResetCause_t cause)
if (cause > RESET_CAUSE_LOW_POWER) {
cause = RESET_CAUSE_UNKNOWN;
return reset_cause_strings;
/**
* @brief 打印复位原因到日志
* @note 需要在串口或其他日志输出设备初始化完成后调用。
* 如果日志设备初始化前就调用了此函数,可能不会有任何输出。
*/
void ResetCause_Print(void)
ResetCause_t cause = ResetCause_Get();
LOG_PRINTF("\r\n========================================\r\n");
LOG_PRINTF(" System Reset Detected!\r\n");
LOG_PRINTF(" Reset Cause: %s\r\n", ResetCause_GetString(cause));
LOG_PRINTF("========================================\r\n");
3.3 主函数调用示例
/* main.c */
#include "reset_cause.h"
/* 包含项目自己的串口初始化头文件 */
/* #include "usart.h" */
int main(void)
/* 1. 初始化HAL库 */
HAL_Init();
/* 2. 初始化串口(用于打印日志) */
/* MX_USART1_UART_Init(); */
/* 3. 初始化复位原因检测(必须在其他与日志相关的初始化之后调用,
* 但必须在RCC时钟初始化之后,且在清除RCC_CSR之前) */
ResetCause_Init();
/* 4. 打印复位原因 */
ResetCause_Print();
/* 5. 正常应用程序 */
while (1) {
/* 你的应用代码 */
四、不同STM32系列的适配
不同系列的STM32,RCC_CSR寄存器的位定义略有差异。一定要参考所使用的单片机型号。
五、实际应用案例
案例1:看门狗复位排查
某设备在现场频繁复位,客户投诉。通过日志发现:
========================================
System Reset Detected!
Reset Cause: IWDG Reset
========================================
确认是独立看门狗复位。进一步分析发现,主循环中有一个耗时操作超过了看门狗喂狗周期。优化后问题解决。
案例2:电源不稳排查
另一设备在特定工况下复位,日志显示:
========================================
System Reset Detected!
Reset Cause: BOR Reset
========================================
确认是欠压复位。检查电源发现,电机启动瞬间电压跌落超过BOR阈值。增加大电容后解决。
六、进阶技巧
待续。

版权声明
本文仅代表作者观点,不代表xx立场。
本文系作者授权xx发表,未经许可,不得转载。
最新留言