Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3308624
  • 博文数量: 366
  • 博客积分: 9916
  • 博客等级: 中将
  • 技术积分: 7194
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-29 23:27
个人简介

简单!

文章分类

全部博文(366)

文章存档

2013年(51)

2012年(269)

2011年(46)

分类: 嵌入式

2011-06-05 01:33:26

中断体系结构

ARM体系CPU有以下7种工作模式。

.用户模式(usr):ARM处理器正常的程序执行状态。

.快速中断模式(fiq):用于高速数据传输或通道处理。

.中断模式(irq):用于通用的中断处理。

.管理模式(svc):操作系统使用的保护模式。

.数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。

.系统模式(sys):运行具有特权的操作系统任务。

.未定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。

     可以通过软件来进行模式切换,或者发生各类中断、异常时CPU自动进入相应的模式。除用户模式外,其它6种工作模式都属于特权模式。大多数程序运行于用户模式,进入特权模式是为了处理中断、异常,或者访问被保护的系统资源。

     ARM920T31个通用的32位寄存器和6个程序状态寄存器。这37个寄存器分为7组,进入某个工作模式时就使用它那组的寄存器。有些寄存器,不同的工作模式下有自己的副本,当切换到另一个工作模式时,那个工作模式的寄存器副本将被使用:这些寄存器被称为备份寄存器。

     ARM状态下,每种工作模式都有16个通用寄存器和1个(或2个,这取决于工作模式)程序状态寄存器。

     图中R0~R15可以直接访问,这些寄存器中除R15外都是通用寄存器,即它们既可以用于保存数据也可以用于保存地址。另外,R13~R15稍有些特殊。R13又被称为栈指针寄存器,通常被用于保存栈指针。R14又被称为程序连接寄存器或连接寄存器,当执行BL子程序调用指令时,R14得到R15(程序计数器PC)的备份。而当发生中断或异常时,对应的R14_svcR14_irqR14_fiqR14_abtR14_und中保存R15返回值。

     快速中断模式有7个备份寄存器R8_fiq~R14_fiq,这使得进入快速中断模式执行很大部分程序时,甚至不需要保存任何寄存器(只要它们不改变R0~R7)。

每种工作模式除R0~R1516个寄存器外,还有第17个寄存器CPSR,即“当前程序状态寄存器”。CPSR中一些位被用于标识各种状态,一些位被用于标识当前处于什么工作模式。

CPSR外,还有快速中断模式、中断模式、管理模式、数据访问终止模式和未定义指令中止模式等5种工作模式和一个寄存器——SPSR,即“程序状态保存寄存器”。当切换进入这些工作模式时,在SPSR中保存前一个工作模式的CPSR值,这样,当返回前一个工作模式时,可以将SPSR的值恢复到CPSR中。

综上所述,当一个异常发生时,将切换进入相应的工作模式(为表述方便,下文中将它称为异常模式),这时ARM920T CPU核将自动完成如下事情。

a.在异常工作模式的连接寄存器R14中保存前一个工作模式的下一条,即将执行的指令的地址。对于ARM状态,这个值是当前PC值加4或加8.

b.CPSR的值复制到异常模式的SPSR

c.CPSR的工作模式位设为这个异常对应的工作模式。

d.PC值等于这个异常模式在异常向量表中的地址,即跳转去执行异常向量表中的相应指令。

相反地,从异常工作模式退回到之前的工作模式时,需要通过软件完成如下事情。

