应对STM32 CortexM3 HardFault异常Word格式文档下载.docx

上传人:b****1 文档编号:377286 上传时间:2023-04-28 格式:DOCX 页数:15 大小:1.18MB
下载 相关 举报
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第1页
第1页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第2页
第2页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第3页
第3页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第4页
第4页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第5页
第5页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第6页
第6页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第7页
第7页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第8页
第8页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第9页
第9页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第10页
第10页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第11页
第11页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第12页
第12页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第13页
第13页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第14页
第14页 / 共15页
应对STM32 CortexM3 HardFault异常Word格式文档下载.docx_第15页
第15页 / 共15页
亲,该文档总共15页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

应对STM32 CortexM3 HardFault异常Word格式文档下载.docx

《应对STM32 CortexM3 HardFault异常Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《应对STM32 CortexM3 HardFault异常Word格式文档下载.docx(15页珍藏版)》请在冰点文库上搜索。

应对STM32 CortexM3 HardFault异常Word格式文档下载.docx

程序跑飞以后,就会停在上面的4个断点的一个地方。

可以通过两种方式查找原因。

第一种,在KEIL软件下,利用软件提供的功能查找故障原因。

在点出的窗口中,可以大体确定是哪个寄存器、什么原因造成了HardFault。

第二种,通过在内存观察窗口,直接输入上面那些寄存器的值来确定,通过观看寄存器那个位被置1了,确定出错原因。

2.2确定出错地方

然后查看左侧寄存器栏中Banked确定现在使用的是那个堆栈,MSP或者是PSP,确定以后,在内存查看窗口,输入堆栈的地址,以这个地址开始的8个32位数值,应该依次是R0,R1,R2,R3,R12,R14,R15,XPSR的数值,据此判定你的堆栈地址是不是对的(有时需要考虑堆栈的增长方向)。

R14,R15的地址就是我们出错的代码所在的地址,需要在这个地址基础上,首先偶数对齐,然后向上减去8个字节。

需要考虑的是,在使用MSP的时候,有出错的地方并不一定在R14,R15处,而是在XPSR往后的第二个地址处,在这个附近查找,排除故障。

3两个例子

下面就我之前碰到过的,举例说明,这两个例子分析出结果后,会觉得很简单,但是查找原因的过程有点费劲。

3.1memcpy内存拷贝函数引发

总线故障寄存器中IMPERCISERR位,标示不精确的数据总线访问错误,权威指南中对此有详尽的说明,“或者传送的数据单位尺寸不能为设备所接受,此时有可能是LDM/STM指令造成的”。

Memcpy函数的原因是这样的void*memcpy(void*dest,constvoid*src,size_tn),其中src是源地址,dest是目的地址,n是要拷贝的字节长度。

KEIL自带的函数中并不检查这三个参数是否有效,我所开发的程序中,源地址和目的地址都在外存(外部扩展的内存,本次大小是4M)中,假设size的大小是0xFFFFFFFF,这样的数值非常的大,单纯的拷贝都需要10多秒。

程序中定义了很多的变量都在外存,这个拷贝函数所在的任务优先级比较低,可能被中断或者其它的任务打断。

我调试程序的时候,首先是发生在了中断的地方,外存数组地址到了0x21FF2200,原来定义在68021000,加起来立刻超出了外存大小。

修改中断,最终确定是传入的参数n太大了,直接是0xFFFFFFFF,这样memcpy函数会在这里陷入死循环,一直到外存耗尽,地址再增加,找不到外存地址了,然后触发HardFault。

3.2滥用临界区

程序中的一些关键代码,有时候需要在临界区中执行,但是临界区若使用不当,则也会造成错误。

OS_ENTER_CRITICAL();

OS_EXIT_CRITICAL();

#defineOS_ENTER_CRITICAL(){cpu_sr=OS_CPU_SR_Save();

}

OS_CPU_SR_Save

MRSR0,PRIMASK;

