Hard Fault定位利器——开源组件CmBacktrace

前言

        记录学习过程STM32/GD32+FreeRTOS+CmBacktrace移植。在MCU的开发中,由于程序设计的疏忽或者没有考虑到的一些异常情况,非常容易触发Hard Fault,在开发前期阶段触发Hard Fault时,我们可以通过连接调试工具利用keil比较容易定位到问题所在,但是如果已经处于快接近成品阶段,有时不那么容易能接上调试器了。或者说,这个Hard Fault非常难触发,我们也不可能一直接上调试器等着。所以这里可以使用CmBackTrace开源组件,帮助我们不需要连接调试器就可以定位到问题原因。

CmBackTrace介绍

        CmBacktrace(Cortex Microcontroller Backtrace)是一个开源库,可以自动跟踪和定位 ARM Cortex-M 系列 MCU 的错误代码,并自动分析错误原因。该库可以将错误信息输出到控制台,或者把错误信息保存在FLASH中,之后通过读取控制台输出内容或者flash中存储内容,再配合addr2line工具,可以看到函数调用堆栈、故障诊断结果、堆栈、故障寄存器和产品固件信息,大大提高了错误定位的效率和准确性。开源项目链接: https://github.com/armink/CmBacktrace

        该开源组件的作者有一句话说的很有道理,值得学习。在此分享给大家:"工欲善其事,必先利其器。所以有时候效率低下的原因可能是你会使用的工具种类太少"。学习嵌入式,就是要多面手,需要见多识别广。

组件移植

移植平台环境:

        GD32F103+Freertos + keil5

1、添加源文件 

        下载源码文件,把cm_backtrace文件下所有文件放入自己工程下。

然后在自己工程中添加了源码,根据自己选用的编译器,把fault_handler文件下对应的cmb_fault.S添加进去,我使用的是keil 就选择如下图所示。

再把源码路径添加进去,开启C99的支持。

2、配置cmb_cfg.h

 这些配置必须得配置,不然会编译不过,具体的配置根据平台和使用场景配置。以我使用场景为例子,自己使用printf输出信息,使用了RTOS且类型为FreeRTOS,GD32F303内核为M4核,使能DUMP,默认语言英语,所以我的设置如下:

/* print line, must config by user */
#define cmb_println(...)          printf(__VA_ARGS__);printf("\r\n")       /* e.g., printf(__VA_ARGS__);printf("\r\n")  or  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_M4  /* CMB_CPU_ARM_CORTEX_M0 or CMB_CPU_ARM_CORTEX_M3 or CMB_CPU_ARM_CORTEX_M4 or CMB_CPU_ARM_CORTEX_M7 or CMB_CPU_ARM_CORTEX_M33 */
/* 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 or CMB_PRINT_LANGUAGE_CHINESE_UTF8 */

2、解决编译报错

直接编译会报Error: L6200E: Symbol HardFault_Handler multiply defined (by cmb_fault.o and gd32f30x_it.o).错误,CmBacetrace库提供的 cmb_fault.s 中的HardFault_Handler 和gd32f30x_it.c重复定义了,我们直接把gd32f30x_it.c 中HardFault_Handler 注释了。

//TODO:注释HardFault_Handler 
// /*!
//     \brief      this function handles HardFault exception
//     \param[in]  none
//     \param[out] none
//     \retval     none
// */
// void HardFault_Handler(void)
// {
//     /* if Hard Fault exception occurs, go to infinite loop */
//     while (1){
//     }
// }

继续编译,报错。因为该组件还不能直接FreeRTOS,因为 FreeRTOS 的 TCB 中没有 StackSize 信息,所以需要自己修改一下FreeRTOS源码。

好消是应该下个版本就可以直接使用了,在PR记录中已经更新,等着验证后合并了。

那么正式版本还没发布,我们还是自己来修改一下FreeRTOS的源码,移植demo里面也有FreeRTOS修改的参考demo,具体参考这里:

