当前位置: 首页 > news >正文

STM32单片机编程调试常见问题(一) HardFault_Handler故障分析与解决

文章目录

    • 一.概要
    • 二.什么是Hard fault
    • 三.Hard fault 产生的原因分析
    • 四.制作一个Hard fault程序并定位出问题原因
      • 1.查看堆栈指针SP的地址以及内容
      • 2.找到Return address地址
      • 3.查看汇编界面
      • 4.输入Return address地址,查找到问题代码
    • 小结

一.概要

在嵌入式开发中,偶尔会遇到单片机HardFault_Handler死机的异常,单片机无法正常运行,出现Hardfault错误时,问题比较难定位的原因在于此时代码无法像正常运行时一样,所以显得无从下手。通常情况下我们都是通过在某个区间打断点,然后通过单步执行去逐步缩小“包围圈”去找到产生Hard Fault的代码位置,接着再去推敲、猜测问题的原因。对于不是很复杂的程序,这种方法是有效的,但是当用户代码量进一步增大,再用这种单步+断点去逐步缩小包围圈的方式就很难查到问题点,效率也很低。尤其是在有操作系统的应用中,很多代码的跳转是由操作系统调度的,不是严格的顺序执行,所以很难依靠缩小包围圈的方式去有效找到问题产生的点,进一步增加了定位到Hard Fault触发原因的难度。
本文就介绍了出现HardFault_Handler出现的原因,以及发现异常之后,问题的排查分析与解决方法。

在这里插入图片描述

二.什么是Hard fault

对于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 */
}
}

三.Hard fault 产生的原因分析

硬件方面常见原因:
1.电源设计有错误,造成器件供电不稳;
2.电源质量不好,文波,噪声过大;
3.器件接地不良;
4.对于带有Vcap引脚的器件,管脚处理不当;
5.电路中有强干扰源,对器件造成干扰;

软件方面常见原因:
1.使用了空指针;
2.对地址偏移量的计算有误;
3.数组越界导致程序出错;
4.动态内存使用不当,导致访问了已释放的内存地址;
5.通过地址访问了已失效的局部变量;
一般因为硬件造成Hard Fault错误的可能性较低,大多数都是软件原因造成的。所以遇到硬件中断错误,基本就是通过软件来排查。

由于异常发生时,内核将R0~R3、R12、Returnaddress、PSR寄存器依次入栈,其中Return address即为发生异常前PC将要执行的下一条指令地址,所以我们可以通过找到Return address,从而排查出产生异常的指令。

四.制作一个Hard fault程序并定位出问题原因

硬件准备:
STLINK接STM32F103C8T6小系统板,STLINK接电脑USB口。
在这里插入图片描述
程序准备:
在普通的GPIO例程中修改代码

uint32_t Data2=100,Data3;
uint8_t Temp[100],*p;
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();/* USER CODE BEGIN 2 */Data3=Data2*100;memset(Temp,0x55,100);Data=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);Data3=100*(Data2-1);if(Data3>10){Data=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);	}memset(p,0x55,1000);memset(Temp,0x55,100);Data=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

下载完程序调试运行效果:

程序进入HardFault_Handler
在这里插入图片描述

查找问题步骤:

1.查看堆栈指针SP的地址以及内容

堆栈指针SP地址为0x20000468
在这里插入图片描述

查看Memory,地址为0x20000468
在这里插入图片描述

2.找到Return address地址

内核是将R0~R3、R12、Returnaddress、PSR等寄存器依次入栈,所以找到Return address是0x08000FE7
在这里插入图片描述

3.查看汇编界面

在这里插入图片描述

4.输入Return address地址,查找到问题代码

在这里插入图片描述
输入地址0x08000FE7
在这里插入图片描述
找到Return address的指令,以及上一条指令,发现上一条指令指针是空指针,问题找到
在这里插入图片描述

再举一个例子

uint32_t Data2=100,Data3;
uint8_t Databuff[10000];
uint8_t Temp[100];
void Datacpy(void)
{memcpy(Temp,Databuff,10000);}
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();/* USER CODE BEGIN 2 */memset(Temp,0x55,100);Data=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);Data3=100*(Data2-1);if(Data3>10){Data=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);	}Datacpy();Data=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

根据上述方法,查出有问题的函数是void Datacpy(void),Return Address是0x080001A3,发现是操作数组的时候越界了。
在这里插入图片描述

小结

使用调试工具和技术来定位HardFault的根源在单片机开发和调试中是必不可少的,可以帮助我们快速地排查问题所在,提供产品开发效率。

相关文章:

  • c语言200例 64
  • 深入解析:Kubernetes 如何使用 etcd 作为配置中心和注册中心
  • MacOS Sequoia安装geant4.10.07
  • 赵长鹏今日获释,下一步会做什么?币安透露2024年加密货币牛市的投资策略!
  • 教师工作量|基于springBoot的教师工作量管理系统设计与实现(附项目源码+论文+数据库)
  • 【漏洞复现】数字通云平台智慧政务 login 存在登录绕过漏洞
  • C++ 标准模板库(STL)之集合(set)
  • GO Fsnotify学习与使用
  • 前端必知必会-jQuery 遍历 - 后代
  • 音视频生态下Unity3D和虚幻引擎(Unreal Engine)的区别
  • Excel 获取某列不为空的值【INDEX函数 | SMALL函数或 LARGE函数 | ROW函数 | ISBLANK 函数】
  • Three.js动画与交互
  • win10文件共享设置 - 开启局域网文件共享 - “您没有权限访问,请与网络管理员联系请求访问权限”解决方案
  • 抖店电商怎么使用云账户解决资金提现?
  • 公网IP和内网IP比较
  • ----------
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • 【node学习】协程
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • Android系统模拟器绘制实现概述
  • Flex布局到底解决了什么问题
  • java8-模拟hadoop
  • javascript从右向左截取指定位数字符的3种方法
  • leetcode388. Longest Absolute File Path
  • Making An Indicator With Pure CSS
  • Material Design
  • MySQL数据库运维之数据恢复
  • nfs客户端进程变D,延伸linux的lock
  • React as a UI Runtime(五、列表)
  • SpringCloud集成分布式事务LCN (一)
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 今年的LC3大会没了?
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 驱动程序原理
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 通过npm或yarn自动生成vue组件
  • 微服务核心架构梳理
  • 为什么要用IPython/Jupyter?
  • 由插件封装引出的一丢丢思考
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • #APPINVENTOR学习记录
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • $jQuery 重写Alert样式方法
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (二开)Flink 修改源码拓展 SQL 语法
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (一)SpringBoot3---尚硅谷总结
  • (一)面试需要掌握的技巧
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .mysql secret在哪_MySQL如何使用索引
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET CORE使用Redis分布式锁续命(续期)问题