保存全局中断标志;

CPSIDI;

关中断

BXLR

将全局中断标志保存到R0中,此时R0是0,CPSIDI则执行关中断命令,此时PRIMASK是1。

#defineOS_EXIT_CRITICAL(){OS_CPU_SR_Restore(cpu_sr);

}

OS_CPU_SR_Restore

MSRPRIMASK,R0;

恢复全局中断标志

将R0放入全部中断寄存器中,则允许所有中断了。

程序中如何保护R0的,细看汇编发现,实际上在执行关中断后,将R0保存到了sp+8处,开中断时再取出来,这样才保证了不会被修改。

STRr0,[sp,#0x08]tPendTimes=0;

同时,开中断,LDRr0,[sp,#0x08],则从sp+8处取出来,保存到R0中。

临界区中的代码完成如下内容:

netconn_write(tradeconn,g_u8TcpSendBuf,l_u32CodeSendLen,NETCONN_COPY);

调用TCPIP_APIMSG(&

msg);

sys_mbox_post(mbox,&

OSQPost(mbox->

hMBox,msg)发送消息,OS_EventTaskRdy函数修改线程的状态,使OSTCBStatPend变为等待完毕;

此时若协议栈线程优先级高于当前任务,则会触发任务调度,悬起OSPendSV,但是由于关闭了中断,即使在调用OS_ENTER_CRITICAL()后,也无法打开中断,故不能执行中断,任务无法切换。

同理,调用sys_arch_sem_wait(apimsg->

msg.conn->

op_completed,0);

,也无法阻塞自身,执行任务调度,程序在临界区里面变成了单线程在跑。

一直等待代码执行完毕开中断后,悬起的软中才能执行,本来应该在发送消息和等待消息处执行任务切换的,现在只能等待临界区执行完毕后,才能执行任务切换中断。

此刻的PSP是0x2000DFAC,临界区的那段代码我们也有压栈操作,即是0x2000DFAC后面的内容也是我们需要的,如下图所示。

原来的内容是这样的,如下图所示:

此时在OSPendSV中,执行如下语句

MRSR0,PSP;

PSPisprocessstackpointer

CBZR0,OSPendSV_nosave;

SUBSR0,R0,#0x20;

saveremainingregsr4-11onprocessstack

STMR0,{R4-R11}

从PSP-32个字节处开始,保存R4到R11这8个寄存器32个字节,则原来的内容都被覆盖了,而这些内容正好是我们需要的。

被修改后的截图如所示,原来的内容被改成R4到R11这几个寄存器的值。

其中从0801556D变成了68130000,协议栈线程如下执行。

msg->

msg.apimsg->

function(&

(msg->

msg));

函数的地址变成了68130000,而68130000,是我们的外存,

在这里执行代码0x68130006F63A07E1DCD0xF63A07E1;

?

Undefined

最终是这句话,触发了Hardfault。

3.3运行中记录出错位置

以3.2为例子,进行简单的反推。

启动文件中的Hard中断处理一般如下所示,即让程序陷入这个死循环。

;

现在我们要在记录重要数据,即此刻系统的运行情况,主要包括:

此刻堆栈情况、以及R0等8个寄存器的值、相关Hard硬件寄存器的值,若是任务引发的,还要记录任务的ID号,因此修改这个异常处理函数。

TSTLR,#4;

将LR的值与4按位相与

ITEEQ//若为0则是MSP,否则是PSP

MRSEQR0,MSP

MRSNER0,PSP

Bhard_fault_handler_c//这个是C语言编写的函数

voidhard_fault_handler_c(unsignedint*hardfault_args)

{

unsignedintstacked_r0,stacked_r1,stacked_r2,stacked_r3;

unsignedintstacked_r12,stacked_lr,stacked_pc,stacked_psr;

stacked_r0=((unsignedlong)hardfault_args[0]);

stacked_r1=((unsignedlong)hardfault_args[1]);

stacked_r2=((unsignedlong)hardfault_args[2]);

stacked_r3=((unsignedlong)hardfault_args[3]);

stacked_r12=((unsignedlong)hardfault_args[4]);

stacked_lr=((unsignedlong)hardfault_args[5]);

stacked_pc=((unsignedlong)hardfault_args[6]);

stacked_psr=((unsignedlong)hardfault_args[7]);

sprintf((char*)g_cDataBuf,"

[Hardfaulthandler]\n"

);

Usart232SendStr(g_cDataBuf);

Thetaskpriid=0x%0.8x\n"

OSPrioCur);

//任务ID号

SP=0x%0.8x\n"

hardfault_args);

//堆栈地址

R0=0x%0.8x\n"

stacked_r0);

R1=0x%0.8x\n"

stacked_r1);

