S3C2410A的异常机制[中断处理]

以YLE2410A开发板为例,说说ARM的异常是如何处理滴。

 

 

;Pre-defined constants

USERMODE     EQU   0x10

FIQMODE        EQU   0x11

IRQMODE        EQU   0x12

SVCMODE       EQU   0x13

ABORTMODE   EQU   0x17

UNDEFMODE   EQU   0x1B

MODEMASK     EQU   0x1F

NOINT             EQU   0xC0

 

;The location of stacks

UserStack     EQU   (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~ 

SVCStack      EQU   (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~

UndefStack   EQU   (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~

AbortStack    EQU   (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~

IRQStack       EQU   (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~

FIQStack       EQU   (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~ 

 

 

首先当然是进行堆栈初始化啦。。。有7种不同的异常处理,因为它们的堆栈是独立滴,所以需要单独进行堆栈分配以及初始化。初始化代码如下:

 

InitStacks

;Don't use DRAM,such as stmfd,ldmfd......

;SVCstack is initialized before

;Under toolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'

mrs r0,cpsr

bic r0,r0,#MODEMASK

orr r1,r0,#UNDEFMODE|NOINT

msr cpsr_cxsf,r1 ;UndefMode

ldr sp,=UndefStack

orr r1,r0,#ABORTMODE|NOINT

msr cpsr_cxsf,r1 ;AbortMode

ldr sp,=AbortStack


orr r1,r0,#IRQMODE|NOINT

msr cpsr_cxsf,r1 ;IRQMode

ldr sp,=IRQStack

    

orr r1,r0,#FIQMODE|NOINT

msr cpsr_cxsf,r1 ;FIQMode

ldr sp,=FIQStack


bic r0,r0,#MODEMASK|NOINT

orr r1,r0,#SVCMODE

msr cpsr_cxsf,r1 ;SVCMode

ldr sp,=SVCStack

;USER mode has not be initialized.

mov pc,lr 


做好这个初始化之后呢,当然就是IRQ的分配了。就说说普通的IRQ吧,一般在WINCE下很难用到FIQ滴。嘿嘿。。。一家之言。

因为ARM有7种不同的异常处理,所以:

 

LTORG   

HandlerFIQ      HANDLER HandleFIQ

HandlerIRQ      HANDLER HandleIRQ

HandlerUndef    HANDLER HandleUndef

;HandlerUndef

; sub sp, sp, #4 ;decrement sp(to store jump address)

; stmfd sp!, {r14} ;PUSH the work register to stack(lr does't push because it return to original address)

; ldr r0, =HandleUndef ;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}

HandlerSWI      HANDLER HandleSWI

HandlerDabort   HANDLER HandleDabort

HandlerPabort   HANDLER HandlePabort

也是需要7种不同的中断向量了哦。这个和单片机是差不多滴。。。一般都是放在前面撒。

 

异常服务程序 

这里不用中断(interrupt)而用异常(exception),毕竟中断只是异常的一种情况,

 

下面主要分析的是“中断异常”说白了,就是我们平时单片机里面用的中断!!!所有由器件引起的中断,例如TIMER中断,UART中断,外部中断等等,都有一个统一的入口,那就是中断异常 IRQ ! 然后从IRQ的服务函数里面分辨出,当前究竟是什么中断,再跳转到相应的中断服务程序。这样看来,ARM比单片机要复杂一些了,不过原理是不变的。 

 

 

HandlerIRQ HANDLER HandleIRQ 

 

这里是一个宏定义,我们再找到这个宏,看他是怎么定义的: 

 

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 

 

用 HandlerIRQ 将这个宏展开之后得到的结果实际是这样的 

 

HandlerIRQ 

     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,=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 还是一个标号,IRQ异常向量就是跳转到这里执行的,这里粗略看一下,应该是保存现场,然后跳转到真正的处理函数,那么很容易发现了这么一句 ldr r0,=HandleIRQ ,没错,我们又找到了一个标号 HandleIRQ ,看来真正的处理函数应该是这个 HandleIRQ ,继续寻找。。。。。

 

     AREA RamData, DATA, READWRITE 

 

     ^ _ISR_STARTADDRESS         ; _ISR_STARTADDRESS=0x33FF_FF00 

HandleReset           # 4 

HandleUndef           # 4 

HandleSWI              # 4 

HandlePabort          # 4 

HandleDabort         # 4 

HandleReserved    # 4 

HandleIRQ              # 4 

 

最后我们发现在这里找到了 HandleIRQ ,^ 其实就是 MAP ,这段程序的意思是,从 _ISR_STARTADDRESS 开始,预留一个变量,每个变量一个标号,预留的空间为 4个字节,也就是32BIT,其实这里放的是真正的C写的处理函数的地址,说白了,就是函数指针 - -    这样做的话就很灵活了 

 

接着,我们需要安装IRQ处理句柄,说白了,就是设置处理函数的地址,让PC指针可以正确的跳转。于是我们在接着的找到安装句柄的语句 

 

       ; 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] 

 

说白了就是将 IsrIRQ 的地址填到 HandleIRQ对应的地址里面,前面说了HandleIRQ 放的是中断处理的函数的入口地址,我们继续找 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] 

     str     r8,[sp,#8] 

     ldmfd     sp!,{r8-r9,pc} 

 

要理解这个代码,得先学学S3C2410的中断系统了,INTOFFSET存放的是当前中断的偏移号,根据偏移就知道当前是哪个中断源发生的中断

 

 

 

注意了,我们说的是中断,而不是异常,看看原来的表是啥样子的 

 

     ^ _ISR_STARTADDRESS         ; _ISR_STARTADDRESS=0x33FF_FF00

 

HandleReset         # 4 

HandleUndef         # 4 

HandleSWI           # 4 

HandlePabort       # 4 

HandleDabort       # 4 

HandleReserved   # 4 

HandleIRQ            # 4 

HandleFIQ             # 4 

 

HandleEINT0         # 4 

HandleEINT1         # 4 

HandleEINT2         # 4 

HandleEINT3         # 4 

....... 

 

可以看到,前面几个是异常,从      HandleEINT0 就是 IRQ异常的向量存放的地方了,这样就可以理解为什么上面 IsrIRQ 里面里面要执行那条指令 

     ldr     r8,=HandleEINT0 

     add     r8,r8,r9,lsl #2 

道理很简单, HandleEINT0 就是所有IRQ中断向量表的入口,在这个地址上面,加上一个适当的偏移量,INTOFFSET ,那么我们知道现在,到底是哪个IRQ在申请中断了。 

 

至于具体怎么跳转的? 

首先,我们说了,HandleEINT0 开始的一段内存里面,存放的就是中断服务函数的函数指针,ARM的体系的话,每个指针变量就是占4个字节,这里就解释了,为什么这里为每个标号分配了4个字节的空间,里面放的就是函数指针!!!下面再看看怎么跳转,继续看 IsrIRQ 里面就实现了跳转了 

     str         r8,[sp,#8] 

     ldmfd     sp!,{r8-r9,pc} 

其实最核心就是这两句了,先查找到当前中断服务程序的地址,将他放到 R8 里面,然后出栈,弹出给PC那么PC很自然就跳到中断服务程序了。至于这里的堆栈问题又是一个非常棘手的,需要好好的参透ARM的中断架构,需要了解的可以自己仔细的阅读 《ARM体系结构与编程》里面说的很详细。我们这里的重点是研究怎么跳转

你可能感兴趣的:(编程,c,timer,exception,service,WinCE)