bootloader启动代码init.s解析----IRQ中断处理函数

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_FF00
HandleReset     #   4        ;0x33FF_FF04
HandleUndef     #   4        ;0x33FF_FF08
HandleSWI       #   4        ;0x33FF_FF0c
HandlePabort    #   4        ;0x33FF_FF10
HandleDabort    #   4        ;0x33FF_FF14
HandleReserved  #   4        ;0x33FF_FF18
HandleIRQ       #   4        ;0x33FF_FF1C 
HandleEINT0     #   4        ;@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,#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. 2440之中断管理 http://www.cnblogs.com/hnrainll/archive/2011/07/01/2095464.html

原文地址:https://www.cnblogs.com/mylinux/p/4140691.html