mini2440-----keil for ARM之中斷一

在篇文章中主要講講調試中斷過程中獲得的知識,也許會對大家有用。

大家應該都知道在使用S3C2440這塊芯片時,有一個十分大的問題,就是對於keil軟件自帶的初始化代碼沒有給全,在初始化代碼中主要做了以下幾件事

可以看出,也就是對I/O口進行了配置,看門狗,時鐘進行了初始化,同時也對內存塊進行了一定的配置,是十分基本的一些初始化,其中對於中斷向量表根本就沒有進行建立,因此如果要用他的文件進行中斷的實驗,必須自己去改動初始化代碼,完成他沒有完成的工作,自己嘗試了一下,沒有成功,因此就對ADS事例程序的初始化代碼進行移植(參看某大神的教程實現,)

一、ARM中斷的執行方式

這裏中斷有兩種,一種外部中斷,一種內部中斷,對於兩種中斷稍有不同,原理相似,我以外部中斷進行介紹。

1)當然首先必須配置好各種寄存器,當外部中斷在滿足條件的時候會發生中斷,這時EINTPEND這個寄存器的對應位會被寫爲1,其中這個寄存器可以有多位置一

2)再繼續和EINTMASK這個寄存器進行操作,如果觸發的中斷沒有被屏蔽,則可以產生,這時SRCPND對應的位置會置1,當然SRCPND這個寄存器也可能會多位被置1,僅僅表示此時有多箇中斷髮生了。

3)查看中斷模式,是普通中斷還是快速中斷,如果是快速中斷直接進入FIQ異常,如果是普通中斷還需要進行後續操作。

4)如果是普通中斷,再與INTMSK進行操作,操作後,如果還有多箇中斷,則進入中斷判優,最終對INTPND中的某一位進行置1,表示某個中斷產生並相應。注意INTPND這個寄存器有且僅有一位能夠被置1。

以上就是有中斷來了後,整個硬件操作的過程,因此我們可以看出,其實我們有時候可以不通過中斷服務的方式來進行,直接進行中斷位這些標誌的查詢就可以了,(類似單片機),但是這樣沒有任何效率的提升了。

對於復位、未定義指令、軟件中斷、指令異常、數據異常、普通中斷、快速中斷,在這裏我們叫他們異常

對於普通中斷中具體的中斷,我們才叫中斷。

對於以上異常發生時,都會有一個固定的跳轉地址,即異常向量表:

地址 異常
0x0000,0000 復位
0x0000,0004 未定義指令
0x0000,0008 軟件中斷
0x0000,000c 指令異常
0x0000,0010 數據異常
0x0000,0014 保留
0x0000,0018 普通中斷
0x0000,001c 快速中斷

下面是當進入IRQ中斷髮生後,軟件方面的一些操作:

1)當中斷產生時,程序會自動的跳轉到0x0000,0018這個地址上去,在這個地址上一般會有一個跳轉指令,就可以直接跳轉到異常處理函數。

2)但是對於普通中斷異常中還有相當多的中斷,因此對於普通中斷異常跳入的異常處理函數不是一個正真的處理函數,而是一箇中斷偏移在此跳轉的中間過程。其函數如下:

;呵呵,來了來了.好戲來了,這一段程序就是用來進行第二次查表的過程了.
;如果說第一次查表是由硬件來完成的,那這一次查表就是由軟件來實現的了.
;爲什麼要查兩次表??
;沒有辦法,ARM把所有的中斷都歸納成一個IRQ中斷異常和一個FIRQ中斷異常
;第一次查表主要是查出是什麼異常,可我們總要知道是這個中斷異常中的什麼中斷呀!
;沒辦法了,再查一次表唄!
;===================================================================================
;//外部中斷號判斷,通過中斷服務程序入口地址存儲器的地址偏移確定
;//PC=[HandleEINT0+[INTOFFSET]]
;H|------|             
; |/ / / |               
; |--isr-|   ====>pc
;L|--r8--|           
; |--r9--|<----sp               
IsrIRQ
 sub sp,sp,#4        ;給PC寄存器保留 reserved for PC
 stmfd sp!,{r8-r9} ;把r8-r9壓入棧

 ldr r9,=INTOFFSET ;把INTOFFSET的地址裝入r9  INTOFFSET是一個內部的寄存器,存着中斷的偏移
 ldr r9,[r9]   ;I_ISR
 ldr r8,=HandleEINT0 ;這就是我們第二個中斷向量表的入口的,先裝入r8
