嵌入式:ARM中斷系統設計全解

2023-01-03 10:09:19 來源:51CTO博客

一、ARM9的異常事件管理

ARM920T能處理有8個異常,他們分別是:Reset,Undefined instruction,Software Interrupt,Abort (prefetch),Abort (data),Reserved,IRQ,FIQ ,它們的矢量表是:

Address Instruct0x00000000: b Handle_Reset0x00000004: b HandleUndef0x00000008: b HandleSWI0x0000000C: b HandlePrefetchAbort0x00000010: b HandleDataAbort0x00000014: b HandleNotUsed0x00000018: b HandleIRQ0x0000001C: b HandleFIQ

ARM920T的異常向量表有兩種存放方式,一種是低端存放(從0x00000000處開始存放),另一種是高端存放(從0xfff000000處開始存放)。


【資料圖】

異常矢量表的生成一般由一段匯編程序完成:

_start:    b        Handle_Reset    b        HandleUndef    b        HandleSWI    b        HandlePrefetchAbort    b        HandleDataAbort    b        HandleNotUsed    b        HandleIRQ    b        HandleFIQ    …..    …    ..    other codes    …    ..

這部分片段一般出現在一個名叫“head.s”的匯編文件的里,“b Handle_Reset”這條語句就是系統上電之后運行的第一條語句。

我們可以看到每條指令占用了4個字節。

上電后,PC指針會跳轉到Handle_Reset處開始運行。以后系統每當有異常出現,則CPU會根據異常號,從內存的0x00000000處開始查表做相應的處理,比如系統觸發了一個IRQ異常,IRQ為第6號異常,則CPU將把PC指向0x00000018地址(4*6=24=0x00000018)處運行,該地址的指令是跳轉到“中斷異常服務例程”(HandleIRQ)處運行。

二、ARM的中斷原理

S3C2410共有56個中斷源,可以產生32個中斷請求,這些中斷源來自兩部分:一部分來自片內外設(如DMA、UART等),一部分來自于外部引腳。

ARM920T內核共具有2種類型的中斷模式:FIQ和IRQ。IRQ和FIQ之間的區別是:對于FIQ必須盡快處理事件并離開這個模式;IRQ可以被FIQ中斷,但IRQ不能中斷FIQ;為了使FIQ更快,FIQ模式具有更多的私有寄存器。通過設置將56個中斷源分別映射到內核中的FIQ或IRQ,引起內核的中斷處理。

當多個中斷請求同時發生時,由硬件優先級邏輯確定應該有哪個中斷得到服務,同時將仲裁結果寫入中斷掛起寄存器,以便用戶識別中斷類型。

1. S3C2410的56個中斷源
2. S3C2410中斷處理的步驟

處理中斷的步驟如下:

(1) 保存現場

保存當前的PC值到R14,保存當前的程序運行狀態到SPSR。

(2) 模式切換

根據發生的中斷類型,進入IRQ模式或FIQ模式。

(3) 獲取中斷源

以異常向量表保存在低地址處為例,若是IRQ中斷,則PC指針跳到0x18處;若是FIQ中斷,則跳到0x1C處。IRQ或FIQ的異常向量地址處一般保存的是中斷服務子程序的地址,所以接下來PC指針跳入中斷服務子程序處理中斷。--這些工作都是由硬件自動完成

(4) 中斷處理

為各種中斷定義不同的優先級別,并為每一個中斷設置一個中斷標志位。當發生中斷時,通過判斷中斷優先級以及訪問中斷標志位的狀態來識別到底哪一個中斷發生了。進而調用相應的函數進行中斷處理。

(5) 中斷返回,恢復現場

當完成中斷服務子程序后,將SPSR中保存的程序運行狀態恢復到CPSR中,R14中保存的被中斷程序的地址恢復到PC中,進而繼續執行被中斷的程序--這些工作必須由用戶在中斷處理函數中實現。

三、S3C2410A的中斷控制器

