RT-Thread使用CmBacktrace进行HardFault_Handler 追踪

本文介绍了在使用STM32微控制器时遇到的HardFault问题及其常见原因,如数组越界、内存溢出、堆栈溢出和中断处理错误。提出了一种利用开源库CmBacktrace进行错误定位的简单方法,详细阐述了CmBacktrace库的集成步骤,包括解决编译错误、冲突和初始化设置。通过CmBacktrace库,开发者可以获取详细的错误信息和调用栈,从而更有效地定位和修复HardFault问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家在用STM32的时候有没有遇到过HardFault的问题呢:

======001

下面针对这个问题做个小总结。

现象还原:在debug模式下进行仿真调试,全速运行再停止运行,程序会跑到 HardFault_Handler函数中,产生 HardFault,即硬错。其产生的原因大概有如下几类:

(1)数组越界操作;

​ (2)内存溢出,访问越界;

​ (3)堆栈溢出,程序跑飞;

​ (4)中断处理错误;

针对HardFault问题的定位,网上有几种方法,大概都是围绕着:在debug模式下,查看一些地址,分析寄存器、函数调用栈等,这是很让人头疼的事情。这里分享一种简单的、直观的HardFault错误定位的方法,使用开源库:CmBacktrace 。这个库之前已经有介绍过了,这篇笔记我们来实践一下。

CmBacktrace 源码地址:

https://github.com/armink/CmBacktrace

======002

下面说下使用方法,具体使用方法如下:

一、把cm_backtrace文件夹复制到我们的工程目录下,并添加至keil工程中,并添加头文件、勾选C99模式

此时,编译会产生几个错误:
======005

那是因为有些预处理宏没有找到,打开、修改cmb_cfg.h文件的内容。cmb_cfg.h文件默认内容为:(我是使用RT-Thread  带系统的)

#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_

/* print line, must config by user */
#define cmb_println(...)        rt_kprintf(__VA_ARGS__);rt_kprintf("\r\n") /* e.g., printf(__VA_ARGS__);printf("\r\n") */
/* enable bare metal(no OS) platform */
/* #define CMB_USING_BARE_METAL_PLATFORM */
/* enable OS platform */
#define CMB_USING_OS_PLATFORM 
/* OS platform type, must config when CMB_USING_OS_PLATFORM is enable */
#define CMB_OS_PLATFORM_TYPE           CMB_OS_PLATFORM_RTT //or CMB_OS_PLATFORM_UCOSII or CMB_OS_PLATFORM_UCOSIII or CMB_OS_PLATFORM_FREERTOS */
/* cpu platform type, must config by user */
#define CMB_CPU_PLATFORM_TYPE        CMB_CPU_ARM_CORTEX_M4  /* CMB_CPU_ARM_CORTEX_M0 or CMB_CPU_ARM_CORTEX_M3 or CMB_CPU_ARM_CORTEX_M4 or CMB_CPU_ARM_CORTEX_M7 */
/* enable dump stack information */
/* #define CMB_USING_DUMP_STACK_INFO */
/* language of print information */
#define CMB_PRINT_LANGUAGE            CMB_PRINT_LANUUAGE_ENGLISH // CMB_PRINT_LANGUAGE_ENGLISH(default) or CMB_PRINT_LANGUAGE_CHINESE */
#endif /* _CMB_CFG_H_ */

二、在使用了本库提供的 cmb_fault.s 汇编文件时,因为该汇编文件内部已经定义了 HardFault_Handler ,所以如果项目中还有其他地方定义了该函数,则会提示 HardFault_Handler 被重复定义的错误。此时有两种解决方法:

  • 1、注释/删除其他文件中定义的 HardFault_Handler 函数,仅保留 cmb_fault.s 中的;(我用的是stm32f302,注释代码如下)将context_rvds.S中最后的HardFault_Handler 注释掉,注意保留最后的END
  • ; compatible with old version
    rt_hw_interrupt_thread_switch PROC
        EXPORT rt_hw_interrupt_thread_switch
        BX      lr
        ENDP
    
        IMPORT rt_hw_hard_fault_exception
        ;EXPORT HardFault_Handler
    ;HardFault_Handler    PROC
    
        ;; get current context
        ;MRS     r0, psp                 ; get fault thread stack pointer
        ;PUSH    {lr}
        ;BL      rt_hw_hard_fault_exception
        ;POP     {lr}
    
        ;ORR     lr, lr, #0x04
        ;BX      lr
        ;ENDP
    
        ;ALIGN   4
    
        END

  • 2、将 cmb_fault.s 移除工程,手动添加 cm_backtrace_fault 函数至现有的故障处理函数,但需要注意的是,务必 保证该函数数入参的准备性 ,否则可能会导致故障诊断功能及堆栈打印功能无法正常运行。所以如果是新手,不推荐第二种解决方法