a.前面进入异常工作模式时,连接寄存器中保存了前一个工作模式的一个指令地址,将它减去一个适当的值后赋值给PC寄存器。

    b.SPSR的值复制回CPSR

 

 

    不论何种CPU,中断的处理过程是相似的。

    1)中断控制器汇集各类外设发出的中断信号,然后告诉CPU

    2CPU保存当前程序的运行环境(各个寄存器等),调用中断服务程序(ISR)来处理这些中断。

    3)在ISR中通过读取中断控制器、外设的相关寄存器来识别这是哪个中断,并进行相应的处理。

    4)清除中断:通过读写中断控制器和外设的相关寄存器来实现。

    5)最后恢复被中断程序的运行环境(即上面保存的各个寄存器等),继续执行。

 

     SUBSRCPNDSRCPND寄存器表明有哪些中断被触发了,正在等待处理;SUBMASKMASK用于屏蔽某些中断。

     Request sourcewithout sub-register)”中的中断源被触发之后,SRCPND寄存器中相应位被置1,如果此中断没有被INTMASK寄存器屏蔽或者快速中断的话,它将被进一步处理。

     Request sourcewith sub-register)”中的中断源被触发之后,SUBSRCPND寄存器中的相应位被置1,如果此中断没有被INTSUBMASK寄存器屏蔽的话,它在SRCPND寄存器中的相应位也被置1,之后的处理过程就和“Request sourcewithout sub-register)”一样了。

     SRCPND寄存器中,被触发的中断的相应位被置1,等待处理。

     如果被触发的中断有快速中断(FIQ)——MODEINTMODE寄存器,FIQ只能分配一个,即INTMOD中只能有一位设为1)中为1的位对应的中断是FIQ,则CPU进入快速中断模式(FIQ Mode)进行处理。

     对于一般中断IRQ,可能同时有几个中断被触发,未被INTMASK寄存器屏蔽的中断经过比较后,选出优先级最高的中断,此中断在INTPND寄存器中的相应位被置1,然后CPU进入中断模式(IRQ Mode)进行处理。中断服务程序可以通过读取INTPND寄存器或者INTOFFSET寄存器来确定中断源。

     使用中断的步骤如下:

     1)设置好中断模式和快速中断模式下的栈:当发生中断IRQ时,CPU进入中断模式,这时使用中断模式下的栈;当发生快速中断FIQ时,CPU进入快速中断模式,这时使用快速中断模式下的栈。

InitStacks:

    mrs r0,cpsr

    bic r0,r0,#MODEMASK

    orr r1,r0,#UNDEFMODE|NOINT

    msr cpsr_c,r1            @UndefMode

    ldr sp,=UndefStack          @ UndefStack=0x33FF_5C00

   

    orr r1,r0,#ABORTMODE|NOINT

    msr cpsr_c,r1            @AbortMode

    ldr sp,=AbortStack          @ AbortStack=0x33FF_6000

   

    orr r1,r0,#IRQMODE|NOINT

    msr cpsr_c,r1            @IRQMode

    ldr sp,=IRQStack         @ IRQStack=0x33FF_7000

   

    orr r1,r0,#FIQMODE|NOINT

    msr cpsr_c,r1            @FIQMode

    ldr sp,=FIQStack         @ FIQStack=0x33FF_8000

   

    bic r0,r0,#MODEMASK|NOINT

    orr r1,r0,#SVCMODE

    msr cpsr_c,r1            @SVCMode

    ldr sp,=SVCStack         @ SVCStack=0x33FF_5800

     2)准备好中断处理函数。

     a.异常向量表中设置好当进入中断模式或快速中断模式时的跳转函数,它们的异常向量地址分别为0x000000180x0000001c     

    b   Reset

 

@ 0x04: 未定义指令中止模式的向量地址

    b   HandlerUndef

 

@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式

    b   HandlerSWI

 

@ 0x0c: 指令预取终止导致的异常的向量地址

    b   HandlerPrefetchAbort

 

@ 0x10: 数据访问终止导致的异常的向量地址

    b   HandlerDataAbort

 

@ 0x14: 保留

    b   HandlerNotUsed

 

@ 0x18: 中断模式的向量地址

    b   HandlerIRQ

 

@ 0x1c: 快中断模式的向量地址

b   HandlerFIQ

跳转到具体的异常处理函数:

HandlerFIQ:

    HANDLER       HandleFIQ

HandlerIRQ:

    HANDLER       HandleIRQ

HandlerUndef:

    HANDLER       HandleUndef

HandlerSWI:

    HANDLER       HandleSWI

HandlerDataAbort:

    HANDLER       HandleDabort

HandlerPrefetchAbort:

    HANDLER       HandlePabort

HandlerNotUsed:

    b   .