R2=0x%0.8x\n"

stacked_r2);

R3=0x%0.8x\n"

stacked_r3);

R12=0x%0.8x\n"

stacked_r12);

LR=0x%0.8x\n"

stacked_lr);

PC=0x%0.8x\n"

stacked_pc);

PSR=0x%0.8x\n"

stacked_psr);

exit(0);

//terminate

return;

以3.2为例,发生异常后,串口的输出入下所示:

[Hardfaulthandler]

Thetaskpriid=0x00000014//任务优先级是20

SP=0x200077d8//当前任务的堆栈地址是0x200077D8

R0=0x2000dfa0

R1=0x68130000

R2=0x2000df9c

R3=0x20002100

R12=0x00000001

LR=0x0801c7fb//分析得出,这个地址就是出错的地方

PC=0x68130000

PSR=0x00000000

此时需要借助map文件分析,map文件中得出对应的代码和数据位置。

tcpip_thread0x0801c7adThumbCode190tcpip.o(i.tcpip_thread)

i.tcpsvr_accept_200x0801c874Section64ftpmanage.o(i.tcpsvr_accept_20)

0x0801c7fb应该在tcpip文件中的tciip_thread函数里。

T_LWIP_THREAD_STK0x20007000Data2048sys_arch.o(.bss)

rsuPib0x20007800Data32para.o(.bss)

堆栈空间是0x200077D8,是在T_LWIP_THREAD_STK这个栈空间里,这也是协议栈任务的堆栈空间,证明判断的任务优先级为20是正确的。

从0x0801C7AD处开始的16进制文件如下图所示,再将汇编文件列出(需要KeiL生成)。

tcpip_threadPROC

232staticvoid

233tcpip_thread(void*arg)

000000b508PUSH{r3,lr}//开始

