嵌入式Linux驱动开发笔记(七)

七、系统移植

1.概述

在移植 Linux之前我们需要先移植一个 bootloader 代码,这个 bootloader 代码用于启动 Linux 内核, bootloader有很多,常用的就是 U-Boot。 移植好 U-Boot 以后再移植 Linux 内核,移植完 Linux 内核以后Linux 还不能正常启动,还需要再移植一个根文件系统(rootfs),根文件系统里面包含了一些最常用的命令和文件。所以 U-Boot、 Linux kernel 和 rootfs 这三者一起构成了一个可以正常使用、功能完善的 Linux 系统。

2.U-Boot 的移植

2.1.U-Boot 简介

Linux 系统要启动就必须需要一个 bootloader 程序,也就说芯片上电以后先运行一段bootloader程序。这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(NAND,NOR FLASH, SD, MMC 等)拷贝到 DDR 中,最后启动 Linux 内核。当然了, bootloader 的实际工作要复杂的多,但是它最主要的工作就是启动 Linux 内核 , bootloader 和 Linux 内核的关系就跟 PC 上的 BIOS 和 Windows 的关系一样, bootloader 就相当于 BIOS。 其中,有很多现成的 bootloader 软件可以使用,比如 U-Boot、 vivi、 RedBoot 等等。

uboot 的全称是 Universal Boot Loader, uboot 是一个遵循 GPL 协议的开源软件, uboot 是一个裸机代码 ,现在的 uboot 已经支持液晶屏、网络、 USB 等高级功能。uboot 官网为 http://www.denx.de/wiki/U-Boot/ 。

2.2.U-Boot 启动体验

本次直接使用正点原子已经移植好的 uboot :ubootimx-2016.03-2.1.0-ge468cdc-v1.5.tar.bz2。

2.2.1.U-Boot 初次编译

首先在 Ubuntu 中安装 ncurses 库, 否则编译会报错,安装命令如下:

sudo apt-get install libncurses5-dev

将正点原子提供的 uboot 源码拷贝到目录:/Linux/uboot/alientek_uboot/cmd下

使用如下命令对其进行解压缩:

tar -vxjf uboot-imx-2016.03-2.1.0-gee88051-v1.6.tar.bz2


我使用的是 512MB+8GB 的 EMMC 核心板,使用如下命令来编译对应的 uboot:

#ARCH=arm 设置目标为 arm 架构#CROSS_COMPILE 指定所使用的交叉编译器#第一条命令相当于“make distclean”,目的是清除工程,一般在第一次编译的时候最好清理一下工程。make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean#第二条指令相当于“make mx6ull_14x14_ddr512_emmc_defconfig”,#用于配置 uboot,配置文件为 mx6ull_14x14_ddr512_emmc_defconfigmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig#最后一条指令相当于 “make -j4”也就是使用 4 核来编译 uboot(我是虚拟机下,只分配了4核)make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4


可以看出,编译完成以后 uboot 源码多了一些文件,其中 u-boot.bin 就是编译出来的 uboot二进制文件。uboot是个裸机程序,因此需要在其前面加上头部(IVT、DCD等数据)才能在I.MX6U上执行 。

图 2.2.1 中的 u-boot.imx 文件就是添加头部以后的 u-boot.bin, u-boot.imx 就是我们最终要烧写到开发板中的 uboot 镜像文件 。

每次编译 uboot 都要输入一长串命令,为了简单起见,我们可以新建一个 shell 脚本文件,将这些命令写到 shell 脚本文件里面,然后每次只需要执行 shell 脚本即可完成编译工作。新建名为 mx6ull_alientek_emmc.sh 的 shell 脚本文件,然后在里面输入如下内容:

#!/bin/bashmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distcleanmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfigmake V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

在执行mx6ull_alientek_emmc.sh前需要给脚本文件权限:

chmod 755 mx6ull_alientek_emmc.sh

然后就可以使用这个 shell脚本文件来重新编译 uboot,命令如下:

./mx6ull_alientek_emmc.sh
2.2.2.U-Boot 烧写与启动

uboot 编译好以后就可以烧写到板子上使用了, 这里我们跟前面裸机例程一样,将 uboot烧写到 SD 卡中,然后通过 SD 卡来启动来运行 uboot。使用 imxdownload 软件烧写,命令如下:

#给予 imxdownload 可执行权限,一次即可chmod 777 imxdownload #/烧写到 SD 卡,不能烧写到/dev/sda 或 sda1 设备里面!./imxdownload u-boot.bin /dev/sdb


等待烧写完成,完成以后将 SD 卡插到 I.MX6U-ALPHA 开发板上, BOOT 设置从 SD 卡启动,使用 USB 线将 USB_TTL 和电脑连接 ,打开“SecureCRT 7.3”,设置好串口参数并打开,最后复位开发板 ,在 软件 上出现“Hit any key tostop autoboot: ”倒计时的时候按下键盘上的回车键,默认是 3 秒倒计时,在 3 秒倒计时结束以后如果没有按下回车键的话 uboot 就会使用默认参数来启动 Linux 内核了。如果在 3 秒倒计时结束之前按下回车键,那么就会进入 uboot 的命令行模式 ,如下图所示

2.3.U-Boot 命令使用

进入 uboot 的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前 uboot 所
支持的命令 ,例,输入如下命令即可查看“bootz”这个命令的用法:

