S3C2440中断学习

一.ARM中断体系

当一个“异常”发生时,或者说当收到一个中断触发信号时,ARM9将会自动完成如下一些工作:

(1)       在异常工作模式的连接寄存器R14中保存前一个工作模式的下一条指令地址,即是pc+4或者pc+8。

(2)       将CPSR的值复制到异常模式的SPSR中。

(3)       将CPSR的工作模式位设为这个异常对应的工作模式。

(4)       令PC值等于这个异常模式在异常向量表中的地址,即跳转去执行异常向量表中的相应指令。既是执行中断服务程序。  

当从异常模式恢复至之前的工作模式时,软件需要完成如下工作:

(1)       将前面进入异常时保存在R14中的指令地址减去适当值后赋给PC寄存器。

异常模式
退出异常时PC值
进入异常模式时R14值

Svc
MOVS PC,R14
PC+4

Und
MOVS PC,R14
PC+4

Fiq
SUBS PC,R14,#4
PC+4

Irq
SUBS PC,R14,#4
PC+4

Abt
指令预取终止

SUBS PC,R14,#4
PC+4

指令读取终止

SUBS PC,R14,#8
PC+8





(2)       将SPSR的值复制回CPSR。

二. S3C2440中断控制器

其实,无论哪种CPU,中断处理过程都是类似的:

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

2.       CPU保存当前程序的运行环境,调用中断服务程序来处理中断。

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

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

5.       最后恢复被中断程序的运行环境,继续执行中断前的程序。

当然,对于不同的CPU体系结构,具体的寄存器操作自然就不同了。

对于S3C2440,寄存器SUBSRCPND和SRCPND表明有哪些中断被触发了,正在等待处理(pending),INTSUMMSK和INTMSK寄存器用于屏蔽某些中断

基本中断处理过程如下:

中断源被触发过后,SUBSRCPND寄存器中的相应位会被置位,如果此中断没有被INTMSK寄存器屏蔽掉,它将会进一步处理。在SRCPND寄存器中,被触发的中断相应位被置1,等待处理。如果被触发的中断源中有FIQ,则CPU进入FIQ Mode进行处理。如果被触发的是一般的IRQ,可能同时有几个中断被触发,未被INTMSK寄存器屏蔽的中断经过比较,选出优先级最高的中断,此中断在INTPND寄存器中的相应位被置1,然后CPU进入IRQ Mode进行处理。中断服务程序可以通过读取INTPND寄存器或者 INTOFFSET寄存器来确定中断源。

综上,使用中断的步骤如下:

1.       设置好中断模式和快速中断模式下的栈。

2.       准备好中断处理函数。包括异常向量表,ISR,清除中断。 

清除中断的方法:

(1)    操作具体外设清除中断信号。

(2)    清除SUBSRCPND,SRCPND寄存器中的相应位。

(3)    清除INTPND寄存器中的相应位。

3.       进入和退出时需要对中断程序的运行环境进行保护和恢复。

(1)进入和退出IRQ的一般处理:

view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
sub lr, lr, #4                  @ 计算返回地址   
stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器   
……                          @中断处理   
ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr  
sub lr, lr, #4                  @ 计算返回地址
stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器
……                                                        @中断处理
ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr


(2)进入和退出FIQ的一般处理:



view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
sub lr, lr, #4                  @ 计算返回地址   
stmdb   sp!,    { r0-r7,lr }   @ 保存使用到的寄存器   
……                      @快速中断处理   
ldmia   sp!,    { r0-r7,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr  
sub lr, lr, #4                  @ 计算返回地址
stmdb   sp!,    { r0-r7,lr }   @ 保存使用到的寄存器
……                                                @快速中断处理
ldmia   sp!,    { r0-r7,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr


4.       根据具体中断,设置相关外设,比如对于GPIO中断,需要将相应引功能设置为“外部中断”,设置中断触发条件等等。

5.       将INTSUBMSK寄存器相应位设为0,如果需要的话。 

6.       确定使用中断的方式:FIQ或者 IRQ

7.       如果是FIQ,则INTMOD寄存器中设置相应位为1;如果是IRQ,则在PRIORITY寄存器中设置优先级。 

8.        如果是IRQ,将INTMSK寄存器相应位设为0,FIQ不受INTMSK寄存器控制。

9.       设置CPSR寄存器中的I 位(IRQ)或者 F位(FIQ),全能IRQ或者FIQ。

中断控制寄存器:

(1)      子中断源未决寄存器SUBSRCPND,地址:0x4A000018

(2)      子中断屏蔽寄存器INTSUBMSK,地址:0x4A00001C

(3)      中断源未决寄存器SRCPND,地址:0x4A000000

(4)      中断屏蔽寄存器INTMSK,地址:0x4A000008

(5)      中断模式寄存器INTMOD,地址:0x4A000004

(6)      优先级寄存器PRIORITY,地址:0x4A00000C

(7)      中断未决寄存器INTPND,地址:0x4A000010

(8)      中断偏移寄存器INTOFFSET,地址:0x4A000014

各个寄存器的具体操作说明请参考S3C2440的datasheet。

三.KEY对应的中断连接

EINT19(GPG11)-------K1

EINT11(GPG3)-------K2

EINT2GPF2)-------K3

EINT0GPF0)-------K4

将这几个I/O口设置成外部中断功能。如果某个key按下时就产生中断,点亮对应的led。

四. 按键中断实验代码

(1)初始化,设置中断模式、系统模式的栈,设置好中断处理函数



