完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
STM32出现HardFault_Handler故障的原因主要有两个方面:
1、内存溢出或者访问越界。这个需要自己写程序的时候规范代码,遇到了需要慢慢排查。 2、堆栈溢出。增加堆栈的大小。 出现问题时排查的方法: 1、发生异常之后可首先查看LR寄存器中的值,确定当前使用堆栈为MSP或PSP,然后找到相应堆栈的指针,并在内存中查看相应堆栈里的内容。由于异常发生时,内核将R0~R3、R12、Return address、PSR、LR寄存器依次入栈,其中Return address即为发生异常前PC将要执行的下一条指令地址,因此在堆栈中反数第三个字即为出错位置。 2、默认的HardFault_Handler处理方法是B .将它改成BX LR直接返回的形式。然后在这条语句打个断点,一旦在断点中停下来,说明出错了,然后再返回,就可以返回到出错的位置的下一条语句那儿。 这个有时候可能需要在反汇编模式下调试,因为可以是程序跑飞一会儿才出现HardFault_Handler。 3、还是将中断函数修改,打印中断时的一些信息: HardFault_Hander()定义如下: void HardFault_Handler(void) { uint32_t r_sp ; r_sp = __get_PSP(); //获取SP的值 PERROR(ERROR,Memory Access Error!); Panic(r_sp); while (1); } 死机过程 基本概念: 连接寄存器LR:调动子程序时,自动存储下一次返回的地址,其实就是最近调用的那一次函数的地址。 死机的过程: 这里我们最需要关注的是第一步入栈: 其中8个寄存器的顺序是 其中出现异常时LR里面的值是固定的 最后更新堆栈指针,我们根据最后使用的堆栈里面的内容,就可以知道出问题时的最后现场。 死机定位 思路简单来讲就是: 找到死机时候的lr寄存器,然后根据lr寄存器的值,找到此时压入的是psp堆栈,还是msp堆栈。然后根据堆栈里的内容(最后压入堆栈的8个寄存器的值)。其中压入到堆栈里面的return address这个值至关重要,这个是上一次,压入堆栈的最后一个函数,由此就可以定位出死机的位置。 使用keil环境直接debug定位 以实际的例子分析: 首先写一个能够使单片机死机的代码,debug跑起来: 故意使用空指针将程序跑死 debug的时候点开view,选择register, 根据LR的值判断,使用的是psp堆栈,然后打开memory windows,查看地址为0x20002FF0的内存数据,即为最后一次入栈的内容。右键选择,long显示 找到第六个0x08034a17这个地址,view,disassembly window这个窗口,查看反汇编文件,在反汇编窗口,右键,点击show disassembly at address ,输入地址,就可以找到对应汇编文件的位置,同时可以定位到c语言中对应的位置。 这样就可以定位到,死机之前的位置 2. 还有一种方式就是,通过看函数的调用关系,直接看到程序是死在那里的 点击 view call stack window ,直接可以看到,程序死机之前的函数调用关系 这样也可快速定位,原理其实是一样的,都是通过看堆栈数据,第一种方法只是自己手动的走了一遍这个流程而已。 完善的解决方案 因为在实际项目中死机问题很多都是难复现的,产品在使用过程中难以长期debug,导致以上的办法实际上不是很实用。因此,我们需要更加方便的解决办法。 思路:相当于给单片机做一个黑盒子,每当系统崩溃时。我们会记录,重要的寄存器以及堆栈信息,存在flash里面。这样一旦出现问题。我们只要重新debug一下,从flash里面将保存的信息读出来。这样难复现的死机问题,我们也是有办法锁定位置的。
IMPORT hard_fault_handler_c TST LR, #4 ITE EQ MRSEQ R0, MSP MRSNE R0, PSP B hard_fault_handler_c ENDP 选择一块区域作为死机时,信息存储的地方。 #define ADDR_FLASH_SECTOR_2 ((u32)0x08008000) //扇区2起始地址, 16 Kbytes #define SYS_CRASH_INFO_ADDR ADDR_FLASH_SECTOR_2 //记录系统死机时的重要信息 1 2 定义死机存储信息的结构体 typedef struct { unsigned int crash_time; unsigned int is_crash; /* register info*/ unsigned long stacked_r0; unsigned long stacked_r1; unsigned long stacked_r2; unsigned long stacked_r3; unsigned long stacked_r12; unsigned long stacked_lr; unsigned long stacked_pc; unsigned long stacked_psr; unsigned long SHCSR; unsigned long MFSR; unsigned long BFSR; unsigned long UFSR; unsigned long HFSR; unsigned long DFSR; unsigned long MMAR; unsigned long BFAR; } System_Crash_Info; void hard_fault_handler_c(unsigned int * hardfault_args) { static System_Crash_Info crash_info; memset(&crash_info, 0, sizeof(System_Crash_Info)); crash_info.is_crash = 1; crash_info.crash_time = (unsigned int)HAL_GetTick(); crash_info.stacked_r0 = ((unsigned long) hardfault_args[0]); crash_info.stacked_r1 = ((unsigned long) hardfault_args[1]); crash_info.stacked_r2 = ((unsigned long) hardfault_args[2]); crash_info.stacked_r3 = ((unsigned long) hardfault_args[3]); crash_info.stacked_r12 = ((unsigned long) hardfault_args[4]); crash_info.stacked_lr = ((unsigned long) hardfault_args[5]); crash_info.stacked_pc = ((unsigned long) hardfault_args[6]); crash_info.stacked_psr = ((unsigned long) hardfault_args[7]); crash_info.MFSR = (*((volatile unsigned char *)(0xE000ED28))); //存储器管理fault状态寄存器 crash_info.BFSR = (*((volatile unsigned char *)(0xE000ED29))); //总线fault状态寄存器 crash_info.UFSR = (*((volatile unsigned short int *)(0xE000ED2A)));//用法fault状态寄存器 crash_info.HFSR = (*((volatile unsigned long *)(0xE000ED2C))); //硬fault状态寄存器 crash_info.DFSR = (*((volatile unsigned long *)(0xE000ED30))); //调试fault状态寄存器 crash_info.MMAR = (*((volatile unsigned long *)(0xE000ED34))); //存储管理地址寄存器 crash_info.BFAR = (*((volatile unsigned long *)(0xE000ED38))); //总线fault地址寄存器 u8 ret = STMFLASH_EraseSector(STMFLASH_GetFlashSector(SYS_CRASH_INFO_ADDR)); u8 ret2 = STMFLASH_Write(SYS_CRASH_INFO_ADDR, (u32 *)(&crash_info), (3+sizeof(System_Crash_Info))/4); while (1); } 正常代码里加上 System_Crash_Info crash_info = {0}; crash_info = *(System_Crash_Info*)(SYS_CRASH_INFO_ADDR); if (crash_info.is_crash != 1) { STMFLASH_EraseSector(STMFLASH_GetFlashSector(SYS_CRASH_INFO_ADDR)); } else { CT_PRINTF("code has ever crashn"); //这里查看之前死掉时的情况,或者是将crash_info 里的信息打印出来分析也可以 } 实际的例子: 这样代码重新debug,就可以看到之前死机的情况了。直接看lr寄存器的值,在反汇编的代码里查看一下,就知道代码最后死在哪里了。 |
|
|
|
更多的是,程序上对内存的不正确访问导致的,或是硬件上有问题,如晶振之类的。
|
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1632 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1559 浏览 1 评论
985 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
688 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1605 浏览 2 评论
1869浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
652浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
521浏览 3评论
539浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
508浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-25 09:19 , Processed in 0.881667 second(s), Total 78, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号