天天看點

s3c2440中斷控制器操作

一、ARM中斷體系結構

arm有7中異常工作模式

使用者模式、快中斷模式、管理模式、資料通路終止模式、中斷模式、系統模式、未定義指令終止模式。

s3c2440中斷控制器操作

幾種模式有什麼不同呢,

1、不同的寄存器

2、不同的權限

3、觸發條件

對于不同的寄存器,ARM920T有31個通用的32位寄存器和6個程式狀态寄存器。這37個寄存器分為7組,進入某個工作模式時就使用他那組的寄存器。有些寄存器,不同的工作模式下有他的副本,當切換到另一個工作模式時,那個工作模式的寄存器副本将被使用:這些寄存器被稱為備份寄存器(圖中灰色部分的寄存器)

s3c2440中斷控制器操作

在ARM狀态下,每種工作模式都有16個通用的寄存器和一個(或兩個,這取決于不同的工作模式)程式狀态寄存器。圖中R0-R15可以直接通路,這些寄存器除了R15外都是通用寄存器,即他們既可以儲存資料又可以儲存位址。另外R13-R15稍有特殊。R13是棧指針寄存器,通常被用來儲存棧指針。14被稱為程式連接配接寄存器,當執行BL子程式調用指令時,R14中得到R15(程式計數器PC)的備份。而當發生中斷或異常時,對應的R14_svc、R14_irq、R14_fiq、R14_abt、R14_und中儲存R15的傳回值。

快速中斷模式有7個備份寄存器R8-R14,這使得進入快速中斷模式執行很大部分程式時,隻要不改變R0-R7,不用儲存任何寄存器。七種工作模式都有自己獨占的寄存器副本R13和R14,這使得每個模式都有自己的棧指針寄存器和連接配接寄存器。

每種工作模式除了R0-R15外,還有第17個寄存器CPSR,即目前程式狀态寄存器。CPSR中一些位被用來辨別各種狀态,一些位被用來辨別目前處于什麼工作模式。

如圖CPSR

s3c2440中斷控制器操作

CPSR各位的意義:

(1)T位:置位時,CPU處Thumb狀态,否則處于ARM狀态

(2)中斷禁止位:I位和F位屬于中斷禁止位。他們被置位時IRQ中斷和FIQ中斷分别被禁止。

(3)工作模式位:辨別CPU目前處于什麼工作模式。可以編寫這些位,使CPU進入指定的工作模式。

除CPSR外,還有快速中斷模式、中斷模式、管理模式、資料通路終止模式和未定義指令終止魔獸等5中工作模式都有獨占的寄存器SPSR,即程式狀态儲存寄存器。當切換進入這些工作模式時,在SPSR中儲存前一個工作模式的CPSR的值,這樣,當傳回前一個工作模式時,可以将SPSR中的值恢複到CPSR中。

綜上所述,當一個異常發生時,将切換進入相應的工作模式,這時ARM920T CPU核将自動完成如下的事情:

(1)、在異常工作模式的連接配接寄存器R14中儲存前一個工作模式的下一條即将執行指令的位址,對于ARM狀态,這個值是目前pc值加4或加8。

(2)、将CPSR的值複制到異常模式的SPSR

(3)、将CPSR的工作模式位設定為這個異常模式對應的工作模式。

(4)、令PC值等于這個異常模式在異常向量表的位址。即跳轉去執行異常向量表中的相應的指令。

相反地、從異常工作模式退出傳回之前的工作模式時,需要通過軟體完成以下事情:

(1)、前面進入異常工作模式時,連接配接寄存器儲存了前一工作模式的一個指令位址,将他減去一個适當的值後賦給PC寄存器。

(2)、将SPSR的值複制回CPSR。

CPSR模式位設定如下圖:

s3c2440中斷控制器操作

二、s3c2440的中斷控制器

CPU工作的過程中如何知道各類外設發生了某些不預期的事件呢,比如序列槽接收到了新資料、USB接口插入了裝置、按下了某個按鍵等。主要有兩種方法:

