arm工作模式——异常和中断使用方法

Arm920T寄存器简单介绍:
R1-R15:通用寄存器
R13:栈指针寄存器
R14:程序连接寄存器,当执行BL子程序调用指令时,R14中得到R15(程序计数寄存器pc)的备份,而当
        发生中断或异常时,对应的R14_svc、R14_irq等保存R15返回值。
R15:程序计数器pc
CPSR:当前程序状态寄存器
       1、T位(1位) Thumb/Arm。
       2、I位与F位(5、6位)IRQ中断、FIQ中断开关。
SPSR:程序状态保存寄存器,SPSR中保存前一个工作模式的CPSR值。

    异常发生时,将切换进入相应的异常模式,ARM920T cpu核自动完成如下工作,注意是自动完成。
1、在异常模式的连接寄存器R14中保存前一个工作模式的下一条即将执行的指令地址(R15->R14_xxx),
    对于ARM状态,这个值是当前pc值加4或加8。
2、将CPSR的值复制到异常模式的SPSR(CPSR->SPSR)。(所用模式共用一个CPSR,各异异常模式有一个
    自己的SPSR,用于保存前一个模式下的CPSR值)。
3、将CPSR的工作模式位设置为这个异常模式对应的工作模式。
4、令pc值等于这个异常模式在异常向量表中的地址,即跳转去执行异常向量表中的相应指令。

    相反地,从异常模式回到之前的工作模式,需要通过软件完成如下事情:
1、将异常模式下的R14减去一个适当值后赋给pc寄存器。
2、将SPSR的值复制回CPSR。
    退出异常时pc的值计算方法见韦东山的《嵌入式Linux应用开发完全手册》表9.1 p146。

    中断处理过程:
1、中断控制器汇集各类外设发出的中断信号,然后告诉cpu。
2、cpu保存当前程序的运行环境,调用中断服务程序(ISR,Interrupt Service Rountine)。
3、在ISR中通过读取中断控制器,外设的相关寄存器来识别这是哪一个中断,并进行相应的处理。
4、清除中断,通过读写中断控制器和外设的相关寄存器实现。
5、恢复运行环境,继续执行。

    各寄存器用途:
SUBSRCPND:标识INT_RXD0、INT_TXD0等中断是否已经发生。
INTSUBMSK:屏蔽SUBSRCPND寄存器所标识的中断。
SRCPND:每一位用来表示一个(或一类)中断是否已经发生,两类中断,这两类中断是:使用
               SUBSRCPND/INTSUBMSK控制的中断request source(with sub register)使用SRCPND/INTMSK
               控制的中断request source(without sub register)。
INTMSK:屏蔽SRCPND寄存器所标识的中断。
INTMOD:某位被置1时,它对应的中断被设为FIQ。同一时刻只能设置其中的一位,只能设置一个FIQ。
PRIORITY:优先级仲裁器。多个普通中断同时发生的时候,根据这个寄存器找出优先级最高的处理。
INTPND:cpu即将处理的中断标志。同一时刻只能设置其中1位,ISR中可根据这个位确定发生的是哪个中断。
INTOFFSET:表示INTPND哪位被置1。

      中断有两类,是request_source(without sub register)和request_source(with usb register),这两类对
应不同的屏蔽寄存器(INTMSK和INTSUBMSK)和发生中断的标志寄存器(SRCPND和SUBSRCPND)。

      如果被触发的中断有快速中断(IFQ)(INTMOD寄存器中为1的为对应的是FIQ),则cpu进如快中断处理。若几个IRQ同时,则选出优先级最高的,其在INTPND寄存器中的相应位被置1,然后cpu进入中断模式处理。
      中断服务程序可通过读取INTPND寄存器或者INTOFFSET寄存器来确定中断源。
      优先级在PRIORITY寄存器设置。

中断使用:
(1)、设置好中断与快中断模式下的栈
(2)、准备好中断处理函数
              1、异常向量
                          在异常向量表中设置好当进入中断模式或快中断模式时的跳转函数,他们的异常向量地
                          址分别是0x00000018、0x0000001c。
             2、中断服务程序(ISR)
                          跳转函数最终调用具体的中断服务函数,对于IRQ、读取INTPND寄存器或INTOFFSET寄存
                          器的值来确定中断源,然后分别处理,对于FIQ,无须判断。
             3、清除中断
                          可在调用ISR之前,也可以在ISR之后,取决于是否嵌套。
                          清除顺序:1外设 2SUBSRCPND(用到的话)SRCPND中相应的位 3 轻INTPND相应位。
