在使用 ARM Cortex-M 系列 MCU时候,有时候会遇到各种异常(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault),这时候我们根据经验查询PC指针,LR寄存器,堆栈数据定位地址然后再通过反汇编确定异常位置,但往往会花很多时间,那么有没有一种工具可以很快定位出错位置呢?这里推荐使用 CmBacktrace
J-Link RTT – Real Time Transfer (segger.com)
segger rtt 可以简单理解 Jlink的组件吧,我们不需要多余外设(如串口等),只需要调试口,将需要的数据打印到窗口。组件获取很简单,如果安装了Jlink驱动,直接到安装路径会找到组件压缩文件,将文件解压到工程就行了。
CmBacktrace: ARM Cortex-M 系列 MCU 错误追踪库 (gitee.com)
GitHub - armink/CmBacktrace: Advanced fault backtrace library for ARM Cortex-M series MCU | ARM Cortex-M 系列 MCU 错误追踪库
用户需要自行手动配置,常用配置如下:
配置名称 | 功能 | 备注 |
---|---|---|
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 ,根据链接脚本添加栈顶地址,栈截至地址和代码段起始地址,截止地址
注意压栈方向,因为我们是满降栈,所以 __StackTop 和 __StackLimit 如下填写
修改 cm_backtrace.c 使用C99或gnu99
笔者工程默认用的gnu99标准,所以屏蔽了如下信息(如果是C99可保留)
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);
}
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 >*/
uint32_t *ptr32=0xFFFFFFF1;
uint32_t tmp;
//笔者放到一个线程中
static void xAPP_KeyScanTask(void* pvParameters)
{
while(1)
{
/*按键扫描*/
xSYS_LEDKEY_KeyScan();
#if CONFIG_EN_CMBACKTRACE
tmp = *ptr32; //这句话放到任一地方,将会引起异常
#endif
vTaskDelay(10);
}
}
SEGGER_RTT_Init();
cm_backtrace_init("rt1176_rtos_app", "B1.0.0", "B1.0.0");
SEGGER_RTT_printf(0, "Enable Cmbacktrace\r\n");
打开 J-Link RTT Viewer将监听到异常打印信息,甚至知道错误代码在函数:xAPP_KeyScanTask 中,除此之外我们可以通过终端控制台,输入如下命令(addr2line.exe 为 CmBacktrace 提供工具):
.\addr2line.exe -e .\rt1176_rtos_app.elf -a -f 00001fc0 00003c26