HANDLER是一个宏定义,它将跳转到具体的函数进行执行。

.macro     HANDLER    HandleLabel

    sub     sp,sp,#4         

    stmfd  sp!,{r0}

    ldr     r0,=\HandleLabel      

    ldr     r0,[r0]              

    str     r0,[sp,#4]       

    ldmfd   sp!,{r0,pc}      

    .endm

     b.中断服务程序(ISR)。IRQFIQ的跳转函数,最终将调用具体中断的服务函数。

对于IRQ,读取INTPND寄存器或INTOFFSET寄存器的值来确定中断源,然后分别处理。

IsrIRQ:

    sub    lr, lr, #4          @ 计算返回地址

    stmfd   sp!,{ r0-r12,lr }   @ 保存使用到的寄存器

   

    sub sp,sp,#4            @保留pc寄存器的值

    stmfd  sp!,{r8-r9}          @r8 r9按入堆栈

 

    ldr lr, =int_return             @ 设置调用ISREINT_Handle函数后的返回地址 

   

    ldr r9,=INTOFFSET       @把中断偏移INTOFFSET的地址装入r9里面

    ldr r9,[r9]              @取出INTOFFSET单元里面的值给r9

    ldr r8,=HandleEINT0         @向量表的入口地址赋给r8

    add r8,r8,r9,lsl #2         @求出具体中断向量的地址

    ldr r8,[r8]              @中断向量里面存储的中断服务程序的入口地址赋给r8

    str r8,[sp,#8]             @按入堆栈

    ldmfd  sp!,{r8-r9,pc}    @堆栈弹出,跳转到相应的中断服务程序

   

int_return:

    ldmfd   sp!,{ r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

对于FIQ,因为只有一个中断可以设为FIQ,无须判断中断源。

     c.清除中断:如果不清除中断,则CPU会误以为中断又一次发生了。

     可以在调用ISR之前清除中断,也可以在调用ISR之后清除中断,这取决于在ISR执行过程中,这个中断是否可能继续发生、是否能够丢弃。如果在ISR执行过程中,这个中断可能发生并不能丢弃,则在调用ISR之前清除中断,这样在ISR执行过程中发生的中断能够被各寄存器再次记录并通知CPU;如果在ISR执行过程中,这个中断不会发生或者可以丢弃,则在调用ISR之后清除中断。

     清除中断时,从源头开始:首先,需要的话,操作具体外设清除中断信号;其次,清除SUBSRCPND(用到的话),SRCPND寄存器中相应位(往相应位写1即可);最后,清除INTPND寄存器中的相应位(往相应位写1即可),最简单的方法就是“INTPND=INTPND”。

     3)进入、退出中断模式或快速中断模式时,需要保存、恢复被中断程序的运行环境。

a.对于IRQ,进入和退出的代码如下:

sub lrlr#4            //计算返回地址

stmdb sp!,{r0-r12,lr}     //保存使用到的寄存器

……                     //中断处理

ldmia sp!,{r0-r12,pc}^    //中断返回,^表示将spsr的值赋给cpsr

b.对于FIQ,进入和退出的代码如下:

sub lrlr#4           //计算返回地址

stmdb sp!,{r0-r7,lr}     //保存使用到的寄存器

……                     //中断处理

ldmia sp!,{r0-r7,pc}^    //中断返回,^表示将spsr的值赋给cpsr

     4)根据具体中断,设置相关外设。

     5)对于“Request sourceswith sub-register)”中的中断,将INTSUBMASK寄存器中相应位设为0

     6)确定使用此中断的方式:FIQIRQ。如果是FIQ,则在INTMODE寄存器中设置相应位为1.如果是IRQ,则在PRIORITY寄存器中设置优先级。

     7)如果是IRQ,将INTMASK寄存器中相应位设为0FIQ不受INTMASK寄存器控制)。

     8)设置CPSR寄存器中的I-bit(对于IRQ)或F-bit(对于FIQ)为0,使能IRQFIQ

 

     以下是一个按键的中断测试程序。主要做以下几件事:初始化中断,中断函数注册,开中断,如果中断测试完毕则关掉相应的中断。基于bootloader的设计,在写中断的时候,只需要将中断函数的地址写到我们规定的地址即可。关于bootloader的设计,后续再作介绍。

