编辑、通信软件的安装都比较简单,只有按照其使用说明安装即可,或者参考经典案例中的方法。
在开发基于T-PAD的软件前必须完成其开发环境的建立,嵌入式软件开发环境是由编辑工具,编译、链接、调试工具,下载通讯工具等。
本课程开发主机系统选择为Linux兼容系统,所以编辑工具软件可以使用linux下支持的任何文本编辑工具软件,推荐用vim,另外在windows下可以使用source insight。串口终端软件,在Linux下可以使用minicom,ckermit,gtkterm等,在windows下可以使用SecureCRT、putty和超级终端等。tftp网络通信软件在linux系统下可以使用tftpd-hpa等,在windows下可以使用tftpd32。开发调试工具软件我们使用GNU ARM工具集。
本案例主要完成GNU ARM 工具软件的获取,安装,使用,为后续软件开发打下基础。
使用arm-2009q3软件包,先完成该工具软件的安装和PATH配置,然后熟悉arm-linux-gcc等相关程序的使用。
实现此案例需要按照如下步骤进行。
步骤一:将arm-2009q3.tar.bz2复制到ubuntu系统的/opt目录下,并解压安装。解压操作如下。
$ sudo chmod 777 /opt $ cp /mnt/hgfs/day02/arm-2009q3.tar.bz2 /opt $ cd /opt $ sudo tar -jxvf arm-2009q3.tar.bz2
步骤二:编辑用户主目录下的 .bashrc文件,添加PATH 路径设置。
$ cd $ vim .bashrc PATH = /opt/arm-2009q3/bin:$PATH: export PATH
步骤三:使刚刚设置的PATH起作用可执行如下命令。
$ source .bashrc
步骤四:测试刚刚安装的工具集,可以输入如下命令。
$ arm-linux-gcc -v
执行如上命令,终端窗口会有arm-linux-gcc的信息输出,如果提示命令找不到则安装或PATH变量没设置或者PATH没有生效。需要回头查找前述步骤是否正确完成。
步骤五:GNU ARM命令行工具包中包含多个基本命令,现简单介绍一下其使用。
1.编译器arm-linux-gcc。
编译器用来编译源码文件产生可执行文件。可以编写一个打印hello word的C程序文件hello.c,然后将其编译成可执行文件。hello.c示例代码如下:
#include<stdio.h> int main(void) { printf("\n Hello World!\n"); return 0; }
在命令行输入如下命令:
$ arm-linux-gcc hello.c
这样就产生了一个名为a.out的可执行文件,遗憾的是我们不能在x86平台的linux命令行下执行该文件,为什么呢?arm-linux-gcc产生的不是可执行文件吗?可以尝试执行刚刚产生的可执行文件试试。
$ ./a.out
则提示bash: ./a.out: cannot execute binary file,如图-1所示:
图-1
在上图中可以看到,首先用arm-linux-gcc编译hello.c产生a.out可执行文件,然后执行该文件,则提示“bash: ./a.out: cannot execute binary file”。
原因在于arm-linux-gcc是用来为ARM体系结构的硬件平台开发软件的交叉编译器,其产生的可执行文件是要运行在基于 ARM 硬件平台上的Linux系统中,而不能运行在开发主机(X86)系统下的Linux系统中。
可以使用Linux的file命令来查看一下刚刚产生的a.out的文件结构。命令行输入命令:
$ file a.out
可以看到如图-2所示信息:
图-2
可以看到,刚刚产生的a.out是 LSB(小端格式)的ELF(执行与链接)格式的文件,该文件是能够运行在ARM Linux环境下的可执行程序。
2.汇编器 arm-linux-as。
编写do_sub.s汇编文件,其文件内容如下:
.text .global do_sub .global _start .code 32 _start: do_sub: mov r0, #0 mov r1, #9 mov r2, #2 sub r0, r1, r2 b . .end
汇编器用于将汇编源文件汇编成目标文件,示例用法如下:
$ arm-linux-as -o do_sub.o do_sub.s
汇编通过后将生成do_sub.o目标文件。
3.链接器arm-linux-ld。
arm-linux-ld用于链接目标文件和系统库中的函数代码(目标代码),示例用法如下:
$ arm-linux-ld -o do_sub do_sub.o
如果没有错误将产生do_sub文件,该文件是ELF格式文件,可以使用file命令查看其格式,如图-3所示:
$ file do_sub
图-3
可以看到do_sub是包含ARM指令集的32位ELF格式可执行文件(小端格式)。
4.文件格式转换 arm-linux-objcopy。
用于文件格式转换,将上步生成的do_sub文件转换成二进制文件的示例用法如下:
$arm-linux-objcopy -O binary do_sub do_sub.bin
do_sub.bin就是产生的纯二进制文件。对于裸机软件开发中通常需要将elf格式的可执行文件转换成纯二进制文件下载到TPAD中运行。
5.反汇编 arm-linux-objdump。
arm-linux-objdump用于将目标文件或elf格式可执行文件反汇编成汇编代码文件,示例用法如下:
$ arm-linux-objdump -S do_sub.o>do_sub.asm
这样就将do_sub.o文件反汇编成 do_sub.asm汇编文件了,反汇编后得到的do_sub.asm文件内容如图-4所示:
图-4
6.elf格式文件查看:arm-linux-readelf。
arm-linux-readelf可以用来查看elf格式的文件的信息,示例用法如下:
$ arm-linux-readelf -a do_sub
可以输出do_sub文件的文件头信息,以及段信息等等。
7.静态库管理 arm-linux-ar。
可以将产生的目标文件“.o”文件生成或添加到库中,也可以查看库中有的目标文件等。使用的示例代码如下:
$ arm-linux-ar do_sub.a do_sub.o
将do_sub.o生成do_sub.a库文件。
8.符号表生成指令 arm-linux-nm。
生成elf文件中的符号,使用示例如下:
$ arm-linux-nm do_sub>sym
重定向输出的sym文件内容如图-5所示:
图-5
从sym可以看到,程序中的标号的值。
9.去掉elf文件中不需要的信息和代码arm-linux-strip。
arm-linux-strip用于去掉文件中不使用的一些信息,如调试信息等,以减小目标文件的体积。从而节省存储空间或提高加载、执行效率,如图-6所示。
图-6
可以看到do_sub的大小是798,do_sub.o的大小是618,可以执行以下指令:
$ arm-linux-strip do_sub $ arm-linux-strip do_sub.o $ ls –l do_sub*
如图-7所示,可以看到do_sub大小变为320,do_sub.o大小变为376,经比较可以清楚的看到使用arm-linux-strip处理前后do_sub和do_sub.o文件大小的变化。
图-7
掌握使用C语言编程控制T-PAD上的LED1的闪烁(亮、灭)。
通过看T-PAD底板原理图,得到LED1的亮灭是由GPC1[3]控制的,GPC1[3]给出高电平LED1亮,GPC1[3]给出低电平LED1灭。
通过阅读s5pv210的手册可以知道要让GPC1[3]输出高电平或者低电平,需要先将其配置为输出口,并禁止其上、下拉电阻,然后通过将GPC1的数据寄存器的BIT3置1使GPC1[3]输出高电平,GPC1的数据寄存器的BIT3清零来使GPC1[3]输出低电平。
C语言中可以使用指针来访问GPC1的寄存器,实现控制T-PAD上的LED1闪烁,程序流程如下,如图-8所示:
图-8
实现此案例需要按照如下步骤进行。
步骤一:建立程序头文件。
1. 在当前用户(本例用户为tarena)的主目录下建立led目录,进入目录并建立led.h文件。命令序列如下。
$cd $mkdir led $cd led $vim led.h
2. 使用#define对用到的GPC1寄存器做如下定义。
#define GPC1CON (*((volatile unsigned int *)0xE0200080)) #define GPC1DAT (*((volatile unsigned int *)0xE0200084)) #define GPC1PUD (*((volatile unsigned int *)0xE0200088))
步骤二:建立程序源文件。
1. 在刚刚建立led目录下,建立led.c文件。命令如下。
$vim led.c
2. 编写寄存器赋值语句实现对灯的控制
使GPC1[3]作为输出口。
GPC1CON =(GPC1CON & 0xFFFF0FFF)|0x00001000;
禁止GPC1[3]的上下拉电阻功能。
GPC1PUD &= ~0xC0;
控制GPC1[3]输出高电平(LED1亮)。
GPC1DAT |= (1<<3);
控制GPC1[3]输出低电平(LED1灭)。
GPC1DAT &= ~8;
步骤三:编译源文件。
从源文件产生了一个名为led.o的目标文件
$arm-linux-gcc -march=armv5te -nostdlib-c -o led.o led.c
注:-march=armv5te 指定生成指令的架构(armv5te);-nostdlib 指定不使用标准库。
步骤四:链接文件。
arm-linux-ld用于链接目标文件生成一个ELF格式可执行程序
$arm-linux-ld -nostartfiles -nostdlib -Ttext=0x20008000 -e led_main -o led led.o
注:-nostartfiles不使用启动文件;-nostdlib不使用标准库;-Ttext指定代码段的起始位置 0x20008000;-e led_main用于指定程序的入口点。
可以使用file 命令查看文件的格式,命令如下:
$file led
图-9
步骤五:格式转换产生led.bin。
ELF格式的可执行程序需要在有操作系统的情况下才能执行,裸板,无法解析这些格式信息,通过arm-linux-objcopy命令将上步生成的 led文件纯指令拷贝出来。
$arm-linux-objcopy -O binary led led.bin
步骤六:复制文件到/tftboot目录下
代码如下所示:
$cp led.bin /tftboot
步骤七:将led.bin下载到TPAD运行。
1. 用和TPAD配套的串口线连接TPAD和PC机。
2. 启动SecureCRT(参考前例)。
3. 启动TPAD并进入到u-boot主菜单,如图-10所示:
图-10
在上图界面的U-boot提示行输入如下命令,如图-11所示:
tarena# tftp 0x20008000 led.bin
图-11
可以看到新的led.bin文件的大小为264个字节。
步骤八:程序在TPAD上执行
在u-boot提示符下输入:go 0x20008000命令执行程序,这时TPAD上的LED将不停闪烁。代码如下所示:
tarena# go 0x20008000
本案例的完整代码如下所示:
1. led.h主要做函数声明和寄存器定义,内容如下。
#ifndef __LED_H__ #define __LED_H__ #define GPC1CON (*((volatile unsigned int *)0xE0200080)) #define GPC1DAT (*((volatile unsigned int *)0xE0200084)) #define GPC1PUD (*((volatile unsigned int *)0xE0200088)) extern void led_init(void); extern void led_on(void); extern void led_off(void); #endif //__LED_H__
2. LED控制c源程序文件,led.c示例代码如下。
#include "led.h" //延时函数,LED灯状态保持,以便用户观察 static void delay(unsigned int n); voidled_main(void) { led_init(); while (1) { led_on(); delay(0x100000); led_off(); delay(0x100000); } } static void delay(unsigned int n) { unsignedinti; for (i = n; i != 0; i--); } voidled_init(void) { //1.配置GPC1_3管脚配置为输出功能 GPC1CON =(GPC1CON & 0xFFFF0FFF)|0x00001000; //2.禁止GPC1_3管脚的上下拉电阻 GPC1PUD &= ~0xC0; } voidled_on(void) { //控制GPC1_3管脚输出高电平 操作GPC1DAT bit[3]=1 GPC1DAT |= 8; } voidled_off(void) { //控制GPC1_3管脚输出低电平 操作GPC1DAT bit[3]=0 GPC1DAT &= ~8; }