STM32 Cortex-M3 HardFault异常定位

目录

[1 Fault类异常](#1 Fault类异常)

[1.1 总线Fault](#1.1 总线Fault)

[1.2 存储器管理Fault](#1.2 存储器管理Fault)

[1.3 用法Fault](#1.3 用法Fault)

[1.4 硬件Fault](#1.4 硬件Fault)

[2 Core Registers](#2 Core Registers)

[2.1 R14(LR)寄存器](#2.1 R14(LR)寄存器)

[2.2 R15(PC)寄存器](#2.2 R15(PC)寄存器)

[3 HardFault处理原理](#3 HardFault处理原理)

[4 真实项目调试](#4 真实项目调试)


1 Fault类异常

Cortex-M3的Fault异常分为:

  • 总线Fault
  • 存储器管理Fault
  • 用法Fault
  • 硬件Fault

1.1 总线Fault

这里引用《Cortex-M3权威指南》说法:

可以通过地址0xe000ed29查看具体是什么异常。

1.2 存储器管理Fault

这里引用《Cortex-M3权威指南》说法:

可以通过地址0xe000ed28查看具体是什么异常。

1.3 用法Fault

这里引用《Cortex-M3权威指南》说法:

可以通过地址0xe000ed2a查看具体是什么异常。

1.4 硬件Fault

硬件Fault也就是我们所说的HardFault,只要产生了总线、存储器管理、用法Fault其中之一,就会上报给硬件Fault,所以,最终的Falut我们都可以通过HardFault查看。

可以通过地址0xe000ed2c查看具体是什么异常。

2 Core Registers

讲了几种Fault之后,我们需要了解一些基本的内核寄存器,后续查找Fault提供帮助。

2.1 R14(LR)寄存器

R14(LR):连接寄存器,当呼叫一个子程序时,由R14存储返回地址。

当只有一级子程序的时候:把返回地址直接存储在R14寄存器中,这样就无需访问内存(堆栈内存,进栈和出栈),从而提高子程序的调用效率;

当子程序多于一级的时候:前一级存储在R14的返回地址值压到堆栈里,保留最后一级的返回地址在R14里面;

注意:在RAM上编程时,应尽量只使用寄存器保存中间结果,迫不得已时才访问内存。

  1. 如图所示,当程序执行到0x08001224地址的时候,CM3内核会把0x08001228存到LR中(实际存入的是0x08001229,因为是Thumb-2指令),并在进入子程序之后把LR压栈(因为子程序里面存在子程序,如果子程序不存在子程序,就不会压栈);
  2. 当程序执行到0x08001400的时候,由于LR已经压栈,所以LR的值就会重新存储0x08001404,当执行完NVIC_Init程序之后,BL LR(LR的值为0x08001404)就会无条件跳转到0x08001404处继续执行(此时需要把LR出栈,出栈的值是0x08001228);
  3. 最后执行完NVIC_Configuration之后,就会执行BL LR返回到0x08001228处继续执行代码。

2.2 R15(PC)寄存器

R15:程序计数寄存器,指向下一次要执行的程序地址。必须保证PC的数值是奇数,用于表示在Thumb状态下执行。

当中断来临的时候,R0-R3、R12、LR、PC、xPRS寄存器会自动的压栈,如果中断中使用了其它寄存器,在进入中断之前,也会对使用到的寄存器自动的压栈。

3 HardFault处理原理

如果程序进入hardfault,那么首先找到SP指针的值,然后根据SP指针的值,找出对应地址所存放的寄存器(CM0和CM3目前这些地址存放的寄存器相同)。

然后主要是定位LR和PC的值,LR指的是发生异常前本来要执行的下一条指令的地址,但是由于异常的产生,不会执行这条指令(只需要在这个地址之前打一个断点,根据条件来找寻出错的点),PC是发生异常前紧接着执行的地址。

4 真实项目调试

真实项目中,我们大多不必关心Fault状态寄存器的值,因为只要我们定位到错误的代码的位置,我们大致都能知道是什么错误了。

  1. 首先工程跑到hardfault了;
  1. 通过SP指针找到PC的值为0x08000046,LR的值为0x08001DEB,可知程序在执行了0x08000046指令之后就进入了HardFault,此时程序不会执行0x08001DEB这条指令。如果程序没有进入HardFault,那么子函数就会返回0x08001DEB这条指令。
  1. 找到LR和PC指针指向地址的程序位置,可知道PC指针的值是中断向量表,这里又没用开PVD中断,所以断定是这里出错了;

  1. 在LR地址的前一条指令的位置打上断点,确定程序在这里执行完之后,就出错了;
  1. 通过发现是(*pEpInt_IN[EPindex-1])();函数的位置出错,所以把watch窗口打开,查看这个函数的地址和返回值,发现返回值是0x08000046,所以这个值出错了,因为返回值成功是0,失败不可能是0x08000046(从调试信息可知,此函数的地址是0x20000018,所以返回值写入的是0x20000018);
  1. 增加条件断点,当0x20000018地址的值被修改了之后产生断点,捕捉哪里把0x20000018的值修改为了0x08000046,通过断点可知,在下面代码的时候导致了hardfault,进一步得知是数组溢出导致。
相关推荐
智商偏低1 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen3 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森5 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白5 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D5 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术8 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt9 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘9 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang9 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n11 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件