(1)是查詢方式:程式循環地查詢各裝置的狀态并作出相應的反應。實作比較簡單,常用在功能相對單一的系統中,比如在一個溫度檢測系統對溫度的不斷檢查。缺點是占用CPU資源,不适用于多任務系統。

(2)、中斷方式:當某事件發生時,硬體會設定某個寄存器;CPU在每執行完一個指令時,通過硬體檢視這個寄存器,如果發現所關注的時

間發生了,則中斷目前程式流程,跳轉到一個固定的位址處理這件事,最後傳回繼續執行被中斷的程式。他的實作相對複雜,但是效率很高,是常用的方法。

中斷體系外設、内部外設與CPU核的硬體框圖:

s3c2440中斷控制器操作

不論何種CPU,中斷的處理是相似的。

(1)、中斷控制器彙集各類外設發出的中斷信号,然後告訴CPU。

(2)、CPU儲存目前程式的運作環境,調用中斷服務程式來處理這些中斷。

(3)、在ISR中通過讀取中斷控制器,外設的相關寄存器來識别這是哪個中斷,并進行相應的處理。

(4)、清楚中斷:通過讀寫中斷控制器和外設的相關寄存器來實作。

(5)、最後恢複被中斷程式的運作環境(即上面儲存的各個寄存器等),繼續執行。

對于不同的CPU而言,中斷的處理隻是細節的不同。

中斷處理框圖:

s3c2440中斷控制器操作

從中斷控制器結構圖看出

SUBSRCPND和SRCPND寄存器表明有哪些中斷被觸發,正在等待處理;SUBMASK(INTSUBMASK寄存器)和MASK(INTMASK寄存器)用于屏蔽某些中斷。