#define GPB5_out        (1<<(5*2))      // LED1

#define GPB6_out        (1<<(6*2))      // LED2

#define GPB7_out        (1<<(7*2))      // LED3

#define GPB8_out        (1<<(8*2))      // LED4

#define GPF4_eint       (2<<(4*2))     // K2,EINT4

#define GPF2_eint       (2<<(2*2))      // K3,EINT2

#define GPF1_eint       (2<<(1*2))      // K1,EINT1

#define GPF0_eint       (2<<(0*2))      // K4,EINT0

                

void init_led(void)

{

    GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out ;

    GPBDAT = 0xffff;

}

 

/*

 * 初始化GPIO引脚为外部中断

 * GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD)

 */

void init_irq(void)

{

    GPFCON  = GPF0_eint | GPF1_eint | GPF2_eint | GPF4_eint;

    EINTMASK &= (~(1<<4));

    PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;

    INTMSK   &= (~(1<<0)) & (~(1<<1)) & (~(1<<2))& (~(1<<4));

}

 

void EINT_Handle()

{

    unsigned long oft = INTOFFSET;

   

    switch( oft )

    {

        // K4被按下

        case 0:

        {  

            GPBDAT |= (0x0f<<5);   // 所有LED熄灭

            GPBDAT &= ~(1<<8);      // LED4点亮

            break;

        }

       case 1:

        {  

            GPBDAT |= (0x0f<<5);   // 所有LED熄灭

            GPBDAT &= ~(1<<5);      // LED3点亮

            break;

        }

        // K3被按下

        case 2:

        {  

            GPBDAT |= (0x0f<<5);   // 所有LED熄灭

            GPBDAT &= ~(1<<7);      // LED3点亮

            break;

        }

        // K1K2被按下

        case 4:

        {  

            GPBDAT |= (0x0f<<5);   // 所有LED熄灭

            GPBDAT &= ~(1<<6);      // K2被按下,LED2点亮

            break;

        }

        default:

            break;

    }

 

    //清中断

    if( oft == 4 )

        EINTPEND = (1<<4);

 

    SRCPND = 1<

    INTPND = 1<

}

 

void KeyScan_Test(void)

{

    char c;

    init_led();    //led 初始化

    init_irq();

    printf("\nKey Scan Test, press ESC key to exit !\n");  

   

    ClearPending(BIT_EINT0|BIT_EINT1|BIT_EINT2|BIT_EINT4_7);

    pISR_EINT0 = pISR_EINT1 = pISR_EINT2 = pISR_EINT4_7 = (int)EINT_Handle;    //注册中断函数

    EnableIrq(BIT_EINT0|BIT_EINT1|BIT_EINT2|BIT_EINT4_7);  

 

    //while(1);

    while( (c=get_c())!=0x1b);

    DisableIrq(BIT_EINT0|BIT_EINT1|BIT_EINT2|BIT_EINT4_7); 

}

对于pISR_EINT0 pISR_EINT1 pISR_EINT2 pISR_EINT4_7等相关的二级中断地址我们已经有了定义。

#define    _ISR_STARTADDRESS        0x31ffff00

 

// Exception vector

#define pISR_RESET       (*(unsigned *)(_ISR_STARTADDRESS+0x0))

#define pISR_UNDEF       (*(unsigned *)(_ISR_STARTADDRESS+0x4))

#define pISR_SWI     (*(unsigned *)(_ISR_STARTADDRESS+0x8))

#define pISR_PABORT      (*(unsigned *)(_ISR_STARTADDRESS+0xc))

#define pISR_DABORT      (*(unsigned *)(_ISR_STARTADDRESS+0x10))

#define pISR_RESERVED    (*(unsigned *)(_ISR_STARTADDRESS+0x14))

#define pISR_IRQ     (*(unsigned *)(_ISR_STARTADDRESS+0x18))

#define pISR_FIQ     (*(unsigned *)(_ISR_STARTADDRESS+0x1c))

