stm32H7进入HardFault_Handler机制

异常和中断区别:

在Cortex-M7中,所有能打断CPU正常执行流程的事件统称为异常。而中断是异常的一个子集,特指由外设信号或软件触发的事件

Cortex-M7支持最多256个异常,其中前16个是系统异常,从编号16开始是外部中断(IRQ)。

异常编号 异常类型 优先级 描述
1 Reset -3 (最高) 系统复位。优先级固定,不可配置。
2 NMI -2 不可屏蔽中断。通常用于看门狗或安全警报等最高优先级的硬件事件。
3 HardFault -1 硬件错误。当其他故障处理程序无法处理时触发,是最后一道防线。
4 MemManage 可配置 内存管理错误。由MPU(内存保护单元)检测到的违规访问引发。
5 BusFault 可配置 总线错误。发生在指令或数据访问的总线传输阶段。
6 UsageFault 可配置 用法错误。由执行未定义指令、非对齐访问等非法操作引发。
7-10 - - 保留
11 SVCall 可配置 系统服务调用。由执行SVC指令触发,常用于操作系统内核服务。
12 Debug Monitor 可配置 调试监视器。用于调试功能。
13 - - 保留
14 PendSV 可配置 可挂起的系统调用。常用于操作系统的上下文切换,优先级通常设为最低。
15 SysTick 可配置 系统节拍定时器。为操作系统提供时间基准。
16 及以上 IRQ0 ~ IRQn 可配置 外部中断。由芯片厂商定义的具体外设中断,如GPIO、UART、定时器等

STM32H753(基于 Cortex-M7 内核),进入 HardFault_Handler() 的机制和故障分级,比普通内核更严谨、更精细。它的核心是一个优先级固定、不可屏蔽的"终极异常",主要用于兜底那些其他异常机制处理不了的严重错误。

进入HardFault_Handler()触发条件:

1.错误升级(Fault Escalation)------ 最常见原因

UsageFault,BusFault和 MemManage Fault可以进行配置,提前拦截,不用进入HardFault

A. 用法错误 (UsageFault)

B. 总线错误 (BusFault)

C. 内存管理错误 (MemManage Fault)

2.直接触发的 HardFault(无法被屏蔽的硬错误)

3.STM32H753 (Cortex-M7) 独有的/高发的陷阱

代码现象与触发来源对应关系:

HardFault Status Register, HFSR

如何追溯到HardFault_Hanler()产生来源:

方式1:借助 IDE 寄存器窗口手动追溯(最基础直观)

方式2:编写智能捕获代码(汇编提取栈上下文 + C语言解析)

cpp 复制代码
volatile uint32_t g_HF_R0;
volatile uint32_t g_HF_R1;
volatile uint32_t g_HF_R2;
volatile uint32_t g_HF_R3;
volatile uint32_t g_HF_R12;
volatile uint32_t g_HF_LR;   /* Return address of the faulting instruction's caller */
volatile uint32_t g_HF_PC;   /* Faulting instruction address */
volatile uint32_t g_HF_PSR;
volatile uint32_t g_HF_CFSR;  /* Configurable Fault Status Register */
volatile uint32_t g_HF_HFSR;  /* HardFault Status Register */
volatile uint32_t g_HF_MMFAR; /* MemManage Fault Address Register */
volatile uint32_t g_HF_BFAR;  /* BusFault Address Register */
volatile uint32_t g_HF_AFSR;  /* Auxiliary Fault Status Register */
cpp 复制代码
__asm void HardFault_Handler(void)  
{
  IMPORT HardFault_C_Handler
  TST LR, #4
  ITE EQ
  MRSEQ R0, MSP
  MRSNE R0, PSP
  B HardFault_C_Handler
}
/*
//__asm是一个编译器关键字,告诉编译器不要当作c语言处理,直接翻译成ARM机器码
*/

/**
  * @brief  Captures fault diagnostics into globals and halts on a breakpoint.
  * @param  hardfault_args Pointer to the exception stack frame
  *         (r0, r1, r2, r3, r12, lr, pc, psr).
  */
void HardFault_C_Handler(uint32_t *hardfault_args)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */
  g_HF_R0  = hardfault_args[0];
  g_HF_R1  = hardfault_args[1];
  g_HF_R2  = hardfault_args[2];
  g_HF_R3  = hardfault_args[3];
  g_HF_R12 = hardfault_args[4];
     //= hardfault_args[5];
  g_HF_PC  = hardfault_args[6];
  g_HF_PSR = hardfault_args[7];

  g_HF_CFSR  = SCB->CFSR;
  g_HF_HFSR  = SCB->HFSR;
  g_HF_MMFAR = SCB->MMFAR;
  g_HF_BFAR  = SCB->BFAR;
  g_HF_AFSR  = SCB->AFSR;

  __BKPT(0);
  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}
/*
寄存器数据是如何进入 fault_args 数组的:
这是利用了 ARM 函数传参标准 (AAPCS):
汇编代码 (MRSEQ R0...) 将提取到的栈指针物理地址放进了 R0 寄存器。
ARM 标准规定,C 函数的第一个参数强制对应 R0 寄存器。
因此跳转到 C 函数后,R0 里的栈首地址直接传给了指针参数 fault_args。栈里面硬件自动压入的 8 个寄存器连续存放在这个地址后,在 C 语言看来,就顺理成章地变成了 fault_args[0] 到 fault_args[7] 的数组元素。
*/

Configurable Fault Status Register, CFSR

from:Arm® v7-M Architecture Reference Manual

SCB->CFSR(可配置错误状态寄存器)是"案情鉴定书",它告诉你死因(WHAT)

SCB->BFAR(总线错误地址寄存器)或 SCB->MMFAR 是"案发地点",它告诉你惹祸的地址(WHERE)

