周立功lpc21xx/lpc22xx系列ARM7启动代码分析

发布者:静静思索最新更新时间:2016-07-20 来源: eefocus关键字:周立功  ARM7  启动代码 手机看文章 扫描二维码
随时随地手机看文章
网上已经有人做了一个周立功lpc2000(ARM7TDMI)启动代码分析的文章, 我本来想做一个s3c2410(ARM920T)的启动代码分析的, 但是看来了一下2410的启动代码,发现有些东西还不是理解的很清楚, 我ARM9的经验比较少.

所以还是做一个ARM7的启动代码分析吧, 网上那一份相比,我这个主要关注startup.s文件.网上那个startup.s几乎是一笔带过的.

红色标记的是源码.

 

SVC_STACK_LEGTH         EQU         0

FIQ_STACK_LEGTH         EQU         0

IRQ_STACK_LEGTH         EQU         256

ABT_STACK_LEGTH         EQU         0

UND_STACK_LEGTH         EQU         0

NoInt       EQU 0x80

USR32Mode   EQU 0x10

SVC32Mode   EQU 0x13

SYS32Mode   EQU 0x1f

IRQ32Mode   EQU 0x12

FIQ32Mode   EQU 0x11

 

上面几行代码,不用过多分析, 定义几个符号而已, 把EQU想像成C中的#define就可以了. 具体定义的数值,下面的代码用到我再解释.

IMPORT __use_no_semihosting_swi

上面这一句的作用是在代码中禁用 semihosting 机制. 到底什么是semihostiong这里不多说, 网上有很多. 这里只说明Semihosting主要用来调试, 在release版本的代码中一般是要禁用的.

IMPORT  FIQ_Exception                   

IMPORT  __main                             

IMPORT  TargetResetInit

上面三行是把要引入的外部标号声明一下,以便下面使用.

EXPORT  bottom_of_heap

EXPORT  StackUsr

EXPORT  Reset

EXPORT __user_initial_stackheap

上面四行是把要给其它文件使用的标号声明

 

AREA    vectors,CODE,READONLY

        ENTRY

上面这一行声明汇编文件的入口, 整个文件是从这里开始执行的.

