1 FreeRTOS中如何定位HardFault?-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

FreeRTOS中如何定位HardFault?

麦克泰技术 来源:麦克泰技术 作者:麦克泰技术 2022-11-29 14:30 次阅读

今天继续聊聊开发中常见的 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

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

61daf8a8-6fa0-11ed-8abf-dac502259ad0.png

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

_call_main ->  main -> fult_test_by_div0

相当实用。

同时,本篇笔记不仅适用于在 FreeRTOS 定位 Hardfault,实际上uCOSrt-thread 等其它 RTOS 照样可以修改后使用(裸机更不用说了)。

仓库例子支持的平台:裸机、rt-thread、ucoss-ii、freertos。

这里重点在如何移植这个组件到freertos 中(实际上,仓库的说明文档也非常详细,可以参考)。由于freertos 也是不断更新中,所以这个组件的例子不能完全适用于新版本,而鱼鹰刚好移植好了,在此记录一下,方便大家移植。

1、将仓库中的 cm_backtrace(源码文件)整个文件夹拷贝到自己的工程文件夹下。

61fddfee-6fa0-11ed-8abf-dac502259ad0.png

2、在自己的工程中添加这些文件(我们可以打开 demos -> os -> freertos 工程查看)

62210834-6fa0-11ed-8abf-dac502259ad0.png

只有两个文件,相当简单。

一个是核心源码,另外一个则是汇编代码,代码执行入口。

注意,根据 IDE 不同,选择的汇编文件也不同:

623a3d72-6fa0-11ed-8abf-dac502259ad0.png

其实就是将 startup_stm32f10x_hd.s 中的hardfault 默认处理函数重定位到 cmb_fault.S 中了。

62595ff4-6fa0-11ed-8abf-dac502259ad0.png

注意这里有一个weak,这样链接的时候就不会链接这个,而是cmb_fault.S这个:

62736502-6fa0-11ed-8abf-dac502259ad0.png

为了更方便的定位问题,我们后面还需要修改一下这个代码才行。

注意,如果你的启动文件内的 hardfault 代码被修改了,而你不懂汇编,建议恢复成上面那种,不然可能运行不正常。

3、主函数中初始化代码。

62941478-6fa0-11ed-8abf-dac502259ad0.png

这里的字符串需要和这个一样(根据自己的工程名修改):

62a71672-6fa0-11ed-8abf-dac502259ad0.png

所以建议用英文建工程。这个在输出错误信息的时候用的上,否则每次查看调用栈都需要修改一下,比较麻烦。

如果开启了内部看门狗,建议关闭一下:

//HAL 库
__HAL_DBGMCU_FREEZE_IWDG1();
//标准库
DBGMCU_Config(DBGMCU_IWDG_STOP, ENABLE);

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

62bb7928-6fa0-11ed-8abf-dac502259ad0.png

这样断言失败了也能看到调用栈了。

4、FreeROTS内核文件修改(内核版本 V10.2.1)

为了分析出错的代码,必须知道每个任务的栈信息,而 FreeRTOS 可能没有这些信息,因此,我们需要添加进去。

task.c

62db59be-6fa0-11ed-8abf-dac502259ad0.png

FreeRTOS.h

6303c084-6fa0-11ed-8abf-dac502259ad0.png

注意,老版本freertos 是只要修改一处的,但新版本需要修改两处,否则会断言失败,运行不下去。

建议把注释也一起添加进去。

UBaseType_t     uxSizeOfStack;      /*< Support For CmBacktrace >*/

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

63324f08-6fa0-11ed-8abf-dac502259ad0.png

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

63585766-6fa0-11ed-8abf-dac502259ad0.png

为方便复制,在此贴代码

/*-----------------------------------------------------------*/
/*< 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;
}
/*-----------------------------------------------------------*/

5、根据所属 RTOS 平台和芯片内核修改组件配置信息

cmb_cfg.h

6378da40-6fa0-11ed-8abf-dac502259ad0.png

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

#define cmb_println(...)               printf(__VA_ARGS__);printf("
")

