cortex-M除0异常中断

简介

众所周知,除数为0是不合法的运算,在一般情况下编译器、单片机中,对除0的运算结果为0。在某些情况下为了方便debug,Cortex-M系列单片机支持除0异常中断。如何使用,请参考《M4 Devices Generic User Guide.pdf》。

下面以Cortex-M4作为硬件平台,IAR作为编译工具。简单介绍如何使用除0中断异常。

1. Cortex-M Fault异常

每个符合CMSIS规范的编译器所提供的启动文件(Startup_device)都会定义好设备所有的异常和中断向量。这些向量表定义了异常或中断处理程序的入口地址。参考《M4 Devices Generic User Guide.pdf》中的p34~37。

其中HardFault异常处理程序在非硬Fault异常(BusFault、MemMange和UsageFault)被禁能,并且这些Fault异常被触发时,非硬Fault异常将上访为HardFault。

除0异常将触发UsageFault,如UsageFault未使能,将触发HardFault。

 

2. 打开除0异常中断,SCB->CCR 寄存器

除0异常捕捉需要通过使能CCR寄存器bit4(div_0_trp)打开,参考《M4 Devices Generic User Guide.pdf》p236。

 

DIV_0_TRP:

默认的情况下,div_0_trp=0,除0不会产生异常,商总是为0;当div_0_trp=1,将触发除0中断。

 

3.打开UsageFault中断,SCB->SHCSR寄存器

除0异常将触发UsageFault,默认情况下,UsageFault是被禁能,需要通过使能SHCSR寄存器bit18打开,参考《M4 Devices Generic User Guide.pdf》p239。

 

4. 中断前使用MSP还是PSP?

在中断中判断LR(即EXC_RETURN)bit2,可以推断使用的是MSP还是PSP,确定使用的栈后,通过栈找死机发生时的PC指针及LR。

 

5. 确定异常中断时的PC指针

       获得了栈指针后(SP),sp+6即为PC指针,通过PC指针可以定位除0发生的为止,参考《M4 Devices Generic User Guide.pdf》p40

 

6. 参考代码

#include "am_mcu_apollo.h"

void div0_test(void)
{
    volatile uint32_t i =0, k;
   
    am_util_stdio_printf("\r\nexample start %s\r\n", __func__);  

    //未开启除0异常中断,除0的结果为0
    k = 6/i;

    am_util_stdio_printf("before div 0 trp 6/0=%d\r\n", k);

    //使能除0异常上报
    SCB->CCR |= 0x10;

    //使能UsageFault_Handler
    SCB->SHCSR |= (1<<18);

    //打开总中断,因为UsageFault可以被关闭
    __enable_irq();
//除0,将进入UsageFault_Handler am_util_stdio_printf("after div 0 trp 6/0=%d\r\n"); k = 6/i; }
void call_UsageFault(uint32_t u32IsrSP) { uint16_t ufsr; uint32_t *p; volatile uint32_t k; am_util_stdio_printf("\r\nenter %s\r\n", __func__); ufsr = (SCB->CFSR>>16); if (ufsr & (1<<9)) { //除0错误 am_util_stdio_printf("div 0 error\r\n"); } p = (uint32_t *)u32IsrSP;
am_util_stdio_printf(
"LR=0x%x\r\n", p[5]); am_util_stdio_printf("PC=0x%x\r\n", p[6]); am_util_stdio_printf("example end\r\n");
while(1); }

void UsageFault_Handler(void) { __asm(" push {r0,lr}"); __asm(" tst lr, #4"); //lr = exc_return; bit2--0:msp 1:psp __asm( "ITET EQ \n" "MRSEQ R0, MSP \n" "MRSNE R0, PSP \n" "ADDSEQ R0, R0, #8 \n" ); __asm(" bl call_UsageFault"); __asm(" pop {r0,pc}"); } int main(void) { div0_test(); }

7. 运行结果

打印结果

 

通过打印结果的PC指针,在模拟仿真界面,打开汇编窗口,输入0x84112,查找触发异常中断位置。从图片可以看出,改异常触发在div0_test(),对应的指令为 UDIV R0, R0, R1。可以推断出div0_test最后一行k = 6/i; 触发了除0中断。

 

 

posted @ 2020-10-28 22:58  摩羯兔有点冷  阅读(1255)  评论(0编辑  收藏  举报