STM32F103错误异常和错误处理(HardFault)

这两天在程序开发时,遇到了程序卡死的现象,所以,就怀疑是发生了HardFault,从而导致程序进入了HardFault的死循环。

参考的是这篇文章

stm32 HardFault错误调试记录_fault reports_苏提春晓_的博客-CSDN博客

不过这篇文章里有的地方没讲清楚,所以这里先对我自己的实践步骤,做一个总结,并附上额外说明。

keil排查步骤(具体细节后面再解释):

1、打开debug模式

STM32F103错误异常和错误处理(HardFault)_第1张图片
2、打开STM32标准库工程的文件stm32f10x_it.c

STM32F103错误异常和错误处理(HardFault)_第2张图片

找到HardFault的中断处理函数,并打一个断点

STM32F103错误异常和错误处理(HardFault)_第3张图片

3、调试模式下,点击全速运行

STM32F103错误异常和错误处理(HardFault)_第4张图片

运行程序,如果确实发生了HaultFault,就会跳到刚才打的断点处

STM32F103错误异常和错误处理(HardFault)_第5张图片

4、点击Keil里的菜单Peripherals-Core Peripherals-Fault Reports(这一步是可选的)

STM32F103错误异常和错误处理(HardFault)_第6张图片

此时就能看到刚才发生的错误报告

STM32F103错误异常和错误处理(HardFault)_第7张图片

注意,这里打上勾的就是发生的错误类型。

注意:如果关闭该报告弹窗,下一次打开就不是当前的报告了,所以暂时不要关闭,或者截图先保存下来。

因为所有的错误都会上访成HardFault,所以HardFaults的FORCED对钩被打上了,属于强制的。

这里其实真正的错误类型是Bus Faults的IMPRECISERR。

这里可能看不太明白,但是暂且不管。

总之,要明白的是,确实发生了HardFault。

为什么上面说这个步骤是可选的呢?因为其实没有这个报告,看到程序确实进入了HardFault中断,也能做出这一判断。这里的报告只是尽可能告诉我们具体的错误类型。

接下来就是关键,要找到HardFault是从代码的什么地方进入的,因为程序的错误就存在那附近,可能是数组越界,也有可能是内存溢出等等。总之,要先找到错误的地方,然后才好排查。

5、在寄存器那个窗口找到LR寄存器的值

STM32F103错误异常和错误处理(HardFault)_第8张图片

看该寄存器的第三位,可以看到最低位是0x9,也就是二进制的1001,从最低位开始,第三位就是0,根据这一位判断当前使用的堆栈是MSP还是PSP,具体概念先不用管

STM32F103错误异常和错误处理(HardFault)_第9张图片

我的情况是使用的MSP

另外,还可以看到,SP寄存器的值和MSP的值是一致的,也可以说明使用的是MSP

实际上,裸机开发全程使用的只有MSP.

6、还是刚才的寄存器窗口,展开Banked,找到MSP的值

STM32F103错误异常和错误处理(HardFault)_第10张图片

7、打开内存查看窗口

STM32F103错误异常和错误处理(HardFault)_第11张图片

输入刚才的MSP的值,即0x20002518

STM32F103错误异常和错误处理(HardFault)_第12张图片

注意,最好在该窗口右键切换显示方式(我这里已经切换过了)

STM32F103错误异常和错误处理(HardFault)_第13张图片

这样才刚好是显示4个字节,即32位地址,方便查看。

8、接下来要找到发生HardFault之前的程序地址,也就是哪里发生的HardFault

从刚才显示的MSP地址处,往后加6个地址处,就是出错的地方

STM32F103错误异常和错误处理(HardFault)_第14张图片

先不要问为什么要加6,具体可参考上面给的链接,或者《ARM Cortex-M3 Cortex-M4 权威指南》

这样,我们就找到了出错的地址08000E25

双击复制该地址

9、回到汇编窗口,右键点击跳转该地址处

STM32F103错误异常和错误处理(HardFault)_第15张图片

STM32F103错误异常和错误处理(HardFault)_第16张图片

粘贴刚才的地址08000E25,注意,粘贴后不要直接Go To,必须得加上个0x,要不就不对了,也就是0x08000E25

STM32F103错误异常和错误处理(HardFault)_第17张图片

点击Go To,会同时跳到对应的汇编和C语言处 

STM32F103错误异常和错误处理(HardFault)_第18张图片

到此,就可以知道,是这863行代码处进入了HardFault,所以,这行程序是有问题的,或者这行程序的附近是有问题的.

