嵌入式linux应用开发手册笔记
-O
大写
连接器选项
下面的选项用于连接obj文件,输出可执行文件或库文件
-llibrary
连接名为library的库文件
-nostartfiles
不连接系统标准启动文件,用于编译bootloader、内核
-nostdlib
不连接系统标准启动文件和标准库文件,用于编译内核、bootloader,他们不需要启动文件标准库文件
-static
阻止使用连接共享库
-shared 生成一个共享obj库
目录选项
-Idir 在头文件的搜索路径列表中添加dir目录
头文件的搜索方法:#include<>只从标准库目录开始搜索(包括使用-Idir选项定义的目录)
#include“”先从用户的工作目录开始搜索,在搜索标准库目录
-I-
只是用于#includ“”,在-I-前面的-I搜索路径只是用于#include “”
;如果用“-I”选项指定的搜索路径位于-I-选项后面,就可以在这些路径中搜索所有的#include指令,
-I选项能够阻止当前目录成为搜索“#include”的第一选择
-Ldir
在-I选项的搜索路径列表中添加dir目录
在-I选项的搜索路径列表中添加dir目录
-T可以直接指定代码段数据段,bss段的起始地址也可以用来指定一个连接脚本,在连接脚本中进行更复杂的地址设置;只用于bootloader、内核等“没有底层软件支持”的软件;连接运行与操作系统之上的应用程序无需指定-T选项,
格式如下:
-Ttext startaddr
-Tdata startaddr
-Tbss startaddr
b、bl、mov
不依赖于-Ttext选项,ldr依赖。
2.使用连接脚本设置地址
arm-linux-ld -Ttimer.lds -o xxx
他使用连接脚本timer.lds来设置可执行文件timer_elf的地址信息,timer_elf文件内容如下:
SECTIONS {
.=0X30000000;
//设置“当前运行地址”为0x30000000
.text
:
{*(.text)} //定义了一个名为‘.text'的段它的内容为“*(.text)”,表示所有输入文件的代码段。这些代码段被集合在一起,起始运行地址为0x30000000.
.rodata ALIGN(4)
:{*(.rodata)}//定义“.rodata”段,ALIGN(4)表示起始运行地址为4字节对齐,假设前面段的地址范围是0x30000000~0x300003f0,则“.rodata”段的地址是4字节对齐后的0x300003f4
.data ALIGN(4)
:{*(.data)}
.bss ALINGN(4)
:{*(.bss
*(COMMON)}
}
完整的连接脚本格式如下:
SECTIONS {
...
secname start ALIGN(align)(NOLOAD)
:AT(ldadr)
{contents}>region:phdr
=fill
...
}
secname
和contents是必需的,前者用来命名这个段,后者用来确定代码中的说明部分
(NOLOAD):用来告诉加载器,在运行时不用加载这个段,
AT(ldadr):指定这个段在编译出来的映像文件中的加载地址如果不使用这个选项,则加载地址等于运行地址
3.1.3
arm-linux-objcopy用来将elf格式的可执行文件转换为二进制文件
被用来复制一个目标文件的内容到另一个文件,可以使用不同于源文件的格式来输出目的文件,即可以进行格式转换。
arm-linux-objcopy
-O binary -S led_elf led.bin
arm-linux-objdump -D xxx > xxx.dis
反汇编指令
3.2 Makefile
target..:依赖 ....
(command)
eg:
hello:hello.c
gcc
-o hello hello.c
//执行编译hello.c
clean:
rm
-f hello
//清除编译出来的文件
注意:每个命令行前面必须是一个tab字符,即命令行第一个字符是tab,这是容易出错的地方
3.2.2
MAKEFILE
赋值
分为延时变量和立即变量区别在于前者在使用时才扩展开(才确定),后者是在定义时它的值已经确定了。
immeddiate=deferred
//延时变量
immeddiate?=deferred
//用来定义第一次出现的延时变量
immeddiate:=deferred
//立即变量
immeddiate+=deferred
or immediate
//若右边变量在前面使用(:=)定义为立即变量则它也是立即变量,否则均为延时变量
3.2.3
函数格式:$(function arguments)
$(subst from,to,text)
在文本中to替换每一处from(字母)
$(patsubst
pattern,replacement,text)//寻找text中符合pattern的字用replacement替换它们(字符串?)
$(strip
string)//去掉前导和结尾空格,并将中间的多个空格压缩为单个空格,如$(strip a
b c)结果为a b c
$(findstring
find,in)在字符串“in”中搜寻“find”,如果找到,则返回值是“find”,否则返回值为空
$(filter pattern...,text)
//返回text中由空格隔开且匹配格式“pattern...”的字
$(sort list)
//将list中的字按字母顺序排序,并去掉重复的字
以options程序的Makefile为例,options目录下所有的文件为main.c
Makefile sub.c sub.h 。Makefile 内容如下:
src :=$(shell ls *.c)
//src=main.c sub.c
objs:=$(patsubst
%.c,%.o,$(src)) //objs=main.o sub.o
test:$(objs)
//test:main.o sub.o
gcc -o $@ $^
%.o:%.c
gcc -c -o $@ $<
clean:
rm -f test *.o
3.3.1 arm
asm and atpcs rules
跳转指令b、bl
bl除了跳转之外,还将返回地址(bl的下一条指令的地址)保存在lr寄存器中,这两条指令的可调转范围是当前指令的前后32mb
mov
mov r1,r2
//r1=r2
mov r1,#4096
//传送的常数必须能用立即数来表示
ldr:从内存中读取数据到寄存器,str
:相反,它们的操作数据都是32位
ldr
即可能是大范围的地址读取指令可能是内存访问指令,当第二个参数有=时表示伪指令
ldr r1,=4097 //r1=4097// d
ldr r1,=label
label:
....
ldr r1,[r2,#4]
//将r2+4的内存地址数据读取到r1中
ldr r1,[r2],#4
//将r2的内存地址数据读取到r1中然后r2=r2+4
str r1,[r2,#4] //[r2+4]=r1
str r1,[r2],#4 //[r2]=r1,r2=r2+2
ldmia
strdb
add sub
add r1,r2,#4 .r1=r2+4
sub r1,r2,#4 .r1=r2-4
msr and mrs
ARM有一个程序状态寄存器cpsr,它用来控制处理器的工作模式、设置中断的总开关
msr cpsr,r0
.复制r0到cpsr中
mrs r0,cpsr .复制cpsr到r0中
other asm
eg:
.extern
main
//定义一个外部符号变量或函数,在这里指引用外部函数main
.text
//表示下面的语句都是代码段
.global
_start
//.global将文件中的某个程序标号定义为全局的
_start:
汇编指令执行条件2012年9月3日 15:10:34
ATPCS(子程序调用规则)
1.寄存器使用规则
arm
处理器中有r0~r15共16个寄存器
FD:Full Descending 满递减
ED:Empty Descending 空递减
FA:Full Ascending 满递增
EA:empty ascending 空递增
数据栈为FD,8字节对齐使用stmdb/ldmia批量内存访问指令来操作fd数据栈
使用stmdb命令玩数据栈中保存内容时先递减sp指针,在保存数据(入栈)
eg:stmdb sp!,{fp,ip,lr}
使用ldmia命令从数据栈中恢复数据先获得数据在递增sp指针,sp指针总是指向栈顶元素
eg:ldmia sp,{fp,sp,pc}
3.参数传递规则
int copycode2sdram(unsigned char *buf,unsigned long
strart_addr,int size)
在汇编代码中,使用下面的代码调用它并判断返回值
ldr r0,=0x30000000
//
*buf=0x30000000
mov r1,#0 //
start_addr=0
mov r2,#16*1024 //size=16*1024
cmp a0,#0 //return value
第五章
gpio接口
s3c2440有130个I/O口:gpa、gpb...gpj。
通过寄存器来操作gpio引脚
GPXCON(32位)
GPXCON用于选择引脚功能、gpxdat用于读/写引脚数据;gpxup用于确定是否使用上拉电阻。
gpacon寄存器对应gpa(共23个引脚),当gpacon某位=0,我们可以在gpadat相应位写入0或1让此引脚输出低电平或高电平,gpacon=1,gpa相应引脚为地址线或用于地址控制,一般gpacon全设为1以便访问外部存储器件
gpbcon~gpjcon
没两位控制一根引脚:00=输入;01=输出;10=特殊功能;11=保留不用
gpb=[10:0]
GPXDAT
GPXDAT根据gpxcon设置来读/写引脚状态
GPXUP
gpxup=1,相应引脚无内部上拉电阻;gpxup=0,使用内部上拉电阻
1.访问单引脚
#define GPBCON (*(volatile
unsigned long *)0x56000010)
#define GPBDAT
(*(volatile unsigned long*)
0x56000014)
#define GPB5_out
(1<<(5*2))
GPBCON=GPB5_out;
// gpb5=out,
GPBDAT&=~(1<<5);
//GPB5=0
2.总线方式访问
5.2 点亮第一个led灯
tq2440 4个灯
gpbcon对应地址 0x56000010
gpbdat对应地址0x56000014
估汇编程序如下:
1 .text
2 .global _start
3 _start:
4
LDR R0,=0x56000010
5
MOV R1,#0x400
6
STR R1,[R0]
@GPBCON SET OK
7
8
9
LDR R0,=0x56000014
10
MOV R1,#0x0
11
STR R1,[R0]
@GPBDAT SET OK
12
13 main:
14
B main
对应makefile 如下:
1 led.bin:led.s
2
arm-linux-gcc -g -c -o led.o led.s
//-g 可执行程序中包含标准调试信息
-s只编译不连接生成汇编代码
-c只编译不链接生成目标文件”o“
3
arm-linux-ld -Ttext 0x30000000 -g led.o -o led_elf
//链接
4
arm-linux-objcopy -O binary -S led_elf led.bin
-O//elf转为二进制bin并优化
5 clean:
6
rm -f *elf *.o //删除
使用C语言点灯
crt0.s
@*****************
@trun to c
@*******************
.text
.global _start
_start:
ldr r0,=0x56000010 @watchdog conf addr
mov r1,#0x0
str
r1,[r0]
@stop
watchdog
ldr
sp,=1024*4
@sp<=4kb
bl main
@use c main
halt_loop:
b
halt_loop
ledonc.c
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
int main()
{
GPBCON=0X400;//GPB5C5=01 OUT
GPBDAT=0X00;
//GPB5=0
return 0;
}
MAKEFILE:
ledonc.bin:crto.s ledonc.c
arm-linux-gcc -g -c -o
crto.o
crto.s
arm-linux-gcc -g -c -o ledonc.o ledonc.c
arm-linux-ld -Ttext 0x0000000 -g crto.o ledonc.o -o
ledonc_elf
arm-linux-objcopy -O binary -S ledonc_elf ledonc.bin
arm-linux-objdump -D -m arm ledonc_elf >ledonc.dis
clean:
rm -f ledonc.dis ledonc.bin ledonc_elf *.o
3.使用按键是对应灯亮
gpfcon=0x56000050 gpfdat=0x56000054
crt0.s
@*****************
@trun to c
@*******************
.text
.global _start
_start:
ldr r0,=0x56000010 @watchdog conf addr
mov r1,#0x0
str
r1,[r0]
@stop
watchdog
ldr
sp,=1024*4
@sp<=4kb
bl main
@use c main
halt_loop:
b
halt_loop
keyledon.c
#define GPBCON (*(volatile unsigned long*)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
int main()
{
GPBCON=0x15400; //gpbcon=010101010000000000
GPFCON=0x00; //00 means input
int rdat; //use toread gpfdat
while(1)
{
rdat=GPFDAT;
if(rdat&0x01)
GPBDAT=0x10;//led0 off
else
GPBDAT=0xef; //led0 on
//
if(rdt&)
}
}
Makefile
keyled.bin:crto.s keyledon.c
arm-linux-gcc -g -c -o crto.o crto.s
arm-linux-gcc -g -c -o keyledon.o keyledon.c
arm-linux-ld -Ttext 0x00000000 -g crto.o keyledon.o -o
keyledelf
arm-linux-objcopy -O binary -S keyledelf keyledelf.bin
clean:
rm -f *.o *.bin *elf