HardFault 调试:异常栈与故障定位
2026/9/29大约 2 分钟
HardFault 调试:异常栈与故障定位
1. HardFault 发生后,CPU 做了什么?
SCB寄存器分布图

异常进入时,Cortex-M 会由硬件自动保存部分寄存器到当前栈:
R0
R1
R2
R3
R12
LR
PC
xPSR形成异常栈帧。
因此进入 HardFault_Handler 后,可以从栈中找到发生异常前的 CPU 状态。

2. 为什么需要栈回溯?
真正有价值的是:
PC LR因为 PC 保存的是发生异常时的程序位置。
因此:
HardFault
↓
获取异常栈
↓
读取 PC LR
↓
找到发生错误的代码位置这就是所谓的 HardFault 栈回溯。
3. 为什么不能直接在 C 函数里拿到栈?
Cortex-M 异常发生时,可能使用的是:
MSP也可能使用:
PSP所以通常先用一小段汇编判断异常现场来自哪个栈。
典型形式:
TST LR, #4
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B HardFault_C_Handler然后把栈地址传给 C:
传递函数参数时是R0
void HardFault_C_Handler(uint32_t *stack)
{
uint32_t pc = stack[6];
}因为异常栈帧顺序为:
stack[0] → R0
stack[1] → R1
stack[2] → R2
stack[3] → R3
stack[4] → R12
stack[5] → LR
stack[6] → PC
stack[7] → xPSR
4. HFSR 能告诉我们什么?
HardFault 调试时可以查看:
SCB->HFSR最重要的两个标志:
FORCED
VECTTBL
FORCED
表示:
某个可配置 Fault 没有被正常处理,最终升级成 HardFault。
因此看到 FORCED,还需要继续检查:
CFSR寻找真正的 Fault 原因。
VECTTBL
表示:
异常向量表读取过程中发生了 BusFault。

5. HardFault 调试的完整思路
HardFault
↓
读取 HFSR
↓
判断 FORCED / VECTTBL
↓
获取异常栈
↓
读取 LR PC
↓
定位出错指令
↓
继续查看 CFSR / BFAR / MMFAR 等寄存器
↓
确定真正原因所以:
HardFault_Handler 不是终点,而是故障定位的入口。
6. 最核心的一句话
Cortex-M 的异常机制真正强大的地方,是硬件自动保存异常现场,让我们能够从异常栈中的 PC 反推出“程序死在哪里”,再结合 Fault 状态寄存器找到“为什么死”。

