Tiny6410裸机开发笔记(一)汇编点亮LED

开发版信息

CPU: S3C6410
RAM:128MB
NAND: 256MB(SLC) ID:ECDA1095
Touch Device: 1-wire
LCD Type: S70

1. 查阅原理图

Tiny6410 板上提供了 4 个可编程用户 LED,原理图如下:
LEDS
GPIO
LED1,2,3,4 分别使用的 CPU 端口资源为GPK_4,5,6,7

2. 程序编写

2.1 Start.S

2.1.1 代码编写

由原理图可知,点亮Tiny6410 的4 个LED 需如下2 个步骤:
把外设的基地址告诉CPU;对于6410 来说,内存的地址范围为0x00000000~0x60000000,外设的地址范围为0x70000000-0x7fffffff。
关闭看门狗,防止程序不断重启;
设置寄存器GPKCON0,使GPK_4/5/6/7 四个引脚为输出功能;
往寄存器GPKDAT 写0,使GPK_4/5/6/7 四个引脚输出低电平,4 个LED 会亮;相反,
往寄存器GPKDAT 写1,使GPK_4/5/6/7 四个引脚输出高电平,4 个LED 会灭;
Address Map

S3C6410X支持32位物理地址字段,该地址字段可以分为两部分,一部分是内存,另一部分是外设。

主存通过SPINE总线访问,其地址范围为0x0000_0000到0x6FFF_FFFF。这个主存部分分为四个区域,启动映像区、内存区、静态内存区和动态内存区。

启动映像区的地址范围是从0x0000_0000到0x07FF_FFFF,但是没有真正的映射内存。启动映像区具有镜像映像,镜像映像指向内部内存区或静态内存区的部分区域。启动映像的起始地址固定为0x0000_0000。

内部存储器区用于访问内部ROM和内部SRAM,用于引导加载程序,也称为Stepping Stone。每个内存的起始地址是固定的。内部ROM的地址范围是0x0800_0000到0x0BFF_FFFF,但实际存储空间只有32KB。此区域是只读的,当选择内部ROM引导时,可以映射到引导映像区。内部SRAM的地址范围是从0x0C00_0000到0x0FFF_FFFF,但实际存储空间只有4KB。

静态存储区的地址范围是从0x1000_0000到0x3FFF_FFFF。SROM、SRAM或闪存,异步NOR接口设备、OneNAND Flash和Steppingstone可以通过这个地址区访问。

每个区域代表一个芯片选择,例如,地址范围从0x1000_0000到0x17FF_FFFF代表Xm0CSn[0]。每个芯片选择的起始地址是固定的。NAND闪存和CF/ATA不能通过静态内存区域访问,所以如果Xm0CSn[5:2]中的任何一个映射到NFCON或CFCON,则相关的地址区域不应该被访问。一个例外是,如果Xm0CSn[2]用于NAND闪存,则Steppingstone地址将从0x2000_0000镜像到27FF_FFFF。

动态存储区的地址范围是从0x4000_0000到0x6FFF_FFFF。DMC1有权使用地址范围0x5000_0000到0x6FFF_FFFF。每个芯片选择的起始地址是可配置的。

外设通过PERI总线访问,其地址范围为0x7000_ 00000x7FFF_ FFFF。所有SFR可以在此地址范围内访问。此外,如果需要从NFCON或CFCON传输数据,这些数据应该通过外围总线传输。

.global _start
_start: 
		
	//外设基地址及大小告诉CPU
	ldr r0, =0x70000000		//ldr: load
	orr r0, r0, #0x13 		//0x13=b10011=256M, 参见arm1176jzfs内核参考手册Page3-130
	mcr p15,0,r0,c15,c2,4       	//把r0的值(包括了外设基地址+外设大小)告诉cpu
	
	//关看门狗
	ldr r0, =0x7E004000		//watch dog timer base address
	mov r1, #0
	str r1, [r0]			//disable watch dog.  str: Store
	
	//设置GPKCON0
	ldr r0, =0x7F008800		//GPKCON0 address
	ldr r1, =0x11110000		//GPK_4,5,6,7设置为输出, GPKn设置为0001时,GPKn引脚设置为输出
	str r1, [r0]
	
	mov r2, #0x1000			//LED循环计数
led_blink:
	ldr r0, =0x7F008808		//GPKDAT address
	mov r1, #0xF0			//设置GPK_4,5,6,7为高电平,Led熄灭
	str r1, [r0]
	
	bl  delay
	
	ldr r0, =0x7F008808
	mov r1, #0x00			//设置GPK_4,5,6,7为低电平,Led点亮
	str r1, [r0]
	
	bl  delay
	
	sub r2, r2, #1			//r2=r2-1
	cmp r2, #0
	bne led_blink			//如果r2!=0,则跳转至led_blink处执行。bne:Branch Not Equal
		
halt:
	b   halt			//b: Branch
		
delay:
	mov r0, #0x1000000	
delay_loop:		
	sub r0, r0, #1
	cmp r0, #0
	bne delay_loop
	mov pc, lr			//从delay子程序返回
		

2.1.2 汇编指令学习

1. 跳转语句 B,BL
程序流程的跳转,在 ARM 程序中有两种方法可以实现程序流程的跳转指令用于实现

l  使用专门的跳转指令 B

l  直接向程序计数器PC 写入跳转地址值

n  这是几乎是任何一种CPU必备的机器,PC表示CPU当前执行语句位置,改变PC的值,相当于实现程序跳转

n  如实现类似C语言的Return 语句,就是用MOV PC,LR

n  这里可以在任意4G的空间进行跳转

 

B指令(Branch)表示无条件跳转.

   B main ;跳转到标号为main地代码处

 