;===================================================================================
;哈哈,這查表方法夠好了吧,r8(入口)+index*4(別望了一條指令是4 bytes的喔),
;這不就是我們要找的那一項了嗎.找到了表項,下一步做什麼?肯定先裝入了!
;==================================================================================
 add r8,r8,r9,lsl #2  ;地址對齊,因爲每個中斷向量佔4個字節,即isr = IvectTable + Offeset * 4
 ldr r8,[r8]    ;裝入中斷服務程序的入口
 str r8,[sp,#8]   ;把入口也入棧,準備用舊招
 ldmfd sp!,{r8-r9,pc} ;施招,彈出棧,哈哈,順便把r8彈出到PC了,跳轉成功!



這段語句就實現了普通中斷異常到具體中斷的偏移處理。

這樣就可以跳轉到具體的中斷處理程序中了。

對於如果用S3C2440.S這個初始化文件,肯定是沒有二次查表,並且也沒有建立後續具體中斷的偏移地址,這樣如果我們自己僅僅在S3C2440.S添加出硬件中斷髮生後,異常跳轉,讓異常跳轉到一個C程序中,再在C程序中檢測INTPND這個寄存器的值,根據這個置調用不同的子函數也可以實現中斷。(當然在跳轉過程中,從正常情況進入中斷異常,需要進行模式轉換,棧的保存等) 

二、在ARM中執行中斷時,內存的映射情況

第一部分所說的直接跳轉地址,都是硬件執行時直接使用的地址,當MMU沒有開啓的時候,上面的地址就是物理地址,直接去實際的那塊地址,但是當MMU開啓後,上面的地址就是虛擬地址(開啓MMU之後,所有使用的地址都應該是虛擬地址了,都會被映射到某一塊對應的物理地址中去)。但是從keil forARM的工程配置

我們程序代碼是從ROM1的0x3000,0000這裏開始存放的,因此如果產生中斷了,MMU沒有開啓,那硬件直接去訪問0x0000,0000這裏,是不可能找到我們的代碼,這時候程序就跑飛了。因此爲了當硬件去訪問0x0000,0000時,其實訪問的是ROM1的0x3000,0000,我們必須開啓MMU把0x0000,0000變成一個虛擬的地址,這個虛擬地址映射的實際物理地址是0x3000,0000,這裏我們就需要介紹一個函數MMU_SetMTT,源代碼如下

void MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr)
{
    volatile U32 *pTT;
    volatile int i,nSec;
    pTT=(U32 *)_MMUTT_STARTADDRESS+(vaddrStart>>20);
    nSec=(vaddrEnd>>20)-(vaddrStart>>20);
    for(i=0;i<=nSec;i++)*pTT++=attr |(((paddrStart>>20)+i)<<20);
}

在內存映射中增加MMU_SetMTT(0x00000000,0x03F00000,0x30000000,RW_CB);

或者增加MMU_SetMTT(0x00000000,0x03f00000,(int)__ENTRY,RW_CB);  因爲ENTRY就是等於0x30000000;

這樣在中斷產生的時候就不會跑飛了,並且能按照程序代碼找到對應中斷入口等。

三、特別提醒

在上面兩個問題解決後,順利的把中斷調試通過,但是需要注意的小細節是:

1)在每次中斷髮生後,記得要對中斷進行清除,並且清除的過程是從內部向外部清除,

2)在對於的中斷函數一定要記住掛載對應的中斷向量地址上,不然同樣程序會跑飛,如果沒有注意這裏,可能會調試很久。

四、中斷向量表

這是初始化代碼中用於上面二次偏移的標號,定義在RAM中

	ALIGN

	AREA RamData, DATA, READWRITE

	^   _ISR_STARTADDRESS		; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset 	#   4
HandleUndef 	#   4
HandleSWI		#   4
HandlePabort    #   4
HandleDabort    #   4
HandleReserved  #   4
HandleIRQ		#   4
HandleFIQ		#   4

;Do not use the label 'IntVectorTable',
;The value of IntVectorTable is different with the address you think it may be.
;IntVectorTable
;@0x33FF_FF20
HandleEINT0		#   4
HandleEINT1		#   4
HandleEINT2		#   4
HandleEINT3		#   4
HandleEINT4_7	#   4
HandleEINT8_23	#   4
HandleCAM		#   4		; Added for 2440.
HandleBATFLT	#   4
HandleTICK		#   4
HandleWDT		#   4
HandleTIMER0 	#   4
HandleTIMER1 	#   4
HandleTIMER2 	#   4
HandleTIMER3 	#   4
HandleTIMER4 	#   4
HandleUART2  	#   4
;@0x33FF_FF60
HandleLCD 		#   4
HandleDMA0		#   4
HandleDMA1		#   4
HandleDMA2		#   4
HandleDMA3		#   4
HandleMMC		#   4
HandleSPI0		#   4
HandleUART1		#   4
HandleNFCON		#   4		; Added for 2440.
HandleUSBD		#   4
HandleUSBH		#   4
HandleIIC		#   4
HandleUART0 	#   4
HandleSPI1 		#   4
HandleRTC 		#   4
HandleADC 		#   4
;@0x33FF_FFA0
	END

