如何在MCU工程中启用HardFault硬错误中断

news2025/3/22 18:20:19

文章目录

  • 一、HardFault出现场景
  • 二、启动HardFault
  • 三、C代码示例


一、HardFault出现场景

HardFault(硬故障) 错误中断是 ARM Cortex-M 系列微控制器中一个较为严重的错误中断,一旦触发,表明系统遇到了无法由其他异常处理机制解决的问题。
HardFault 错误中断常见于以下几种触发情况:

  • 内存访问错误

    1. 访问非法地址
      当代码尝试访问未映射的内存区域,例如访问一个超出芯片内存范围的地址,就会触发 HardFault。比如,使用了一个未初始化的指针,该指针指向一个随机的无效地址,当对这个地址进行读写操作时,就会产生错误。访问受保护的内存区域也会引发问题。一些内存区域可能被设置为只读或者受特殊权限保护,若代码试图对其进行写操作,就会触发 HardFault。
    2. 内存对齐错误
      ARM Cortex - M 处理器对某些数据类型有内存对齐要求。例如,32 位的数据访问需要在 4 字节对齐的地址上进行。如果代码试图在非对齐的地址上进行 32 位数据的读写操作,就可能触发 HardFault。
  • 总线错误

    1. 总线传输错误
      在数据通过总线进行传输时,可能会因为电气干扰、信号衰减、硬件故障等原因,导致数据传输错误。比如,在外部存储器读写操作中,由于总线线路故障,数据无法正确传输,就会触发 HardFault。
    2. 总线仲裁失败
      当多个主设备同时请求使用总线时,需要进行总线仲裁。如果仲裁机制出现问题,导致某个主设备无法正常获得总线使用权,就可能引发总线错误,进而触发 HardFault。
  • 堆栈溢出或损坏

    1. 堆栈溢出
      堆栈用于保存函数调用时的局部变量、返回地址等信息。如果函数调用层次过深或者局部变量占用空间过大,就可能导致堆栈溢出。当堆栈溢出发生时,新的数据会覆盖其他重要的内存区域,从而引发 HardFault。
    2. 堆栈损坏
      代码中对堆栈指针的错误操作,例如意外修改了堆栈指针的值,会导致堆栈结构被破坏。后续的函数调用和返回操作就会出现异常,最终触发 HardFault。
  • 异常处理错误

    1. 异常向量表错误
      异常向量表存储了各个异常处理函数的入口地址。如果异常向量表被错误修改,或者其地址设置不正确,当发生异常时,处理器无法正确跳转到相应的异常处理函数,就可能触发 HardFault。
    2. 异常嵌套错误
      在异常处理过程中,如果发生了新的异常,并且异常嵌套处理机制出现问题,例如嵌套层数超过了处理器的限制,就会导致系统混乱,触发 HardFault。
  • 指令执行错误

    1. 未定义指令
      当处理器执行到一条未定义的指令时,会触发 HardFault。这可能是由于代码中包含了无效的机器码,或者是在程序加载过程中出现了错误。
    2. 非法指令访问
      某些指令可能需要特定的权限才能执行。如果代码在不具备相应权限的情况下尝试执行这些指令,就会触发 HardFault。
  • 硬件故障

    1. 时钟故障
      处理器的正常运行依赖于稳定的时钟信号。如果时钟源出现故障,例如晶振停振、时钟频率偏移过大,会导致处理器的指令执行和数据处理出现异常,从而触发 HardFault。
    2. 电源故障
      电源电压不稳定、过压或欠压等情况,可能会影响处理器内部电路的正常工作,导致数据处理错误,最终引发 HardFault。

二、启动HardFault

1. 打开启动文件(.s文件)
在这里插入图片描述
在启动文件中,定义中断向量表,为 HardFault 提供占位符处理程序

__Vectors       DCD     __initial_sp                        ; 栈顶指针(Top of Stack)
                DCD     HardFault_Handler                   ; HardFault 处理程序

__initial_sp 是栈顶指针的初始化值,在复位时由处理器自动加载到 SP 寄存器。
HardFault_HandlerHardFault 的中断服务程序,在 CPU 触发 HardFault 时会执行这个函数。

2. 占位符的弱定义

HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler                   [WEAK]
                B       .
                ENDP

EXPORT HardFault_Handler [WEAK] 将 HardFault_Handler 导出,并标记为弱定义(WEAK)。
B . 代表无限循环,相当于死循环,防止处理器进入未知状态。

Tips: 由于该 HardFault_Handler只是一个占位符,用户可以在 C 代码中重新定义 HardFault_Handler,而不会与启动文件冲突。

3. 定义源文件 HardFault 处理程序
在 C 代码中,需要实现 HardFault_Handler 以便进行错误分析和调试。

void HardFault_Handler_t(uint8_t  * hardfault_args)
{
	......
	while(1);
}

在HardFault 中断中一般可以去查看相关寄存器的数值用于分析错误原因,常见寄存器参考如下:

R0 ~ R3:通用寄存器
R12:特殊寄存器
LR(Link Register):返回地址
PC(Program Counter):导致 HardFault 的指令地址
PSR(Program Status Register):状态寄存器

