《电子技术应用》
您所在的位置:首页 > 嵌入式技术 > 设计应用 > 基于ARM和Linux的嵌入式平台的构建
基于ARM和Linux的嵌入式平台的构建
来源:微型机与应用2011年第12期
曾福振,闵联营
(武汉理工大学 计算机科学与技术学院,湖北 武汉430063)
摘要: 首先介绍了嵌入式系统的概念,及相关硬件平台和软件版本。然后,主要介绍了嵌入式Linux的引导程序U-Boot的移植,以及开源、免费操作系统Linux2.6.32.2的移植。最后,构建了基于Nand Flash存储器的Yaffs2文件系统,利用BusyBox创建根文件系统。基于ARM和嵌入式Linux的嵌入式系统平台搭建基本完成,可以在此平台上添加更多驱动,以便更好地开发应用程序。
Abstract:
Key words :

摘  要: 首先介绍了嵌入式系统的概念,及相关硬件平台和软件版本。然后,主要介绍了嵌入式Linux的引导程序U-Boot移植,以及开源、免费操作系统Linux2.6.32.2的移植。最后,构建了基于Nand Flash存储器的Yaffs2文件系统,利用BusyBox创建根文件系统。基于ARM和嵌入式Linux的嵌入式系统平台搭建基本完成,可以在此平台上添加更多驱动,以便更好地开发应用程序。
关键词: 嵌入式;Linux;U-Boot;移植

 进入后PC时代以来,伴随着设计和制造技术的发展,集成电路从当初的晶体管集成发展到现在的IP集成,即SoC(System on Chip)设计技术。促使嵌入式系统渗透到了当今社会中的各个行业,并且发挥越来越重要的作用。嵌入式系统一般可定义为以应用为中心、以计算机技术为基础、软硬件可裁剪、适用于应用系统且对功能、成本、体积、功耗有严格要求的专用计算机系统,它的主要特点是嵌入、应用[1]。
    随着各种嵌入式设备功能越来越强大,在设备中使用嵌入式操作系统也成为必然。Linux操作系统具有开放源代码、易于移植、资源丰富、免费等特点,在嵌入式领域的地位越来越重要。嵌入式Linux和PC上的Linux是同一套内核代码,只是裁剪的程度不一样,所以,很多在PC上开发的软件,经过交叉编译后可以直接在嵌入式设备上运行。本文主要涉及到Bootloader移植和Linux-2.6.32.2内核的移植、根文件系统移植、在S3C2440平台上构建完整的嵌入式开发平台三个方面。
1 交叉开发环境的建立
    在进行嵌入式软件开发之前,必须要在PC上建立ARM的交叉编译环境。交叉编译就是在PC平台上生成可以在ARM平台上运行的代码。其中主要包括ARM的交叉编译器arm-elf-gcc和交叉连接器arm-elf-ld。本文采用的交叉编译器的版本是gcc-3.4.5-glibc-2.3.6[2]。交叉编译流程如图1所示。


2 BootLoader引导加载程序
 BootLoader是一段在系统上电时开始执行的程序,用以初始化硬件设备,准备好软件环境,设置好启动参数,最后引导操作系统,与PC上的BIOS程序相似。当前开放源码的Linux引导程序主要有x86架构的LILO、GRUB,对于ARM架构的主要有Vivi和U-Boot。本文使用U-Boot作为引导程序。U-Boot(Universal Boot Loader),即通用的BootLoader,遵循GPL条款开放源代码。U-Boot相对于Vivi功能更加强大,也更方便后续程序的调试。
    BootLoader的启动一般分为两个阶段,第一阶段的代码主要是用汇编语言编写,主要的功能是完成硬件设备的初始化,为加载第二阶段的代码准备RAM空间,设置好堆栈;第二阶段主要用C语言编写,检测内存映射,将内核映像和根文件系统从Nand Flash读到RAM中,为内核启动设置参数,引导内核。
    U-Boot的源代码可以从ftp://ftp.denx.de/pub/u-boot/进行下载,本文使用的U-Boot版本是U-Boot2009.08。
    移植U-Boot的关键步骤如下:
    (1)首先,将include/configs目录下的smdk2410.h复制并改名为mini2440.h,根据U-Boot的说明可以知道,如果要使用开发板board/<board_name>,则先执行“make<board_name>”_config命令进行配置,然后执行“make all”,生成可执行文件。所以,修改U-Boot顶层的Makefile文件,添加下面一行mini2440_config : unconfig @$(MKCONFIG) $(@:_config=) arm arm920t mini2440 frank s3c24x0。这里有几个重要的参数,arm指CPU的架构,arm920t指CPU的类型,s3c24x0指CPU的型号。这样就可以使用make mini2440_config 这条命令进行配置。
    (2)本文使用的U-Boot是从Nand Flash启动的, CPU可以直接访问Nand Flash中前4 KB代码,利用这4 KB代码把U-Boot中绝大部分代码拷贝到内存中[3]。其中下面的代码就是调用C语言中的Nand Flash的读写函数,该函数主要把Nand Flash中4 KB以后的代码复制到RAM中。在编写nand_read_ll的函数时,注意参考Nand Flash的数据手册,对大页和小页的Nand Flash,其读写的命令和时序是不同的。
    @copy U-Boot to RAM
    ldr r0,=TEXT_BASE
    mov r1,#0x0
    mov r2,#0x60000
    bl nand_read_ll
    tst r0,#0x0
    beq ok_nand_read
    由于在后面加载Linux内核和根文件系统时,使用的是tftp方式,所以必须添加DM9000EP网卡的驱动。在mini2440.h文件中,其主要的配置如下:
    #define CONFIG_DRIVER_DM9000 1
    #define CONFIG_NET_MULTI 1
    #define CONFIG_DM9000_NO_SROM 1
    #define CONFIG_DM9000_BASE 0x20000300
    #define DM9000_DATA (CONFIG_DM9000_BASE +4)
