STM32异常定位方法

       当单片机发生程序异常时,会进入到HardFault_Handler中断,相当于windows的蓝屏,我现在介绍的是如何获取中断位置,并自动记录异常位置(我的做法是将异常的时间,与代码地址存储到备份区,这样哪怕重启了依旧可以查询上一次发生异常的位置,这部分代码需要自己去实现,我现在实现的是获取异常代码位置)。

2019-01-29 发现增加了OS后不一样,增加裸机与OS下的异常处理。

 

原理如下:当异常时,硬件会将一些CPU的寄存器保存到栈中,通过在异常时获取堆栈指针SP的值,通过SP获取当前栈的位置,然后获取异常之前的PC指针的值,就知道异常的位置了。

//这个就是CPU寄存器在栈中的排列
//栈数据定义
typedef struct
{
	u32 R0;
	u32 R1;
	u32 R2;
	u32 R3;
	u32 R12;
	u32 LR;
	u32 PC;
	u32 xPSR;
}STACK_DATA_TYPE;







//获取当前CPU堆栈指针MSP
__asm u32 getMSP(void) 
{
    mrs r0, msp
	bx lr
}



//获取当前CPU堆栈指针PSP
__asm u32 getPSP(void) 
{
    mrs r0, psp
	bx lr
}



//没有操作系统时的异常处理
void NotOSHardFault_Handler(u32 msp_addr)
{
	STACK_DATA_TYPE *p;										//堆栈中存储的数据
	
	msp_addr -= 4;											//堆栈指针减去4,因为默认堆栈指针指向的是下一个空的地方-所以必须减去4
	uart_printf_enable();									//开启串口调试信息
	uart_printf("\r\n-----------------ERROR -----------------\r\nHardFault_Handler\r\n");
	

	if((msp_addr >> 20) != 0x200)		//判断地址范围,必须是0x200xxxxx 范围
	{
		uart_printf("警告:堆栈指针被破坏,无法记录现场!\r\n");
		return;
	}
	
	
	msp_addr += 8;											//进入中断后,堆栈又进入了2个u32数据,因此需要往后推
	p = (STACK_DATA_TYPE *)msp_addr;
	p->PC -= 3;												//PC指针要减去3
	//BackupArea_RecordHardFault(RTC_GetSec(), p->PC);		//记录异常pc指针信息到备份区
	uart_printf("R0:0x%08X\r\n", p->R0);
	uart_printf("R1:0x%08X\r\n", p->R1);
	uart_printf("R2:0x%08X\r\n", p->R2);
	uart_printf("R3:0x%08X\r\n", p->R3);
	uart_printf("R12:0x%08X\r\n", p->R12);
	uart_printf("LR:0x%08X\r\n", p->LR);
	uart_printf("PC:0x%08X\r\n", p->PC);
	uart_printf("xPSR:0x%08X\r\n", p->xPSR);
	uart_printf("系统即将复位...\r\n");
}


//有操作系统时的异常处理
void OSHardFault_Handler(u32 psp_addr)
{
	STACK_DATA_TYPE *p;										//堆栈中存储的数据
	
	psp_addr -= 4;											//堆栈指针减去4,因为默认堆栈指针指向的是下一个空的地方-所以必须减去4
	uart_printf_enable();									//开启串口调试信息
	uart_printf("\r\n-----------------ERROR -----------------\r\nHardFault_Handler\r\n");
	

	if((psp_addr >> 20) != 0x200)		//判断地址范围,必须是0x200xxxxx 范围
	{
		uart_printf("警告:堆栈指针被破坏,无法记录现场!\r\n");
		return;
	}
	
	p = (STACK_DATA_TYPE *)psp_addr;
	p->PC -= 3;												//PC指针要减去3
	//BackupArea_RecordHardFault(RTC_GetSec(), p->PC);		//记录异常pc指针信息到备份区
	uart_printf("R0:0x%08X\r\n", p->R0);
	uart_printf("R1:0x%08X\r\n", p->R1);
	uart_printf("R2:0x%08X\r\n", p->R2);
	uart_printf("R3:0x%08X\r\n", p->R3);
	uart_printf("R12:0x%08X\r\n", p->R12);
	uart_printf("LR:0x%08X\r\n", p->LR);
	uart_printf("PC:0x%08X\r\n", p->PC);
	uart_printf("xPSR:0x%08X\r\n", p->xPSR);
	uart_printf("系统即将复位...\r\n");
}



//硬件中断
void HardFault_Handler (void)
{
	u32 msp_addr = getMSP();		//获取线程模式下堆栈指针位置
	u32 psp_addr = getPSP();		//获取中断下的堆栈指针位置-用于OS启动后
	
#if(UCOS_II_EN)						//使能了操作系统的一个宏定义,自己去定义
	if(SYS_GetOsStartup())			//操作系统运行了-自己定义一个状态,可以获取操作系统是否启动
	{
		OSHardFault_Handler(psp_addr);
	}
	else
	{
		NotOSHardFault_Handler(msp_addr);
	}
#else //没有使能操作系统
	NotOSHardFault_Handler(msp_addr);	
#endif //UCOS_II_EN
	
	Delay_MS(10); 					//延时一下,防止重启速度太快
	SYSTEM_SoftReset();				//复位重启	
}

可以自己将PC指针的值存储到备份区,这样可以查询到产品是否发生过异常,并且异常位置,通过代码仿真可以找到异常代码位置。

异常前CPU寄存器数据

 

异常后,通过堆栈指针获取到堆栈中的数据,存放有异常前的PC指针,也就是异常位置。

 

扫描二维码关注公众号,回复: 9213960 查看本文章

获取了PC指针就能定位位置了

串口输出的异常前CPU寄存器数据

发布了143 篇原创文章 · 获赞 370 · 访问量 81万+

猜你喜欢

转载自blog.csdn.net/cp1300/article/details/86562580