Chinaunix首页 | 论坛 | 博客
  • 博客访问: 23699
  • 博文数量: 25
  • 博客积分: 1000
  • 博客等级: 少尉
  • 技术积分: 220
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-12 09:09
文章分类
文章存档

2010年(25)

我的朋友
最近访客

分类: 嵌入式

2010-05-12 14:41:11

s3c2410完全开发1(转)
2008-03-08 14:56
一.简介

本书面向由传统51单片机转向ARM嵌入式开发的硬件工程师、由硬件转嵌
入式软件开发的工程师、没有嵌入式开发经验的软件工程师。分9个部分:

1、开发环境建立
2、S3C2410功能部件介绍与实验(含实验代码)
3、bootloader vivi详细注释
4、linux移植
5、linux驱动
6、yaffs文件系统详解
7、调试工具
8、GUI开发简介
9、UC/OS移植
通过学习第二部分,即可了解基于ARM CPU的嵌入式开发所需要的外围器
件及其接口。对应的实验代码实现了对这些接口的操作,这可以让硬件工程师形
成一个嵌入式硬件开发的概念。这部分也可以当作S3C2410的数据手册来使用。

一个完整的嵌入式linux系统包含4部分内容:bootloader、parameters、kernel、
root file system。3、4、5、6部分详细介绍了这4部分的内容,这是linux底层软
件开发人员应该掌握的。通过学习这些章节,您可以详细了解到如何在一个裸板
上裁减、移植linux,如何构造自己的根文件系统,如何编写适合客户需求的驱
动程序——驱动程序这章将结合几个经典的驱动程序进行讲解。您还可以了解到
在用在nand flash上的非常流行的yaffs文件系统是如何工作的,本书将结合yaffs
代码详细介绍yaffs文件系统。

第7部分介绍了嵌入式linux开发中使用gdb进行调试的详细过程。






此文档目前完成了1、2、3部分,后面部分将陆续完成。希望能对各位在嵌
入式开发方面献上棉力。



欢迎来信指出文中的不足与错误,欢迎来信探讨技术问题。



Email :thisway.diy@163.com

MSN :thisway.diy@163.com

QQ :17653039



二.建立开发环境

(1)编译器arm-linux-gcc-3.4.1

下载地址:

ftp://ftp.handhelds.org/projects/toolchain/arm-linux-gcc-3.4.1.tar.bz2

执行如下命令安装:

bunzip2 arm-linux-gcc-3.4.1.tar.bz2

tar xvf arm-linux-gcc-3.4.1.tar -C /

生成的编译工具在目录/usr/local/arm/3.4.1/bin下,修改/etc/profile,
增加如下一行。这可以让我们直接运行arm-linux-gcc,而不必将其绝对路径都
写出来,不过这得重新启动后才生效:

pathmunge /usr/local/arm/3.4.1/bin

(2)Jflash-s3c2410:S3C2410芯片的JTAG工具

我们的第一个程序就是通过它下载到开发板上的nor flash或者nand flash
上去的。把它放到/usr/local/bin目录下。

下载地址:e

ftp://ftp.mizi.com/pub/linuette/SDK/1.5/target/box/Jflash/Jflash-
s3c2410



注意:步骤3您现在不必理会,可以等进行到“调试”部分时再回过头来看。

(3)安装gdb调试工具

下载地址:





执行如下命令安装:

a.安装在主机上运行的arm-linux-gdb工具:

tar xvzf gdb-6.3.tar.gz

cd gdb6.3

./configure --target=arm-linux

make

make install

此时,在/usr/local/bin中生成arm-linux-gdb等工具

b.继续上面的步骤,安装gdbserver。需要将此工具下载到开发板上运
行,这在后面会详细描述:

cd gdbserver

export CC=/usr/local/arm/3.4.1/bin/arm-linux-gcc

./configure arm-linux

make

此时在当前目录中生成了gdbserver工具,当我们讲到如何调试时,
会把这个文件下载到开发板上去。