主要修改三个地方,第一在在 `FreeRTOS/tasks.c` 中任务TCB控制块中增加 `uxSizeOfStack` 字段:

第二在  `FreeRTOS/tasks.c` , prvInitialiseNewTask 函数里面添加 

pxNewTCB->uxSizeOfStack = ulStackDepth;   /*< Support For CmBacktrace >*/

具体位置如下:

第三在 `FreeRTOS/tasks.c`末尾增加 `vTaskStackAddr()` 、 `vTaskStackSize()` 、 `vTaskName()` 函数。具体如下:

//TODO:支持CmBacktrace
/*-----------------------------------------------------------*/
/*< 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;
}
/*-----------------------------------------------------------*/

具体使用

我们只需要程序初始化的时候调用一次init即可。

//CmBacktrace
#define HARDWARE_VERSION               "V1.0.0"
#define SOFTWARE_VERSION               "V0.1.0"int main(void)
{uint32_t clk;uint32_t ahb_clk;uint32_t apb1_clk;uint32_t apb2_clk;SystemInit();SystemCoreClockUpdate();nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);/* CmBacktrace initialize */cm_backtrace_init("CmBacktrace", HARDWARE_VERSION, SOFTWARE_VERSION);//后面省略 示意while(1){}}

我们利用官方提供的测试函数,人为制造一个除0错误看触发Hard Fault后,这个组件实际情况:这里设置SCB->CCR寄存器目的是打开除0异常触发,否则即使除以0也不会触发Hard Fault。

void fault_test_by_div0(void) {volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCRint x, y, z;*SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */x = 10;y = 0;z = x / y;printf("z:%d\n", z);
}

运行程序之后,触发除0 Hard Fault后,串口会输出打印信息如下,通过串口输出的提示信息可以看到,触发错误的原因是除0导致,具体函数触发位置在0x080024b4位置:

