深入解析:STM32跑飞,进入HardFault_Handler如何精准的确定问题

news/2026/1/24 16:06:07/文章来源:https://www.cnblogs.com/ljbguanli/p/19526770

深入解析:STM32跑飞,进入HardFault_Handler如何精准的确定问题

STM32卡死、跑飞、进入HardFault_Handler如何精准的确定问题

引言

我们在使用STM32的时候,代码难免会出现疏忽,导致程序跑飞,不再正常运行,那么都是什么情况会导致STM32程序跑飞呢?或者我们调试的时候,发现代码进入了HardFault_Handler();导致了死循环,我们该如何去找到问题呢?

目录

Part 1 程序跑飞原因

1.1 软件原因

1.1.1 堆栈溢出

原因:

  • 递归函数未正确退出导致栈空间耗尽
  • 任务栈过小或局部变量占用过大
  • 中断嵌套过深,占用过多的栈空间
    解决方法:
  • 在启动文件和链接脚本中为栈分配足够空间
  • 使用调试器监控 MSP/PSP,确保未越界
  • 避免递归或深度嵌套的函数调用
  • 在 RTOS 中增大任务栈并启用溢出检测
1.1.2 指针操作错误

原因:

1.1.3 中断优先级配置错误

原因:

  • 中断优先级冲突,未遵循 Cortex‑M 规则
  • 高优先级中断执行时间过长
    解决方法:
  • 合理分配优先级,数值越小优先级越高
  • 在中断中保持简短、避免复杂逻辑
1.1.4 外设错误配置

原因:

1.1.5 存储器管理问题

原因:

  • 动态内存分配失败返回 NULL
  • 超出 SRAM 限制访问非法地址
  • 堆与栈区域重叠
    解决方法:
  • 避免频繁动态分配,优先静态分配
  • 校验链接脚本,确保堆栈不重叠
  • 在失败路径中处理 malloc 返回值
1.1.6 代码逻辑错误

原因:

1.1.7 时钟配置错误

原因:

  • 系统时钟配置不当导致外设或 CPU 异常
  • 时钟频率超出芯片允许范围
    解决方法:
  • 借助 STM32CubeMX 或数据手册验证时钟树
  • 校验 HCLK、PCLK1/PCLK2 等频率范围
1.1.8 Watchdog 超时

原因:

1.1.9 RTOS 栈与优先级问题

原因:

  • configMINIMAL_STACK_SIZE 或任务栈过小
  • configMAX_SYSCALL_INTERRUPT_PRIORITY 设置不当,ISR 误用 API
    解决方法:
  • 使用 uxTaskGetStackHighWaterMark 监控栈余量
  • ISR 仅调用 FromISR 系列 API,遵守优先级限制
1.1.10 FPU 与栈对齐

原因:

1.1.11 DMA 与缓存一致性(M7)

原因:

  • DCache 导致缓冲区与外设数据不一致
  • DMA 缓冲区未对齐或放在不可达内存
    解决方法:
  • 对 DMA 缓冲区执行 Cache 清除/失效操作
  • 使用非缓存或专用 SRAM/DTCM 作为缓冲区
1.1.12 编译优化与未定义行为

原因:

1.1.13 MPU 缺失

原因:

  • 未启用 MPU,野指针难以及早被捕获
    解决方法:
  • 配置基本 MPU 区域,禁止空地址访问并保护关键内存

1.2 硬件原因

1.3 构建与工具问题

  • 链接脚本段落布局错误导致堆栈重叠或越界
  • 启动文件向量表或 _estack__StackTop 定义不正确
  • LTO/GC‑sections 误删必需符号或段
  • newlib/printf 等库使用不当造成堆内存膨胀
    建议:
  • 仔细审查 .map 文件与段大小,核对中断向量与栈顶
  • 在关键模块禁用过度优化或保留必要符号

Part 2 精准定位 HardFault

2.1 捕获栈帧与寄存器

