基于N32L40x CmBacktrace mdk5平台下的移植测试

首先感谢大神提供的开源库 CmBacktrace

开源地址:
https://github.com/armink/CmBacktrace/releases/latest
https://gitee.com/Armink/CmBacktrace

CmBacktrace是什么

  • CmBacktrace一款针对 ARM Cortex-M 系列 MCU 的错误代码自动追踪、定位,错误原因自动分析的开源库

  • CmBacktrace 输出的信息包括函数调用栈、故障诊断结果、堆栈、故障寄存器及产品固件信息,极大的提升了错误定位的效率及准确性

主要特性如下:

  • 支持的错误包括:
    • 断言(assert)
    • 故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault)
  • 故障原因 自动诊断 :可在故障发生时,自动分析出故障的原因,定位发生故障的代码位置,而无需再手动分析繁杂的故障寄存器;
  • 输出错误现场的 函数调用栈(需配合 addr2line 工具进行精确定位),还原发生错误时的现场信息,定位问题代码位置、逻辑更加快捷、精准。也可以在正常状态下使用该库,获取当前的函数调用栈
  • 支持 裸机 及以下操作系统平台:
  • 根据错误现场状态,输出对应的 线程栈 或 C 主栈;
  • 故障诊断信息支持多国语言(目前:简体中文、英文);
  • 适配 Cortex-M0/M3/M4/M7 MCU;
  • 支持 IAR、KEIL、GCC 编译器;

移植说明

2.3.1 准备工作

  • 1、查看 \demos 目录下有没有合适自己的 Demo ,如有类似,则建议在其基础上修改
  • 2、明确操作系统/裸机平台及 CPU 平台
  • 3、将 \src 下的全部源文件添加至产品工程中,并保证源码目录被添加至头文件路径
  • 4、cmb_fault.s 汇编文件(点击查看)可以选择性添加至工程,添加后需要把项目原有的 HardFault_Handler 注释掉
  • 5、把 cm_backtrace_init 函数放在项目初始化地方执行
  • 6、将 cm_backtrace_assert 放在项目的断言函数中执行,具体使用方法参照下面的 API 说明
  • 7、如果第 4 步骤没有将 cmb_fault.s 汇编文件启用,则需要将 cm_backtrace_fault 放到故障处理函数(例如: HardFault_Handler )中执行,具体使用方法参照下面的 API 说明

配置文件

配置文件名: 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_TYPECPU平台M0/M3/M4/M7
CMB_USING_DUMP_STACK_INFO是否使用 Dump 堆栈的功能使用则定义该宏
CMB_PRINT_LANGUAGE输出信息时的语言CHINESE/ENGLISH

2.4 API 说明

2.4.1 库初始化
void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver)
参数描述
firmware_name固件名称,需与编译器生成的固件名称对应
hardware_ver固件对应的硬件版本号
software_ver固件的软件版本号

注意 :以上入参将会在断言或故障时输出,主要起了追溯的作用

2.4.2 获取函数调用栈
size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp)
参数描述
buffer存储函数调用栈的缓冲区
size缓冲区大小
sp待获取的堆栈指针

示例:

/* 建立深度为 16 的函数调用栈缓冲区,深度大小不应该超过 CMB_CALL_STACK_MAX_DEPTH(默认16) */
uint32_t call_stack[16] = {0};
size_t i, depth = 0;
/* 获取当前环境下的函数调用栈,每个元素将会以 32 位地址形式存储, depth 为函数调用栈实际深度 */
depth = cm_backtrace_call_stack(call_stack, sizeof(call_stack), cmb_get_sp());
/* 输出当前函数调用栈信息
 * 注意:查看函数名称及具体行号时,需要使用 addr2line 工具转换
 */
for (i = 0; i < depth; i++) {
    printf("%08x ", call_stack[i]);
}
2.4.3 追踪断言错误信息
void cm_backtrace_assert(uint32_t sp)
参数描述
sp断言环境时的堆栈指针

注意 :入参 SP 尽量在断言函数内部获取,而且尽可能靠近断言函数开始的位置。当在断言函数的子函数中(例如:在 RT-Thread 的断言钩子方法中)使用时,由于函数嵌套会存在寄存器入栈的操作,此时再获取 SP 将发生变化,就需要人为调整(加减固定的偏差值)入参值,所以作为新手 不建议在断言的子函数 中使用该函数。

2.4.4 追踪故障错误信息
void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp)
参数描述
fault_handler_lr故障处理函数环境下的 LR 寄存器值
fault_handler_sp故障处理函数环境下的 SP 寄存器值

该函数可以在故障处理函数(例如: HardFault_Handler)中调用。另外,库本身提供了 HardFault 处理的汇编文件(点击查看,需根据自己编译器进行选择),会在故障时自动调用 cm_backtrace_fault 方法。所以移植时,最简单的方式就是直接使用该汇编文件。

移植

1.复制文件到工程

在这里插入图片描述

2.添加文件