上面的信息不够直观,无法直接判断是哪个函数出了问题,我们使用addr2line 工具获取函数调用栈详细信息。addr2line (它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。关于addr2line更多详细信息可以参考https://github.com/armink/CmBacktrace/blob/master/docs/zh/how_to_use_addr2line_for_call_stack.md

把下载源码里面tools 文件夹中 addr2line.exe ,直接拷贝至 C:\Windows 下。cmd把路径切换到自己工程文件axf文件下,再运行下面的命令即可:

addr2line -e gd32f303cct6_template.axf -afpiC 080024b4 08003786// 其中 gd32f303cct6_template.axf 是自己工程编译生成的axf文件
// 080024b4 是PC所指向地址,可以理解为当前触发错误的地方
// 08003786 是LR所指向地址,理解为程序调用PC之后所返回的地址

从输出信息看到发生Hard Fault 的地方是serialrecv_task.c 文件中的第23行出现了除0错误。也就是我们源码的位置除0位置。

拓展

实际产品中,可以通过把打印的信息写入FLASH中,死机了就复位重启继续运行程序。需要查询具体信息时,直接把FLASH里面的内容读取输出即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/282613.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

liunx之Samba服务器

环境&#xff1a;虚拟机CENTOS 7和 测试机相通 一、Samba服务器_光盘共享&#xff08;匿名访问&#xff09; 1.在虚拟机CENTOS 7安装smb服务&#xff0c;并在防火墙上允许samba流量通过 2. 挂载光盘 3.修改smb.conf配置文件&#xff0c;实现光盘匿名共享 4. 启动smb服务 5.在…

OceanBase数据库初识

文章目录 说明分布式数据库发展发展历史OceanBase和传统数据库的对比总结 OceanBase数据库产品简介应用案例 OceanBase数据库产品OceanBase数据库内核OceanBase开发者中心&#xff08;ODC&#xff09;产品架构OMS核心功能简介 说明 本文仅供学习和交流学习内容参考官方的培训资…

电子元器件介绍——电容(二)

电子元器件 文章目录 电子元器件前言2.1 电容的基本知识2.2 电容的等效电路2.3 电容的分类2.4电容的特性参数2.5电容的作用旁路&#xff08;去耦&#xff09;高低通滤波 2.6 两端电容2.7电容的失效 总结 前言 这节我们介绍电容。 2.1 电容的基本知识 电容器是一种能储存电荷…

【华为】文档中命令行约定格式规范(命令行格式规范、命令行行为规范、命令行参数格式、命令行规范)

文章目录 命令行约定格式**粗体&#xff1a;命令行关键字***斜体&#xff1a;命令行参数*[ ]&#xff1a;可选配置{ x | y | ... } 和 [ x | y | ... ]&#xff1a;选项{ x | y | ... }* 和 [ x | y | ... ]*&#xff1a;多选项&<1-n>&#xff1a;重复参数#&#xff…

嵌入式开发中的总线与时钟

总线 AHB总线 AHB的全称是"Advanced High-performance Bus",中文翻译就是"高级高性能总线"。这是一种在计算机系统中用于连接不同硬件组件的总线架构,它可以帮助这些组件之间高效地传输数据和信息。这个总线架构通常用于处理速度较快且对性能要求较高的…

java内置的数据结构

Java语言提供了许多内置的数据结构&#xff0c;包括&#xff1a; 1. 数组&#xff08;Array&#xff09;&#xff1a;数组是最基本的数据结构之一&#xff0c;它是一个有序的元素集合&#xff0c;每个元素都有一个对应的索引。在Java中&#xff0c;数组可以通过声明和初始化来创…

gitee gihub上传步骤

上传 1. 到具体要上传的文件目录 2. 右击git Bash Here 初始化仓库&#xff1a;git init 3. 添加文件 添加所有文件 : git add . &#xff08;注意这里有个点&#xff09;添加具体文件&#xff1a; git add test.md 4. 添加到暂存区 git commit -m 暂存区 5. 将本地代…

《人工智能导论》知识思维导图梳理【第6章节】

文章目录 第六章 知识图谱1 知识图谱概述2 知识图谱相关概念3 知识图谱的逻辑结构4 知识图谱的数据存储5 知识图谱的构建过程6 例题 markdown内容的分享 第六章 知识图谱 1 知识图谱概述 2 知识图谱相关概念 3 知识图谱的逻辑结构 4 知识图谱的数据存储 5 知识图谱的构建过程 6…

【LangChain学习之旅】—(3) LangChain快速构建本地知识库的智能问答系统

【LangChain学习之旅】—&#xff08;3&#xff09; LangChain快速构建本地知识库的智能问答系统 项目及实现框架开发框架核心实现机制数据准备及加载加载文本文本的分割向量数据库存储文本的“嵌入”概念向量数据库概念 相关信息获取RetrievalQA生成回答并展示示例小结 Refere…

自然语言处理阅读第二弹

HuggingFace 镜像网站模型库 NLP中的自回归模型和自编码模型 自回归&#xff1a;根据上文内容预测下一个可能的单词&#xff0c;或者根据下文预测上一个可能的单词。只能利用上文或者下文的信息&#xff0c;不能同时利用上文和下文的信息。自编码&#xff1a;对输入的句子随…

《ThreadLocal使用与学习总结:2023-12-15》由浅入深全面解析ThreadLocal

由浅入深全面解析ThreadLocal 目录 由浅入深全面解析ThreadLocal简介基本使用ThreadLocal与synchronized的区别ThreadLocal现在的设计&#xff08;JDK1.8&#xff09;ThreadLocal核心方法源码分析ThreadLocalMap源码分析弱引用与内存泄露&#xff08;内存泄漏和弱引用没有直接关…

Vue中插槽的使用

目录 一、默认插槽 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;代码展示 &#xff08;3&#xff09;后备内容 二、具名插槽 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;代码展示 三、作用域插槽 &#xff08;1&#xff09;概念 &#xff0…