在项目处于调试期间,Fault处理程序可能只是一个断点指令,调试器遇到这个指令后停止程序的运行。默认情况下,由于非硬Fault被禁能,所有发生的非Fault都会上访成硬Fault,因此只要在硬Fault处理程序中设置一个断点,就可以观察所有Fault信息。当使用MDK-ARM的RealView编译器时,你可以用下面的C代码替代默认硬Fault处理程序,这段代码检测产品是否连接到一个调试器,只有在连接到一个调试器的情况下,才会执行断点指令。

void HardFault_Handler (void) {
    ) { // check C_DEBUGEN == 1 -> Debugger Connected
    __breakpoint ();            // halt program execution here
}
    );                   // enter endless loop otherwise
} 
      说明一下,在这段代码中,关于这个CoreDebug->DHCSR也可以在core_cm3.h中找到;__breakpoint()函数是ARM编译器所支持的内部指令,这个函数的作用是在指令流中插入一个断点指令(BKTP)。详细可以查看编译器手册Compiler Reference Guide – Compiler-specific Features - __breakpoint。

为了使能除数为零以及未对齐内存访问产生Fault,应用程序初始化代码要设置SCB->CCR寄存器,下面的C代码清单用于使能除数为零以及未对齐内存访问产生Fault。

     SCB ->CCR |= 0x18; /* enable div-by-0 and unaligned fault*/ 

对于最终的应用程序,Fault处理程序或许会按照下面所说的实现:

  • 系统复位:通过置一SCB->AIRCR(应用程序中断和复位控制寄存器)的位2(SYSRESETREQ)。这样将会强制对调试设备之外的所有主要设备进行一次大的系统复位。如果你不想复位整个系统,你可以只置一SCB->AIRCR寄存器的位0.(注:LPC1778/88不支持这个位)
  • 恢复:在某些情况下,还是有希望解决产生Fault的问题的。例如,如果程序尝试访问了协处理器,可以通过一个协处理器的软件模拟器来解决。
  • 终止任务:如果系统运行了一个RTOS,则相关的任务可以被终结或者重新开始。

注:下面的C代码清单可以用来使能用法、存储器管理和总线Fault:

SCB ->SHCSR |= 0x00007000;     // enable Usage Fault, Bus Fault, and MMU Fault

利用串口上报Fault异常信息

在调试期间,最主要的是要弄清楚触发了哪类Fault,什么原因触发了Fault以及定位到触发Fault的代码,可以利用一份空闲串口当作调试用,将以上信息发给PC,通过串口调试助手接收这些Fault信息。

主要步骤如下:

1.如有必要,使能非硬Fault(用法、存储器管理和总线Fault)

2.如果有必要使能捕获除法为零和未对齐内存访问

3.编写Fault处理程序

4.将启动代码中默认的Fault处理程序更换成自己需要的Fault处理程序

补充一些基础知识,有利于理解下面的代码:

堆栈:

       Cortex-M3的堆栈是使用“向下生长的满栈”模型,SP指针指向最后一个被压入堆栈的32位数值。在下一次压栈时,SP先自减4,再存入新的数值。POP操作正好相反,先弹出当前SP指针处的32位数值,再将SP的值增4.

Cortex-M3的异常/中断过程:

1. 入栈:硬件自动把8个寄存器的值压入堆栈(8个寄存器依次为:xPSR、PC、LR、R12以及R3~R0)。如果异常发生时,当前的代码正在使用PSP(进程堆栈),则上面8个寄存器压入PSP;否则就压入MSP(主堆栈)。一旦进入服务例程,就将一直使用MSP。Cortex-M3内核响应中断/异常的延时固定为12个时钟周期。以上操作使用Cortex-M3的数据总线。

2.取向量:与入栈同时,Cortex-M3内核从向量表中找出正确的异常向量,然后在服务程序的入口地址欲取指。以上操作使用Cortex-M3的指令总线

3.更新寄存器:入栈和取向量操作完成后,在执行服务例程之前,还要更新一些列寄存器:

SP:在入栈操作后,会把堆栈指针(PSP或MSP)更新到新的位置。在执行中断/异常服务例程时,一定是使用MSP。

PSR:更新IPSR位段(PSR最低部分)的值为新响应的异常编号。

PC:在取向量完成后,PC将指向服务例程的入口地址。

LR:在出入ISR的时候,LR保存一些在异常返回时用到的特殊位。

寄存器:

1.通用寄存器:R0~R3、R12,C函数调用标准会使用R0~R3用来传递参数,R12(用于子程序保存SP指针,不太清楚)