三.S3C2410基础实验

本章将逐一介绍S3C2410各功能模块,并结合简单的程序进行上机实验。您
不必将本章各节都看完,完全可以看了一、两节,得到一个大概的印象之后,就
开始下一章。本章可以当作手册来用。

注意:了解S3C2410各部件最好的参考资料是它的数据手册。本文不打算翻
译该手册,在进行必要的讲解后,进行实际实验——这才是本文的重点。

(1)实验一:LED_ON

led_on.s只有7条指令,它只是简单地点亮发光二极管LED1。本实验的目
的是让您对开发流程有个基本概念。



实验步骤:

a.把PC并口和开发板JTAG接口连起来、确保插上“BOOT SEL”跳线、上
电(呵呵,废话,如果以后实验步骤中未特别指出,则本步骤省略)


b.进入LED_ON目录后,执行如下命令生成可执行文件led_on:

make

c.执行如下命令将led_on写入nand flash:

i. Jflash-s3c2410 led_on /t=5

ii.当出现如下提示时,输入0并回车:

iii.当出现如下提示时,输入0并回车:



iv.当再次出现与步骤ii相同的提示时,输入2并回车

d.按开发板上reset键后可看见LED1被点亮了



实验步骤总地来说分3类:编写源程序、编译/连接程序、烧写代码。

先看看源程序led_on.s:

1 .text

2 .global _start

3 _start:

4 LDR R0,=0x56000010 @R0设为GPBCON寄存器。此寄存器

@用于选择端口B各引脚的功能:

@是输出、是输入、还是其他

5 MOV R1,#0x00004000

6 STR R1,[R0] @设置GPB7为输出口



7 LDR R0,=0x56000014 @R0设为GPBDAT寄存器。此寄存器

@用于读/写端口B各引脚的数据


8 MOV R1,#0x00000000 @此值改为0x00000080,

@可让LED1熄灭

9 STR R1,[R0] @GPB7输出0,LED1点亮



10 MAIN_LOOP:

11 B MAIN_LOOP

对于程序中用到的寄存器GPBCON、GPBDAT,我稍作描述,具体寄存器的操作
可看实验三:I/O PORTS。GPBCON用于选择PORT B的11根引脚的功能:输出、输
入还是其他特殊功能。每根引脚用2位来设置:00表示输入、01表示输出、10表
示特殊功能、11保留不用。LED1-3的引脚是GPB7-GPB10,使用GPBCON中位[12:13]、
[13:14]、[15:16]、[17:18]来进行功能设置。GPBDAT用来读/写引脚:GPB0对应
位0、GPB1对应位1,诸如此类。当引脚设为输出时,写入0或1可使相应引脚输出
低电平或高电平。



程序很简单,第4、5、6行3条指令用于将LED1对应的引脚设成输出引脚;
第7、8、9行3条指令让这条引脚输出0;第11行指令是个死循环。

实验步骤b中,指令“make”的作用就是编译、连接led_on.s源程序。Makefile
的内容如下:

1 led_on:led_on.s

2 arm-linux-gcc -g -c -o led_on.o led_on.s

3 arm-linux-ld -Ttext 0x0000000 -g led_on.o -o led_on_tmp.o

4 arm-linux-objcopy -O binary -S led_on_tmp.o led_on

5 clean:

6 rm -f led_on

7 rm -f led_on.o

8 rm -f led_on_tmp.o

make指令比较第1行中文件led_on和文件led_on.s的时间,如果led_on
的时间比led_on.s的时间旧(led_on未生成时,此条件默认成立),则执行第2、
3、4行的指令更新led_on。您也可以不用指令make,而直接一条一条地执行2、
3、4行的指令——但是这样多累啊。第2行的指令是预编译,第3行是连接,
第4行是把ELF格式的可执行文件led_on_tmp.o转换成二进制格式文件led_on。
执行“make clean”时强制执行6、7、8行的删除命令。