其中,CONFIG_DM9000_BASE宏是最重要的,因为它定义的是网卡的地址,不同的网卡有不同的地址,DM9000EP访问的基址为0x20000000,之所以再偏移0x300是由它的特性决定的。
    (3)要正确引导Linux内核,还需要配置下面几个重要的宏定义,这几个宏定义不同,意味着引导Linux内核的方式也不同。
    #define CONFIG_BOOTARGS"noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200 mem=64M"
其中,root=/dev/mtdblock3是由Linux中的Nand Flash分区所决定的,意味着Nand Flash的第4个分区为根文件系统。
    #define CONFIG_BOOTCOMMAND"nand read 0x32000000 0x60000 0x560000;bootm 0x32000000"
    这个宏定义是将Nand Flash中0x60000-0x560000(和kernel分区一致)的内容读到内存0x32000000中,然后用bootm命令来执行。
    要正常地引导Linux内核,必须要具备如下几个条件[4]:
    (1)CPU寄存器
    R0=0;
    R1=机器类型ID;对于ARM结构的CPU,其机器类型ID在linux/arch/arm/tools/mach-types;
    R2=启动参数标记列表在RAM中起始基地址。
    (2)CPU工作模式
    必须禁止中断(IRQs和FIQs);
    CPU必须为SVC模式。
    (3)Cach和MMU的设置
    MMU必须关闭;
    指令Cach可以打开也可以关闭;
    数据Cach必须关闭。
3 Linux2.6.32.2内核的移植
3.1 内核的获取

    Linux内核的更新很快,可以从http://www.kernel.org/pub/linux/kernel/得到最新的Linux内核版本,本文使用的Linux内核版本是Linux-2.6.32.2, 交叉编译工具使用符合EABI标准的arm-linux-gcc-4.3.2。
3.2 内核的移植
    可以在内核的根目录下,运行make menuconfig命令,对内核进行适当的裁剪,以适应硬件平台。
    (1)修改Makefile文件
    欲设置Linux的默认平台为ARM平台,需进入Linux-2.6.32文件夹中,修改此目录下的Makefile文件。
    export KBUILD_BUILDHOST := $(SUBARCH)
ARCH ?=arm//使用的目标平台
CROSS_COMPILE ?=arm-linux-//使用的交叉编译器,
这里使用系统默认的编译器
    (2)关于机器码
    在启动内核时,根据BootLoader传入的机器码(MACH_TYPE)来决定应启动哪种目标平台[6],本开发平台的机器码为1999。机器码存放在文件opt/kernel/linux-2.6.32.2/arch/arm/tools/mach-types中。
    mini2440   MACH_MINI2440   MINI2440   1999//机器码
    如果机器码不匹配,引导内核不成功,则会出现如下的错误提示:
    Uncompressing