圖中的“Request sources(with sub-register)”表示INT_RXD0、INT_TXD0等中斷源(這類中斷源S3C2440有15個)。他們不同于“Request sources(without sub-register”。

附圖s3c2440異常向量表:

s3c2440中斷控制器操作

綜上所述,使用中斷的步驟如下:

(1)設定好中斷模式和快速中斷模式下的棧:當發生中斷IRQ時,CPU進入中斷模式,這時使用中斷模式下的棧;當發生快速中斷時,CPU進入快速中斷模式,這時使用快速中斷模式下的棧。

(2)準備好中斷處理函數。

a、異常向量:

在異常向量表中設定好進入中斷模式或快速中斷模式時的跳轉函數,他們的異常向量分别是0x00000018、0x0000001c。

b、中斷服務程式(ISR)

IRQ、FIQ的跳轉函數,具體将調用具體中斷的服務函數、

對于IRQ,讀取INTPND寄存器或INTOFFSET寄存器的值來判斷中斷源,然後分别處理。

對FIQ,由于隻有一個中斷可以設為FIQ,無需判斷中斷源。

c、清除中斷:如果不清除中斷,則CPU會誤以為這個中斷又一次發生。

清除中斷時從源頭開始:首先,需要的話,操作具體外設清除中斷信号,比如EINTPEND;其次,清除SUBSRCPND(用到的話)、SRCPND寄存器中的相應的位;最後清除INTPND寄存器中相應的位。

(3)、進入、退出中斷模式或快速中斷模式時,需要儲存、恢複被中斷程式的運作環境。

(4)、根據具體中斷設定相關外設,比如GPIO中斷,需要将相應的引腳功能設定為外部中斷、設定中斷觸發條件等,一些中斷有自己的屏蔽寄存器,還要開啟它,比如,GPIO的外設中斷,EINTMASK。

(5)、對于“Request sources(without sub-register)”的中斷,将INTSUBMASK寄存器中相應的位設為0.

(6)、确定使用中斷的方式:FIQ或IRQ

a、如果是FIQ,則在INTMOD寄存器中設定相應位為1

b、如果是IRQ,則在RIORITY寄存器中設定優先級

(7)、如果是IRQ,将INTMAK寄存器中相應的位設為0(FIQ不受INITMSK寄存器的控制)。

(8)、設定CPSR寄存器中的I位(對于IRQ)或F位(對于FIQ)為0,使能IRQ或FIQ、

在s3c2440中中斷控制器相關的8個寄存器:

1、SUBSRCPND

SUBSRCPND被用來辨別INT_RXD0、INT_TXD0等中斷是否已經發生,每位對應一個中斷。當這些中斷發生且沒有被INTSUBMSK寄存器屏蔽,則他們的若幹位将彙集出現在SRCPND寄存器的一位上,比如、隻要INT_RXD0、INT_TXD0或INT_ERR0一個發生且沒有被屏蔽,則SRCPND寄存器INT_UART0位被置1.

要清除中斷時,往SUBSRCPND中相應的位寫1

2、INTSUBMSK

用來屏蔽SUBSRCPND寄存器所辨別的中斷

3、SRCPND

SRCPND寄存器每一位用來辨別一個或一類中斷是否已經發生,清除中斷時與SUBSRCPND類似。

4、INTMSK

INTMSK寄存器用來屏蔽SRCPND寄存器所辨別的中斷。

5、INTMOD

當INTMOD寄存器中某位寫入1時,它對應的中斷被設為FIQ,同一時間裡,INTMOD寄存器中隻能有一位寫入1。

6、PRIORITY

優先級寄存器,有多個中斷同時發生時,中斷控制器将選出優先級最高的中斷源

7、INTPND

經過優先級仲裁器選出優先級最高的中斷後,這個中斷在INTPND寄存器中相應的位被置1,随後,CPU将進入中斷模式處理它。同一時間段裡,此寄存器隻有一位被置1:在ISR中,可以根據這個位确定是哪個中斷。清除中斷時,往這個位寫1.

8、INTOFFSET寄存器

這個寄存器被用來表示INTPND寄存器中哪個位被置1了,即INTPND寄存器中位【x】位1時,INTOFFSET寄存器的值為x(x為0-31)。

在清除SRCPND、INTPND寄存器時,INTOFFSET寄存器被自動清除。

三、實作按鍵中斷,led燈亮程式

head.S

@******************************************************************************
@ 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, #0x5f       @ 設定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
    

 

init.c

 

/*
 * init.c: 進行一些初始化
 */ 

#include "s3c24xx.h"

/*
 * LED1,LED2,LED4對應GPF4、GPF5、GPF6
 */
#define    GPF4_out    (1<<(4*2))
#define    GPF5_out    (1<<(5*2))
#define    GPF6_out    (1<<(6*2))

#define    GPF4_msk    (3<<(4*2))
#define    GPF5_msk    (3<<(5*2))
#define    GPF6_msk    (3<<(6*2))

/*
 * S2,S3,S4對應GPF0、GPF2、GPG3
 */
#define GPF0_eint     (0x2<<(0*2))
#define GPF2_eint     (0x2<<(2*2))
#define GPG3_eint     (0x2<<(3*2))

#define GPF0_msk    (3<<(0*2))
#define GPF2_msk    (3<<(2*2))
#define GPG3_msk    (3<<(3*2))

/*
 * 關閉WATCHDOG,否則CPU會不斷重新開機
 */
void disable_watch_dog(void)
{
    WTCON = 0;  // 關閉WATCHDOG很簡單,往這個寄存器寫0即可
}

void init_led(void)
{
    // LED1,LED2,LED4對應的3根引腳設為輸出
    GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk);
    GPFCON |= GPF4_out | GPF5_out | GPF6_out;
}

/*
 * 初始化GPIO引腳為外部中斷
 * GPIO引腳用作外部中斷時,預設為低電平觸發、IRQ方式(不用設定INTMOD)
 */ 
