STM32出现HardFault_Handler硬件错误的原因主要有两个方面:
1、内存溢出或者访问越界。(包括使用野指针)
2、堆栈溢出。
下面的几个方法不一定能找对地方,所以有时候需要多试几个方法。并且结合变量值分析错误原因,实际情况可能随程序复杂程度不同,但根本原因基本是上面这两点之一。
方法一
<下面用来测试的芯片是STM32L475,Cotex-M4内核>
在中断HardFault_Handler中的while()处打上断点,全速运行后,想法让程序问题复现触发HardFault,让程序执行到此处停止。(如何使用调试器DEBUG因为不是本文重点这里就不赘述了)
看左侧Registers Window窗口(界面上找不到窗口的话,Keil菜单栏点击“View”——“Registers Window”),在寄存器查看窗口查找R14(LR)的值。如果R14(LR) = 0xFFFFFFE9,继续查看MSP(主堆栈指针)的值,如果R14(LR) = 0xFFFFFFFD,继续查看PSP(进程栈指针)的值。
发生异常之后可首先查看LR寄存器中的值,确定当前使用堆栈为MSP或PSP,然后找到相应堆栈的指针,并在内存中查看相应堆栈里的内容。
在Cortex_M3权威指南中可以看到如下图所示:
这里解释一下关于 LR 寄存器的工作原理。如上所述,当 Cortex-M4 处理器接受了一个异常后,寄存器组中的一些寄存器值会被自动压入当前栈空间里,这其中就包括链接寄存器(LR )。这时的 LR 会被更新为异常返回时需要使用的特殊值(EXC_RETURN)。关于
EXC_RETURN 的定义如下,其为 32 位数值,高 28 位置 1,第 0 位到第三位则提供了异常返回机制所需的信息,如下表所示。可见其中第 2 位标示着进入异常前使用的栈是 MSP还是PSP。在异常处理过程结束时,MCU 需要根据该值来分配 SP 的值。这也是本方法中用来判断所使用堆栈的原理。
我的程序R14(LR) = 0xFFFFFFF9,注意这里R13(SP)的值实际上与MSP的值一致。
Keil菜单栏点击“View”——“Memory Windows”——“Memory1”,在“Address”地址栏中输入MSP的值:0x20011BA0,然后在对应行里找到地址。地址一般以0x08开头的32位数。注意从右往左看。发生异常后我们可以首先查看LR寄存器的值,确认当前使用的堆栈是MSP还是PSP,然后找到相对应的堆栈指针,并在内存中查看相对应堆栈的内容,内核将R0~R3,R12,LR,PC(Return address),xPRS寄存器依次入栈,其中堆栈后第25个字节到28字节PC(Return address)即为发生异常前PC将要执行的下一条指令地址。本例中,地址为0x08002D0C和0x08002953。
注意这里可以右键设置为32位查看,这样看得比较方便:
设置后如下图:
◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
【补充】
从Memory1可以看到0x20011BA0开始的第一个32位值就对应了R0的值,后面也分别对应了R1,R2,R3,R12
◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
在Keil菜单栏点击“View”——“Disassembly Window”(此反汇编窗口一般默认已经开启),在“Disassembly”窗口中右击,在下拉菜单中选择“Show Disassemblyat Address…”。在弹出框“Show Code atAdress”的地址框中输入地址0x08002D0C进行搜索,然后就会找到相对应的代码。这里的代码就是进入循环中断之前的情况。仔细查看附近区域的相关代码来排查错误具体原因。
用同样的方法在show code at address中输入0x08002953,发现查看的是出错的上一层函数(好像不太准确)
方法二
在硬件中断函数HardFault_Handler里的while(1)处打调试断点,程序执行到断点处时自动停止。
在Keil菜单栏点击“View”——“Call Stack Window”弹出“Call Stack + Locals”对话框。然后在对话框中HardFault_Handler处右键选择“Show Caller Code”,就会跳转到出错之前的函数处,仔细查看这部分函数被调用或者内存使用情况。
◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
【补充】
当进入HardFault断点后,菜单栏Peripherals >Core Peripherals >FaultReports打开异常发生的报告,可以查看发生异常的原因。
上面的报告发生了BUS FAULT,并将Fault的中断服务转向Hard Fault。
下面部分寄存器说明来自CM3权威指南,与错误报告中的状态位相对应。
总线BUS出现PRECISERR大概率是出现访问越界。
◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
方法三
此方法有点类似方法一,这种方法不用借助调试器,只需在HardFault_Handler中增加打印相关寄存器的代码。
下面的例程中的寄存器地址是根据CM3内核来的。其他内核可能要随之变化。
void hard_fault_handler_c(unsigned int * hardfault_args)
{
static unsigned int stacked_r0;
static unsigned int stacked_r1;
static unsigned int stacked_r2;
static unsigned int stacked_r3;
static unsigned int stacked_r12;
static unsigned int stacked_lr;
static unsigned int stacked_pc;
static unsigned int stacked_psr;
static unsigned int SHCSR;
static unsigned char MFSR;
static unsigned char BFSR;
static unsigned short int UFSR;
static unsigned int HFSR;
static unsigned int DFSR;
static unsigned int MMAR;
static unsigned int BFAR;
stacked_r0 = ((unsigned long) hardfault_args[0]);
stacked_r1 = ((unsigned long) hardfault_args[1]);
stacked_r2 = ((unsigned long) hardfault_args[2]);
stacked_r3 = ((unsigned long) hardfault_args[3]);
stacked_r12 = ((unsigned long) hardfault_args[4]);
stacked_lr = ((unsigned long) hardfault_args[5]);
stacked_pc = ((unsigned long) hardfault_args[6]);
stacked_psr = ((unsigned long) hardfault_args[7]);
SHCSR = (*((volatile unsigned long *)(0xE000ED24)));
MFSR = (*((volatile unsigned char *)(0xE000ED28)));
BFSR = (*((volatile unsigned char *)(0xE000ED29)));
UFSR = (*((volatile unsigned short int *)(0xE000ED2A)));
HFSR = (*((volatile unsigned long *)(0xE000ED2C)));
DFSR = (*((volatile unsigned long *)(0xE000ED30)));
MMAR = (*((volatile unsigned long *)(0xE000ED34)));
BFAR = (*((volatile unsigned long *)(0xE000ED38)));
printf("\n\n[Hard fault handler - all numbers in hex]\n\n");
printf("R0 = %x\n",stacked_r0);
printf("R1 = %x\n",stacked_r1);
printf("R2 = %x\n",stacked_r2);
printf("R3 = %x\n",stacked_r3);
printf("R12 = %x\n",stacked_r12);
printf("LR[R14] = %x subroutine call return address\n",stacked_lr);
printf("PC[R15] = %x program counter\n",stacked_pc);
printf("PSR = %x\n",stacked_psr);
printf("SHCSR = %x\n",(*((volatile unsigned long*)(0xE000ED24))));
printf("BFAR = %x\n",(*((volatile unsigned long*)(0xE000ED38))));
printf("CFSR = %x\n",(*((volatile unsigned long*)(0xE000ED28))));
printf("HFSR = %x\n",(*((volatile unsigned long*)(0xE000ED2C))));
printf("DFSR = %x\n",(*((volatile unsigned long*)(0xE000ED30))));
printf("AFSR = %x\n",(*((volatile unsigned long*)(0xE000ED3C))));
printf("SCB_SHCSR = %x\n",SCB->SHCSR);
while (1);
}
参考鸣谢:
https://www.cnblogs.com/zhangshenghui/p/5944881.html
https://blog.csdn.net/u011857683/article/details/79388652
http://news.eeworld.com.cn/mcu/article_2017110835733.html
http://www.51hei.com/bbs/dpj-39846-1.html
https://blog.csdn.net/qq_36413982/article/details/89249477
http://news.eeworld.com.cn/mcu/article_2016062327235.html
好文推荐:
https://blog.csdn.net/_xiao/article/details/78475195