ARM异常---一个Uart中断的触发处理过程:
首先给出一些定义:
//2440addr.inc
INTOFFSET EQU 0x4a000014 ;Interruot request source offset
//option.inc
_ISR_STARTADDRESS EQU 0x33ffff00
//2440init.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
下面进入正题:
//2440init.s
PRESERVE8 AREA RESET,CODE,READONLY ENTRY EXPORT __ENTRY __ENTRY ResetEntry b ResetHandler ;0x0 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 b EnterPWDN ; Must be @0x20. ... HandlerIRQ HANDLER HandleIRQ ... ............. ; Setup IRQ handler//建立中断表 ldr r0,=HandleIRQ ;This routine is needed ldr r1,=IsrIRQ ;if there isn t 'subs pc,lr,#4' at 0x18, 0x1c str r1,[r0] ................ ^ _ISR_STARTADDRESS ;0x33ffff00 HandleReset # 4 HandleUndef # 4 HandleSWI # 4 HandlePabort # 4 HandleDabort # 4 HandleReserved # 4 HandleIRQ # 4 HandleFIQ # 4 ;0x33ffff1C ;IntVectorTable ;@0x33FF_FF20 HandleEINT0 # 4 ;0x33ffff20 HandleEINT1 # 4 HandleEINT2 # 4 .................................
HandleUART1 # 4 ;0x33ffff7C
.................................
uart是一个外部中断,走的是FIQ.
外部中断 --> b HandlerFIQ ;
看代码发现HandlerFIQ在init.s中进行了宏定义,展开之后得到:
//展开宏 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 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)
可以看到,HandlerIRQ是一个标准的中断处理过程(正因如此使用了宏进行封装): 首先保存现场,然后跳转到HandleIRQ,从HandleIRQ回来之后恢复现场.
HandleIRQ其实是一个函数指针,它可以在程序中被我们指向某一个处理函数. 这里我们指向了IsrIRQ. 在IsrIRQ里,我们读取INTOFFSET寄存器的值,加上外部中断的起始值HandleEINT0,这样我们就获得了世纪的中断入口HandleUART1. 通过ldmfd sp!,{r8-r9,pc},我们跳转进入了HandleUART1对应的实际的中断处理函数(见后面的分析).
//2440init.s
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}
上面说到,"通过ldmfd sp!,{r8-r9,pc},我们跳转进入了HandleUART1对应的实际的中断处理函数." 怎么跳转的呢,在代码里,我们又实现并绑定了HandleUART1的处理函数Uart1_TxRxInt:
//2440addr.h #define pISR_UART1 (*(unsigned *)(_ISR_STARTADDRESS+0x7c))
//2440lib.c pISR_UART1=(unsigned)Uart1_TxRxInt; extern unsigned char UartBuf1[256]; void __irq Uart0_TxRxInt(void)//这里只处理了接收中断 { unsigned char *pbuf = UartBuf1; if(rSUBSRCPND & BIT_SUB_RXD0) //接收中断 { rINTSUBMSK |= BIT_SUB_RXD0; while((rUFSTAT0&0x3f)) { *pbuf++ = rURXH0; } *pbuf = '\0'; rINTSUBMSK &= ~BIT_SUB_RXD0; rSRCPND |= BIT_UART0; rINTPND |= BIT_UART0; rINTSUBMSK &= ~(BIT_SUB_TXD0); } }