2.LR:连接寄存器。LR用于在调用子程序时存储返回地址。

3.PC:程序计数器,CM3内部使用了指令流水线,读PC时返回的值是当前指令的地址+4.

4.xPSR程序状态寄存器

下面以硬Fault处理为例,介绍一下如何将Fault信息上报到PC的调试助手上。

1. 在程序初始化代码中,使能非硬Fault(使用Keil MDK编译器,必须包含core_cm3.h头文件)

static __INLINE void EnableFault(void)
{
    SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk|SCB_SHCSR_BUSFAULTENA_Msk|SCB_SHCSR_MEMFAULTENA_Msk);
}

2. 编写硬Fault处理程序

/*
* 截获硬Fault异常
* arg:堆栈指针
*/
void HardFaultHandle(unsigned int *arg)
{
    unsigned int stacked_r0,stacked_r1,stacked_r2,stacked_r3,stacked_r12,stacked_lr,stacked_pc,stacked_psr;
    stacked_r0=((unsigned ]);
    stacked_r1=((unsigned ]);
    stacked_r2=((unsigned ]);
    stacked_r3=((unsigned ]);
    stacked_r12=((unsigned ]);
    stacked_lr=((unsigned ]);
    stacked_pc=((unsigned ]);
    stacked_psr=((unsigned ]);

    PLC_DEBUGF(TEST_DEBUG,("致命错误:系统发生硬Fault!!\n"));
    PLC_DEBUGF(TEST_DEBUG,("捕获错误发生时的环境,上报Fault状态寄存器:\n"));
    PLC_DEBUGF(TEST_DEBUG,("R0= 0x%x\n",stacked_r0));
    PLC_DEBUGF(TEST_DEBUG,("R1= 0x%x\n",stacked_r1));
    PLC_DEBUGF(TEST_DEBUG,("R2= 0x%x\n",stacked_r2));
    PLC_DEBUGF(TEST_DEBUG,("R3= 0x%x\n",stacked_r3));
    PLC_DEBUGF(TEST_DEBUG,("R12= 0x%x\n",stacked_r12));
    PLC_DEBUGF(TEST_DEBUG,("LR= 0x%x\n",stacked_lr));
    PLC_DEBUGF(TEST_DEBUG,("PC= 0x%x\n",stacked_pc));
    PLC_DEBUGF(TEST_DEBUG,("PSR= 0x%x\n",stacked_psr));

    PLC_DEBUGF(TEST_DEBUG,("HFSR= 0x%x\n",HFSR));

    PLC_DEBUGF(TEST_DEBUG,("BFSR= 0x%x\n",BFSR));
    PLC_DEBUGF(TEST_DEBUG,("BFAR= 0x%x\n",BFAR));

    PLC_DEBUGF(TEST_DEBUG,("MMSR= 0x%x\n",MMSR));
    PLC_DEBUGF(TEST_DEBUG,("MMAR= 0x%x\n",MMAR));

    PLC_DEBUGF(TEST_DEBUG,("UFSR= 0x%x\n",UFSR));

    HFSR=0xFFFFFFFF;
    BFSR=0xFF;
    MMSR=0xFF;
    UFSR=0xFFFF;

    );
}

3.  在启动代码中,将默认硬Fault处理程序更换为自己需要的Fault处理程序

HardFault_Handler\
        PROC
        IMPORT   HardFaultHandle
        TST      LR,#
        ITE      EQ
        MRSEQ    R0,MSP
        MRSNE    R0,PSP
        B        HardFaultHandle
        ENDP

