44b启动代码详解
S3C44B0的IRQ向量中断模式和非向量中断模式的概念:
注:arm7的工作模式是arm7的这个内核提供的,而厂商在生产时,在arm7这个内核的基础上,外扩了各种中断及其相关的中断控制器,而这些中断发生一般会进入IRQ模式,这点很好理解。
(1)向量中断模式(IRQ支持,FIQ不支持)
当系统发生IRQ中断时候,cpu的PC会读取位于0x18处的IRQ中断指令(也就是说跳到地址0x18处去执行指令),然后CPU自动读取对应于该中断源确定地址上的指令取代0x18处的指令(这个过程是由系统自动完成的,不需要用户编程),通过跳转指令,系统就直接跳转到对应地址,这样节省了中断处理时间,提高中断处理速度。——通俗点说,如果采用了向量中断模式,一旦发生中断,CPU就会跳到相应的地址来执行指令。例如ADC中断的向量地址为0xC0,一旦发生ADC中断,PC就先跳到了地址0x18再跳到0xC0上来执行命令,但对于用户来说,就跟一发生中断就直接跳到了0xC0上没有区别,这个先跳到0x18的过程可以忽略不计,由系统自动完成。如果在0xC0处放如下代码: ldr PC , =HandlerADC ,当ADC中断产生的时候系统会自动跳转到HandlerADC函数中。
(2)非向量中断模式
这种模式处理方式是一种传统的中断处理方法,当系统产生中断的时候,系统将interrupt pending寄存器中对应标志位置位(当然向量中断模式也有这步发生),然后跳转到地址0x18处。用户需要在地址0x18处写一个统一中断函数,该函数通过读取interrupt pending寄存器中对应标志位,来判断中断源,并根据优先级关系再跳到对应中断源的处理代码中。
二、向量中断模式的启动代码分析及编程。
1、中断向量模式中,具体实现如下:
中断服务入口地址处,写入一个调用宏的指令(类似于子程序),这个宏是一个“加载程序”,把中断服务的首地址装载于pc中。具体调用一个框图来表示,结合以下的程序理解,接下来还有一个针对框图的具体的讲解。以ADC中断为例子:
注:(1)步骤①②③在出现在44binit.s,详见我的启动代码中的注释。
(2)_ISR_STARTADDRESS的定义在option.inc中:
_ISR_STARTADDRESS EQU 0xc7fff00
(3)步骤④在44b.H中定义:
#define pISR_ADC (*(unsigned *)(_ISR_STARTADDRESS+0x20))
(4)在main.C或者其他的c文件里,写入:
void __IRQ ISR_ADC(){……}和pISR_ADC=(unsignde)ISR_ADC
必须加入在中断服务程序中__IRQ,以高速编译器这个是一个中断服务程序,这样编译器会在编译时候加上返回用户模式的代码,以实现中断后程序的自动返还。
2、具体的代码解释:
从上面的流程图看出,44B0 中断系统中有两张中断转移表,经过二重转移才跳到中断处理程序。第一张中断向量表由硬件决定,所在区域为ROM(flash),地址空间从0X00开始,其中0X00-0X1C为异常向量入口地址,0X20-0XC0为中断向量入口地址。另一张中断向量表在RAM 中,可以随便改,其位置在程序连接后才定。由于 RAM 放在地址空间的高端(距离中断向量超过了 32M),为了从第一张中断向量表跳到第二张中断向量表,故在第一张中断向量表对应位置上写上 ldr PC,# interrupt_service 如:ldr PC,=HandlerEINT4567。
(1) 一级中断向量表 以下为汇编代码片断,详见44B0数据手册11章——中断控制器)
AREA Init,CODE,READONLY
;说明:关键字ENTRY告诉编译器保留这段代码;从代码看Init段就是要写入0x00地址的原始中断向量,因此把这个文件编译生成的44binit.O和Init填入ADS-Linker-Layout页对应项中。【这样编译器会把该段代码编译到0X0地址。】
ENTRY ;ENTRY程序入口标号需要顶格式写,否则出错。
b ResetHandler ;复位异常 0x0000 0000
b HandlerUndef ;未定义异常 0x0000 0004
b HandlerSWI ;软件中断异常 0x0000 0008
b HandlerPabort ;指令预取异常 0x0000 000C
b HandlerDabort ;数据预取异常 0x0000 0010
b . ;保留 0x0000 0014
b HandlerIRQ ;外部中断外设中断都是在这里扩展的 0x0000 0018
b HandlerFIQ ;快速中断 0x0000 001C
VECTOR_BRANCH
ldr pc,=HandlerEINT0 ;mGA H/W interrupt vector table 0x00000020
ldr pc,=HandlerEINT1 ;
ldr pc,=HandlerEINT2 ;
……
……
b .
ldr pc,=HandlerADC ;mGKB 0x0000 00C0
b . ;
b . ;
b . ;
b . ;
b . ;mGKB
b .
b .
;0xe0=EnterPWDN
ldr pc,=EnterPWDN
LTORG
通过这段代码,就在44B0的ROM中以0x00为起始地址的地方建立起了一张中断向量表,而且这个表的顺序完全符合44B0数据手册中对中断向量地址的定义要求。
2、 二级中断向量表 ^ _ISR_STARTADDRESS ;一般为 #define _ISR_STARTADDRESS 0xc7fff00
HandleReset # 4
HandleUndef # 4
HandleSWI # 4
HandlePabort # 4
HandleDabort # 4
HandleReserved # 4
HandleIRQ # 4
HandleFIQ # 4
;Do not use the label ’IntVectorTable’,
;because armasm.exe can not recognize this label correctly.
;the value is different with an address you think it may be.
;IntVectorTable
HandleADC # 4
HandleRTC # 4
……
……
HandleEINT2 # 4
HandleEINT1 # 4
HandleEINT0 # 4 ;0xc1(c7)fff84
END
这段第一行 “^” 符号表示在RAM区开辟空间,就是伪代码中的MAP。
^ _ISR_STARTADDRESS 表示在RAM的_ISR_STARTADDRESS处开辟一段空间。这里开辟的空间是用来存放中断服务程序及其他异常处理程序地址的。每个中断或其他异常都开辟4个字节的空间,这是因为RAM区的程序地址要4个字节才能放得下。而中断程序的地址是应用程序在使用这个中断时,将中断服务程序的地址存入这对应的空间内。
3、 中断向量表最终指向 一般在44B.H等头文件能找到对应于二级中断向量表的宏定义:片断如下
#define pISR_RESET (*(unsigned *)(_ISR_STARTADDRESS+0x0))
#define pISR_UNDEF (*(unsigned *)(_ISR_STARTADDRESS+0x4))
#define pISR_SWI (*(unsigned *)(_ISR_STARTADDRESS+0x8))
#define pISR_PABORT (*(unsigned *)(_ISR_STARTADDRESS+0xc))
#define pISR_DABORT (*(unsigned *)(_ISR_STARTADDRESS+0x10))
#define pISR_RESERVED (*(unsigned *)(_ISR_STARTADDRESS+0x14))
#define pISR_IRQ (*(unsigned *)(_ISR_STARTADDRESS+0x18))
#define pISR_FIQ (*(unsigned *)(_ISR_STARTADDRESS+0x1c))
#define pISR_ADC (*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_RTC (*(unsigned *)(_ISR_STARTADDRESS+0x24))
#define pISR_UTXD1 (*(unsigned *)(_ISR_STARTADDRESS+0x28))
#define pISR_UTXD0 (*(unsigned *)(_ISR_STARTADDRESS+0x2c))
……
……
#define pISR_EINT2 (*(unsigned *)(_ISR_STARTADDRESS+0x7c))
#define pISR_EINT1 (*(unsigned *)(_ISR_STARTADDRESS+0x80))
#define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x84))
一般在C文件中将自定义的中断服务子程序地址放进对应宏中,如:
pISR_TIMER0 = (int) time_interrupt()
2、向量中断和非向量中断模式的详细源码分析
(1)向量中断
在1中讲到当发生IRQ中断时,中断控制器会在数据总线上加载分支指令跳到相应中断源的向量地址,即一级中断向量表中对应的中断向量入口地址。
ldr pc,=HandlerEINT0 ; 0x20
ldr pc,=HandlerEINT1
……
ldr pc,=HandlerTIMER0 ; 0x60
ldr pc,=HandlerTIMER1
……
ldr pc,=HandlerADC ; 0xb4
……
由上述代码可知,程序会跳转到HandlerXXX中,定义如下:
HandlerFIQ HANDLER HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
HandlerADC HANDLER HandleADC
HandlerRTC HANDLER HandleRTC
……
HandlerEINT1 HANDLER HandleEINT1
HandlerEINT0 HANDLER HandleEINT0
HandlerXXX HANDLER HandleXXX为一个汇编宏,定义如下:
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
;由于ADS仅支持FD(满递减)型堆栈
sub sp,sp,#4 ;将堆栈退一个字用于保存下面用到的R0
stmfd sp!,{r0} ;将R0压入堆栈
ldr r0,=$HandleLabel ;将HandleLabel的地址赋给R0
ldr r0,[r0] ;将HandleLabel的地址指向的内容(实际的执行地址)赋给R0
str r0,[sp,#4] ;将对应的中断函数首地址入栈保护
ldmfd sp!,{r0,pc} ;将中断函数的首地址出栈,放入PC中
MEND
由上宏定义可知,程序又跳到HandleXXX中执行,即二级中断向量表中的地址。为此综上所述,便可跳到对应的中断服务子程序地址,执行中断!
(2)非向量中断
前面讲过,一旦产生IRQ中断,微控制器会切换到IRQ模式,并且跳转到一级向量表0x0000018地址处执行程序;而FIQ则跳到0x000001C地址处。即
ENTRY
b ResetHandler ;复位异常 0x0000 0000
……
b HandlerIRQ ;外部中断 0x0000 0018
b HandlerFIQ ;快速中断 0x0000 001C
……
然后跳转到宏HandlerIRQ HANDLER HandleIRQ 或 HandlerFIQ HANDLER HandleFIQ。
由以下代码可知,HandleIRQ最终对应的是IsrIRQ地址标号。
;****************************************************
;* Setup IRQ handler *
;****************************************************
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ str r1,[r0]
上述代码位于ResetHandler为标号的汇编代码段内,板子上电或复位后,都要从位于0x0执行b ResetHandler 的指令跳转到ResetHandler为标号的汇编代码段处执行,也就将HandleIRQ初始化为IsrIRQ。IsrIRQ代码段如下:
IsrIRQ ;using I_ISPR register.
sub sp,sp,#4 ;reserved for PC
stmfd sp!,{r8-r9}
;IMPORTANT CAUTION
;if I_ISPC is not used properly, I_ISPR can be 0 in this routine.
ldr r9,=I_ISPR
ldr r9,[r9]
cmp r9, #0x0 ;If the IDLE mode work-around is used, r9 may be 0 sometimes.
beq %F2 ;相等就向后搜索跳转标号2.
mov r8,#0x0
0
movs r9,r9,lsr #1
bcs %F1 ;>= 就向后搜索跳转标号1.
add r8,r8,#4
b %B0 ;向前搜索跳转标号0.
1
ldr r9,=HandleADC
add r9,r9,r8 ;以HandleADC(r9)为基址,r8对应所发生中断源的偏移量
ldr r9,[r9] ;结果为二级中断向量表中对应中断标号地址HandleXXX
str r9,[sp,#8]
ldmfd sp!,{r8-r9,pc}
2
ldmfd sp!,{r8-r9}
add sp,sp,#4
subs pc,lr,#4
上述这段程序就是用来处理非向量中断(比向量中断方式多执行的代码段),具体判断I_ISPR中各位是否置1 置1表示目前此中断等待响应(每次只能有一位置1),从最高优先级中断位开始判断,检测到等待服务,中断就将pc置为中断服务函数首地址。这段代码就是向量中断和非向量中断区别的根本所在,向量中断利用硬件计算,直接通过一级中断向量表中0X20-0XC0中断向量入口地址跳转到二级中断向量表中对应的HandleXXX地址,省掉了这段代码的操作。
文章版权归西部工控xbgk所有,未经许可不得转载。