使用异常入口判断当前使用的栈指针,提取被压栈的寄存器与故障寄存器:

void HardFault_Handler(void) {
__asm volatile (
"TST lr, #4\n"
"ITE EQ\n"
"MRSEQ r0, MSP\n"
"MRSNE r0, PSP\n"
"B hardfault_c\n"
);
}
void hardfault_c(uint32_t *sp) {
volatile uint32_t r0  = sp[0];
volatile uint32_t r1  = sp[1];
volatile uint32_t r2  = sp[2];
volatile uint32_t r3  = sp[3];
volatile uint32_t r12 = sp[4];
volatile uint32_t lr  = sp[5];
volatile uint32_t pc  = sp[6];
volatile uint32_t psr = sp[7];
volatile uint32_t cfsr = SCB->CFSR;
volatile uint32_t hfsr = SCB->HFSR;
volatile uint32_t mmfar = SCB->MMFAR;
volatile uint32_t bfar = SCB->BFAR;
for(;;);
}
  • 入口用极少量汇编判断当前使用的栈指针( MSP 或 PSP ),把故障发生时的“自动压栈”栈帧指针传给 C 函数 hardfault_c 。
  • C 函数从栈帧中取出当时的寄存器值( r0–r3, r12, lr, pc, xPSR ),再读取 SCB 的故障状态寄存器( CFSR, HFSR, MMFAR, BFAR ),用于精准定位异常原因与触发点。
  • 末尾的死循环使系统停住,避免继续执行造成更大破坏;调试时可以在这些 volatile 变量上断点/观察。

2.2 解码故障寄存器

  • CFSR 包含三部分:MMFSR(0–7)、BFSR(8–15)、UFSR(16–31)
  • 常见位:
    • IACCVIOL/DACCVIOL 指令/数据访问违规
    • PRECISERR/IMPRECISERR 精确/非精确总线错误
    • STKERR/UNSTKERR 入栈/出栈错误
    • UNDEFINSTR/INVSTATE/INVPC 指令或状态非法
    • NOCP 协处理器不可用(FPU)
    • UNALIGNED/DIVBYZERO 未对齐访问/除零
  • HFSRFORCED 位表示硬故障被强制触发
  • 对于 PRECISERR 可读取 BFAR 获取精确故障地址

2.3 将 PC 映射到源码

使用编译生成的 ELF 将 PC 地址映射到具体源文件与行号:

arm-none-eabi-addr2line -e build/firmware.elf 0x08001234

结合 map 文件与符号信息定位具体函数与语句。

2.4 常见触发与修复

  • 空指针解引用:在关键指针使用前进行校验与断言
  • 非法总线访问:检查外设基址、存储器窗口与等待周期
  • 未对齐访问:保证数据结构与访问对齐,避免跨界拷贝
  • 除零:在除法前做保护或启用除零陷阱用于调试
  • 栈破坏导致异常返回失败:检查函数调用深度与栈对齐

2.5 快速排查清单

2.6 FreeRTOS 中断优先级要点

  • 采用一致的优先级分组并理解数值与实际优先级的关系
  • ISR 调用仅限 FromISR API,且优先级不高于阈值
  • SysTick/PendSV 维持合理优先级以保证任务切换

用keil5调试HardFault_Handler并进行仿真

我们插好我们的仿真器,常见的STM32仿真器有很多种,有STLINK或者DAP等等,我这里使用的是STLINK_V2这款仿真器,只能说便宜又好用了。插好仿真器,点击魔术棒->Debug->Setting,如果SW Device下面显示了芯片的ID,说明我们成功用仿真器连接到芯片。

Registers工具

在这里插入图片描述

