學習bootloader制作的過程中,學到 “通過按鍵進入中斷控制LED亮滅”的實驗時,自己所用的開發板和視頻講解中的不同,于是琢磨了一下中斷涉及到的各個寄存器,并進行編碼嘗試,最終完成了實驗,達到了通過按鍵以中斷方式控制LED亮滅的目的。2440屬于非向量中斷方式,和6410、210的向量中斷方式對于中斷的處理有一些不同,因此本文的講解并不完全適合6410和210。
## 一、概念
中斷分為兩大類:外部中斷和內部中斷。
1、外部中斷:S3C2440的24個外部中斷占用GPF0~GPF7(EINT0~EINT7)、???????????????????????????????????????????????? GPG0~GPG15(EINT8~EINT23)。使用這些引腳作為中斷輸入時,必須將引腳配置為EINT模式,配置方法可參考datasheet。
2、內部中斷:內部中斷包括DMA中斷、UART中斷、IIC中斷等等由內部外設觸發的中斷。
3、相關寄存器:S3C2440中斷控制涉及到10個寄存器--?SRCPND、INTMOD、INTMSK、PRIORITY、INTPND、INTOFFSET、SUBSRCPND、INTSUBMSK、EINTMASK、EINTPEND,這10個寄存器的定義及其功能描述如下表一。對中斷的控制無外乎配置和處理這10個寄存器中的某幾個。
? ? ? ????????????????????????????表一 ?與S3C2440中斷相關的10個寄存器的信息

4、各寄存器的關系(工作流程)。根據中斷源進行分類,中斷的處理流程可用下圖1表示:

圖1 ?S3C2440中斷處理流程圖
?由上圖1可知,S3C2440的中斷可分為四種情況:由外部中斷源(EINT0~EINT3)觸發的中斷、由外部中斷子中斷源(EINT4~EINT23)觸發的中斷、由內部中斷源(內部子中斷)觸發的中斷和由內部中斷源(非子中斷)觸發的中斷。
5、中斷的開啟(中斷初始化,INTMOD 和 PRTORITY使用默認值)
(a)如果是外部中斷(EINT0~EINT3)和內部中斷(不帶子中斷),需設置INTMSK,讓它不屏蔽中斷即可;
(b)如果是帶子中斷的內部中斷,需設置INTSUBMSK 和 INTMSK,讓它們不屏蔽中斷即可;
(c)如果是外部中斷(EINT4~EINT23),需設置EINTMASK 和 INTMSK,讓它們不屏蔽中斷即可;
注意:CPSR中的第7位I也需清除(在start.S中關閉了中斷,這一步是針對在bootloader設計中進行EINT實驗時的操作)
6、中斷處理流程
(a)如果是外部中斷(EINT0~EINT3)和 不帶子中斷的內部中斷,發生中斷后SRCPND相應位置1,如果沒有被 INTMSK屏蔽,那么等待進一步處理;
(b)如果是帶子中斷的內部中斷,發生中斷后SUBSRCPND相應位置1,如果沒有被INTSUBMSK屏蔽,則SRCPND相應位置1,如果沒有被INTMSK屏蔽,那么等待進一步處理;
(c)如果是外部中斷(EINT4~EINT23),發生中斷后EINTPEND相應位置1,如果沒有被EINTMASK屏蔽,則SRCPND相應位EINT4-7和EINT8~23置1,如果沒有被INTMSK屏蔽,那么等待進一步處理;
?三種中斷都等待進一步處理了,接下來從SRCPND繼續往前看,看看INTMSK,如果中斷被屏蔽了,就不用說了(注意:快中斷也能被屏蔽)。如果沒有被屏蔽,那么會進一步到INTMOD。如果是快中斷,那么直接出來,進入FIQ(即CPU進入快中斷模式處理)。如果是普通中斷,那么SRCPND可以有多位置1(FIQ只能有一個),這時就會經過PRIORITY選出一個優先級高的,然后根據選出的中斷把INTPND相應位置1(注意:只能選出一個),進入IRQ,讓CPU處理。
?INTOFFSET寄存器用來表示INTPND中哪一位置1了,可以用來判斷請求中斷的中斷源,但是,對于外部中斷EINT4~EINT23是無法判斷的,如EINT4~EINT7中任何一個中斷源請求中斷,都會將INTOFFSET中的位EINT4_7置1,所以要判斷具體是哪個中斷源請求的中斷,可讀取EINTPEND中的值進行判斷。
7、中斷的清除
(a)如果是外部中斷EINT0~EINT3和不帶子中斷的內部中斷,只需清除SRCPND(注意:清除時對相應位寫“1”);
(b)如果是帶子中斷的內部中斷,需清除SRCPND和SUBSRCPND,注意先清除SUBSRCPND,再清除SRCPND。因為如果先清除SRCPND的話,在清除SUBSRCPND的過程中,SRCPND會以為又有中斷發生,又會置1,也就是說一次中斷會響應兩次,所以必須先掐斷源頭,對它們同樣是寫“1”清除
(c)如果是外部中斷EINT4~EINT23,需清除EINTPEND和SRCPND(同樣注意順序),寫“1”清除。
## 二、按鍵中斷實驗
1、所使用開發板的按鍵與芯片連接示意如下圖2,其中KEY1 --> GPF4, KEY2--> GPF5,KEY3 --> GPF6,KEY4 --> GPF7。