这里涉及到了数组的索引,猜测极有可能是发生了数组的越界问题。

10、具体得排查出错的程序,优化BUG即可。

OK排查步骤结束。

接下来好好研究下STM32的错误异常相关内容。

错误异常和错误处理

内容均参考《ARM Cortex-M3 Cortex-M4 权威指南》以及网络。

此处仅记录重点内容

错误处理函数中,也是可以进行业务操作的。

上面只是介绍了异常处理的流程,还有一些令人不太明白的地方,接下来进行补充。

要想理解上面步骤的所以然,至少需要阅读《ARM Cortex-M3 Cortex-M4 权威指南》第4章寄存器部分、第8章简介部分和第12章的几乎所有内容。

第4章:架构

第8章:深入了解异常处理

第12章:错误异常和错误处理

另外,要会用Keil的调试工具。

首先,认识错误报告

 STM32F103错误异常和错误处理(HardFault)_第19张图片

错误报告中,当发生对应异常后,就会被自动打上勾,表示该异常类型标志位置位了,也就是说,确实发生了对应的异常。

从上到下,分为存储器管理异常、总线异常、使用异常、硬错误、调试错误、还有辅助错误。

对于每一个错误:

Address行SCB->xFAR是错误地址寄存器;

Status行SCB->xFSR是错误状态寄存器,数值就表示这些寄存器的数值,对应了下面的那些选项,打钩的是1,没打钩的就是0;

STM32F103错误异常和错误处理(HardFault)_第20张图片

这些寄存器都可以在上面报告里找到对应的地方。 

其他列出来的都是对应的错误状态标志位。

存储器管理异常

STM32F103错误异常和错误处理(HardFault)_第21张图片

总线错误

STM32F103错误异常和错误处理(HardFault)_第22张图片

STM32F103错误异常和错误处理(HardFault)_第23张图片

上面的定位步骤中,就是发生了不精确的总线错误。

其实,按上面的方式找到的只是不准确的地址。

为了得到准确的错误地址,需要禁止写缓冲。

STM32F103错误异常和错误处理(HardFault)_第24张图片 

如何在C中禁止写缓冲呢?后面再优化说明。

使用错误

STM32F103错误异常和错误处理(HardFault)_第25张图片

HardFault

STM32F103错误异常和错误处理(HardFault)_第26张图片 

本例中,FORCED被打钩,也就是置位了,是由于总线错误引起的。 

剩下的两种错误类型可以不必关注。 

LR的位2表示什么?

STM32F103错误异常和错误处理(HardFault)_第27张图片

 

LR寄存器的当前值是什么意思?

STM32F103错误异常和错误处理(HardFault)_第28张图片

阅读下方红框部分文字

STM32F103错误异常和错误处理(HardFault)_第29张图片

STM32F103错误异常和错误处理(HardFault)_第30张图片 

同时,也能看出为什么要看LR的位2来判断MSP还是PSP

STM32F103错误异常和错误处理(HardFault)_第31张图片 

 

为什么是往后移动6个位置? 

STM32F103错误异常和错误处理(HardFault)_第32张图片

STM32F103错误异常和错误处理(HardFault)_第33张图片 STM32F103错误异常和错误处理(HardFault)_第34张图片
这里要注意ARM使用的是满减栈。 STM32F103错误异常和错误处理(HardFault)_第35张图片

综合可知

异常处理时,可以根据当前MSP的值获取到返回地址。

STM32F103错误异常和错误处理(HardFault)_第36张图片

注意,往后+0x18(24),也就是往后移动24个字节,每个地址4个字节,也就是往后移动6个地址位置,得到的就是PC值,即返回地址。

注意,这里为什么PC就是返回地址?而不是看LR呢?

上面有讲,因为异常处理时,LR用来存放EXC_RETURN,此时的PC原本是要存在LR中的,但是LR被占用了,就只能压栈保存。

STM32F103错误异常和错误处理(HardFault)_第37张图片

那为什么不取压栈的LR?而是看PC。

因为此时PC就是原来的LR,而此时压栈的LR,是调用该函数的后一个指令地址,不是发生异常处的地址。

举例说明:

STM32F103错误异常和错误处理(HardFault)_第38张图片

此时的压栈LR其实是C指令的地址,而我们要的是I处的地址,也就是此时压栈的PC值。

还有个问题,就是此时得到的PC值其实并不准,需要禁止缓冲后再调试一次。

优化调试

你可能感兴趣的:(stm32,嵌入式硬件,单片机)