本文中使用的目标平台AT91SAM9G20是Atmel公司使用ARM926EJ-S处理器内核开发的一块SoC嵌入式微处理器,主频达到400MHz,具有Atmel 先进的外设DMA 和分布式存储器架构,连同6层总线矩阵,可实现存储器、外设和外部接口之间的多重数据同时传送,而无需耗费CPU的时钟周期。与引脚兼容的200 MHz AT91SAM9260相比,AT91SAM9G20提供多达4倍的高速缓存和片上 SRAM 内存,并具有增强的外接 NAND 闪存错误校正功能,以及更大的以太网 FIFO,能够减少传输延迟。其外部总线接口(EBI)的时钟频率为133 MHz,用于片外存储器的高速数据传送。这些特性使得开发人员可以将WindowsCE和Linux等操作系统移植到基于这块微处理器的目标系统中。
操作系统也是嵌入式系统的重要组成部分。当今的嵌入式操作系统各种各样,有VxWorks、QNX、Palm OS,Windows CE、Linux, uClinux,ucos ii等,每种操作系统都有其与众不同之处,本文移植的嵌入式操作系统为Linux。广泛开放源代码的Linux应用已经被移植到嵌入式的运行环境中,可以任意剪裁和修改后将其移植入自己的硬件平台上,因此使用Linux开发嵌入式操作系统,可以加快嵌入式系统的开发速度,缩短产品进入市场的时间。
本文的工作主要包括Boot loader实现,Linux内核移植、文件系统的实现三个部分。
搭建交叉开发环境
在移植操作系统之前,首先要在一台装有Linux的PC机上搭建好开发环境,例如我们的宿主机为Ubuntu10.04,为获得所有操作权限,以root身份登录宿主机Linux系统。在 usr/local/目录下建立arm路径,下载交叉编译工具arm-linux-gcc-4.3.2.tgz,将之复制到文件夹/usr/local/arm/下,并将其解压;最后需要修改环境变量,设置默认交叉编译工具为arm-linux-gcc-4.3.2。利用Linux下编辑工具(如vim)打开/root/.bashrc文件,在文件末尾添加如下代码:
if
[ -d /usr/local/arm ] ;
then PATH=/usr/local/arm/4.3.2/bin:"${PATH}"
fi
到此交叉编译工具搭建完毕,为验证交叉编译工具是否搭建成功,可以在终端下输入命令 arm-linux-gcc -v,搭建成功后会在终端下显示arm-linux-gcc的版本。注意此处必须注销用户,重新登录系统后设置才会生效。
引导程序
系统上电之后,需要一段程序来进行初始化:关闭看门狗、改变系统时钟、初始化存储控制器、将更多的代码复制到内存中等,这段程序被称为Boot loader。简言之Boot loader就是在系统上电后开始执行,初始化硬件设备、准备好软件环境、最后调用操作系统内核。Boot loader的实现非常依赖与具体的硬件,在嵌入式系统中硬件配置千差万别,即使是相同的CPU,外设资源也不尽相同,因此需要根据特定的硬件进行移植。
Boot Loader包含两种不同的操作模式:启动加载模式和下载模式。上电后,Boot loader从板子上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程没有用户的介入,一般用于最终产品。下载模式下则在开发过程中使用,开发人员可以使用各种命令,通过串口或网络等通信手段从宿主机下载文件(比如内核映像,文件系统映像),将它们直接放入内存或是烧入flash类固态存储设备中。
为了在AT91SAM9G20上实现嵌入式操作系统运行,AT91SAM9G20采用了三.级引导方式,其Boot Loader程序由三部分组成,即RomBoot、Bootstrap和U-Boot,三.级引导程序的流程如图1所示。
第一级引导程序Romboot固化在AT9lSAM9G20内部,上电或复位后先运行这段引导代码,其作用是将存储于外部FLASH第二级引导程序Bootstrap加载到CPU内部的SRAM中执行。Bootstrap存储在外部FLASH的前4KB空间,其功能包括初始化时钟、SDRAM控制器以及DEBUG串口等硬件资源,并将第三 级引导程序U-Boot从FLASH加载到SDRAM执行。U-Boot将嵌入式Linux操作系统从FLASH引导和加载到SDRAM中,并将CPU的控制权交给Linux。
本次移植使用的U-boot的版本为1.3.4,限于篇幅,本文不做详细的介绍。
内核的裁剪编译
1.准备工作
从Linux官网下载linux内核源码linux-2.6.30.tar.bz2,并从Atmel官网下载at91sam9g20的补丁文件(2.6.30-at91.patch.gz 和2.6.30-at91-exp.3.tar.gz)。在宿主机Linux开发环境下,建立自己的工作目录,例如工作目录为home/work/,将上述三个文件拷贝至工作目录下,解压Linux2.6.30,并将2.6.30-at91.patch.gz和2.6.30-at91-exp.3.tar.gz拷贝至Linux2.6.30根目录下,同时解压2.6.30-at91-exp.3.tar.gz。完成此部分工作如下图2所示。
2.添加补丁文件
进入上述linux-2.6.30根目录,执行如下两条命令:
zcat 2.6.30-at91.patch.gz | patch -p1
for p in 2.6.30-at91-exp.3/*; do patch -p1 < $p ; done
执行成功之后,即将从ATMEL官网下载的针对at91sam9g20的补丁文件添加进linux-2.6.30内核中,包括驱动程序,内核配置文件等。
3.U-boot引导Linux内核启动
a、机器码的确定
通常,在u-boot和kernel中都会有一个机器码(即:MACH_TYPE),只有这两个机器码一致时才能引导内核。首先,确定U-boot中的MACH_TYPE,在U-boot的include/asm-arm/mach-types.h文件中针对不同的CPU定义了非常多的MACH_TYPE,可以找到下面这个定义:
#define MACH_TYPE_AT91SAM9G20EK_2MMC 2288
其次,确定kernel中的MACH_TYPE。在Linux2.6.30的arch/arm/tools/mach-types文件中也针对不同的CPU定义了非常多的MACH_TYPE,也可以找到下面这个定义
at91sam9g20ek_2mmc MACH_AT91SAM9G20EK_2MMC AT91SAM9G20EK_2MMC 2288
只有这两者定义一致,u-boot才能完成对内核的引导,否则就会出现mach的错误信息。
b、添加引导地址
位于内核平台arm目录kernel下的head.S文件是用汇编代码完成的,它是内核最先执行的一个文件。这一段汇编代码的主要作用,是检查cpu id,architecture number,初始化页表、cpu、bbs等操作,并跳到start_kernel函数。
找到ENTRY(stext),添加如下代码:
ENTRY(stext)
mov r0, #0
mov r1, #0x8f0
ldr r2, =0x20000100
其中r1存放是是architectureID(2288十六进制为0x8f0),r2存放 atags 指针。 Linux内核的启动顺序如图3所示。
4.配置和编译
首先要配置编译工具,打开Linux的根目录下的Makefile文件,找到下面的代码进行修改:
export KBUILD_BUILDHOST := $(SUBARCH)
ARCH ?= arm
CROSS_COMPILE ?= /usr/local/arm/4.3.2/bin/arm-linux-
制定平台为arm平台,交叉编译工具为arm-linux-gcc-4.3.2;保存之后进入linux-2.6.30根目录,执行以下命令:
#make distclean
#make clean
此步骤删除一些以前编译产生的一些中间文件、临时文件,以避免编译出错。最后执行命令
#make at91sam9g20ek_2mmc_defconfig
#make uImage
即根据at91sam9g20配置内核,并编译生成内核映像文件,其中make uImage是U-boot编译生成的工具,在uboot的/tools目录下寻找mkimage文件,将其复制到系统/usr/local/bin目录下,这样就可以运行make uImage命令。编译完成之后(大概需要十几分钟),便可以在arch/arm/boot/目录下发现uImage文件,就是我们需要的内核映像文件,下载至工控板便可以运行。
5.修改和添加驱动程序
以上步骤生成的内核映像文件是按照Atmel官网的配置进行裁剪修改的,由于实际硬件配置的不同,就要针对具体的硬件设备修改相应的驱动程序,在Linux内核中增加驱动程序需要完成以下3项工作:
将编写的源代码复制到Linux内核源代码的相应目录中。在目录的Kconfig文件中增加新源代码对应项目的编译配置选项。在目录的Makefile文件中增加对新源代码的编译条件。
以添加nandflash驱动程序为例进行说明。at91sam9g20工控板的存储芯片芯片采用三星的K9F2G08U0A实现,针对at91sam9g20编写此芯片驱动,例如驱动文件为9g20_nandflash.c,将此文件添加至/linux2.6.30/driver/mtd/nand目录下,并修改Kconfig和Makefile文件。其中Makefile文件添加
obj-$(CONFIG_MTD_NAND_ATMEL) += 9g20_nandflash.o
Kconfig文件中添加如下代码:
config MTD_NAND_ATMEL
tristate "Support for SAM9G20 NAND Flash"
depends on ARCH_AT91
help
完成之后在linux2.6.30根目录执行make menuconfig,选择nandflash驱动程序。如下所示。
Device Drivers --->
<*> Memory Technology Device (MTD) support --->
<*> NAND Device Support --->
<*> Support for SAM9G20 NAND Flash
其中配置内核文件,选中要配置的选项,此时按下“Y”键,将此选项编译进内核,按下“M”键,将此选项编译成内核模块,按下“N”键表示从内核中删除此选项。
如需要添加分区信息,则在board-sam9g20ek-2slot-mmc.c文件中添加NAND闪存的分区表:
static struct mtd_partition __initdata ek_nand_partition[] = {
{
.name = "BootLoader",
.offset = 0,
.size = 0x00200000,
},//0分区
{
.name = "Linux Kernel",
.offset = 0x00200000,
.size = 0x00200000,
},//2M
{
.name = "Cramfs",
.offset = 0x00400000,
.size = 0x00A00000,
},//10M
{
.name = "FS",
.offset = 0x00E00000,
.size = 0x0F200000,
},
};
保存后,重新编译内核,即可生成带有nandflash驱动的内核映像文件。
按照上述分区表进行分区,nandflash的资源分布如图4所示,我们可以看到引导程序所说明的Bootloader在闪存中的存储情况,Linux内核以及下文要阐述的文件系统在此工控板上的存储情况。
文件系统的实现
选择文件系统,要根据具体应用的需求,一般要考虑可靠性,健壮性和增强的需求。如果是像工控这样的不需经常更新控制程序的应用来说,选择CARAMFS这样的只读文件系统已经足够了,而且它还可以带来的另外一个好处就是CRAMFS的压缩率高达50%,可以大大节省我们的存储空间。但是如果是像涉及到数据采集这类需要保存数据的应用来说,只读的文件系统就很难满足应用系统的需求,我们可以选择 JFFS 或者 YAFFS 这样的可读写的文件系统。不过在实际应用中,需要考虑的因素还应该更多。根据不同文件系统的特点,结合本9g20工控板的应用场合,采用了cramfs+jffs2文件系统。
文件系统的制作步骤大致如下:
下载busybox进行编译,生成Linux系统最常用的命令,这里可以根据需求选择常用的系统工具。本文件系统采用busybox-1.14.2,使用交叉编译工具为arm-linux-gcc-4.3.2,这里需要注意的是编译busybox和编译linux内核的交叉编译工具最好使用同一版本的编译工具。
建立文件系统所需要的文件,如下所示:
mkdir /9g20_rootfs
cd /9g20_rootfs
mkdir bin dev etc home lib mnt proc sbin sys tmp usr var
同时将busybox生成的bin和sbin目录拷贝至9g20_rootfs目录下,并将编译busybox所需的动态连接库文件,拷贝至9g20_rootfs /lib目录下。
编写必须的配置文件下,如etc目录下的rcS、inittab、fstab等等文件,并注意文件的执行权限。
下载cramfs制作工具cramfs-1.1.tar,编译后将生成的可执行程序mkcramf拷贝至/usr/bin目录下,即可执行mkcramfs 9g20_rootfs 9g20.cramfs,生成的9g20.cramfs文件就是所需的文件系统。
添加jffs2分区,在/etc/init.d/rcS文件中添加如下命令:
#mount -t jffs2 /dev/mtdblock3 /home
即将mtdblock3以jffs2文件系统方式挂载为home目录,这样/home目录是可读可写,既解决了cramfs只读的限制,同时系统文件是只读,用户程序和数据可读可写,有利于保护系统文件,又减少了文件系统的体积,特别适用于嵌入式系统。
将生成的内核映像和文件系统映像下载至工控板后,可以看到移植完成后,系统正常启动,经测试移植后的系统正常稳定运行。
本文小结
本文系统地阐述了以AT91SAM9G20的ARM9处理器为硬件平台的嵌入式Linux系统的构建过程,并以nandflash驱动为例阐述了Linux下驱动程序的编写步骤,最后给出了文件系统的解决方案。构建嵌入式Linux系统是个复杂的课题,需要对Linux操作系统内核有一定的了解,特别是Linux的启动过程;驱动程序的编写既要熟悉硬件知识,又要熟悉Linux内核数据结构;而且系统的实时性、安全性、稳定性、精简化等方面都需要开发人员在设计中进一步考虑。经过系统测试,本文移植的Linux系统运行稳定,AT91SAM9G20的良好性能得以充分应用,为后续应用程序的开发提供了坚实的基础。
作者:作者:袁莎莎,蒋健,陈炜
参考文献
[1] Atmel Corporation.AT91SAM9G20 Preliminary,2009.
[2] [美]博韦,西斯特,陈莉君,张琼声,张宏伟译.深入理解LINUX内核第三版[M].北京:中国电力出版社,2007.09.
[3] 韦东山.嵌入式Linux应用开发完全手册[M].北京:人民邮电出版社,2008.08.
[4] 黄深喜,樊晓平,刘利方,杨胜跃.基于AT91SAM926X的嵌入式LINUX引导程序设计[J].微计算机应用,2009(30).