keil遇到hardfault时原因的查找
当硬件仿真遇到hardfault会:依次将 xPSR、PC、LR、R12以及 R3~R0的8个寄存器压栈。参考2
1 首先通过LR(看LR的BIT2是0还是1)判断出异常产生时当前使用的SP是MSP还是PSP。此时LR 会被更新为异常返回时需要使用的特殊值(EXC_RETURN)定义如下:
2 根据SP对应的堆栈值查看内存,根据压栈的8个寄存器顺序找到出错时的PC值(倒数第二个)。
3 goto PC寄存器位置。
进入响应的中断软件陷阱中void HardFault_Handler(void),此时通过view-registers中的
1 如果STACK=MSP,则查看SP的堆栈值,在memrory窗口输入sp的值回车,在地址内容之后的第21字节开始的4个字节为LR的值,在堆栈调用窗口右击选择show callee code,在反汇编窗口右击选择show code at address,输入LR的值然后回车,就是发生hardfault前的调用大致位置,仔细查找即可,
一般都是因为数组越界,访问了超过范围或者未定义的地址,或者利用字符串库函数或者内存操作库函数时出现的情况。扩展参考,
或者用
CmBacktrace +addr2line定位hardfault:
其中addr2line(addr2line (它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具)可全盘搜索,然后将其放到C盘WINDOWS下,在ENV中添加软件包,在keil-mdk中设置axf输出(注意output下的bin和user下axf名字要一致),然后在出现hard fault后,在win 的命令行中切换到项目axf文件所在的文件夹,然后按照cmbacktrace中的提示在命令行中运行addr2line -e........
最终:
《HardFault的诊断.pdf》:

1 /* 2 **无操作系统只用MSP;带操作系统中断用 MSP,任务用 PSP; 3 **SP究竟是MSP还是PSP,取决于LR.2(1=MSP;0=PSP. 4 **将下述代码分别放入一个hard_fault_handler.c文件和 5 **HardFault_Handler.s文件,再用cmbacktrace分析 6 **/ 7 8 9 10 /*建独立文件hard_fault_handler.c**/ 11 void hard_fault_handler (unsigned int * hardfault_args) 12 { 13 unsigned int stacked_r0; 14 unsigned int stacked_r1; 15 unsigned int stacked_r2; 16 unsigned int stacked_r3; 17 unsigned int stacked_r12; 18 unsigned int stacked_lr; 19 unsigned int stacked_pc; 20 unsigned int stacked_psr; 21 22 stacked_r0 = ((unsigned long) hardfault_args[0]); 23 stacked_r1 = ((unsigned long) hardfault_args[1]); 24 stacked_r2 = ((unsigned long) hardfault_args[2]); 25 stacked_r3 = ((unsigned long) hardfault_args[3]); 26 stacked_r12 = ((unsigned long) hardfault_args[4]); 27 stacked_lr = ((unsigned long) hardfault_args[5]); 28 stacked_pc = ((unsigned long) hardfault_args[6]); 29 stacked_psr = ((unsigned long) hardfault_args[7]); 30 31 printf ("\n\n[Hard fault handler - all numbers in hex]\n"); 32 printf ("R0 = %x\n", stacked_r0); 33 printf ("R1 = %x\n", stacked_r1); 34 printf ("R2 = %x\n", stacked_r2); 35 printf ("R3 = %x\n", stacked_r3); 36 printf ("R12 = %x\n", stacked_r12); 37 printf ("LR [R14] = %x subroutine call return address\n", stacked_lr); 38 printf ("PC [R15] = %x program counter\n", stacked_pc); 39 printf ("PSR = %x\n", stacked_psr); 40 printf ("BFAR = %x\n", (*((volatile unsigned long *)(0xE000ED38)))); 41 printf ("CFSR = %x\n", (*((volatile unsigned long *)(0xE000ED28)))); 42 printf ("HFSR = %x\n", (*((volatile unsigned long *)(0xE000ED2C)))); 43 printf ("DFSR = %x\n", (*((volatile unsigned long *)(0xE000ED30)))); 44 printf ("AFSR = %x\n", (*((volatile unsigned long *)(0xE000ED3C)))); 45 printf ("SCB_SHCSR = %x\n", SCB->SHCSR); 46 while (1); 47 } 48 49 ;建独立文件“HardFault_Handler.s” 50 HardFault_Handler: 51 TST LR, #4 52 ITE EQ 53 MRSEQ R0, MSP 54 MRSNE R0, PSPB 55 hard_fault_handler_c
扩展阅读:
CmBacktrace在keil上的移植
RTT相关处理
Tracealyzer:支持FreeRTOS、Keil RTX5、Linux、On Time RTOS-32、ThreadX、µC/OS-III、VxWorks
Segger提供了一种分析CortexM内核芯片HardFault的方法:文档,相关软件。总体思路:屏蔽软件工程自己的hardfuault_hander,在它提供的软件(汇编或C)中修改_Continue让其退出hardfualut_hander后基本就是错误位置。
_Continue = 0u;
while (_Continue == 0u) { }
当发生异常,会将保存上述寄存器到当前堆栈,LR.bit2的决定是主堆栈还是线程栈,其它28位都为1加载到PC.
Imprecise Bus Fault一般是由于写无效的地址,由于PC比实际写总线时间超前一点,所以要适当回退找到错误位置。
lr为连接寄存器里面保存的是调用子程序之前的PC的值。 因为 CM3 内部 使用了指令流水线,读 PC 时返回的值是当前指令的地址+4 ,所以进入异常之前自动加载到lr的值也为调用子程序前的指令地址+4。由于PC的最低位保存ARM/Thumb 运行状态,Cortex-M3只能运行在Thumb 状态,即LSB = 1。所以我们想要找到出错误程序的指令位置, 只需要对打印出来的lr的值减去5即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2018-07-03 软件模拟I2C时输入与输出切换