view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
@******************************************************************************   
@ 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       @ 进入中断模式,M[4:0]=10010 , [IFT]=110   
    ldr sp, =3072           @ 设置中断模式栈指针   
  
    msr cpsr_c, #0xdf       @ 进入系统模式   
    ldr sp, =4096           @ 设置系统模式栈指针,   
                            @ 其实复位之后,CPU就处于系统模式,   
                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略   
  
    bl  init_led            @ 初始化LED的GPIO管脚   
    bl  init_irq            @ 调用中断初始化函数,在init.c中   
    msr cpsr_c, #0x5f       @ 设置I-bit=0,开IRQ中断,[IFT]=010   
       
    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  
@******************************************************************************
@ 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       @ 进入中断模式,M[4:0]=10010 , [IFT]=110
    ldr sp, =3072           @ 设置中断模式栈指针

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

    bl  init_led            @ 初始化LED的GPIO管脚
    bl  init_irq            @ 调用中断初始化函数,在init.c中
    msr cpsr_c, #0x5f       @ 设置I-bit=0,开IRQ中断,[IFT]=010
    
    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

(2)初始化,关闭watchdog,,初始化GPIO引脚为外部中断。



view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
* init.c: 进行一些初始化   
*/   

#include "s3c24xx.h"   
  
/*  
* LED1-4对应GPB5、GPB6、GPB7、GPB8  
*/ 
#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   
  
/*  
* K1-K4对应GPG11、GPG3、GPF2、GPF0  
*/ 
#define GPG11_eint      (2<<(11*2))     // K1,EINT19  
#define GPG3_eint       (2<<(3*2))      // K2,EINT11  
#define GPF2_eint       (2<<(2*2))      // K3,EINT2  
#define GPF0_eint       (2<<(0*2))      // K4,EINT0   
  
    
/*  
* 关闭WATCHDOG,否则CPU会不断重启  
*/  
void disable_watch_dog(void)   
{   
    WTCON = 0;  // 关闭WATCHDOG很简单,往这个寄存器写0即可   
}   
  
void init_led(void)   
{   
    GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out ;   
}   
  
/*  
* 初始化GPIO引脚为外部中断  
* GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD)  
*/    
void init_irq( )   
{   
    GPFCON  = GPF0_eint | GPF2_eint;   
    GPGCON  = GPG3_eint | GPG11_eint;   
       
    // 对于EINT11、19,需要在EINTMASK寄存器中使能它们   
    EINTMASK &= (~(1<<11)) & (~(1<<19));   
           
    /*  
     * 设定优先级:  
     * ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2  
     * 仲裁器1、6无需设置  
     * 最终:  
     * EINT0 > EINT2 > EINT11,EINT19,即K4 > K3 > K1,K2  
     * EINT11和EINT19的优先级相同  
     */  
    PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;   
  
    // EINT0、EINT2、EINT8_23使能   
    INTMSK   &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));   
}  
* init.c: 进行一些初始化
*/ 

#include "s3c24xx.h"

/*
* LED1-4对应GPB5、GPB6、GPB7、GPB8
*/
#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

/*
* K1-K4对应GPG11、GPG3、GPF2、GPF0
*/
#define GPG11_eint      (2<<(11*2))     // K1,EINT19
#define GPG3_eint       (2<<(3*2))      // K2,EINT11
#define GPF2_eint       (2<<(2*2))      // K3,EINT2
#define GPF0_eint       (2<<(0*2))      // K4,EINT0


/*
* 关闭WATCHDOG,否则CPU会不断重启
*/
void disable_watch_dog(void)
{
    WTCON = 0;  // 关闭WATCHDOG很简单,往这个寄存器写0即可
}

void init_led(void)
{
    GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out ;
}

/*
* 初始化GPIO引脚为外部中断
* GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD)
*/ 
void init_irq( )
{
    GPFCON  = GPF0_eint | GPF2_eint;
    GPGCON  = GPG3_eint | GPG11_eint;
    
    // 对于EINT11、19,需要在EINTMASK寄存器中使能它们
    EINTMASK &= (~(1<<11)) & (~(1<<19));
        
    /*
     * 设定优先级:
     * ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2
     * 仲裁器1、6无需设置
     * 最终:
     * EINT0 > EINT2 > EINT11,EINT19,即K4 > K3 > K1,K2
     * EINT11和EINT19的优先级相同
     */
    PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;

    // EINT0、EINT2、EINT8_23使能
    INTMSK   &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
}

(3)      真正的ISR

view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
void EINT_Handle()   
{   
    unsigned long oft = INTOFFSET;   
    unsigned long val;   
       
    switch( oft )   
    {   
        // K4被按下   
        case 0:    
        {      
            GPBDAT |= (0x0f<<5);   // 所有LED熄灭   
            GPBDAT &= ~(1<<8);      // LED4点亮   
            break;   
        }   
           
        // K3被按下   
        case 2:   
        {      
            GPBDAT |= (0x0f<<5);   // 所有LED熄灭   
            GPBDAT &= ~(1<<7);      // LED3点亮   
            break;   
        }   
  
        // K1或K2被按下   
        case 5:   
        {      
            GPBDAT |= (0x0f<<5);   // 所有LED熄灭   
               
            // 需要进一步判断是K1还是K2,或是K1、K2被同时按下   
            val = EINTPEND;   
            if (val & (1<<11))   
                GPBDAT &= ~(1<<6);      // K2被按下,LED2点亮   
            if (val & (1<<19))   
                GPBDAT &= ~(1<<5);      // K1被按下,LED1点亮   
                   
            break;   
        }   
  
        default:   
            break;   
    }   
  
    //清中断   
    if( oft == 5 )    
        EINTPEND = (1<<11) | (1<<19);   // EINT8_23合用IRQ5   
    SRCPND = 1<<oft;   
    INTPND = 1<<oft;   
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值