2440启动代码和中断处理过程
汇编指令操作,这个应该是CODE输入段,而SMRDATA DATA引领DCD用于开辟一片数据存储空间,这部分应该是DATA输入段,它与RW里的数据不同之处在于这部分数据不能被修改。
在ADS编译前在ARM-Linker里的Ro_Base和Rw_Base两个地址值,就是指两个输出段的起始地址,即程序是按照你设置的这种方式排布在内存中的,各个地址标号根据这两个值确定。然而,用Ultraedit打开bin文件却发现其实Rw是跟在Ro后面的,也就是说,这两个段并没有按照你设置的地址起始,由此引出映像文件的加载域和运行时域两个概念。
加载域就是用Ultraedit打开看到的程序最原始的状态,而运行时域则是程序在执行时按照你设定的方式排布的状态,显然,上面设置的两个地址是针对运行时域来设置的,程序要满足上面的设置才能正确连接。也就是程序开始阶段(加载域状态)是不能正确连接的,不过开始时不需要用到Rw里的数据,程序是可以运行的,因此必须在需要用到Rw数据之前把它拷贝到上面设置的位置上,这就是bootloader里初始化用户程序的执行环境部分的作用,把数据移动到正确的位置!
拷贝完Rw里的数据之后,所有的符号都可以正确连接,这时跳转到main函数里去执行程序就可以了。2410的这段启动代码没有进行Ro的拷贝,所以如果你把程序烧在0x0地址,那么Ro就必须设置成0x0,如果你设置成0x30000000,那么Ro就必须设置成0x30000000,如果Rw不设置,它将默认跟在Ro后面,否则就执行上面的搬迁代码,挪到正确的位置上。由于本系统是采用NandFlash启动的,最初的启动代码必须要在0x0处的SRAM里执行,所以,如果要把这段启动代码当作NandFlash的启动代码的话,Ro就必须设成0x0。
6、中断处理过程
要使用中断,首先需要清掉程序状态寄存器CPSR里的IRQ位,这个很容易被忽略了。再之后才是考虑与中断有关的相应寄存器.
这个几个寄存器比较容易弄混了:
SRCPND/SUBSRCPND:只要中断产生的条件满足,例如外部电平,定时溢出等等,SRCPND/SUBSRCPND的相应位就会被置位,它不管其他地方的设置如何,所以某一时刻可能有几个位同时被置位了(几个中断同时产生)。
INTMSK/INTSUBMSK:这个是中断屏蔽位,清零表示允许中断请求,默认是禁止了所有的中断请求。
INTPND:它表示处理器接下来就要去处理的那个中断,某一时刻只可能有一个位被置位。这个寄存器置位的必要条件是SRCPND/SUBSRCPND已经是1,而且INTMSK/INTSUBMSK相应位已经清零。
SRCPND/SUBSRCPND和INTPND都不会自动清零,要程序向相应的位写1才能清零。
2410不支持中断嵌套,中断产生后处理器进入到IRQ模式,只有在等到这个中断处理完之后才能响应下一次中断。
如果同时产生多个中断,就涉及到了中断优先级的问题。SRCPND寄存器对应的32个中断源总共被分为6个组,每个组由一个ARBITER(0~5)对其进行管理。中断必须先由所属组的ARBITER(0~5)进行第一次优先级判断然后再到ARBITER6进行第二次判断。可以更改的只是组里的优先级顺序。
PRIORITY的各个位被分为两种类型,一种是ARB_MODE,另一种为ARB_SEL,拿ARBITER0来说,这个组一共包含了四种中断源:EINT0~EINT3,分别对应Req0~Req3,很明显ARB_SEL0就是决定了这四种中断的优先顺序,如果这个组里的两个中断同时产生,将会把排在前面的先传递给ARBITER06进行第二次判断。ARB_MODE0置1代表开启优先级次序旋转,当该位置为1之后,ARB_SEL0的值会在每处理完一次中断后顺次改变。
中断处理流程
启动代码开始是一个异常向量表,这个向量表是固定的,由处理器决定,必须要放在0x0地址那个地方,这个跟51单片机的中断向量表相类似。
b ResetHandler
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
当产生IRQ中断时,PC首先无条件地来到0x18这个地址处,这个0x18就是处理器决定的IRQ中断的入口地址,所以要在这个地址处放一条跳转指令b HandlerIRQ,PC接着跳转到HandlerIRQ地址标号处,这里存放着一个宏语句:
HandlerIRQ HANDLER HandleIRQ
按照上面说的宏展开,其实是执行这么一段语句:
sub sp,sp,#4 ;留下堆栈的第一个位置
stmfd sp!,{r0} ;保护R0因为后面要用R0传递值
ldrr0,=HandleIRQ ;将HandleIRQ这个地址标号的值传如R0
ldrr0,[r0];取存放在HandleIRQ里的那个值
strr0,[sp,#4] ;把取到的值压入栈
ldmfdsp!,{r0,pc};恢复R0并把PC指向HandleIRQ里存放的地址值
HandleIRQ里存放是什么值呢?代码最后有个这样的表,这个表就是在SDRAM里的另外一张异常向量表,这张表可以根据需要修改_ISR_STARTADDRESS的值来随意更改它的位置。
^_ISR_STARTADDRESS
HandleReset#4
HandleUndef#4
HandleSWI#4
HandlePabort #4
HandleDabort #4
HandleReserved #4
HandleIRQ#4
HandleFIQ#4
这里实现结构化一片地址空间的目的,可见在HandleIRQ这里预留了4个字节的空间,但是这个空间里现在放的是什么东西呢?
在代码的初始化过程中有这么一段代码:
ldr r0,=HandleIRQ ; Setup IRQ handler
ldr r1,=IsrIRQ
str r1,[r0]
原来是把IsrIRQ所在的地址值放到这个地方,那就是宏实现了把PC指向IsrIRQ的目的。程序来到IsrIRQ:
IsrIRQ
sub sp,sp,#4;预留堆栈
stmfd sp!,{r8-r9} ;保护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} ;将PC指向相应的中断处理地址
假如产生了EINT0中断来到了这里,那么PC将会跳转到HandleEINT0里存放的地址值,与上面的相同,程序里有这个表:
HandleEINT0#4
HandleEINT1#4
HandleEINT2#4
HandleEINT3#4
HandleEINT4_7 #4
.
.
.
这个表在2410addr.h头文件里也有对应的定义,指向的是同样的一块地方:
.
.
.
#define pISR_EINT0(*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_EINT1(*(unsigned *)(_ISR_STARTADDRESS+0x24))
#define pISR_EINT2(*(unsigned *)(_ISR_STARTADDRESS+0x28))
.
.
.
问题是HandleEINT0存放的又是什么值呢?这就需要在初始化EINT0的时候写上这么一句:
pISR_EINT0 = (unsigned )_IsrEINT0Service;
也就是把EINT0的中断处理函数的地址写到HandleEINT0地址处存放,那么到此PC就可以跳转到_IsrEINT0Service里去了,这里完成你所需要的中断处理过程。
2440启动代码中断处 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)