STM32 HardFault_Handler错误定位方法

本文分析了程序崩溃的原因可能是非法内存访问,并提供了增加栈大小、重构硬故障处理程序以追踪运行时错误的方法。同时建议检查堆基址定义,避免与数据段或栈重叠。

Your program most likely crashes because of an illegal memory access, which is almost always an indirect (subsequent) result of a legal memory access, but one that you did not intend to perform.

For example (which is also my guess as to what's happening on your system):

Your heap most likely begins right after the stack. Now, suppose you have a stack-overflow in main. Then one of the operations that you perform in main, which is naturally a legal operation as far as you're concerned, overrides the beginning of the heap with some "junk" data.

As a subsequent result, the next time that you attempt to allocate memory from the heap, the pointer to the next available chunk of memory is no longer valid, eventually leading to a memory access violation.

So to begin with, I strongly recommend that you increase the stack size from 0x200 bytes to 0x400 bytes. This is typically defined within the linker-command file, or through the IDE, in the project's linker settings.

If your project is on IAR, then you can change it in the icf file:

define symbol __ICFEDIT_size_cstack__ = 0x400

Other than that, I suggest that you add code in your HardFault_Handler, in order to reconstruct the call-stack and register values prior to the crash. This might allow you to trace the runtime error and find out exactly where it happened.

In file 'startup_stm32f03xx.s', make sure that you have the following piece of code:

EXTERN  HardFault_Handler_C        ; this declaration is probably missing

__tx_vectors                       ; this declaration is probably there
    DCD     HardFault_Handler

Then, in the same file, add the following interrupt handler (where all other handlers are located):

    PUBWEAK HardFault_Handler
    SECTION .text:CODE:REORDER(1)
HardFault_Handler
    TST LR, #4
    ITE EQ
    MRSEQ R0, MSP
    MRSNE R0, PSP
    B HardFault_Handler_C

Then, in file 'stm32f03xx.c', add the following ISR:

void HardFault_Handler_C(unsigned int* hardfault_args)
{
    printf("R0    = 0x%.8X\r\n",hardfault_args[0]);         
    printf("R1    = 0x%.8X\r\n",hardfault_args[1]);         
    printf("R2    = 0x%.8X\r\n",hardfault_args[2]);         
    printf("R3    = 0x%.8X\r\n",hardfault_args[3]);         
    printf("R12   = 0x%.8X\r\n",hardfault_args[4]);         
    printf("LR    = 0x%.8X\r\n",hardfault_args[5]);         
    printf("PC    = 0x%.8X\r\n",hardfault_args[6]);         
    printf("PSR   = 0x%.8X\r\n",hardfault_args[7]);         
    printf("BFAR  = 0x%.8X\r\n",*(unsigned int*)0xE000ED38);
    printf("CFSR  = 0x%.8X\r\n",*(unsigned int*)0xE000ED28);
    printf("HFSR  = 0x%.8X\r\n",*(unsigned int*)0xE000ED2C);
    printf("DFSR  = 0x%.8X\r\n",*(unsigned int*)0xE000ED30);
    printf("AFSR  = 0x%.8X\r\n",*(unsigned int*)0xE000ED3C);
    printf("SHCSR = 0x%.8X\r\n",SCB->SHCSR);                
    while (1);
}

If you can't use printf at the point in the execution when this specific Hard-Fault interrupt occurs, then save all the above data in a global buffer instead, so you can view it after reaching the while (1).

Then, refer to the 'Cortex-M Fault Exceptions and Registers' section athttp://www.keil.com/appnotes/files/apnt209.pdf in order to understand the problem, or publish the output here if you want further assistance.

UPDATE:

In addition to all of the above, make sure that the base address of the heap is defined correctly. It is possibly hard-coded within the project settings (typically right after the data-section and the stack). But it can also be determined during runtime, at the initialization phase of your program. In general, you need to check the base addresses of the data-section and the stack of your program (in the map file created after building the project), and make sure that the heap does not overlap either one of them.

I once had a case where the base address of the heap was set to a constant address, which was fine to begin with. But then I gradually increased the size of the data-section, by adding global variables to the program. The stack was located right after the data-section, and it "moved forward" as the data-section grew larger, so there were no problems with either one of them. But eventually, the heap was allocated "on top of" part of the stack. So at some point, heap-operations began to override variables on the stack, and stack-operations began to override the contents of the heap.

### STM32 HardFault_Handler 问题分析与调试解决方案 在嵌入式系统中,STM32 微控制器的 HardFault 是一种异常情况,通常表示系统中发生了严重的错误。HardFault 可能由多种原因触发,包括但不限于访问非法地址、堆栈溢出或未定义指令执行等[^1]。 #### HardFault 的常见触发原因 HardFault 的触发可能来源于以下几种情况: - **非法内存访问**:尝试访问不存在的地址或受保护的内存区域。 - **堆栈溢出**:当函数调用层次过深或局部变量过大时,可能导致堆栈空间耗尽。 - **未定义指令**:执行了不被支持的指令。 - **总线错误**:例如外设配置错误或数据传输失败。 为了更精确地定位问题,可以使用 HardFault_Handler 中断服务程序捕获具体的故障信息。 #### HardFault_Handler 的实现与调试方法 以下是 HardFault_Handler 的一个典型实现方,通过读取故障状态寄存器(CFSR)、硬件故障地址寄存器(HFSR)和故障地址寄存器(MMFAR/BFAR),可以获取更多关于故障的信息: ```c void HardFault_Handler(void) { // 获取故障状态寄存器值 uint32_t CFSR = SCB->CFSR; uint32_t HFSR = SCB->HFSR; uint32_t MMFAR = SCB->MMFAR; uint32_t BFAR = SCB->BFAR; // 打印相关信息以帮助调试 if (CFSR & 0x80) { // 表示存在使用故障 printf("Usage Fault: %lx\n", CFSR); } if (CFSR & 0x100) { // 表示存在总线故障 printf("Bus Fault: %lx\n", CFSR); } if (CFSR & 0x200) { // 表示存在存储器管理故障 printf("Memory Management Fault: %lx\n", CFSR); } // 检查是否为强制硬故障 if (HFSR & 0x40000000) { printf("Forced HardFault: %lx\n", HFSR); } // 如果存在有效的故障地址,则打印出来 if (CFSR & 0x80) { printf("Faulting Address: %lx\n", MMFAR); } else if (CFSR & 0x100) { printf("Faulting Address: %lx\n", BFAR); } while (1); // 停留在这里以便调试 } ``` 上述代码通过检查 SCB 寄存器中的不同字段,可以帮助开发者快速定位问题的源。此外,结合调试工具(如 JTAG 或 SWD 接口)可以进一步观察程序运行时的状态。 #### 调试技巧 在实际调试过程中,可以采取以下措施来缩小问题范围: - **检查堆栈指针(SP)**:确保堆栈指针指向合法的内存区域。 - **启用调试断点**:在 HardFault_Handler 中设置断点,逐步分析程序崩溃前的状态。 - **审查代码逻辑**:特别是涉及动态内存分配、中断处理或外设操作的部分。 - **优化堆栈大小**:如果怀疑是堆栈溢出导致的问题,可以适当增加任务堆栈大小。 #### 总结 通过在 HardFault_Handler 中添加详细的日志记录,并结合调试工具,可以有效定位 STM32 系统中 HardFault 的具体原因。同时,对代码进行静态分析和动态测试也是预防此类问题的重要手段[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值