……
;***数据段,为NewVectorTable分配数据空间***
NewVectorTable # 128;大小根据需要定义,每向量2个字(8字节);
程序运行时,中断服务的初始化 程序必须设置好新的中断向量表,即在NewVectorTable表中动态生成下
列指令:
NewVectorTable;表安排在RAM顶端0x0c1fff00处(由硬件设定)
LDR PC,[PC,#4];指令代码为0xe51ff004,功能为PC〈-[PC 4]
nVt00 DCD ISR_RESET_HANDLE
LDR PC,[PC,#4];与LDR PC,nVt01指令等效
nVt01 DCD ISR_UNDEF_HANDLE
LDR PC,[PC,#4]
nVt02 DCD ISR_SWI_HANDLE
LDR pC,[PC,#4]
nVt03 DCD ISR_UNDEF_HANDLE
LDR PC,[PC,#4]
nVt04 DCD ISR_UNDEF_HANDLE
LDR PC,[PC,#4]
nVt05 DCD ISR_UNDEF_HANDLE
LDR PC,[PC,#4]
NVt06 DCD ISR_IRQ_HANDLE
LDR PC,[PC,#4]
nVt07 DCD ISR_FIQ_HANDLE
……
可用C函数在NweVectorTable中生成含上述指令的向量表,具体实现如下:
#define VECTOR_TABLE 0x0c1fff00
//向量表首地址,根据实际硬件来配置
#define INSTRUCTION_LDR_PC 0xe51ff004
//加载PC寄存器的指令码
//设置向量C函数,ISR_Handle中断服务程序地址
void SetVector(unsigned char no,unsigned long int ISR_Handle){
unsigned long int * pVectorTable;
//定义32位无符号数指令,指向向量表
pVectorTable=((unsigned long int *)(VECTOR_TABLE (no < <3)));
*pVectorTable =INSTRUCTION_LDR_PC;
//在向量表中放置LDR PC,[PC,#4]指令
*pVectorTable=ISR_Handle;//设置中断服务例程入口地址}
//读取向量C函数,no代表中断号
unsigned long int GetVector(unsigned char no){
unsigned long int *pVectorTable;
pVectorTable=((unsigned long int *)(VECTOR_TABLE (no < <3)));
return *( pVectorTable);//返回中断处理程序入口地址
}
使用上述初始化代码和向量设置函数,除复位向量外,其它所有中断向量都可以指向了在RAM 数据区中
的新向量表,并给定一个统一的中断编号。中断服务程序可以放在任何模块文件中编译连接,不需要修
改原向量表代码,但在打开中断使用中断服务例程前必须使用C函数SetVector()设置中断向量。
4 结论
本文提出的中断向量表配置策略和实现方法,简便高效,仅比标准处理方法增加一条指令的执行时间。
当把ARM的C初始化汇编代码中所有中断源(包括扩展的内外部中断源)的向量都指向了新向量表,并统
一编号,此后编写任何中断服务程序几乎不需要修改汇编代码,C初始化代码完全可以对C程序员隐藏起
来,并可以像在X86体系下一样动态地设置和修改中断向量。初始化应用程序执行环境
映像一开始总是存储在ROM/Flash里面的,其RO部分即可以在ROM/Flash里面执行,也可以转移到
速度更快的RAM中执行;而RW和ZI这两部分是必须转移到可写的RAM里去。所谓应用程序执行环境的初始化
,就是完成必要的从ROM到RAM的数据传输和内容清零。
下面是在ADS下,一种常用存储器模型的直接实现:
LDR r0,=|Image$$RO$$Limit| 得到RW数据源的起始地址
LDR r1,=|Image$$RW$$Base| RW区在RAM里的执行区起始地址
LDR r2,=|Image$$ZI$$Base| ZI区在RAM里面的起始地址
CMP r0,r1 比较它们是否相等
BEQ %F1
0 CMP r1,r3
LDRCC r2,[r0],#4STRCC r2,[r1],#4
BCC %B0
1 LDR r1,=|Image$$ZI$$Limit|
MOV r2,#0
2 CMP r3,r1
STRCC r2,[r3],#4
BCC %B2
程序实现了RW数据的拷贝和ZI区域的清零功能。其中引用到的4个符号是由链接器第一输出的。
|Image$$RO$$Limit|:表示RO区末地址后面的地址,即RW数据源的起始地址
|Image$$RW$$Base|:RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址
|Image$$ZI$$Base|:ZI区在RAM里面的起始地址
|Image$$ZI$$Limit|:ZI区在RAM里面的结束地址后面的一个地址
程序先把ROM里|Image$$RO$$Limt|开始的RW初始数据拷贝到RAM里面|Image$$RW$$Base|开始的地
址,当RAM这边的目标地址到达|Image$$ZI$$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI
区进行清零操作,直到遇到结束地址|Image$$ZI$$Limit|
改变处理器模式
因为在初始化过程中,许多操作需要在特权模式下才能进行(比如对CPSR的修改),所以要特别注
意不能过早的进入用户模式。
内核级的中断使能也可以考虑在这一步进行。如果系统中另外存在一个专门的中断控制器,这么做
总是安全的。
呼叫主应用程序
当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是:
IMPORT main
B main
直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。
在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。
IMPORT __mainB __main
__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳
转到main()函数
本文章来自中国IT实验室