Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1034790
  • 博文数量: 178
  • 博客积分: 10222
  • 博客等级: 上将
  • 技术积分: 2215
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-03 11:27
个人简介

有所追求

文章分类

全部博文(178)

文章存档

2012年(1)

2011年(5)

2010年(3)

2009年(78)

2008年(91)

我的朋友

分类: BSD

2008-03-12 12:56:34

摘要:Bootloader是系统上电或复位后首先运行的一段代码,Bootloader代码(即启动代码)的好坏对整个系统的运行效率有着重要的影响, 而Bootloader代码与芯片的特性有着紧密的联系。本文根据用LPC2210在ADS(ARM Developer Suite)开发环境下进行嵌入式系统设计的实际经验,总结出基于ARM7TDMI内核的LPC2210处理器的Bootloader代码设计的详细流程及其中的一些关键技术和代码
关键词:ARM7TDMI核   ADS   Bootloader代码   LPC2210

前言:

基于ARM核的微处理器是目前嵌入式领域应用最广泛的RISC微处理器,以其低成本、低功耗、高性能等优点占据了嵌入式系统应用领域的领先地位。基于ARM核的芯片多数为复杂的片上系统集成, 在芯片上电或复位时,需要专门的一段代码来完成对系统的初始化。启动代码就是芯片上电或复位后进入应用程序main( )函数前执行的一段代码,它主要为运行C程序提供基本的运行环境。

1 启动代码的流程

Bootloader(启动)代码类似于PC机的BIOS(基本输入输出系统),它从系统上电或复位开始接管CPU,Bootloader根据实现的功能不同,其复杂程度也各不相同,一般随具体的目标系统和开发系统有所区别,使得每一种芯片的启动代码差别较大,不易编出统一的启动代码,但通常包括以下几个步骤:

·设置程序入口指针

·设置中断向量表

·初始化存储器系统

·初始化CPU各种模式的堆栈和寄存器

·初始化目标系统中要使用的各种片内外设

·初始化用户程序执行环境

·引导主应用程

2  基于LPC2210的Bootloader关键代码设计

2.1  LPC2210的Bootloader代码流程:

根据前面介绍的一般步骤,利用ARM公司推出的ADS开发工具,设计出LPC2210的Bootloader的具体流程(如图1)和关键代码。

Philips公司生产的LPC2210是基于ARM7TDMI核的RISC微处理器,特别适用于工业控制、医疗系统、访问控制。ADS是ARM公司推出的ARM核微处理器集成开发工具,成熟的版本是ADS1.2, ADS由命令行开发工具、ARM实时库、GUI开发环境、实用程序和支持软件组成,支持软件调试及JTAG硬件仿真调试,支持汇编、C和C++源程序。

 

2.2设置中断向量表

ARM芯片上电或复位后,系统进入管理模式、ARM状态、PC(R15)指向0x00000000地址处,所以必须保证向量表代码定位在0x00000000处,或者映射到0x00000000处,LPC2210系统上电或复位时,存储器映射控制寄存器(MEMMAP)等于0,Boot Block被重映射到地址0x00000000处,选择从Boot Block读取中断向量。中断向量表为每一个中断设置1个字(4个字节)的存储空间,存放一条跳转指令,通过这条指令使PC指针指向相应的中断服务程序入口,继而执行相应的中断处理程序。LPC2210的中断向量表和其它基于ARM核的芯片中断向量表较类似,只要注意LPC2210要使向量表所有数据32位累加和为零(0x00000000~0x0000001C的8个字的机器码累加), 才能使用户的程序脱机运行,限于篇幅,在此省略中断向量表的代码。

 

2.3初始化存储器系统

初始化存储器系统是初始化代码中的一个重要部分,因为许多操作系统在开始运行之前,希望了解存储器的组织情况。存储器系统初始化是通过软件设定FLASH、RAM存储器的地址范围,数据总线宽度。由于LPC2210是总线开放型芯片,具有4个Bank的存储器组,总线宽度可设置为8位、16位或32位。初始化存储器系统代码如下:

ResetInit

       LDR     R0, =PINSEL2

    IF :DEF: EN_CRP

        LDR     R1, =0x0f814910           ;芯片加密,禁止JTAG调试

    ELSE

        LDR     R1, =0x0f814914           ;设置总线的I/O引脚

    ENDIF

        STR     R1, [R0]

        LDR     R0, =BCFG0

        LDR     R1, =0x1000ffef

        STR     R1, [R0]                   ;设置外部第0个存储区

        LDR     R0, =BCFG1              

        LDR     R1, =0x1000ffef

        STR     R1, [R0]                   ;设置外部第1个存储区

        LDR     R0, =BCFG2

        LDR     R1, =0x2000ffef

        STR     R1, [R0]                   ;设置外部第2个存储区

        LDR     R0, =BCFG3

        LDR     R1, =0x2000ffef

        STR     R1, [R0]                   ;设置外部第3个存储区

 

2.4 初始化堆栈、改变处理器模式

ARM处理器有7种处理器模式,分别为:用户模式(usr)、快速中断模式(fiq)、普通中断模式(irq)、管理模式(svc)、数据访问中止模式(abt)、未定义指令中止模式(und)、系统模式(sys)。除用户模式之外的其他6种模式称为特权模式,在这些模式下,程序可以访问所有的系统资源,也可以任意进行处理器模式的切换。

