參考文檔
S3C2440之MMU驅動代碼模板
keil下基於arm9的TX2440開發板的外部中斷編寫
S3C2440中斷過程詳解(ADS,TQ2440)
mini2440—–keil for ARM之中斷一
前言
本文主要描述如何在Keil自帶的S3C2440.s文件中添加中斷配置及中斷地址映射,從而可以在中斷產生時跳轉到用戶代碼中的中斷服務函數。目前在TQ2440開發板是實測可用。
本文所有代碼都是截取代碼,“…”代表其還有上下文。可根據代碼中上下文的殘缺部分找到該代碼添加的位置。
一,添加中斷相關寄存器地址符號映射
因爲原始S3C2440.s文件中不含中斷相關寄存器的地址,所以需要在開頭添加:
...
; * RAM_INTVEC: when set the startup code copies exception vectors
; * from execution address to on-chip RAM.
; */
;=================
; INTERRUPT
;=================
SRCPND EQU 0x4a000000 ;Interrupt request status
INTMOD EQU 0x4a000004 ;Interrupt mode control
INTMSK EQU 0x4a000008 ;Interrupt mask control
PRIORITY EQU 0x4a00000c ;IRQ priority control <-- May 06, 2002 SOP
INTPND EQU 0x4a000010 ;Interrupt request status
INTOFFSET EQU 0x4a000014 ;Interruot request source offset
SUSSRCPND EQU 0x4a000018 ;Sub source pending
INTSUBMSK EQU 0x4a00001c ;Interrupt sub mask
_ISR_STARTADDRESS EQU 0x33ffff00
; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
Mode_USR EQU 0x10
Mode_FIQ EQU 0x11
Mode_IRQ EQU 0x12
...
二,添加中斷向量偏移表
在文件末尾添加中斷向量表,對應2440addr.h文件中相應的中斷向量地址,起始地址參見_ISR_STARTADDRESS。
...
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ENDIF
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
三,添加程序__ENTRY入口點
定義程序入口點的位置,在ram調試狀態下,就是0x30000000對應的Reset Handler的跳轉代碼。
...
; Mapped to Address 0.
; Absolute addressing mode must be used.
; Dummy Handlers are implemented as infinite loops which can be modified.
EXPORT __ENTRY
__ENTRY
Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
...
四,修改添加中斷現場保護及跳轉代碼
除了IRQ中斷以外的所有中斷的現場保護及跳轉代碼,因爲這些中斷在向量表都只有一種狀態,所以不需要偏移。這裏是宏定義的實際代碼,宏定義的調用見五,修改添加IRQ中斷現場保護及跳轉代碼中的代碼。
...
GPJCON_Val EQU 0x00000000
GPJUP_Val EQU 0x00000000
;// </e> I/O Setup‘
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to original address)
ldr r0,=$HandleLabel;load the address of HandleXXX to r0
ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
;----------------------- CODE --------------------------------------------------
PRESERVE8
; Area Definition and Entry Point
; Startup Code must be linked first at Address at which it expects to run.
...
五,修改添加IRQ中斷現場保護及跳轉代碼
IRQ中斷的現場保護及跳轉代碼。IRQ中斷有很多可能,比如按鍵的外部中斷,串口中斷,IIC中斷。。。等,需要根據實際中斷標誌指示跳轉到對應的服務函數,所以需要一套獨立的中斷處理代碼。此處代碼來自《mini2440—–keil for ARM之中斷一》。
...
ELSE
IRQ_Addr DCD IRQ_Handler
ENDIF
FIQ_Addr DCD FIQ_Handler
;Undef_Handler B Undef_Handler
;IF :DEF:__RTX
;ELSE
;SWI_Handler B SWI_Handler
;ENDIF
;PAbt_Handler B PAbt_Handler
;DAbt_Handler B DAbt_Handler
;IRQ_Handler PROC
;EXPORT IRQ_Handler [WEAK]
;B .
;ENDP
;FIQ_Handler B FIQ_Handler
FIQ_Handler HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
Undef_Handler HANDLER HandleUndef
SWI_Handler HANDLER HandleSWI
DAbt_Handler HANDLER HandleDabort
PAbt_Handler HANDLER HandlePabort
;呵呵,來了來了.好戲來了,這一段程序就是用來進行第二次查表的過程了.
;如果說第一次查表是由硬件來完成的,那這一次查表就是由軟件來實現的了.
;爲什麼要查兩次表??
;沒有辦法,ARM把所有的中斷都歸納成一個IRQ中斷異常和一個FIRQ中斷異常
;第一次查表主要是查出是什麼異常,可我們總要知道是這個中斷異常中的什麼中斷呀!
;沒辦法了,再查一次表唄!
;===================================================================================
;外部中斷號判斷,通過中斷服務程序入口地址存儲器的地址偏移確定
;PC=[HandleEINT0+[INTOFFSET]]
;IsrIRQ
IRQ_Handler
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了,跳轉成功!
; Reset Handler
EXPORT Reset_Handler
...
六,屏蔽USER模式,開啓SVC模式,打開IRQ中斷
因爲在ram調試時,code地址是從0x30000000開始,而IRQ中斷跳轉會直接調到0x00000018,然而我們期望跳到0x30000018(這個纔是真正的IRQ入口),所以需要用mmu做一個0x00000018到0x30000018的映射(這個在TQ2440工程中的mmu初始化代碼中完成)。但是mmu調用cp15協處理器的彙編代碼在USER模式下會出問題?!目前只能先用SVC模式。此外,Keil自帶的S3C2440.s文件默認是關閉SVC模式的IRQ中斷,需要修改代碼開啓。
...
SUB R0, R0, #FIQ_Stack_Size
; Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size
; Enter Supervisor Mode and set its Stack Pointer
;MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MSR CPSR_c, #Mode_SVC:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size
; Enter User Mode and set its Stack Pointer
;MSR CPSR_c, #Mode_USR
;MSR CPSR_c, #Mode_USR:OR:I_Bit:OR:F_Bit
;MOV SP, R0
;SUB SL, SP, #USR_Stack_Size
; Enter User Mode and set its Stack Pointer
;MSR CPSR_c, #Mode_USR
IF :DEF:__MICROLIB
EXPORT __initial_sp
...
補充一下
再去看了一下Keil自帶的S3C2440的Blinky例程,發現其實裏面是有中斷配置的,而且工作的很正常。其使用的是用戶模式並開啓了IRQ和FIQ,詳見如下代碼:
...
; Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size
; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR
MOV SP, R0
SUB SL, SP, #USR_Stack_Size
; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR
IF :DEF:__MICROLIB
...
而最關鍵的中斷入口函數,直接是用C寫的:
...
/* Common interrupt handler, handles all interrupts */
__irq void IRQ_Handler (void)
{
unsigned int msk = (1 << INTOFFSET);
switch (msk) {
case BIT_TIMER0: /* Timer 0 interrupt mask */
if (ticks++ >= ((Speed << 3)+4)) {/* Set Clock to 1 periodically */
ticks = 0;
Clock = 1;
}
break;
}
SRCPND = msk; /* Clear Interrupt */
INTPND = msk;
}
...
這個方式也很是高明,有點像PIC的中斷處理方式,優先級完全由代碼決定。一開始先入爲主,網上說沒有也就這麼認爲,現在想來,三星/ARM的工程師怎麼可能這麼水?總之此處中斷服務函數可以處理所有的IRQ中斷,不過要寫其他的中斷,比如FIQ,SWI等等,還是要動啓動文件吧。。。