前言
在使用 ARM Cortex-M 系列 MCU时候,有时候会遇到各种异常(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault),这时候我们根据经验查询PC指针,LR寄存器,堆栈数据定位地址然后再通过反汇编确定异常位置,但往往会花很多时间,那么有没有一种工具可以很快定位出错位置呢?这里推荐使用 CmBacktrace
开发前期准备
- 硬件平台 rt1176开发板
- IDE: GNU(vscode)
- CmBacktrace + segger rtt
1.Segger RTT 获取
J-Link RTT – Real Time Transfer (segger.com)
segger rtt 可以简单理解 Jlink的组件吧,我们不需要多余外设(如串口等),只需要调试口,将需要的数据打印到窗口。组件获取很简单,如果安装了Jlink驱动,直接到安装路径会找到组件压缩文件,将文件解压到工程就行了。
2.CmBacktrace 获取
CmBacktrace: ARM Cortex-M 系列 MCU 错误追踪库 (gitee.com)
GitHub - armink/CmBacktrace: Advanced fault backtrace library for ARM Cortex-M series MCU | ARM Cortex-M 系列 MCU 错误追踪库
3.移植 CmBacktrace 和 Segger RTT
- cmake中添加相关文件和路径
- 修改 cmb_cfg.h
用户需要自行手动配置,常用配置如下:
配置名称 | 功能 | 备注 |
---|---|---|
cmb_println(…) | 错误及诊断信息输出 | 必须配置 |
CMB_USING_BARE_METAL_PLATFORM | 是否使用在裸机平台 | 使用则定义该宏 |
CMB_USING_OS_PLATFORM | 是否使用在操作系统平台 | 操作系统与裸机必须二选一 |
CMB_OS_PLATFORM_TYPE | 操作系统平台 | RTT/UCOSII/UCOSIII/FREERTOS |
CMB_CPU_PLATFORM_TYPE | CPU平台 | M0/M3/M4/M7 |
CMB_USING_DUMP_STACK_INFO | 是否使用 Dump 堆栈的功能 | 使用则定义该宏 |
CMB_PRINT_LANGUAGE | 输出信息时的语言 | CHINESE/ENGLISH |
#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_#include "SEGGER_RTT.h"/* print line, must config by user */
#define cmb_println(...) SEGGER_RTT_printf(0, __VA_ARGS__);SEGGER_RTT_WriteString(0, "\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_FREERTOS // CMB_OS_PLATFORM_RTT or CMB_OS_PLATFORM_UCOSII or CMB_OS_PLATFORM_UCOSIII or CMB_OS_PLATFORM_FREERTOS or CMB_OS_PLATFORM_RTX5 */
/* cpu platform type, must config by user */
#define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M7 /* 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_LANGUAGE_ENGLISH /* CMB_PRINT_LANGUAGE_ENGLISH(default) or CMB_PRINT_LANGUAGE_CHINESE */
#endif /* _CMB_CFG_H_ */
- 修改 cmb_def.h ,根据链接脚本添加栈顶地址,栈截至地址和代码段起始地址,截止地址
- 修改 cm_backtrace.c 使用C99或gnu99
笔者工程默认用的gnu99标准,所以屏蔽了如下信息(如果是C99可保留)
- 修改 HardFault_Handler MemManage_Handler 中断函数(笔者没添加cm_fault.S)
void HardFault_Handler(void)
{__asm volatile(" mov r0, lr \n"" mov r1, sp \n"" bl cm_backtrace_fault \n");while(1);
}void MemManage_Handler(void)
{__asm volatile(" mov r0, lr \n"" mov r1, sp \n"" bl cm_backtrace_fault \n");while(1);
}
- FreeRtos修改内容(没有使用不用关心)
task.c 末尾添加
/*-----------------------------------------------------------*/
/*< Support For CmBacktrace >*/
uint32_t * vTaskStackAddr()
{return pxCurrentTCB->pxStack;
}uint32_t vTaskStackSize()
{#if ( portSTACK_GROWTH > 0 )return (pxNewTCB->pxEndOfStack - pxNewTCB->pxStack + 1);#else /* ( portSTACK_GROWTH > 0 )*/return pxCurrentTCB->uxSizeOfStack;#endif /* ( portSTACK_GROWTH > 0 )*/
}char * vTaskName()
{return pxCurrentTCB->pcTaskName;
}
/*-----------------------------------------------------------*/
在typedef struct tskTaskControlBlock添加
#if( portSTACK_GROWTH <= 0)UBaseType_t uxSizeOfStack; /*< Support For CmBacktrace >*/
#endif
static void prvInitialiseNewTask添加
pxNewTCB->uxSizeOfStack = ulStackDepth; /*< Support For CmBacktrace >*/
4.写一个能进异常中断的代码
uint32_t *ptr32=0xFFFFFFF1;
uint32_t tmp;//笔者放到一个线程中
static void xAPP_KeyScanTask(void* pvParameters)
{while(1){/*按键扫描*/xSYS_LEDKEY_KeyScan();
#if CONFIG_EN_CMBACKTRACEtmp = *ptr32; //这句话放到任一地方,将会引起异常
#endif vTaskDelay(10);}
}
5.添加 CmBacktrace 和 Segger RTT 初始化代码
SEGGER_RTT_Init();cm_backtrace_init("rt1176_rtos_app", "B1.0.0", "B1.0.0");SEGGER_RTT_printf(0, "Enable Cmbacktrace\r\n");
6.运行代码,查询异常信息
打开 J-Link RTT Viewer将监听到异常打印信息,甚至知道错误代码在函数:xAPP_KeyScanTask 中,除此之外我们可以通过终端控制台,输入如下命令(addr2line.exe 为 CmBacktrace 提供工具):
.\addr2line.exe -e .\rt1176_rtos_app.elf -a -f 00001fc0 00003c26