应对STM32Cortex-M3HardFault异常

上传人:平*** 文档编号:10675901 上传时间:2017-10-10 格式:DOCX 页数:10 大小:1.20MB
返回 下载 相关 举报
应对STM32Cortex-M3HardFault异常_第1页
第1页 / 共10页
应对STM32Cortex-M3HardFault异常_第2页
第2页 / 共10页
应对STM32Cortex-M3HardFault异常_第3页
第3页 / 共10页
应对STM32Cortex-M3HardFault异常_第4页
第4页 / 共10页
应对STM32Cortex-M3HardFault异常_第5页
第5页 / 共10页
点击查看更多>>
资源描述

《应对STM32Cortex-M3HardFault异常》由会员分享,可在线阅读,更多相关《应对STM32Cortex-M3HardFault异常(10页珍藏版)》请在金锄头文库上搜索。

1、STM32 Cortex-M3 Hard FaultHard fault (硬错误,也有译为硬件错误的 )是在 STM32(如无特别说明,这里的 STM32指的是 Cortex-M3 的核)上编写程序中所产生的错误,造成 Hard Fault 错误的原因也是最为纷繁复杂的。由于能导致该错误的原因很多,所以一但出现,比较难找到其原因。网上有很多类似的这种方法,现在我将其稍加整理,并结合我曾经遇到过的问题,详细说明。硬 fault 是总线 fault、存储器管理 fault 以及用法 fault 上访的结果。如果这些 fault 的服务例程无法执行,它们就会成为“硬伤”上访(escalation)

2、成硬 fault。另外,在取向量(异常处理是对异常向量表的读取)时产生的总线 fault,也按硬 fault 处理。在 NVIC 中有一个硬 fault 状态寄存器(HFSR) ,它指出产生硬 fault 的原因。如果不是由于取向量造成的,则硬 fault 服务例程必须检查其它的 fault 状态寄存器,以最终决定是谁上访的。1 寄存器描述首先查看硬故障寄存器,判别原因。对于调试故障,有个调试故障寄存器,在 0xE000ED30 处,有详细介绍,不做探讨;对于取中断发生的,有两类原因,一是在取向量过程中发生总线 fault,二是向量表偏移量设置有误。本文重点介绍位 30 所示的,上访类错误。这

3、样 Fault 类异常有了三类,用法错误,存储管理错误,总线错误。对于这些寄存器详尽的描述,见权威指南。2 确定发生错误的地方2.1 查找出错原因Cortex-M3 有双堆栈功能,在带有操作系统时,一般都会使用。在 Keil 软件使用JTAG 调试为例,系统的启动文件中,将断点打在下面 4 个地方。HardFaultExceptionB HardFaultExceptionMemManageExceptionB MemManageExceptionBusFaultExceptionB BusFaultExceptionUsageFaultExceptionB UsageFaultExcepti

4、on程序跑飞以后,就会停在上面的 4 个断点的一个地方。可以通过两种方式查找原因。第一种,在 KEIL 软件下,利用软件提供的功能查找故障原因。在点出的窗口中,可以大体确定是哪个寄存器、什么原因造成了 Hard Fault。第二种,通过在内存观察窗口,直接输入上面那些寄存器的值来确定,通过观看寄存器那个位被置 1 了,确定出错原因。2.2 确定出错地方然后查看左侧寄存器栏中 Banked 确定现在使用的是那个堆栈,MSP 或者是 PSP,确定以后,在内存查看窗口,输入堆栈的地址,以这个地址开始的 8 个 32 位数值,应该依次是 R0,R1,R2,R3,R12,R14,R15,XPSR 的数值

5、,据此判定你的堆栈地址是不是对的(有时需要考虑堆栈的增长方向) 。R14 ,R15 的地址就是我们出错的代码所在的地址 ,需要在这个地址基础上,首先偶数对齐,然后向上减去 8 个字节。需要考虑的是,在使用 MSP 的时候,有出错的地方并不一定在 R14,R15 处,而是在XPSR 往后的第二个地址处,在这个附近查找,排除故障。3 两个例子下面就我之前碰到过的,举例说明,这两个例子分析出结果后,会觉得很简单,但是查找原因的过程有点费劲。3.1 memcpy 内存拷贝函数引发总线故障寄存器中 IMPERCISERR 位,标示不精确的数据总线访问错误,权威指南中对此有详尽的说明, “或者传送的数据单

6、位尺寸不能为设备所接受,此时有可能是LDM/STM 指令造成的” 。Memcpy 函数的原因是这样的 void *memcpy(void *dest, const void *src, size_t n),其中src 是源地址,dest 是目的地址,n 是要拷贝的字节长度。 KEIL 自带的函数中并不检查这三个参数是否有效,我所开发的程序中,源地址和目的地址都在外存(外部扩展的内存,本次大小是 4M)中,假设 size 的大小是 0xFFFF FFFF,这样的数值非常的大,单纯的拷贝都需要 10 多秒。程序中定义了很多的变量都在外存,这个拷贝函数所在的任务优先级比较低,可能被中断或者其它的任务

