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

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

文章目录

  • 一、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.kler.cn/a/595314.html

相关文章:

  • FPGA中串行执行方式之状态机
  • 蓝桥杯 之 数论
  • Spring Boot 集成 Quartz 实现定时任务(Cron 表达式示例)
  • Windows10配置OpenJDK11
  • 基于深度学习的目标追踪技术全解析
  • 验证码背后:前端安全问题的深度剖析
  • 前端网络请求
  • 【强化学习】Reward Model(奖励模型)详细介绍
  • 智能工厂能耗分析:Python驱动的高效能源管理
  • 「0基础学爬虫」爬虫基础之抓包工具的使用
  • SQLite 查询数据库属性
  • AI视频是否会影响原创价值
  • 人工智能:企业RAG方案
  • 浅谈跨平台框架的演变(H5混合开发->RN->Flutter)
  • 【C++11】左值引用、右值引用、移动语义和完美转发
  • 编程语言选择分析:C#、Rust、Go 与 TypeScript 编译器优化
  • 【华为Pura先锋盛典】华为Pura X“阔折叠”手机发布:首次全面搭载HarmonyOS 5
  • 城市更新浪潮下的破局之道:中建海龙模块化集成建筑技术的新应用
  • 2020年全国职业院校技能大赛改革试点赛高职组“云计算”竞赛赛卷第三场次题目:公有云部署与运维
  • centos 9 编译安装 rtpengine