zoukankan      html  css  js  c++  java
  • STM32 F4xx Fault 异常错误定位指南

    STM32 F407 采用 Cortex-M4 的内核,该内核的 Fault 异常可以捕获非法的内存访问和非法的编程行为。Fault异常能够检测到以下几类非法行为:

    • 总线 Fault: 在取址、数据读/写、取中断变量、进入/退出中断时寄存器堆栈操作(入栈/出栈)时检测到内存访问错误。
    • 存储器管理 Fault: 检测到内存访问违反了内存保护单元(MPU, MemoryProtection Unit)定义的区域。
    • 用法 Fault: 检测到未定义的指令异常,未对其的多重加载/存储内存访问。如果使能相应控制位,还可以检测出除数为零以及其他未对齐的内存访问。
    • 硬 Fault: 如果上述的总线 Fault、存储器管理 Fault、用法 Fault 的处理程序不能被执行(例如禁能了总线 Fault、存储器管理Fault、用法Fault 的异常或者在这些异常处理程序中又出现了新的Fault)则触发硬Fault。

    为了解释所述的 Fault 中断处理程序的原理,这里重述一下当系统产生异常时 MCU 的处理过程:

    • 有一个压栈的过程,若产生异常时使用 PSP(进程栈指针),就压入到 PSP 中,若产生异常时使用MSP(主栈指针),就压入MSP 中。
    • 会根据处理器的模式和使用的堆栈,设置 LR 的值(当然设置完的LR 的值再压栈)。
    • 异常保存,硬件自动把 8 个寄存器的值压入堆栈(8 个寄存器依次为 xPSR、PC、LR、R12以及 R3~R0)。如果异常发生时,当前的代码正在使用PSP,则上面8 个寄存器压入PSP; 否则就压入MSP。

    当系统产生异常时,我们需要两个关键寄存器值,一个是 PC ,一个是 LR (链接寄存器),通过 LR找到相应的堆栈,再通过堆栈找到触发异常的PC 值。将产生异常时压入栈的 PC 值取出,并与反汇编的代码对比就能得到哪条指令产生了异常。
      这里解释一下关于 LR 寄存器的工作原理。如上所述,当 Cortex-M4 处理器接受了一个异常后,寄存器组中的一些寄存器值会被自动压入当前栈空间里,这其中就包括链接寄存器(LR )。这时的 LR 会被更新为异常返回时需要使用的特殊值(EXC_RETURN)。关于EXC_RETURN 的定义如下,其为 32 位数值,高 28 位置 1,第 0 位到第三位则提供了异常返回机制所需的信息,如下表所示。可见其中第 2 位标示着进入异常前使用的栈是 MSP还是PSP。在异常处理过程结束时,MCU 需要根据该值来分配 SP 的值。这也是本方法中用来判断所使用堆栈的原理,其实现方法可以从后面_init_hardfault_isr 中看到。

      异常处理流程:
    首先要定义异常处理函数,在M4和M3核中,这两个是一样的,可以直接在stm32_f4xx.s中定义:

    .cpu cortex-m3
    .thumb
    
    .global HardFault_Handler
    .extern hard_fault_handler_c
    
    HardFault_Handler:
    TST LR, #4
    ITE EQ
    MRSEQ R0, MSP
    MRSNE R0, PSP
    B hard_fault_handler_c

      这里有几个命令要说明一下含义: TST 是Bit级别的与操作。ITE 是 MRSEQ和MRSNE都是两个命令的合体,分别可以拆开成:MRS,EQ和MRS,NE,分别的意思是如果两者相等,则把MSP的值赋值到R0,如果R0和PSP不等,则把PSP赋植到R0.ITE读为 if-then-else

      关于HardFault_Handler 这个函数,一般在stm32_f4xx.s的中断向量表中,我的系统中的代码如下所示:

      

      g_pfnVectors:
      .word  _estack
      .word  Reset_Handler
      .word  NMI_Handler
      .word  HardFault_Handler
      .word  MemManage_Handler
      .word  BusFault_Handler
      .word  UsageFault_Handler

      接下来就是整个流程的代码实现:

    / 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 - all numbers in hex]
    ");
    printf (“R0 = %x
    ”, stacked_r0);
    printf (“R1 = %x
    ”, stacked_r1);
    printf (“R2 = %x
    ”, stacked_r2);
    printf (“R3 = %x
    ”, stacked_r3);
    printf (“R12 = %x
    ”, stacked_r12);
    printf (“LR [R14] = %x subroutine call return address
    ”, stacked_lr);
    printf (“PC [R15] = %x program counter
    ”, stacked_pc);
    printf (“PSR = %x
    ”, stacked_psr);
    printf (“BFAR = %x
    ”, (*((volatile unsigned long )(0xE000ED38))));
    printf (“CFSR = %x
    ”, (((volatile unsigned long )(0xE000ED28))));
    printf (“HFSR = %x
    ”, (((volatile unsigned long )(0xE000ED2C))));
    printf (“DFSR = %x
    ”, (((volatile unsigned long )(0xE000ED30))));
    printf (“AFSR = %x
    ”, (((volatile unsigned long *)(0xE000ED3C))));
    printf (“SCB_SHCSR = %x
    ”, SCB->SHCSR);
    
    while (1);
    }
    
    /* hard fault interrupt handler */
    void _int_hardfault_isr( )
    {
    __asm(“TST LR, #4”);
    __asm(“ITE EQ”);
    __asm(“MRSEQ R0,MSP”);
    __asm(“MRSNE R0,PSP”);
    __asm(“B hard_fault_handler_c”);
    }
    
    void HardFault_Handler(void)
    {
    /* Go to infinite loop when Hard Fault exception occurs */
    DEBUG_ERR(" hard fault handler ");
    _int_hardfault_isr();
    while (1)
    {
    }
    }

      上面的这些代码,一般的工程师就可以看懂了,就不多做介绍了,假如你有啥这方面的问题,欢迎交流和沟通,反正是我的板子可以正常使用这些代码了。

    参考文档:

    https://community.arm.com/cn/b/blog/posts/3-thumb-2

  • 相关阅读:
    工作流
    工作流管理系统
    Domino(群组工作软件)
    Integer与int的区别(转)
    Java NIO和IO的区别(转)
    String、StringBuffer与StringBuilder之间区别(转)
    JAVA 是否会发生内存泄露(转)
    Java关键字final、static使用总结(转)
    数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)(转)
    Java多线程:用三个线程控制循环输出10次ABC
  • 原文地址:https://www.cnblogs.com/dylancao/p/11142205.html
Copyright © 2011-2022 走看看