STM32 触发HardFault_Handler如何查找原因

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

  • 52
    点赞
  • 261
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吾爱技术圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值