每一种模式的堆栈指针寄存器(SP)都是独立的,因此对程序中需要用到的每一种模式都要给SP寄存器定义一个堆栈地址。方法是改变状态寄存器CPSR中的状态位,使处理器切换到相应的模式,然后再给SP赋值。这里有两点要注意:一、要尽量给堆栈分配快速和高带宽的存储器,因为堆栈性能的好坏对系统整体性能有非常重要的影响。二、在处理器模式切换时,不要过早地进入用户模式,因为进入用户模式就不能对CPSR进行修改切换到别的模式,这样会对接下去地程序造成影响,一般在系统初始化的最后阶段才将运行模式切换到用户模式。初始化堆栈代码如下:

InitStack   

        MOV     R0, LR            ;因芯片模式切换,故将程序返回地址保存到R0,同时在初始化堆栈完成后使用R0返回

MSR     CPSR_c, #0xd3

LDR     SP, StackSvc              ;设置管理模式堆栈

        MSR     CPSR_c, #0xd2

        LDR     SP, StackIrq      ;设置中断模式堆栈

        MSR     CPSR_c, #0xd1

        LDR     SP, StackFiq      ;设置快速中断模式堆栈

        MSR     CPSR_c, #0xd7

        LDR     SP, StackAbt      ;设置中止模式堆栈

MSR     CPSR_c, #0xdb           

        LDR     SP, StackUnd      ;设置未定义模式堆栈

        MSR     CPSR_c, #0xdf

        LDR     SP, =StackUsr     ;设置用户模式堆栈

       MOV     PC, R0

 

2.  5初始化目标系统

为了LPC2210基本能够工作,在进入main( )函数前,须对目标系统进行一些基本的初始化工作。如:LPC2210有不同的存储器映射方式,须根据硬件来设置存储器映射方式;为了避免混乱,最好在进入main( )函数前设置系统各部分时钟等,关键代码如下:

void TargetResetInit(void)

{

#ifdef __DEBUG   

    MEMMAP = 0x3;                  

#endif

#ifdef __OUT_CHIP   

    MEMMAP = 0x3;                  

#endif

#ifdef __IN_CHIP   

    MEMMAP = 0x1;                       /*根据预定义的宏,设置存储器映射方式*/                  

#endif

/* 设置系统各部分时钟 */

    PLLCON = 1;                        /*使能PLL(锁相环),但不连接PLL*/

#if (Fpclk / (Fcclk / 4)) == 1

    VPBDIV = 0;

#endif

#if (Fpclk / (Fcclk / 4)) == 2

    VPBDIV = 2;

#endif

#if (Fpclk / (Fcclk / 4)) == 4

    VPBDIV = 1;                           

#endif                                   /*设置外设时钟(VPB时钟pclk)与系统时钟(cclk)的分频比*/

#if (Fcco / Fcclk) == 2

    PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);

#endif

#if (Fcco / Fcclk) == 4

    PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);

#endif

#if (Fcco / Fcclk) == 8

    PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);

#endif

#if (Fcco / Fcclk) == 16

    PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);    /*根据PLL的电流控制振荡器和系统时钟的频率比,设置PLL的乘因子和除因子*/

#endif

    PLLFEED = 0xaa;

    PLLFEED = 0x55;                      /*使用芯片要求的访问序列把数据写入PLL相关寄存器*/

    while((PLLSTAT & (1 << 10)) == 0);       /*等待PLL跟踪完成*/

    PLLCON = 3;

    PLLFEED = 0xaa;

    PLLFEED = 0x55;                      /*使PLL连上系统*/

/* 设置存储器加速模块(MAM) */

    MAMCR = 0;                          /*关闭MAM*/

#if Fcclk < 20000000

    MAMTIM = 1;

#else

#if Fcclk < 40000000

    MAMTIM = 2;

#else

    MAMTIM = 3;                        /*根据Fcclk的大小来设置MAM定时寄存器*/

#endif

#endif

    MAMCR = 2;                        /*使能MAM*/

/* 初始化VIC */

VICIntEnClr = 0xffffffff                   ;禁止所有中断

VICVectAddr = 0                        ;设置向量地址寄存器(VICVectAddr)的值为0

VICIntSelect = 0                         ;将所有中断设置为IRQ中断

程序中的Fcclk、Fcco、Fpclk须在头文件中定义。

 

2.6 初始化应用程序

当所有的初始化工作完成后,调用ADS提供的__main, __main是ADS的编译系统提供的一个函数,负责完成C运行时库的初始化和应用程序执行环境的初始化,最后自动跳转到用户的main( )函数。代码为:

B    __main

3   结论

启动代码是嵌入式程序的开头部分,它直接面对处理器内核和硬件控制器进行编程,并首先在系统上运行,因此写好启动代码是设计好嵌入式系统的关键。本文介绍的Bootloader代码已经在Philips公司的LPC2210处理器上测试通过并运行稳定。对不同的ARM核芯片编写Bootloader代码,不但要了解ARM内核结构、指令系统,还要了解具体芯片的结构和各种片上资源。

参考文献

1           杜春雷.ARM体系结构与编程[M].北京:清华大学出版社.2004

2           马忠梅等.ARM嵌入式处理器结构与应用基础[M].北京:北京航空航天大学出版社.2003

3           周立功等.ARM微控制器基础与实践[M].北京:北京航空航天大学出版社.2003

4           Andrew N.Sloss、Dominic Symes、Chris Wright著.ARM嵌入式系统开发―软件设计与优化[M].沈建华译.北京:北京航空航天大学出版社.2005

阅读(2337) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-08-06 19:09:17

写的挺好。。。。。