当前头条:FreeRTOS中如何定位HardFault?
来源:面包芯语时间:2023-04-24 15:10:09

大家好,我是鱼鹰。今天我们聊一聊开发中常见的 HardFault,这个问题应该从学习 STM32 开发以来就一直伴随着我们,很多人遇到这种问题也是不知道该如何定位。

如果只是独立开发,遇到这种问题,一般都是看代码、修改代码等等这些常规手段,因为自己写的代码最熟悉,改动一般也不会太大,容易缩小范围,也更容易定位。

但现在的产品越来越复杂,目前的开发模式都是合作开发,每个人负责各自的模块,这样的项目代码量大、复杂度高,也就更难定位问题。


(资料图片仅供参考)

而有的时候,刚入职一家公司,什么代码都不熟悉,又出现了 HardFault,更是让人崩溃,分分钟有跑路的冲动(你和代码,有一个能跑就行)。

此时,有一个能解决这种疑难杂症的大牛是能大大节省时间的,而我在公司也解决不少类似的问题,所以经验也算丰富,充当的也是这一类角色。

而定位 Hardfault 的方法一般是靠 KEIL在线调试+C语言+权威指南中的知识搞定。

目前鱼鹰的解 BUG 差不多是这样的:

1、必现,代码熟悉的情况下,几个小时内搞定。

2、偶现,根据出现情况决定解决问题的时间,一般出现个四五次,基本就能定位。

3、难现,这种一般要挂一个记录仪实时记录运行情况。

经历了这么多,已经很少有能让鱼鹰需要花费几天时间才能解决的 Hardfault 问题了(犹记得刚来深圳时,因为别人写的一个 BUG 导致的 Hardfault,不得已加了几天通宵,要不是偶然机会还不一定能搞定)。

这里打个小广告,如果难解决,可以有偿请鱼鹰解决 Hardfault 问题哦。

不过最近工作上因为用了 C++,这个基础不是很熟悉,解决 Hardfault 的速度又下降了。而工程编译优化等级 -O2 也加大了不少调试难度,因此掌握下面的方法是很重要的:

总结 MDK 几种编译优化设置的方法

关于 Hardfault,鱼鹰以前也是分享了不少笔记的,不知道有多少人认真看过。

HardFault 之 INVSTAE 错误定位(一)

见鬼,过年回来后板子就 hardfault 了?

今天将继续分享关于在 FreeRTOS 定位 Hardfault 方法。

这里需要一个大佬写的组件 :CmBacktrace(事实上,如果能在线调试,鱼鹰是不需要借助这个组件的,但是难复现的情况下用这个组件还是比较香的)。

gitee 仓库:https://gitee.com/Armink/CmBacktrace

这个组件估计很多道友都听说过,也用过,但鱼鹰想说的是,有些道友在用的组件可能比较老,没有下面这种追踪功能,建议大家更新一下。

上面可以看到出错时,函数的调用栈(有时可能是错误的,需要实际分析,仅做参考)

在断言失败的位置添加该函数 cm_backtrace_assert:

相关函数修改 task.c prvInitialiseNewTask():

task.c 文件最后添加如下代码用于获取栈地址、大小、名字:

为方便复制,在此贴代码:

cmb_cfg.h

1)需要定义打印输出函数,一般用 printf 打印,也可以用你自定义的一些打印函数,功能和 printf 类似即可。

2)使能 RTOS 支持

5)打印虚拟栈,可以将出错时的原始栈信息打印出来,可能对分析有些帮助

7)如果是 C++ 编译的,有可能出错,可以在开头定义这个:

或者使用相对路径的方式添加头文件:

另外,我们可以让程序进入 Hardfault 前,让代码自动停止,这样我们能更好的利用在线调试代码,《传说中的软件断点到底是什么?》。

8、实验。

上面都搞定了,就可以验证一下效果了。这里我们我们可以模拟仿真看看情况(修改工程配置,这些内容鱼鹰以前分享过,不多说)。

运行仓库例子后,应该能打印下面的信息,告诉我们出现了 div 0 错误。

而你移植好的工程也应该打印类似的信息(加入测试代码 :fault_test_by_div0();)如果打印不出来,有两种可能:

1、打印函数没初始化好就进入了Hardfault

2、打印函数有问题

之后我们复制最后一行,然后运行仓库 tools 里面的 add2line 工具看看调用栈信息:

在 git bash 中可能会执行失败,可以加入程序路径,当然也可以将该工具路径加入到Windows 环境变量中。有可能提示找不到 axf 文件,把这个文件拷贝到工具下即可。

正确的做法是,将该工具放到 C 盘目录下,同时添加环境变量,之后就可以在 axf 目录下打开 gitbash 或者 cmd 窗口执行命令即可。

这里只作为演示,就不多介绍这些了。

最后再简单介绍一下这个组件的实现原理:

如果出现hardfault, 首先进入汇编文件的 HardFault_Handler处理,这里会得到当前的栈指针和 LR,并且根据 LR 确定出错的栈是哪个《STM32 两个栈,你用哪一个?》(这里是 PSP 栈)

根据错误寄存器信息,确定哪种错误(这里为除 0 错误)。

然后对栈信息 和 LR 、PC 进行 FLASH 上的汇编代码分析,找出可能的跳转指令,这里找到的两个跳转地址为 0x08001f96 0x08000368,进而得到调用栈。

因此要能准确的得到调用栈,有两点重要前提条件(建议优化等级 -O0):

1、栈未被破坏

2、芯片运行代码和 axf 文件保持一致

即使如此,你也不能保证找出的调用栈就是正确的,比如你使用 fault_test_by_unalign() 测试,得到的结果如下:

中间多了一个 fputc。

因此,这些打印信息只能作为参考使用。

但在线调试不同,它更专业,不容易出现错误调用关系。

来源:鱼鹰谈单片机

关键词: