
bootloader启动代码init.s解析----IRQ中断处理函数
init.s源代码如下:
;/////////////////////////////////////////////
;option.inc
_ISR_STARTADDRESS EQU 0x33ffff00
;2440addr.inc
INTOFFSET EQU 0x4a000014 ;Interruot request source offset
;/////////////////////////////////////////////
;init.s
;/////////////////////宏
MACRO
$HandlerLabel HANDLER $HandleLabel $HandlerLabel
sub sp,sp,# ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does t 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,#] ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND ;////////////////////////;入口处定义中断跳转
ResetEntry
b ResetHandler
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort IsrIRQ ;定义IsrIRQ函数
sub sp,sp,# ;reserved for PC
stmfd sp!,{r8-r9} ldr r9,=INTOFFSET
ldr r9,[r9]
ldr r8,=HandleEINT0
add r8,r8,r9,lsl # ;r8=r8+(r9*4)
ldr r8,[r8]
str r8,[sp,#]
ldmfd sp!,{r8-r9,pc} ResetHandler
;....此处省略,其他操作如看门狗、sram初始化、堆栈初始化等 ....
;Setup IRQ handler
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0] ;这样,在地址值为HandleIRQ(0x33FF_FF1C)处,装着IsrIRQ函数指针。
;....此处省略,拷贝程序、跳转main等....
;///////////////////////////////////////////////////////////////;定义中断地址
^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset # ;0x33FF_FF04
HandleUndef # ;0x33FF_FF08
HandleSWI # ;0x33FF_FF0c
HandlePabort # ;0x33FF_FF10
HandleDabort # ;0x33FF_FF14
HandleReserved # ;0x33FF_FF18
HandleIRQ # ;0x33FF_FF1C
HandleEINT0 # ;@0x33FF_FF20
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
把异常处理的宏展开,以HandlerIRQ HANDLER HandleIRQ为例:
HandlerIRQ HANDLER HandleIRQ
展开:
HandlerIRQ
sub sp,sp,# ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does t push because it return to original address)
ldr r0,=HandleIRQ ;load the address of HandleIRQ to r0
ldr r0,[r0] ;load the contents(service routine start address) of HandleIRQ
;这里的contents是参照下面Setup IRQ handler ,为IsrIRQ函数指针
str r0,[sp,#] ;store the contents(ISR) of HandleIRQ to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
中断发生时,b HandlerIRQ
HandlerIRQ 展开后代码如上所示,流程如下图,最终跳转到了中断处理函数IsrIRQ【*HandleIRQ = IsrIRQ】:
在IsrIRQ里面,跳转到具体的中断处理函数。
IsrIRQ
sub sp,sp,# ;reserved for PC
stmfd sp!,{r8-r9} ldr r9,=INTOFFSET
ldr r9,[r9]
ldr r8,=HandleEINT0
add r8,r8,r9,lsl #
ldr r8,[r8] ;//r8 = HandleEINT0 + INTOFFSET寄存器的值*4 ;这里对应的是C代码中定义的中断响应函数的地址。怎么对应上的?见如下的代码:
str r8,[sp,#]
ldmfd sp!,{r8-r9,pc}
C代码中:
关联中断处理函数和启动代码中的中断地址。 //option.h
#define _ISR_STARTADDRESS 0x33ffff00
//addr.h
#define pISR_UART1 (*(unsigned *)(_ISR_STARTADDRESS+0x7c))
#define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20)) ;0x33FF_FF20
//IsrBind() // isr_init.c
pISR_UART1=(unsigned int)ProcomUart1IsrFunction; 在逻辑代码中,地址值是绝对的,因此C中的_ISR_STARTADDRESS正是启动代码中的_ISR_STARTADDRESS。 那pISR_UART1表示的就是*(HandleUART1)。pISR_EINT0就是*(HandleEINT0).
这里可以得出:我们的C代码中可以在pISR_EINT0里面分发外部中断,也可以直接绑定到具体中断源。这无非是一个中断地址表和中断处理函数的对应。
IsrIRQ跳转图解:
下面总结中断触发如何引发调用中断函数:
//先后关系:
硬件中断发生
--->
中断相关寄存器的值产生相应变化(INT_OFFSET反映了具体的外部中断源)
--->
b HandlerIRQ() ---> HandleIRQ() --->
IsrIRQ() --->//如上图IsrIRQ的解析所示,根据INT_OFFSET和事先制定的地址对照表,跳转到C中初始化时绑定的中断函数 one of irq_funcs
扩展阅读:
1. 2440之中断管理 http://www.cnblogs.com/hnrainll/archive/2011/07/01/2095464.html