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,#4 ;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,#4] ;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,#4 ;reserved for PC stmfd sp!,{r8-r9} ldr r9,=INTOFFSET ldr r9,[r9] ldr r8,=HandleEINT0 add r8,r8,r9,lsl #2 ;r8=r8+(r9*4) ldr r8,[r8] str r8,[sp,#8] 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_FF00HandleReset # 4 ;0x33FF_FF04HandleUndef # 4 ;0x33FF_FF08HandleSWI # 4 ;0x33FF_FF0cHandlePabort # 4 ;0x33FF_FF10HandleDabort # 4 ;0x33FF_FF14HandleReserved # 4 ;0x33FF_FF18HandleIRQ # 4 ;0x33FF_FF1C HandleEINT0 # 4 ;@0x33FF_FF20
HandleEINT1 # 4
HandleEINT2 # 4HandleEINT3 # 4HandleEINT4_7 # 4HandleEINT8_23 # 4HandleCAM # 4 ; Added for 2440.HandleBATFLT # 4HandleTICK # 4HandleWDT # 4HandleTIMER0 # 4HandleTIMER1 # 4HandleTIMER2 # 4HandleTIMER3 # 4HandleTIMER4 # 4HandleUART2 # 4;@0x33FF_FF60HandleLCD # 4HandleDMA0 # 4HandleDMA1 # 4HandleDMA2 # 4HandleDMA3 # 4HandleMMC # 4HandleSPI0 # 4HandleUART1 # 4HandleNFCON # 4 ; Added for 2440.HandleUSBD # 4HandleUSBH # 4HandleIIC # 4HandleUART0 # 4HandleSPI1 # 4HandleRTC # 4HandleADC # 4
把异常处理的宏展开,以HandlerIRQ HANDLER HandleIRQ为例:
HandlerIRQ HANDLER HandleIRQ 展开:HandlerIRQ sub sp,sp,#4 ;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,#4] ;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,#4 ;reserved for PC stmfd sp!,{r8-r9} ldr r9,=INTOFFSET ldr r9,[r9] ldr r8,=HandleEINT0 add r8,r8,r9,lsl #2 ldr r8,[r8] ;//r8 = HandleEINT0 + INTOFFSET寄存器的值*4 ;这里对应的是C代码中定义的中断响应函数的地址。怎么对应上的?见如下的代码: str r8,[sp,#8] 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. http://www.cnblogs.com/hnrainll/archive/2011/07/01/2095464.html