234{

235structtcpip_msg*msg;

236LWIP_UNUSED_ARG(arg);

237

238#ifIP_REASSEMBLY

239sys_timeout(IP_TMR_INTERVAL,ip_reass_timer,NULL);

240#endif/*IP_REASSEMBLY*/

241#ifLWIP_ARP

242sys_timeout(ARP_TMR_INTERVAL,arp_timer,NULL);

0000022200MOVSr2,#0

000004492eLDRr1,|L11.192|

000006f2413088MOVr0,#0x1388

00000af7fffffeBLsys_timeout

243#endif/*LWIP_ARP*/

244#ifLWIP_DHCP

245sys_timeout(DHCP_COARSE_TIMER_MSECS,dhcp_timer_coarse,NULL);

246sys_timeout(DHCP_FINE_TIMER_MSECS,dhcp_timer_fine,NULL);

247#endif/*LWIP_DHCP*/

248#ifLWIP_AUTOIP

249sys_timeout(AUTOIP_TMR_INTERVAL,autoip_timer,NULL);

250#endif/*LWIP_AUTOIP*/

251#ifLWIP_IGMP

252sys_timeout(IGMP_TMR_INTERVAL,igmp_timer,NULL);

253#endif/*LWIP_IGMP*/

254#ifLWIP_DNS

255sys_timeout(DNS_TMR_INTERVAL,dns_timer,NULL);

256#endif/*LWIP_DNS*/

257

258if(tcpip_init_done!

=NULL){

00000e482dLDRr0,|L11.196|

0000106800LDRr0,[r0,#0];

tcpip_init_done

000012b128CBZr0,|L11.32|

259tcpip_init_done(tcpip_init_done_arg);

000014482bLDRr0,|L11.196|

0000161d00ADDSr0,r0,#4

0000186800LDRr0,[r0,#0];

tcpip_init_done_arg

00001a492aLDRr1,|L11.196|

00001c6809LDRr1,[r1,#0];

00001e4788BLXr1

|L11.32|

260}

261

262LOCK_TCPIP_CORE();

263while

(1){/*MAINLoop*/

000020e04cB|L11.188|

|L11.34|

264sys_mbox_fetch(mbox,(void*)&

0000224669MOVr1,sp

0000244827LDRr0,|L11.196|

0000261f00SUBSr0,r0,#4

0000286800LDRr0,[r0,#0];

mbox

00002af7fffffeBLsys_mbox_fetch

265switch(msg->

type){

00002e9800LDRr0,[sp,#0]

0000307800LDRBr0,[r0,#0]

0000322805CMPr0,#5

000034d240BCS|L11.184|

000036e8dff000TBB[pc,r0]

00003a030bDCB0x03,0x0b

00003c222b3500DCB0x22,0x2b,0x35,0x00

266#ifLWIP_NETCONN

267caseTCPIP_MSG_API:

268//if(msg->

msg.conn==NULL)

269//break;

270LWIP_DEBUGF(TCPIP_DEBUG,("

tcpip_thread:

APImessage%p\n"

(void*)msg));

271msg->

0000409a00LDRr2,[sp,#0]

0000426892LDRr2,[r2,#8]

0000441d10ADDSr0,r2,#4

0000469a00LDRr2,[sp,#0]

0000486892LDRr2,[r2,#8]

00004a6811LDRr1,[r2,#0]

00004c4788BLXr1

272break;

00004ee034B|L11.186|//0x0801c7fb对应的代码

273#endif/*LWIP_NETCONN*/

274

275caseTCPIP_MSG_INPKT:

276LWIP_DEBUGF(TCPIP_DEBUG,("

PACKET%p\n"

277#ifLWIP_ARP

278if(msg->

if->

flags&

NETIF_FLAG_ETHARP){

0000509800LDRr0,[sp,#0]

从代码看地址对应是00004ee034B|L11.186|,即switch分支的break语句,但是实际应该是上面的那句,BLXr1,而此时R1的值是

R1=0x68130000,即跳转到68130000处执行,与在3.2的分析是一样的。

这也只能判断出出错的位置,原因还是需要仿真调试,才能找到。

3.4总结

发生HardFault以后,意味着程序跑飞了,有的原因是很简单的,但是有的需要仔细分析,以上两个例子应该都算比较简单的。

以第一个例子来说,若从首次定位来看是在中断里,中断里变量的数值太大,超出了外存的大小,但是这个值为什么会这么大?

正常中断接收数据不会变成有0x21FF2200这么多字节的,此时就需要考虑是别的地方踩到了此处的内存,导致取出来的数据一下子变成了这么大。

然后逐步定位,才能找出真正的原因。

4参考文献

网上有几篇非常不错的文章,可以看看,加深理解。

[1]Cortex-M3权威指南,JosephYiu著,宋岩译。

(在书的附录E中对Fault类异常有非常详尽的介绍)

[2]Cortex-M3技术参考手册,周立功。

(在书的P89到96页对以上所介绍的寄存器有很详细的描述)

[3]ApplicationNote209,UsingCortex-M3andCortex-M4FaultExceptions.KEILToolsbyARM.(这个是KEIL软件下使用的说明,介绍的例子可以一看)

[4]教你如何找到导致程序跑飞的指令,,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2