注意:Makefile文件中相应的命令行前一定有一个制表符(TAB)





汇编语言可读性太差,现在请开始实验二,我用C语言来实现了同样的功能,
而以后的实验,我也尽可能用C语言实现。

(2)实验二:LED_ON_C

C语言程序执行的第一条指令,并不在main函数中。当我们生成一个C程序
的可执行文件时,编译器总是在我们的代码前加一段固定的代码——crt0.o,它
是编译器自带的一个文件。此段代码设置C程序的堆栈等,然后调用main函数。
很可惜,在我们的裸板上,这段代码无法执行,所以我们得自己写一个。这段代
码很简单,只有3条指令。

crt0.s代码:


1 .text

2 .global _start

3 _start:

4 ldr sp, =1024*4 @设置堆栈,注意:不能大于4k

@nand flash中的代码在复位后会

@移到内部ram中,它只有4k

5 bl main @调用C程序中的main函数

6 halt_loop:

7 b halt_loop



现在,我们可以很容易写出控制LED的程序了,led_on_c.c代码如下:



1 #define GPBCON (*(volatile unsigned long *)0x56000010)

2 #define GPBDAT (*(volatile unsigned long *)0x56000014)

3 int main()

4 {

5 GPBCON = 0x00004000; //设置GPB7为输出口

6 GPBDAT = 0x00000000; //令GPB7输出0

7 return 0;

8 }



最后,我们来看看Makefile:

1 led_on_c : crt0.s led_on_c.c

2 arm-linux-gcc -g -c -o crt0.o crt0.s

3 arm-linux-gcc -g -c -o led_on_c.o led_on_c.c

4 arm-linux-ld -Ttext 0x0000000 -g crt0.o led_on_c.o -o
led_on_c_tmp.o

5 arm-linux-objcopy -O binary -S led_on_c_tmp.o led_on_c

6 clean:

7 rm -f led_on_c

8 rm -f led_on_c.o

9 rm -f led_on_c_tmp.o

10 rm -f crt0.o



第2、3行分别对源程序crt0.s、led_on_c.c进行预编译,第4行将预编译
得到的结果连接起来,第5行把连接得到的ELF格式可执行文件led_on_c_tmp.o
转换成二进制格式文件led_on_c。

好了,可以开始上机实验了:

实验步骤:

a.进入LED_ON_C目录后,执行如下命令生成可执行文件led_on_c:

make

b.执行如下命令将led_on_c写入nand flash:

i. Jflash-s3c2410 led_on_c /t=5

ii.当出现如下提示时,输入0并回车:


K9S1208 NAND Flash JTAG Programmer Ver 0.0

0:K9S1208 Program 1:K9S1208 Pr BlkPage 2: Exit

Select the function to test :

iii.当出现如下提示时,输入0并回车:

Input target block number:

iv.当出现与步骤ii相同的提示时,输入2并回车

c.按开发板上reset键后可看见LED1被点亮了



目录LEDS中的程序是使用4个LED从0到15轮流计数,您可以试试:

a.进入目录后make
b.Jflash-s3c2410 leds /t=5
c.reset运行





另外,如果您有兴趣,可以使用如下命令看看二进制可执行文件的反汇编码:

arm-linux-objdump -D -b binary -m arm xxxxx(二进制可执行文件名)



注意:本文的所有程序均在SOURCE目录中,各程序所在目录均为大写,其
可执行文件名为相应目录名的小写,比如LEDS目录下的可执行文件为leds。以
后不再赘述如何烧写程序:直接运行Jflash-s3c2410即可看到提示。





(3)实验三:I/O PORTS

请打开S3C2410数据手册第9章IO/ PORTS,I/O PORTS含GPA、GPB、..、
GPH八个端口。它们的寄存器是相似的:GPxCON用于选择引脚功能,GPxDAT用
于读/写引脚数据,GPxUP用于确定是否使用内部上拉电阻(x为A、B、..、H,
没有GPAUP寄存器)。

