HardFault(硬错误) 是一种 系统级异常(System Exception), 在 Cortex-M 架构中,表示内核检测到严重故障且无法交给其他 Fault 处理器处理时触发。

参考: https://blog.csdn.net/weixin_43684318/article/details/120857170

在 STM32H7 的异常向量表中:

异常 名称 向量号 优先级(固定)
Reset 复位 1
NMI 不可屏蔽中断 2
HardFault 硬错误 3 固定最高(仅次于 NMI)

一、HardFault 的常见触发原因

类型 说明 典型示例
非法内存访问 访问不存在或未映射的地址 *(uint32_t*)0xFFFFFFF0 = 0;
空指针/野指针 指针未初始化或被破坏 p = NULL; *p = 1;
栈溢出或栈越界 任务栈空间不够或栈被破坏 RTOS任务或递归太深
执行非法指令 跳转到无效指令区 函数指针出错、堆栈破坏
未对齐访问 按 4 字节访问未对齐地址 LDR R0, [R1, #1] 且 R1 未对齐
除零操作 除数为零且 DIV_0_TRP 置位 / 0
总线错误(BusFault)上报 严重总线访问错误 访问 Flash/外设时序异常
内存管理错误上报 MPU 检测非法区域访问 非法访问受保护内存
使用非法返回地址 PC/LR 被破坏 栈损坏后 BX LR 跳错地址

二、Fault 分类结构(在 Cortex-M7 中)

Fault 类型 含义 是否能屏蔽 常引起的 HardFault
MemManage Fault 内存保护错误(MPU) 可屏蔽
BusFault 总线访问错误 可屏蔽
UsageFault 指令或数据使用错误 可屏蔽
HardFault 无法分类或升级错误 不可屏蔽 否(最高)

当以上三种 Fault 没有被单独开启或无法处理时,会自动升级为 HardFault


三、HardFault 时 CPU 的堆栈内容

当 HardFault 发生时,CPU 自动将以下寄存器压栈(在当前 SP 指向的栈):

压栈顺序 内容 说明
1 R0 参数或通用寄存器
2 R1 参数或通用寄存器
3 R2 参数或通用寄存器
4 R3 参数或通用寄存器
5 R12 临时寄存器
6 LR 返回地址
7 PC 出错指令地址
8 xPSR 程序状态寄存器

四、如何定位 HardFault 位置(调试方法)

方法 1:使用调试器(IAR)

当进入 HardFault_Handler 时:

  1. 打开 寄存器窗口,可以通过读取 CONTROL 寄存器 来判断:

📘 CONTROL[1] 位:

  • 0 → 使用 MSP(主堆栈指针)

  • 1 → 使用 PSP(进程堆栈指针)

2. 打开Memory窗口,搜索相应的栈指针地址(四字节查看)

3. 找到栈指针里面存的被卡死的PC地址(栈顶指针偏移7位),因为顺序是R0,R1,R2,R3,R12,LR,PC

4. 在反汇编里面获取PC对应的函数。

5. 根据汇编定位分析问题。

以我这个举例就是去非法获取123456地址的值。

方法 2:在 HardFault_Handler 中提取出错现场。程序调试

void HardFault_Handler(void)
{
    __asm volatile
    (
        "TST lr, #4            \n"   // 检查使用 MSP 还是 PSP
        "ITE EQ                \n"
        "MRSEQ r0, MSP         \n"
        "MRSNE r0, PSP         \n"
        "B hard_fault_handler_c\n"
    );
}
​
void hard_fault_handler_c(uint32_t *stacked_addr)
{
    uint32_t stacked_r0  = stacked_addr[0];
    uint32_t stacked_r1  = stacked_addr[1];
    uint32_t stacked_r2  = stacked_addr[2];
    uint32_t stacked_r3  = stacked_addr[3];
    uint32_t stacked_r12 = stacked_addr[4];
    uint32_t stacked_lr  = stacked_addr[5];
    uint32_t stacked_pc  = stacked_addr[6];  //  出错指令地址
    uint32_t stacked_psr = stacked_addr[7];
​
    printf("HardFault!\n");
    printf("R0  = 0x%08lx\n", stacked_r0);
    printf("R1  = 0x%08lx\n", stacked_r1);
    printf("R2  = 0x%08lx\n", stacked_r2);
    printf("R3  = 0x%08lx\n", stacked_r3);
    printf("R12 = 0x%08lx\n", stacked_r12);
    printf("LR  = 0x%08lx\n", stacked_lr);
    printf("PC  = 0x%08lx\n", stacked_pc);
    printf("PSR = 0x%08lx\n", stacked_psr);
​
    while(1);
}

在调试终端中即可看到 出错的 PC 地址 然后在 .map 文件或反汇编窗口中查找该地址属于哪个函数即可定位问题。

总结

分类 含义 定位方法
原因 非法访问 / 栈破坏 / 指令错误 由 CPU 自动进入 HardFault
入口 HardFault_Handler 在向量表中定义
分析 查看 PC、LR、CFSR、BFAR 定位代码位置
修复 校验指针、检查栈、任务、FPU 设置 防止再次发生
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