Cortex-M 的故障状态寄存器:
BFAR(Bus Fault Address Register):总线错误的地址
CFSR(Configurable Fault Status Register):配置错误状态寄存器
HFSR(Hard Fault Status Register):硬错误状态寄存器
DFSR(Debug Fault Status Register):调试故障状态寄存器
AFSR(Auxiliary Fault Status Register):辅助故障状态寄存器

4. 汇编代码提取堆栈帧

__asm void hard_fault_handler_asm(void)
{
    IMPORT  HardFault_Handler_t
    TST      LR, #4          ; 检查异常发生时使用的堆栈
    ITE      EQ
    MRSEQ   R0, MSP          ; 如果是主堆栈,获取 MSP
    MRSNE   R0, PSP          ; 如果是进程堆栈,获取 PSP
    B        HardFault_Handler_t
}

TST LR, #4:检查 LR(异常返回指针)的 bit 2,判断异常发生时使用的堆栈:

  • 0:使用主堆栈(MSP)
  • 1:使用进程堆栈(PSP)

ITE EQ:条件执行语句

  • MRSEQ R0, MSP:如果使用 MSP,则将 MSP 传入 R0
  • MRSNE R0, PSP:如果使用 PSP,则将 PSP 传入 R0

B HardFault_Handler_t:跳转到 HardFault_Handler_t,并将 R0 作为参数传递。


综上:

  • Cortex-M 发生 HardFault 时,处理器跳转到 HardFault_Handler。
  • HardFault_Handler由 startup.s 文件定义,但只是一个占位符。
  • hard_fault_handler_asm 通过汇编代码获取当前堆栈指针,并传递给 HardFault_Handler_t。
  • HardFault_Handler_t解析故障信息,读取寄存器内容,调试解错。

启动文件 负责 定义中断向量表,并提供一个默认的弱定义 HardFault_Handler,允许用户在 C 代码中覆盖它。
汇编代码 负责 提取故障发生时的堆栈帧 并传递给 C 代码。C 代码 负责 解析寄存器信息并打印故障信息,便于调试和分析故障原因。

三、C代码示例

C代码示例可参考如下:

__asm void hard_fault_handler_asm(void)
{
    IMPORT  HardFault_Handler_t
    TST      LR, #4          ; 检查异常发生时使用的堆栈
    ITE      EQ
    MRSEQ   R0, MSP          ; 如果是主堆栈,获取 MSP
    MRSNE   R0, PSP          ; 如果是进程堆栈,获取 PSP
    B        HardFault_Handler_t
}

typedef struct 
{
    uint8_t  r0;
    uint8_t  r12;
    uint8_t  lr;
    uint8_t  pc;
    uint8_t  psr;
} hardfault_t;
hardfault_t hardfault;

