【STM32】HardFault问题详细分析及调试笔记

本文详细描述了一次在STM32项目中遇到的HardFault故障问题,通过软件和硬件的分析,最终定位到中断栈溢出导致的问题。文章介绍了Cortex-M4处理器的中断和异常处理机制,包括中断处理流程、栈帧结构、异常返回值等,并提供了问题定位的具体步骤,如确定栈指针、LR值和PSP栈,最后通过增大ISR栈大小解决了问题。
摘要由CSDN通过智能技术生成

目录

1. 概述

2. 问题描述

3. 问题分析

4. 相关知识

4.1异常和中断

4.2 中断输入与挂起行为

4.3 Cortex-M4处理器的寄存器简介

4.4  C实现的异常处理

4.5 栈帧

4.6 异常返回值

4.7 异常流程

5. 问题定位

5.1 确定栈指针

5.2 确定LR的值

5.3 查询C代码位置

5.4 确定PSP栈

5.5 处理方法

6. 总结


1. 概述

        最近做的项目中出现了HardFault故障现象,查阅了网上关于HardFault的排故思路,详尽程度不同,均有所帮助,但深入分析时,又觉得指导的不够到位,本文参考了《ARM Cortex-M3Cortex-M4权威指南》,借鉴了网友的经验,结合了map文件加以分析,准确定位了故障原因,解决了实际问题,希望能帮助到广大网友。

2. 问题描述

        接入以太网运行几个小时后,偶发单片机程序卡死现象。

