已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也

Cortex-M 处理器 hardfault 定位方法和步骤(基于Keil mdk)

一. 问题的产生

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 */
  }
}
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[i]=1;

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

方法1.查看寄存器

1)查看fault种类

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

upload_downloader_1702949813167_68063077.png

 

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。

  upload_downloader_1702949812992_6410919.png

  upload_downloader_1702949813161_95694832.png

 

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

  upload_downloader_1702949812599_14694682.png

 

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

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

  upload_downloader_1702949812563_65983676.png

 

LR = 0x0800632B

PC = 0x08006300

c.Disassembly中,查找定位代码

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

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

  upload_downloader_1702949813214_3487211.png

 

 

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

  upload_downloader_1702949813111_8805558.png

 

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

  upload_downloader_1702949813001_32017377.png

 

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

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

在stm32中,需先修改启动文件startup_stm32f091xc.s:

HardFault_Handler\
PROC
MOVS r0, #4
MOV r1, LR
TST r0, r1
BEQ stacking_used_MSP
MRS R0, PSP
B get_LR_and_branch
stacking_used_MSP
MRS R0, MSP
get_LR_and_branch
MOV R1, LR
IMPORT hard_fault_handler_c
BL hard_fault_handler_c
ENDP
该段代码会判断当前堆栈使用的是MSP或PSP,然后将堆栈参数传递给hard_fault_handler_c函数,该函数定义如下:

void hard_fault_handler_c(unsigned int * hardfault_args, unsigned lr_value)
{
  unsigned int stacked_r0;
  unsigned int stacked_r1;
  unsigned int stacked_r2;
  unsigned int stacked_r3;
  unsigned int stacked_r12;
  unsigned int stacked_lr;
  unsigned int stacked_pc;
  unsigned int stacked_psr;
  stacked_r0 = ((unsigned long) hardfault_args[0]);
  stacked_r1 = ((unsigned long) hardfault_args[1]);
  stacked_r2 = ((unsigned long) hardfault_args[2]);
  stacked_r3 = ((unsigned long) hardfault_args[3]);
  stacked_r12 = ((unsigned long) hardfault_args[4]);
  stacked_lr = ((unsigned long) hardfault_args[5]);
  stacked_pc = ((unsigned long) hardfault_args[6]);
  stacked_psr = ((unsigned long) hardfault_args[7]);
  while(1)
  {
    printf ("[Hard fault handler]\n");
    printf ("R0 = %x\n", stacked_r0);
    printf ("R1 = %x\n", stacked_r1);
    printf ("R2 = %x\n", stacked_r2);
    printf ("R3 = %x\n", stacked_r3);
    printf ("R12 = %x\n", stacked_r12);
    printf ("Stacked LR = %x\n", stacked_lr);
    printf ("Stacked PC = %x\n", stacked_pc);
    printf ("Stacked PSR = %x\n", stacked_psr);
    printf ("Current LR = %x\n", lr_value);

  for(int i = 10000;i>0;i--)
  for(int j = 1000;j>0;j--);
  }

  //while(1); // endless loop
}

代码跑死后,会将R0~R3、R12、LR、PC信息通过串口打印,之后根据寄存器信息排查问题代码即可~

参考资料:

(1)XXXXXXXXXXXXXXXXXXXXt/geek_liyang/article/details/83510518

(2)XXXXXXXXXXXXXXXXXXXXt/Maple_Leaf_15/article/details/51443310

(3)XXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXml

(4)XXXXXXXXXXXXXXXXXXXXt/Wang_yf_/article/details/75003611

(5)XXXXXXXXXXXXXXXXXXXXXXX/s/blog_XXXXXXXXXXXXXXXXXXXml

(6)XXXXXXXXXXXXXXXXXXXXt/guozhongwei1/article/details/88418760

(7)XXXXXXXXXXXXXXXXXXXXXXXXX/thread/306244

(8)XXXXXXXXXXXXXXXXXXX/appnotes/files/apnt209.pdf

(9)《ARM Cortex-M0权威指南》Joseph Yiu 著,宋岩 译


文号 / 927753

千古风流
名片专栏发私信
学术分 1
总主题 145 帖总回复 1861 楼拥有证书:进士 学者 机友 笔友
注册于 2010-12-30 03:32最后登录 2024-04-23 15:18
主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步

个人简介

暂未填写
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

插入资源
全部
图片
视频
音频
附件
全部
未使用
已使用
正在上传
空空如也~
上传中..{{f.progress}}%
处理中..
上传失败,点击重试
等待中...
{{f.name}}
空空如也~
(视频){{r.oname}}
{{selectedResourcesId.indexOf(r.rid) + 1}}
处理中..
处理失败
插入表情
我的表情
共享表情
Emoji
上传
注意事项
最大尺寸100px,超过会被压缩。为保证效果,建议上传前自行处理。
建议上传自己DIY的表情,严禁上传侵权内容。
点击重试等待上传{{s.progress}}%处理中...已上传,正在处理中
空空如也~
处理中...
处理失败
加载中...
草稿箱
加载中...
此处只插入正文,如果要使用草稿中的其余内容,请点击继续创作。
{{fromNow(d.toc)}}
{{getDraftInfo(d)}}
标题:{{d.t}}
内容:{{d.c}}
继续创作
删除插入插入
插入公式
评论控制
加载中...
文号:{{pid}}
加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}
ID: {{user.uid}}