1、PORT A与PORT B-H在功能选择方面有所不同,GPACON中每一位对应一根引
脚(共23根引脚)。当某位设为0时,相应引脚为输出引脚,此时我们可以在
GPADAT中相应位写入0或1让此引脚输出低电平或高电平;当某位设为1时,
相应引脚为地址线或用于地址控制,此时GPADAT无用。一般而言GPACON通
常设为全1,以便访问外部存储器件。PORT A我们暂时不必理会。
2、PORT B-H在寄存器操作方面完全相同。GPxCON中每两位控制一根引脚:00
表示输入、01表示输出、10表示特殊功能、11保留不用。GPxDAT用于读/
写引脚:当引脚设为输入时,读此寄存器可知相应引脚的状态是高是低;当
引脚设为输出时,写此寄存器相应位可令此引脚输出低电平或高电平。GpxUP:
某位为0时,相应引脚无内部上拉;为1时,相应引脚使用内部上拉。


其他寄存器的操作在后续相关章节使用到时再描述;PORT A-H中引脚的特殊
功能比如串口引脚、中断引脚等,也在做相关实验时再描述。



目录KEY_LED中的程序功能为:当K1-K4中某个按键按下时,LED1-LED4中
相应LED点亮。

key_led.c代码:







1 #define GPBCON (*(volatile unsigned long *)0x56000010)

2 #define GPBDAT (*(volatile unsigned long *)0x56000014)



3 #define GPFCON (*(volatile unsigned long *)0x56000050)

4 #define GPFDAT (*(volatile unsigned long *)0x56000054)



/*

LED1-4对应GPB7-10

*/

5 #define GPB7_out (1<<(7*2))

6 #define GPB8_out (1<<(8*2))

7 #define GPB9_out (1<<(9*2))

8 #define GPB10_out (1<<(10*2))



/*

K1-K3对应GPF1-3

K4对应GPF7

*/

9 #define GPF1_in ~(3<<(1*2))

10 #define GPF2_in ~(3<<(2*2))

11 #define GPF3_in ~(3<<(3*2))

12 #define GPF7_in ~(3<<(7*2))





13 int main()

{

//LED1-LED4对应的4根引脚设为输出

14 GPBCON =GPB7_out | GPB8_out | GPB9_out | GPB10_out ;



//K1-K4对应的4根引脚设为输入

15 GPFCON &= GPF1_in & GPF2_in & GPF3_in & GPF7_in ;



16 while(1){

//若Kn为0(表示按下),则令LEDn为0(表示点亮)

17 GPBDAT = ((GPFDAT & 0x0e)<<6) | ((GPFDAT & 0x80)<<3);
}



18 return 0;

}



实验步骤:


a.进入目录KEY_LED,运行make命令生成key_led
b.烧写key_led















(4)实验四:arm-linux-ld

在开始后续实验之前,我们得了解一下arm-linux-ld连接命令的使用。在
上述实验中,我们一直使用类似如下的命令进行连接:

arm-linux-ld -Ttext 0x00000000 crt0.o led_on_c.o -o led_on_c_tmp.o

我们看看它是什么意思:-o选项设置输出文件的名字为led_on_c_tmp.o;
“--Ttext 0x00000000”设置代码段的起始地址为0x00000000;这条指令的作用就
是将crt0.o和led_on_c.o连接成led_on_c_mp.o可执行文件,此可执行文件的代
码段起始地址为0x00000000。

我们感兴趣的就是“—Ttext”选项!进入LINK目录,link.s代码如下:

1 .text

2 .global _start

3 _start:

4 b step1

5 step1:

6 ldr pc, =step2

7 step2:

8 b step2





Makefile如下:

1 link:link.s

2 arm-linux-gcc -c -o link.o link.s

3 arm-linux-ld -Ttext 0x00000000 link.o -o link_tmp.o

4 # arm-linux-ld -Ttext 0x30000000 link.o -o link_tmp.o

5 arm-linux-objcopy -O binary -S link_tmp.o link