注意这里的cmb_fault.s\middle\cm_backtrace\fault_handler\keil下的文件

在这里插入图片描述


在这里插入图片描述

3.添加头文件路径

…\middle\cm_backtrace

…\middle\cm_backtrace\Languages\en-US

在这里插入图片描述

4.屏蔽原有的HardFault_Handler函数实现

在这里插入图片描述

5.配置文件设置cmb_cfg.h

裸机配置

#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_

/* 打印函数配置 */
#define cmb_println(...)               printf(__VA_ARGS__);printf("\r\n")

/* 平台选择  enable bare metal(no OS) platform
裸机:CMB_USING_BARE_METAL_PLATFORM
操作系统:CMB_USING_OS_PLATFORM
*/
#define CMB_USING_BARE_METAL_PLATFORM


/* cpu内核选择 cpu platform type, must config by user */
#define CMB_CPU_PLATFORM_TYPE          CMB_CPU_ARM_CORTEX_M4

/* 使能栈的dump功能 enable dump stack information */
#define CMB_USING_DUMP_STACK_INFO

/* 语言选择 默认选择英语language of print information */
#define CMB_PRINT_LANGUAGE             CMB_PRINT_LANUUAGE_ENGLISH
#endif /* _CMB_CFG_H_ */

FreeRTOS 操作系统配置


#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_

/* 打印函数配置 */
#define cmb_println(...)               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_FREERTOS



/* cpu内核选择 cpu platform type, must config by user */
#define CMB_CPU_PLATFORM_TYPE          CMB_CPU_ARM_CORTEX_M4

/* 使能栈的dump功能 enable dump stack information */
#define CMB_USING_DUMP_STACK_INFO

/* 语言选择 默认选择英语language of print information */
#define CMB_PRINT_LANGUAGE             CMB_PRINT_LANUUAGE_ENGLISH

#endif /* _CMB_CFG_H_ */

**默认支持的ARMCortex-M 系列 CPU平台可选 **

#define CMB_CPU_ARM_CORTEX_M0 0
#define CMB_CPU_ARM_CORTEX_M3 1
#define CMB_CPU_ARM_CORTEX_M4 2
#define CMB_CPU_ARM_CORTEX_M7 3
#define CMB_CPU_ARM_CORTEX_M33 4

可选 RTOS 操作系统

#define CMB_OS_PLATFORM_RTT 0
#define CMB_OS_PLATFORM_UCOSII 1
#define CMB_OS_PLATFORM_UCOSIII 2
#define CMB_OS_PLATFORM_FREERTOS 3

语言的选择

#define CMB_PRINT_LANGUAGE_ENGLISH 0//英语
#define CMB_PRINT_LANGUAGE_CHINESE 1//中文
#define CMB_PRINT_LANGUAGE_CHINESE_UTF8 2//中文utf-8

注意freertos作者做了修改

因为 FreeRTOS 的 TCB 中没有 StackSize 信息,所以修改了其源码(基于 V9.0.0),在 FreeRTOS/tasks.c 中增加了 uxSizeOfStack 字段, 以及 vTaskStackAddr()vTaskStackSize()vTaskName() 函数。

源码内部修改了3个地方

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

6.我们的assert_failed调用cm_backtrace_assert可以输出一些信息

这一步并非关键

在这里插入图片描述

7.CmBacktrace初始化

#include <cm_backtrace.h>
//cm_backtrace 初始化,这里的初始化并非必要,仅仅设置一些非必要参数
cm_backtrace_init(“N32L40x”,“HV1.0”,“SV1.0”);

在这里插入图片描述

8.添加测试函数

void fault_test_by_unalign(void) {
    volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR
    volatile int * p;
    volatile int value;

    *SCB_CCR |= (1 << 3); /* bit3: UNALIGN_TRP. */

    p = (int *) 0x00;
    value = *p;
    printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);

    p = (int *) 0x04;
    value = *p;
    printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);

    p = (int *) 0x03;
    value = *p;
    printf("addr:0x%02X value:0x%08X\r\n", (int) p, value);
}

void fault_test_by_div0(void) {
    volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR
    int x, y, z;

    *SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */

    x = 10;
    y = 0;
    z = x / y;
    printf("z:%d\n", z);
}

裸机示例1:内存地址异常访问异常

将N32L40x.axf 复制到Linux系统中既可以得到问题所在

addr2line -e N32L40x.axf -a -f 0800350e 08003552 080080c2

在这里插入图片描述

根据addr2line工具分析找到问题所在,在函数fault_test_by_unalign 中,在文件fault_test.c 的27行

在这里插入图片描述

裸机示例2:除数为零分析

在这里插入图片描述

addr2lin分析结果

在这里插入图片描述

freertos示例3:

线程内部调用除数为零的异常函数,CmBacktrace明确说明出现问题的线程名称和发生错误的原因

在这里插入图片描述

addr2line -e N32L40x.axf -a -f 080035b6 080008de

在这里插入图片描述

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Car12

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值