ARM Cortex-M3无法触发Bus Fault、Usage Fault等异常的问题排查
咱们一步步拆解你代码里的问题,以及为什么触发异常后没进入预期的handler:
1. 错误配置了SCB->CFSR寄存器
SCB->CFSR是可配置故障状态寄存器,它本质是个**只读(仅支持写1清零错误位)**的状态记录寄存器,用来标记已发生的错误类型,而非配置异常开启的控制寄存器。你直接给它赋值0x0367E7C3完全不合理——这些位要么是只读的状态位,要么只能通过写1来清除错误标记,初始化时应该清零(写对应位为1完成清除)或者保持上电默认值(0)。这个错误的写操作可能干扰了故障检测的正常逻辑。
正确的处理方式:
// 写1清除所有可配置故障状态位 SCB->CFSR = 0xFFFFFFFF;
2. 除零异常未触发:你用的是浮点数除法(软件模拟)
你提到除零后变量显示为"Infinity",这说明你执行的是浮点数除法。而Cortex-M3的DIV_0_TRP位(SCB->CCR的bit4)只对**硬件整数除法指令(SDIV/UDIV)**生效。
如果是C语言中的浮点数除法,编译器会调用软件库处理,除零会返回IEEE标准的Infinity值,不会触发硬件UsageFault。要触发除零异常,你需要用整数除法,比如:
int a = 1; int b = 0; int c = a / b; // 这会触发硬件除零异常,进入UsageFault_Handler
另外要确保编译器没有把整数除法优化成软件模拟,比如ARM GCC的-mthumb -mcpu=cortex-m3默认会启用硬件除法支持,不用额外配置。
3. Bus Fault未触发的可能原因
首先要确认你触发Bus Fault的方式是否正确,常见的有效触发场景:
- 访问不存在的内存地址(比如超出芯片地址空间的0xFFFFFFF0)
- 开启对齐检查(SCB->CCR的
UNALIGN_TRP位,bit3)后,执行非对齐的内存访问(比如用32位指针访问奇数地址的内存) - 访问未就绪或地址错误的外设
另外,检查你的Bus Fault handler是否被编译器优化掉了——如果handler是空的,编译器可能会把它优化成空函数甚至直接忽略,导致调试时无法命中断点。你可以在handler里加死循环防止优化:
void BusFault_Handler(void){ while(1){ volatile int dummy = 0; // 用volatile变量阻止编译器优化 dummy++; } }
4. 其他注意事项
- 调试器配置:有些调试器会在故障发生时自动暂停,而非进入自定义handler,你可以关闭调试器里类似"Auto halt on fault"的选项,让程序进入你自己的handler。
- SCB->SHCSR的设置是对的:
0x00070000对应开启MemManage(bit16)、BusFault(bit17)、UsageFault(bit18),这部分没问题。 - NVIC优先级:你把三个故障的优先级设为0x01是可行的,但要注意Cortex-M3的优先级分组(SCB->AIRCR的PRIGROUP字段),确保优先级值在有效范围内。
最后,修正后的EnableExceptions函数参考:
void EnableExceptions(void) { UINT32 uReg = SCB->SHCSR; // 开启MemManage、BusFault、UsageFault(用宏定义更易读) uReg |= (SCB_SHCSR_MEMFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk | SCB_SHCSR_USGFAULTENA_Msk); SCB->SHCSR = uReg; // 清除所有可配置故障状态位 SCB->CFSR = 0xFFFFFFFF; // 开启除零触发UsageFault SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk; // 设置故障handler优先级 NVIC_SetPriority(MemoryManagement_IRQn, 0x01); NVIC_SetPriority(BusFault_IRQn, 0x01); NVIC_SetPriority(UsageFault_IRQn, 0x01); }
这样修改后,用整数除零或正确的Bus Fault触发方式,应该就能进入对应的handler了。
内容的提问来源于stack exchange,提问作者justsomeoneelse