圖2 ?按鍵與S3C2440連接示意
通過查閱s3c2440的datasheet可知,GPF4~GPF7對應外部中斷EINT4~EINT7。所以對于中斷初始化,需設置寄存器EINTMASK和INTMSK,取消中斷屏蔽。中斷處理過程中,可通過閱讀EINTPEND寄存器的值判定觸發中斷的中斷源。中斷清除時,向EINTPEND和SRCPND中相應位寫“1”,清除中斷(注意順序)。以下為實驗的源代碼:
~~~
/*interrupt registes*/
#define SRCPND (volatile unsigned long *)0x4a000000 //Interrupt request status
#define INTMOD (volatile unsigned long *)0x4a000004 //Interrupt mode control
#define INTMSK (volatile unsigned long *)0x4a000008 //Interrupt mask control
#define PRIORITY (volatile unsigned long *)0x4a00000c //IRQ priority control
#define INTPND (volatile unsigned long *)0x4a000010 //Interrupt request status
#define INTOFFSET (volatile unsigned long *)0x4A000014 //Interrupt request source offset
#define SUBSRCPND (volatile unsigned long *)0x4A000018 //Sub source pending
#define INTSUBMSK (volatile unsigned long *)0x4A00001c //Interrupt sub mask
#define EINTMASK (volatile unsigned long *)0x560000a4 //External interrupt mask register
#define EINTPEND (volatile unsigned long *)0x560000a8 //External interrupt pending register
/*******************************************************************
*函數名稱:init_irq()
*功能描述:中斷初始化(取消中斷屏蔽)
*其他說明:按鍵中斷對應的是外部中斷EINT4~7,所以設置EINTMASK、INTMSK
*修改日期 版本號 修改人 修改內容
*------------------------------------------------------------------
*2015.12.5 V1.0
*******************************************************************/
void init_irq()
{
*(EINTMASK) &= ((~(1 << 4)) & (~(1 << 5)) & (~(1 << 6)) & (~(1 << 7))); //取消EINT4~7的子中斷屏蔽(注意,這步必須在后面一步之前)
*(INTMSK) &= (~(1 << 4)); //取消EINT4~7的中斷屏蔽
/***清除CPSR寄存器中的第7位I(IRQ disable),因為在start.S中關閉了中斷****/
__asm__(
"mrs r0, cpsr\n"
"bic r0, r0, #0x80\n"
"msr cpsr_c, r0\n"
:
:
);
}
/*******************************************************************
*函數名稱:handle_int()
*功能描述:中斷處理函數
*其他說明:
*修改日期 版本號 修改人 修改內容
*------------------------------------------------------------------
*2015.12.6 V1.0
*******************************************************************/
void handle_int()
{
/*判斷產生中斷的中斷源*/
unsigned long value = *(INTOFFSET);
unsigned long value_offset = *(EINTPEND); //value_offset的值分別對應EINT4~EINT7
if (4 == value) //value = 4 表明EINT4 ~ EINT7請求中斷 (這個條件不是必須的,可直接判斷value_offset)
{
switch (value_offset)
{
case 0x00000010 : //EINT4 --> k1
led_on();
break;
case 0x00000020 : //EINT5 --> k2
led_on();
break;
case 0x00000040 : //EINT6 --> k3
led_off();
break;
case 0x00000080 : //EINT7 --> k4
led_off();
break;
default :
break;
}
}
/*清除中斷標志*/
*(EINTPEND) = value_offset; //把EINTPEND中當前值重新賦給EINTPEND,即相當于對相應位置1清除(不過這
//樣做的前提是可以明確只發生一個中斷,不然有可能將其他的中斷請求清除)
*(INTPND) = 1 << 4;
}
~~~
參考文章:http://blog.chinaunix.net/uid-25100840-id-351208.html