Cortex-M3和Cortex-M4 Fault异常应用之二 ----- Fault处理函数的实现的更多相关文章

  1. Cortex-M3和Cortex-M4 Fault异常应用之一 ----- 基础知识

    1. 摘要 Cortex-M内核实现了一个高效异常处理模块,可以捕获非法内存访问和数个程序错误条件.本应用笔记从程序员角度描述Cortex-M Fault异常,并且讲述在软件开发周期中的Fault用法 ...

  2. ARM 架构、ARM7、ARM9、STM32、Cortex M3 M4 、51、AVR 之间有什么区别和联系?(转载自知乎)

    ARM架构:  由英国ARM公司设计的一系列32位的RISC微处理器架构总称,现有ARMv1~ARMv8种类. ARM7:       一类采用ARMv3或ARMv4架构的,使用冯诺依曼结构的内核. ...

  3. 【ARM-Linux开发】ARM7 ARM9 ARM Cortex M3 M4 有什么区别

    ARM7 ARM9 ARM Cortex M3 M4 区别 arm7 arm9 可以类比386和奔腾, 不同代,arm9相比arm7指令集和性能都有所增强,arm7和arm9都有带mmu和无mmu的版 ...

  4. 【freertos】002-posix模拟器设计与cortex m3异常处理

    目录 前言 posix 标准接口层设计 模拟器的系统心跳 模拟器的task底层实质 模拟器的任务切换原理 cortex M3/M4异常处理 双堆栈指针 双操作模式 栈帧 EXC_RETURN 前言 如 ...

  5. ARM Cortex M3(V7-M架构)硬件启动程序 一

    Cortex-m3启动代码分析笔记 启动代码文件名是STM32F10X.S,它的作用先总结下,然后再分析. 启动代码作用一般是: 1)堆和栈的初始化: 2)中断向量表定义: 3)地址重映射及中断向量表 ...

  6. Implementation of Serial Wire JTAG flash programming in ARM Cortex M3 Processors

    Implementation of Serial Wire JTAG flash programming in ARM Cortex M3 Processors The goal of the pro ...

  7. ARM Cortex M3系列GPIO口介绍(工作方式探讨)

    一.Cortex M3的GPIO口特性    在介绍GPIO口功能前,有必要先说明一下M3的结构框图,这样能够更好理解总线结构和GPIO所处的位置. Cortex M3结构框图     从图中可以看出 ...

  8. STM32学习之路入门篇之指令集及cortex——m3的存储系统

    STM32学习之路入门篇之指令集及cortex——m3的存储系统 一.汇编语言基础 一).汇编语言:基本语法 1.汇编指令最典型的书写模式: 标号 操作码        操作数1, 操作数2,... ...

  9. STM32 F4xx Fault 异常错误定位指南

    STM32 F407 采用 Cortex-M4 的内核,该内核的 Fault 异常可以捕获非法的内存访问和非法的编程行为.Fault异常能够检测到以下几类非法行为: 总线 Fault: 在取址.数据读 ...

随机推荐

  1. [汇编语言]-第九章 在屏幕中间分别显示绿底红色,白底蓝色字符串"welcome to masm!"

    ;在屏幕中间分别显示绿色,绿底红色,白底蓝色字符串"welcome to masm!" assume cs:codesg,ds:datasg,ss:stacksg datasg s ...

  2. python提取隐含结构的字符串

    当我用Stanford CoreNLP和A Python wrapper for the Java Stanford Core NLP tools(NLP的python调用工具)进行句法分析时,遇到一 ...

  3. SQL Server 与 Windows 内存使用上的约定

    什么时候SQL Server 会释放出自己的内存!以提供给别的程序用呢? --------------------------------------------------------------- ...

  4. Codeforces #345 Div.1

    Codeforces #345 Div.1 打CF有助于提高做题的正确率. Watchmen 题目描述:求欧拉距离等于曼哈顿距离的点对个数. solution 签到题,其实就是求有多少对点在同一行或同 ...

  5. linux命令之uname

    uname是linux中查询系统基本信息的命令. 命令形式: uname [选项] 选项包括:(若不跟任何选项:则默认-s选项) -s, --kernel-name 输出内核名称   -n, --no ...

  6. vc6.0 使用Ado 连接MS-SqlServer2000 连接字符串

    vc6.0 使用Ado 连接MS-SqlServer2000 连接字符串 分类: C/C++ VC 2012-04-12 20:23 836人阅读 评论(0) 收藏 举报 sql server数据库服 ...

  7. poj 3233 Matrix Power Series

    A为一个n*n的矩阵,求A+A^2+A^3+...+A^n Sk = A + A2 + A3 + - + Ak       =(1+Ak/2)*(A + A2 + A3 + - + Ak/2  )+{ ...

  8. js 多媒体audio video

    本文主要简单的介绍一下audio 和 video两个标签的用法 <audio src="music.mp3"></audio> <video src= ...

  9. 事件:target与currentTarget区别

    target在事件流的目标阶段:currentTarget在事件流的捕获,目标及冒泡阶段.只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的 ...

  10. UVA 1613 K-Graph Oddity

    题意; 在一个n个点的无向连通图中,n是奇数,k是使得所有点的度数不超过k的最小奇数,询问一种染色方案,使得相邻点的颜色不同. 分析: k=所有点中最大的入度.如果最大入度是偶数,则k+1.每个点可选 ...