《电子技术应用》
您所在的位置:首页 > 嵌入式技术 > 设计应用 > 基于ARM和Linux的嵌入式平台的构建
基于ARM和Linux的嵌入式平台的构建
EEworld
摘要: 本文通过对U-Boot 移植和Linux 内核移植的讨论,给出了移植U-Boot 和Linux 到大多数开发板的关键部分。由于移植的复杂性, 不可能包括全部步骤, 但通过本文的阐述可以了解移植的基本流程和关键点, 为移植不同版本到其他硬件平台提供了参考, 也为应用程序的开发搭建了一个比较完整的嵌入式平台。
关键词: ARM Linux
Abstract:
Key words :

关键字:ARM Linux

    进入后PC 时代以来, 伴随着设计和制造技术的发展, 集成电路从当初的晶体管集成发展到现在的IP 集成, 即SoC(System on Chip ) 设计技术。促使嵌入式系统渗透到了当今社会中的各个行业, 并且发挥越来越重要的作用。嵌入式系统一般可定义为以应用为中心、以计算机技术为基础、软硬件可裁剪、适用于应用系统且对功能、成本、体积、功耗有严格要求的专用计算机系统,它的主要特点是嵌入、应用。

  随着各种嵌入式设备功能越来越强大, 在设备中使用嵌入式操作系统也成为必然。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 。

  交叉编译流程如图1 所示。

图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/ , 则先执行“make ”_config 命令进行配置, 然后执行“make all ” , 生成可执行文件。所以, 修改U-Boot 顶层的Makefile 文件, 添加下面一行mini2440_config : unconfig@ $ (MKCONFIG) $ (@:_config = ) arm arm920t mini2440frank 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 内核, 必须要具备如下几个条件:

  (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 文件中将staticvoid__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 所示。

表1 128 MB Nand Flash 的分区结构图

  其部分实现代码如下:

  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 rootfsrootfs.img 生成根文件系统映像文件。

  本文通过对U-Boot 移植和Linux 内核移植的讨论,给出了移植U-Boot 和Linux 到大多数开发板的关键部分。由于移植的复杂性, 不可能包括全部步骤, 但通过本文的阐述可以了解移植的基本流程和关键点, 为移植不同版本到其他硬件平台提供了参考, 也为应用程序的开发搭建了一个比较完整的嵌入式平台。
 

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