7、打断。我调试程序的时候,首先是发生在了中断的地方,外存数组地址到了 0x21FF 2200,原来定义在 6802 1000,加起来立刻超出了外存大小。修改中断,最终确定是传入的参数 n 太大了,直接是 0xFFFF FFFF,这样 memcpy 函数会在这里陷入死循环,一直到外存耗尽,地址再增加,找不到外存地址了,然后触发 Hard Fault。3.2 滥用临界区程序中的一些关键代码,有时候需要在临界区中执行,但是临界区若使用不当,则也会造成错误。OS_ENTER_CRITICAL();。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。 。

8、。 。 。 。 。 。 。 。OS_EXIT_CRITICAL();#define OS_ENTER_CRITICAL() cpu_sr = OS_CPU_SR_Save();OS_CPU_SR_SaveMRS R0, PRIMASK ;保存全局中断标志 ; CPSID I ;关中断BX LR将全局中断标志保存到 R0 中,此时 R0 是 0,CPSID I 则执行关中断命令,此时PRIMASK 是 1。#define OS_EXIT_CRITICAL() OS_CPU_SR_Restore(cpu_sr);OS_CPU_SR_RestoreMSR PRIMASK, R0 ;恢复全局中断标志B

9、X LR将 R0 放入全部中断寄存器中,则允许所有中断了。程序中如何保护 R0 的,细看汇编发现,实际上在执行关中断后,将 R0 保存到了 sp+8 处,开中断时再取出来,这样才保证了不会被修改。STR r0,sp,#0x08tPendTimes = 0;同时,开中断, LDR r0,sp,#0x08,则从 sp+8 处取出来,保存到 R0 中。 临界区中的代码完成如下内容:netconn_write(tradeconn,g_u8TcpSendBuf,l_u32CodeSendLen,NETCONN_COPY);调用 TCPIP_APIMSG(&msg);,sys_mbox_post(mbox

10、, &msg);OSQPost(mbox-hMBox, msg)发送消息,OS_EventTaskRdy 函数修改线程的状态,使OSTCBStatPend 变为等待完毕;此时若协议栈线程优先级高于当前任务,则会触发任务调度,悬起 OSPendSV,但是由于关闭了中断,即使在调用 OS_ENTER_CRITICAL()后,也无法打开中断,故不能执行中断,任务无法切换。同理,调用 sys_arch_sem_wait(apimsg-msg.conn-op_completed, 0);,也无法阻塞自身,执行任务调度,程序在临界区里面变成了单线程在跑。一直等待代码执行完毕开中断后,悬起的软中才能执行,本

11、来应该在发送消息和等待消息处执行任务切换的,现在只能等待临界区执行完毕后,才能执行任务切换中断。此刻的PSP 是 0x2000DFAC,临界区的那段代码我们也有压栈操作,即是 0x2000 DFAC 后面的内容也是我们需要的,如下图所示。原来的内容是这样的,如下图所示:此时在 OSPendSV 中,执行如下语句MRS R0, PSP ; PSP is process stack pointerCBZ R0, OSPendSV_nosave ; SUBS R0, R0, #0x20 ; save remaining regs r4-11 on process stackSTM R0, R4-R1

12、1从 PSP-32 个字节处开始,保存 R4 到 R11 这 8 个寄存器 32 个字节,则原来的内容都被覆盖了,而这些内容正好是我们需要的。被修改后的截图如所示,原来的内容被改成 R4 到R11 这几个寄存器的值。其中从 0801556D 变成了 68130000,协议栈线程如下执行。msg-msg.apimsg-function(&(msg-msg.apimsg-msg);函数的地址变成了 6813 0000,而 6813 0000,是我们的外存,在这里执行代码 0x68130006 F63A07E1 DCD 0xF63A07E1 ; ? Undefined最终是这句话,触发了 Hard

13、fault。3.3 运行中记录出错位置以 3.2 为例子,进行简单的反推。启动文件中的 Hard 中断处理一般如下所示,即让程序陷入这个死循环。HardFaultException; B HardFaultException现在我们要在记录重要数据,即此刻系统的运行情况,主要包括:此刻堆栈情况、以及 R0等 8 个寄存器的值、相关 Hard 硬件寄存器的值,若是任务引发的,还要记录任务的 ID 号,因此修改这个异常处理函数。HardFaultExceptionTST LR, #4 ;将 LR 的值与 4 按位相与ITE EQ /若为 0 则是 MSP,否则是 PSPMRSEQ R0, MSP

14、MRSNE R0, PSP B hard_fault_handler_c /这个是 C 语言编写的函数void hard_fault_handler_c(unsigned int * hardfault_args) unsigned int stacked_r0,stacked_r1,stacked_r2,stacked_r3; unsigned int stacked_r12,stacked_lr, stacked_pc, stacked_psr; stacked_r0 = (unsigned long) hardfault_args0); stacked_r1 = (unsigned lo

15、ng) hardfault_args1); stacked_r2 = (unsigned long) hardfault_args2); stacked_r3 = (unsigned long) hardfault_args3); stacked_r12 = (unsigned long) hardfault_args4); stacked_lr = (unsigned long) hardfault_args5); stacked_pc = (unsigned long) hardfault_args6); stacked_psr = (unsigned long) hardfault_args7); sprintf(char*)g_cDataBuf,Hard fault handlern); Usart232SendStr(g_cDataBuf);sprintf(char*)g_cDataBuf,The task pri id = 0x%0.8xn, OSPrioCur); /任务 ID 号Usart232SendStr(g_cDataBuf);sprintf(char*)g_cDataBuf,SP =

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 其它办公文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号