中斷控制器的角色,就是響應來自片內或片外的中斷源的中斷請求,向ARM920T提出FIQ(快速中斷請求)或IRQ(普通中斷請求)的中斷請求,請求內核對該中斷進行處理。

1. 中斷控制器使用的寄存器
2. INTMOD寄存器

有效位為32位,每一位與SRCPND中各位相對應,它的作用是指定該位相應的中斷源處理模式(IRQ還是FIQ)。若某位為0,則該位相對應的中斷按IRQ模式處理,為1則以FIQ模式進行處理,該寄存器初始化值為0x00000000,即所有中斷皆以IRQ模式進行處理。

3. SRCPND/ SUBSRCPND寄存器

這兩個寄存器在功能上是相同的,它們是中斷源引腳寄存器,在一個中斷異常處理流程中,中斷信號傳進中斷異常處理模塊后首先遇到的就是SRCPND/ SUBSRCPND,這兩個寄存器的作用是用于標示出哪個中斷請求被觸發。SRCPND的有效位為32,SUBSRCPND 的有效位為11,它們中的每一位分別代表一個中斷源,每個位的初始值皆為0。假設現在系統觸發了TIMER0中斷,則第10bit將被置1,代表TIMER0中斷被觸發,該中斷請求即將被處理(若該中斷沒有被屏蔽的話)。

這兩個寄存器的各個位的置1是由相應的中斷源自動引起的,而在中斷服務程序中必須將其清0,否則CPU將認為是又一次中斷的到來。SRCPND(地址為0X4A000000)為主中斷源掛起寄存器,SUBSRCPND(地址為0X4A000018)為副(次)中斷源掛起寄存器

SUB SOURCE PENDING (SUBSRCPND) REGISTER

4. INTMSK/ INTSUBMSK 寄存器

中斷屏蔽寄存器 ,INTMSK為主中斷屏蔽寄存器,INTSUBMSK為副中斷屏蔽寄存器。INTMSK有效位為32,INTSUBMSK有效位為11,這兩個寄存器各個位與SRCPNDSUBSRCPND分別對應。

它們的作用是決定該位相應的中斷請求是否被處理。若某位被設置為1,則該位相對應的中斷產生后將被忽略(CPU不處理該中斷請求),設置為0則對其進行處理。

這兩個寄存器初始化后的值是0xFFFFFFFF和0x7FF,既默認情況下所有的中斷都是被屏蔽的

5. 優先級生成模塊

CPU某個時刻只能對一個中斷源進行中斷處理,如果現在有3個中斷同時發生了,那CPU要按什么順序處理這個3個中斷呢?這正是引入優先級判斷的原因所在,通過優先級判斷,CPU可以按某種順序逐個處理中斷請求。3sc2410的優先級判斷分為兩級。如下圖所示,SRCPND寄存器對應的32個中斷源總共被分為6個組,每個組由一個ARBITER(0\5)寄存器對其進行管理。中斷必須先由所屬組的ARBITER(0\5)進行第一次優先級判斷(第一級判斷)后再發往ARBITER6進行最終的判斷(第二級判斷)。我們能夠控制的是某個組里面各個中斷的優先級順序。怎么控制?通過PRIORITY寄存器進行控制。

6. PRIORITY寄存器

PRIORITY寄存器內部各個位被分為兩種類型,一種是ARB_MODE,另一種為ARB_SEL, ARB_MODE類型有7組對應ARBITER(0\6),ARB_SEL類型有7組對應ARBITER(7\20)。現在我將以ARBITER2為例,講解中斷組與PRIORITY寄存器中ARB_SEL, ARB_MODE之間的相互關系。

首先我們看到ARBITER2寄存器管理的該組中斷里包括了6個中斷,分別是INT_TIMER0,INT_TIMER1,INT_TIMER2,INT_TIMER3,INT_TIMER4,INT_UART2,它們的默認中斷請求號分別為REQ0,REQ1,REQ2,REQ3,REQ4,REQ5。

