一、Uboot介绍
Linux 系统要启动就必须需要一个 bootloader 程序,也就说芯片上电以后先运行一段 bootloader 程序。这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(NAND, NOR FLASH,SD,MMC 等)拷贝到 DDR 中,最后启动 Linux 内核。当然了,bootloader 的实际工作要复杂的多,但是它最主要的工作就是启动 Linux 内核,bootloader 和 Linux 内核的关系 就跟 PC 上的 BIOS 和 Windows 的关系一样,bootloader 就相当于 BIOS。所以我们要先搞定 bootloader,很庆幸,有很多现成的 bootloader 软件可以使用,比如 U-Boot、vivi、RedBoot 等 等,其中以 U-Boot 使用最为广泛,为了方便书写,本书会将 U-Boot 写为 uboot。
uboot 的全称是 Universal Boot Loader,uboot 是一个遵循 GPL 协议的开源软件,uboot 是一 个裸机代码,可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB 等高 级功能。
接着就是关于 uboot 的下载问题,我们可以在官网直接进行下载。具体内容就不过多去说了。但是你在官网下载的uboot的源码,就是 uboot 原汁原味的源码文件。但是我们一般不 会直接用 uboot 官方的 U-Boot 源码的。uboot 官方的 uboot 源码是给半导体厂商准备的,半导 体厂商会下载 uboot 官方的 uboot 源码,然后将自家相应的芯片移植进去。也就是说半导体厂 商会自己维护一个版本的 uboot,这个版本的 uboot 相当于是他们定制的。既然是定制的,那么 肯定对自家的芯片支持会很全,虽然 uboot 官网的源码中一般也会支持他们的芯片,但是绝对是没有半导体厂商自己维护的 uboot 全面。uboot 官方的 uboot 代码 由 uboot 官方维护开发的 uboot 版本,版本更新快,基本包含所 有常用的芯片。 半导体厂商的 uboot 代码 半导体厂商维护的一个 uboot,专门针对自家的芯片,在对自家芯片支持上要比 uboot 官方的好。 开发板厂商的 uboot 代码 开发板厂商在半导体厂商提供的 uboot 基础上加入了对自家开发板的支持。
二、Uboot 编译
首先在 Ubuntu 中安装 ncurses 库,否则编译会报错,安装命令如下:
sudo apt-get install libncurses5-dev
使用如下命令对其进行解压缩:
tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2
接下来呢,就是要根据你的板端资源,来使用合适的命令。
如果使用的是 512MB+8GB 的 EMMC 核心板,使用如下命令来编译对应的 uboot:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- (加空格)
mx6ull_14x14_ddr512_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
这三条命令中 ARCH=arm 设置目标为 arm 架构,CROSS_COMPILE 指定所使用的交叉编 译器。第一条命令相当于“make distclean”,目的是清除工程,一般在第一次编译的时候最好清 理一下工程。第二条指令相当于“make mx6ull_14x14_ddr512_emmc_defconfig”,用于配置 uboot, 配置文件为 mx6ull_14x14_ddr512_emmc_defconfig。最后一条指令相当于 “make -j12”也就是 使用 12 核来编译 uboot。当这三条命令执行完以后 uboot 也就编译成功了,如图所示:
可以看出,编译完成以后 uboot 源码多了一些文件,其中 u-boot.bin 就是编译出来的 uboot 二进制文件。uboot是个裸机程序,因此需要在其前面加上头部(IVT、DCD等数据)才能在I.MX6U 上执行,图 中的 u-boot.imx 文件就是添加头部以后的 u-boot.bin,u-boot.imx 就是我们最 终要烧写到开发板中的 uboot 镜像文件。
每次编译 uboot 都要输入一长串命令,为了简单起见,我们可以新建一个 shell 脚本文件, 将这些命令写到 shell 脚本文件里面,然后每次只需要执行 shell 脚本即可完成编译工作。新建名为 mx6ull_alientek_emmc.sh 的 shell 脚本文件。
如果用的 256MB+512MB 的 NAND 核心板的话,其实也就是我刚刚说的内容,shell 脚 本文件,然后在里面输入如下内容:
#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- (加空格)
mx6ull_14x14_ddr256_nand_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
那我们问题现在回到分析下上述命令吧,
清理编译环境
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
选择默认内核配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
编译 Linux 内核
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
三、UBoot 烧写与启动
uboot 编译好以后就可以烧写到板子上使用了,这里我们跟前面裸机例程一样,将 uboot 烧写到 SD 卡中,然后通过 SD 卡来启动来运行 uboot。使用 imxdownload 软件烧写,命令如下:
chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可
./imxdownload u-boot.bin /dev/sdd //烧写到 SD 卡,不能烧写到/dev/sda 或 sda1 设备里面!
该日志是 U-Boot 2016.03 版本启动时的输出,显示了 CPU 信息、存储设备、显示设备、网络状态等内容。以下是逐行解析:
U-Boot 2016.03-gd3f0479 (Aug 07 2020 - 20:47:37 +0800)
U-Boot 版本:
2016.03
,表示 U-Boot 基于 2016 年 3 月的代码。Git 版本号:
gd3f0479
,表示编译 U-Boot 的 Git commit ID。编译时间:2020 年 8 月 7 日 20:47:37(+0800 表示中国时区)。
CPU: Freescale i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
处理器型号:
Freescale i.MX6ULL
,属于 NXP(原 Freescale)的 ARM Cortex-A7 处理器,低功耗,常用于工业和嵌入式设备。版本:
rev1.1
,表示芯片版本 1.1。最高主频:
792 MHz
。当前运行频率:
396 MHz
,说明 CPU 可能在低功耗模式下。CPU: Industrial temperature grade (-40C to 105C) at 51C
工业级芯片:支持
-40°C ~ 105°C
的工业温度范围。当前温度:
51°C
,说明当前芯片温度正常。Reset cause: POR
POR (Power-On Reset)
:表示复位原因是 上电复位,即设备刚刚开机。Board: I.MX6U ALPHA|MINI
开发板型号:
I.MX6U ALPHA|MINI
,一般是厂家定制的 i.MX6ULL 开发板。I2C: ready
I2C 设备初始化成功,可以用 I2C 访问外设,如 EEPROM、温度传感器等。DRAM: 512 MiB
RAM 容量:
512 MiB
,符合mx6ull_14x14_ddr512_emmc
配置。MMC: FSL_SDHC: 0, FSL_SDHC: 1
说明开发板上有 2 个 SD/MMC 设备,U-Boot 可能从 SD 卡或 eMMC 启动。Display: ATK-LCD-7-1024x600 (1024x600)
U-Boot 识别到了 LCD 屏幕,并设置了分辨率。
In: serial
Out: serial
Err: serialU-Boot 主要通过串口进行交互。
switch to partitions #0, OK
mmc1(part 0) is current deviceU-Boot 可能会从 eMMC 加载操作系统。
Net: FEC1
Error: FEC1 address not set.网络接口:
FEC1
(Fast Ethernet Controller,NXP i.MX6ULL 内置的 10/100M 以太网控制器)。错误:
Error: FEC1 address not set.
,表示 MAC 地址未设置,可能导致网络功能无法使用。
四、Uboot命令的使用
进入 uboot 的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前 uboot 所 支持的命令。
信息查询命令如下:
常用的和信息查询有关的命令有 3 个:bdinfo、printenv 和 version。先来看一下 bdinfo 命 令,此命令用于查看板子信息,直接输入“bdinfo”即可。
图中可以得出 DRAM 的起始地址和大小、启动参数保存起始地址、波特率、 sp(堆栈指针)起始地址等信息。 命令“printenv”用于输出环境变量信息,uboot 也支持 TAB 键自动补全功能,输入“print” 然后按下 TAB 键就会自动补全命令,直接输入“print”也可以。输入“print”,然后按下回车键。
命令 version 用于查看 uboot 的版本号,输入“version”,uboot 版本号。
接下来就是修改环境变量的问题了。
环境变量的操作涉及到两个命令:setenv 和 saveenv,命令 setenv 用于设置或者修改环境变 量的值。命令 saveenv 用于保存修改后的环境变量,一般环境变量是存放在外部 flash 中的, uboot 启动的时候会将环境变量从 flash 读取到 DRAM 中。所以使用命令 setenv 修改的是 DRAM 中的环境变量值,修改以后要使用 saveenv 命令将修改后的环境变量保存到 flash 中,否则的话 uboot 下一次重启会继续使用以前的环境变量值。
比如我们要将环境变量 bootdelay 改为 5。也就是如下所示了。
setenv bootdelay 5
saveenv
有时候我们修改的环境变量值可能会有空格,比如 bootcmd、bootargs 等,这个时候环境变 量值就得用单引号括起来,比如下面修改环境变量 bootargs 的值:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv
内存操作命令就是用于直接对 DRAM 进行读写操作的,常用的内存操作命令有 md、nm、 mm、mw、cp 和 cmp。我们依次来看一下这些命令都是做什么的。
md[.b, .w, .l] address [# of objects]
命令中的[.b .w .l]对应 byte、word 和 long,也就是分别以 1 个字节、2 个字节、4 个字节 来显示内存值。address 就是要查看的内存起始地址,[# of objects]表示要查看的数据长度,这个数据长度单位不是字节,而是跟你所选择的显示格式有关。比如你设置要查看的内存长度为 20(十六进制为 0x14),如果显示格式为.b 的话那就表示 20 个字节;如果显示格式为.w 的话就 表示 20 个 word,也就是 20*2=40 个字节;如果显示格式为.l 的话就表示 20 个 long,也就是 20*4=80 个字节。另外要注意:
uboot 命令中的数字都是十六进制的!不是十进制的!
比如你想查看以 0X80000000 开始的 20 个字节的内存值,显示格式为.b 的话,应该使用 如下所示命令:
md.b 80000000 14
而不是:
md.b 80000000 20
上面说了,uboot 命令里面的数字都是十六进制的,所以可以不用写“0x”前缀,十进制 的 20 其十六进制为 0x14,所以命令 md 后面的个数应该是 14,如果写成 20 的话就表示查看 32(十六进制为 0x20)个字节的数据。分析下面三个命令的区别:
md.b 80000000 10
md.w 80000000 10
md.l 80000000 10
nm 命令用于修改指定地址的内存值
mm 命令也是修改指定地址内存值的,使用 mm 修改内存值的时候地址会自增,而使用命 令 nm 的话地址不会自增。比如以.l 格式修改从地址 0x80000000 开始的连续 3 个内存块(3*4=12 个字节)的数据为 0X05050505
命令 mw 用于使用一个指定的数据填充一段内存
cp 是数据拷贝命令,用于将 DRAM 中的数据从一段内存拷贝到另一段内存中,或者把 Nor Flash 中的数据拷贝到 DRAM 中
还有更多的命令,这边笔者就不过多的写了,更多是要去你应用的内容了。