// Interrupt vector

#define pISR_EINT0       (*(unsigned *)(_ISR_STARTADDRESS+0x20))

#define pISR_EINT1       (*(unsigned *)(_ISR_STARTADDRESS+0x24))

#define pISR_EINT2       (*(unsigned *)(_ISR_STARTADDRESS+0x28))

#define pISR_EINT3       (*(unsigned *)(_ISR_STARTADDRESS+0x2c))

#define pISR_EINT4_7 (*(unsigned *)(_ISR_STARTADDRESS+0x30))

#define pISR_EINT8_23    (*(unsigned *)(_ISR_STARTADDRESS+0x34))

#define pISR_CAM     (*(unsigned *)(_ISR_STARTADDRESS+0x38))

#define pISR_BAT_FLT (*(unsigned *)(_ISR_STARTADDRESS+0x3c))

#define pISR_TICK    (*(unsigned *)(_ISR_STARTADDRESS+0x40))

#define pISR_WDT_AC97    (*(unsigned *)(_ISR_STARTADDRESS+0x44))  

#define pISR_TIMER0     (*(unsigned *)(_ISR_STARTADDRESS+0x48))

#define pISR_TIMER1     (*(unsigned *)(_ISR_STARTADDRESS+0x4c))

#define pISR_TIMER2      (*(unsigned *)(_ISR_STARTADDRESS+0x50))

#define pISR_TIMER3      (*(unsigned *)(_ISR_STARTADDRESS+0x54))

#define pISR_TIMER4      (*(unsigned *)(_ISR_STARTADDRESS+0x58))

#define pISR_UART2       (*(unsigned *)(_ISR_STARTADDRESS+0x5c))

#define pISR_LCD     (*(unsigned *)(_ISR_STARTADDRESS+0x60))

#define pISR_DMA0    (*(unsigned *)(_ISR_STARTADDRESS+0x64))

#define pISR_DMA1    (*(unsigned *)(_ISR_STARTADDRESS+0x68))

#define pISR_DMA2    (*(unsigned *)(_ISR_STARTADDRESS+0x6c))

#define pISR_DMA3    (*(unsigned *)(_ISR_STARTADDRESS+0x70))

#define pISR_SDI     (*(unsigned *)(_ISR_STARTADDRESS+0x74))

#define pISR_SPI0    (*(unsigned *)(_ISR_STARTADDRESS+0x78))

#define pISR_UART1       (*(unsigned *)(_ISR_STARTADDRESS+0x7c))

#define pISR_NFCON       (*(unsigned *)(_ISR_STARTADDRESS+0x80))  

#define pISR_USBD    (*(unsigned *)(_ISR_STARTADDRESS+0x84))

#define pISR_USBH    (*(unsigned *)(_ISR_STARTADDRESS+0x88))

#define pISR_IIC     (*(unsigned *)(_ISR_STARTADDRESS+0x8c))

#define pISR_UART0       (*(unsigned *)(_ISR_STARTADDRESS+0x90))

#define pISR_SPI1    (*(unsigned *)(_ISR_STARTADDRESS+0x94))

#define pISR_RTC     (*(unsigned *)(_ISR_STARTADDRESS+0x98))

#define pISR_ADC     (*(unsigned *)(_ISR_STARTADDRESS+0x9c))

 

 

 中断体系结构.doc   

阅读(1485) | 评论(2) | 转发(0) |
0

上一篇:arm编译出错

下一篇:系统时钟和定时器

给主人留下些什么吧!~~

txgc_wm2011-06-05 13:11:40

http://blog.csdn.net/Forcy/archive/2009/12/20/5044818.aspx
此篇文章写的不错!

txgc_wm2011-06-05 01:58:43

中断是异步发生的,就是说从软件的角度来说,不知道中断什么时候发生,而且中断发生之后,ISR的调度并不是软件来完成,而是由硬件的中断控制器来完成的(通过直接修改PC跳转到ISR的入口或是去查找中断向量表)。可以这么理解,ISR在代码流中并没有确切的“调用者”,那么谁给它准备“参数”呢?谁又接收ISR的“返回值”呢?