3. 问题分析

        采用一贯的解决嵌入式单片机问题的思路。从软件和硬件两个方面切入分析。(本人长期坚持这一原则,避免硬件问题被忽略

        该工程应用了CAN、串口、网络、AD等外设,在前期测试中,单独应用CAN、串口、网络均未出现问题,尤其是网络,进行过长时间压力测试(间隔1mS PING测试)均未出现问题,可初步排除硬件问题。

        分析当时的应用场景,网络接入后引起的单片机程序卡死,复现时间较短。首先想到的方案是进行CAN、网络同时压力测试,并用仿真器在线调试。考虑程序大概率会卡死在HardFault处,因此在HardFault中断服务程序的while ( 1 )设置断点,在故障复现时,程序停在了HardFault_Handler的while循环处,代码如下

void HardFault_Handler ( void )
{
    /* Go to infinite loop when Hard Fault exception occurs */
    while ( 1 )
    {
    }
}

        要分析这个问题,我们需对STM32的中断及异常处理有一定的了解,才能更好的分析故障产生的原因,参考《ARM Cortex-M3Cortex-M4权威指南》,整理了相关知识。

4. 相关知识

4.1异常和中断

        要了解HardFault中断,先要了解中断。几乎所有的微控制器,中断是一种常见的特性。中断一般是由硬件(如外设和外部输入引脚)产生的事件,它会引起程序流偏离正常的流程(如给外设提供服务)。当外设或硬件需要处理器的服务时,执行的流程如图 1所示。

 1外设中断处理流程

        所有的Cortex-M处理器都会提供一个用于中断处理的嵌套向量中断控制器(NVIC)。除了中断请求,还有其他需要服务的事件,将其称为“异常”,按照ARM的说法,中断也是一种异常。典型的Cortex-M4微控制器中,NVIC接收多个中断源产生的中断请求,如图 2所示。

2 典型微控制器中的各种异常源

        Cortex-M4的NVIC支持最多240个IRQ(中断请求)、1个不可屏蔽中断(NMI)、1个SysTick(系统节拍)定时中断及多个系统异常。具体如表 1所示。

1 Cortex-M4异常源

异常源

异常源说明

备注

IRQ

多数IRQ由定时器、I/O端口和通信接口(如UART、SPI、CAN和I2C)等外设产生

NMI

由看门狗定时器或掉电检测器等外设产生

SysTick定时器

系统节拍

系统异常

硬件错误、总线错误、存储管理错误、使用错误、PendSV等。

        微控制器复位后,所有中断都处于禁止状态,且默认的优先级为0,在使用任何一个中断之前,需要:

  • 设置所需中断的优先级。
  • 使能外设中的可以触发中断的中断产生控制。
  • 使能NVIC中的中断。

4.2 中断输入与挂起行为

        每个中断源都有多个属性:

  • 每个中断都可被禁止(默认)或使能。(这里不包含NMI)
  • 每个中断都可被挂起(等待服务的请求)或解除挂起。
  • 每个中断都可处于活跃(正在处理)或非活跃状态。

        挂起状态的意思是,中断被置于一种等待处理器处理的状态。有些情况下,处理器在中断挂起时就会进行处理。不过,若处理器已经在处理另外一个更高或同等优先级的中断,或者中断被某个中断屏蔽寄存器给屏蔽掉了,那么在其他的中断处理结束前或中断屏蔽被清楚前,挂起请求会一直保持。

        中断挂起和激活行为的简单情况如图 3所示。当中断请求产生时,中断进入挂起状态,处理器暂停当前处理的线程并完成压栈和取向量后,处理中断,中断挂起状态清除,进入活跃状态。处理器处理完中断后出栈,继续处理之前的线程,同时中断活跃状态结束。

3 中断挂起和激活行为的简单情况

4.3 Cortex-M4处理器的寄存器简介

        在介绍异常处理前,需要先了解一下Cortex-M4处理器的寄存器。Cortex-M4处理器在处理器内核中都有多个执行数据处理核控制的寄存器,并以寄存器组的形式进行分组。Cortex-M4处理器的寄存器组中有16个寄存器,其中13个位32位通用寄存器,其他3个为特殊用途,如图 4所示。

4 Cortex-M处理器寄存器组中的寄存器

  • R0~R12为通用目的寄存器,
  • R13为栈指针,物理上存在两个栈指针:主栈指针(MSP)为默认的栈指针,在复位后和处理器处于处理模式时,会被选择使用;另一个栈指针为进程栈指针(PSP),只能用于线程模式(有RTOS时)。
  • R14为链接寄存器(LR),用于函数或子程序调用时返回地址的保存。在函数或子程序结束时,程序控制可以通过将LR的数值加载程序计数器(PC)中返回调用程序处并继续执行,在异常处理期间,LR会自动更新为特殊的EXC_RETURN(异常返回)数值。
  • R15为程序计数器(PC)。

4.4  C实现的异常处理

        对于Cortex-M处理器,可以将异常处理或中断服务程序(ISR)实现为普通的C程序/函数。用于ARM架构的C编译器遵循ARM的一个名为AAPCS(ARM架构过程调用标准)的规范。根据这份标准,C函数可以修改R0~R3、R12、R14(LR)、以及PSR,若C函数需要使用R4~R11,应该将这些寄存器保存在栈空间中,并且在函数结束前将它们恢复,如图 5所示。

5 AAPCS规定的函数调用中的寄存器使用

        C函数可以用作异常处理,异常机制需要再异常入口处自动保存R0~R3、R12、LR和PSR,并在异常退出时将它们恢复,这些都是由处理器硬件控制。具有浮点单元的处理器,如果用到了浮点单元,则异常机制还需要保存S0~S15和FPSCR。

4.5 栈帧

        在异常入口处被压入占空间的数据块为栈帧。对于有浮点单元的处理器,栈帧可能是8或26个字。栈指针的数值在函数入口和出口处应该是双字对齐的。无浮点单元的栈帧格式如图 6所示,有浮点上下文的异常栈帧格式如图 7所示。

 6 未启用浮点单元的异常栈帧

7 具有浮点上下文的异常栈帧

4.6 异常返回值

        处理器进入异常处理或中断服务程序(ISR)时,链接寄存器(LR)的数值会被更新为EXC_RETURN数值。其合法值如表 2所示。

2 异常返回(EXC_RETURN)合法值

浮点单元在中断前使用(FPCA=1)

浮点单元未在中断前使用(FPCA=0)

返回处理模式(总是使用主栈)

0xFFFFFFE1

0xFFFFFFF1

返回线程模式并在返回后使用主栈

0xFFFFFFE9

0xFFFFFFF9

返回处理模式并在返回后使用进程栈

0xFFFFFFED

0xFFFFFFFD

4.7 异常流程

        当异常产生且被处理器接受时,压栈流程会将寄存器压入栈中并组织栈帧,如图 8所示。

8 异常流程

5. 问题定位

工程开发环境为MDK5.25,定位过程如下。

5.1 确定栈指针

     在HardFault_Handler的while循环处设置断电,故障复现时R14(LR)寄存器值为0xFFFFFFFD

        查询“异常返回(EXC_RETURN)合法值”表格可知,返回后将使用进程栈,查看进程栈指针PSP的值为0x2000B278。具体如图 9所示。

9 故障复现调试示意图

5.2 确定LR的值

        打开Memory的监视窗口,输入PSP的值为0x2000B278则显示进程栈的内容如图 10所示,可知LR的值为0x080290E9

10 查找LR值示意图

5.3 查询C代码位置

     在Disassembly窗口单击右键—>选择Show Didassembly at Address>输入地址0x080290E9—>定位到os_int.c文件的void  OS_IntQPost ()函数,如图 11、图 12所示。该函数用于中断中延迟发布消息,分析问题可能和中断有关,和实际故障现象吻合。

11 定位C代码位置方法示意图

 

12 定位C代码位置示意图

5.4 确定PSP栈

        根据前面的分析,PSP的值为0x2000B278打开工程的map文件,找到0x2000B278附近的变量为OSCfg_IntQTaskStk(栈地址为0x2000b114~0x2000b2a3)和OSCfg_ISRStk(0x2000b2a4~0x2000b433),均为任务栈(线程栈),如图 13所示。

13 MAP文件示意图

5.5 处理方法

        用watch窗口分别观察2个栈空间,发现OSCfg_ISRStk栈溢出,经分析可定位问题。接入网线后,由于网络优先级较高,更容易出现中断嵌套,导致OSCfg_ISRStk栈溢出,出现卡死在HardFault现象。

        在os_cfg_app.h中定义了OSCfg_ISRStk的大小,如下:

                                                            /* --------------------- MISCELLANEOUS ------------------ */
#define  OS_CFG_MSG_POOL_SIZE            100u               /* Maximum number of messages                             */

#define  OS_CFG_ISR_STK_SIZE             100u               /* Stack size of ISR stack (number of CPU_STK elements)   */

#define  OS_CFG_TASK_STK_LIMIT_PCT_EMPTY  10u               /* Stack limit position in percentage to empty            */


                                                            /* ---------------------- IDLE TASK --------------------- */
#define  OS_CFG_IDLE_TASK_STK_SIZE       128u               /* Stack size (number of CPU_STK elements)                */


                                                            /* ------------------ ISR HANDLER TASK ------------------ */
#define  OS_CFG_INT_Q_SIZE                10u               /* Size of ISR handler task queue                         */
#define  OS_CFG_INT_Q_TASK_STK_SIZE      100u               /* Stack size (number of CPU_STK elements)                */


                                                            /* ------------------- STATISTIC TASK ------------------- */
#define  OS_CFG_STAT_TASK_PRIO     (OS_CFG_PRIO_MAX - 2u)   /* Priority                                               */
#define  OS_CFG_STAT_TASK_RATE_HZ         10u               /* Rate of execution (1 to 10 Hz)                         */
#define  OS_CFG_STAT_TASK_STK_SIZE       100u               /* Stack size (number of CPU_STK elements)                */


                                                           /* ------------------------ TICKS ----------------------- */
#define  OS_CFG_TICK_RATE_HZ            1000u               /* Tick rate in Hertz (10 to 1000 Hz)                     */
#define  OS_CFG_TICK_TASK_PRIO             1u               /* Priority                                               */
#define  OS_CFG_TICK_TASK_STK_SIZE       128u               /* Stack size (number of CPU_STK elements)                */


                                                            /* ----------------------- TIMERS ----------------------- */
#define  OS_CFG_TMR_TASK_PRIO             2u               /* Priority of 'Timer Task'                               */
#define  OS_CFG_TMR_TASK_RATE_HZ         100u               /* Rate for timers (100 Hz Typ.)                           */
#define  OS_CFG_TMR_TASK_STK_SIZE        128u               /* Stack size (number of CPU_STK elements)                */

        将OSCfg_ISRStk大小修改为200,代码如下:

#define  OS_CFG_ISR_STK_SIZE             200u

        更改后观察OSCfg_ISRStk的空间,在压力测试下使用约60%,系统正常工作。

6. 总结

        HardFault问题涉及的原因比较复杂,有的简单、有的难,均和ARM底层相关,碰到该类问题让人头疼,对于初学者来说更容易发懵。本文通过实际案例分析,提供了解决问题的方法,旨在给读者提供软硬件问题的解决思路,未必能解决所有的问题。但请读者切记,碰到问题时一定要静下心来,认真分析,找到问题的根本原因才是终极目的,排故过程也是知识积累的过程,也是提高个人能力的过程。

  • 36
    点赞
  • 188
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
### 回答1: STM32 死机(也称为硬件故障)是指STM32单片机在运行过程中出现了无法恢复的错误,并导致程序停止响应或崩溃的情况。硬件故障是由于程序运行时发生了非法指令、存储器读写错误、中断异常等原因引起的。 对于STM32硬件故障的分析,可以通过以下步骤来进行: 1. 异常向量表(NVIC)定位:硬件故障会导致程序跳转到异常处理器,通过查看异常向量表可以确定具体是哪个异常处理器被触发。 2. 寄存器值分析:查看产生硬件故障时相关寄存器的值,如程序计数器(PC)、堆栈指针(SP)等。这些值可以提供更多关于故障发生时的程序状态的信息。 3. 中断日志分析:如果硬件故障是由于中断异常引起的,可以通过打印中断日志来获取更多信息。中断日志包括中断号、中断源、中断服务例程等。 4. 调试工具使用:使用STM32开发板上的调试工具(如J-Link、ST-Link等)进行调试,可以通过断点、查看寄存器、查看存储器等功能,进一步分析硬件故障。 5. 代码回溯:通过查看产生硬件故障之前执行的代码,可以确定是否存在编程错误或者不合法操作,如访问空指针、溢出问题等。 在分析完硬件故障后,可以根据具体情况采取相应措施来修复问题。常见的修复措施包括修改代码错误、检查硬件连接、更新驱动程序等。同时,也推荐在开发过程中使用断言(assert)功能,及时捕获并处理潜在的硬件故障。 ### 回答2: STM32死机HardFault通常是由于程序错误或硬件故障引起的。下面是对STM32死机HardFault进行分析的常见步骤: 1. 排查硬件问题:确保硬件电源和时钟正常工作,并检查是否存在短路或其他硬件故障,例如引脚连接错误等。 2. 检查编程错误:查看代码中是否有潜在的错误,例如没有初始化变量、数组越界、栈溢出等。使用调试工具(例如GDB)可以更方便地检测程序中的错误。 3. 检查中断问题HardFault中断可能是由于异常中断导致的。检查中断的优先级设置和中断服务程序是否正确。 4. 查看硬件异常:使用STM32芯片自带的硬件调试模块(例如Cortex-M3的Fault Status Register)可以查看硬件异常的状态。这些状态信息可以提供有关错误原因的线索。 5. 查看堆栈信息:查看堆栈的状态可以帮助我们了解在死机时程序执行的位置。这些信息可以通过调试工具或在程序中添加日志输出来获得。 6. 运行时错误检测:在程序中使用合适的错误检测方法,例如检测指针是否为空、检测函数返回值等,可以帮助我们在错误发生时及时发现问题。 7. 固件更新:如果已确定硬件问题和代码错误都不存在,则可能是芯片固件本身的问题。在这种情况下,更新芯片固件可能是解决问题的方法。 以上是对STM32死机HardFault进行分析的常见步骤,可以根据具体情况进行适当的调整。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值