void init_irq( )
{
    // S2,S3對應的2根引腳設為中斷引腳 EINT0,ENT2
    GPFCON &= ~(GPF0_msk | GPF2_msk);
    GPFCON |= GPF0_eint | GPF2_eint;

    // S4對應的引腳設為中斷引腳EINT11
    GPGCON &= ~GPG3_msk;
    GPGCON |= GPG3_eint;
    
    // 對于EINT11,需要在EINTMASK寄存器中使能它
    EINTMASK &= ~(1<<11);
        
    /*
     * 設定優先級:
     * ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2
     * 仲裁器1、6無需設定
     * 最終:
     * EINT0 > EINT2 > EINT11即K2 > K3 > K4
     */
    PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;

    // EINT0、EINT2、EINT8_23使能
    INTMSK   &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
}      
s3c2440.h
           
/* WOTCH DOG register */
#define     WTCON           (*(volatile unsigned long *)0x53000000)

/* SDRAM regisers */
#define     MEM_CTL_BASE    0x48000000
#define     SDRAM_BASE      0x30000000

/* NAND Flash registers */
#define NFCONF              (*(volatile unsigned int  *)0x4e000000)
#define NFCMD               (*(volatile unsigned char *)0x4e000004)
#define NFADDR              (*(volatile unsigned char *)0x4e000008)
#define NFDATA              (*(volatile unsigned char *)0x4e00000c)
#define NFSTAT              (*(volatile unsigned char *)0x4e000010)

/*GPIO registers*/
#define GPBCON              (*(volatile unsigned long *)0x56000010)
#define GPBDAT              (*(volatile unsigned long *)0x56000014)

#define GPFCON              (*(volatile unsigned long *)0x56000050)
#define GPFDAT              (*(volatile unsigned long *)0x56000054)
#define GPFUP               (*(volatile unsigned long *)0x56000058)

#define GPGCON              (*(volatile unsigned long *)0x56000060)
#define GPGDAT              (*(volatile unsigned long *)0x56000064)
#define GPGUP               (*(volatile unsigned long *)0x56000068)

#define GPHCON              (*(volatile unsigned long *)0x56000070)
#define GPHDAT              (*(volatile unsigned long *)0x56000074)
#define GPHUP               (*(volatile unsigned long *)0x56000078)



/*UART registers*/
#define ULCON0              (*(volatile unsigned long *)0x50000000)
#define UCON0               (*(volatile unsigned long *)0x50000004)
#define UFCON0              (*(volatile unsigned long *)0x50000008)
#define UMCON0              (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0            (*(volatile unsigned long *)0x50000010)
#define UTXH0               (*(volatile unsigned char *)0x50000020)
#define URXH0               (*(volatile unsigned char *)0x50000024)
#define UBRDIV0             (*(volatile unsigned long *)0x50000028)


/*interrupt registes*/
#define SRCPND              (*(volatile unsigned long *)0x4A000000)
#define INTMOD              (*(volatile unsigned long *)0x4A000004)
#define INTMSK              (*(volatile unsigned long *)0x4A000008)
#define PRIORITY            (*(volatile unsigned long *)0x4A00000c)
#define INTPND              (*(volatile unsigned long *)0x4A000010)
#define INTOFFSET           (*(volatile unsigned long *)0x4A000014)
#define SUBSRCPND           (*(volatile unsigned long *)0x4A000018)
#define INTSUBMSK           (*(volatile unsigned long *)0x4A00001c)

/*external interrupt registers*/
#define EINTMASK            (*(volatile unsigned long *)0x560000a4)
#define EINTPEND            (*(volatile unsigned long *)0x560000a8)      
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<<oft;
    INTPND = 1<<oft;
}      
main.c
           
int main()
{
    while(1);
    return 0;
}      
Makefile
           
objs := head.o init.o interrupt.o main.o

int.bin: $(objs)
    arm-linux-ld -Ttext 0x00000000 -o int_elf $^
    arm-linux-objcopy -O binary -S int_elf [email protected]
    arm-linux-objdump -D -m arm int_elf > int.dis
    
%.o:%.c
    arm-linux-gcc -Wall -O2 -c -o [email protected] $<

%.o:%.S
    arm-linux-gcc -Wall -O2 -c -o [email protected] $<

clean:
    rm -f int.bin int_elf int.dis *.o      

轉載于:https://www.cnblogs.com/Ye-Jason/p/7374726.html