版权声明:本文为博主原创文章,未经博主允许不得转载。https://blog.csdn.net/u013184273/article/details/84440177
在编写STM32程序代码时由于自己的粗心会发现有时候程序跑着跑着就进入了HardFault_Handler中断,导致异常的原因很多,例如:直接使用未分配空间的指针、栈溢出等一场非法操作便会使程序进入HardFault异常状态。按照经验来说进入HardFault_Handler故障的原因主要有两个方面
1:内存溢出或则访问越界。
2:堆栈溢出。
发生异常后我们可以首先查看LR寄存器的值,确认当前使用的堆栈是MSP还是PSP,然后找到相对应的堆栈指针,并在内存中查看相对应堆栈的内容,内核将R0~R3,R12,LR,PC,XPRS寄存器依次入栈,其中LR即为发生异常前PC将要执行的下一条指令地址。
那么Cortex-M3 内核HardFault错误调试定位方法有:
方法1 如何精确定位出问题代码的所在位置:
以访问越界为例:(对STM32F103C8T6内部flash模拟EEPROM)
#define STM32_FLASH_SIZE 64
#define STM32_FLASH_WREN 1
#define FLASH_SAVE_ADDR 0X08078000
#define FLASH_HIS_ADDR 0X08078002
...
FLASH_SAVE_ADDR是开始存储的基地址,STM32F103C8T6内部flash大小是64K,在STM32的内部闪存(FLASH)地址起始于0x08000000,一般情况下,程序就从此地址开始写入。因此STM32F103C8T6的结束地址应该是64*1024转换成16进制后加上单片机flash的基地址得到的结果就是0x08010000,那么以上代码设置了FLASH_SAVE_ADDR为0X08078000已经超出了该单片机的范围,因此如果在用此单片机操作flash是如果对这个地址进行写和读的会发生错误。现在假设你在不知情的状况下对这个地址进行了操作,然后程序运行时进入
HardFault_Handler中断中。那么要找出错误代码在哪个地方,可以使用以下方法(调试软件MDK):
1:进入调试调试界面在HardFault_Handler的while(1)处打上断点。
3:等待代码运行到此,这时查看LR寄存器的
如果是正常运行那么显示的寄存器类似下图所示:
如果进入HardFault_Handler中断,那么显示的寄存器如下图所示:
发生异常之后可首先查看LR寄存器中的值,确定当前使用堆栈为MSP或PSP,然后找到相应堆栈的指针,并在内存中查看相应堆栈里的内容。
在Cortex_M3权威指南中可以看到如下图所示:
由这张图可见,这个按位或的作用是把R14寄存器的低4位设为D,在这个异常返回后进入线程模式,使用线程堆栈PSP,因为任务运行时要确保使用的是线程模式,只有发生中断或异常时,才让系统进入Handle模式并使用MSP。在xPortPendSVHandler里之所以没有这一行,是因为在进入这个异常前,系统正在跑任务,使用的就是线程模式和PSP,进入异常后变成Handle模式和MSP,但在异常返回时会自动回到这个异常发生前的模式也就是线程模式与PSP。在vPortSVCHandler这个函数被调用之前,系统一直是处于Handle模式并使用MSP的。(因为复位后就是Handle模式,因此大部分没用上系统的STM32的工程,都是让STM32处于Handle这个最高模式下运行的。)
看到LR寄存器中的值是0xFFFFFFF9,因此我应该去看MSP的地址,找到该地址的地址然后如下图所示打开内存,输入上面找到的寄存器地址,右键选择以long型查看地址如下所示:
然后查看这个地址向下数六个long地址,为什么是6个long地址呢,因为由于异常发生时,内核将R0~R3、R12、Returnaddress、PSR、LR寄存器依次入栈,其中Return address即为发生异常前PC将要执行的下一条指令地址;大概是0x08xxxxxx这样开始的即为出错的代码位置,然后可以反汇编查看,如下图所示:
可以看到是对应的C语言程序是在读FLASH函数中发生了错误,因此可以判断为访问越界的问题。
方法2:最简单,最明显
在调试状态下,当进入HardFault断点后,菜单栏Peripherals >Core Peripherals >FaultReports打开异常发生的报告,查看发生异常的原因。
跳出如下表格:
上面的报告发生了BUS FAULT,并将Fault的中断服务转向Hard Fault
相对于检测发生了什么异常,定位异常发生位置显得更重要。
(1)打开Call Stack窗口(如下图,断点停在Hard Fault服务程序中)
(2)在Call Stack的HardFault_Handler上右键Show CallerCode
这时将跳转到发生异常的源代码位置(如上图),即发生错误的地方在读FLASH处,可以想到应该是刚刚设置的FLASH地址越界问题
方法3:此方法和方法一大致相同
下面介绍怎么找出程序中的异常。
接下来在keil_MDK工程中,编译代码,并debug,之后全速运行,可以看到如下图所示程序进入HardFault异常。
如下所示我们找到SP寄存器,0x200045B8即为栈地址,栈里面的值依次为R0~R3、R12、PC(Return address)、xPSR(CPSR或SPSR)、LR。如图我们看到划红线的地方,注意从右往左看。分别为0x0800427D和0x08004BFA。
在show code at address中输入0x08004BFA,点击go to即找到出现异常的代码段附近下面要执行的程序。
我们用同样的方法在show code at address中输入0x0800427D,找到如下代码段
可以发现异常代码就在uart_send_noackdata这个函数里,这个函数里我们定义了一个指针,没有给他分配空间便开始使用了。由此我们掌握了第一种查找异常的方法。只要记录栈里面第21~24以及25到28字节的内容即可方便的找到异常代码。下面介绍使用.map文件查找异常。.map文件在keil工程里面随着程序的编译会自动生成。
、
在.map文件里我们查找0x08004BFA,找到了0x08004bd8指示是uart_send_noackdata函数,到此我们找到了异常代码所在的位置。
由此我们知道我们只要找到栈里面PC(Return address)、xPSR(CPSR或SPSR)寄存器里的内存地址便可以找到异常代码。
CPSR
当前程序状态寄存器 (Current Program State Register)
SPSR
保存的程序状态寄存器 (Saved Program State Register), 有6个,主要是在处理异常的时候使用.
每一种处理器模式下都有一个专用的物理寄存器作为备份的程序状态寄存器SPSR , 当特定的异常发生时,这个物理寄存器负责保存CPSR当前程序状态寄存器的内容, 当异常处理程序返回时,再将内容恢复到当前程序状态器中,继续向下执行原来程序.
PC
程序计数器,是用来计数的,指示指令在存储器的存放位置,也就是个地址信息.