三、可能会有提示uint32_t重复定义的错误,这个问题可能不太好找。原因是cmb_def.h中引用了arm中的stdint.h和工程里的stdint.h中同时定义了uint32_t,解决方法为:将#include <stdint.h>改为#include "stdint.h"即可,具体可以研究下include<>与include“”的区别。

四、将一下两行代码 放在void init_thread_entry(void* parameter)中进行初始化。   

  cm_backtrace_init("CmBacktrace", HARDWARE_VERSION, SOFTWARE_VERSION);

 /* set exception hook */
  rt_hw_exception_install(exception_hook);    ‘

同时需要hook函数,如下:

#define HARDWARE_VERSION               "V1.0.0"
#define SOFTWARE_VERSION               "V0.1.0"

static rt_err_t exception_hook(void *context) {
    extern long list_thread(void);
    uint8_t _continue = 1;

    rt_enter_critical();

#ifdef RT_USING_FINSH
    list_thread();
#endif

    cm_backtrace_fault(*((uint32_t *)(cmb_get_sp() + sizeof(uint32_t) * 8)), cmb_get_sp() + sizeof(uint32_t) * 9);

    while (_continue == 1);

    return RT_EOK;
}

至此工程已经配置完毕并且可以编译通过了​​​,我们可以人为制造一个HardFault,比如使用一个没有初始化的信号量。这时候运行程序会有如下的串口打印出来:

Firmware name: CmBacktrace, hardware version: V1.0.0, software version: V0.1.0
Fault on thread uart1_rx€
=================== Registers information ====================
  R0 : 200027ac  R1 : 20001a2c  R2 : 00000000  R3 : 00008000
  R12: 00000000  LR : 0800438f  PC : 080056f8  PSR: 41000000
==============================================================
Bus fault is caused by imprecise data access violation
Show more call stack info by run: addr2line -e CmBacktrace.axf -a -f 080056f8 0800438e 0800456a 08000a0e 

五、

可以看到,列出的信息很详细,包括出错原因。按照它的提示,我们运行命令:

addr2line -e CmBacktrace.axf -a -f 080056f8 0800438e 0800456a 08000a0e

运行这个命令需要用到addr2line.exe工具,这个工具在CmBacktrace源码目录下的tools文件夹中:

======013

======014

在这个文件中进入到cmd窗口,方法:按下Shift键的同时点击鼠标右键:
======015

运行上面那条命令:
======016

但是win10按住shift右键打开控制台运行这个会有问题,因为我们可以写一个bat脚本来运行:

addr2line.exe -e STM32F302_B0.axf -a -f 080056f8 0800438e 0800456a 08000a0e 

pause

