定位ARMHardFault的⽅法
1, ⽤Keil的话,可以做如下操作:
先将fault中断函数的内容改为:
HardFault_Handler\
PROC
;EXPORT HardFault_Handler [WEAK]
;B .
IMPORT hard_fault_handler_c
TST LR, #4
ITE EQ
MRSEQ R0, MSP
MRSNE R0, PSP
B hard_fault_handler_c
ENDP
************************************************inrt start*************************************************
不可救药的意思摘抄⾃Triton.zhang——eeworld
1. MSP和PSP 的含义是Main_Stack_Pointer 和Process_Stack_Pointer,在逻辑地址上他们都是R13
等爱的人
2. 权威⼿册上说的很清楚PSP主要是在Handler的模式下使⽤,MSP主要在线程模式下使⽤(当然你在线程模式下也可以调⽤PSP,需要你做特殊的处理).
3. 这意味着同⼀个逻辑地址,实际上有两个物理寄存器,⼀个为MSP,⼀个为PSP,在不同的⼯作模式调⽤不同的物理寄存器。举⼀个简单的例⼦,很多MCU的的UART只有⼀个BUFF,TXBUFF和RXBUFF都是⼀个地址,当你写BUFF时写⼊的是TXBUFF, 读操作时调⽤的是RXBUFF。基本原理就是这样。
4. ⾄于为什么这么设计,我想是为了在进⾏模式转换的时候,减少堆栈的保存⼯作。同时也可以为不同权限的⼯作模式设置不同的堆栈。************************************************inrt end***************************************************
然后在源程序⾥添加下⾯的函数代码:
// hard fault handler in C,
// with stack frame location as input parameter
void hard_fault_handler_c(unsigned int * hardfault_args)
{
unsigned int stacked_r0;
unsigned int stacked_r1;
unsigned int stacked_r2;
unsigned int stacked_r3;
unsigned int stacked_r12;
unsigned int stacked_lr;
unsigned int stacked_pc;
unsigned int stacked_psr;
stacked_r0 = ((unsigned long) hardfault_args[0]);
stacked_r1 = ((unsigned long) hardfault_args[1]);
stacked_r2 = ((unsigned long) hardfault_args[2]);
stacked_r3 = ((unsigned long) hardfault_args[3]);
stacked_r12 = ((unsigned long) hardfault_args[4]);
stacked_lr = ((unsigned long) hardfault_args[5]);
stacked_pc = ((unsigned long) hardfault_args[6]);
stacked_psr = ((unsigned long) hardfault_args[7]);
printf ("[Hard fault handler]\n");
printf ("R0 = %x\n", stacked_r0);
printf ("R1 = %x\n", stacked_r1);
printf ("R2 = %x\n", stacked_r2);
printf ("R3 = %x\n", stacked_r3);
printf ("R12 = %x\n", stacked_r12);
printf ("LR = %x\n", stacked_lr);
printf ("PC = %x\n", stacked_pc);
printf ("PSR = %x\n", stacked_psr);
printf ("BFAR = %x\n", (*((volatile unsigned long *)(0xE000ED38)))); printf ("CFSR = %x\n", (*((volati
le unsigned long *)(0xE000ED28)))); printf ("HFSR = %x\n", (*((volatile unsigned long *)(0xE000ED2C)))); printf ("DFSR = %x\n", (*((volatile unsigned long *)(0xE000ED30)))); printf ("AFSR = %x\n", (*((volatile unsigned long *)(0xE000ED3C))));
while(1)
{
;;
}
}
如果使⽤调试器,则可以在第⼀个printf处设置断点.没有的话看串⼝打印结果
通过查看stacked_lr的内容可以知道程序运⾏到哪个位置出现fault
然后查看编译后汇编代码,可以知道源程序是哪个函数哪⼀步出现问题,
配合其它寄存器的内容来分析找出原因
2.⽤IAR的话,把startup_ewarm.c⽂件中的FaultISR()函数的内容改为:
volatile unsigned int stacked_r0;
volatile unsigned int stacked_r1;
volatile unsigned int stacked_r2;
volatile unsigned int stacked_r3;华清温泉
volatile unsigned int stacked_r12;
volatile unsigned int stacked_lr;
volatile unsigned int stacked_pc;
volatile unsigned int stacked_psr;
//unsigned long cc;乌鸡的家常做法
stacked_r0 = ((unsigned long) hardfault_args[0]);
stacked_r1 = ((unsigned long) hardfault_args[1]);
stacked_r2 = ((unsigned long) hardfault_args[2]);
stacked_r3 = ((unsigned long) hardfault_args[3]);
stacked_r12 = ((unsigned long) hardfault_args[4]);
stacked_lr = ((unsigned long) hardfault_args[5]);
stacked_pc = ((unsigned long) hardfault_args[6]);
stacked_psr = ((unsigned long) hardfault_args[7]);
printf ("[Hard fault handler]\n");
printf ("R0 = %x\n", stacked_r0);
printf ("R1 = %x\n", stacked_r1);
printf ("R2 = %x\n", stacked_r2);
printf ("R3 = %x\n", stacked_r3);
printf ("R12 = %x\n", stacked_r12);
printf ("LR = %x\n", stacked_lr);
printf ("PC = %x\n", stacked_pc);
printf ("PSR = %x\n", stacked_psr);
printf ("BFAR = %x\n", (*((volatile unsigned long *)(0xE000ED38)))); printf ("CFSR = %x\n", (*((volatile unsigned long *)(0xE000ED28)))); printf ("HFSR = %x\n", (*((volatile unsigned long *)(0xE000ED2C)))); printf ("DFSR = %x\n", (*((volatile unsigned long *)(0xE000ED30)))); printf ("AFSR = %x\n", (*((volatile unsigned long *)(0xE000ED3C))));
while(1)
{
;;
}
如果使⽤调试器,则可以在第⼀个printf处设置断点.没有的话看串⼝打印结果
通过查看stacked_lr的内容可以知道程序运⾏到哪个位置出现fault
然后查看编译后汇编代码,可以知道源程序是哪个函数哪⼀步出现问题,
配合其它寄存器的内容来分析找出原因
另⼀种⽅法:
默认的HardFaudler 处理⽅法不是死循环么?将它改成BX LR直接返回的形式。然后再这条语句打个断点,⼀旦在断点中停下来,说明出错了,然后再返回,就可以返回到出错的位置的下⼀条语句哪⾥。
_asm void wait()
{
BX lr //BX⽆条件转移指令
}
void HardFault_Handler(void)
{
wait();
}
1. SWI_Exception:
2. STMFD SP!, {R2-R3,LR} //把R2,R3,LR值⼊栈
3. #0号软中断的处理程序
4. CMP R0, #0 //将R0和0⽐较
5. //以下4⾏带EQ条件的代码均为当R0为0时应该执⾏的语句
6. MRSEQ R2, SPSR //把SPSR读⼊到R2中
水土不服长痘痘怎么治7. STREQ R2, [R1] /把R2的值存⼊到[R1]中
8. ORREQ R2, R2, #0x80 //把R2的Bit7位置1
9. MSREQ SPSR_c, R2 //把R2的值写⼊到SPSR_c中,即禁⽌IRQ塔吊指挥
10. #1号软中断的处理程序
11. CMP R0, #1 //⽐较R0值和1
12. LDREQ R2, [R1] //相等则把[R1]中的数据存⼊R2中
13. MSREQ SPSR_c, R2 //相等把R2的值写⼊到SPSR_c中,恢复IRQ
14.
15. #11号软中断的处理程序
16. CMP R0, #11 //⽐较R0的值和11
17. MRSEQ R2, SPSR //相等则把SPSR的值转存⼊到R2中
hidi
18. BICEQ R2, R2, #0x1F //相等则把R2的Bit0~Bit4全部清零
19. ORREQ R2, R2, #Mode_SYS //相等则把R2与#Mode_SYS相与再存⼊R2
20. MSREQ SPSR_c, R2 //相等则把R2的值存⼊SPSR_c中,即进⼊系统模式
21. #12号软中断的处理程序
22. CMP R0, #12 //⽐较R0的值和12
23. MRSEQ R2, SPSR //相等则把SPSR的值存⼊R2
24. BICEQ R2, R2, #0x1F //相等则把R2的Bit0~Bit4清零
25. ORREQ R2, R2, #Mode_USR //相等则把R2与#Mode_USR相与再存⼊R2中教育产业化
26. MSREQ SPSR_c, R2 //相等则把R2存⼊SPSR_c,即进⼊⽤户模式
27.
28. LDMFD SP!, {R2-R3,PC} //恢复R2、R3、PC值,返回
29.
30.
31. .END //汇编代码段结束