2)使能 RTOS 支持

#define CMB_USING_OS_PLATFORM

3)具体 RTOS 选择FreeRTOS

#define CMB_OS_PLATFORM_TYPE           CMB_OS_PLATFORM_FREERTOS

4)芯片内核根据实际选择,目前支持 M0、M3、M4M7。

#define CMB_CPU_PLATFORM_TYPE          CMB_CPU_ARM_CORTEX_M3

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

#define CMB_USING_DUMP_STACK_INFO

6)语言支持:英语。实际也支持中文,但建议使用英语(不配置,默认就是英语)

#define CMB_PRINT_LANGUAGE             CMB_PRINT_LANGUAGE_ENGLISH

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

#define__CLANG_ARM

7、根据需要修改组件,方便使用(这些看看能不能有机会合并到大佬的分支里面)

1)因为功能涉及范围小,因此可以将相关头文件包含形式改成这种,这样就不需要改头文件路径了,移植更方便:

#include 
-->>
#include"./cm_backtrace.h"


#include 
-->>
#include "./cmb_cfg.h"


#include "cmb_def.h"
-->>
#include "./cmb_def.h"

main 中也不需要包含头文件,而是在需要位置直接声明这个函数即可,因为外部只需要调用这个函数

#include 
-->
void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver);

这样一来,就不需要添加头文件的路径了。

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

#include "../../driver/cm_backtrace/cm_backtrace.h"

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

