《基于ARM7TDMI内核的芯片里多数硬件模块都是可配置的.pdf》由会员分享,可在线阅读,更多相关《基于ARM7TDMI内核的芯片里多数硬件模块都是可配置的.pdf(16页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、1/16 基于 ARM7TDMI 内核的芯片里多数硬件模块都是可配置的,需要由软件来设置其需要的工作状态。因此在用户的应用程序之前,需要由专门的一段代码来完成对系统的初始化。由于这类代码直接面对处理器内核和硬件控制器进行编程,一般都是用汇编语言。一般通用的内容包括:中断向量表初始化存储器系统初始化堆栈初始化有特殊要求的断口,设备初始化用户程序执行环境改变处理器模式呼叫主应用程 一.中断向量表 ARM 要求中断向量表必须放置在从 0 地址开始,连续 8X4 字节的空间内。每当一个中断发生以后,ARM 处理器便强制把 PC 指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1 个字的
2、存储空间,只能放置一条 ARM 指令,使程序跳转到存储器的其他地方,再执行中断处理。中断向量表的程序实现通常如下表示:AREA Boot,CODE,READONLY ENTRY B ResetHandler B UndefHandler B SWIHandler B PreAbortHandler B DataAbortHandler B B IRQHandler B FIQHandler 其中关键字 ENTRY 是指定编译器保留这段代码,因为编译器可能会认为这是一段亢余代码而加以优化。的时候要确保这段代码被在 0 地址处,并且作为整个程序的入口。放在 0 地址处的中断向量表的 ResetHa
3、ndler 一般放在 FLASH 内,其他中断向量的入口地址可以是 FLASH 内的,也可以是 SDRAM 内的,但是在为操作系统初始化时应该为 SDRAM 的地址。例如作为 uClinux 的启动代码,此处应该为内存地址。一般有两种实现方式:2/16 1.1 1第一种实现方式 b reset add pc,pc,#0 x0c000000 add pc,pc,#0 x0c000000 add pc,pc,#0 x0c000000 add pc,pc,#0 x0c000000 add pc,pc,#0 x0c000000 add pc,pc,#0 x0c000000 add pc,pc,#0 x
4、0c000000 0 x0c000000 为内存起始地址,uClinux 将中断向量放入地址 0 x0c000008,因为cpu 发生中断时仍然会跳转到 0 地址处的中断向量表中去,所以此处要修改中断向量表的地址,使程序能正确跳转到 uClinux 实现的中断向量处。此处需要注意,由于 ARM 系统的三级流水线技术,当程序执行到 x 地址处,pc 指针的值其实等于 x+8.在 uClinux 中实现如下:#ifdef CONFIG_ARCH_S3C44B0#undef vectors_base()#define vectors_base()(0 x0c000008)3/16#endif 所以
5、add pc,pc,#0 x0c000000 这条语句将会有 8 的偏移量。1.2 2第二种实现方式:b rest ldr pc,=0 x0c000004 ldr pc,=0 x0c000008 ldr pc,=0 x0c00000c ldr pc,=0 x0c000010 b.ldr pc,=0 x0c000018 /irq 中断 ldr pc,=0 x0c00001c 但相应的 linux 源代码应作修改,这时 uClinux 中实现如下:#ifdef CONFIG_ARCH_S3C44B0#undef vectors_base()#define vectors_base()(0 x0c0
6、00000)#endif 4/16 即只要当发生中断时,cpu 发生中断时跳转到 0 地址处的中断向量表中去,再这里能跳转到 uClinux 的 vectors_base()地址处。ARM7 有两种 IRQ 中断模式(1).向量中断时 0 地址代码如下:其中从 0 x20 开始处一定要按顺序放入 ENTRY b ResetHandler;0 x00 b HandlerUndef;0 x04 b HandlerSWI;0 x08 b HandlerPabort;0 x0c b HandlerDabort;0 x10 b.;0 x14 b HandlerIRQ;0 x18 b HandlerFIQ
7、;0 x1c ldr pc,=HandlerEINT0;0 x20 ldr pc,=HandlerEINT1 ldr pc,=HandlerEINT2 ldr pc,=HandlerEINT3 5/16 ldr pc,=HandlerEINT4567 ldr pc,=HandlerTICK;0 x34 b.b.ldr pc,=HandlerZDMA0;0 x40 ldr pc,=HandlerZDMA1 ldr pc,=HandlerBDMA0 ldr pc,=HandlerBDMA1 ldr pc,=HandlerWDT ldr pc,=HandlerUERR01;0 x54 b.b.ldr
8、 pc,=HandlerTIMER0;0 x60 ldr pc,=HandlerTIMER1 ldr pc,=HandlerTIMER2 ldr pc,=HandlerTIMER3 ldr pc,=HandlerTIMER4 ldr pc,=HandlerTIMER5;0 x74 b.6/16 b.ldr pc,=HandlerURXD0;0 x80 ldr pc,=HandlerURXD1 ldr pc,=HandlerIIC ldr pc,=HandlerSIO ldr pc,=HandlerUTXD0 ldr pc,=HandlerUTXD1;0 x94 b.b.ldr pc,=Hand
9、lerRTC;0 xa0 b.b.b.b.b.b.ldr pc,=HandlerADC;0 xb4 7/16 2非向量 IRQ 中断模式 ENTRY b ResetHandler;for debug b HandlerUndef;handlerUndef b HandlerSWI;SWI interrupt handler b HandlerPabort;handlerPAbort b HandlerDabort;handlerDAbort b.;handlerReserved b IsrIRQ b HandlerFIQ.IsrIRQ sub sp,sp,#4;reserved for PC
10、stmfd sp!,r8-r9 ldr r9,=I_ISPR ldr r9,r9 mov r8,#0 x0 8/16 0 movs r9,r9,lsr#1 bcs%F1 add r8,r8,#4 b%B0 1 ldr r9,=HandleADC add r9,r9,r8 ldr r9,r9 str r9,sp,#8 ldmfd sp!,r8-r9,pc.HandleADC#4 HandleRTC#4 HandleUTXD1#4 HandleUTXD0#4.HandleEINT3#4 HandleEINT2#4 HandleEINT1#4 HandleEINT0#4;9/16 此处通过判断 I_
11、ISPR 的值可以跳到相应的中断处理函数处。此处的 Bootloader 采用非向量 IRQ 中断方式,通过以下头文件的定义可以方便的把中断向量处理函数的地址传入:/*ISR*/#define pISR_RESET (*(unsigned*)(_ISR_STARTADDRESS+0 x0)#define pISR_UNDEF (*(unsigned*)(_ISR_STARTADDRESS+0 x4)#define pISR_SWI (*(unsigned*)(_ISR_STARTADDRESS+0 x8)#define pISR_PABORT (*(unsigned*)(_ISR_STARTA
12、DDRESS+0 xc)#define pISR_DABORT (*(unsigned*)(_ISR_STARTADDRESS+0 x10)#define pISR_RESERVED (*(unsigned*)(_ISR_STARTADDRESS+0 x14)#define pISR_IRQ (*(unsigned*)(_ISR_STARTADDRESS+0 x18)#define pISR_FIQ (*(unsigned*)(_ISR_STARTADDRESS+0 x1c)#define pISR_ADC(*(unsigned*)(_ISR_STARTADDRESS+0 x20)#defin
13、e pISR_RTC (*(unsigned*)(_ISR_STARTADDRESS+0 x24)#define pISR_UTXD1 (*(unsigned*)(_ISR_STARTADDRESS+0 x28)#define pISR_UTXD0 (*(unsigned*)(_ISR_STARTADDRESS+0 x2c)#define pISR_SIO (*(unsigned*)(_ISR_STARTADDRESS+0 x30)10/16#define pISR_IIC (*(unsigned*)(_ISR_STARTADDRESS+0 x34)#define pISR_URXD1 (*(
14、unsigned*)(_ISR_STARTADDRESS+0 x38)#define pISR_URXD0 (*(unsigned*)(_ISR_STARTADDRESS+0 x3c)#define pISR_TIMER5 (*(unsigned*)(_ISR_STARTADDRESS+0 x40)#define pISR_TIMER4 (*(unsigned*)(_ISR_STARTADDRESS+0 x44)#define pISR_TIMER3 (*(unsigned*)(_ISR_STARTADDRESS+0 x48)#define pISR_TIMER2 (*(unsigned*)(
15、_ISR_STARTADDRESS+0 x4c)#define pISR_TIMER1 (*(unsigned*)(_ISR_STARTADDRESS+0 x50)#define pISR_TIMER0 (*(unsigned*)(_ISR_STARTADDRESS+0 x54)#define pISR_UERR01 (*(unsigned*)(_ISR_STARTADDRESS+0 x58)#define pISR_WDT(*(unsigned*)(_ISR_STARTADDRESS+0 x5c)#define pISR_BDMA1 (*(unsigned*)(_ISR_STARTADDRE
16、SS+0 x60)#define pISR_BDMA0 (*(unsigned*)(_ISR_STARTADDRESS+0 x64)#define pISR_ZDMA1 (*(unsigned*)(_ISR_STARTADDRESS+0 x68)#define pISR_ZDMA0 (*(unsigned*)(_ISR_STARTADDRESS+0 x6c)#define pISR_TICK(*(unsigned*)(_ISR_STARTADDRESS+0 x70)#define pISR_EINT4567(*(unsigned*)(_ISR_STARTADDRESS+0 x74)#defin
17、e pISR_EINT3 (*(unsigned*)(_ISR_STARTADDRESS+0 x78)#define pISR_EINT2 (*(unsigned*)(_ISR_STARTADDRESS+0 x7c)11/16#define pISR_EINT1 (*(unsigned*)(_ISR_STARTADDRESS+0 x80)#define pISR_EINT0 (*(unsigned*)(_ISR_STARTADDRESS+0 x84)通过如下代码就可以实现 UART0 口的中断处理了:rINTCON=0 x5;/Non-vectored,IRQ enable,FIQ disab
18、le rINTMOD=0 x0;/All=IRQ mode pISR_URXD0=(unsigned)Uart0_RxInt;当在地址 0 处将中断向量跳到内存后,相应的中断向量就应该拷贝到内存地址处。把以下代码从 FLASH 拷贝到 SDRAM 中:real_vectors:b reset b undefined_instruction b software_interrupt b prefetch_abort b data_abort b not_used b irq b fiq 12/16 undefined_instruction:mov r6,#3 b endless_blink s
19、oftware_interrupt:mov r6,#4 b endless_blink prefetch_abort:mov r6,#5 b endless_blink data_abort:mov r6,#6 b endless_blink not_used:/*we*should*never reach this*/mov r6,#7 b endless_blink irq:sub sp,sp,#4;reserved for PC stmfd sp!,r8-r9 13/16 ldr r9,=I_ISPR ldr r9,r9 mov r8,#0 x0 0 movs r9,r9,lsr#1 b
20、cs%F1 add r8,r8,#4 b%B0 1 ldr r9,=HandleADC add r9,r9,r8 ldr r9,r9 str r9,sp,#8 ldmfd sp!,r8-r9,pc.fiq:mov r6,#9 b endless_blink 有了如上步骤就可以在 bootloader 中实现中断处理了。14/16 1.3 二Reset 中断处理如下内容 1 初始化存储器系统存储器地址分布一种典型的情况是启动 ROM 的地址重映射。初始化堆栈因为 ARM 有 7 种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此,对程序中需要用到的每一种模式都要给 SP 定义一个堆栈
21、地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,让后给 SP 赋值。注意:不要切换到 User 模式进行User 模式的堆栈设置,因为进入 User 模式后就不能再操作 CPSR 回到别的模式了,可能会对接下去的程序执行造成影响。这是一段堆栈初始化的代码示例,其中只定义了三种模式的 SP 指针:MRS R0,CPSR BIC R0,R0,#MODEMASK;安全起见,屏蔽模式位以外的其他位 ORR R1,R0,#IRQMODE MSR CPSR_cxfs,R1 LDR SP,=UndefStack ORR R1,R0,#FIQMODE MSR CPSR_cxsf,R1 LDR
22、SP,=FIQStack ORR R1,R0,#SVCMODE MSR CPSR_cxsf,R1 LDR SP,=SVCStack 2 初始化有特殊要求的端口,设备 这里视不同的硬件设计而不同。15/16 3 初始化应用程序执行环境映像一开始总是存储在 ROMFlash 里面的,其 RO 部分即可以在 ROMFlash 里面执行,也可以转移到速度更快的 RAM 中执行;而 RW 和 ZI 这两部分是必须转移到可写的 RAM 里去。所谓应用程序执行环境的初始化,就是完成必要的从 ROM 到 RAM 的数据传输和内容清零。下面是在 ADS 下,一种常用存储器模型的直接实现:LDR r0,=|Ima
23、ge$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,#4 STRCC 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 个符号是由
24、器第一输出的。|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 区的16
25、/16 开始,接下去就对这片 ZI 区进行清零操作,直到遇到结束地址|Image$ZI$Limit|4 改变处理器模式因为在初始化过程中,许多操作需要在特权模式下才能进行(比如对 CPSR 的修改),所以要特别注意不能过早的进入用户模式。内核级的中断使能也可以考虑在这一步进行。如果系统中另外存在一个专门的中断控制器,这么做总是安全的。5 呼叫主应用程序当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是:IMPORT main B main 直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。在ARM ADS 环境中,还另外提供了一套系统级的呼叫机制。IMPORT _main B _main _main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到 main()函数。