void HardFault_Handler_t(unint8_t  * hardfault_args)
{
    hardfault.stacked_r0 = ((uint32_t) hardfault_args[0]);
    hardfault.stacked_r12 = ((uint32_t) hardfault_args[4]);
    hardfault.stacked_lr  = ((uint32_t) hardfault_args[5]);
    hardfault.stacked_pc  = ((uint32_t) hardfault_args[6]);
    hardfault.stacked_psr = ((uint32_t) hardfault_args[7]);

    printf("HardFault_Handler_t!\r\n");
    printf("R0 = %x\r\n", hardfault.r0);
    printf("R12 = %x\r\n", hardfault.r12);
    printf("LR = %x\r\n", hardfault.lr);
    printf("PC = %x\r\n", hardfault.pc);
    printf("PSR = %x\r\n", hardfault.psr);

    while(1);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2319717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL -- 复合查询

数据库的查询是数据库使用中比较重要的环节,前面的基础查询比较简单,不做介绍,可自行查阅。本文主要介绍复合查询,并结合用例进行讲解。 本文的用例依据Soctt模式的经典测试表,可以自行下载,也可以自己创建…

卷积神经网络 - 卷积层(具体例子)

为了更一步学习卷积神经网络之卷积层,本文我们来通过几个个例子来加深理解。 一、灰度图像和彩色图像的关于特征映射的例子 下面我们通过2个例子来形象说明卷积层中“特征映射”的概念,一个针对灰度图像,一个针对彩色图像。 例子 1&#x…

测试Claude3.7 sonnet画蛋白质

测试Claude3.7 sonnet画蛋白虽然画的很粗糙,但是大致画了出来

java项目之基于ssm的游戏攻略网站(源码+文档)

项目简介 游戏攻略网站实现了以下功能: 管理员主要负责填充图书和其类别信息,并对已填充的数据进行维护,包括修改与删除,管理员也需要审核老师注册信息,发布公告信息,管理自助租房信息等。 💕…

本地基于Ollama部署的DeepSeek详细接口文档说明

前文,我们已经在本地基于Ollama部署好了DeepSeek大模型,并且已经告知过如何查看本地的API。为了避免网络安全问题,我们希望已经在本地调优的模型,能够嵌入到在本地的其他应用程序中,发挥本地DeepSeek的作用。因此需要知…

python NameError报错之导库报错

在日常代码编写中,经常出现如 图1 一样的报错,在代码多时很难找到问题,但翻看代码后就会发现是因为未导库, 图1 报错 代码: time.sleep(0.1) print("time库") 解决方法: 第一步:在代码中添加导库代码 import time #…

Web3网络生态中数据保护合规性分析

Web3网络生态中数据保护合规性分析 在这个信息爆炸的时代,Web3网络生态以其独特的去中心化特性,逐渐成为数据交互和价值转移的新平台。Web3,也被称为去中心化互联网,其核心理念是将数据的控制权归还给用户,实现数据的…

C++ 语法之数组指针

一维数组: 如果我们定义了一个一维数组,那么这个数组名,就是指向第一个数组元素的地址,也即,是整个数组分配的内存空间的首地址。 比如 int a[3]; 定义了一个包含三个元素的数组。因为一个int占4个字节,那…

PLY格式文件如何转换成3DTiles格式——使用GISBox软件实现高效转换

一、概述 在三维GIS和数字孪生领域,3DTiles格式已成为主流的数据格式之一。它由Cesium团队提出,专为大规模3D数据可视化设计,能够高效地加载和展示海量模型数据。而PLY格式则是一种常见的三维模型文件格式,主要用于存储点云数据或…

Java定时任务的三重境界:从单机心跳到分布式协调

《Java定时任务的三重境界:从单机心跳到分布式协调》 本文将以生产级代码标准,揭秘Java定时任务从基础API到分布式调度的6种实现范式,深入剖析ScheduledThreadPoolExecutor与Quartz Scheduler的线程模型差异,并给出各方案的性能压…

【Linux网络】手动部署并测试内网穿透

📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…

java项目之在线购物系统(源码+文档)

项目简介 在线购物系统实现了以下功能: 使用在线购物系统的用户分管理员和用户两个角色的权限子模块。 管理员所能使用的功能主要有:主页、个人中心、用户管理、商品分类管理、商品信息管理、系统管理、订单管理等。 用户可以实现主页、个人中心、我的…

OO_Unit1

第一次作业 UML类图 代码复杂度分析 其中Expr中的toString方法认知复杂度比较高,主要源于多层条件嵌套和分散的字符串处理逻辑,重构时可重点关注这两部分的解耦。 代码量分析 1.”通用形式“ 我觉得我的设计的最大特点就是“通用形式”,具…

重要重要!!fisher矩阵元素有什么含义和原理; Fisher 信息矩阵的形式; 得到fisher矩阵之后怎么使用

fisher矩阵元素有什么含义和原理 目录 fisher矩阵元素有什么含义和原理一、对角线元素( F i , i F_{i,i} Fi,i​)的含义与原理二、非对角线元素( F i , j F_{i,j} Fi,j​)的含义与原理Fisher 信息矩阵的形式矩阵的宽度有位置权重数量决定1. **模型参数结构决定矩阵维度**2.…

[已解决]jupyter notebook报错 500 : Internal Server Error及notebook闪退

jupyter notebook出现如上图的报错,可以在黑色窗口中检查是为什么报错。 我检查发现是nbconvert导致的问题,卸载重装nbconvert。 但是这时候出现,jupyter notebook闪退问题。jupyter的黑色窗口出现一秒钟就没了。 在Anaconda Prompt中检查ju…

2025年渗透测试面试题总结- 某亭-安全研究员(题目+回答)

网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 一、SQL注入过滤单引号绕过方法 二、MySQL报错注入常用函数 三、报错注入绕WAF 四、MySQL写文件函数…

Redis分布式锁如何实现——简单理解版

目录 前言 满足条件 加锁之后产生的问题 避免死锁的方法 Lua脚本实现避免释放其他锁 看门狗判断过期 扩展 Lua脚本 Redission 前言 在如今开发的某些项目中,多个进程必须以互斥的方式独占共享资源,这时用分布式锁是最直接有效的,分布式…

数字化转型驱动卫生用品安全革新

当315晚会上晃动的暗访镜头揭露卫生巾生产车间里漂浮的异物、纸尿裤原料仓中霉变的碎屑时,这一触目惊心的场景无情地撕开了“贴身安全”的遮羞布,暴露的不仅是部分企业的道德缺失,更凸显了当前检测与监管体系的漏洞,为整个行业敲响…

自适应柔顺性策略:扩散引导控制中学习近似的柔顺

24年10月来自斯坦福大学和 TRI 的论文“Adaptive Compliance Policy: Learning Approximate Compliance for Diffusion Guided Control”。 柔顺性在操作中起着至关重要的作用,因为它可以在不确定的情况下平衡位置和力的并发控制。然而,当今的视觉运动策…

SVN简明教程——下载安装使用

SVN教程目录 一、开发中的实际问题二、简介2.1 版本控制2.2 Subversion2.3 Subversion的优良特性2.4 工作原理2.5 SVN基本操作 三、Subversion的安装与配置1. 服务器端程序版本2. 下载源码包3. 下载二进制安装包4. 安装5. 配置版本库① 为什么要配置版本库?② 创建目…