stm32 HardFault_Handler调试及问题查找方法

同时,github上面有很多项目用于stm32等cortex 内核的HardFault_Handler故障查找,可以到硬汉电子论坛去看看,我这里记得segger有SEGGER_HardFaultHandler.c和HardFaultHandler.S,硬汉哥已经将做了stm32的移植修改工作。

还有一个CmBacktrace-master,github可以下载的

以上两个都不叫方便的查找HardFault_Handler。

我在博客园也有整理过文章。有兴趣的可以看看

https://www.cnblogs.com/CodeWorkerLiMing/p/12029880.html

https://www.cnblogs.com/CodeWorkerLiMing/p/12028589.html

http://www.armbbs.cn/forum.php?mod=viewthread&tid=91746&fromuid=58

http://www.armbbs.cn/forum.php?mod=viewthread&tid=86354&fromuid=58

http://www.armbbs.cn/forum.php?mod=viewthread&tid=94364&highlight=MDK%BF%EC%CB%D9%B6%A8%CE%BB

http://www.armbbs.cn/forum.php?mod=viewthread&tid=19133&highlight=hardfault%2Bsegger

STM32出现HardFault_Handler故障的原因主要有两个方面:

1、内存溢出或者访问越界。这个需要自己写程序的时候规范代码,遇到了需要慢慢排查。

2、堆栈溢出。增加堆栈的大小。

出现问题时排查的方法:

发生异常之后可首先查看LR寄存器中的值,确定当前使用堆栈为MSP或PSP,然后找到相应堆栈的指针,并在内存中查看相应堆栈里的内容。由于异常发生时,内核将R0~R3、R12、Returnaddress、PSR、LR寄存器依次入栈,其中Return address即为发生异常前PC将要执行的下一条指令地址。

注意:寄存器均是32位,且STM32是小端模式。(参考Cortex-M3权威)

stm32 HardFault_Handler调试及问题查找方法_第1张图片

编写问题代码如下:

 

  1. void StackFlow(void)
  2.  
  3. {
  4.  
  5. int a[3],i;
  6.  
  7. for(i=0; i<10000; i++)
  8.  
  9. {
  10.  
  11. a[i]=1;
  12.  
  13. }
  14.  
  15. }
  16.  
  17. void SystemInit(void)
  18.  
  19. {
  20.  
  21.  
  22.  
  23.  
  24.  
  25. RCC->CR |= (uint32_t)0x00000001;
  26.  
  27.  
  28.  
  29. RCC->CFGR = 0x00000000;
  30.  
  31.  
  32.  
  33. RCC->CR &= (uint32_t)0xFEF6FFFF;
  34.  
  35.  
  36.  
  37. RCC->PLLCFGR = 0x24003010;
  38.  
  39. StackFlow();
  40.  
  41.  
  42.  
  43. RCC->CR &= (uint32_t)0xFFFBFFFF;
  44.  
  45. 。。。。。。。。。。。。。。
  46.  
  47. }

复制代码


 

 

DEBUG如下图

SP值为0x20008560,查看堆栈里面的值依次为R0~R3、R12、Return address、PSR、LR, 例如R0(1027 00 00), 显然堆栈后第21个字节到24字节即为Returnaddress,该地址0x08001FFD即为异常前PC将要执行的下一条指令地址(即StackFlow()后面的语句处RCC->CR &= (uint32_t)0xFFFBFFFF)

另一种方法:

默认的HardFault_Handler处理方法不是B .这样的死循环么?楼主将它改成BXLR直接返回的形式。然后在这条语句打个断点,一旦在断点中停下来,说明出错了,然后再返回,就可以返回到出错的位置的下一条语句那儿

 

      Cortex-M3/4的Fault异常是由于非法的存储器访问(比如访问0地址、写只读存储位置等)和非法的程序行为(比如除以0等)等造成的。常见的4种异常及产生异常的情况如下:


BusFault:在fetch指令、数据读写、fetch中断向量或中断时存储恢复寄存器栈情况下,检测到内存访问错误则产生BusFault。
Memory ManagementFault:访问了内存管理单元(MPU)定义的不合法的内存区域,比如向只读区域写入数据。
UsageFault:检测到未定义指令或在存取内存时有未对齐。还可以通过软件配置是否检测到除0和其它未对齐内存访问也产生该异常,默认关闭,需要在工程初始化时配置:

[cpp] viewplaincopyprint?

 

  • SCB->CCR |= 0x18; // enable div-by-0 and unaligned fault  



HardFault:在调试程序过程中,这种异常最常见。上面三种异常发生任何一种异常都会引起HardFault,在上面的三种异常未使能的情况下,默认发生异常时进入HardFault中断服务程序。使能前三种异常也要在初始化时配置:

[cpp] viewplaincopyprint?

 

  • SCB->SHCSR |= 0x00007000;   // enable Usage Fault, Bus Fault, and MMU Fault  




在默认复位初始化时,HardFault使能,其它三者不使能,因此当程序中出现不合法内存访问(一般是指针错误引起)或非法的程序行为(一般就是数学里面常见的除0)时都将产生HardFault中断

[url=]2 HardFault调试方法[/url]假设IDE环境为Keil,芯片为STM32F103。
在stm32f10x_it.c中,添加软件断点,一旦调试时出现Hard Fault则会在停在__breakpoint(0)处。



 

  •   
  • void HardFault_Handler(void)  
  • {  
  •    
  •   if (CoreDebug->DHCSR & 1) {  //check C_DEBUGEN == 1 -> Debugger Connected  
  •       __breakpoint(0);  // halt program execution here         
  •   }  
  •   while (1)  
  •   {  
  •   }  
  • }  




当进入HardFault断点后,菜单栏Peripherals >Core Peripherals >FaultReports打开异常发生的报告,查看发生异常的原因。
stm32 HardFault_Handler调试及问题查找方法_第2张图片  
stm32 HardFault_Handler调试及问题查找方法_第3张图片
上面的报告发生了BUS FAULT,并将Fault的中断服务转向Hard Fault。


相对于检测发生了什么异常,定位异常发生位置显得更重要。
(1)打开Call Stack窗口(如下图左侧,断点停在Hard Fault服务程序中)

stm32 HardFault_Handler调试及问题查找方法_第4张图片
(2)在Call Stack的HardFault_Handler上右键Show CallerCode(有的Keil版本也可以直接双击)
stm32 HardFault_Handler调试及问题查找方法_第5张图片

这时将跳转到发生异常的源代码位置(如上图),异常发生在p->hour=0这一行。这里错误很明显:指针p尚未为成员变量分配内存空间,直接访问未分配的内粗空间肯定出错。

再说明2点:
[1] 在复杂的情况下,即使定位了异常发生位置也很难容易的改正错误,要学会使用Watch窗口对发生错误的指针变量进行跟踪;
[2]在问题不明晰的情况下,尝试分析反汇编代码,就自己遇到的,部分情况下的异常发生在BL等跳转指令处,BL跳转到了不合法的内存地址产生异常

Refrences:
[1] Application Note209. Using Cortex-M3 and Cortex-M4 FaultExceptions. 

 

[2] Cortex-M3权威指南

你可能感兴趣的:(嵌入式/stm32)