ARM处理器中断处理的理解
- 格式:doc
- 大小:151.00 KB
- 文档页数:4
ARM处理器中断处理的理解在发生外部中断时的处理过程如下图所示:在发生外部中断时ARM程序跳转到b HandlerIRQ;handler for IRQ interrupt根据如下语句LTORGHandlerFIQ HANDLER HandleFIQHandlerIRQ HANDLER HandleIRQHandlerUndef HANDLER HandleUndef就会调用如下HANDLER宏:HandlerIRQ HANDLER HandleIRQ ;发生外部中断时调用这个宏,跳转到IsrIRQHANDLER宏如下:MACRO$HandlerLabel HANDLER $HandleLabel;在一个标号前使用$表示程序被汇编时将使用相应的值来代替$后的标号$HandlerLabel ;可以将其想像成函数名,但这个函数名可以被不同名称(HandlerIRQ,HandlerFIQ)替代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 ;可以当成函数参数, HandleIRQ所指向的地址內裝的是IsrIRQ的入口地址;所以下面的語句实际上是load IsrIRQ的入口地址到r0ldr r0,[r0] ;load the contents(service routine start address IsrIRQ的入口地址) of HandleXXX to r0 str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack, r0也就是IsrIRQ的入口地址to SPldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR) IsrIRQ的入口地址to PCMEND具体使用时用HandlerIRQ代替上面宏內的HandleLabel:HandlerIRQsub sp,sp,#4 ;decrement sp(to store jump address) ; 先假设sp=0x1014, sp-4=0x1010stmfd sp!,{r0} ;PUSH the work register to stack(lr does't push because it return to original address);sp=sp-4=0x100C,r0->sp[0x100C],ldr r0,= HandleIRQ ;load the address of HandleIRQ to r0, HandleIRQ所指向的地址內裝的是IsrIRQ的入口地址 ;所以下面的語句实际上是加载IsrIRQ的入口地址到r0ldr r0,[r0] ;load the contents(service routine start address 即IsrIRQ的入口地址) of HandleIRQ to r0 str r0,[sp,#4] ;store the contents(ISR) of HandleIRQ to stack, r0也就是IsrIRQ的入口地址to SP;r0->sp[0x100C]+4(=0x1010),but sp=0x100C不变ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR) IsrIRQ的入口地址to PC;sp[0x100C]->r0,sp=sp+4,sp[0x1010]->pc,sp=sp+4=0x1014,这样就实现了pc指向中断处理程序函数入口下面两张图片指示了这个过程SP内容的变化:因为程序启动时已执行如下代码將用来解析具体中断函数入口的子函数IsrIRQ地址写入HandleIRQ地址; Setup IRQ handler;初始化时將IsrIRQ地址写入HandleIRQ地址ldr r0,=HandleIRQ;This routine is neededldr r1,=IsrIRQ ;取IsrIRQ的绝对地址'str r1,[r0] ;将IsrIRQ的绝对地址写入HandleIRQ地址内则执行完HandlerIRQ宏后,程序跳转到IsrIRQ。
开始执行IsrIRQ解析程序,根据INTOFFSET寄存器的值确定中断源并跳转到对应的C程序中断服务程序的入口。
下面是IsrIRQ的详细代码:IsrIRQ ; 取得具体中断的入口子函数sub sp,sp,#4 ;SP=SP-4, reserved for PCstmfd sp!,{r8-r9} ;保存R9->[SP-4], r8->[SP-8]ldr r9,=INTOFFSET ;外部中断的偏移量Register ; INTOFFSET EQU 0x4a000014, Interrupt Offset Register; The value in the INTOFFSET shows which interrupt request of IRQ mode is in the INTPND register ldr r9,[r9] ;取出Interrupt Offset Register內外部中断的偏移量,即具体是什么中断ldr r8,=HandleEINT0 ; 外部中断处理向量的首地址,此时所有中断向量地址里面沒有內容,; 编译主程式里的中断处理函数后这些中断处理向量地址里就装载了中断处理函数的入口地址add r8,r8,r9,lsl #2 ;计算出具体的中断向量所对应的标号的地址,R8=(R8+R9*4),若R9=0表示是EINT0中断ldr r8,[r8] ;取出具体的中断处理函数的入口地址赋给PC r8<-[r8]str r8,[sp,#8] ;将r8存入[SP+8],下一步取出赋给PCldmfd sp!,{r8-r9,pc} ;从SP中恢复r8,r9的值, 取出新的PC的值(即具体的中断处理C函数的入口地址)主程序中具体的中断处理函数(如void __irq Eint0_ISR())的入口地址是在编译时确定的。
知道了Eint0中断函数的地址,以其为基址,其它的中断只要通过加上Interrupt Offset Register内的偏移量就可以知道是什么中断,并得到中断函数的目标地址。
那么具体的中断处理函数(如void __irq Eint0_ISR())的入口地址是如何与start.s中HandleEINT0联系起來的?在2410addr.h中有如下定义:#define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20))上述语句把无符号整数_ISR_STARTADDRESS+0x20(即HandleEINT0 # 4)强制转换为指针,即pISR_EINT0就代表start.s 中的HandleEINT0。
主程序中void __irq Eint0_ISR(void); //关键字_irq表示是中断处理函数, 满足异常响应对现场保护和恢复的需要pISR_EINT0=(unsigned)Eint0_ISR;则指针pISR_EINT0指向中断处理函数Eint0_ISR(void)的入口地址,就这样将中断处理函数Eint0_ISR(void)的入口地址与start.s中HandleEINT0联系起來了。
中断如何切换工作状态?当发生异常中断时,处理器自动进入相应的异常模式。
;===================================================================================;呵呵,来了来了.好戏来了,这一段程序就是用来进行第二次查表的过程了.;如果说第一次查表是由硬件来完成的,那这一次查表就是由软件来实现的了.;为什么要查两次表??;没有办法,ARM把所有的中断都归纳成一个IRQ中断异常和一个FIRQ中断异常;第一次查表主要是查出是什么异常,可我们总要知道是这个中断异常中的什么中断呀!;没办法了,再查一次表呗!;===================================================================================;//外部中断号判断,通过中断服务程序入口地址存储器的地址偏移确定;//PC=[HandleEINT0+[INTOFFSET]];H|------|; |/ / / |; |--isr-| ====>pc;L|--r8--|; |--r9--|<----spIsrIRQsub sp,sp,#4 ;给PC寄存器保留 reserved for PCstmfd sp!,{r8-r9} ;把r8-r9压入栈ldr r9,=INTOFFSET ;把INTOFFSET的地址装入r9 INTOFFSET是一个内部的寄存器,存着中断的偏移ldr r9,[r9] ;I_ISRldr r8,=HandleEINT0 ;这就是我们第二个中断向量表的入口的,先装入r8;===================================================================================;哈哈,这查表方法够好了吧,r8(入口)+index*4(别望了一条指令是4 bytes的喔),;这不就是我们要找的那一项了吗.找到了表项,下一步做什么?肯定先装入了!;==================================================================================add r8,r8,r9,lsl #2 ;地址对齐,因为每个中断向量占4个字节,即isr = IvectTable + Offeset * 4 ldr r8,[r8] ;装入中断服务程序的入口str r8,[sp,#8] ;把入口也入栈,准备用旧招ldmfd sp!,{r8-r9,pc} ;施招,弹出栈,哈哈,顺便把r8弹出到PC了,跳转成功!。