我们一般在Windows下的MDK环境或者是ADS环境下开发裸机程序
下面来体验一下Linux下GNU-ARM的裸奔程序开发
GNU ARM裸奔程序的开发步骤如下:
1)编写程序(汇编/C程序)
2)编写链接脚本
3)编译、汇编程序文件,生产目标文件(.o)
4)利用链接脚本链接目标文件,生产可执行文件(elf格式)
5)利用格式转换工具(objcopy)将elf格式文件转换为bin格式
6)烧写
这里汇编指令就略过了。
汇编行结构
[标号:] 汇编语句 @注释
标号
标号只能由a~z,A~Z,0~9,“.”,“_”等字符组成;
“.” 标号 :特殊的标
“_”表示当前位置的运行地址 ;
注释
@ 从符合开始到行结尾
# 整行注释
汇编程序中的分段
汇编系统预定义的段名
.text @代码段
.data @已经初始化的全局变量和静态变量
.bss @是未初始化的全局变量和静态变量
自定义一个段
.section 自定义段名
汇编程序中的关键字
.extern symbol
声明symbol的定义在其他源文件中
.global symbol
声明symbol是全局可见的
.include "filename"
在当前源文件中插入filename的内容
.set symbol, expression
设置symbol的值为expression
GNU的伪指令跟MDK的伪指令不同;但是两个编译器都能够识别ARM指令;
GNU编译环境下的伪操作可分为以下几类:
常量编译控制伪操作
汇编程序代码控制伪操作
宏及条件编译控制伪操作
其他伪操作
常量编译控制伪操作
汇编程序代码控制伪操作
宏及条件编译控制伪操作
其他伪操作
连接脚本文件
elf格式文件:
GNU编译器生成的目标文件缺省为elf格式;
elf文件由若干段(section)组成;
目标代码中包含如下段:
.text: 包含程序的指令代码;
.data: 包含固定的数据,如常量、字符串;
.bss: 包含未初始化的变量、数组等;
连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。
链接脚本
内置缺省连接脚本:
如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行(操作系统上的应用程序采用缺省脚本)。
自定义链接脚本:
为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。
链接脚本格式
链接器脚本由一系列命令组成;
“SECTIONS”命令:描述输出文件的内存布,SECTIONS段的基本格式如下:
SECTIONS
{
段名 : {段内容}
}
ENTRY(symbol) :设置入口点,参数是一个符号名;
OUTPUT_FORMAT(bfdname):输出文件的BFD格式;
OUTPUT_ARCH(bfdarch) :指定平台架构;
对于ARM,bfdname=elf32-littlearm,bfdarch=arm
LED跑马灯程序链接脚本:
led.lds:
1 OUTPUT_FORMAT(elf32-littlearm)
2 OUTPUT_ARCH(arm)
3 ENTRY(_start)
4
5 SECTIONS
6 {
7 >---. = 0x0;
8 >---. = ALIGN(4);
9 >---.text : {
10 >--->---led.o (.text)
11 >--->---delay.o (.text)
12 >---}
13
14 >---. = ALIGN(4);
15 >---.data : {
16 >--->---*(.data)
17 >---}
18 >---
19 >---. = ALIGN(4);
20 >---.bss : {
21 >--->---*(.bss)
22 >---}
23
24 }
编译链接过程
Arm-linux-ld –Txxx.lds xx.o –o xxxx.elf
–T 直接指定代码段
xxx.lds 为连接脚本
arm-linux-objcopy被用来复制一个目标文件的内容到另一个文件中,可用于不同源文件的之间的格式转换
Arm-linux-objcopy –O binary –S elf_file bin_file
-O bfdname 输出的格式
-F bfdname 同时指明源文件,目的文件的格式
-R sectionname 从输出文件中删除掉所有名为sectionname的段
-S 不从源文件中复制重定位信息和符号信息到目标文件中
-g 不从源文件中复制调试符号到目标文件中
makefile:
1 led.bin:led.o delay.o led.lds
2 >---arm-linux-ld -Tled.lds led.o delay.o -o led.elf
3 >---arm-linux-objcopy -O binary -S led.elf led.bin
4
5 led.o:led.s
6 >---arm-linux-gcc -c led.s -o led.o>
7
8 delay.o:delay.s
9 >---arm-linux-gcc -c delay.s -o delay.o
10
11 clean:-
12 >---rm -rf *.o *.elf *.bin
13
led.s:
.text
.extern delay
.global _start
_start:
@set GPBCON
ldr r0,=0x56000010
ldr r1,[r0]
bic r1,r1,#0xc00
orr r1,r1,#0x15400
str r1,[r0]
@set GPBUP
ldr r0,=0x56000018
ldr r1,[r0]
orr r1,r1,#0x20
str r1,[r0]
loop:
ldr r0,=0x56000014
ldr r3,=0x1e0
str r3,[r0]
bl delay
ldr r3,=0xfd0
str r3,[r0]
bl delay
ldr r3,=0xfb0
str r3,[r0]
bl delay
ldr r3,=0xf70
str r3,[r0]
bl delay
ldr r3,=0xef0
str r3,[r0]
bl delay
b loop
.end
delay.s:
1
2 .text
3 .global delay
4 delay:
5 >---ldr r2,=0x2000000
6 delay_loop:
7 sub r2,r2,#1
8 cmp r2,#0x0
9 bne delay_loop
10 mov pc,lr
11
12 .end
最后,把make出来的bin烧到板上。
假如用jtag可以烧到0地址。此时cup频率是12mhz,delay.s下的ldr r2,=0x20000,此时开发板上的bootloader已经挂了,所以不推荐用这种方法。
假如用dnw下到内存里运行,此时的cup频率是400mhz,delay.s下的ldr r2,=0x2000000,ok