一、大多是情况下hardfault_handler处理硬件错误中断的解决办法

1.60%硬件中断由内存越界/堆栈溢出引发,需优先检查动态内存分配和任务堆栈配置

2.检查是否有堆栈溢出的情况,可以通过增加堆栈大小或者减少函数调用深度来解决。

3.检查是否有中断优先级设置不当的情况,可以通过调整中断优先级来解决。

4.检查代码中是否有指针未初始化、数组越界访问和内存未释放的情况。

5. 电源不稳定:电源电压波动、过压或欠压可能导致STM32工作异常,触发中断错误。

二、寄存器R0-R15的含义

R0-R3 :保存函数参数或者返回值

R4-R11:保存局部变量

R13:SP(堆栈指针,保存当前堆栈顶部的地址)

R14:PC(程序计数器),用于保存指令指针,这个寄存器中保存的地址处的指针将要执行,当该指令执行完成后,PC会自动递增指向下一条指令的地址。

R15:LR(链接寄存器),LR会自动保存下一条指令的地址,以便子程序结束后能正确返回。

三、硬件中断错误找到错误点

3.1 方法一 :

1、在硬件中断处点击断点

2、打开Call Stack Window

3、打开Disassembly Window

4、右击空白处,输入遇到硬件中断前地址,即可知道错误原因

 

程序就会自动跳转硬件中断错误处

​​​​​​ 

3.2 方法二:

1.1 在硬件中断函数HardFault_Handler里的while(1)处打调试断点,全速运行

1.2 在Keil菜单栏点击“View”——“Registers Window”,在寄存器查看窗口查找R14(LR)的值。

如果R14(LR) = 0xFFFFFFE9,继续查看MSP(主堆栈指针)的值,如果R14(LR) = 0xFFFFFFFD,继续查看PSP(进程栈指针)的值(也可以通过查看MSP指针的值,是否依次和R0,R1,R2....对应,如果对应就是查看MSP,如果不对应就是查看PSP)

1.3 在Keil菜单栏点击“View”——“Memory Windows”——“Memory1”,在“Address”地址栏中输入MSP的值:0x20001288,然后在对应的行里找到地址。地址一般以0x08开头的32位数。(找到第7个32位的数,即PC指针指向的值)

PC(程序计数器),用于保存指令指针,这个寄存器中保存的地址处的指针将要执行,当该指令执行完成后,PC会自动递增指向下一条指令的地址。(在此时,由于出现硬件中断错误,所以PC指向的就是导致进入硬件中断的代码) 

LR(链接寄存器),LR会自动保存下一条指令的地址,以便子程序结束后能正确返回。(在文章中,LR指向的就是退出该函数后的下一行地址,所以错误就在上一个函数里面)

1.4 在Keil菜单栏点击“View”——“Disassembly Window”,在“Disassembly”窗口中右击,在下拉菜单中选择“Show Disassemblyat Address...”。在弹出框“Show Code atAdress”的地址框中输入地址0x0801C0E2进行搜索,然后就会找到相对应的代码。这里的代码就是进入循环中断之前的情况。仔细查看附近区域的相关代码来排查错误具体原因。

3.3 方法三:

使用库:CmBacktrace-master,backtrace就是回溯堆栈,简单的说逆向追踪错误发生时的函数调用链,定位故障源头

项目首页 - CmBacktrace:Advanced fault backtrace library for ARM Cortex-M series MCU | ARM Cortex-M 系列 MCU 错误追踪库 - GitCode

第一步: 加入cm_backtrace文件夹和对应路径

第二步:修改cmd_cfg.h文件(注意:重定义打印函数时不能使用RTT,他会识别不到部分命令行,需要采用C语言底层的printf函数)


#ifndef _CMB_CFG_H_
#define _CMB_CFG_H_

#ifdef	CMB_USER_CFG
#include "cmb_user_cfg.h"
#else
/* 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 or CMB_OS_PLATFORM_THREADX */
/* cpu platform type, must config by user */
#define CMB_CPU_PLATFORM_TYPE        CMB_CPU_ARM_CORTEX_M3  /* 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/*(default)  or CMB_PRINT_LANGUAGE_CHINESE or CMB_PRINT_LANGUAGE_CHINESE_UTF8 */
#endif

#endif /* _CMB_CFG_H_ */

第三步:屏蔽HardFault_Handler函数 

第四步:添加初始化函数

#include <cm_backtrace.h>

#define HARDWARE_VERSION               "V1.0.0"
#define SOFTWARE_VERSION               "V0.1.0"

/* CmBacktrace initialize */
    cm_backtrace_init("CmBacktrace", HARDWARE_VERSION, SOFTWARE_VERSION);

第五步:人为制造硬件中断错误

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);
}

第六步:操作系统是FreeRTOS,则移植时需要修改FreeRTOS源码

修改一:task.c任务后面添加

/*-----------------------------------------------------------*/
/*< Support For CmBacktrace >*///lh 
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;
}
/*-----------------------------------------------------------*/

 修改二:

找到task.c任务中的 typedef struct tskTaskControlBlock 结构体,添加下面的代码到 第271和272行

#else		//lh 
		UBaseType_t     uxSizeOfStack;      /*< Support For CmBacktrace >*/

修改三:找到在task.c的prvInitialiseNewTask函数,将下面代码添加到第865行

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

第7步:下载代码运行

第8步:安装addr2line命令,找到axf的文件地址,在该文件路径下打开中断,运行下面指令

addr2line -e APP.axf -afpiC 0800cb7a 08007e9c 08000240 080017e2

四、针对博主出来问题解决办法

移植freeRTOS之后,会出现SysTick_Handler中断函数的重复定义,此时我们一般会屏蔽我们自己写的SysTick_Handler中断处理函数,因为FreeRTOS的SysTick_Handler中断处理函数用于任务的调度。

然而此时我们还在主函数里面调用delay_ms函数,它底层采用的也是systick中断,但是FreeRTOS也会用systick中断,此时两者会打架,导致硬件中断,解决办法1,FreeRTOS使用定时器提供时基,delay_ms()使用systick中断.解决办法2:使用FreeRTOS的延时函数

五、如何判断硬件中断错误原因是不是堆栈溢出

(1)栈的增长方向,举个例子,栈顶地址为0x20001e30,栈空间大小为0x400,其使用情况和增长方向如图:

(2) 查看栈空间使用情况:双击工程名字查看*.map文件,查找栈顶地址__initial_sp为0x20001e30,栈的增长方式是向下增长的(由高地址到底地址),设置栈空间大小是0x400,则sp指向的栈底地址为0x20001a30。

(3) 人为模拟堆栈溢出错误,仿真进入硬件中断,且此时MSP或者PSP的值不在栈顶和栈底地址区间范围内,即堆栈溢出,如果进入硬件中断错误时不是此现象,说明不是堆栈溢出。

    int arr[100000];
	for(int i=0;i<100000;i++)
	{
		arr[i] = i;
	}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