注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
wstt的个人空间 https://home.eeworld.com.cn/space-uid-241610.html [收藏] [复制] [分享] [RSS]
日志

ARM Cortex-M3中断跳转过程

已有 9916 次阅读2011-2-25 13:21 |个人分类:Cortex-M3

在学习CM3的时候,仔细学习了CM3的中断跳转过程,发现嵌入式的MCU在这一块基本上是一样的,当然不同架构的MCU也有自己的特性。

我来介绍下CM3的中断跳转过程,首先假设中断发生,CM3内核开始响应中断,由于不同厂家的CM3可能略有区别,但CM3的内核肯定是一样的,所以我们在这个前提下开始讨论,暂时把中断屏蔽位,标志位之类的东西放在一边。

现在介绍中断响应的过程:

1、压栈。从这一点来讲几乎所有的处理器都是一样的,用压栈保护现场。压入哪些寄存器呢,又是怎样一个顺序?如果就大多数的C语音编程来讲,这个不是很关心的内容。但是CM3的压栈寄存器特点,让我们来见识下ARM设计的特点。其压栈顺序如下图所示,请注意压栈的地址顺序和时间顺序不是相同的。

地址(SP

寄存器

被保护顺序(时间顺序)

N-0

之前已压栈内容

 

N-4

xPSR

2

N-8

PC

1

N-12

LR

8

N-16

R12

7

N-20

R3

6

N-24

R2

5

N-28

R1

4

N-32

R0

3

这一点就我们普通coding来讲,是非常奇特的,堆栈的空间顺利和进栈时间没有必然联系,跟我们“后进先出”的观点有很大出入,那么显然这里的“堆栈”,并不是我们传统意义的上的堆栈,具体怎样实现ARM没有详述,只是说他们可以做到这点。

我们可以看到PCxPSRR0R1R2R3是率先入栈的(时间上),这样做的目的,是为了编译器优先使用入栈了的寄存器来保存中间结果(如果程序过大也可能要用到R4-R11,此时编译器负责生成代码来push它们)。这也是要求ISR尽量短小的原因,用更少的寄存器,以加快响应。

2、查找中断向量表。其实这一步跟第一步是并行的,只是为了分别介绍我,列了序号。ARM是有D-Code(数据总线)和I-Code(指令总线),两条总线。可以看到PC是第一个压栈的,此时数据总线正忙于压栈操作,与此同时指令总线就可以查找中断向量表,查询中断服务程序的入口地址。在CM3中中断向量表位于地址从0x00000000开始的一段存储空间,每个表项占一个字(4byte)。这是中断向量表没有重定位的情况,当然中断向量表也可以重定位,即存储在其他地方。这个需要设置相应的寄存器,我个人认为还是让其固定在这个默认的位置比较好,以免出现以外情况。在看中断向量表的时候我遇到了一个很有意思的问题:

中断服务函数的入口地址为0x67C(图1所示),但是中断向量表中存储的地址确是0x67D(图2所示),竟然加了1

    这让我纠结了很久,后来一位整ARM7的大牛解答了我的问题。ARMPC最低位是0的时候ARM会进入ARM模式,但是最低位是1的时候会进入thumb模式。而我们的CM只支持thumb模式,所以PC最低位必须为1。而且thumb指令集是16位的,所以0x67D就是指向0x67C所存储的指令,但是减一就不行了,就变成了指令空间内上一个地址存储的指令。

         CM3中断跳转过程写出来跟大家分享,若有不妥之处,望大家斧正。

图1 方框内为所对应中断服务函数入口地

图2 方框内为中断向量表中中断服务函数入口地址

发表评论 评论 (2 个评论)
回复 wt-3333 2011-8-30 15:34
解释的很详细,很有启发
谢谢
回复 cuanli007 2012-8-10 17:08
我串口中断进不去,求帮忙谢谢
#include "msp430x14x.h"
//#include "io430.h"
//#include "nbc430.h"
#include <stdio.h>
#include <string.h>
#include "GPS.h"
#include "LCD.h"
#include "display.h"

char    rev_buf[80];        //接收缓存     
uchar   rev_start = 0;     //接收开始标志      
uchar   rev_stop  = 0;     //接收停止标志     
uchar   gps_flag = 0;      //GPS处理标志   
uchar   change_page = 0;   //换页显示标志   
uchar   num = 0;           //


extern GPS_INFO   GPS;  //在display.c中定义,使用时要加extern
//***********************************************************************************
//定时器初始化
//***********************************************************************************
int TimerA_init(void)
{
     //定时器A设置
     CCTL0 = CCIE;                  //设置捕获/比较控制寄存器中CCIE位为1,CCR0捕获/比较功能中断为允许。
     CCR0 = 400;                      //实现4x(1/4096)=0.001S秒的定时,捕获/比较控制寄存器CCR0初值为4。
     TACTL = TASSEL_1+ID_3 + MC_1;  //时钟源选择为ACLK(32768HZ)、8分频、增计数模式(0-CCR0溢出),32768/8=4096 HZ.
       return 0;                 
}
//***********************************************************************************
//中断服务子程序
//***********************************************************************************
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A0 (void)
{
        _DINT();                     //关总中断       
        static uchar count = 0;
        CCR0 = 400;
        count++;
        if (count == 100)  //2*5秒钟   
        {
                count = 0;
                change_page++;  //换页
                if (change_page == 10)
                        change_page = 0;
        }       
        _EINT();                //开总中断
}
//**************************************************************************************************
//时钟初始化
//**************************************************************************************************
/*3种时钟源:1、DCO:内部RC振荡器 2、XTAL1(LFXTAL1):片外时精确低频时32768Hz,产生ACLK.
          3、XTAL2:片外时钟2,精确高频时钟8MHz,产生MCLK和SMCLK。
输出时钟: 1、MCLK:系统主时钟 2、SMCLK:子系统时钟 3、ACLK:辅助时钟
系统上电时,默认使用DCO时钟做为MCLK,SMCLK;使用XTAL1做为ACLK。XTAL2没有启振,没有
被使用。系统时钟可以选择使用MCK,SMCLK,ACLK其中的一种。需要系统启动后,用软件设置。*/
void init_clock()
{
    uchar i;
    BCSCTL1 &= ~XT2OFF;  //控制XT2振荡器开启(XT2OFF=0)
    BCSCTL2 |=SELM1+SELS;//选择主要时钟 MCLK:XTAL2, SMCLK:XTAL2,不分频
    //清除振荡器错误中断标志,系统启动时不稳定造成的,必须要复位
    do
    {
        IFG1 &= ~OFIFG; //清除振荡器失效标志
        i = 255;        
        while(i--);     //延时等待至少50us
    }while(IFG1&OFIFG); //判断XT2是否起振
}
void Uart_Init(void)
{
        P3SEL |= 0x30;                            // 选择P3.4和P3.5做UART通信端口
        
        UCTL0 |= ~SWRST;                         //开始设置串口   
       ME1 |= URXE0;                             // 使能USART0的发送和接受
      UCTL0 |= CHAR;                            // 选择8位字                    
      UTCTL0 |= SSEL0;                          //选择模块时钟源 ; // UCLK = ACLK
      UBR00 = 0x06;                             // 波特率9600
      UBR10 = 0x00;                             //
      UMCTL0 = 0xfb;                            // Modulation
       UCTL0 &= ~SWRST;                    //设置串口 完毕
       IE1 |= URXIE0;
}
#pragma vector=UART0RX_VECTOR
__interrupt void usart0_rx (void)
{
                  
              
            uchar ch;
            _DINT();
             IE1 &= ~URXIE0; //ES = 0;_DINT();
        if((IFG1 & URXIFG0))
       
        {
               ch = RXBUF0;     
                if ((ch == '$') && (gps_flag == 0))  //如果收到字符'$',便开始接收           
                {
                        rev_start = 1;
                        rev_stop  = 0;
                }
       
                if (rev_start == 1)  //标志位为1,开始接收            
                {
                        rev_buf[num++] =ch;  //字符存到数组中           
                        if (ch == '\n')     //如果接收到换行           
                        {
                                rev_buf[num] = '\0';
                                rev_start = 0;
                                rev_stop  = 1;
                                gps_flag = 1;
                                num = 0;
                        }
                }
        }
       
        IFG1&=~URXIFG0;
            IE1 |= URXIE0;            //    _EINT();
             _EINT();
}
int main( void )
{
  // Stop watchdog timer to prevent time out reset
    WDTCTL = WDTPW + WDTHOLD;
    init_clock();
    P1DIR = 0XFF;P1OUT = 0XFF;
    P2DIR = 0XFF;P2OUT = 0XFF;
    P3DIR = 0XFF;P3OUT = 0XFF;
    P4DIR = 0XFF;P4OUT = 0XFF;
    P5DIR = 0XFF;P5OUT = 0XFF;
    P6DIR = 0XFF;P6OUT = 0XFF;
    uchar error_num = 0;
    Uart_Init();  //初始化串口
    Lcd_Init();          //初始化LCD   
    GPS_Init();   //初始化GPS
    TimerA_init();            //定时器初始化   
    _EINT();
    rev_stop=0;
        while(1)
        {
                if (rev_stop)   //如果接收完一行         
                {
                        CCTL0 = CCIE;        
                       
                        if (change_page % 2 == 1)  //换页     
                        {
                                if (GPS_GGA_Parse(rev_buf, &GPS))  //解析GPGGA      
                                {
                                       
                                        GPS_DisplayTwo();  //显示第二页信息
                                        error_num = 0;
                                        gps_flag = 0;
                                        rev_stop  = 0;
                                       
                                }
                                else
                                {
                                        error_num++;
                                        if (error_num >= 20) //如果数据无效超过20次               
                                        {
                                               
                                                error_num = 20;
                                                GPS_Init();     //返回初始化            
                                        }
                                        gps_flag = 0;
                                        rev_stop  = 0;
                                       
                                }

                        }
                        else
                        {
                                if (GPS_RMC_Parse(rev_buf, &GPS)) //解析GPRMC         
                                {
                                       

                                        GPS_DisplayOne();          //显示GPS第一页信息         
                                        error_num = 0;
                                        gps_flag = 0;
                                        rev_stop  = 0;
                                       
                                }
                                else
                                {
                                        error_num++;
                                        if (error_num >= 20) //如果数据无效超过20次               
                                        {
                                               
                                                error_num = 20;
                                                GPS_Init();     //返回初始化         
                                        }
                                        gps_flag = 0;
                                        rev_stop  = 0;
                                       
                                }
                        }
                }
        }
}

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章