本篇目标:HardFault 是嵌入式开发的高频噩梦。本篇讲清故障寄存器怎么看、现场怎么抓、stp_rtos 的 HardFault 处理怎么写的。
一、Cortex-M 的故障体系
Cortex-M3/M4/M7 有四类故障异常:
异常 | 默认状态 | 说明 |
UsageFault | 可配置使能 | 除零、未对齐访问、未定义指令、非法状态 |
BusFault | 可配置使能 | 总线错误、取指/取数失败 |
MemManageFault | 可配置使能 | MPU 违规、非法内存访问 |
HardFault | 总是使能 | 兜底,上面三类未使能时都升级为 HardFault |
二、四个关键寄存器
都在 SCB(系统控制块)里:
寄存器 | 地址 | 作用 |
CFSR | 0xE000ED28 | 可配置故障状态(MFSR+BFSR+UFSR 三合一) |
HFSR | 0xE000ED2C | HardFault 状态(FORCED 位表示由其他 fault 升级) |
MMFAR | 0xE000ED34 | MemManage 出错地址 |
BFAR | 0xE000ED38 | BusFault 出错地址 |
CFSR 按字节拆成三个:
三、抓现场:HardFault_Handler
出故障时,硬件已经把 R0~R3、R12、LR、PC、xPSR 压进栈。关键一步是判断用的是 MSP 还是 PSP:
EXC_RETURN 的 bit2 是金标准:0 = MSP(异常里出的事),1 = PSP(线程里出的事)。这决定了去哪个栈里找 PC。四、stp_hw_hard_fault_exception
hard_fault_track 读 HFSR 的 FORCED 位,再分发到 usage_fault_track / bus_fault_track / mem_manage_fault_track,逐位翻译成人类可读的故障名(DIV_BY_ZERO、UNALIGNED_ACCESS、PRECISERR……)。五、分析流程(实战)
- 看 PC:反汇编定位出错指令;
- 看 LR:
LR - 1 - 4反推调用链(Thumb 地址减 1,LR 是返回地址的下一条);
- 看 CFSR/HFSR:确定故障类型;
- 看 BFAR/MMFAR:精确故障时给出出错地址;
- 常见根因:栈溢出(PC 跑到数据区)、野指针(BFAR 指向非法地址)、除零(UFSR DIV_BY_ZERO)。
注意:先读 BFAR/MMAR,再读 VALID 位,顺序反了可能被更高优先级 fault 抢占导致地址被擦除。
六、小结
- HardFault 是兜底,具体原因看 CFSR/HFSR;
- EXC_RETURN bit2 决定去 MSP 还是 PSP 找现场;
- 先读地址寄存器再读状态位,最后清状态位。
核心一句话:故障诊断 = 抓 PC/LR + 读状态寄存器 + 定位出错地址,三步定位根因。


