最近拿出了空闲已久的Tiny6410开发板来玩一下,一直对Linux有变态的崇拜,所以这次过度没有悬念的抛弃诸如ADS1.2或RVDS等IDE开发环境。完全的VIM + GCC搞定一切。
言简意赅,生成的BIN放入SD卡中启动,详细过程参见Tiny6410的帮助文档,配置文件附送光盘里都有。
这里仅主要介绍一下如何点亮核心板上的4个LED,当然在此之前arm-linux-gcc交叉编译环境要搭建起来,还要添入环境变量之中(在终端中输入arm-linux-gcc -v 看是否能正确的查询到当前交叉编译器的版本号以检查是否添加成功,出现如图所示说明添加成功)。
为了点亮LED,我们先看一下友善之臂Tiny6410开发板关于LED的原理图部分。
由图可见,我们要操作的LED分别由GPK[4-7]口控制。
然后就是看一下三星的datasheet了。
可见我们把输入方向配置为输出,并输出低电平LED才会亮。
首先先看汇编代码。
@******************************************** @ File:led_on.s @ function: LED @ author: pang123hui @******************************************** .text @ 定义一个代码段 .global _start @ 定义一个全局入口 _start: @ 全局入口处 LDR R0,=0x7F008800 @ 设R0为GPKCON0寄存器 MOV R1,#0x11112222 @ 设置GPKC[4-7]为输出 STR R1,[R0] LDR R0,=0x7F008808 @ R0设为GPKDAT寄存器 MOV R1,#0x00000000 @ 设置GPKDAT[4-7]为低电平 STR R1,[R0] MAIN_LOOP: B MAIN_LOOP
汇编里的B指令为跳转指令,直接向PC寄存器赋值。B跳转是个相对跳转指令,其机器吗格式如下:
即B跳转指令依赖于当前PC寄存器的值,这个特点使得使用B指令的程序不依赖于代码存储的位置——即不管这条代码放在什么位置,B指令都可以跳到正确的位置。这类指令被称为位置无关码。
还有一条BL指令,除了条装之外,还将返回地址(BL的下一条指令的地址)保存在LR寄存器中,也是位置无关的指令。
B和BL指令可跳转的范围是当前指令的前后32MB.
相关Makefile文件。
CC=arm-linux-gcc CFLAGS=-g -c -o led_on.bin:led_on.s $(CC) $(CFLAGS) led_on.o led_on.s arm-linux-ld -Ttext 0x0000000 -g led_on.o -o led_on_elf arm-linux-objcopy -O binary -S led_on_elf led_on.bin clean: rm -f led_on.bin led_on_elf *.o
简单介绍一下相关指令。
A. arm-linux-gcc
-c 对源文件进行预处理、编译、汇编,但不做链接,生成中间OBJ文件,通常以.o结尾。
-g 添加调试信息
-o 指定输出文件。如果不指定-o filename 选项,默认输出为a.out文件。
B. arm-linux-ld
-Ttext startaddr
-Tdada startaddr
-Tbss startaddr
其中-T选项用来指定代码段、数据段、BSS段的起始地址。如果不定义数据段和BBS段的起始地址,它们会被依次放在代码段的后面。
C. arm-linux-objcopy
用于将一个目标文件复制到另一个文件内,可以使用不同于源文件的格式输出到目的文件。常用于格式转化。
-O 用于指定输出的文件格式。如二进制 –O binary
-I 用于指定源文件的格式
-S 不从源文件中复制重定位信息和符号信息到目标文件
D. arm-linux-objdump用于显示二进制文件住处。常用于进行反汇编,方便调试。
-D 反汇编所有段
-m 指定反汇编目标文件所使用的架构,如 –m arm 指定为ARM体系架构。
-b 指定输入文件的格式,这不是必须的,arm-linux-objdump能自动识别多种格式。
C语言代码。
#define rGPKCON0 (*(volatile unsigned *)(0x7F008800)) #define rGPKCON1 (*(volatile unsigned *)(0x7F008804)) #define rGPKDAT (*(volatile unsigned *)(0x7F008808)) #define rGPKPUD (*(volatile unsigned *)(0x7F00880C)) int main( void ) { // 设置GPKCON[4-7]为输出 rGPKCON0 = 0x11112222; // 设置GPKDAT[4-7]为低电平 rGPKDAT = 0xffffff0f; return 0; }
有些学习过Bootloader和内核的朋友肯定会知道,在调用C语言函数之前肯定会有一段汇编代码在前面铺路,进行一些必要的初始化工作;而那些只学过单片机而没有学过ARM的朋友肯定会觉得很奇怪,在单片机中写C代码,前面完成可以不用任何汇编代码。这是为什么呢?
这主要是因为我们的开发环境(这里主要是指编译环境)的不同,在开发单片机程序的时候,开发环境(如KEIL)会在编译C代码的时候,给我添加启动代码(startup-51)或者在编译时已经由编译器在后台为我们初始化好了。而在开发ARM程序时,ARM处 理器支持多种模式,多种功能,而在不同的领域不同的项目里面,我们可以有选择的、适当的选择这些功能,这时,编译器就不知道我们需要什么功能,需要什么模 式,编译器也就无法给我们提供默认的“初始化”代码,所以,编译器干脆就“不管”这些了,把这些工作交由我们开发者来处理。
这里最简单的启动代码。
@**************************************** @ File: startup.s @ function: start up @ author: pang123hui @**************************************** .text .global _start _start: LDR R0,=0x7E004000 @ WATCHDOG寄存器地址 MOV R1,#0x0 STR R1,[R0] @ 写入0,禁止WATCHDOG,否则CPU会不断重启 LDR SP,=0x0c001000 @ 设置堆栈,注意:这时我们是将程序直接烧录到SDRAM中,所以堆栈要设置在SDRAM中 @ 如果将程序烧在NAND FLASH中,需将堆栈改为1024*4,因为NAND FLASH中的代码 @ 在复位后会移到内部RAM中,此RAM只有4K BL main @ 调用C程序中的main函数 halt_loop: B halt_loop
这里要注意的一点是S3C6410中RAM的地址,见datasheet,如图。
最后是Makefile文件。
CC=arm-linux-gcc CFLAGS=-g -c -o led_on.bin:startup.s main.c $(CC) $(CFLAGS) startup.o startup.s $(CC) $(CFLAGS) main.o main.c arm-linux-ld -Ttext 0x0c000000 -g startup.o main.o -o led_on_elf arm-linux-objcopy -O binary -S led_on_elf led_on.bin clean: rm -f led_on.bin led_on_elf *.o
东西很简单没什么好说的,在此记录,以免忘记,如有疑问或发现本文有何错误,欢迎提出。
---------------------------------------------------------------------------------------------------------------------------------------
作者:庞辉
出处:
本文基于许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名(包含链接).