HardFault_Handler    PROC
    LDR     r0, =0xE000EDF0; DEMCR
    LDR     r0,[r0,#0x00]
    AND     r0,r0,#0x00000001
    CBZ     r0,not_in_debug
    BKPT    0
not_in_debug
    MOV     r0, lr                  ; get lr
    MOV     r1, sp                  ; get stack pointer (current is MSP)
    BL      cm_backtrace_fault

因为刚进入 Hardfault 时的信息最全,又不想每次打断点,上面的代码很好的实现了功能,同时也不会影响程序的正常运行(会自动判断是否处于调试模式)。

8、实验。

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

639ec7f0-6fa0-11ed-8abf-dac502259ad0.png

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

63cb7908-6fa0-11ed-8abf-dac502259ad0.png

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

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

2、打印函数有问题。

63e46c9c-6fa0-11ed-8abf-dac502259ad0.png

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

63fc89ee-6fa0-11ed-8abf-dac502259ad0.png

在 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() 测试,得到的结果如下:

641fe20e-6fa0-11ed-8abf-dac502259ad0.png

中间多了一个 fputc。

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

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

6440fd2c-6fa0-11ed-8abf-dac502259ad0.png

鱼鹰想分享的内容到此就结束了,下期再见!

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    180

    文章

    7604

    浏览量

    136676
  • FreeRTOS
    +关注

    关注

    12

    文章

    484

    浏览量

    62132

原文标题:FreeRTOS 中如何定位 HardFault?

文章出处:【微信号:麦克泰技术,微信公众号:麦克泰技术】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    freertos最多支持多少个任务

    FreeRTOS是一个轻量级的实时操作系统(RTOS),其设计初衷就是为了提供简单、可靠且高效的实时任务管理。关于FreeRTOS最多支持多少个任务的问题,实际上并没有一个固定的上限,这主要取决于
    的头像 发表于 09-02 14:21 844次阅读

    STM32U5退出stop2模式后进入HardFault_Hand的原因?

    HardFault_Handler,但是连接stlink开启调试下,程序能连续进入和退出stop2模式,有无大佬了解该问题或者该如何定位问题?
    发表于 07-24 06:25

    使用rt_thread_mdelay函数后出现hardfault的原因?

    问题:在一个线程中共有前后2部分使用rt_thread_mdelay()进行延时等待,前面部分延时是正常的,后面部分进入rt_thread_mdelay()函数后立马打印hardfault错误,定位
    发表于 07-16 07:07

    HPM SDK 1.6.0 FreeRTOS LTS更改及适配指南

    1、HPM_SDKFreeRTOSLTS特性总览HPMSDKV1.6.0FreeRTOS升级前后特性支持对比:特性V10.4.4LTS202210.01断抢占√√RISCV浮点扩展支持
    的头像 发表于 07-06 08:17 3477次阅读
    HPM SDK 1.6.0 <b class='flag-5'>FreeRTOS</b> LTS更改及适配指南

    STM32H743IIT6在SystemInit跳转到HardFault_Handler函数里面,为什么?

    每当运行到这个地方就会跳转到HardFault_Handler函数里面 这个是调试的界面
    发表于 05-23 07:32

    运行ble_hello_sensor时出现了HardFault_Handler,为什么?

    当我运行 ble_hello_sensor 时,出现了 HardFault_Handler。 我认为 cy_rtos_create_thread(cybt_platform_task.c) 用于
    发表于 05-21 07:35

    FOC 4.2调试进入HardFault_Handler(void)的原因?

    ( PWMnCurrFdbkParamsM2,R3_DDParamsM2); 这条命令死掉,强迫停止后,发现进入了HardFault_Handler(void)命令段。感觉似乎硬件配置方面有问题,大神们给解释下,有配置成功的吗
    发表于 05-11 06:25

    浅析FreeRTOS任务调度器的三种调度算法和应用

    FreeRTOS在MCU领域应用非常广泛,今天就给大家讲解一下FreeRTOS调度器的三种调度算法,以及在瑞萨RZ/T2L MPU的应用。
    的头像 发表于 05-10 14:02 7311次阅读
    浅析<b class='flag-5'>FreeRTOS</b>任务调度器的三种调度算法和应用

    STM32程序运行时会莫名的进入HardFault硬件中断,为什么?

    程序运行时会莫名的进入HardFault硬件中断,尝试了一周的时间,试过以下方法始终不能找到问题出在哪里,希望有人能指导下: 1.换硬件,依然出现,可以排除硬件自身原因; 2.在代码中加入nop
    发表于 04-10 06:29

    STM32H750+STEMWIN+FreeRTOS调用绘图函数绘制位图进入HardFault的原因?

    如题,在工程需要调用GUI_DrawBitmap()、GUI_DrawBitmapEx()等函数绘制图片,当位图尺寸小的时候可以正常显示,但是位图尺寸超过一定尺寸就进入HardFault故障(这里
    发表于 04-10 06:29

    h750+STEMWIN+FreeRTOS调用绘图函数绘制位图进入HardFault的原因?

    如题,在工程需要调用GUI_DrawBitmap()、GUI_DrawBitmapEx()等函数绘制图片,当位图尺寸小的时候可以正常显示,但是位图尺寸超过一定尺寸就进入HardFault故障(这里
    发表于 04-09 07:09

    STM32F7使用FreeRTOS,程序运行一段时间后进入HardFault_Handler的原因?

    大家好,最近学习在STM32F7上使用FreeRTOS,程序运行一段时间后进入HardFault_Handler,该时间不定。故障分析器给的报错信息为“总线,存储器管理或使用失败(FORCED
    发表于 04-08 07:23

    CUBEMX为什么找不到FreeRTOS的选项?

    请教大神,CUBEMX 怎么找不到 FreeRTOS的选项,需要怎么安装
    发表于 03-25 06:23

    使用CUBEMX6.9.2,LWIP 2.1.2配置LWEIP,使用FREERTOS后,一开始运行任务就进入了HardFault异常的原因?

    使用CUBEMX6.9.2,LWIP 2.1.2配置LWEIP,裸机运行正常 使用FREERTOS后,一开始运行任务就进入了HardFault异常 请问有遇到类似问题的同学吗?谢谢
    发表于 03-12 06:26

    FreeRTOS内存机制详解

    FreeRTOS是一种实时操作系统,它提供了多种内存分配方式,包括动态内存分配和静态内存分配。
    的头像 发表于 12-31 16:49 2681次阅读
    <b class='flag-5'>FreeRTOS</b>内存机制详解