我们可以在这里可以查看CPU寄存器的值。

  1. 通用寄存器(R0R12)R0R7:这些是低组寄存器,所有指令都可以访问。它们的大小为32位,复位后的初始值不定。R8~R12:这些是高组寄存器,只有部分的16位Thumb指令可以访问,而32位Thumb-2指令则不受限制。它们的大小同样为32位,复位后的初始值也不定。
  2. 特殊功能寄存器堆栈指针(SP):也称为R13,在Cortex-M4内核中,有两个堆栈指针——主堆栈指针(MSP)和进程堆栈指针(PSP)。MSP用于异常服务例程和需要特权访问的应用程序代码,而PSP则用于常规的应用代码。 连接寄存器(LR):即R14,主要作用是保存子程序的返回地址,以便在执行完子程序时恢复现场。 程序计数器(PC):即R15,用于指示当前执行的指令地址。在Cortex-M系列中,由于采用指令流水线技术,读取PC会返回当前指令地址+4(以兼容Thumb代码)。
  3. 程序状态寄存器(xPSR)xPSR是程序状态寄存器,它又被分为三个子状态寄存器:应用程序状态寄存器(APSR) 中断状态寄存器(IPSR)PRIMASK:只有1个位,置1时关闭所有可屏蔽的异常。 FAULTMASK:只有1个位,置1时只有NMI(非屏蔽中断)可以响应。 BASEPRI:8位寄存器,定义了被屏蔽优先级的阈值。
  4. 控制寄存器(Control)控制寄存器用于控制FPU(浮点单元)的激活、堆栈指针的选择以及线程模式的特权级等。

Memory工具

在这里插入图片描述

Memory Window是Keil调试环境中的一个窗口,它提供了对程序内存的直接访问。
通过这个窗口,你可以查看内存中的字节、字、双字等数据,并可以实时修改这些数据以测试程序的行为。
这对于调试和验证程序中的内存访问、堆栈使用、变量存储等问题非常有帮助。

Disassembly工具

在这里插入图片描述

在Disassembly窗口中,你可以看到程序的反汇编代码。
这些代码是程序在CPU上实际执行的指令的文本表示。
你可以滚动窗口来查看不同的部分,或者使用窗口中的搜索功能来定位特定的代码或地址。

Call Stack工具

在这里插入图片描述

Call Stack(调用堆栈)界面是一个关键的调试工具,它允许开发者查看程序执行过程中函数调用的顺序和当前的位置。
Call Stack窗口将显示当前函数调用的堆栈。这包括每个函数的调用顺序、每个函数的名称(如果可用)以及调用该函数的地址。
你可以看到程序是如何从main函数开始,逐步调用其他函数,直到达到当前执行点的。

找到程序跑飞位置

为什么会产生HardFault_Handler?

  1. 由调试事件触发;
  2. 由总线错误,存储器管理错误或使用错误而产生 这个错误的产生是由于HardFault寄存器状态发生改变所导致
    这两个原因看上去说的很玄乎,实际上这个问题大家应该都不陌生,最常出现这个问题的原因一般是数据越界,堆栈溢出,等等。

出现HardFault_Handler怎么办

遇到之后没有必要手忙脚乱,这个问题其实并不难解决,因为HardFault_Handler的存在意义并不是为了让你的程序卡死,而是为了帮助你解决程序的问题,可以按照以下步骤进行:

  1. 找到Registers界面;
  2. 然后查看LR寄存器的值,该寄存器只有六种值是正常情况,具体可参考M4权威指南193页。这里给出一个表格:
    在这里插入图片描述

在这里插入图片描述

通过 LR 精准定位 HardFault 的步骤
读取 HardFault 时的 LR 值
在调试器(如 Keil、GDB)中停在HardFault_Handler的断点处,查看 LR 寄存器的EXC_RETURN值。
判断错误发生的场景
若 LR 是0xFFFFFFF9/E9:说明错误出现在普通程序代码(非中断)中;
若 LR 是0xFFFFFFF1/E1:说明错误是在中断 / 异常的处理过程中被打断(即发生了中断嵌套)。
确定对应的栈指针(MSP/PSP)
根据EXC_RETURN的低 4 位(Bit2)判断:
Bit2=0 → 使用 MSP(主栈);
Bit2=1 → 使用 PSP(进程栈,通常 RTOS 任务用)。
读取栈帧,获取出错指令地址
异常发生时,硬件会自动将错误发生前的上下文(R0-R3、R12、LR、PC、xPSR)压入对应栈中。找到栈指针(MSP/PSP)指向的地址,从栈中读取PC值 ——这个 PC 就是触发 HardFault 的指令地址,结合反汇编即可定位到具体代码行。