上面的數據與後面

// Exception vector
#define pISR_RESET		(*(unsigned *)(_ISR_STARTADDRESS+0x0))
#define pISR_UNDEF		(*(unsigned *)(_ISR_STARTADDRESS+0x4))
#define pISR_SWI		(*(unsigned *)(_ISR_STARTADDRESS+0x8))
#define pISR_PABORT		(*(unsigned *)(_ISR_STARTADDRESS+0xc))
#define pISR_DABORT		(*(unsigned *)(_ISR_STARTADDRESS+0x10))
#define pISR_RESERVED	(*(unsigned *)(_ISR_STARTADDRESS+0x14))
#define pISR_IRQ		(*(unsigned *)(_ISR_STARTADDRESS+0x18))
#define pISR_FIQ		(*(unsigned *)(_ISR_STARTADDRESS+0x1c))
// Interrupt vector
#define pISR_EINT0		(*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_EINT1		(*(unsigned *)(_ISR_STARTADDRESS+0x24))
#define pISR_EINT2		(*(unsigned *)(_ISR_STARTADDRESS+0x28))
#define pISR_EINT3		(*(unsigned *)(_ISR_STARTADDRESS+0x2c))
#define pISR_EINT4_7	(*(unsigned *)(_ISR_STARTADDRESS+0x30))
#define pISR_EINT8_23	(*(unsigned *)(_ISR_STARTADDRESS+0x34))
#define pISR_CAM		(*(unsigned *)(_ISR_STARTADDRESS+0x38))		// Added for 2440.
#define pISR_BAT_FLT	(*(unsigned *)(_ISR_STARTADDRESS+0x3c))
#define pISR_TICK		(*(unsigned *)(_ISR_STARTADDRESS+0x40))
#define pISR_WDT_AC97		(*(unsigned *)(_ISR_STARTADDRESS+0x44))   //Changed to pISR_WDT_AC97 for 2440A 
#define pISR_TIMER0	 	(*(unsigned *)(_ISR_STARTADDRESS+0x48))
#define pISR_TIMER1	 	(*(unsigned *)(_ISR_STARTADDRESS+0x4c))
#define pISR_TIMER2		(*(unsigned *)(_ISR_STARTADDRESS+0x50))
#define pISR_TIMER3		(*(unsigned *)(_ISR_STARTADDRESS+0x54))
#define pISR_TIMER4		(*(unsigned *)(_ISR_STARTADDRESS+0x58))
#define pISR_UART2		(*(unsigned *)(_ISR_STARTADDRESS+0x5c))
#define pISR_LCD		(*(unsigned *)(_ISR_STARTADDRESS+0x60))
#define pISR_DMA0		(*(unsigned *)(_ISR_STARTADDRESS+0x64))
#define pISR_DMA1		(*(unsigned *)(_ISR_STARTADDRESS+0x68))
#define pISR_DMA2		(*(unsigned *)(_ISR_STARTADDRESS+0x6c))
#define pISR_DMA3		(*(unsigned *)(_ISR_STARTADDRESS+0x70))
#define pISR_SDI		(*(unsigned *)(_ISR_STARTADDRESS+0x74))
#define pISR_SPI0		(*(unsigned *)(_ISR_STARTADDRESS+0x78))
#define pISR_UART1		(*(unsigned *)(_ISR_STARTADDRESS+0x7c))
#define pISR_NFCON		(*(unsigned *)(_ISR_STARTADDRESS+0x80))		// Added for 2440.
#define pISR_USBD		(*(unsigned *)(_ISR_STARTADDRESS+0x84))
#define pISR_USBH		(*(unsigned *)(_ISR_STARTADDRESS+0x88))
#define pISR_IIC		(*(unsigned *)(_ISR_STARTADDRESS+0x8c))
#define pISR_UART0		(*(unsigned *)(_ISR_STARTADDRESS+0x90))
#define pISR_SPI1		(*(unsigned *)(_ISR_STARTADDRESS+0x94))
#define pISR_RTC		(*(unsigned *)(_ISR_STARTADDRESS+0x98))
#define pISR_ADC		(*(unsigned *)(_ISR_STARTADDRESS+0x9c))

完全相互對應,因此這樣就可以解釋爲什麼直接掛載到這些預定義的常數上,就可以跳轉到中斷了。

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章