在 Cortex-M7 中,CFSR 是一个 32 位的寄存器,从物理上分为三部分:

UFSR(用法错误):高 16 位 (Bit 16~31)

BFSR(总线错误):中 8 位 (Bit 8~15)

MMFSR(内存管理错误):低 8 位 (Bit 0~7)

代码现象与寄存器标志映射关系:

指针与内存管理类

  • 1.1.2 指针操作错误 (野指针/空指针)
    • CFSR 表现: BFSR 区域的 PRECISERR (精确数据总线错误,Bit 9) 置 1
    • BFAR 表现: 有效(BFARVALID 置 1)BFAR 寄存器里保存的,就是那个空指针(0x00000000)或野指针(如 0x2009ABCD)的地址。
  • 1.1.5 存储器管理问题 (越界/非法区域)
    • CFSR 表现: MMFSR 区域的 DACCVIOL (数据访问违例,Bit 1) 置 1
    • BFAR/MMFAR 表现: MMFAR 里保存了试图访问的受 MPU 保护或不可访问的内存地址。

栈与 RTOS 崩溃类

  • 1.1.1 堆栈溢出
    • CFSR 表现: 如果是发生中断压栈时溢出,BFSR 区域的 STKERR (压栈错误,Bit 12)MMFSRMSTKERR (压栈内存违例,Bit 4) 会置 1。
    • (注:普通的局部变量导致栈溢出,通常表现为修改了其他数据区,后续引发 1.1.2 的指针错误)
  • 1.1.3 中断优先级配置错误 & 1.1.9 RTOS 栈与优先级问题
    • 现象本质:由于 FreeRTOS 临界段被破坏,导致寄存器上下文错乱,中断返回时给 PC/LR 赋了乱码。
    • CFSR 表现: UFSR 区域的 INVSTATE (无效状态,Bit 24)INVPC (无效PC,Bit 18) 置 1。表示试图跳转到无效地址执行,或没有处于 Thumb 模式。
  • 1.1.10 FPU 与栈对齐
    • 现象本质:M7 对浮点的多字操作必须 8 字节对齐,如果 RTOS 任务栈未对齐就执行了 FPU 代码。
    • CFSR 表现: UFSR 区域的 UNALIGNED (非对齐访问,Bit 24) 置 1

H7 (Cortex-M7) 特有硬件/外设类

  • 1.1.4 外设错误配置 (没开时钟就初始化)
    • CFSR 表现: 通常是 BFSRIMPRECISERR (不精确数据总线错误,Bit 10) ,或者是 PRECISERR (精确错误,Bit 9) 置 1
    • BFAR 表现: 如果是精确错误,BFAR 会明确指出你配置出错的那个外设寄存器地址(例如 GPIOB 的 0x40020400)。
  • 1.1.7 时钟配置错误 (主频太高,Flash读不过来)
    • CFSR 表现: BFSRIBUSERR (指令总线错误,Bit 8) 置 1
    • 解释:CPU 试图从 Flash 取指令,但 Flash 控制器没有返回正确数据,导致指令总线报错。
  • 1.1.11 DMA 与缓存一致性 & 1.1.13 MPU 缺失
    • 现象本质:CPU 读到了 Cache 中的脏数据,把它当成了正常数据去跑,导致程序跑飞。
    • CFSR 表现: 非常随机! 极可能是 PRECISERR(脏数据变成了野指针),也可能是 UNDEFINSTR (未定义指令,Bit 16)(脏数据变成了错误的代码指令)。
    • BFAR 表现: 会指明跑飞后访问的那个奇怪地址。

代码逻辑优化类

  • 1.1.6 代码逻辑错误 (除以 0 等)
    • CFSR 表现: UFSR 区域的 DIVBYZERO (除以零,Bit 25) (前提是 SCB 中使能了除零异常) 或 UNDEFINSTR (未定义指令,Bit 16) 置 1
  • 1.1.12 编译优化与未定义行为
    • 现象本质:强制类型转换被 O2/O3 优化为了 LDM/STM 或 LDRD 双字读取。
    • CFSR 表现: UFSR 区域的 UNALIGNED (非对齐访问,Bit 24) 将坚定地置 1
相关推荐
小僧景贤2 小时前
STM32 GPIO 详解(含寄存器、HAL 库、电气特性、低功耗深度剖析)
stm32·单片机·嵌入式硬件
远翔调光芯片^138287988724 小时前
从参数到应用:FP7208如何以0.2V精密反馈提升LED驱动性能
科技·单片机·嵌入式硬件·智能家居·能源
碧海银沙音频科技研究院4 小时前
4 BES2710编译环境搭建方法
嵌入式硬件·算法
别催小唐敲代码6 小时前
STM32 定时器学习笔记:TIM 架构、输入捕获与 PWM 一篇讲透
笔记·stm32·学习
csdn杰哥6 小时前
瑞萨单片机AI教程【六】导入外部电机状态数据训练模型
人工智能·单片机·嵌入式硬件·瑞萨·ra6m5
深念Y6 小时前
ZTE_UZ901_NVRAM经验总结
linux·服务器·网络·嵌入式硬件·嵌入式·随身wifi
远翔调光芯片^138287988726 小时前
高效驱动!FP7153赋能3A大电流手电筒,单节锂电 / 5V 供电也能迸发强劲照明力
科技·单片机·嵌入式硬件·智能家居·能源
wuyk5557 小时前
89.嵌入式内存管理的“稳定利器”:内存池原理与C语言实现
c语言·开发语言·stm32·单片机·嵌入式硬件
芯片设计-曾工7 小时前
远乐YL1628 SSOP28驱动芯片详解
驱动开发·单片机·51单片机·硬件工程