6 arm-linux-objdump -D -b binary -m arm link >ttt.s

7 # arm-linux-objdump -D -b binary -m arm link >ttt2.s

8 clean:

9 rm -f link

10 rm -f link.o

11 rm -f link_tmp.o



实验步骤:

1.进入目录LINK,运行make生成arm-linux-ld选项为“-Ttext 0x00000000”
的反汇编码ttt.s

2.make clean

3.修改Makefile:将第4、7行的“#”去掉,在第3、6行前加上“#”

4.运行make生成arm-linux-ld选项为“-Ttext 0x30000000”的反汇编码ttt2.s



link.s程序中用到两种跳转方法:b跳转指令、直接向pc寄存器赋值。我们先
把在不同“—Ttext”选项下,生成的可执行文件的反汇编码列出来,再详细分析这
两种不同指令带来的差异。

ttt.s: ttt2.s

0: eaffffff b 0x4 0: eaffffff b 0x4


4: e59ff000 ldr pc, [pc, #0] ; 0xc 4: e59ff000 ldr pc, [pc, #0] ; 0xc

8: eafffffe b 0x8 8: eafffffe b 0x8

c: 00000008 andeq r0, r0, r8 c: 30000008 tsteq r0, #8 ; 0x8



先看看b跳转指令:它是个相对跳转指令,其机器码格式如下:



[31:28]位是条件码;[27:24]位为“1010”时,表示B跳转指令,为“1011”时,表示BL
跳转指令;[23:0]表示偏移地址。使用B或BL跳转时,下一条指令的地址是这样计算的:将指
令中24位带符号的补码立即数扩展为32(扩展其符号位);将此32位数左移两位;将得到的值
加到pc寄存器中,即得到跳转的目标地址。我们看看第一条指令“b step1”的机器码eaffffff:

1. 24位带符号的补码为0xffffff,将它扩展为32得到:0xffffffff
2.将此32位数左移两位得到:0xfffffffc,其值就是-4
3.pc的值是当前指令的下两条指令的地址,加上步骤2得到的-4,这恰好是第
二条指令step1的地址
各位不要被被反汇编代码中的“b 0x4”给迷惑了,它可不是说跳到绝对地址0x4
处执行,绝对地址得像上述3个步骤那样计算。您可以看到b跳转指令是依赖于当
前pc寄存器的值的,这个特性使得使用b指令的程序不依赖于代码存储的位置——
即不管我们连接命令中“--Ttext”为何,都可正确运行。



再看看第二条指令ldr pc, =step2:从反汇编码“ldr pc, [pc, #0]”可以看出,
这条指令从内存中某个位置读出数据,并赋给pc寄存器。这个位置的地址是当前
pc寄存器的值加上偏移值0,其中存放的值依赖于连接命令中的“--Ttext”选项。
执行这条指令后,对于ttt.s,pc=0x00000008;对于ttt2.s, pc=0x30000008。于
是执行第三条指令“b step2”时,它的绝对地址就不同了:对于ttt.s,绝对地址
为0x00000008;对于ttt.s,绝对地址为0x30000008。



ttt2.s上电后存放的位置也是0,但是它连接的地址是0x30000000。我们以后
会经常用到“存储地址和连接地址不同”(术语上称为加载时域和运行时域)的特性:
大多机器上电时是从地址0开始运行的,但是从地址0运行程序在性能方面总有很
多限制,所以一般在开始的时候,使用与位置无关的指令将程序本身复制到它的连
接地址处,然后使用向pc寄存器赋值的方法跳到连接地址开始的内存上去执行剩下
的代码。在实验5、6中,我们将会作进一步介绍。

arm-linux-ld命令中选项“-Ttext”也可以使用选项“-Tfilexxx”来代替,在
文件filexxx中,我们可以写出更复杂的参数来使用arm-linux-ld命令——在实验
6中,我们就是使用这种方法来指定连接参数的。
阅读(442) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~