对于学习ARM的朋友来说,中断处理是一块硬骨头,尤其是中断向量表以及中断的跳转。下面主要对中断向量表的建立问题及重映射问题进行探讨。近来做一些东 西用到中断时,总会出现一些问题。
各种Bootloader的初始化相关代码摘要,首先来看下面的程序:
^ DRAM_BASE //DRAM的基地址
HandleReset # 4 // 空留4个单元
HandleUndef # 4
HandleSwi # 4
HandlePrefetch # 4 //用于填充地址的
HandleAbort # 4
HandleReserv # 4
HandleIrq # 4
HandleFiq # 4
注: 这里的^是RMAP,#是FIELD,分配的意思
就是在SDARM的BANK0开始的地方定义了一个中断向量表,相当于0地址的FLASH。空留单元用于存放中断程序的入口地址。
ExceptionHandlerTable // 实际的映射地址
DCD UserCodeArea
DCD SystemUndefinedHandler
DCD SystemSwiHandler
DCD SystemPrefetchHandler
DCD SystemAbortHandler
DCD SystemReserv
DCD SystemIrqHandler
DCD SystemFiqHandler
这个表中存放的是汇编程序中中断处理函数的入口地址,每一项对应一个中断函数。这次的跳转后就进入了C服务程序。
从初始化程序的开始处来看:(各种Bootloader的初始化代码)
AREA Init, CODE, READONLY //相当于init进程
ENTRY //入口
B Reset_Handler
B Undefined_Handler //无条件的跳转
B SWI_Handler
B Prefetch_Handler
B Abort_Handler
NOP Reserved vector
B IRQ_Handler
B FIQ_Handler
FIQ_Handler
SUB sp, sp, #4
STMFD sp!, {r0} FD满递减堆栈 执行寄存器压栈操作.
LDR r0, =HandleFiq 汇编里的处理函数地址,然后跳到C中,在DRAM。
LDR r0, [r0] 中断向量地址给R0.
STR r0, [sp, #4] 中断向量地址给PC
LDMFD sp!, {r0, pc}
稍微解释一下:
首先执行了压栈,然后给出了中断入口地址.这个HandleFiq就是我们前面的在DRAM中建立的中断向量其中一个的地址。
在 HandleFiq开始的四个字节中,放着汇编中断处理函数的入口地址。
那么汇编中断处理函数的地址是如何放到DRAM中断向量表里的呢?
上面的第一个表就起作用了。看下面这段程序:
EXCEPTION_VECTOR_TABLE_SETUP
LDR r0, =HandleReset
LDR r1, =ExceptionHandlerTable
MOV r2, #8
ExceptLoop
LDR r3, [r1], #4
STR r3, [r0], #4
SUBS r2, r2, #1 //填充8个地址
BNE ExceptLoop //从表里取出来给了HandleReset后面的空间
这一段把ExceptionHandlerTable里的中断处理函数的地址拷给了SDRAM里的中断向量表。这样两者就联系起来了。
在执行程序开始的跳转之后就自然跳到了*****Handler.真正的处理函数如下:
它实际上只调用了C语言的中断处理函数,其他什么也没做。
SystemFiqHandler
IMPORT ISR_FiqHandler
STMFD sp!, {r0-r7, lr}
BL ISR_FiqHandler //真正的中断处理服务函数
LDMFD sp!, {r0-r7, lr}
SUBS pc, lr, #4
它实际上只调用了C语言的中断处理函数,其他什么也没做。写中断处理服务程序其实就只写C中相应的处理部分就好了。
void ISR_FiqHandler(void)
{
IntOffSet = (U32)INTOFFSET;
(IntOffSet>>2)
(*InterruptHandlers[IntOffSet>>2])(); // Call interrupt service routine
}
其实就是将中断向量表重映射,以提高中断的响应速度。代码在SDRAM的运行速度要比在FLASH中运行速度快。