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,进一步得知是数组溢出导致。
相关推荐
石板小湫28 分钟前
17【Protues单片机仿真】基于51单片机的太阳能智能谷物翻晒机器人
单片机·机器人·51单片机
夜间去看海1 小时前
基于51单片机的汽车倒车防撞报警器系统
嵌入式硬件·汽车·51单片机
Projectsauron1 小时前
STM32 通过 SPI 驱动 W25Q128
stm32·单片机·嵌入式
会讲英语的码农1 小时前
MCS-51汇编
汇编·嵌入式硬件·51单片机
极客小张2 小时前
从图像处理到字符识别:基于STM32与C语言的车牌识别系统实现
c语言·图像处理·人工智能·stm32·物联网·毕业设计·课程设计
步达硬件2 小时前
【FPGA】编程方式
笔记·单片机·嵌入式硬件·fpga开发
Tlog嵌入式2 小时前
蓝桥杯【物联网】零基础到国奖之路:十. OLED
stm32·单片机·物联网·蓝桥杯
FLYAOTO2 小时前
供应RM500UCNAB-D10-SNADA模块
单片机·5g
jmlinux3 小时前
电学基础概念详解及三相电公式汇总
stm32·单片机·嵌入式硬件
libai3 小时前
STM32 USB HOST CDC 驱动CH340
java·前端·stm32