Linux…………………………………………………………………
…………done, booting the kernel.
    (3)修改时钟源
    将/kernel/linux-2.6.32.2/arch/arm/mach-s3c2440/目录下的mach-smdk2440.c文件改名为mach-mini2440.c。因为mini2440和mach-smdk2440.c极其相似,以该文件为基础进行修改,在mach-mini2440.c文件中将static void__init smdk2440_map_io(void)函数中的晶振频率修改为mini2440 开发板上实际使用的12000000。
 (4)为内核打上yaffs2补丁
    ①Yaffs2文件系统是专门针对嵌入式设备,特别是使用Nand Flash作为存储器的嵌入式设备而创建的一种文件系统,使用yaffs2就可以支持大页的Nand Flash。进入 yaffs2 源代码目录执行如下命令:
    #./patch-ker.sh c /opt/FriendlyARM/mini2440/linux-2.6.32.2
    ②配置内核以支持Yaffs2文件系统
    在Linux内核源代码根目录运行make xconfig,在“File Systems”选项中,找到“Miscellaneous filesystems”菜单项,找到“YAFFS2 file system support”并选中它,这样就在内核中添加了yaffs2 文件系统的支持,保存并退出。然后在命令行中,执行make zImage。
    (5)修改Nand Flash分区信息
    ①在mach-mini2440.c文件中添加Nand Flash的分区信息,下面的代码将Nand Flash分成了4个分区,第1分区也是BootLoader所在的分区,对应dev/mtdblock0;第2个分区是U-Boot的参数分区,对应dev/mtdblock1;第3个分区是内核分区,对应dev/mtdblock2;第4个分区为根文件系统分区对应dev/mtdblock3。分区结构图如表1所示。

    其部分实现代码如下:
    static struct mtd_partition mini2440_default_nand_part[] ={
    [0] = {
        .name="U-boot",
    .offset= 0,
        .size= 0x00040000,
    }
其中 name是分区的名字,offset是偏移的开始地址,size是分区的大小,其余部分的分区与此类似。
    ②下面代码是添加Nand Flash的设置表,因为板子上只有一片Nand Flash,因此也就只有一个设置表。
    static struct s3c2410_nand_set mini2440_nand_sets[] = {
    [0] = {
        .name= "NAND",
        .nr_chips= 1,
        .nr_partitions=
ARRAY_SIZE(mini2440_default_nand_part),
        .partitions= mini2440_default_nand_part,
    }
    }
    ③上面的设置完成后,还需要将Nand Flash 设备注册到系统中。下面这段代码就是将Nand Flash设备添加到开发板的设备列表结构。
    static struct platform_device *mini2440_devices[] __initdata = {
    &s3c_device_nand,
    }
    ④在mini2440_machine_init函数中添加平台的数据信息。
    static void __init mini2440_machine_init(void){
    s3c_device_nand.dev.platform_data=&mini2440_nand_info;
    }
    现在可以进入kernel/linux-2.6.32.2/arch/arm/boot目录,然后执行下面的命令,就会在该目录下生成uImage.img格式的、U-Boot可以引导的内核镜象。
    Mkimage –n ‘linux-2.6.32.2’ –A arm –O linux –T kernel –C none –a 0x30008000 –e 0x30008000 –d zImage uImage.img
    至此,可以把生成的uImage.img格式的镜像文件复制到tftp目录下,使用tftp进行下载。
3.3 文件系统
    所谓根文件系统,就是创建各个目录,例如在/bin、/sbin/目录下存放各种可执行的程序,在/etc目录下存放配置文件,在/lib目录下存放库文件。
    可以利用Busybox工具创建根文件系统,Bosybox 是一个遵循 GPL v2协议的开源项目,它在编写过程中对文件大小进行优化,并考虑了系统资源有限(例如内存)的情况,使用 Busybox可以自动生成根文件系统所需的bin、sbin、usr 目录和 linuxrc 文件,可以使用make menuconfig对Busybox的选项进行配置。
    (1)进入opt/kernel,创建一个shell脚本用于构建根文件系统的各个目录,并且为其增加执行权限;
    (2)Linux中的init进程会根据etc/inittab文件创建其他子进程,下面代码是inittab文件中的内容,说明了系统启动后首先执行的脚本文件是rcS,虚拟的终端是串口0,当按下ctr+alt+del时重启系统,inittab文件的作用就是控制系统启动时和启动后一些程序的运行。
    #etc/inittab
    ::sysinit:/etc/init.d/rcS
    s3c2410_serial0::askfirst:-/bin/sh
    ::ctrlaltdel:/sbin/reboot
    ::shutdown:/bin/umount -a-r
    (3)创建etc/init.d/rcS文件,这是一个脚本文件,可以在里面添加要自动执行的一些命令。
    #!/bin/sh
    PATH=/sbin:/bin:/usr/sbin:/usr/bin
    runlevel=S//运行的级别
    prevlevel=N
    umask 022//文件夹的掩码
    mount -a//挂载/etc/fstab/文件指定的所有的文件系统
    mdev-s
    /bin/hostname -F /etc/sysconfig/HOSTNAME//主机的名字
    使用yaffs源码提供的工具制作文件系统的映像文件。由于128 MB的Nand Flash是大页结构,所以需要使用相应的大页制作工具;使用命令mkyaffs2image rootfs rootfs.img生成根文件系统映像文件。
    本文通过对U-Boot移植和Linux内核移植的讨论,给出了移植U-Boot和Linux到大多数开发板的关键部分。由于移植的复杂性,不可能包括全部步骤,但通过本文的阐述可以了解移植的基本流程和关键点,为移植不同版本到其他硬件平台提供了参考,也为应用程序的开发搭建了一个比较完整的嵌入式平台。
参考文献
[1] 韦东山.嵌入式Linux应用开发完成手册[M].北京:人民邮电出版社,2008.
[2] 孙琼.嵌入式Linux应用程序开发详解[M].北京:人民邮电出版社,2006.
[3] Samsung Electronics.S3C2440A 32-bit RISC microprocessor user′s manual[S].2004.
[4] RUSSELL K.ARM Linux kernel Boot requirements[EB/OL].  [2002-03-18].http://www.arm.linux.org.uk/developer/booting.php.
[5] 陈莉君.深入理解Linux内核[M].北京:中国电力出版社,2007.
[6] JONATHAN C,ALESSANDRO R,GREG KROAH H.Linux 设备驱动程序[M].北京:中国电力出版社,2006.

此内容为AET网站原创,未经授权禁止转载。