" />help bootz
2.3.1.信息查询命令

常用的和信息查询有关的命令有 3 个: bdinfo、 printenv 和 version。

#获取DRAM 的起始地址和大小、启动参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息bdinfo#用于输出环境变量信息printenv #用于查看 uboot 的版本号version

uboot 也支持 TAB 键自动补全功能

2.3.2.环境变量操作命令

(1)环境变量的操作涉及到两个命令: setenv 和 saveenv 。

#用于设置或者修改环境变量的值,例如,环境变量 bootdelay 改为 5setenv bootdelay 5#用于保存修改后的环境变量saveenv

一般环境变量是存放在外部 flash 中的,uboot 启动的时候会将环境变量从 flash 读取到 DRAM 中。所以使用命令 setenv 修改的是 DRAM中的环境变量值,修改以后要使用 saveenv 命令将修改后的环境变量保存到 flash 中,否则的话uboot 下一次重启会继续使用以前的环境变量值。

因为我们现在将 uboot烧写到了 SD 卡里面,所以会保存到 MMC(0)中。如果烧写到 EMMC 里面就会提示保存到
MMC(1),也就是 EMMC 设备。

有时候我们修改的环境变量值可能会有空格, 比如 bootcmd、 bootargs 等, 这个时候环境变量值就得用单引号括起来,比如下面修改环境变量 bootargs 的值:

setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'saveenv

(2)新建环境变量

命令 setenv 也可以用于新建命令 ,比如我们新建一个环境变量author ,值为sudeki

setenv author sudekisaveenv

(3)删除环境变量

删除环境变量也是使用命令 setenv ,要删除一个环境变量只要给这个环境变量赋空值即可

setenv authorsaveenv

上面命令中通过 setenv 给 author 赋空值,也就是什么都不写来删除环境变量 author。重启uboot 就会发现环境变量 author 没有了。

2.3.3.内存操作指令

内存操作命令就是用于直接对 DRAM 进行读写操作的,常用的内存操作命令有 md、 nm、mm、 mw、 cp 和 cmp。

(1)md 命令

md 命令用于显示内存值,格式如下:

md[.b, .w, .l] address [# of objects]#例;查看以 0X80000000 开始的 20 个字节的内存值md.b 80000000 14

命令中的[.b .w .l]对应 byte、 word 和 long,也就是分别以 1 个字节、 2 个字节、 4 个字节来显示内存值。 address 就是要查看的内存起始地址, [# of objects]表示要查看的数据长度, 数据长度单位不是字节,而是跟你所选择的显示格式有关。

注意:uboot 命令中的数字都是十六进制的!不是十进制的!

(2)nm 命令

nm 命令用于修改指定地址的内存值,命令格式如下:

nm [.b, .w, .l] address

?后面就可以输入要修改后的数据 ,输入完成以后按下回车,然后再输入‘q’即可退出

(3)mm 命令

mm 命令也是修改指定地址内存值的,使用 mm 修改内存值的时候地址会自增,而使用命令 nm 的话地址不会自增。

(4)mw 命令

命令 mw 用于使用一个指定的数据填充一段内存,命令格式如下:

mw [.b, .w, .l] address value [count]

mw 命令同样可以以.b、 .w 和.l 来指定操作格式, address 表示要填充的内存起始地址, value为要填充的数据, count 是填充的长度。

(5)cp 命令

cp 是数据拷贝命令,用于将 DRAM 中的数据从一段内存拷贝到另一段内存中,或者把 NorFlash 中的数据拷贝到 DRAM 中。命令格式如下:

cp [.b, .w, .l] source target count

cp 命令同样可以以.b、 .w 和.l 来指定操作格式, source 为源地址, target 为目的地址, count为拷贝的长度。

(6)cmp 命令

cmp 是比较命令,用于比较两段内存的数据是否相等,命令格式如下:

cmp [.b, .w, .l] addr1 addr2 count

cmp 命令同样可以以.b、 .w 和.l 来指定操作格式, addr1 为第一段内存首地址, addr2 为第二段内存首地址, count 为要比较的长度。

2.3.4.网络操作命令

uboot 是支持网络的,我们在移植 uboot 的时候一般都要调通网络功能,因为在移植 linuxkernel 的时候需要使用到 uboot 的网络功能做调试。uboot 支持大量的网络相关命令,比如 dhcp、ping、 nfs 和 tftpboot。

在使用 uboot 的网络功能之前先用网线将开发板的 ENET2 接口和电脑或者路由器连接起来, I.MX6U-ALPHA 开发板有两个网口: ENET1 和 ENET2,一定要连接 ENET2,不能连接错了。

建议开发板和主机 PC 都连接到同一个路由器上! 本次测试,使用的是开发板与电脑直接相连。最后设置表中所示的几个环境变量。

最后设置表 30.4.4.1 中所示的几个环境变量。

setenv ipaddr 192.168.1.50setenv ethaddr b8:ae:1d:01:00:00setenv gatewayip 192.168.1.1setenv netmask 255.255.255.0setenv serverip 192.168.1.253saveenv

注意,网络地址环境变量的设置要根据自己的实际情况,确保 Ubuntu 主机和开发板的 IP地址在同一个网段内.

设置好网络相关的环境变量以后就可以使用网络相关命令了。