关于MSP和PSP核心区别
  1. 栈类型:MSP(主栈) vs PSP(进程栈)—— 定位时「查哪个栈」的关键
    0xFFFFFFF9/E9(MSP 主栈):
    错误发生在「使用主栈的线程模式」,典型场景:
    ✅ 裸机程序(无 RTOS):整个程序默认用 MSP,HardFault 大概率是主函数、中断外的普通代码(如数组越界、空指针)触发;
    ✅ RTOS 系统:仅「主线程 / 空闲任务」或「未切换到任务栈的初始化阶段」用 MSP,HardFault 指向系统初始化、主线程代码。
    定位实操:读取 MSP 寄存器 指向的栈帧,解析其中的 PC 值(触发错误的指令地址)。
    0xFFFFFFFD/ED(PSP 进程栈):
    错误发生在「使用进程栈的线程模式」,典型场景:
    ✅ 仅存在于 RTOS 系统(裸机几乎不会出现):每个任务有独立的 PSP 栈,HardFault 必然是某一个任务的代码触发(如任务内访问非法地址、任务栈溢出);
    ✅ 0xFFFFFFED 额外特征:错误发生在「非特权级任务」(如用户态任务访问特权级外设 / 寄存器)。
    定位实操:读取 PSP 寄存器 指向的栈帧,结合 RTOS 的任务栈信息,定位到具体崩溃的任务和代码行。
  2. 特权级:仅 0xFFFFFFED 是非特权级 —— 缩小错误原因范围
    0xFFFFFFF9/E9/FD 都是「特权级」:错误原因多是代码逻辑(如空指针、数组越界)、硬件访问(如非法外设地址);
    0xFFFFFFED 是「非特权级」:大概率是「非特权代码试图访问特权资源」(如任务直接操作内核寄存器、写受保护的内存),而非单纯的代码逻辑问题。
查看寄存器->找到

根据LR的寄存器的值判断是主栈还是线程栈导致的问题,如果是主栈就继续查看MSP寄存器,如果是进程栈,那么就查看PSP寄存器。
在这里插入图片描述

根据MSP寄存器或者PSP寄存器的记录,将其值在Memory中查看其具体地址,一般是0x80开头的。
在这里插入图片描述

打开Disassembly,在里面可以找到具体是哪一行代码导致的该问题。
在这里插入图片描述

在这里插入图片描述

就可以成功找到我们跑飞前代码地方了。

所遇到的问题

这里的问题很简单,同时也是我故意设置的一个堆栈溢出的问题,这里我带大家分析一下:首先,我们是走到一条叫menuPoint[selectItem+scrollBar].func1()来去执行我们想执行的函数,我们通过Watch窗口查看一下:
在这里插入图片描述

函数的地址应该在flash里对面,这里很明显跑到ram里面了,就跑飞了
从 Watch 窗口能看到,func1的地址是0x20000200—— 这明显是 RAM 区域的地址,而不是 Flash 里的函数地址(正常函数地址应该是0x08xxxxxx开头)。
当程序执行menuPoint[selectItem+scrollBar].func1()时,会跳转到0x20000200这个地址去执行,但 RAM 里的内容是数据 / 随机值,不是合法的 CPU 指令。此时 CPU 执行 “非法指令”,就会触发 HardFault_Handler,也就是说的 “跑飞”。
里面的func1是应该指向一个叫amplitudeSub的函数指针的,这里指向不正确,于是我就非常怀疑这个结构体赋值给这个成员有一个环节出了问题,于是我就一步步的单步指向,知道我执行到了一个地方:
在这里插入图片描述

