优秀的编程知识分享平台

网站首页 > 技术文章 正文

新手必学!通过寄存器来查找STM32运行遇到HardFault

nanyue 2025-05-23 19:00:47 技术文章 2 ℃

一、定位HardFault的核心方法

  1. 启用调试器捕获异常上下文
  2. 在Keil中启动调试模式,当程序触发HardFault时,调试器会自动暂停。通过查看Call Stack窗口寄存器窗口(如LR、PC、SP),可获取异常发生时的程序状态。
  3. 关键寄存器分析
  4. SCB->CFSR(可配置故障状态寄存器):判断故障类型(如内存访问错误、未对齐访问、指令执行错误)。
  5. SCB->HFSR(硬件故障状态寄存器):确认是否为HardFault的直接触发原因。
  6. SCB->MMFAR/SCB->BFAR:记录导致内存管理或总线故障的具体地址。

  7. 堆栈回溯分析
  8. 通过SP寄存器获取异常时的堆栈指针,手动解析堆栈中保存的返回地址(PC值)。在反汇编文件(.map或.lst)中查找对应的代码位置。
  9. 示例步骤:
  10. 在调试器中查看SP的值,找到堆栈中保存的PC(通常位于SP+24偏移处)。
  11. 使用fromelf工具生成反汇编文件(.axf转.lst),根据PC值定位出错的汇编指令及对应的C代码行。

  12. 自定义HardFault处理函数
  13. 在代码中重写HardFault_Handler函数,捕获异常信息并输出(如通过串口)。需在启动文件(如startup_stm32fxxx.s)中确保异常向量指向此函数。
  14. 示例代码片段:
void HardFault_Handler(void) {
    volatile uint32_t *sp = (uint32_t*)__get_MSP(); // 获取堆栈指针
    uint32_t pc = sp[6]; // 提取PC值
    printf("HardFault at 0x%08X\n", pc);
    while(1);
}

4.示例讲解

(1)HardFault_Handler出现的情况一般有两种:

一种是:数组越界.

一种是:堆栈溢出,程序指针指飞。

(2)制造一个HardFault故障;

void Test_UART(void)
{
  uint8_t *pData;
  *pData = 10;
  printf("%d,\r\n", *pData);
}

(3)将上面函数Test_UART放在程序里面运行,将会出现以下情况:

(4)值得特别注意是,你要先判断将MSP(R14(LR) = 0xFFFFFFE9)或PSP(R14(LR) = 0xFFFFFFFD)的值作为SP值来使用。在Registers里面找到R14(LR)的值,我的这里是:0xFFFFFFFD

说明一下:

0xFFFFFFF9对应的是要看MSP寄存器

0xFFFFFFFD对应的是要看PSP寄存器

所以这里需要查找的内存地址是MSP的值:0x20005EA8

(5)在memory里面查找MSP的值:0x20005EA8,然后在对应的行里面找到地址,地址一般格式都是:0x080085EF这样的。

(6)在Disassembly里面右键选择Show Code at Address,把找到的地址输进去进行搜索,然后就会找到相对应的代码,这里的代码就是在进入循环中断之前的时候的情况,仔细查看这部分函数被调用或者数组内存使用情况。

不过你也可以简单这样操作:

在中断HardFault_Handler中的while()处打上断点,让程序执行到此处停止。

在keil中打开Call Stack + Locals,然后在HardFault_Handler上 右键选择:Show Caller Code,就会跳转到进入循环中断之前的函数处。仔细查看这部分函数被调用或者数组内存使用情况。

关注我,获取更多技术干货

Tags:

最近发表
标签列表