BL指令(Branch with Link)表示带返回值的跳转.

   BL比B多做一步,在跳转前,BL会把当前位置保存在R14(即LR寄存器),当跳转代码结束后,用MOV PC,LR指令跳回来,这实际上就是C语言执行函数的用法,

   汇编里调子程序都用BL,执行完子函数后,可以用MOV PC,LR跳回来.

   BL delay ;执行子函数或代码段delay ,delay可以为C函数.

与MOV PC,XXX能在4G空间跳转不同,B语句只能32M空间跳转,(因为偏移量是一个有符号26bit的数值=32M)


2. 传输数据指令MOV,MVN
n  MOV(MOVE)指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器

MOV R0,R1 ; 把R1的值传到R0

MOV R3,#3 ;把常数3传给R3,MOV中用#表示常数,这个值不能超过

n  MVN( MOVE Negative)取反后再传值,比MOV多了一步取反

MVN R0, #0 ;0取反(即-1)传给R0

MVN R1,R2  ;把R2的值取反传给R1

3. 加载/存储指令,LDR,STR
n  LDR,STR是用于寄存器和外部存储器交换数据指令,注意与MOV的区别,后面只在寄存器或常数交换.

u  LDR/STR可以采用多种寻址方式,以下只举出使用频率最高几种用法

n  LDR(load)用于把一个32Bit的WORD数据从外部存储空间装入到寄存器中

LDR R0,[R1]; R1的值当成地址,再从这个地址装入数据到R0 (R0=*R1)

LDR R1,=0x30008000 ; 把地址0x30008000的值装入到R1中,LDR中用常数要用=打头.(注意跟MOV的区别,MOV是#)

ldr  r0, =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)

用位与的方法赋值

 

n  STR(Store) 用于把一个寄存器的值存入外部存储空间,是LDR的逆操作.

STR R0,[R1] ; 把R0的值,存入到R1对应地址空间上(*R1 = R0)

STR R0,=0x30008000 ;把R0中值存入到地址0x30008000

2.2 Makefile

2.2.1 代码编写

led.bin: start.o
	arm-linux-ld -Ttext 0x50000000 -o led.elf $^
	arm-linux-objcopy -O binary led.elf led.bin
	arm-linux-objdump -D led.elf > led_elf.dis
%.o : %.S
	arm-linux-gcc -o $@ $< -c

%.o : %.c
	arm-linux-gcc -o $@ $< -c 

clean:
	rm *.o *.elf *.bin *.dis  -rf

2.2.2 Makefile学习

1. 主要功能
Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile 文件是许多编译器--包括 Windows NT 下的编译器--维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。

2. 规则

target ... : prerequisites ...
command
...
...

目标:依赖
执行指令 ...

target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)
 prerequisites就是,要生成那个target所需要的文件或是目标。
 command也就是make需要执行的命令。(任意的Shell命令)

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行(command一定要以Tab键开始,否则编译器无法识别command),减少重复编译,提高了其软件工程管理效率。

3. 自动化变量
在上述的模式规则中,目标和依赖文件都是一系例的文件,那么我们如何书写一个命令来完成从不同的依赖文件生成相应的目标?因为在每一次的对模式规则的解析时,都会是不同的目标和依赖文件。
自动化变量就是完成这个功能的。在前面,我们已经对自动化变量有所提涉,相信你看到这里已对它有一个感性认识了。所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。
下面是所有的自动化变量及其说明:
$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o""$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
$<
依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
所有比目标新的依赖目标的集合。以空格分隔。
$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$+
这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$*
这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。
当你希望只对更新过的依赖文件进行操作时,"$?"在显式规则中很有用,例如,假设有一个函数库文件叫"lib",其由其它几个object文件更新。那么把object文件打包的比较有效率的Makefile规则是:
lib : foo.o bar.o lose.o win.o
ar r lib $?
在上述所列出来的自动量变量中。四个变量($@、$<、$%、$*)在扩展时只会有一个文件,而另三个的值是一个文件列表。这七个自动化变量还可以取得文件的目录名或是在当前目录下的符合模式的文件名,只需要搭配上"D""F"字样。这是GNU make中老版本的特性,在新版本中,我们使用函数"dir""notdir"就可以做到了。"D"的含义就是Directory,就是目录,"F"的含义就是File,就是文件。
下面是对于上面的七个变量分别加上"D"或是"F"的含义:
$(@D)
表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。
$(@F)
表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o""$(@F)"相当于函数"$(notdir $@)"
"$(*D)"
"$(*F)"
和上面所述的同理,也是取文件的目录部分和文件部分。对于上面的那个例子,"$(*D)"返回"dir",而"$(*F)"返回"foo"
"$(%D)"
"$(%F)"
分别表示了函数包文件成员的目录部分和文件部分。这对于形同"archive(member)"形式的目标中的"member"中包含了不同的目录很有用。
"$(<D)"
"$(<F)"
分别表示依赖文件的目录部分和文件部分。
"$(^D)"
"$(^F)"
分别表示所有依赖文件的目录部分和文件部分。(无相同的)
"$(+D)"
"$(+F)"
分别表示所有依赖文件的目录部分和文件部分。(可以有相同的)
"$(?D)"
"$(?F)"
分别表示被更新的依赖文件的目录部分和文件部分。
最后想提醒一下的是,对于"$<",为了避免产生不必要的麻烦,我们最好给$后面的那个特定字符都加上圆括号,比如,"$(<)"就要比"$<"要好一些。
还得要注意的是,这些变量只使用在规则的命令中,而且一般都是"显式规则""静态模式规则"(参见前面"书写规则"一章)。其在隐含规则中并没有意义。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值