(3)进入、退出中断模式或快速中断模式是,需要保存、恢复被中断程序的运行环境。
             1、对于IRQ,进入和退出的代码如下:              
		sub lr,lr,#4            @计算返回地址
		stmdb	sp!,{r0-r12,lr}	@保存使用到的寄存器
		......  		@处理中断
		ldmia	sp!,{r0-r12,pc}^@中断返回
					@ ^ 表示将spsr的值赋给cpsr

             2、对于FIQ、进入和退出的代码如下:                            

		sub lr,lr #4			@计算返回地址
		stmdb 	sp!,{r0-r7,lr}		@保存使用到的寄存器
		……				@处理快速中断
		ldima	sp!,{r0-r7,pc}^	@ ^ 表示将spsr的值赋给cpsr
(4)根据具体中断,设置相关外设。比如对于GPIO中断,需要将相应的引脚的功能设为“外部中断”、设置
        中断触发条件(低电 平触发、高电平触发、下降沿触发还是上升沿触发)等。一些中断拥有自己的屏
        蔽寄存器,还要开启他。
(5)对于“request sources(without sub-register)"中的中断,将INTSUBMSK寄存器中相应的位设为0。
(6)确定使用此中断的方式:FIQ或IRQ。
            如果是FIQ,则在INTMOD寄存器中设置相应的位为1.
            如果是IRQ,则在RIORITY寄存器中设置优先级。
(7)如果是IRQ,将INTMSK寄存器中相应位设为0(IFQ不受INTMSK寄存器控制)。
(8)设置CPSR寄存器中的I-bit(对于IRQ)或F-bit(对于FIQ)为0,使能IRQ或IFQ。


中断实例:在JZ2440开发板上,把K1-K4四个按键所接的CPU引脚设成外部中断功能。本程序的main函数是一
个不做任何事情的无限循环,程序的功能完全靠中断来驱动:当按下某个按键时,CPU调用其中断服务程序

来点亮对应的LED。主要看一下head.S和interrupt.c

@******************************************************************************
@ File:head.S
@ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数
@******************************************************************************       
   
.extern     main
.text 
.global _start 
_start:
@******************************************************************************       
@ 异常向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************       
    b   Reset

@ 0x04: 未定义指令中止模式的向量地址
HandleUndef:
    b   HandleUndef 
 
@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
    b   HandleSWI

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

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

@ 0x14: 保留
HandleNotUsed:
    b   HandleNotUsed

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

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

Reset:                  
    ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈
    bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启
    
    msr cpsr_c, #0xd2       @ 进入中断模式
    ldr sp, =3072           @ 设置中断模式栈指针

    msr cpsr_c, #0xd3       @ 进入管理模式
    ldr sp, =4096           @ 设置管理模式栈指针,
                            @ 其实复位之后,CPU就处于管理模式,
                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略

    bl  init_led            @ 初始化LED的GPIO管脚
    bl  init_irq            @ 调用中断初始化函数,在init.c中
    msr cpsr_c, #0x53       @ 设置I-bit=0,开IRQ中断
    
    ldr lr, =halt_loop      @ 设置返回地址
    ldr pc, =main           @ 调用main函数
halt_loop:
    b   halt_loop

HandleIRQ:
    sub lr, lr, #4                  @ 计算返回地址
    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器
                                    @ 注意,此时的sp是中断模式的sp
                                    @ 初始值是上面设置的3072
    
    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址  
    ldr pc, =EINT_Handle            @ 调用中断服务函数,在interrupt.c中
int_return:
    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

interrupt.c:

#include "s3c24xx.h"

void EINT_Handle()
{
    unsigned long oft = INTOFFSET;
    unsigned long val;
    
    switch( oft )
    {
        // S2被按下
        case 0: 
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<4);      // LED1点亮
            break;
        }
        
        // S3被按下
        case 2:
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<5);      // LED2点亮
            break;
        }

        // K4被按下
        case 5:
        {   
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<6);      // LED4点亮                
            break;
        }

        default:
            break;
    }

    //清中断
    if( oft == 5 ) 
        EINTPEND = (1<<11);   // EINT8_23合用IRQ5
    SRCPND = 1<



你可能感兴趣的:(裸板)