stm32F4系列出现HardFault_Handler解决方法

在基于stm32F4系列的微处理器上进行嵌入式编程,有很大可能会遇到HardFault,一是由于C语言自身机制问题,二是由于在芯片上编程,就需要考虑到芯片自身架构,如存储,总线结构等。博主自身有一年的参加robocon的经历,也上网查过许多相关资料,本着分享知识的精神,总结此文,希望能帮助大家。

文章结构:
一 硬件中断产生的原因
二 硬件中断处理方法


HardFault产生原因

1.内存的溢出,包括堆栈的溢出。
其中单片机内存和堆栈的关系,可以参考http://blog.csdn.net/c12345423/article/details/53004747
2.越界访问。
这常指数组的使用,具体来说,访问只有5个元素的数组的第6个元素时,就出现了越界访问。而这一错误,常常出现于数组作为函数参数传入时,由于只传入指针,而函数中不确定指针访问的平移量,就可能出现越界访问的错误。值得注意的是,C语言并没有越界访问的编译查询,也就是说,在编译时不会检测是否存在越界访问。
3.错误使用flash造成的异常错误。
一是由于在使用flash存储数据时,其存储空间有可能和代码区重叠;二是由于自身需求,需要转换指向flash的指针的指向类型,如转换成float*,使指针在flash上以4个单位的间隔移动,但是由于flash是分区的,如果区首地址和被转换指针之间的间隔不是4的倍数也会出现错误。
4.这一年大家都是自己画PCB,自己写程序,有时候会发现PCB的焊接也会造成HardFault,并且这种错误从程序开始就会存在。
5.野指针寻址,除以0(也可能得出inf而不会产生错误)等常见C语法错误。

以上是博主所了解的可能出现硬件中断的原因,欢迎广大网友补充。

HardFault解决方法

单步调试法
顾名思义,当HardFault每次都会出现时,我们可以用仿真工具进行逐步调试,进而确定硬件错误发生的语句。
查看LR寄存器法
LR(Link Register),连接寄存器的英文缩写,在ARM体系结构中LR的特殊用途有两种:一是用来保存子程序返回地址;二是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2),因此在各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行。(百度百科)
如下图在HardFault函数头打上断点
stm32F4系列出现HardFault_Handler解决方法_第1张图片
此时LR寄存器如下图所示
stm32F4系列出现HardFault_Handler解决方法_第2张图片
查看 LR 寄存器的值,如果是 0XFFFFFFF9,那么中断前使用的是 MSP,如果是 0XFFFFFFFD,那么中断前使用的是 PSP。(MSP和PSP 的含义是Main_Stack_Pointer 和Process_Stack_Pointer,在逻辑地址上他们都是R13。 权威手册上说的很清楚PSP主要是在Handler的模式下使用,MSP主要在线程模式下使用(当然你在线程模式下也可以调用PSP,需要你做特殊的处理)。这意味着同一个逻辑地址,实际上有两个物理寄存器,一个为MSP,一个为PSP,在不同的工作模式调用不同的物理寄存器。举一个简单的例子,很多MCU的的UART只有一个BUFF,TXBUFF和RXBUFF都是一个地址,当你写BUFF时写入的是TXBUFF, 读操作时调用的是RXBUFF。基本原理就是这样。 至于为什么这么设计,我想是为了在进行模式转换的时候,减少堆栈的保存工作。同时也可以为不同权限的工作模式设置不同的堆栈。(侵删))
这其中也涉及到中断函数入栈的问题,查阅Cotex-3手册,可以找到如下:
stm32F4系列出现HardFault_Handler解决方法_第3张图片根据找到的堆栈指针, 在内存中查看相应堆栈里的内容。由于异常发生时,内核将 R0~R3、 R12、 LR、 PC、 XPRS 寄存器依次入栈,其中 LR 即为发生异常前 PC 将要执行的下一条指令地址。
因此我们去查看堆栈指针SP所指向的内容,如下,LR的内容即0X08001E98,即发生异常前 PC 将要执行的下一条指令地址。

stm32F4系列出现HardFault_Handler解决方法_第4张图片

    输入指令地址即可跳转到对应程序处

stm32F4系列出现HardFault_Handler解决方法_第5张图片

上述可以debug实现,对于过程中偶然出现的错误,可以让其在发生错误时报告错误信息,代码如下

@/**
* @brief  This function handles Hard Fault exception.
* @param  None
* @retval None
*/  

void HardFault_Handler(void)
{  
      static uint32_t r_sp ;
        /*判断发生异常时使用MSP还是PSP*/
        if(__get_PSP()!=0x00) //获取SP的值
            r_sp = __get_PSP(); 
        else
            r_sp = __get_MSP(); 
        /*因为经历中断函数入栈之后,堆栈指针会减小0x10,所以平移回来(可能不具有普遍性)*/
        r_sp = r_sp+0x10;
        /*串口发数通知*/
        USART_OUT(USART1,"HardFault");
    char sPoint[2]={0};
        USART_OUT(USART1,"%s","0x");
        /*获取出现异常时程序的地址*/
        for(int i=3;i>=0;i--){
            Hex_To_Str((uint8_t*)(r_sp+i+24),sPoint,2);
            USART_OUT(USART1,"%s",sPoint);
        }
        /*发送回车符*/
        USART_Enter();
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

查看Call Stack + Locals法
在中断HardFault_Handler中的while()处打上断点,让程序执行到此处停止。
这里写图片描述
stm32F4系列出现HardFault_Handler解决方法_第6张图片
在keil中打开Call Stack + Locals,然后在HardFault_Handler上 右键选择:Show Caller Code,就会跳转到进入循环中断之前的函数处。仔细查看这部分函数被调用或者数组内存使用情况。
这里写图片描述
stm32F4系列出现HardFault_Handler解决方法_第7张图片
0x0800BA68即为发生异常前 PC 将要执行的下一条指令地址。


    以上便是本人对HardFault_Handler的总结,欢迎交流指正。

你可能感兴趣的:(stm32)