<think>嗯,用户想了解在RT1052上使用CMBacktrace的方法。首先,我需要回忆一下CMBacktrace的基本功能,它是一个用于嵌入式系统的错误追踪库,特别是在ARM Cortex-M系列MCU上。RT1052属于NXP的i.MX RT系列,基于Cortex-M7内核,应该支持CMBacktrace。 接下来,我得确认用户的使用环境。RT1052通常使用Keil MDK或者IAR这样的IDE,但根据引用[4]提到使用MDK-ARM作为开发环境,可能用户也在用这个。另外,RT-Thread操作系统被提及,所以可能用户的项目是基于RT-Thread的,需要结合该操作系统进行配置。 首先,安装CMBacktrace库。根据引用[2],CMBacktrace需要集成到项目中,可能通过软件包管理器添加,比如在RT-Thread的env工具中使用pkgs --update命令。然后配置硬件相关参数,比如CPU架构、编译器类型、内存映射表。内存映射表很重要,因为CMBacktrace需要知道内存区域来解析调用栈,用户需要根据实际工程修改sct文件或链接脚本。 然后,初始化部分,在main函数或系统初始化阶段调用cm_backtrace_init,传入项目名称、硬件版本、软件版本。这些信息会在崩溃时输出,帮助识别问题。接下来,需要覆盖fault处理函数,比如HardFault_Handler,调用库提供的cm_backtrace_fault函数。这里需要注意,原工程可能已经有HardFault处理,需要替换或整合。 测试阶段,需要故意触发一个错误,比如空指针访问或除零,观察串口输出是否符合预期。如果输出调用栈和寄存器信息,说明配置成功。另外,根据引用[2],建议结合调试工具如J-Link和CMBacktrace,提高定位效率,所以用户可能需要配置调试器与串口配合使用。 可能的问题点:内存映射表是否正确,编译器是否支持,堆栈大小是否足够。用户如果遇到无法解析函数名,可能需要检查是否启用了调试信息,或者map文件是否正确生成。另外,RT-Thread的线程栈可能也需要调整,确保足够空间记录回溯信息。 最后,示例代码部分需要提供初始化、错误触发和结果解析的步骤,帮助用户实际验证。同时,提醒用户定期更新CMBacktrace库,并详细记录错误信息,如引用[2]所述的最佳实践。还要注意RT-Thread的文档规范,如引用[3]提到的代码规范和BSP制作教程,确保配置符合项目要求。</think>### RT1052中使用CMBacktrace的详细步骤 #### 1. **环境准备** - **硬件**:RT1052开发板(需支持串口调试输出) - **开发环境**:Keil MDK-ARM(版本5.30+)或IAR Embedded Workbench(需与CMBacktrace兼容) - **依赖库**:RT-Thread操作系统(已集成CMBacktrace包)或手动移植CMBacktrace库[^2] #### 2. **集成CMBacktrace库** - **通过RT-Thread软件包管理器**: 1. 在RT-Thread env工具中执行 `pkgs --update` 更新软件包列表 2. 输入 `menuconfig` 进入配置界面 3. 选择 `Components → Utilities → Enable CmBacktrace`,保存配置后执行 `scons --target=mdk5` 重新生成工程[^3] - **手动移植**: 1. 下载CMBacktrace源码(GitHub仓库) 2. 将 `cm_backtrace` 文件夹添加到工程目录 3. 在工程设置中添加头文件路径和源文件 #### 3. **硬件适配配置** 修改 `cmb_cfg.h` 文件以匹配RT1052参数: ```c #define CM_CPU_ARM_CORTEX_M7 // 定义CPU架构 #define CM_USE_PLATFORM_DUMP_FLAGS // 启用平台相关诊断 #define CM_BACKTRACE_DUMP_STACK // 启用堆栈转储 ``` #### 4. **内存映射表配置** 在链接脚本(如`RT1052.sct`)中定义内存区域,并在代码中声明: ```c const CMB_MEM_REGION g_mem_region[] = { {0x80000000, 0x81000000, CMB_MEM_REGION_RW}, // OCRAM {0x60000000, 0x61000000, CMB_MEM_REGION_RW}, // SDRAM {0x00000000, 0x00200000, CMB_MEM_REGION_RO} // Flash }; ``` #### 5. **初始化与错误处理** 在 `main.c` 中增加初始化代码: ```c void hardware_init(void) { cm_backtrace_init("RT1052_Demo", "V1.0", "FW1.2"); // 其他硬件初始化... } // 重写HardFault处理函数 void HardFault_Handler(void) { cm_backtrace_fault("HardFault", __get_PSP(), __get_MSP()); while(1); } ``` #### 6. **触发测试与结果解析** **测试用例**: ```c void trigger_fault(void) { volatile int *null_ptr = NULL; *null_ptr = 0xDEAD; // 触发空指针写操作 } ``` **典型输出解析**: ``` [CMB] Firmware: RT1052_Demo_HW_V1.0_SW_FW1.2 [CMB] ===== Stack information ===== #0 trigger_fault (param=0x0) at main.c:128 #1 0x60001234 in rt_thread_entry (parameter=0x6000abcd) at thread.c:88 #2 0x08005678 in $Sub$$main () PC:0800ABCD LR:0800BEEF PSR:61000000 ``` #### 7. **高级调试技巧** - **调用栈深度优化**:通过修改 `CMB_CALL_STACK_MAX_DEPTH` 调整最大回溯深度(建议8-16) - **符号表解析**:使用 `addr2line` 工具定位代码位置: ```bash arm-none-eabi-addr2line -e rtthread.elf 0800abcd ``` - **实时诊断**:结合J-Link RTT Viewer实现崩溃信息实时传输 #### 8. **典型问题排查** | 现象 | 可能原因 | 解决方案 | |-----------------------|-------------------------|----------------------------| | 无错误输出 | 串口未初始化 | 检查串口驱动初始化顺序 | | 函数名显示为?? | 未启用调试符号 | 编译时添加`-g`选项 | | 堆栈信息不完整 | 堆栈被破坏 | 增大线程栈空间 | | 重复进入HardFault | 未清除错误状态寄存器 | 在Handler中添加SCB->CFSR清除操作 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值