《arm电子相册项目——启动加载篇startup.s》
工具:keil4 , arm开发板(Mini2451)(s3c2451) , 串口线
功能:显示照片(自动切换 / 手动切换 / 暂停切换)
显示时钟(精确显示 / 可设置)
进度:第1篇——startup.s
描述:启动开发板最先运行的是这段汇编程序,通过这段启动代码设置异常向量表,关闭看门狗,设置中断允许,跳转到main函数
AREA Init , CODE , READONLY ;定义一个代码段,只读。格式:AREA 段名,属性1,属性2
PRESERVE8 ;8位对齐
ENTRY ;汇编程序入口点
start ;启动代码中设置异常向量表
b reset ;重启异常
b halt
b halt
b halt
b halt
b halt
b _irq ;中断异常
b halt
reset
ldr r0,=0x53000000 ;r0中放地址
ldr r1,=0x0 ;r1中放值
str r1,[r0] ;将r1中的值,赋值给r0的地址中去(关闭看门狗)
ldr sp,=(0x32000000+0x100000) ;设置栈指针
mov r0,#0x53
msr CPSR_cxsf,r0 ;设置ARM9的工作模式和中断允许
IMPORT clock_init ;声明,启动时加载clock_init函数
IMPORT Main ;声明,在汇编中可以调用c函数
bl clock_init ;启动时加载clock_init函数(设置晶振最高800M)
bl Main ;从汇编跳转到c函数,接下来都用c语言写,易读,移植性好
EXPORT delay ;声明,c函数可以调用汇编中delay
delay ;延时
ldr r0,=0x1000000 ;赋初值,让其自减
delay_loop
cmp r0,#0 ;比较是否等于0
sub r0,r0,#1 ;自减算法
bne delay_loop ;若不等于0,就跳转到delay_loop继续,类似于c循环
mov pc,lr ;当等于0时跳出,跳转到原来调用delay时的位置
halt
b halt ;当是其它异常时,循环(无视其它异常,不处理)
_irq
ldr sp,=(0x31000000 + 0x100000) ;设置栈指针
stmdb sp!,{r0-r12,lr} ;入栈
IMPORT do_irq ;声明,汇编可以调用c函数
bl do_irq ;跳转到中断函数
ldmia sp!,{r0-r12,pc}^ ;出栈(有点类似保护/恢复现场的意思)
END ;和ENTRY相对应,结束标志。
以上代码是做arm电子相册的时候写的,刚开始也就是懵懵懂懂写着,做完了之后准备整理一遍,加深自己的理解,同时也做一个分享。
1.BootLoader(启动装载)
系统启动前引导程序
在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。在一个基于ARM7TDMI core的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。
Bootloader启动大多数都分为两个阶段。第一阶段主要包含依赖于CPU的体系结构硬件初始化的代码,通常都用汇编语言来实现。这个阶段的任务有:
基本的硬件设备初始化(屏蔽所有的中断、关闭处理器内部指令/数据Cache等)。
为第二阶段准备RAM空间。
如果是从某个固态存储媒质中,则复制Bootloader的第二阶段代码到RAM。
设置堆栈。
在第一阶段中为什么要关闭Cache?通常使用Cache以及写缓冲是为了提高系统性能,但由于Cache的使用可能改变访问主存的数量、类型和时间,因此Bootloader通常是不需要的。
跳转到第二阶段的C程序入口点。
第二阶段通常用C语言完成,以便实现更复杂的功能,也使程序有更好的可读性和可移植性。这个阶段的任务有:
初始化本阶段要使用到的硬件设备。
检测系统内存映射。
将内核映像和根文件系统映像从Flash读到RAM。
为内核设置启动参数。
调用内核。
2.AREA
语法格式:
AREA 段名 属性1,属性2,……
AREA伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用“|”括起来,如|1_test|。
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。常用的属性如下:
— CODE属性:用于定义代码段,默认为READONLY。
— DATA属性:用于定义数据段,默认为READWRITE。
— READONLY属性:指定本段为只读,代码段默认为READONLY。
— READWRITE属性:指定本段为可读可写,数据段的默认属性为READWRITE。
— ALIGN属性:使用方式为ALIGN 表达式。在默认时,ELF(可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方。
— COMMON属性:该属性定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的COMMON段共享同一段存储单元。
一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。
使用示例:
AREA Init,CODE,READONLY
指令序列
;该伪指令定义了一个代码段,段名为Init,属性为只读
3. ENTRY
语法格式:
ENTRY
ENTRY伪指令用于指定汇编程序的入口点。在一个完整的汇编程序中至少要有一个ENTRY(也可以有多个,当有多个ENTRY时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个ENTRY(可以没有)。
使用示例:
AREA Init,CODE,READONLY
ENTRY ;指定应用程序的入口点
……
4.为什么ARM汇编程序前要加PRESERVE8?
这是字节对齐关键词,以前用ADS编译器的时候可以不用,但是后来的keil编译器时需要加上(譬如用周立功模板时,将ADS工程转到keil工程时就必须加上)。require8和preserve8,c和汇编有8位对齐的要求,这两个伪指令可以满足此要求,另外,REQUIRE8和PRESERVE8并不完成8 byte 对齐的操作,对齐由ALIGN完成。
5. ldr r0,=0x53000000
ldr r1,=0x0
str r1,[r0]
LDR R0,=0x53000000是将0x53000000放到R0中。
LDR R1,=0x0是将立即数0放到R1中。
STR R1,[R0]是一个典型的存储指令,将R1中的值放到以R0中的值为地址的存储单元去。实际就是将0放到地址为0x53000000的存储单元中去。