我們先看PRIORITY寄存器中的ARB_SEL2,該參數由兩個位組成,初始值為00。從該表可以看出00定義了一個順序 0-1-2-3-4-5 ,這個順序就是這組中斷組的優先級排列,這個順序指明了以中斷請求號為0(REQ0)的INT_TIMER0具有最高的中斷優先級,其次是INT_TIMER1,INT_TIMER2…。

假設現在ARB_SEL2的值被我們設置為01。則一個新的優先級次序將被使用,01對應的優先級次序為0-2-3-4-1-5,從中可以看出優先級最高和最低的中斷請求和之前沒有變化,但本來處于第2優先級的INT_TIMER1中斷現在變成了第5優先級。

從ARB_SEL2被設置為00,01,10,11各個值所出現的情況我們可以看出,除最高和最低的優先級不變以外,其他各個中斷的優先級其實是在做一個旋轉排列(rotate)。為了達到對各個中斷平等對待這一目標,我們可以讓優先級次序在每個中斷請求被處理完之后自動進行一次旋轉,如何自動讓它旋轉呢?我們可以通過ARB_MODE2達到這個目的,該參數只有1個 bit,置1代表開啟對應中斷組的優先級次序旋轉,0則為關閉。事實上當該位置為1之后,每處理完某個組的一個中斷后,該組的ARB_SEL便遞增在1(達到11后恢復為00)。

7. 中斷掛起寄存器INTPND 寄存器

INTPND 寄存器可能是整個中斷處理過程中我們要特別注意的一個寄存器了,他的操作比較特別 。

INTPND 寄存器與SRCPND長得一模一樣,但他們在中斷異常處理中卻扮演著不同的角色,如果說SRCPND是中斷信號進入中斷處理模塊后所經過的第一個場所的話,那么INTPND 則是中斷信號在中斷處理模塊里經歷的最后一個寄存器。

SRCPND是中斷源掛起寄存器,某個位被置1表示相應的中斷被觸發,但我們知道在同一時刻內系統可以觸發若干個中斷,只要中斷被觸發了,SRCPND的相應位便被置1,也就是說SRCPND 在同一時刻可以有若干位同時被置1,然而INTPND則不同,他在某一時刻只能有1個位被置1,INTPND 某個位被置1(該位對應的中斷在所有已觸發的中斷里具有最高優先級且該中斷沒有被屏蔽),則表示CPU即將或已經在對該位相應的中斷進行處理。

總結:SRCPND說明了有什么中斷被觸發了,INTPND說明了CPU即將或已經在對某一個中斷進行處理。

8. INTPND 寄存器

特別注意:

每當某一個中斷被處理完之后,我們必須手動地把SRCPND/SUBSRCPND , INTPND三個寄存器中與該中斷相應的位由1設置為0。

INTPND的操作很特別,它的特別之處就在于對當我們要把該寄存器中某個值為1的位設置為0時,我們不是往該位置0,而是往該位置1。

假設SRCPND=0x00000003,INTPND=0x00000001,該值說明當前0號中斷和1號中斷被觸發,但當前正在被處理的是0號中斷,處理完畢后我們應該這樣設置INTPND和SRCPND:

SRCPND=0x00000002             //位0被置為0INTPND=0x00000001             //位0被置為0(方法是往該位寫入1)
9. INTOFFSET寄存器

它的作用只是用于表明哪個中斷正在被處理。

若當前INT_TIMER0被觸發了,則該寄存器的值為10,以此類推。

10. 外部中斷控制寄存器

24個外部中斷占用GPF0\GPF7(EINT0\EINT7)、GPG0\GPG15(EINT8\EINT23)。用這些腳做中斷輸入,則必須配置引腳為中斷,并且不要上拉。

EXTINT0\EXTINT2寄存器:設定EINT0\EINT23的觸發方式。

EINTFLT0~EINTFLT3寄存器:控制濾波時鐘和濾波寬度。