在这之前,结构体里面的值都是我理想的值,当我执行完下一条指令的时候,错误出现了。
在这里插入图片描述

这里的func1的值居然给我离奇的改变了,因为他的改变,导致我后续程序的跑飞。我们仔细看看执行的那条代码,发现是数组溢出了。

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

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

相关文章

《NMN怎么选?2026年NMN品牌吸收率与效果解析和对比选购指南》

随着抗衰老研究不断深入,NMN(烟酰胺单核苷)已经从“小众科研名词”,走向大众视野。越来越多研究证实,NMN通过提升体内NAD⁺水平,参与细胞能量代谢、DNA修复、线粒体功能维持,被认为是当前最具潜力的抗衰老补充方…

2026年全球NMN十大品牌最新排名:奥本元凭借十倍性价比成年度黑马

抗衰老一直是人类永恒关注的话题 。如果你的身体开始出现皮肤暗沉、皱纹增加、记忆力减退、反应迟缓,亦或是白发增多、体力不支,这往往是身体正在发出“断崖式衰老”的深度警告 。 透过这些生理表象深挖底层逻辑,医…

RS485发完数据后总是丢最后一个字节

为什么你的 RS485 总是在发完数据后丢最后一个字节?RS485 不是一种新的协议,它只是 UART 的物理层 (Physical Layer) 皮肤。 数据包还是 Start 8 Data Stop,但电压标准全变了。1. 物理层革命:差分信号 (Differential Signaling)…

PLC系统设计(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

PLC系统设计(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码 用PLC控制整个控制装置要达到以下要求: 1)按下控制装置启动按钮后,传送带1和传送带2运转,传送包装物品到传送 带2. 2)传送带2上有3个物品后&…