Reset

        LDR     PC, ResetAddr

        LDR     PC, UndefinedAddr

        LDR     PC, SWI_Addr

        LDR     PC, PrefetchAddr

        LDR     PC, DataAbortAddr

        DCD     0xb9205f80

        LDR     PC, [PC, #-0xff0]

        LDR     PC, FIQ_Addr

上面几行是配置中断向量表. 中断向量表的顺序是不能变的,因为这是ARM7规定的,可以参考相关书籍. 这里有几个问题要说明一下.

第一, 关于DCD     0xb9205f80, 按照ARM7的中断向量表分布图, 这个位置是个保留位. 但是究竟为什么要用0xb9205f80这个数值呢.

根据周立功的说法, nxp系列的lpc21xx,lpc22xx片子要求"中断向量表中所有数据32位累加和为0,否则程序不能脱机运行", 我在AXD反汇编了一下(如下图),把中断向量表中的8个机器码累加了一下:0xe59ff018*6+0xe51ffff0+0xb9205f80,没错, 结果是零. 但是我遇到一个问题, 就是我在实验中,把0xb9205f80这个数值改成任何值,程序运行都没问题. 头大了, 这个问题待解决中……(希望高手看到了可以指点一二).

 

 

第二, 关于LDR     PC, [PC, #-0xff0]. 这里本应该放IRQ中断的, 为什么是这么一句话. 其实在我blog的其中一篇文章里有提到过这一点.

ARM7的三级流水线结构导致了PC指向的是当前指令的后8个字节. 本来IRQ是应该放在0x00000018处的. LDR     PC, [PC, #-0xff0]这条语句执行后, PC的当前值就是0x00000018+8-0xff0. 很容易计算出它的结果是0xfffff030. 看一下lpc22xx的手册就知道. 这个地址就是VICVectAddr. 也就是说本来这个地址是应该放IRQ服务程序的入口地址的,但是这个地址被放在了VICVectAddr 这个寄存器里. 英文手册里有一段对VICVectAddr 描述. 看了之后就容易明白是怎么回事了: Vector Address Register. When an IRQ interrupt occurs, the IRQ service routine can read this register and jump to the value read

 

ResetAddr           DCD     ResetInit

UndefinedAddr       DCD     Undefined

SWI_Addr            DCD     SoftwareInterrupt

PrefetchAddr        DCD     PrefetchAbort

DataAbortAddr       DCD     DataAbort

Nouse               DCD     0

IRQ_Addr            DCD     0

FIQ_Addr            DCD     FIQ_Handler

这几行是为上面中断向量表中的中断标号分配内存空间, 也就是它们的执行地址. 一开始我有个疑问, 为什么不直接用LDR     PC, ResetInit,还要用DCD中转一下, 后来上网查了一下,才恍然大悟, ldr指令中的地址必须为当前指令地址是4KB范围内, 用DCD中转一下就可以在整个程序空间寻址.

Undefined

        B       Undefined

SoftwareInterrupt                     

        B       SoftwareInterrupt  

PrefetchAbort

        B       PrefetchAbort

DataAbort

        B       DataAbort

FIQ_Handler

        STMFD   SP!, {R0-R3, LR}

        BL      FIQ_Exception

        LDMFD   SP!, {R0-R3, LR}

        SUBS    PC,  LR,  #4

这几行不用过多解释, 只是说明上面几个异常如何执行.

 

InitStack   

        MOV     R0, LR

;设置管理模式堆栈

        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

上面是一个子函数, 函数名为InitStack. 顾名思意, 这个函数设置ARM七种工作模式下的堆栈. 关于这一段代码有三点要说.

第一, MSR     CPSR_c, #0xdf, 这一句把ARM的工作模式设置为系统模式,或者也可以说是用户模式, 因为系统模式与用户模式是共享相同的寄存器组. 用0xdf对CPSR寄存器赋值,就把IRQ中断关闭了(可以查一下CRSR的详细说明), 代码正常执行时处理器是处在用户模式的,所以IRQ中断是不会执行的. 所以,如果用周立功的这个启动代码,当你的程序中需要中断时,要把0xdf改成0x5f. 之前看到很多人在网上说用周立功的ADS工程模板,进不了中断,很多情况下是这个原因.

第二, 并不是每一种模式下的堆栈都用设置的, 比如说如果你的程序中不会用到FIQ,就可以不用设置快速中断下的堆栈.

第三, 注意LDR     SP, =StackUsr这个语句, 其它都是没有=号的, 为什么这个要用等号呢? 这就是LDR伪指令与LDR指令的区别了, LDR     SP, =StackUsr是把StackUsr表示的地址装载到sp, LDR     SP, StackUnd是把StackUnd表示地址的内容装载到sp,注意下面几句

StackSvc           DCD     SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4

StackIrq           DCD     IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4

StackFiq           DCD     FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4

StackAbt           DCD     AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4

StackUnd           DCD     UndtStackSpace + (UND_STACK_LEGTH - 1)* 4

 

可以看到,没有”=”的标号都已经用DCD初始化了, 而StackUsr到底是什么呢, 它是由下面的语句决定的

(startup.s文件)

AREA    Stacks, DATA, NOINIT

StackUsr

(分散加载文件)

STACKS 0x40002000 UNINIT

 {

        Startup.o (Stacks)

 }

这样就明白了, StackUsr肯定是0x40000000~0x400020000之间的某个数. 用户模式下的堆栈空间就是它了.

 

ResetInit

        BL      InitStack

        BL      TargetResetInit

        B       __main

 

处理器上电复位后通过中断向量表进入该函数,__main函数主要工作是初始化C的库函数, 并由它进入C的main函数.

__user_initial_stackheap   

    LDR   r0,=bottom_of_heap

;    LDR   r1,=StackUsr

MOV   pc,lr

__user_initial_stackheap函数是ADS的一个库函数, 如果程序中用到的分散加载文件, 这个函数必须要被实现. 应用程序的栈和heap是在C库函数初始化过程中建立起来的。可以通过重定向对应的子程序来改变堆栈和heap的位置. 堆栈的地址在分散加载文件里已经指定好,本函数不应该修改它们的值. 用r0,r1分别返回heap和stack的基址. 关于ADS的存储器机制大家可以去网上查更详细的资料.

StackSvc           DCD     SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4

StackIrq           DCD     IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4

StackFiq           DCD     FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4

StackAbt           DCD     AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4

StackUnd           DCD     UndtStackSpace + (UND_STACK_LEGTH - 1)* 4

 

 AREA    MyStacks, DATA, NOINIT, ALIGN=2

SvcStackSpace      SPACE   SVC_STACK_LEGTH * 4  ;Stack spaces for Administration Mode

IrqStackSpace      SPACE   IRQ_STACK_LEGTH * 4  ;Stack spaces for Interrupt ReQuest Mode

FiqStackSpace      SPACE   FIQ_STACK_LEGTH * 4  ;Stack spaces for Fast Interrupt reQuest Mode

AbtStackSpace      SPACE   ABT_STACK_LEGTH * 4  ;Stack spaces for Suspend Mode

UndtStackSpace     SPACE   UND_STACK_LEGTH * 4  ;Stack spaces for Undefined Mode

上面几行代码是为各个模式下的堆栈分配空间. 其中MyStacksA的位置会在分散加载文件中指定.

IF :DEF: EN_CRP

        IF  . >= 0x1fc

        INFO    1,"\nThe data at 0x000001fc must be 0x87654321.\nPlease delete some source before this line."

        ENDIF

CrpData

    WHILE . < 0x1fc

    NOP

    WEND

CrpData1

    DCD     0x87654321          ;/*When the Data is 为0x87654321,user code be protected. 当此数为0x87654321时,用户程序被保护 */

    ENDIF

上面这几行其实是加密芯片用的, lpc21xx和lpc22xx系列的ARM7,当你的工程选择RelInFlash时, 代码写进flash,芯片也同时被加密, 加密状态下JTAG也读不到芯片, 也不能单步调试, 要解密的话必须要用ISP完全擦除一下. 上面的代码的意思就是在地址0x1fc处放数据0x87654321, 从而实现加密的功能, 但前提是IF :DEF: EN_CRP, 也就是定义了EN_CPP这个宏. 而这个宏是在当选择了RelInFlash时ADS自动定义的. 然后,再说一下0x87654321的问题. LPC2100 系列ARM7微控制器是世界首款可加密的ARM芯片,对其加密的方法是通过用户程序在指定地址上设置规定的数据。PHILIPS公司规定,对于 LPC2100芯片(除LPC2106/2105/2104外),当片内FLASH地址0x000001FC处的数据为0x87654321时,芯片即被加密. 所以问题搞定.

关键字:周立功  ARM7  启动代码 引用地址:周立功lpc21xx/lpc22xx系列ARM7启动代码分析

上一篇:引用 ADS1.2下ARM映像文件
下一篇:Realview MDK中编译器对中断处理的过程详解

推荐阅读最新更新时间:2024-03-16 15:01

基于ARM7与虚拟仪器的串口通信方案
引言 LPC213X系列是NXP公司开发的基于ARM7TDMI-S核 ,拥有ARM体系结构v4版本的嵌入式单片机,因其优异的性能而广泛应用于自动控制、通信等领域,并逐步成为各种仪器仪表的首选控制芯片之一。虚拟仪器(labview)是美国NI公司推出的图形化编程软件,包含了丰富的处理函数和各种算法。目前大部分虚拟仪器要求配以不同总线标准的NI数据采集卡与之配套使用;而由单片机控制的仪表仪器大多自成系统,不能和虚拟仪器配接,这样的系统,其数据分析功能和图形化处理能力有限。如果能将性能优越的LPC213X系列与LabView图形化软件技术结合应用在以嵌入式单片机为控制核心的虚拟仪器仪表系统中,则不仅能发挥嵌入式单片机实时性强等优
[单片机]
基于<font color='red'>ARM7</font>与虚拟仪器的串口通信方案
arm:启动代码判断是从nand启动还是从norflash启动,拷贝程序到内存的过程
一、nand启动和nor启动:   CPU从0x00000000位置开始运行程序。   1、nand启动:   如果将S3C2440配置成从NANDFLASH启动(将开发板的启动开关拔到nand端,此时OM0管脚拉低)S3C2440的Nand控制器会自动把Nandflash中的前4K代码数据搬到内部SRAM中(地址为0x40000000),同时还把这块SRAM地址映射到了0x00000000地址。CPU从0x00000000位置开始运行程序。   2、如果将S3C2440配置成从Norflash启动(将开发的启动开关拔到nor端,此时OM0管脚拉高),0x00000000就是norflash实际的起始地址,norf
[单片机]
arm:<font color='red'>启动</font><font color='red'>代码</font>判断是从nand<font color='red'>启动</font>还是从norflash<font color='red'>启动</font>,拷贝程序到内存的过程
基于arm7芯片lpc2138的十六进制转换10进制显示程序
#include LPC213X.H void delay(int x) { while(--x); } void DsipInit() { PINSEL0= 0; PINSEL1|= 0x0 6; IO0DIR |= 0xffff 7; IO0CLR = 0xffff 7; } void Display(unsigned int val) { int k, m; IO0CLR = 0xffff 7; k=((val&(0x0f0)) 4)*16+((val&(0xf00)) 8)*16*16+(val&(0x00f));//16进制转换为10进制数,注意往右移位,
[单片机]
基于<font color='red'>arm7</font>芯片lpc2138的十六进制转换10进制显示程序
ARM7系统中实现CF卡存储的文件系统设计
摘要:介绍针对ARM7架构的嵌入式系统中,以CF卡作为存储介抽的文件系统的设计,并通过GPS车辆导航系统中地图信息读写的具体应用介绍了其软硬件电路的实现方法。 关键词:嵌入式文件系统 CF卡 ARM7 随着微控制器性能的不断提高,嵌入式应用越来越广泛。但是目前市场上的大型商用嵌入式实时系统,价格昂贵,而且都针对特定的硬件平台。对于中小型系统开发,购买商用实时系统并不划算。 目前我们正着手将嵌入式系统软件应用于汽车卫星导航仪系统的一步开发。传统的嵌入式应用并不包括文件系统,而我们要实现的文件系统需要在车辆导航系统中实现地图数据文件的读写。因此它既要支持与MS-DOS兼容的文件系统也要支持其它类型的文件系统。 另一方面,从数码相机到M
[嵌入式]
基于ARM7和LM35的温度采集系统设计
0 引言 目前广泛应用的温度采集设备,其温控系统的内部芯片普遍采用单片机,其缺点是采集终端硬件功能简单、芯片性能低、软件设计复杂、任务调度麻烦、系统升级困难等。随着当今社会科技的发展,人们对温度采集系统也有了越来越高的要求,具体体现在系统的实时性、精度、软件设计、升级等方面。由于嵌入式操作系统的发展,本文设计了一种基于ARM7的温度采系统,其具有采集精度高,软件设计简单,软硬件功能修改方便、升级便利等优点,有效地解决了过去采用单片机作为内部芯片中的问题。该系统可用于温室、仓库等需要实时监控温度的场所,为人们的生活生产提供了便利的可靠的解决方案。 1 系统硬件结构 1.1 系统总体设计 该设计采用了Samsung公司所生产的S
[单片机]
ARM9 (2440A) 从启动代码到应用程序(Main) 1
ARM9(2440A) 从启动代码到应用程序 说一下从启动代码到Main函数的过程,以及到了Main还需要设置些什么,才算是一个完整的应用程序。 启动代码 我们知道,uboot的第一阶段的功能是:(1)定义入口;(2)设置异常向量(exception vector);(3)设置CPU的速度、时钟频率及中断控制寄存器;(4)初始化内存控制器 ;(5)将rom中的程序复制到ram中;(6)初始化堆栈;(7)转到ram中执行; 其实我们要实现的启动代码功能也就是实现这些功能,最后跳转到我们自己的应用程序入口。只是这里的启动代码,我们不用uboot来实现,可以说是根据自己的需求来实现,毕竟uboot代码量不小。 keil下创建
[单片机]
ARM9 (2440A) 从<font color='red'>启动</font><font color='red'>代码</font>到应用程序(Main) 1
STM32F10x的启动代码分析
;/*****************************************************************************/ ;/* STM32F10x.s: Startup file for ST STM32F10x device series */ ;/*****************************************************************************/ ;/* Use Configuration Wizard in Context Menu */ ;/*******************
[单片机]
基于ARM7的蓝牙接入点的硬件系统结构和软件流程
  本文着重介绍了蓝牙接入点的硬件系统结构和软件流程,并针对在工业现场上使用蓝牙接入点将阀门、流量计、温度变送器等几个蓝牙设备连接到工业以太网中,实现了带蓝牙通信模块的工业设备和现有的有线网络的通信。   1引言   在工业现场中,由于有些环境比较恶劣,布线不方便等因素可以采用蓝牙无线通信技术来实现数据的通信。同时,工业现场中有很多以不同方式互连的设备,其中包括非智能化简单数据连接单元 (I/O)、智能化设备 (比如智能传感器、单回路控制器和 PLC)和监控系统 (作为 HMI使用,用于数据记录和监控 )等。这些设备大都是以各种不同的通信协议和媒介来互连的,其中有些就可以用蓝牙无线技术代替。本文以 AT91R40008为例,结
[网络通信]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved