[技术问答] Cortex-M 处理器 hardfault 定位方法和步骤

[复制链接]
6940|56
 楼主| pentruman 发表于 2024-8-12 10:16 | 显示全部楼层 |阅读模式

一. 问题的产生

  Hard fault (硬错误,也有译为硬件错误的)是在STM32上编写程序中所产生的错误,造成Hard Fault错误的可能原因较多,排除硬件问题,如何在代码量较大的情况下,快速定位造成的hardfault的问题代码,就成为比较关键的问题。

  本文将基于STM32处理器(stm32f091),keil-MDK开发环境,总结hardfault的调试定位方法。在其他Cortex-M0 (m3,m4)内核处理器,和其他开发环境下,也可作为参考。

二. Cortex-M 处理器内核异常中断简介

1)错误种类

对于Cortex-M内核,架构采用错误异常的机制来检测问题,当核心检测到一个错误时,异常中断会被触发,并且核心会跳转到相应的异常终端处理函数执行,错误异常的终端分为以下四种:

HardFault
MemManage
BusFault
UsageFault

其中hardfault为最常见的错误类型,并且,在没有开启其他异常处理的情况下,默认进入hardfault异常中断处理函数:

void HardFault_Handler(void){  /* USER CODE BEGIN HardFault_IRQn 0 */  /* USER CODE END HardFault_IRQn 0 */  while (1)  {    /* USER CODE BEGIN W1_HardFault_IRQn 0 */    /* USER CODE END W1_HardFault_IRQn 0 */  }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2) 可能的原因

从软件角度,产生hardfault的可能原因有:

(1) 数组越界
(2)野指针
(3)未初始化硬件却开始操作,或无中断服务函数等

(4)任务堆栈溢出

《ARM Cortex-M0权威指南》中提到,关于 Cortex M0+内核主要有以下几点引起 HardFault 的原因:


非法存储器访问
非对齐数据访问
从总线返回错误
异常处理中的栈被破坏
程序在某些 C 函数中崩溃
意外地试图切换至 ARM 状态
在错误的优先级上执行系统服务调用指令(SVC)

下面将以一个stm32f091上运行的数组越界代码为例,具体阐述定位步骤:

产生越界的代码段:

void StackFlow(void){     int a[3],i;     for(i=0; i<10000; i++)    {     a=1;     }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

三. 人工查找hardfault 方法和步骤

方法1.查看寄存器

1)查看fault种类

通过菜单栏Peripherals >Core Peripherals >Fault Reports打开fault reports

2)调试定位步骤

查看fault种类有时可能对解决问题并没有直接帮助,关键是如何定位在进入异常中断前执行的代码段,步骤如下:

a. 确定当前使用堆栈是MSP还是PSP

异常发生后会把进入异常前的 R0-R3,R12, LR, PC,PSR 寄存器值栈入 Main Stack 或Process Stack(取决于异常发生时使用的哪个栈)。 进入异常后链接寄存器 LR 中存放异常返回值 EXC_RETURN, 如果其 bit 2=0 那么用的就是 Main Stack,如果 bit 2=1,那么用的就是 Process Stack。

由下图可以看出,当前使用的堆栈为PSP

b.找到异常发生代码地址

在memory中,定位到堆栈地址:0x200020E0,依据:R0~R3、R12、LR、PC、XPRS 顺序,找到LR的值,即第6个寄存器值

LR = 0x0800632B

PC = 0x08006300

c.Disassembly中,查找定位代码

在反汇编窗口中点击右键,选中show disassembly at address 。

输入LR地址:为发生异常后调用的下一条指令的地址,可看到发生异常的为StackFlow()函数

输入PC地址:可以定位到发生异常的调用语句

方法2:调试步骤
在仿真状态下,调出Call Stack Window,可直接跳转到调用代码

四. 程序查找hardfault 方法和步骤

  实际环境中,由于测试时产品常常无法连接调试器,故需要代码来定位目标语句地址,并通过一定手段保存


geraldbetty 发表于 2024-8-12 20:40 | 显示全部楼层
可以检查Call Stack来确定错误发生的具体位置。
robincotton 发表于 2024-8-12 22:18 | 显示全部楼层

未初始化硬件即开始操作              
班杰明 发表于 2024-8-12 22:44 来自手机 | 显示全部楼层
初始化的问题吗
claretttt 发表于 2024-8-13 02:04 | 显示全部楼层
检查中断服务函数是否正确处理和退出。
mollylawrence 发表于 2024-8-13 05:43 | 显示全部楼层
在发生异常时会被设置。通过分析这些寄存器的状态,可以判断出是哪个具体的Fault导致了Hard Fault。
louliana 发表于 2024-8-13 13:30 | 显示全部楼层
访问无效内存地址
任务堆栈溢出              
i1mcu 发表于 2024-8-13 18:25 | 显示全部楼层
时钟、复位和电源管理电路是否正常工作。
micoccd 发表于 2024-8-14 15:17 | 显示全部楼层
单片机开发最怕遇到hardfault
macpherson 发表于 2024-8-14 21:49 | 显示全部楼层
使用断点来暂停程序执行,然后检查寄存器和堆栈信息。
tifmill 发表于 2024-8-15 12:08 | 显示全部楼层
在调试器中查看异常发生时的 Cortex-M 处理器 状态,包括寄存器值、堆栈内容和程序计数器(PC)。
mmbs 发表于 2024-8-15 15:50 | 显示全部楼层
需要确保硬件没有问题。这包括检查电源、时钟、复位信号等是否正常工作。
pl202 发表于 2024-8-17 11:50 | 显示全部楼层
使用未初始化的指针              
plsbackup 发表于 2024-8-17 14:11 | 显示全部楼层
无中断服务函数等              
albertaabbot 发表于 2024-8-17 16:21 | 显示全部楼层
确保所有数组访问都在其定义范围内。
deliahouse887 发表于 2024-8-17 18:24 | 显示全部楼层
可以在Keil的Watch窗口中查看这些全局变量的值
tabmone 发表于 2024-8-17 22:35 | 显示全部楼层
可以编写代码来保存栈信息到全局变量中。
macpherson 发表于 2024-8-18 09:39 | 显示全部楼层
需要确保硬件没有问题。这包括检查电源、时钟、复位信号等是否正常工作。
jonas222 发表于 2024-8-18 12:50 | 显示全部楼层
简化代码,逐步添加功能,以确定问题的根源
 楼主| pentruman 发表于 2024-8-18 16:40 | 显示全部楼层
检查是否存在内存泄漏、数组越界访问等问题。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

28

主题

1541

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部