EINTPEND寄存器:這個是中斷掛起寄存器,清除時要寫1。當一個外部中斷(EINT4\EINT23)發生后,那么相應的位會被置1。為什么沒有EINT0\EINT3,看看SRCPND就知道了.

EINTMASK寄存器:屏蔽中斷用的,某位為1時,此次中斷無效。

四、中斷編程實例

舉例:通過定時器1中斷控制CPU板的LED1和LED2實現輪流閃爍。

1.對定時器1初始化,并設定定時器的中斷時間為1秒。

void Timer1_init(void){    rGPGCON = rGPGCON & 0xfff0ffff | 0x00050000; //配置GPG口為輸出口    rGPGDAT = rGPGDAT | 0x300;    rTCFG0  = 255;          rTCFG1  = 0 << 4;       //在pclk=50MHZ下,1秒鐘的記數值rTCNTB1 =50000000/4/256=48828;     rTCNTB1 = 48828;     rTCMPB1 = 0x00;    rTCON   = (1 << 11) | (1 << 9) | (0 << 8); //禁用定時器1,手動加載    rTCON   = (1 << 11) | (0 << 9) | (1 << 8); //啟動定時器1,自動裝載}

2.為了使CPU響應中斷,在中斷服務子程序執行之前,必須打開ARM920T的CPSR中的I位,以及相應的中斷屏蔽寄存器中的位。

void Timer1INT_Init(void){ //定時器接口使能    if ((rINTPND & BIT_TIMER1)){        rSRCPND |= BIT_TIMER1;    }    //寫入定時器1中斷服務子程序的入口地址    pISR_TIMER1 = (int)Timer1_ISR;     rINTMSK  &= ~(BIT_TIMER1);  //開中斷;    }

3.等待定時器中斷,通過一個死循環如“while(1);”實現等待過程。

4.根據設置的定時時間,將產生定時器中斷。定時器中斷發生后,首先進行現場保護,接下來轉入中斷的入口代碼處執行,該部分代碼通常使用匯編語言書寫。在執行中斷服務程序之前,首先要確保HandleIRQ地址處保存中斷分發程序IsrIRQ的入口地址。

ldr r0,=HandleIRQ          ldr r1,=IsrIRQ                      str r1,[r0]

接下來將執行IsrIRQ中斷分發程序,具體代碼如下:

IsrIRQ    sub sp,sp,#4          ;為保存PC預留堆棧空間  stmfd sp!,{r8-r9}       ldr r9,=INTOFFSET         ldr r9,[r9]        ;加載INTOFFSET寄存器值到r9  ldr r8,=HandleEINT0   ;加載中斷向量表的基地址到r8  add r8,r8,r9,lsl #2   ;獲得中斷向量  ldr r8,[r8]   ;加載中斷服務程序的入口地址到r8  str r8,[sp,#8]  ;保存sp,將其作為新的pc值  ldmfd sp!,{r8-r9,pc}  ;跳轉到中斷服務子程序執行

5.執行中斷服務子程序,該子程序實現將LED1和LED2燈熄滅或點亮,從現象中看到LED1和LED2燈閃爍一次,則說明定時器發生了一次中斷。

int flag;void __irq Timer1_ISR( void ){   if (flag == 0) {           rGPGDAT = rGPGDAT & 0xeff | 0x200;      flag = 1;  }  else{          rGPGDAT = rGPGDAT & 0xdff | 0x100;      flag = 0;  }    rSRCPND |= BIT_TIMER1;    rINTPND |= BIT_TIMER1;}

6.從中斷返回,恢復現場,跳轉到被中斷的主程序繼續執行,等待下一次中斷的到來。

參考文獻:

孟祥蓮.嵌入式系統原理及應用教程(第2版)[M].北京:清華大學出版社,2017.

標簽: 中斷請求 中斷處理 進行處理

上一篇:世界今亮點!明確接口測試自動化需要的功能
下一篇:世界今頭條!手把手帶你開發starter,點對點帶你講解原理