arm开发经验.docx
- 文档编号:30336829
- 上传时间:2023-08-13
- 格式:DOCX
- 页数:46
- 大小:252.27KB
arm开发经验.docx
《arm开发经验.docx》由会员分享,可在线阅读,更多相关《arm开发经验.docx(46页珍藏版)》请在冰豆网上搜索。
arm开发经验
arm开发经验(连载)
发表于2007-8-99:
59:
22
前一段时间做了arm的一些开发,主要是编写了arm的启动软件和移植了uCOS-II到arm7。
我做事情喜欢深入简出,及从最简单,最原理的方面先做一个框架,然后在这个框架里面进行补充。
我还是一个很喜欢和别人讨论的人,希望有人可以给我提出意见和建议。
我的这个心得很初级,都是一些基本的东西。
现在拿出来和大家分享,希望在我毕业之前能给大家留一些纪念。
^_^
由于这些东西发paper实在是没有价值,但是我感觉可以作为arm开发的入门。
由于我的水平和经验有限,错误也是难免的。
但是如果不拿出来和大家分享,就算有错误我也发现不了,是么?
呵呵。
我现试试发连载的第一篇,看看有多少价值,如果大家觉得有价值,我会继续连载的。
连载一:
前言
这个文档是我学习ARM编程的总结和心得。
阅读这个文档的人应当首先阅读ADS1.2的帮助文档及相关内容。
这个文档不会对编译器及连接器做出详细的说明,在需要的时候会指出具体内容在相关资料的章节。
同时阅读这个文档的人需要了解ARM指令集和一些ARM汇编的基本内容以及C和C++的相关编程内容。
同时还需要了解ARM的流水线结构及一些基本的编程知识。
同时为了方便查阅英文文档,所有的相关术语都使用英文原文
第一章STARTUP
1ARM的启动
一般的嵌入式系统在主程序执行之前都需要执行一些初始化的过程以创造嵌入式程序运行的环境,尤其是一些高级的嵌入式系统,由于核心芯片使用内存映射、内存保护等机制以及编程使用高级语言C,C++甚至JAVA语言,都需要先创建一个适合程序运行的硬件环境,然后初始化或者配置或者剪裁run-timelibrary,这些工作都必须在主程序运行前完成,所以一个startup程序或者程序组对于一个嵌入式系统来说是非常重要的。
要编写startup程序,需要对编译器、链接器和汇编器的细节有一定的了解,同时对ARM芯片硬件本身的地址分配以及memorymapping机制也需要有一些了解。
2ARM程序的工作过程
首先由各种sourcefile经过编译产生object文件,然后object文件经过链接生成Image文件,然后通过ICE的方法,根据描述文件的指定下载到目标板上的固态存储器指定地址当中,比如flash,EEPROM,ROM等等。
在程序执行之前,根据某些描述文件,将需要读写数据的部分读出放入动态存储器比如RAM当中,然后程序从ROM开始执行。
或者有时为了提高程序的运行速度,也可以将所有的程序(有一些root的部分除外,以后会提及)通过一个描述文件放入指定的RAM当中,然后程序从RAM开始执行,但是这样会耗费大量的动态存储器,所以大部分程序会取折中的方法,将需要快速运行的部分和要读写的部分放入RAM中(一般读固态存储器的过程和动态存储器的过程是一样的,但是写就不同了,所以读写的部分一定要放到RAM中),而只读的部分和对速度要求不是那么高的部分放入固态存储器。
同时ARM结构的异常向量表规定放在地址为0x00000000开始的地址空间上,而一般的CPU为了提高异常相应速度,会将这个向量段remap到其他的RAM当中,所以在描述文件当中必须精确指定异常向量跳转程序的地址到remap的地方。
在application程序执行前,还需要由一些文件描述application程序执行的环境。
比如系统工作时钟,总线频率。
现在一般嵌入式编程语言为C,C++等。
如果在使用它们的时候使用的runtime-library,那么在程序执行前还需要为这些库函数初始化heap。
然后ARM可能工作在不同的模式,还需要为不同的工作模式设置stack。
这样,描述链接地址的文件,以及在application运行前所有的初始化程序就是startup程序组
3STARTUP分类
这样,将startup程序所完成的功能分类。
一类是链接地址描述,一类是各种初始化的程序。
根据不同的应用,描述文件和初始化程序的内容以及结构和复杂程度都会不同。
但是基本上,它们都必须实现以下功能。
3.1描述文件实现功能
描述文件可以是链接命令行上简单的几个字符,也可以是一个非常复杂的文件,但是它必须完成如下功能:
;指定程序下载的地址
;指定程序执行的地址
3.2初始化程序实现的功能
初始化程序根据不同的应用,其结构和复杂度也不同,但是它必须完成如下基本功能:
;异常向量初始化
;内存环境初始化
;其他硬件环境初始化
ARM开发经验
(二)
发表于2005-11-2721:
53:
44
注:
这个连载的版权属于自控所158所有。
转载的时候请注明。
转载需要通过作者本人同意。
/*
*********************************************************************************************************
* ProgrammingArm
*
*
* (c)Copyright1992-2008,西安交通大学
* AllRightsReserved
*
* 自控研究所158
*
*文件 :
连载二
*版本 :
V1.00
*作者 :
潘自强
*
*对象 :
ARM7
*模式 :
ARM
*工具 :
ADS1.20
*********************************************************************************************************
*/
4描述文件
要编写描述文件,必须知道ARMImage文件的组成及ARMImage文件执行的机理。
4.1ARMImage的结构
一个ARMImagestructure由linker在以下几个方面定义:
组成它的regions和outputsections
当Image下载的时候这些regions和sections在内存中的位置
当Image执行时这些regions和sections在内存中的位置
4.1.1ARMImage的组成
一个ARMImage被保存在可执行文件当中,它的层次结构可以包括Image,regions,outputsections和inputsections。
一个Image由一个或多个regions组成,每个region包括一个或多个outputsections
每个outputsection由一个或多个inputsections组成
Inputsections是一个objectfile中的code和data信息。
Image的结构如
下图:
1附图:
tu1.JPG(24684字节)
NOTEInputsection,outputsection和region的定义见ADS_LinkerGuide3-3页。
同时Inputsection有几种属性,分别为readonly,read-write,zero-initialized。
分别称为RO,RW和ZI。
属性来源于AREA后的attr属性。
比如CODE是RO,DATA是RW,NOINT默认为ZI,即用0值初始化,但是可以选择不进行0值初始化。
ZI属性仅仅来源于SPACE,DCB,DCD,DCDU,DCQ,DCQU,DCW,或者DCWU。
由以上定义,ZI属性的包含于RW属性,它是有初始值的RW数据。
又例如在C语言中,代码为RO,静态变量和全局变量是RW,ZI的。
ARM开发经验(三)
发表于2005-11-289:
14:
09
注:
这个连载的版权属于自控所158所有。
转载的时候请注明。
转载需要通过作者本人同意。
/*
*********************************************************************************************************
* ProgrammingArm
*
*
* (c)Copyright1992-2008,西安交通大学
* AllRightsReserved
*
* 自控研究所158
*
*文件 :
连载三
*版本 :
V1.00
*作者 :
潘自强
*
*对象 :
ARM7
*模式 :
ARM
*工具 :
ADS1.20
*********************************************************************************************************
*/
4.1.2Image的Loadview和executionview
在下载的时候Imageregions被放置在memorymap当中,而在执行Image前,或许你需要将一些regions放置在它们执行时的地址上,并建立起ZIregions。
例如,你初始化的RW数据需要从它在下载时的在ROM中的地址处移动到执行时RAM的地址处。
1附图:
tu2.jpg(640566字节)
NOTELoadview和executionview的详细定义见ADS_LinkerGuide3-4
以上的描述包括二个内容,一是要指定各个section在loadview和executionview时的地址即memorymap,二是要在执行前根据这些地址进行section的初始化。
4.1.3制定Memorymap
制定memory map的方法基本上有二种,一是在link时使用命令行选项,并在程序执行前利用linker pre-define symbol使用汇编语言制定section的段初始化,二是使用scatter file。
以上二种方法依应用程序的复杂度而定,一针对简单的情况,二针对复杂的情况。
ARM开发经验(四)
注:
这个连载的版权属于自控所158所有。
转载的时候请注明。
转载需要通过作者本人同
意。
/*
******************************************************************************
***************************
* ProgrammingArm
*
*
* (c)Copyright1992-2008,西安交通大学
* AllRightsReserved
*
* 自控研究所158
*
*文件 :
连载四
*版本 :
V1.00
*作者 :
潘自强
*
*对象 :
ARM7
*模式 :
ARM
*工具 :
ADS1.20
******************************************************************************
***************************
*/
4.1.1.1利用linkerpre-definesymbol使用汇编程序
这是简单的方法,针对简单的memorymap。
在link时使用选项-ro,-rw,等等指定memorymap的地址。
详细说明参看ADS_LinkerGuide中命令行选项说明。
然后利用汇编使用pre-definesymbol,来进行各种段的定位。
Linkerpre-define定义如下:
1附图:
tu1.jpg(22811字节)
由前面对ZI的说明,Image$$RW$$Limit=Image$$ZI$$Limit。
2附图:
tu2.jpg(30577字节)
这些都是linker预先定义的外部变量,在使用的时候可以用IMPORT引入。
下面给出一个例子。
假设linker选项为:
-ro-base0x40000000-rw-base0x40003000。
程序和只读变量(const变量)大小为0x84,这样ROsection的大小为0x84bytes。
Data的大小为0x04bytes,并且data被初始化,则RWsection的大小为0x04,ZIsection的大小为0x04。
这样程序
在loadview,地址是这样的:
0x40000000开始到地址0x40000080,是ROsection部分(程序从0x40000000开始),Image$$RO$$Limit=0x40000084.
0x40000084地址开始到地址0x40000084,是RWsection部分。
在executionview,由linker的选项,各个section的地址是这样的:
ROsection的地址不变。
RWsection的起始地址应当为0x40003000,则Image$$RW$$Base=0x40003000。
因为全部的0x04bytesdata被初始化,所以Image$$RW$$Limit=Image$$ZI$$Limt=0x40003004。
现在要做的就是将RWsection移到以0x40003000开始的地方,并且创造一个ZIsection。
一个更通用的做法是:
首先比较Image$$RO$$Limit和mage$$RW$$Base,如果相等,说明executionview下RWsection的地址和loadview下RWsection的地址相同,这样,不需要移动RWsection;如果不等,说明需要移动RWsection到它在executionview中的地方。
然后将Image$$ZI$$Base地址到Image$$ZI$$Limt地址的内容清零。
示例代码如下:
;读入linkerpre-definesymbols
IMPORT|Image$$RO$$Limit|
IMPORT|Image$$RW$$Base|
IMPORT|Image$$ZI$$Base|
IMPORT|Image$$ZI$$Limit|
;.......一些其他的代码或伪指令
;R0读入sectionloadaddress
LDRR0,=|Image$$RO$$Limit|
;R1读入sectionexecutionaddress
LDRR1,=|Image$$RW$$Base|
;R2读入executionsection后的紧跟的wordaddress
LDRR2,=|Image$$ZI$$Base|
;检查RWsection的地址在loadview和executionview下
;是否相等,如果相等,就不移动RWsection,直接建立
;ZIscetion
CMPR0,R1
BEQdo_zi_init
;否则就copyRWsection到executionview下指定的地址
BLcopy
;......
;......
;copy是一个用于copy的子函数,它把从R0中的地址开始的
;sectioncopy到R1中的地址开始的section,这个section的
;上限地址后紧跟的wordaddress保存在R2中
copy
CMPR1,R2
LDRCCR3,[R0],#4
STRCCR3,[R1],#4
BCCcopy
MOVPC,LR
;......
;......
;do_zi_int子函数是为创建ZIsection做一些准备工作
do_zi_int
;将ZIsection开始的地址装入R1
LDRR1,=|Image$$ZI$$Base|
;将ZIsection结束后紧跟的wordaddress装入R2
LDRR2,=|Image$$ZI$$Limit|
;将ZIsection需要的初始化量装入R3
MOVR3,#0
BLzi_int
;......
;......
;zi_int子函数用于建立并初始化ZIsection,ZIsection的
;开始地址储存在R1,ZIsection结束后紧跟的wordaddress
;地址储存在R2
zi_int
CMPR1,R2
STRCCR3,[R1],#4
BCCzi_int
MOVPC,LR
;......
;......
这个方法针对比较简单的应用,如果需要进行一个比较复杂的memorymap,如下图,那么这个方法就不适用了。
为了解决复杂memorymap的问题
需要用到scatterload机制。
3附图:
tu3.jpg(32473字节)
系统分类:
汽车电子
用户分类:
无分类
标签:
无标签
来源:
无分类
发表评论阅读全文(84)|回复(0)
0
实现一个最简单的嵌入式操作系统
发表于2007-8-99:
54:
21
实现一个什么都不能做的嵌入式操作系统
1.首先确定CPU,在这里为了简单,就选用嵌入式的CPU,比如ARM系列,之所以用RISC(简单指令集)
类型的CPU,其方便之处是没有实模式与保护模式之分,采用线性的统一寻址,也就是不需要进行段
页式内存管理,还有就是芯片内部集成了一些常用外设控制器,比如以太网卡,串口等等,不需要像
在PC机的主板上那么多外设芯片
2.确定要实现的模块和功能,为了简单,只实现多任务调度(但有限制,比如最多不超过10),实
现中断处理(不支持中断优先级),不进行动态SHELL交互,不实现动态模块加载,不实现fork之类
的动态进程派生和加载(也就是说要想在你的操作系统上加入用户程序,只能静态编译进内核中;不
支持文件系统,不支持网络,不支持PCI,USB,磁盘等外设(除了支持串口,呵呵,串口最简单嘛),
不支持虚拟内存管理(也就是说多任务中的每个进程都可以访问到任何地址,这样做的话,一个程序
死了,那么这个操作系统也就玩完了)
3.确定要使用的编译器,这里采用GCC,文件采用ELF格式,当然,最终的文件就是BIN格式,GCC和
LINUX有着紧密的联系,自己的操作系统,需要C库支持和系统调用支持,所以需要自己去裁剪C库,
自己去实现系统调用
4.实现步骤:
首先是CPU选型,交叉编译环境的建立,然后就是写BOOTLOADER,写操作系统
实现一个最简单的嵌入式操作系统
(二)
如何实现BOOTLOADER
1.之所以要实现一个专用的BOOTLOADER,一是为了更好的移植和自身的升级,二是为了方便操作系统的调试,当然,你完全可以将这部分所要实现的与操作系统相关的功能集成到操作系统中去
2.确定一个简单的BOOTLOADER所要完成的功能:
我们这里只需要完成两个主要功能,一是将操作系统加载到内存中去运行,二是将自己和操作系统内核固化到ROM存储区(这里的ROM可以是很多设备,比如嵌入式芯片中的FLASH,PC机上的软盘,U盘,硬盘等)
3.BOOTLOADER的编写:
第一步:
要进行相关硬件的初使化,比如在at91rm9200这块嵌入式板子上(以后都使用这一款芯片,主要是我对这款芯片比较熟悉,嘿嘿),大概要做接下来的几方面的工作,其一:
将CPU模式切换进系统模式,关闭系统中断,关闭看门狗,根据具体情况进行内存区域映射,初始化内存控制区,包括所使用的内存条的相关参数,刷新频率等,其二:
设定系统运行频率,包括使用外部晶振,设置
CPU频率,设置总线频率,设置外部设备所采用的频率等。
其三:
设置系统中断相关,包括定时器中断,是否使用FIQ中断,外部中断等,还有就是中断优先级设置,这里只实现两个优先级,只有时钟中断高一级,其它都一样,而中断向量初始化时都将这些中断向量指向0x18处,并关闭这里的所有中断,如果板子还接有诸如FLASH设备的话,还需要设置诸如FLASH相关操制寄存器,其四:
需要关闭CACHE,到此为止,芯片相关内容就完成初始化了
第二步:
中断向量表,ARM的中断与PC机芯片的中断向量表有一点差异,嵌入式设备为了简单,当发生中断时,由CPU直接跳入由0x0开始的一部分区域(ARM芯片自身决定了它中断时就会跳入0x0开始的一片区域内,具体跳到哪个地址是由中断的模式决定的,一般用到的就是复位中断,FIQ,IRQ中断,SWI中断,指令异常中断,数据异常中断,预取指令异常中断),而当CPU进入相应的由0x0开始的向量表中时,这就需要用户自己编程接管中断处理程序了,这就是需要用户自己编写中断向量表,中断向量表里存放的就是一些跳转指令,比如当CPU发生一个IRQ中断时,就会自动跳入到0x18处,这里就是用户自己编写的一个跳转指令,假如用户在此编写了一条跳转到0x20010000处的指令,那么这个地址就是一个总的IRQ中断处理入口,一个CPU可能有多个IRQ中断,在这个总的入口处如何区分不同的中断呢?
就由用户编程来决定了,具体实现请参见以后相关部分,中断向量表的一般用一个vector.S文件,当然,如何命名那是你自己的喜爱,但有一点需要声明,那就是在链接时一定要将它定位在0x0处
第三步:
设置堆栈,一般使用三个栈,一个是IRQ栈,一个是系统模式下的栈(系统模式下和用户模式共享寄存器和内存空间,这主要是为了简单),设置栈的目的主要是为了进行函数调用和局部变量的存放,不可能全用汇编,也不可能不用局部变量
第四步:
将自己以后的代码段和数据段全部拷贝至内存,并将BSS段清零
第五步:
进行串口的初始化(主要是为了与用户交互,进行与PC机的文件传输),F
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- arm 开发 经验