HardFault 不是根因,而是 Cortex-M 无法继续执行时交给你的最后一份现场。异常入口会自动保存一部分寄存器;故障处理程序只要把这份栈帧、SCB 状态寄存器和固件版本保存下来,通常就能把“设备偶尔死机”缩小到具体的指令地址和访问类型。

CPU 进入 HardFault识别 MSP/PSP 栈帧保存寄存器和故障状态地址映射到符号复现并修复根因
故障现场先保存能定位问题的最小集合,再考虑打印日志。
stacked PC异常发生前后正在执行的位置,是符号化的起点。LR 与 xPSR帮助判断调用返回位置和异常执行状态。CFSR区分内存管理、总线和用法错误。HFSR说明 HardFault 是否由其他可配置故障升级而来。BFAR/MMFAR当对应状态位有效时,记录发生错误的地址。固件标识没有 ELF、版本和构建信息,地址很难可靠还原。

先判断异常使用了哪一个栈

线程模式可能使用 PSP,异常和启动代码常用 MSP。HardFault 包装函数需要根据 EXC_RETURN 判断自动压栈的寄存器帧来自哪个栈,再把其中的 pclrxpsr 复制到可靠的存储区域。只打印处理函数自己的调用栈,往往看不到真正出错的任务。

保存后可以用与固件完全匹配的 ELF 解析地址:

1
arm-none-eabi-addr2line -e firmware.elf -f -C 0x08001234

地址、优化级别和符号文件必须对应同一次构建。若使用了 bootloader 或链接脚本重定位,也要先确认记录的 PC 是逻辑地址还是实际映射地址。

异常处理路径越短越好

故障处理程序不应申请堆内存、等待锁、访问可能已经坏掉的外设驱动,也不要依赖串口日志一定能发出去。更稳妥的是把寄存器、状态和少量栈内容写进保留 RAM 或预先准备好的 Flash 槽,随后复位;下一次启动再把转储上传并符号化。这样一次二次故障不会把唯一的线索冲掉。

参考:CmBacktrace