UsageFault发生的原因
2026/9/30大约 2 分钟
Cortex-M UsageFault 发生的原因
1. 什么是 UsageFault?
UsageFault 主要用于报告 CPU 执行指令时出现的使用错误。
常见原因:
- 除 0
- 非对齐访问
- 执行未定义指令
- 非法状态
- 异常返回错误
2. 除 0 为什么默认不一定触发 UsageFault?
以除 0 为例,需要打开两层开关。
第一步:使能 UsageFault

下面这里依旧是根据寄存器各个位的情况来判断出错的类型


SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk;这只是允许 CPU 把错误交给 UsageFault 处理。
第二步:打开除 0 检测
在 CCR 中设置 DIV_0_TRP:


SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;否则除 0 不会产生 UsageFault。
测试:
volatile int a = 1;
volatile int b = 0;
volatile int c = a / b;打开后:
除 0
↓
UsageFault
↓
UsageFault_Handler3. CCR 还可以控制非对齐访问
CCR 中还有:
UNALIGN_TRP打开:
SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;表示对非对齐访问进行陷阱处理。
例如:
uint32_t *p = (uint32_t *)0x20000001;
uint32_t value = *p;地址不是 4 字节对齐:
0x20000001开启 UNALIGN_TRP 后可能触发 UsageFault。

4. 为什么自己写 Handler 后才进自己的函数?
启动文件通常已经提供了默认处理函数:
UsageFault_Handler默认情况下,它通常最终进入:
Default_Handler所以如果我们不重新定义:
void UsageFault_Handler(void)
{
// ...
}发生 UsageFault 后就会进入默认处理函数。
自己实现同名的:
void UsageFault_Handler(void)
{
// 自己的错误处理
}就可以覆盖启动文件中的弱定义,从而进入自己的 Handler。
5. 整体流程
以除 0 为例:
CCR.DIV_0_TRP = 1
↓
SHCSR.USGFAULTENA = 1
↓
执行除 0
↓
触发 UsageFault
↓
UsageFault_Handler
↓
解析 CFSR / UFSR代码
#include <stdio.h>
#include <stdint.h>
#include "ARMCM3.h"
/**
* 硬件自动压栈
*/
typedef struct _stack_frame_t {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t xpsr;
}stack_frame_t;
// 打印寄存器
static void fault_show_regs (stack_frame_t * frame) {
printf("Registers:\n");
printf("R0 = 0x%x\n", frame->r0);
printf("R1 = 0x%x\n", frame->r1);
printf("R2 = 0x%x\n", frame->r2);
printf("R3 = 0x%x\n", frame->r3);
printf("R12 = 0x%x\n", frame->r12);
printf("LR = 0x%x\n", frame->lr);
printf("PC = 0x%x\n", frame->pc - 2); // 读取的值比实际值+2
printf("xpsr = 0x%x\n", frame->xpsr);
}
/**
* 报告BusFault
*/
void BusFault_C_Handler (stack_frame_t * frame) {
uint32_t fault_reason = SCB->CFSR & SCB_CFSR_BUSFAULTSR_Msk;
printf("BusFault fault:\n");
if (fault_reason & SCB_CFSR_IBUSERR_Msk) {
printf("instruction bus error.\n");
}
if (fault_reason & SCB_CFSR_PRECISERR_Msk) {
printf("a data bus error has occurred, and the PC value stacked for the exception"
"return points to the instruction that caused the fault\n");
}
if (fault_reason & SCB_CFSR_IMPRECISERR_Msk) {
printf("a data bus error has occurred, but the return address in the stack frame"
"is not related to the instruction that caused the error\n");
}
if (fault_reason & SCB_CFSR_UNSTKERR_Msk) {
printf("Unstack for an exception return has caused one or more BusFaults\n");
}
if (fault_reason & SCB_CFSR_STKERR_Msk) {
printf("stacking for an exception entry has caused one or more BusFaults\n");
}
if (fault_reason & SCB_CFSR_BFARVALID_Msk) {
printf("Fault address: 0x%x\n", SCB->BFAR);
} else {
printf("Fault address invalid\n");
}
fault_show_regs(frame);
}
// 硬件错误处理
__attribute__((naked)) void BusFault_Handler (void) {
__asm(
".extern BusFault_C_Handler\n"
"mrs r0, msp\n"
"b BusFault_C_Handler"
);
}
int main (void) {
SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk; // 使能
for (;;) {
}
return 0;
}