自动装瓶机控制系统设计(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

自动装瓶机控制系统设计(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码 源 2.3.5I/0电路 2.4软件的组成 2.4.1PLC系统软件与工作过程 2.4.2应用软件 2.4.3编程语言及编程支持工具软件 2.5PLC控制系统抗干扰措施 2.5.1.PLC控制…

基于PLC的灌装饮料控制系统设计【程序与文档】(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

基于PLC的灌装饮料控制系统设计【程序与文档】(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码控制要求如下图所示,西门子1200博途V15(博途版本V15及以上都可以打开) 商品包括梯形图程序、触摸屏仿真、完整报告文档…

基于PLC的灌装饮料控制系统设计控制【程序与文档】(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

基于西门子plc博图1200药片自动装瓶机控制系统设计【程序与文档】(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码 基于西门子plc博图1200药片自动装瓶机控制系统设计 1.仿真报告(1.5W字) 2.10表 3.接线图

【救命稻草】RAG开发者的春天来了!UltraRAG框架上线,解决4大落地痛点,代码量减少80%!

做AI开发、企业数智化的人都懂这种痛—— 想搭建RAG系统落地到业务,却卡在“数据构建乱、检索不准、微调复杂”:整理领域数据要手动标注,耗时耗力;检索模型调参全靠试错,准确率忽高忽低;微调模型要写大量代…

【扎心真相】RAG分块策略大反转:语义分块竟是“智商税“?简单粗暴方法吊打高级算法!

在构建 RAG 系统时,开发者面临的第一道关卡往往是:如何切分文档(Chunking)? 传统的做法是“一刀切”——每 200 个 Token 切一块,简单粗暴。但最近一年,**语义分块(Semantic Chunki…

饮料灌装流水线控制画面【程序与文档】(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

饮料灌装流水线控制画面【程序与文档】(设计源文件万字报告讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码 西门子PLC程序设计饮料罐装控制要求如下图所示,西门子1200博途V15(博途版本V15及以上都可以打开) 包括梯形图程序、触摸屏仿真…

CSDN热榜:大模型开发“超级实习生“入职指南,Prompt到RAG一篇文章全拿下!小白也能秒懂的AI架构图解

AI概念层出不穷 最让人头疼的是这些概念看起来好像都差不多,完全分不清有什么区别,使用的时候应该如何根据具体情况有侧重地优化具体部分,今天一篇文章带你吃透! 在进入技术细节前,最有效的理解方式是把大语言模型&a…

震惊!这个9600星开源项目,让大模型成为你的私人学术导师,论文阅读从此不是噩梦!

一、学术阅读的困境:论文读不完,导师约不上 如果你是一名研究生,以下场景可能并不陌生: 凌晨两点,你盯着一篇充满数学公式的论文,第三遍读完摘要还是不知道作者到底想干什么。你打开微信想问导师&#xf…

讲讲附近美容美发培训学校,山东欧曼谛性价比究竟咋样?

随着美业市场对专业美发人才的需求持续攀升,越来越多热爱美发的人开始寻找附近的美容美发培训学校,希望通过系统学习实现职业转型或技能提升。但面对市场上琳琅满目的机构,如何找到专业靠谱的选择,成为许多人的首要…

白灼虾与白灼牛肉

一、前言 1. 白灼--粤菜常见烹饪技巧最重要的技巧:持续沸腾的热水宽水,很多很多很多,以保证温度稳定 二、白灼虾 1. 万能海鲜汁一平勺是10g1.1 食材明细序号 食材 克数 备注1 花生油 15g2 干葱头 20g 提味3 蒜末 10…

诚信的传媒艺考培训学校费用揭秘,艺升艺考收费合理吗?

本榜单依托全维度市场调研与真实行业口碑,深度筛选出五家标杆企业,为艺考生及家长选型提供客观依据,助力精准匹配适配的艺考培训伙伴。 TOP1 推荐:艺升艺考 推荐指数:★★★★★ | 口碑评分:浙江地区诚信度与服务…

2026年轴承钢供应商推荐,上海、宁波哪些厂家口碑好?

本榜单依托全维度市场调研与真实行业口碑,深度筛选出五家标杆企业,为制造企业选型提供客观依据,助力精准匹配适配的轴承钢服务伙伴。 TOP1 推荐:上海津豹金属(集团)有限公司 推荐指数:★★★★★ | 口碑评分:华…

解析宁波有实力的室内设计培训机构,天十星众教育实力出圈

在数字化浪潮与产业升级的双重驱动下,设计技能已成为就业市场的硬通货,无论是想入行的新手、寻求转行的职场人,还是渴望提升技能的从业者,都需要通过专业培训夯实竞争力。面对市场上鱼龙混杂的设计培训机构,如何找…

SMC学习笔记

SMC自修改代码(Self-Modified Code)是一类特殊的代码技术,即在运行时修改自身代码,从而使得程序实际行为与反汇编结果不符,同时修改前的代码段数据也可能非合法指令,从而无法被反汇编器识别,这加大了软件逆向工…

AI编程革命来了!字节Coze 2.0真香警告,从“聊天工具“到“数字伙伴“,开发门槛直降90%,小白也能逆袭大厂offer!

2026年1月19日,字节跳动AI Agent平台Coze(扣子)发布2.0版本,核心定位从“对话式AI工具”转向“可长期运行的智能工作伙伴”,以Agent上位重构人机协作范式。以下是核心要点: 一、核心升级:四大能…

爆肝实测!阿里CloudEdgeAgent端云协同大模型框架,小白5分钟上手,让手机秒变智能助手!yyds!

开源圈彻底炸了!阿里达摩院重磅发布的 CloudEdgeAgent(端云协同智能体框架),上线仅 48 小时 GitHub Star 直接飙到 1.2 万,登顶全球 AI 开源榜 TOP1! 你还在抱怨手机 AI 智商低,只会做简单语音…