前言:
本文是根据哔哩哔哩网站上“Arm(iMX6U)Linux系统移植和根文件系统构键篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。
引用:
正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com
《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》
正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档
正点原子imx6ull-mini-Linux驱动之Linux I2C 驱动实验(21)-优快云博客
正文:
本文是 “Arm(iMX6U)Linux系统移植和根文件系统构键篇--2.1讲uboot简介。本节将参考正点原子的视频教程和配套的正点原子开发指南文档进行学习。
0. 概述
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官网为
The U-Boot Documentation — Das U-Boot unknown version documentation
我们可以在 uboot官网下载 uboot源码,点击图 30.1.1中左侧 Topics中的“ Source Code
打开如图 30.1.2所示界面:
但是我们一般不会直接用 uboot 官方的 U-Boot 源码的。uboot 官方的 uboot 源码是给半导体厂商准备的,半导体厂商会下载 uboot 官方的 uboot 源码,然后将自家相应的芯片移植进去。也就是半导体厂商会自己维护一个版本的 uboot,这个版本的 uboot 相当于是他们定制的。既然是定制的,那么肯定对自家的芯片支持会很全,虽然 uboot 官网的源码中一般也会支持他们的芯片,但是决议是没有半导体厂商自己维护的 uboot 全面。
- u-boot网站的源码
- SoC芯片厂家从u-boot网站上下载源码,然后把自家的芯片SoC移植进去,SoC厂家自己维护了一个版本的bu-boot,与原始u-boot相比,对自家的芯片支持更全面。
NXP 就 维 护 的 2016.03 这 个 版 本 的 uboot , 下 载 地 址 为 :
http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tag/?h=imx_v2016.03_4.1.15_2.0.0_ga&id=
rel_imx_4.1.15_2.1.0_ga
下载界面如图 30.1.4 所示
图 30.1.4 中的 uboot-imx_rel_imx4.1.15_2.1.0_ga.xx(xx 为 zip、 tar.gz 或 tar.bz2)就是 NXP 官方维护的 uboot,后面我们学习 uboot 移植的时候就是使用的图 30.1.4 中的 uboot,下载ubootimx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。图 30.1.4中的 uboot 基本支持了 NXP 当前所有可以跑 Linux 的芯片,而且支持各种启动方式,比如 EMMC、NAND、 NOR FLASH 等等,这些都是 uboot 官方所不支持的。
但是图 30.1.4 中的 uboot 是针对NXP 自家评估板的,如果是我们自己做的板子就需要修改 NXP 官方的 uboot,使其支持我们自己做的板子,正点原子的 I.MX6U 开发板就是自己做的板子,虽然大部分都参考了 NXP 官方的I.MX6ULL EVK 开发板,但是还是有很多不同的地方,所以需要修改 NXP 官方的 uboot,使其适配正点原子的 I.MX6U 开发板。所以当我们拿到开发板以后,是有三种 uboot 的,这三种 uboot的区别如下表所示:
那么这三种 uboot 该如何选择呢?首先 uboot 官方的基本是不会用的,因为支持太弱了。最常用的就是半导体厂商或者开发板厂商的 uboot,如果你用的半导体厂商的评估板,那么就使用半导体厂商的 uboot,如果你是购买的第三方开发板,比如正点原子的 I.MX6ULL 开发板,那么就使用正点原子提供的 uboot 源码(也是在半导体厂商的 uboot 上修改的)。当然了,你也可以在购买了第三方开发板以后使用半导体厂商提供的 uboot, 只不过有些外设驱动可能不支持, 需要自己移植,这个就是我们常说的 uboot 移植。
本节是 uboot 的使用,所以就直接使用正点原子已经移植好的 uboot,这个已经放到了开发
板光盘中了。
1.U-Boot 初次编译
在 Ubuntu 中创建存放 uboot 的目录,比如我的是/home/chen/linux/uboot,然后在此目录下新建一个名为“alientek_uboot”的文件夹用于存放正点原子提供的 uboot 源码。(从正点原子网站上百度网盘链接下载)
dimon@dimon-VirtualBox:~/I.MX6ULL/uboot_altek$ ls
uboot-imx-2016.03-2.1.0-g0ae7e33-v1.7.tar.bz2
dimon@dimon-VirtualBox:~/I.MX6ULL/uboot_altek$
使用如下命令对其进行解压缩
dimon@dimon-VirtualBox:~/I.MX6ULL/uboot_altek$ tar -xf uboot-imx-2016.03-2.1.0-g0ae7e33-v1.7.tar.bz2
解压之后得到了整点原子修改移植过得针对正点原子 I.MX6ULL Alpha开发板的u-boot的源码。解压完成以后 alientek_uboot 文件夹内容如图 30.2.2 所示。
图 30.2.2 中除了 uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2 这个正点原子提供的 uboot 源码压缩包以外,其他的文件和文件夹都是解压出来的 uboot 源码。
512MB(DDR3)+8GB(EMMC)核心板
如果使用的是 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 也就编译成功了,如图 30.2.3 所示
编译完成以后的 alentek_uboot 文件夹内容如图
可以看出,编译完成以后 uboot 源码多了一些文件,其中 u-boot.bin 就是编译出来的 uboot二进制文件。uboot是个裸机程序,因此需要在其前面加上头部(IVT、DCD等数据)才能在I.MX6U上执行,图 30.2.4 中的 u-boot.imx 文件就是添加头部以后的 u-boot.bin, u-boot.imx 就是我们最终要烧写到开发板中的 uboot 镜像文件.
每次编译 uboot 都要输入一长串命令,为了简单起见,我们可以新建一个 shell 脚本文件,将这些命令写到 shell 脚本文件里面,然后每次只需要执行 shell 脚本即可完成编译工作。新建名为 mx6ull_alientek_emmc.sh 的 shell 脚本文件,然后在里面输入如下内容.
##我购买的是正点原子I.MX6ULL Alpha Mini开发板,是512MB的DDR EMMC开发板
##所以选择的 xxx_defconfig 是256MB emmc的 defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
第 1 行是 shell 脚本要求的,必须是“#!/bin/bash”或者“#!/bin/sh”。
第 2 行使用了 make 命令,用于清理工程,也就是每次在编译 uboot 之前都清理一下工程。这里的 make 命令带有三个参数,第一个是 ARCH,也就是指定架构,这里肯定是 arm;第二个参数 CROSS_COMPILE 用于指定编译器,只需要指明编译器前缀就行了,比如 arm-linuxgnueabihf-gcc 编译器的前缀就是“arm-linux-gnueabihf-”;最后一个参数 distclean 就是清除工程.
第 3 行也使用了 make 命令,用于配置 uboot。同样有三个参数,不同的是,最后一个参数
是 mx6ull_14x14_ddr512_emmc_defconfig。
前面说了 uboot 是 bootloader 的一种,可以用来引导Linux,但是 uboot 除了引导 Linux 以外还可以引导其它的系统,而且 uboot 还支持其它的架构和外设, 比如 USB、 网络、 SD 卡等。这些都是可以配置的,需要什么功能就使能什么功能。所以在编译 uboot 之前,一定要根据自己的需求配置 uboot。
mx6ull_14x14_ddr512_emmc_defconfig就是正点原子针对 I.MX6U-ALPHA 的 EMMC 核心板编写的配置文件,这个配置文件在 uboot源码的 configs 目录中。在 uboot 中,通过“make xxx_defconfig”来配置 uboot, xxx_defconfig就是不同板子的配置文件,这些配置文件都在 uboot/configs 目录中.
在u-boot源码目录下, u-boot/configs/ 目录下有非常多的 xxx_defconfig 的默认配置文件,一般一个xxx_defconfig 配置文件对应着一种硬件开发板(不同硬件开发板有不同的外设,所以u-boot的默认配置有所差异)。
第 4 行有 4 个参数,用于编译 uboot,通过第 3 行配置好 uboot 以后就可以直接“make”编译 uboot 了。其中 V=1 用于设置编译过程的信息输出级别; -j 用于设置主机使用多少线程编译uboot,最好设置成我们虚拟机所设置的核心数,如果在 VMware 里面给虚拟就分配了 4 个核,那么使用-j4 是最合适的,这样 4 个核都会一起编译。
使用 chmod 命令给予 mx6ull_alientek_emmc.sh 文件可执行权限,然后就可以使用这个 shell
脚本文件来重新编译 uboot,命令如下
chmod +x build_im6ull_emmc.sh
bash build_im6ull_emmc.sh
2.U-Boot 烧写与启动
uboot 编译好以后就可以烧写到板子上使用了, 这里我们跟前面裸机例程一样,将 uboot烧写到 SD 卡中,然后通过 SD 卡来启动来运行 uboot。使用 imxdownload 软件烧写,命令如下:
chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可
./imxdownload u-boot.bin /dev/sdd //烧写到 SD 卡,不能烧写到/dev/sda 或 sda1 设备里面
等待烧写完成,完成以后将 SD 卡插到 I.MX6U-ALPHA 开发板上, BOOT 设置从 SD 卡启动,使用 USB 线将 USB_TTL 和电脑连接,也就是将开发板的串口 1 连接到电脑上。打开MobaXterm,设置好串口参数并打开,最后复位开发板。在 MobaXterm 上出现“Hit any key to stop autoboot: ”倒计时的时候按下键盘上的回车键,默认是 3 秒倒计时,在 3 秒倒计时结束以后如果没有按下回车键的话 uboot 就会使用默认参数来启动 Linux 内核了。如果在 3 秒倒计时结束之前按下回车键,那么就会进入 uboot 的命令行模式,如图 30.3.1 所示
从图 30.3.1 可以看出,当进入到 uboot 的命令行模式以后,左侧会出现一个“=>”标志。
uboot 启动的时候会输出一些信息,这些信息如下所示:
U-Boot 2016.03 (Mar 07 2025 - 23:35:20 +0800)
CPU: Freescale i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 23C
Reset cause: POR
Board: I.MX6U ALPHA|MINI
I2C: ready
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc0 is current device
Net: FEC1
Error: FEC1 address not set.
Normal Boot
Hit any key to stop autoboot: 0
=>
第 1 行是 uboot 版本号和编译时间,可以看出,当前的 uboot 版本号是 2016.03,编译时间是 2025 年 3 月 7 日凌晨 23 点 35 分
第 3 和第 4 行是 CPU 信息,可以看出当前使用的 CPU 是飞思卡尔的 I.MX6ULL(I.MX 以前属于飞思卡尔,然而飞思卡尔被 NXP 收购了), 频率为 792MHz,但是此时运行在 396MHz。这颗芯片是工业级的, 结温为-40° C~105° C
第 5 行是复位原因,当前的复位原因是 POR。 I.MX6ULL 芯片上有个 POR_B 引脚,将这个引脚拉低即可复位 I.MX6ULL。
第 6 行是板子名字,当前的板子名字为“I.MX6U ALPHA|MINI”。
第 7 行提示 I2C 准备就绪。
第 8 行提示当前板子的 DRAM(内存)为 512MB,如果是 NAND 版本的话内存为 256MB。
第 9 行提示当前有两个 MMC/SD 卡控制器: FSL_SDHC(0)和 FSL_SDHC(1)。 I.MX6ULL支持两个 MMC/SD,正点原子的 I.MX6ULL EMMC 核心板上 FSL_SDHC(0)接的 SD(TF), FSL_SDHC(1)接的 EMMC。第 10 和第 11 行是 LCD 型号,当前的 LCD 型号是 ATK-LCD-7-1024x600 (1024x600),分辨率为 1024x600,格式为 RGB888(24 位)。
第 12~14 是标准输入、标准输出和标准错误所使用的终端,这里都使用串口(serial)作为终端
第 15 和 16 行是切换到 emmc 的第 0 个分区上,因为当前的 uboot 是 emmc 版本的,也就是从 emmc 启动的。我们只是为了方便将其烧写到了 SD 卡上,但是它的“内心”还是 EMMC的。所以 uboot 启动以后会将 emmc 作为默认存储器,当然了,你也可以将 SD 卡作为 uboot 的存储器,这个我们后面会讲解怎么做。
第 17 行是网口信息,提示我们当前使用的 FEC1 这个网口, I.MX6ULL 支持两个网口。
第 18 行提示 FEC1 网卡地址没有设置,后面我们会讲解如何在 uboot 里面设置网卡地址。
第 20 行提示正常启动,也就是说 uboot 要从 emmc 里面读取环境变量和参数信息启动 Linux内核了
第 21 行是倒计时提示,默认倒计时 3 秒,倒计时结束之前按下回车键就会进入 Linux 命令行模式。如果在倒计时结束以后没有按下回车键,那么 Linux 内核就会启动, Linux 内核一旦启动, uboot 就会寿终正寝。
这个就是 uboot 默认输出信息的含义, NAND 版本的 uboot 也是类似的,只是 NAND 版本的就没有 EMMC/SD 相关信息了,取而代之的就是 NAND 的信息,比如 NAND 容量大小信息。
uboot 是来干活的,我们现在已经进入 uboot 的命令行模式了,进入命令行模式以后就可以给 uboot 发号施令了。当然了,不能随便发号施令,得看看 uboot 支持哪些命令,然后使用这些uboot 所支持的命令来做一些工作。下一节就讲解 uboot 命令的使用。
3 U-Boot 命令使用
进入 uboot 的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前 uboot 所支持的命令,如图 30.4.1 所示
=> ?
? - alias for 'help'
base - print or set address offset
bdinfo - print Board Info structure
bmode - sd1|sd2|qspi1|normal|usb|sata|ecspi1:0|ecspi1:1|ecspi1:2|ecspi1:3|esdhc1|esdhc2|esdhc3|esdhc4 [noreset]
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootelf - Boot from an ELF image in memory
图 30.4.1 中只是 uboot 的一部分命令, 具体的命令列表以实际为准。图 30.4.1 中的命令并不是 uboot 所支持的所有命令,前面说过 uboot 是可配置的,需要什么命令就使能什么命令。所以图 30.4.1 中的命令是正点原子提供的 uboot 中使能的命令, uboot 支持的命令还有很多,而且
也可以在 uboot 中自定义命令。这些命令后面都跟有命令说明,用于描述此命令的作用,但是
命令具体怎么用呢?我们输入“help(或?) 命令名”既可以查看命令的详细用法,以“bootz”这
个命令为例,我们输入如下命令即可查看“bootz”这个命令的用法:
=> help bootz
bootz - boot Linux zImage image from memory
Usage:
bootz [addr [initrd[:size]] [fdt]]
- boot Linux zImage stored in memory
The argument 'initrd' is optional and specifies the address
of the initrd in memory. The optional argument ':size' allows
specifying the size of RAW initrd.
When booting a Linux kernel which requires a flat device-tree
a third argument is required which is the address of the
device-tree blob. To boot that kernel without an initrd image,
use a '-' for the second argument. If you do not pass a third
a bd_info struct will be passed instead
=>
图 30.4.2 中就详细的列出了“bootz”这个命令的详细,其它的命令也可以使用此方法查询具体的使用方法。接下来我们学习一下一些常用的 uboot 命令。
3.1 信息查询命令
常用的和信息查询有关的命令有 3 个: bdinfo、 printenv 和 version。先来看一下 bdinfo 命令,此命令用于查看板子信息,直接输入“bdinfo”即可,结果如图 30.4.1.1 所示:
bdinfo 命令
=> bdinfo
arch_number = 0x00000000
boot_params = 0x80000100
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x20000000
eth0name = FEC1
ethaddr = (not set)
current eth = FEC1
ip_addr = <NULL>
baudrate = 115200 bps
TLB addr = 0x9FFF0000
relocaddr = 0x9FF55000
reloc off = 0x18755000
irq_sp = 0x9EF52EA0
sp start = 0x9EF52E90
=>
从图 30.4.1.1 中可以得出 DRAM 的起始地址和大小、启动参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息
命令“printenv”用于输出环境变量信息, uboot 也支持 TAB 键自动补全功能,输入“print”然后按下 TAB 键就会自动补全命令,直接输入“print”也可以。输入“print”,然后按下回车键,环境变量如图 30.4.1.2 所示
=> printenv
baudrate=115200
board_name=EVK
board_rev=14X14
boot_fdt=try
bootcmd=run findfdt;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi
bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr};
bootdelay=1
bootscript=echo Running bootscript from mmc ...; source
console=ttymxc0
ethact=FEC1
ethprime=FEC
fdt_addr=0x83000000
fdt_file=imx6ull-14x14-emmc-4.3-480x272-c.dtb
fdt_high=0xffffffff
findfdt=if test $fdt_file = undefined; then if test $board_name = EVK && test $board_rev = 9X9; then setenv fdt_file imx6ull-9x9-evk.dtb; fi; if test $board_name = EVK && test $board_rev = 14X14; then setenv fdt_file imx6ull-14x14-evk.dtb; fi; if test $fdt_file = undefined; then echo WARNING: Could not determine dtb to use; fi; fi;
image=zImage
initrd_addr=0x83800000
initrd_high=0xffffffff
ip_dyn=yes
loadaddr=0x80800000
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
logo_file=alientek.bmp
mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=/fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber="" clk_ignore_unused
mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
mmcautodetect=yes
mmcboot=echo Booting from mmc ...; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
mmcdev=0
mmcpart=1
mmcroot=/dev/mmcblk0p2 rootwait rw
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
netboot=echo Booting from net ...; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
panel=ATK-LCD-4.3-480x272
script=boot.scr
splashimage=0x88000000
splashpos=m,m
stderr=serial
stdin=serial
stdout=serial
Environment size: 2583/8188 bytes
=>
在图 30.4.1.2 中有很多的环境变量,比如 baudrate、 board_name、 board_rec、 boot_fdt、 bootcmd
等等。 uboot 中的环境变量都是字符串,既然叫做环境变量,那么它的作用就和“变量”一样。
比如 bootdelay 这个环境变量就表示 uboot 启动延时时间,默认 bootdelay=3,也就默认延时 3
秒。前面说的 3 秒倒计时就是由 bootdelay 定义的,如果将 bootdelay 改为 5 的话就会倒计时 5s
了。 uboot 中的环境变量是可以修改的,有专门的命令来修改环境变量的值,稍后我们会讲解。
命令 version 用于查看 uboot 的版本号,输入“version”, uboot 版本号如图 30.4.1.3 所示:
=> version
U-Boot 2016.03 (Mar 07 2025 - 23:35:20 +0800)
arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2017.01) 4.9.4
GNU ld (Linaro_Binutils-2017.01) 2.24.0.20141017 Linaro 2014_11-3-git
=>
从图 30.4.1.3 可以看出,当前 uboot 版本号为 2016.03, 2020 年 8 月 7 日编译的,编译器为arm-poky-linux-gnueabi-gcc,这是 NXP 官方提供的编译器,正点原子出厂系统用的此编译器编译的,但是本教程我们统一使用 arm-linux-gnueabihf-gcc。
3.2 环境变量操作命令
1、修改环境变量
环境变量的操作涉及到两个命令: setenv 和 saveenv,命令 setenv 用于设置或者修改环境变量的值。命令 saveenv 用于保存修改后的环境变量,一般环境变量是存放在外部 flash 中的,uboot 启动的时候会将环境变量从 flash 读取到 DRAM 中。所以使用命令 setenv 修改的是 DRAM中的环境变量值,修改以后要使用 saveenv 命令将修改后的环境变量保存到 flash 中,否则的话uboot 下一次重启会继续使用以前的环境变量值。
命令 saveenv 使用起来很简单,格式为:
saveenv
比如我们要将环境变量 bootdelay 改为 5,就可以使用如下所示命令
setenv bootdelay 5
saveenv
上述命令执行过程如图 30.4.2.1 所示
=> setenv bootdelay 5
=> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
=>
在图 30.4.2.1 中, 当我们使用命令 saveenv 保存修改后的环境变量的话会有保存过程提示信息,根据提示可以看出环境变量保存到了 MMC(0)中,也就是 SD 卡中。因为我们现在将 uboot烧写到了 SD 卡里面,所以会保存到 MMC(0)中。如果烧写到 EMMC 里面就会提示保存到MMC(1),也就是 EMMC 设备,同理,如果是 NAND 版本核心板的话就会提示保存到 NAND中。
修改 bootdelay 以后,重启开发板, uboot 就是变为 5 秒倒计时,如图 30.4.2.2 所示:
有时候我们修改的环境变量值可能会有空格, 比如 bootcmd、 bootargs 等, 这个时候环境变量值就得用单引号括起来,比如下面修改环境变量 bootargs 的值:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv
上面命令设置 bootargs 的值为“console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw”,其中“console=ttymxc0,115200”、“root=/dev/mmcblk1p2”、“rootwait”和“rw”相当于四组“值”,这四组“值”之间用空格隔开,所以需要使用单引号‘’将其括起来,表示这四组“值”都属于环境变bootargs。
2,新建环境变量
命令 setenv 也可以用于新建命令,用法和修改环境变量一样,比如我们新建一个环境变量author, author 的值为我的名字拼音: zuozhongkai,那么就可以使用如下命令
=> setenv author chenhaoxu
=> saveenv
新建命令 author 完成以后重启 uboot,然后使用命令 printenv 查看当前环境变量,如图
Error: FEC1 address not set.
Normal Boot
Hit any key to stop autoboot: 0
=>
=> printenv
author=chenhaoxu
baudrate=115200
board_name=EVK
board_rev=14X14
从图 30.4.2.3 可以看到新建的环境变量: author,其值为: chenhaoxu
3、删除环境变量
既然可以新建环境变量, 肯定也可以删除环境变量,删除环境变量也是使用命令 setenv,要删除一个环境变量只要给这个环境变量赋空值即可,比如我们删除掉上面新建的 author 这个环境变量,命令如下
=> setenv author
=> saveenv
4,内存操作命令
内存操作命令就是用于直接对 DRAM 进行读写操作的,常用的内存操作命令有 md、 nm、mm、 mw、 cp 和 cmp。我们依次来看一下这些命令都是做什么的。
md 命令
md 命令用于显示内存值,格式如下
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 0x80000000 0x10
md.w 0x80000000 0x10
md.l 0x80000000 0x10
上面这三个命令都是查看以 0X80000000 为起始地址的内存数据,第一个命令以.b 格式显示,长度为 0x10,也就是 16 个字节;第二个命令以.w 格式显示,长度为 0x10,也就是 16*2=32个字节;最后一个命令以.l 格式显示,长度也是 0x10,也就是 16*4=64 个字节。这三个命令的执行结果如图 30.4.3.1 所示:
=> md.b 0x80000000 0x10
80000000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
=> md.w 0x80000000 0x10
80000000: ffff ffff ffff ffff ffff ffff ffff ffff ................
80000010: ffff ffff ffff ffff ffff ffff ffff ffff ................
=> md.l 0x80000000 0x10
80000000: ffffffff ffffffff ffffffff ffffffff ................
80000010: ffffffff ffffffff ffffffff ffffffff ................
80000020: ffffffff ffffffff ffffffff ffffffff ................
80000030: ffffffff ffffffff ffffffff ffffffff ................
=>
nm 命令
nm 命令用于修改指定地址的内存值,命令格式如下:
=> help nm
nm - memory modify (constant address)
Usage:
nm [.b, .w, .l] address
=>
nm 命令同样可以以.b、 .w 和.l 来指定操作格式,比如现在以.l 格式修改 0x80000000 地址的数据为 0x12345678。输入命令
=> nm.l 0x80000000
80000000: ffffffff ? 0x12345678
在图 30.4.3.2 中, 80000000 表示现在要修改的内存地址, 0500e031 表示地址 0x80000000 现在的数据,?后面就可以输入要修改后的数据 0x12345678,输入完成以后按下回车,然后再输入‘q’即可退出,如图 30.4.3.3 所示
=> nm.l 0x80000000
80000000: ffffffff ? 0x12345678
80000000: 12345678 ?
80000000: 12345678 ?
80000000: 12345678 ? q
=>
从上述命令执行过程可以看到nm修改内存中指定地址的数据时,连续输入回车键,内存地址不会自动增加。
修改完成以后在使用命令 md 来查看一下有没有修改成功,如图 30.4.3.4 所示
=> md.l 0x80000000 1
80000000: 12345678 xV4.
=>
从图 30.4.3.4 可以看出,此时地址 0X80000000 的值变为了 0x12345678。
mm 命令
mm 命令也是修改指定地址内存值的,使用 mm 修改内存值的时候地址会自增,而使用命令 nm 的话地址不会自增。
mm命令格式
=> help mm
mm - memory modify (auto-incrementing address)
Usage:
mm [.b, .w, .l] address
=>
比如以.l 格式修改从地址 0x80000000 开始的连续 3 个内存块(3*4=12个字节)的数据为 0X05050505,操作如图 30.4.3.5 所示
=> mm.l 0x80000000
80000000: 12345678 ? 0x50505050
80000004: ffffffff ? 0x50505050
80000008: ffffffff ? 0x50505050
8000000c: ffffffff ? q
=>
从图 30.4.3.5 可以看出,修改了地址 0X80000000、 0X80000004 和 0X8000000C 的内容为0x05050505。使用命令 md 查看修改后的值,结果如图 30.4.3.6 所示
=> md.l 0x80000000 3
80000000: 50505050 50505050 50505050 PPPPPPPPPPPP
=>
从图 30.4.3.6 可以看出内存数据修改成功。
mw 命令
命令 mw 用于使用一个指定的数据填充一段内存,命令格式如下
=> ? mw
mw - memory write (fill)
Usage:
mw [.b, .w, .l] address value [count]
=>
mw 命令同样可以以.b、 .w 和.l 来指定操作格式, address 表示要填充的内存起始地址, value为要填充的数据, count 是填充的长度。比如使用.l 格式将以 0X80000000 为起始地址的 0x10 个内存块(0x10 * 4=64 字节)填充为 0X0A0A0A0A,命令如下:
=> mw 0x80000000 0x0A0A0A0A 0x10
然后使用命令 md 来查看,如图 30.4.3.7 所示
=> md.l 0x80000000 0x10
80000000: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000010: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000020: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000030: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
=>
从图 30.4.3.7 可以看出内存数据修改成功
CP命令
cp 是数据拷贝命令,用于将 DRAM 中的数据从一段内存拷贝到另一段内存中,或者把 Nor Flash 中的数据拷贝到 DRAM 中。命令格式如下:
=> ? cp
cp - memory copy
Usage:
cp [.b, .w, .l] source target count
=>
cp 命令同样可以以.b、 .w 和.l 来指定操作格式, source 为源地址, target 为目的地址, count为拷贝的长度。我们使用.l 格式将 0x80000000 处的地址拷贝到 0X80000100 处,长度为 0x10 个内存块(0x10 * 4=64 个字节),命令如下所示:
=> cp.l 0x80000000 0x80000100 0x10
结果如图 30.4.3.8 所示
=> cp.l 0x80000000 0x80000100 0x10
=> md.l 0x80000000 0x10
80000000: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000010: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000020: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000030: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
=> md.l 0x80000100 0x10
80000100: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000110: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000120: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000130: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
=>
在图 30.4.3.8 中,先使用 md.l 命令打印出地址 0x80000000 和 0x80000100 处的数据,然后使用命令cp.l将0x80000100处的数据拷贝到0x80000100处。最后使用命令md.l查看0x80000100处的数据有没有变化,检查拷贝是否成功。
cmp 命令
cmp 是比较命令,用于比较两段内存的数据是否相等,命令格式如下
=> ? cmp
cmp - memory compare
Usage:
cmp [.b, .w, .l] addr1 addr2 count
=>
cmp 命令同样可以以.b、 .w 和.l 来指定操作格式, addr1 为第一段内存首地址, addr2 为第二段内存首地址, count 为要比较的长度。我们使用.l 格式来比较 0x80000000 和 0X80000100 这两个地址数据是否相等,比较长度为 0x10 个内存块(16 * 4=64 个字节),命令如下所示:
=> cmp.l 0x80000000 0x80000100 0x10
Total of 16 word(s) were the same
=>
从图 30.4.3.9 可以看出两段内存的数据相等。 我们再随便挑两段内存比较一下,比如地址0x80002000 和 0x800003000,长度为 0X10,比较结果如图 30.4.3.10 所示
=> cmp.l 0x80002000 0x80003000 0x10
Total of 16 word(s) were the same
=> md.l 0x80002000 0x10
80002000: ffffffff ffffffff ffffffff ffffffff ................
80002010: ffffffff ffffffff ffffffff ffffffff ................
80002020: ffffffff ffffffff ffffffff ffffffff ................
80002030: ffffffff ffffffff ffffffff ffffffff ................
=> md.l 0x80003000 0x10
80003000: ffffffff ffffffff ffffffff ffffffff ................
80003010: ffffffff ffffffff ffffffff ffffffff ................
80003020: ffffffff ffffffff ffffffff ffffffff ................
80003030: ffffffff ffffffff ffffffff ffffffff ................
=>
哈哈🥲,刚好是一样的,因为上电初始化之后DRAM内存里的数据是随机的,可能刚好DRAM中内存里 0x80002000 和 0x80003000 里的数据都是0xff。
5,网络操作命令
uboot 是支持网络的,我们在移植 uboot 的时候一般都要调通网络功能,因为在移植 linux kernel 的时候需要使用到 uboot 的网络功能做调试。uboot 支持大量的网络相关命令,比如 dhcp, ping、 nfs 和 tftpboot,我们接下来依次学习一下这几个和网络有关的命令。
在使用 uboot 的网络功能之前先用网线将开发板的 ENET2 接口和电脑或者路由器连接起来, I.MX6U-ALPHA 开发板有两个网口: ENET1 和 ENET2,一定要连接 ENET2,不能连接错了, ENET2 接口如图 30.4.4.1 所示。
建议开发板和主机 PC 都连接到同一个路由器上!最后设置表 30.4.4.1 中所示的几个环境变量
=> setenv ipaddr 192.168.1.50
=> setenv ethaddr b8:ae:1d:01:00:00
=> setenv gatewayip 192.168.1.1
=> setenv netmask 255.255.255.0
=> setenv serverip 192.168.1.253
=> saveenv
注意,网络地址环境变量的设置要根据自己的实际情况,确保 Ubuntu 主机和开发板的 IP地址在同一个网段内,比如我现在的开发板和电脑都在 192.168.1.0 这个网段内,所以设置开发板的 IP 地址为 192.168.1.50,我的 Ubuntu 主机的地址为 192.168.1.253,因此 serverip 就是
192.168.1.253。 ethaddr 为网络 MAC 地址,是一个 48bit 的地址,如果在同一个网段内有多个开发板的话一定要保证每个开发板的 ethaddr 是不同的,否则通信会有问题!设置好网络相关的环境变量以后就可以使用网络相关命令了。
1,ping命令
开发板的网络能否使用,是否可以和服务器(Ubuntu 主机)进行通信,通过 ping 命令就可以验证,直接 ping 服务器的 IP 地址即可,比如我的服务器 IP 地址为 192.168.1.253,命令如下:
ping 192.168.1.253
从图 30.4.4.2可以看出, ,192.168.1.253这个主机存在,说明 ping成功, uboot的网络工作常。
注意!只能在 uboot中 ping其他的机器,其他机器不能 ping uboot,因为 uboot没有对 ping命令做处理,如果用其他的机器 ping uboot的话会失败!
2、 dhcp命令
dhcp用于从路由器获取 IP地址,前提得开发板连接到路由器上的,如果开发板是和电脑直连的,那么 dhcp命令就会失效。直接输入 dhcp命令即可通过路由器获取到 IP地址,如图30.4.4.3所示:
从图 30.4.4.3可以看出,开发板通过 dhcp获取到的 IP地址为 192.168.1.137。同时在图30.4.4.3中可以看到“ warning no boot file name;”、 TFTP from server 192.168.1.1”这样的字样。这是因为DHCP 不单单是获取 IP地址,其还会通过 TFTP来启动 linux内核。
输入“ “? dhcp”即可查看 dhcp命令详细的信息,如图 30.4.4.4所示:
3、 nfs命令
nfs(Network File System)网络文件系统,通过 nfs可以在计算机之间通过网络来分享资源,比如我们将 linux 镜像和设备树文件放到 Ubuntu中,然后在 uboot中使用 nfs命令将 Ubuntu中的 linux 镜像和设备树下载到开发板的 DRAM中。这样做的目的是为了方便调试 linux镜像和设备树,也就是网络调试,通过网络调试是 Linux开发中最常用的调试方法。原因是嵌入式 linux开发不像单片机开发,可以直接通过 JLINK或 STLink等仿真器将代码直接烧写到单片机内部的 flash中,嵌入式Linux通常是烧写到 EMMC、 NAND Flash、 SPI Flash等外置 flash中,但是嵌入式 Linux开发也没有 MDK IAR这样的 IDE,更没有烧写算法,因此不可能通过点击一个download”按钮就将固件烧写到外部 flash中。虽然半导体厂商一般都会提供一个烧写固件的软件,但是这个软件使用起来比较复杂,这个烧写软件一般用于量产的。其远没有 MDK、 IAR的一键下载方便,在 Linux内核调阶段,如果用这个烧写软件的话将会非常浪费时间,而这个时候网络调试的优势就显现出来了,可以通过网络将编译好的 linux镜像和设备树文件下载到 DRAM中,然后就可以直接运行。
我们一般使用 uboot中的 nfs命令将 Ubuntu中的文件下载到开发板的 DRAM中,在使用之前需要开启 Ubuntu主机的 NFS服务 ,并且要新建一个 NFS使用的目录,以后所有要通过NFS访问的文件都需要放到这个 NFS目录中。 Ubuntu的 NFS服务开启我们在 4.2.1小节已经详细讲解过了,包括 NFS文件目录的创建,如果忘记的话可以去查看一下 4.2.1小节。我设置的 /home/zuozhongkai/linux/nfs这个目录为我的 NFS文件目录。 uboot中的 nfs命令格式如下所
nfs [loadAddress] [[hostIPaddr:]bootfilename]
loadAddress是要保存的 DRAM地址, ,[[hostIPaddr:]bootfilename]是要下载的文件地址。这里我们将正点原子官方编译出来的 Linux镜像文件 zImage下载到开发板 DRAM的 0x80800000这个址处。正点原子编译出来的 zImage文件已经放到了开发板光盘中。将文件zImage通过 FileZilla发送到 Ubuntu中的 NFS目录下,比如我的就是放到/home/zuozhongkai/linux/nfs这个目录下,完成以后的 NFS目录如图 30.4.4.5所示:
准备好以后就可以使用 nfs命令来将 zImage下载到开发板 DRAM的 0X80800000地址处,命令如下:
nfs 0x80800000 192.168.1.253:/home/zuozhongkai/linux/nfs/zImage
- 命令中的“ 80800000”表示 zImage保存地址
- 192.168.1.253:/home/zuozhongkai/linux/nfs/zImage” 表示 zImage在 192.168.1.253这个主机中,路径为 /home/zuozhongkai/linux/nfs/zImage。
下载过程如图 30.4.4.6所示:
在图 30.4.4.6中会以“ “#”提示下载过程,下载完成以后会提示下载的数据大小,这里下载的 6785272字节 (出厂系统在不断的更更新中,因此以实际的 zImage大小为准 ),而 zImage的大小就是 6785272字节,如图 30.4.4.7所 示:
下载完成以后查看 0x80800000地址处的数据,使用命令 md.b来查看前 0x100个字节的数据,如图 30.4.4.8所示:
在使用 winhex软件来查看 zImage,检查一下前面的数据是否和图 30.4.4.8只的一致,结果如图 30.4.4.9所示:
可以看出图 30.4.4.8和图 30.4.4.9中的前 100个字节的数据一致,说明 nfs命令下载到的
zImage是正确的。
4、 tftp 命令
tftp命令的作用和 nfs命令一样,都是用于通过网络下载东西到 DRAM中,只是 tftp命令使用的 TFTP协议, Ubuntu主机作为 TFTP服务器。因此需要在 Ubuntu上搭建 TFTP服务器,需要安装 tftp-hpa和 tftpd-hpa,命令如下
sudo apt update
sudo apt install tftpd-hpa
和 NFS一样, TFTP也需要一个文件夹来存放文件,在用户目录下新建一个目录,命令如下:
mkdir /home/zuozhongkai/linux/tftpboot
chmod 777 /home/zuozhongkai/linux/tftpboot
这样我就在我的电脑上创建了一个名为 tftpboot的目录 (文件夹 ),路径为 /home/zuozhongkai/linux/tftpboot。注意!我们要给 tftpboot文件夹权限,否则的话 uboot不能从tftpboot文件夹里面下载文件。
最后配置 tftp,安装完成以后新建文件 /etc/xinetd.d/tftp 如果没有 /etc/xinetd.d目录的话自行创建, 然后在里面输入如下内容:
sudo nano /etc/default/tftpd-hpa
然后输入如下内容
# /etc/default/tftpd-hpa
TFTP_OPTIONS="--secure"
TFTP_DIRECTORY="/var/lib/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_USERNAME="tftp"
TFTP_TYPE="regular"
完成配置后,重启 TFTP 服务以应用更改:
sudo systemctl restart tftpd-hpa
确保服务在启动时自动运行:
sudo systemctl enable tftpd-hpa
测试 TFTP 服务器
你可以使用 tftp
客户端工具来测试 TFTP 服务器。在另一台机器上或本机上,可以使用以下命令连接到 TFTP 服务器:
tftp localhost
在 TFTP 提示符下,你可以尝试下载或上传文件。例如:
tftp> get filename.txt
或者上传文件:
tftp> put localfile.txt
万事俱备,只剩验证了, uboot中的 tftp命令格式如下:
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
看起来和 nfs命令格式一样的, loadAddress是文件在 DRAM中的存放地址,
[[hostIPaddr:]bootfilename]是要从 Ubuntu中下载的文件。但是和 nfs命令的区别在于, tftp命令不需要输入文件在 Ubuntu中的完整路径,只需要输入文件名即可。比如我们现在将 tftpboot文件夹里面的 zImage文件下载到开发板 DRAM的 0X80800000地址处,命令如下:
tftp 0x80800000 zImage
从图 30.4.4.10可以看出, zImage下载成功了,网速为 1.4MibB/s,文件大小为 6071136字节。同样的,可以使用 md.b命令来查看前 100个字节的数据是否和图 30.4.4.9中的相等。有时候使用 tftp命令从 Ubuntu中下载文件的时候会出现如图 30.4.4.11所示的错误提示:
在图 30.4.4.11中可以看到 TFTP error: 'Permission denied' (0)”这样的错误提示,提示没有权限,出现这个错误一般 有两个原因:
①、在 Ubuntu中创建 tftpboot目录的时候没有给予 tftboot相应的权限。
②、 tftpboot目录中要下载的文件没有给予相应的权限。
针对上述两个问题,使用命令“ chmod 777 xxx”来给予权限,其中 xxx”就是要给予权限的文件或文件夹。
好了, uboot中关于网络的命令就讲解到这里,我们最常用的就是 ping、 nfs和 tftp这三个命令。使用 ping命令来查看网络的连接状态,使用 nfs和 tftp命令来从 Ubuntu主机中下载文件。
6,EMMC和 SD卡操作 命令
uboot支持 EMMC和 SD卡,因此也要提供 EMMC和 SD卡的操作命令。一般认为 EMMC和 SD是同一个东西,所以没有特殊说明,本教程统一使用 MMC来代指 EMMC和 SD卡。uboot中常用于操作 MMC设备的命令为“ mmc”。
mmc是一系列的命令,其后可以跟不同的参数,输入“? mmc”即可查看 mmc有关的命令,如图 30.4.5.1所示:
从图 30.4.5.1可以看出, mmc后面跟不同的参数可以实现不同的功能,如表 30.4.5.1所示:
命令 | 描述 |
mmc info | 输出MMC设备信息 |
mmc read | 读取MMC中的数据。 |
mmc wirte | 向MMC设备写入数据。 |
mmc rescan | 扫描MMC设备。 |
mmc part | 列出MMC设备的分区。 |
mmc dev | 切换MMC设备。 |
mmc list | 列出当前有效的所有MMC设备。 |
mmc hwpartition | 设置MMC设备的分区。 |
mmc bootbus…… | 设置指定MMC设备的 BOOT_BUS_WIDTH域的值。 |
mmc partconf…… | 设置指定MMC设备的 PARTITION_CONFG域的值。 |
mmc rst | 复位MMC设备 |
mmc setdsr | 设置DSR寄存器的值。 |
1,mmc info命令
mmc info命令用于输出当前选中的 mmc info设备的信息,输入命令“ mmc info”即可,如图 30.4.5.2所示:
从图 30.4.5.2可以看出,当前选中的 MMC设备是 EMMC,版本为 5.0,容量为 7.1GiB(EMMC为8GB),速度为 52000000Hz=52MHz 8位宽的总线。还有一个与 mmc info命令相同功能的命令: mmcinfo,"mmc”和 info”之间没有空格。 实际量产的 EMMC核心板所使用的 EMMC芯片是多厂商供应的,因此 EMMC信息以实际为准 ,但是容量都为 8GB的。
2,mmc rescan命令
mmc rescan命令用于扫描当前开发板上所有的 MMC设备,包括 EMMC和 SD卡,输入mmc rescan”即可。
3,mmc list命令
mmc list命令用于来查看当前开发板一共有几个 MMC设备,输入“ mmc list”,结果如图30.4.5.3所示:
可以看出当前开发板有两个 MMC设备: FSL_SDHC:0和 FSL_SDHC:1 (eMMC),这是因为我现在用的是 EMMC版本的核心板,加上 SD卡一共有两个 MMC设备,
- FSL_SDHC:0是 SD卡
- FSL_SDHC:1(eMMC)是 EMMC。
默认会将 EMMC设置为当前 MMC设备,这就是为什么输入“ mmc info”查询到的是 EMMC设备信息,而不是 SD卡。要想查看 SD卡信息,就要使用命令“ mmc dev”来将 SD卡设置为当前的 MMC设备。
4、 mmc dev命令
mmc dev命令用于切换当前 MMC设备,命令格式如下:
mmc dev [dev] [part]
[dev]用来设置要切换的 MMC设备号,,[part]是分区号。如果不写分区号的话默认为分区 0
使用如下命令切换到 SD卡:
mmc dev 0 //切换到 SD卡, 0为 SD卡, 1为 eMMC
结果如图 30.4.5.4所示:
从图 30.4.5.4可以看出,切换到 SD卡成功, mmc0为当前的 MMC设备,输入命令“ mmc info”即可查看 SD卡的信息,结果如图 30.4.5.5所示:
从图 30.4.5.5可以看出当前 SD卡为 3.0版本的,容量为 14.8GiB(16GB的 SD卡 4位宽的总线。
5,mmc part命令
有时候 SD卡或者 EMMC会有多个分区,可以使用命令“ mmc part”来查看其分区,比如查看 EMMC的分区情况,输入如下命令:
mmc dev 1 //切换到 EMMC
mmc part //查看 EMMC分区
结果如图 30.4.5.6所示:
从图 30.4.5.6中可以看出,此时 EMMC有两个分区, 第一个分区起始扇区为 20480,长度为 262144个扇区; 第二个分区 起始扇区为 282624,长度为 14594048个扇区 。如果 EMMC里面烧写了 Linux系统的话, EMMC是有 3个分区的,第 0个分区存放 uboot,第 1个分区存放Linux镜像文件和设备树,第 2个分区存放根文件系统。但是在图 30.4.5.6中只有两个分区,那是因为第 0个分区没 有格式化,所以识别不出来,实际上第 0个 分区是存在的。一个 新 的 SD卡 默认只有一分区,那就是分区 0,所以前面讲解的 uboot烧写 到 SD卡 ,其实就是将 u-boot.bin烧写 到了 SD卡的分区 0里面 。后面学习 Linux内核移植的时候再讲解怎么在 SD卡中创建并格式化第二个分区,并将 Linux镜像文件和设备树文件存放到第二个分区中。
如果要将 EMMC的分区 2设置为当前 MMC设备 ,可以使用如下命令
mmc dev 1 2
结果如图 30.4.5.7所示:
6, mmc read命令
mmc read命令用于读取 mmc设备的数据,命令格式如下:
mmc read addr blk# cnt
addr是数据读取到 DRAM中的地址, blk是要读取的块起始地址 (十六进制 ),一个块是 512字节这里的块和扇区是一个意思,在 MMC设备中我们通常说扇区, cnt是要读取的块数量 (十六进制 )。比如从 EMMC的第 1536(0x600)个块开始,读取 16(0x10)个块的数据到 DRAM的0X80800000地址处,命令如下:
mmc dev 1 0 //切换到 MMC分区 0
mmc read 0x80800000 0x600 0x10 //读取数据
结果如图 30.4.5.8所示:
这里我们还看不出来读取是否正确,通过 md.b命令查看 0x80800000处的数据就行了,查看 16*512=8192(0x2000)个字节的数据,命令如下:
md.b 0x80800000 0x2000
结果如图 30.4.5.9所示:
从图 30.4.5.9可以看到“ baudrate=115200.board_name=EVK.board_rev=14X14.”等字样这个就是 uboot中的环境变量。 EMMC核心板 uboot环境 变量的存储起始地址就是1536*512=786432。
7,mmc write命令
要将数据写到 MMC设备里面,可以使用命令“ mmc write”,格式如下
mmc write addr blk# cnt
addr是要写入 MMC中的数据在 DRAM中的起始地址, blk是要写入 MMC的块起始地址(十六进制), cnt是要写入的块大小,一个块为 512字节。
我们可以使用命令“ mmc write”来 升级 uboot,也就是在 uboot中更新 uboot。这里要用到 nfs或者 tftp命令,通过 nfs或者 tftp命令将新的 u-boot.bin下载到开发板的 DRAM中,然后再使用命令“ mmc write”将其写入到 MMC设备中。我们就来更新一下 SD中的 uboot,先查看一下 SD卡中的 uboot版本号,注意编译时间,输入命令:
mmc dev 0 //切换到 SD卡
version //查看版本号
可以看出当前 SD卡中的 uboot是 2020年 3月 12日 15:11:51编译的。我们现在重新编译一下 uboot,然后将编译出来的 u-boot.imx(u-boot.bin前面加了一些头文件 )拷贝到 Ubuntu中的tftpboot目录下。最后使用 tftp命令将其下载到 0x80800000地址处,命令如下:
tftp 80800000 u-boot.imx
下载过程如图 30.4.5.11所示:
可以看出, u-boot.imx大小为 379904字节, ,379904/512=742,所以我们要向 SD卡中写入742个块,如果有小数的话就要加 1个块。使用命令“ mmc write”从 SD卡分区 0第 2个块 (扇区 )开始烧写,一共烧写 742(0x2E6)个块,命令如下:
SD卡的前两个扇区包含了重要的引导信息和分区表。
SD 卡的前两个扇区通常保存着与引导和分区相关的关键信息。第一个扇区主要用于引导和描述各分区,而第二个扇区则可以包含更多关于文件系统或额外分区的信息。
mmc dev 0 0 mmc write 80800000 2 32E
烧写过程如图 30.4.5.12所示:
烧写成功,重启开发板 (从 SD卡启动 ),重启以后再输入 version来查看版本号,结果如图30.4.5.13所示:
从图 30.4.5.13可以看出,此时的 uboot是 2020年 10月 27号 11:44:31编译的 说明 uboot更新功。这里我们就学会了如何在 uboot中更新 uboot了,如果要更新 EMMC中的 uboot也是一样的。
同理,如果要在 uboot中更新 EMMC对应的 uboot,可以使用如下所示命令
mmc dev 1 0 //切换到 EMMC分区 0
tftp 80800000 u-boot.imx //下载 u-boot.imx到 DRAM
mmc write 80800000 2 32E //烧写 u-boot.imx到 EMMC中
mmc partconf 1 1 0 0 /分区配置, EMMC需要这一步!
千万不要写 SD卡或者 EMMC的前两个块 (扇区 ),里面保存着分区表
8,mmc erase命令
如果要擦除 MMC设备的指定块就是用命令“ mmc erase”,命令格式如下
mmc erase blk# cnt
blk为要擦除的起始块, cnt是要擦除的数量。
没事不要用 mmc erase来擦除 MMC设备!!!
关于 MMC设备相关的命令就讲解到这里,表 30.4.5.1中还有一些跟 MMC设备操作有关的命令,但是很少用到,这里就不讲解了,感兴趣的可以上网查一下,或者在 uboot中查看这些命令的使用方法。
7,FAT格式文件系统操作命令
有时候需要在 uboot中对 SD卡或者 EMMC中存储的文件进行操作,这时候就要用到文件操作命令,跟文件操作相关的命令有: fatinfo、 fatls、 fstype、 fatload和 fatwrite,但是这些文件操作命令只支持 FAT格式的文件系统!!
,跟文件操作相关的命令有:fatinfo、 fatls、 fstype、 fatload和 fatwrite
fatinfo命令
fatinfo命令用于查询指定 MMC设备 分区的文件系统信息,格式如下:
fatinfo <interface> [<dev[:part]>]
- interface表示接口,比如 mmc dev是查询的设备号,
- part是要查询的分区。
比如我们要查询 EMMC分区 1的文件系统信息,命令如下:
fatinfo mmc 1:1
结果如图 30.4.6.1所示:
从上图可以看出, EMMC分区 1的文件系统为 FAT32格式的。
fatls命令
atls命令用于查询 FAT格式设备的目录和文件信息,命令格 式如下:
fatls <interface> [<dev[:part]>] [directory]
interface是要查询的接口,比如 mmc dev是要查询的设备号, part是要查询的分区, directory是要查询的目录。比如查询 EMMC分区 1中的所有的目录和文件,输入命令:
fatls mmc 1:1
结果如图 30.4.6.2所示:
从上图可以看出, emmc的分区 1中存放着 8个文件
fstype命令
fstype用于查看 MMC设备某个分区的文件系统格式,命令格式如下:
fstype <interface> <dev>:<part>
正点原子 EMMC核心板上的 EMMC默认有 3个分区,我们来查看一下这三个分区的文件系统格式,输入命令:
fstype mmc 1:0
fstype mmc 1:1
fstype mmc 1:2
结果如图 30.4.6.3所示:
从上图可以看出,分区 0格式未知,因为分区 0存放的 uboot,并且分区 0没有格式化,所以文件系统格式未知。分区 1的格式为 fat,分区 1用于存放 linux镜像和设备树。分区 2的格式为 ext4,用于存放 Linux的 根 文件系统 (rootfs)
fatload命令
fatload命令用于将指定的文件读取到 DRAM中,命令格式如下:
fatload <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]
- interface为接口,比如 mmc,
- dev是设备号,
- part是分区,
- addr是保存在 DRAM中的起始地址,
- filename是要读取的文件名字。
- bytes表示读取多少字节的数据,如果 bytes为 0或者省略的话表示读取整个文件。
- pos是要读的文件相对于文件首地址的偏移,如果为 0或者省略的话表示从文件首地址开始读取
我们将 EMMC分区 1中的 zImage文件读取到 DRAM中的0X80800000地址处,命令如下:
fatload mmc 1:1 80800000 zImage
操作过程如图 30.4.6.4所示:
从上图可以看出在 225ms内读取了 6785272个字节的数据,速度为 28.8MiB/s,速度是非常快的,因为这是从 EMMC里面读取的,而 EMMC是 8位的,速度肯定会很快的。
fatwrite命令
注意 uboot默认没有使能 fatwrite命令 ,需要 修改板子配置头文件,
- 比如 mx6ullevk.h、
- mx6ull_alientek_emmc.h等等,
- 板子不同,其配置头文件也不同。
找到自己开发板对应的配置头文件然后添加如下一行宏定义来使能 fatwrite命令:
#define CONFIG_FAT_WRITE /* 使能 fatwrite命令 */
fatwirte命令用于将 DRAM中 的数据写入到 MMC设备中,命令格式如下:
fatwrite <interface> <dev[:part]> <addr> <filename> <bytes>
- interface为接口,比如 mmc dev是设备号,
- part是分区,
- addr是要写入的数据在 DRAM中的起地址,
- filename是写入的数据文件名字,
- bytes表示要写入多少 字节 的数据。
我们可以通过 fatwrite命令在 uboot中更新 linux镜像文件和设备树。我们以更新 linux镜像文件zImage为例,首先将正点原子 I.MX6U-ALPHA开发板提供的 zImage镜像文件拷贝到 Ubuntu中的tftpboot目录下, zImage镜像文件放到了开发板光盘中,路径为:
拷贝完成以后 使用命令 tftp将 zImage下载到 DRAM的 0X80800000地址处,命令如下:
tftp 80800000 zImage
下载过程如图 30.4.6.5所示:
zImage大小为 6785272(0X6788f8)个字节 (注意,由于开发板系统在不断的更新中,因此zImage大小不是固定的,一切以实际大小为准 ),接下来使用命令 fatwrite将其写入到 EMMC的分区 1中,文件名字为 zImage,命令如下
fatwrite mmc 1:1 80800000 zImage 6788f8
结果如图 30.4.6.6所示:
完成以后使用“ fatls”命令查看一下 EMMC分区 1里面的文件,结果如图 30.4.6.7所示:
EXT格式文件系统操作命令
uboot有 ext2和 ext4这两种格式的文件系统的操作命令,
- 常用的就四个命令,分别为:
- ext2load、
- ext2ls、
- ext4load、
- ext4ls
- 和 ext4write。
这些命令的含义和使用与 fatload、 fatls和 fatwrite一样,只是 ext2和 ext4都是针对 ext文件系统的。比如 ext4ls命令, EMMC的分区 2就是 ext4格式 的,使用 ext4ls就可以查询 EMMC的分区 2中的文件和目录,输入命令:
EXT命令的使用方式和FAT命令的使用方式是类似的,这里就不再摘录正点原子官方文档例程里的内容了,可以自行阅读正点原子官方文档例程。
NAND操作命令
uboot是支持 NAND Flash的,所以也有 NAND Flash的操作命令,前提是使用的 NAND版本的核心板,并且编译 NAND核心板对应的 uboot,然后使用 imxdownload软件将 u-boot.bin烧写到 SD卡中,最后通过 SD卡启动。一般情况下 NAND版本的核心板已经烧写好了 uboot、linux kernel和 rootfs这些文件,所以可以将 BOOT拨到 NAND,然后直接从 NAND Flash启动即可。
这里的NAND Flash 指的应该是传统的并口的NAND Flash 。
现在比较常用的是 SPI NAND,串口NAND设备。
NAND版核心板启动信息如图 30.4.8.1所示:
从图 30.4.8.1可以看出,当前开发板的 NAND容量为 512MiB。输入“ “? nand”即可查看
nand info 命令
此命令用户打印 NAND Flash信息,输入“ nand info”,结果如图
中给出了 NAND的页大小、 OOB域大小,擦除大小等信息。可以对照着所使用的 NAND Flash数据手册来查看一下这些信息是否正确。
nand device命令
nand device用于切换 NAND Flash,如果你的板子支持多片 NAND的话就可以使用此命令来设置当前所使用的 NAND。这个需要你的 CPU有两个 NAND控制器,并且两个 NAND控制器各接一片 NAND Flash。就跟 I.MX6U有两个 SDIO接口,这两个 SDIO接口可以接两个 MMC设备一样。不过一般情况下 CPU只有一个 NAND接口,而且在使用中只接一片 NAND。
nand erase命令
nand erase命令用于擦除 NAND Flash,
NAND Flash的特性决定了在向 NAND Flash写数据之前一定要先对要写入的区域进行 擦除。
“ nand erase”命令有三种形式。
nand erase[.spread] [clean] off size //从指定地址开始 (off)开始,擦除指定大小 (size)的区域。
nand erase.part [clean] partition //擦除指定的分区
nand erase.chip [clean] //全篇擦除
NAND的擦除命令一般是配合写命令的,后面讲解 NAND写命令的时候在演示如何使用nand erase”。
nand write命令
此命令用于向 NAND指定地址写入指定的数据,一般和“ nand erase”命令配置使用来更新NAND中的 uboot、 linux kernel或设备树等文件,命令格式如下:
nand write addr off size
addr是要写入的数据首地址, off是 NAND中的目的地址, size是要写入的数据大小。
由于 I.MX6ULL要求 NAND对应的 uboot可执行文件 还需要另外包含 BCB和 DBBT,因此直接编译出来的 uboot.imx不能直接烧写到 NAND里面。
但是我们可以在 uboot里面使用“ nand write”命令烧写 kernel和 dtb。 先编译出来 NAND版本的kernel和 dtb文件,在烧写之前要先对 NAND进行分区,也就是规划好 uboot、 linux kernel、设备树和根文件系统的存储区域, I.MX6U-ALPHA开发板 出厂系统 NAND分区如下
0x000000000000-0x0000003FFFFF : "boot"
0x000000400000-0x00000041FFFF : "env"
0x000000420000-0x00000051FFFF : "logo"
0x000000520000-0x00000061FFFF : "dtb"
0x000000620000-0x000000E1FFFF : "kernel"
0x000000E20000-0x000020000000 : "rootfs"
一共有 六 个分区,
- 第一个分区存放 uboot,地址范围为 0x0~0x3FFFFF(共4MB)
- 第二个分区存放 env(环境变量),地址范围为 0x400000~0x420000(共 128KB);
- 第三个分区存放 logo(启动图标 ),地址范围为 0x420000~0x51FFFF(共
- 第四个分区存放 dtb(设备树 ),地址范围为0x520000~0x61FFFF(共
- 第 五 个分区存放 kernel(也就是 linux kernel),地址范围为
- 0x620000~0xE1FFFF(共 8MB);
- 剩下的所有存储空间全部作为最后一个分区,存放 rootfs(根文件系统 )
可以看出 kernel是从地址 0x620000开始存放的,将 NAND版本 kernel对应的 zImage文件放到 Ubuntu中的 tftpboot目录中,然后使用 tftp命令将其下载到开发板的 0X87800000地址处,最终使用“ nand write”将其烧写到 NAND中,命令如下:
NAND Flash的特性决定了,NAND Flash写之前一定要先擦除。Flash的存储bit位只能从1写为0,不能从0写为1,Flash擦除之后全为1。
tftp 0x87800000 zImage //下载 zImage到 DRAM中
nand erase 0x620000 0x800000 //从地址 0x620000开始擦除 8MB的空间
nand write 0x87800000 0x620000 0x800000 //将接收到的 zImage写到 NAND中
这里我们 擦除 了 8MB的空间,因为一般 zImage就是 6,7MB左右, 8MB肯定够了,如果不够的话就 再 多擦除一点就行了。
同理, 最后烧写设备树 (dtb)文件文件,命令如下:
tftp 0x87800000 imx6ull-14x14-emmc-7-1024x600-c.dtb //下载 dtb到 DRAM中
nand erase 0x520000 0x100000 //从地址 0x520000开始擦除 1MB的空间
nand write 0x87800000 0x520000 0x100000 //将接收到的 dtb写到 NAND中
dtb文件一般只有几十 KB,所以擦除 1M是绰绰有余的了。 注意!正点原子出厂系统在NAND里面烧写了很多种设备树文件! 这里只是举例烧写一种的方法,我们在实际产品开发中肯定只有一种设备树。
根文件系统 (rootfs)就不要在 uboot中更新了,还是使用 NXP提供的 Mfgtool工具来烧写,因为根文件系统太大!很有可能超过开发板 DRAM的大小,这样连下载都没法下载,更别说更新了。
nand read命令
此命令用于从 NAND中的指定地址读取指定大小的数据到 DRAM中,命令格式如下:
nand read addr off size
addr是目的地址, off是要读取的 NAND中的数据源地址, size是要读取的数据大小。比如我们读取设备树 (dtb)文件到 0x83000000地址处,命令如下:
nand read 0x83000000 0x520000 0x19000
过程如图 30.4.8.4所示:
设备树文件读取到 DRAM中以后就可以使用 fdt命令来对设备树进行操作了,首先设置 fdt的地址, fdt地址就是 DRAM中设备树的首地址,命令如下:
fdt addr 83000000
设置好以后可以使用“ fdt header”来查看设备树的头信息,输入命令
fdt header
输入命令“ fdt print”就可以查看设备树文件的内容,输入命令
fdt print
8, BOOT操作命令
uboot的本质工作是引导 Linux,所以 uboot肯定有相关的 boot(引导 )命令来启动 Linux。常用的跟 boot有关的命令有:
bootz、 bootm,和 boot。
bootz命令
要启动 Linux,需要先将 Linux镜像文件拷贝到 DRAM中,如果使用到设备树的话也需要将设备树拷贝到 DRAM中。可以从 EMMC或者 NAND等存储设备中将 Linux镜像和设备树文件拷贝到DRAM,也可以通过 nfs或者 tftp将 Linux镜像文件和设备树文件下载到 DRAM中。不管用那种法,只要能将 Linux镜像和设备树文件存到 DRAM中就行,然后使用 bootz命令来启动,
bootz命令用于 启动 zImage镜像文件, bootz命令格式如下:
bootz [addr [initrd[:size]] [fdt]]
命令 bootz有三个参数,
- addr是 Linux镜像文件在 DRAM中的位置
- initrd是 initrd文件在DRAM中的地址,如果不使用 initrd的话使用‘ ‘-’代替即可
- fdt就是设备树文件在 DRAM中的地址
。现在我们使用网络和 EMMC两种方法来启动 Linux系统,首先将 I.MX6U-ALPHA开发板的 Linux镜像和设备树发送到 Ubuntu主机中的 tftpboot文件夹下。 Linux镜像文件前面已经放到了 tftpboot文件夹中,现在把设备树文件放到 tftpboot文件夹里面。 由于不同的屏幕其设备树不同,因此我们出厂系统提供了很多设备树。
可以看出,我们(正点原子)提供了 14种设备树, 笔者 正在使用的是 EMMC核心板, ,7寸1024××600分辨率的屏幕,所以需要使用 imx6ull-14x14-emmc-7-1024x600-c.dtb这个设备树。 将imx6ull-14x14-emmc-7-1024x600-c.dtb发 送到 Ubuntu主机中的 tftpboot文件夹里面,完成以后的 tftpboot文件夹如图 30.4.9.2所示:
Linux镜像文件和设备树都准备好了,我们先学习如何通过网络启动 Linux,使用 tftp命令将 zImage下载到 DRAM的 0X80800000地址处,然后将设备树 imx6ull-14x14-emmc-7-1024x600c.dtb下载到 DRAM中的 0X83000000地址处,最后之后命令 bootz启动,命令如下:
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb
bootz 80800000 - 83000000
命令运行结果如图 3
上图就是我们通过 tftp和 bootz命令来从网络启动 Linux系统,如果我们要从 EMMC中启动 Linux系统的话只需要使用命令 fatload将 zImage和 imx6ull-14x14-emmc-7-1024x600-c.dtb从EMMC的分区 1中拷贝到 DRAM中,然后使用命令 bootz启动即可。先使用命令 fatls查看要下 EMMC的分区 1中有没有 Linux镜像文件和设备树文件,如果没有的话参考 30.4.6小节中讲解的 fatwrite命令将 tftpboot中的 zImage和 imx6ull-14x14-emmc-7-1024x600-c.dtb文件烧写到 EMMC的分区 1中。然后使用命令 fatload将 zImage和 imx6ull-14x14-emmc-7-1024x600-c.dtb文件拷贝到 DRAM中,地址分别为 0X80800000和 0X83000000,最后使用 bootz启动,命令如下:
fatload mmc 1:1 80800000 zImage
fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb
bootz 80800000 - 83000000
bootm命令
bootm和 bootz功能类似,但是 bootm用于启动 uImage镜像文件。如果不使用设备树的话启动 Linux内核的命令如下:
bootm addr
addr是 uImage镜像在 DRAM中的首地址。
如果要使用设备树,那么 bootm命令和 bootz一样,命令格式如下:
bootm [addr [initrd[:size]] [fdt]]
- 其中 addr是 uImage在 DRAM中的首地址,
- initrd是 initrd的地址,
- fdt是设备树 (.dtb)文件在 DRAM中的首地址,如果 initrd为空的话,同样是用“ “-”来替代。
boot命令
boot命令也是用来启动 Linux系统的,只是 boot会读取环境变量 bootcmd来启动 Linux系统, bootcmd是一个很重要的环境变量!
其名字分为“ boot”和 cmd”,也就是“引导”和“命令”,说明这个环境变量保存着引导命令,其实就是 启动的命令集合,具体的引导命令内容是可以修改的。比如我们要想使用 tftp命令从网络启动 Linux那么就可以设置 bootcmd为“ tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; bootz 80800000 - 83000000”,然后使用saveenv将 bootcmd保存起来。然后直接输入 boot命令即可从网络启动Linux系统,命令如下:
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; bootz 80800000 - 83000000'
saveenv boot
运行结果如图 30.4.9.5所示:
前面说过 uboot倒计时结束以后就会启动 Linux系统,其实就是执行的 bootcmd中的启动命令。只要不修改 bootcmd中的内容,以后每次开机 uboot倒计时结束以后都会使用 tftp命令从网络下载 zImage和 imx6ull-14x14-emmc-7-1024x600-c.dtb,然后启动 Linux。
如果想从 EMMC启动那就设置 bootcmd为“ fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; bootz 80800000 - 83000000”,然后使用 boot命令启动即可,命令如下:
setenv bootcmd 'fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; bootz 80800000 - 83000000'
savenev boot
如果不修改 bootcmd的话,每次开机 uboot倒计时结束以后都会自动从 EMMC里面读取zImage和 imx6ull-14x14-emmc-7-1024x600-c.dtb,然后启动 Linux。
在启动 Linux内核的时候 可能会 遇到如下错误:
Kernel panic not Syncing: VFS: Unable to mount root fs on unknown-block(
这个错误的原因是 linux内核没有找到根文件系统,这个很正常,因为没有设置 uboot的bootargs环境变量,关于 bootargs环境变量后面会讲解!此处我们重点是验证 boot命令, linux内核已经成 功启动了,说明 boot命令工作正常。
9,其他常用命令
uboot中还有其他一些常用的命令,比如 reset、 go、 run和 mtest