[IMX][UBoot] 01.UBoot 常用命令
目录
1.UBoot
1.1.IMX UBoot 编译
1.1.1.编译报错处理 - conflicting types for fdt64_t
1.1.2.编译输出 & 创建编译脚本
1.2.UBoot 烧录与启动
1.2.1.UBoot 启动报错解决
2.UBoot 命令
2.1.检查 UBoot 所支持的命令 - help
2.2.信息查询
2.2.1.查看开发板信息 - bdinfo
2.2.2.查看环境变量 - printenv
2.2.3.查看 UBoot 版本号 - version
2.3.操作环境变量
2.3.1.修改环境变量 - setenv 和 saveenv
2.3.2.新建环境变量 - setenv
2.3.3.删除环境变量 - setenv
2.4.内存操作
2.4.1.显示内存值 - md
2.4.2.修改内存中的值 - nm
2.4.3.自增修改内存中的值 - mm
2.4.4.内存填充 - mw
2.4.5.内存复制 - cp
2.4.6.比较内存中的值是否相等 - cmp
2.5.以太网相关操作
2.5.0.Ubuntu VMware 配置
2.5.1.测试是否可以通信 - ping
2.5.2.从路由器获取 IP 地址 - dhcp
2.5.3.传输文件 - nfs
2.5.4.传输文件 - tftp
2.6.SD/EMMC 操作
2.6.1.查看 MMC 设备信息 - mmc info
2.6.2.扫描开发板上所有的 MMC 设备 - mmc rescan
2.6.3.查看开发板上共有几个 MMC 设备 - mmc list
2.6.4.切换 MMC 设备 - mmc dev
2.6.5.设备分区 - mmc part
2.6.6.读取 MMC 设备中的数据 - mmc read
2.6.7.将数据写入 MMC 设备 - mmc write
2.6.8.擦除 MMC 的指定扇区 - mmc erase
2.7.FAT 文件系统操作
2.7.1.查看 MMC 使用的文件系统 - fatinfo
2.7.2.查询目录和文件信息 - fatls
2.7.3.查看 MMC 设备分区的文件系统 - fstype
2.7.4.将文件读取至 DRAM - fatload
2.7.5.将 DRAM 中的数据写入 EMMC - fatwrite
2.8.EXT 文件系统操作
2.9.NAND 操作命令
2.9.1.查看 NAND Flash 信息 - nand info
2.9.2.切换 NAND Flash 设备 - nand device
2.9.3.擦除 NAND Flash - nand erase
2.9.4.向 NAND 指定位置写入数据 - nand write
2.9.5.从 NAND 中读取数据到 DRAM - nand read
2.10.BOOT 操作
2.10.1.启动系统 - bootz
2.10.2.读取环境变量并启动系统 - bootm
2.10.3.读取引导变量启动系统 - boot
2.11.其他常用命令
2.11.1.复位 - reset
2.11.2.跳转至指定地址执行 - go
2.11.3.运行环境变量中定义的命令 - run
2.11.4.内存读写测试 - mtest
BootLoader 代码用于启动 Linux 内核,常用的有 UBoot、UEFI 等
BootLoader 初始化 DDR 等基本外设,然后将 Linux 内核从 Flash (NAND、SD、MMC 等) 拷贝至 DDR,最后启动 Linux 内核
1.UBoot
UBoot (Universal BootLoader) 是一个裸机代码,可以视为一个综合裸机例程,遵循 GPL 开源协议
UBoot 官网:https://docs.u-boot.org/en/latest/
点击 Obtaining the source 下载官方 UBoot:
-
官方 UBoot 是给 SoC 厂商准备的,一般不会直接使用;
-
SoC 厂商基于官方 UBoot 进行修改,以适配自家的 SoC,该 UBoot 用于 SoC 厂商提供的核心验证板;
-
平台厂商根据自身平台的差异,再次对 SoC 厂商提供的 UBoot 进行修改,以支持平台自身的各种硬件/外设;
这三类 UBoot 的区别如下图所示:
1.1.IMX UBoot 编译
编译 UBoot 前需要先安装 ncurses 库,安装命令如下:
sudo apt-get install libncurses5-dev
手中的开发板内存大小为 512M + 8G (EMMC 版本),对应的编译命令如下:
make 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- -j12
-
ARCH=:指定架构信息,ARCH=arm 指定为 ARM 架构;
-
CROSS_COMPILE=:指定交叉编译器,CROSS_COMPILE=arm-linux-gnueabihf- 指定 ARM 交叉编译工具链;
-
distclean:表示清除工程,一般在第一次编译前建议先清除一下工程;
-
mx6ull_14x14_ddr512_emmc_defconfig:用于配置 UBoot 的配置文件;
-
V=1:表示使用 Verbose 模式,显示编译过程中的详细信息;
-
-j12:表示使用 12 个线程进行编译;
1.1.1.编译报错处理 - conflicting types for fdt64_t
编译时报错:error: conflicting types for ‘fdt64_t’; have ‘uint64_t’ {aka ‘long unsigned int’}
这是因为版本较高的 Ubuntu 自带的 libfdt 库比较新,但 I.MX6U 的 UBoot 比较老旧,因此出现了一些兼容问题
解决方法是先卸载本机的 libfdt-dev,使用 UBoot 自带的 libfdt:
sudo apt-get remove libfdt-devsudo apt autoremove
后续需要新版的 libfdt 库时重新安装即可:
sudo apt-get install libfdt-dev
1.1.2.编译输出 & 创建编译脚本
编译成功后显示如下信息:
同时,会在当前目录下生成 .bin 等文件:
-
u-boot.bin 为编译得到的 UBoot 二进制文件;
-
UBoot 是个裸机程序,因此需要在其前面加上头部数据 (IVT、DCD 等) 才能在 I.MX6U 上运行;
-
u-boot.imx 文件为添加完头部以后的 u-boot.bin,u-boot.imx 为最终要烧写到开发板中的 UBoot 镜像文件;
为了编译方便,新建编译脚本:imx6ull_alientek_emmc.sh
#!/bin/bash
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
为脚本赋予可执行权限:
chmod +x imx6ull_alientek_emmc.sh
这样,后续直接运行该脚本便可进行编译:
./imx6ull_alientek_emmc.sh
1.2.UBoot 烧录与启动
将 UBoot 烧写到 SD 卡中,通过 SD 卡启动运行 UBoot:
./imxdownload u-boot.bin /dev/sdb // 具体烧写至哪个 SD 卡要依实际情况而定
开发板上电后,串口显示如下信息:
UBoot 启动后会进行 3s 的倒计时,3s 内如果没有按下回车键,则会使用默认的参数启动 Linux 内核,如果在倒计时结束前按下回车,则会进入 UBoot 的命令行模式:
进入 UBoot 的命令行模式后,左侧会出现一个 => 标志
UBoot 启动时打印的相关信息如下:
-
U-Boot 2016.03 (Jun 03 2025 - 10:11:08 +0800):UBoot 的版本号和编译时间;
-
CPU: Freescale i.MX6ULL rev1.1 792 MHz (running at 396 MHz):CPU 名称、版本、运行频率;
-
CPU: Industrial temperature grade (-40C to 105C) at 45C:CPU 可运行的温度范围及当前的 CPU 温度;
-
Reset cause: POR:复位原因,POR_B 引脚拉低导致复位;
-
Board: I.MX6U ALPHA|MINI:开发板名称;
-
I2C: ready:I2C 准备就绪;
-
DRAM: 512 MiB:内存大小,512M;
-
MMC: FSL_SDHC: 0, FSL_SDHC: 1:SD/EMMC 控制器信息,当前有两个 SD/EMMC 控制器 FSL_SDHC(0) 和 FSL_SDHC(1),I.MX6ULL 支持两个 SD/EMMC,正点原子 I.MX6ULL EMMC 核心板上 FSL_SDHC(0) 连接 SD (TF) 卡, FSL_SDHC(1) 连接 EMMC;
-
In/Out/Err serial:输入/输出和错误输出的终端选择为串口;
-
switch to partitions #0, OK 和 mmc0 is current device:切换至 EMMC 的第 0 个分区,因为当前的 UBoot 为 EMMC 版本,也就是从 EMMC 启动,只是为了方便将其烧写到 SD 卡中,但是其本质还是 EMMC,所以 UBoot 启动后会将 EMMC 作为默认存储器;
-
Net: FEC1:当前使用 FEC1 网口;
-
Error: FEC1 address not set.:网卡地址未设置;
-
Normal Boot:正常启动,准备从 EMMC 中读取环境变量和参数启动 Linux 内核;
-
Hit any key to stop autoboot: 0:倒计时提示,默认 3s 倒计时,结束前按下回车键进入命令行模式;
1.2.1.UBoot 启动报错解决
UBoot 启动时会出现报错:bad CRC, using default environment
这是由于环境变量错误导致,切换为默认的环境变量即可解决,在 UBoot 命令行中依次输入以下命令:
env default -a // 切换为默认环境变量saveenv // 保存环境变量
重启后 UBoot 不再报错:
或者直接使用 saveenv 将当前的环境变量保存至 SD 卡即可
2.UBoot 命令
进入 UBoot 命令行模式后,通过相关命令可以进行信息查询,设置 IP 地址等操作
2.1.检查 UBoot 所支持的命令 - help
进入命令行模式后,输入 help 或者 ? 查看当前 UBoot 所支持的命令:
可以配置 UBoot 可以使用的命令,也可以自定义命令,在命令行中输入 help + 命令名 (如 help dcache) 即可查看该命令的详细用法:
2.2.信息查询
常用的信息查询命令有 3 个:bdinfo、printenv、version
2.2.1.查看开发板信息 - bdinfo
查看开发板信息,直接在命令行中输入 bdinfo 即可:
从中可以获得 DRAM 的起始地址和大小、启动参数保存的起始地址、波特率、 SP 堆栈指针起始地址等信息
2.2.2.查看环境变量 - printenv
printenv 命令查看 UBoot 所使用的环境变量,直接输入 print 也可以打印出环境变量:
环境变量均为字符串,用于控制程序行为或提供参数信息
例如,环境变量 bootdelay 表示 UBoot 启动延迟,默认 bootdelay=3,即默认延迟 3s,可以通过命令修改为 5 (即通过专门的命令可以修改 UBoot 中的环境变量)
2.2.3.查看 UBoot 版本号 - version
当前 UBoot 的版本号如下所示:
从中可以看出,当前 UBoot 的版本号为 2016.03,编译时间为 2025.06.03,编译器为 arm-linux-gnueabihf-gcc
2.3.操作环境变量
2.3.1.修改环境变量 - setenv 和 saveenv
-
setenv 命令用于设置或修改环境变量的值;
-
saveenv 命令用于保存修改后的环境变量;
环境变量一般存储在外部 Flash 中,UBoot 启动时会将环境变量从 Flash 读取到 DRAM 中,因此,使用 setenv 命令修改的是 DRAM 中的环境变量,修改后使用 saveenv 命令将修改后的环境变量保存到 Flash 中,否则 UBoot 下一次启动时仍会使用以前的环境变量
例如,将 UBoot 启动延迟修改为 5s,可以依次执行如下命令:
setenv bootdelay 5saveenv
上述命令执行的过程及结果如下:
从图中可以看到,执行 saveenv 命令保存修改后的环境变量时,会有保存过程的提示信息,根据提示信息可以看到环境变量保存到了 MMC(0) 即 SD 卡中,因为目前是将 UBoot 烧写到了 SD 卡中,所以会保存至 MMC(0),如果烧写到 EMMC 中就会提示保存到 MMC(1) 即 EMMC 设备,同理,如果是 NAND 核心板会提示保存到 NAND 中
修改 bootdelay 后重启开发板,UBoot 启动延迟变为 5 秒倒计时,如下图所示:
如果要修改的环境变量的值存在空格,如 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” 相当于 4 组值,这 4 组值之间通过空格隔开,所以需要使用单引号 ‘’ 将其括起来,表示这 4 组值都属于环境变量 bootargs
2.3.2.新建环境变量 - setenv
使用 setenv 可以新建环境变量,用法和修改环境变量一样,比如新建一个环境变量 author,author 的值为 test,则可以使用如下命令实现:
setenv author testsaveenv
新建 author 环境变量后重启 UBoot,然后使用 printenv 查看当前的环境变量:
可以看到环境变量 author 已添加
2.3.3.删除环境变量 - setenv
可以使用 setenv 命令删除环境变量,要删除一个环境变量,只需要给这个环境变量赋空值即可,比如删除上面新建的环境变量 author,命令如下:
setenv authorsaveenv
上面的命令使用 setenv 命令给 author 赋空值来将其删除,重启 UBoot 可以看到环境变量 author 已被删除
2.4.内存操作
内存操作命令可以直接对 DRAM 进行读写操作,常用的内存操作命令有 md、nm、mm、mw、cp 和 cmp
2.4.1.显示内存值 - 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;
-
例如,显示起始地址为 0x80000000 的内存中的值,分别使用以下命令:
md.b 80000000 10md.w 80000000 10md.l 80000000 10
这三条命令均为查看起始地址为 0x80000000 的内存中的数据:
-
第一条命令以 .b 格式显示,长度为 0x10,也就是 16 个字节;
-
第二条命令以 .w 格式显示,长度为 0x10,也就是 16*2=32 个字节;
-
第三条命令以 .l 格式显示,长度为 0x10,也就是 16*4=64 个字节
这三条命令执行的结果如下图所示:
2.4.2.修改内存中的值 - nm
nm 命令用于修改内存指定地址的值,命令格式如下:
nm [.b, .w, .l] address
nm 命令同样以 .b、.w 和 .l 指定操作格式,比如以 .l 格式将地址 0x80000000 中的数据修改为 0x1234567,则可以输入以下命令:
nm.l 80000000
输入该命令后显示信息如下:
图中 80000000 为当前要操作的内存地址,ffffffff 为该地址中内存的值,在 ? 后面可以输入需要修改的值,例如 0x1234567,输入完成后按回车键,然后再输入 q 即可退出
修改完成后,使用 md 命令查看是否修改成功,如下图所示:
从输出的结果可以看出,内存地址 0x80000000 中的值已被修改为 0x01234567
2.4.3.自增修改内存中的值 - mm
mm 命令用于修改内存指定地址的值,使用 mm 命令修改内存值时地址会自增,而使用 nm 命令地址不会自增,比如以 .l 格式将起始地址 0x80000000 开始的连续 3 个内存块 (3*4=12 字节) 中的数据修改为 0x05050505,命令如下:
mm.l 80000000
修改完一个地址的内存后,内存地址会按 4 字节递增,此时可以继续进行修改,直到使用 q 退出:
使用 md 指令查看这 3 块内存是否修改成功:
md.l 80000000 3
可以看到这 3 块内存中的值已经被修改为 0x05050505:
2.4.4.内存填充 - mw
mw 命令使用一个指定的数据填充一段内存,命令格式如下:
mw [.b, .w, .l] address value [count]
-
mw 命令可以以 .b、.w 和 .l 指定操作格式;
-
address 表示要填充内存的起始地址;
-
value 为要填充的数据;
-
count 为要填充的长度;
例如,使用 .l 格式,将起始地址为 0x80000000 的 0x10 个内存块 (0x10 * 4=64 字节) ,填充为 0x0A0A0A0A,命令如下:
mw.l 80000000 0A0A0A0A 10
通过 md 命令查看操作执行的结果:
2.4.5.内存复制 - cp
数据拷贝命令 cp 将 DRAM 中的数据从一段内存拷贝到另一段内存中,或将 Nor Flash 中的数据拷贝到 DRAM 中,命令格式如下:
cp [.b, .w, .l] source target count
-
cp 命令可以以 .b、.w 和 .l 指定操作格式;
-
source 为源地址;
-
target 为目标地址;
-
count 为拷贝的长度;
例如,使用 .l 格式,将 0x80000000 地址中的值,拷贝到 0X80000200 处,长度为 0x10 个内存块 (0x10 * 4=64 个字节),命令如下所示:
cp.l 80000000 80000200 10
通过 md 命令查看操作执行的结果:
图中:
-
先通过 md.l 命令查看 0x80000000 内存中的值;
-
再查看 0x80000200 内存中的值;
-
然后使用 cp.l 命令将 0x80000000 内存中的值复制到 0x80000200 处;
-
最后使用 md.l 命令查看操作是否成功;
2.4.6.比较内存中的值是否相等 - cmp
cmp 命令用于比较两段内存中的数据是否相等,命令格式如下:
cmp [.b, .w, .l] addr1 addr2 count
-
cmp 命令可以以 .b、.w 和 .l 指定操作格式;
-
addr1 为第一段内存的首地址;
-
addr2 为第二段内存的首地址;
-
count 为比较长度;
例如,使用 .l 格式,比较 0x80000000 和 0X80000100 这两个地址中的数据是否相等,比较长度为 0x10 个内存块 (16 * 4=64 个字节),命令如下所示:
cmp.l 80000000 80000100 10
比较结果如下图所示:
从图中可以看出两段内存中的数据相等,再选择两段内存进行比较,比如地址 0x80002000 和 0x800003000,长度为 0X10,比较结果下图所示:
从图中可以看出这两段内存中的数据不一样
2.5.以太网相关操作
UBoot 支持网络,移植 Linux Kernel 时需要使用 UBoot 的网络功能进行调试
UBoot 支持大量网络相关的命令,如 DHCP、PING、NFS、TFTP 等
使用 UBoot 的网络功能前,先用网线将开发板的 ENET2 接口和电脑或路由器连接起来
注意:I.MX6U-ALPHA 开发板有两个网口 ENET1 和 ENET2,一定要连接 ENET2 (ENET2 为 UBoot 的 FEC1)
建议将开发板和 PC 连接到同一个路由器上,然后设置下面几个环境变量:
2.5.0.Ubuntu VMware 配置
自己电脑的系统为 Ubuntu22.04,无法使用老版本的 nfs 和 tftp,且由于开发板和 PC 通过 USB-RJ45 转接器连接,因此需要进行很多设置,这里选择在 Ubuntu22.04 中通过 VMware 安装正点原子的虚拟机镜像
首先,查看 PC 的网卡信息,使用 ifconfig 命令:
图中有两个网卡:
-
标号 ① 为 PC 的网卡 enp0s31f6,该网卡可以正常联网;
-
标号 ② 为 USB-RJ45 转接器的网卡 enx00e0990038e2,连接开发板和 PC,无法上网;
首先,配置 USB-RJ45 转接器网卡的 IP 地址,使其和 PC 网卡在同一网段,在设置中进行配置:
选择 IPv4 --> Manual (手动配置) --> 设置相关地址:
按照教程在 Ubuntu22.04 中安装 VMware:https://www.sysgeek.cn/install-vmware-workstation-pro-on-linux/
安装完成后,下载正点原子提供的虚拟机镜像:
按照正点原子的教程在 VMware 中安装虚拟机,安装完成后先配置 VMware 虚拟机的网络
点击 VMware 的 Edit --> Virtual Network Editor:
网络连接方式选择桥接 Bridged,网卡选择 USB-RJ45 转接器的网卡 enx00e0990038e2,点击 save 保存
配置 VMware 虚拟机的网络,在虚拟机上右击,选择底部的 Setting,虚拟机网卡选择 Custom,虚拟网卡选择 vmnet0 (虚拟机的桥接网卡),点击 save 保存:
最后,配置虚拟机中 Ubuntu16.04 系统的网卡,手动设置 IP 地址,确保和 PC 上的两个网卡在同一网段:
!注意,虚拟机配置完成后需要重启!
重启后查看虚拟机中 ubuntu16.04 的 IP 地址:
在虚拟机中 ping PC 看是否可以 ping 通:
可以 ping 通,进行开发板 UBoot 的网络配置,表中环境变量的设置命令如下:
setenv ipaddr 172.17.40.50setenv ethaddr b8:ae:1d:01:00:00setenv gatewayip 172.17.40.1setenv netmask 255.255.255.0setenv serverip 172.17.40.88saveenv
网络地址相关环境变量的设置依实际情况而定,确保 Ubuntu 主机和开发板的 IP 地址在同一网段内,例如:
-
开发板和电脑都在 172.17.40.0 这个网段内,所以设置开发板的 IP 地址为 172.17.40.50;
-
PC 的地址为 172.17.40.39,但是现在使用虚拟机,而虚拟机 Ubuntu16.04 的 IP 地址已设置为 172.17.40.88,因此 serverip 要设置为 172.17.40.88;
-
ethaddr 为设备的 MAC 地址 (48-bit 长度),若同一个网段内有多个开发板,则一定要保证每个开发板的 ethaddr 不同,否则会导致通信异常;
2.5.1.测试是否可以通信 - ping
通过 ping 命令,验证开发板是否可以和服务器进行通信,直接 ping 服务器的 IP 地址即可
使用 ping 命令,测试开发板是否可以和 PC (Ubuntu22.04) 通信:
使用 ping 命令,测试开发板是否可以和 USB-RJ45 转接器通信:
使用 ping 命令,测试开发板是否可以和虚拟机 (正点原子的 Ubuntu16.04 系统) 通信:
全部可以 ping 通,因此后续的 nfs 和 tftp 就可以使用虚拟机操作
注意:只能在 UBoot 中 ping 其他的机器,其他机器不能 ping UBoot,因为 UBoot 中没有对 ping 命令做处理,用其他机器 ping UBoot 会失败!
2.5.2.从路由器获取 IP 地址 - dhcp
开发板和 PC 通过路由器连接时,开发板可以通过 dhcp 命令获取 IP 地址,如果开发板和电脑直连,则 dhcp 命令会失效,直接输入 dhcp 命令,即可从路由器获取 IP 地址,如下图所示:
图中开发板通过 dhcp 获取到的 IP 地址为 192.168.1.137,同时可以看到 “warning:no boot file name;”、“TFTP from server 192.168.1.1” 这样的信息,这是因为 DHCP 不只会获取 IP 地址,还会通过 TFTP 启动 Linux 内核
注意:通过 dhcp 命令获取到的 IP 地址仅本次有效,不会修改环境变量 ipaddr 的值,下次重启仍然会使用 ipaddr 中设置的 IP 地址!
输入 ? dhcp 可以查看 dhcp 命令的详细信息,如下图所示:
2.5.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 中
虽然 SoC 厂商一般都会提供一个烧写固件的软件,但这个软件使用起来比较复杂,该烧写软件一般用于量产设备,没有 MDK、IAR 一键下载方便,在 Linux 内核调试阶段,如果用这个烧写软件会非常浪费时间,此时网络调试的优势就很明显,通过网络可以将编译好的 Linux 镜像和设备树文件下载到 DRAM 中,然后就可以直接运行
一般使用 UBoot 中的 nfs 命令将 Ubuntu 中的文件下载到开发板的 DRAM 中,使用前需要开启 Ubuntu 主机的 NFS 服务,并且要新建一个供 NFS 服务使用的目录,以后所有要通过 NFS 访问的文件都需要放到这个 NFS 目录中
Ubuntu 使用以下命令安装 NFS 服务:
sudo apt-get install nfs-kernel-server rpcbind
安装完成后,在 /home 目录下新建一个文件夹 /linux,后续进行嵌入式 linux 开发时,将相关文件存放在该目录下,在该目录中新建文件夹 /nfs 供 nfs 服务使用:
文件夹创建后配置 nfs,使用以下命令打开配置文件:
sudo vim /etc/exports
在配置文件的末尾添加以下内容:
/home/alientek/linux/nfs *(rw,sync,no_root_squash)
重启 NFS 服务:
sudo /etc/init.d/nfs-kernel-server restart
然后,使用以下命令安装 SSH 服务:
sudo apt-get install openssh-server
SSH 使用默认配置即可
UBoot 中 nfs 命令的格式如下所示:
nfs [loadAddress] [[hostIPaddr:]bootfilename]
-
loadAddress 为要保存文件的 DRAM 起始地址;
-
[[hostIPaddr:]bootfilename] 为要下载的文件地址;
将正点原子官方编译的 Linux 镜像文件 zImage 下载到开发板 DRAM 的 0x80800000 地址处
虚拟机作为服务器,其 IP 地址已设置为 172.17.40.88:
开发板的 IP 地址已经设置为 172.17.40.50,服务器 IP 设置为 172.17.40.88:
首先,将正点原子官方编译的 Linux 镜像文件 zImage,放到虚拟机的 /home/alientek/linux/nfs 文件夹中 (使用共享文件夹):
通过以下命令将 zImage 下载到开发板 DRAM 的 0x80800000 处:
nfs 80800000 172.17.40.88:/home/alientek/linux/nfs/zImage
下载过程如下所示:
图中会以 # 提示下载过程,下载完成后会提示已下载的数据大小,这里显示下载了 6785480 字节 (出厂系统在不断的更新中,因此以实际的 zImage 大小为准),而 zImage 的大小为 6785480 字节
下载完成后查看 0x80800000 地址处的数据,使用命令 md.w 查看,如下图所示:
使用 hexdump 查看 zImage,检查前面的数据是否和图中一致,结果如下图所示:
对比可以看出,前 0x100 个字节的数据一致,说明 nfs 命令下载到开发板中的 zImage 正确
2.5.4.传输文件 - tftp
tftp 命令的作用和 nfs 命令一样,都是通过网络下载文件到 DRAM 中
tftp 命令使用 TFTP 协议,Ubuntu 主机作为 TFTP 服务器,因此需要在 Ubuntu 上搭建 TFTP 服务器
首先在 Ubuntu 上安装 tftp-hpa 和 tftpd-hpa,命令如下:
sudo apt-get install tftp-hpa tftpd-hpasudo apt-get install xinetd
和 NFS 一样,TFTP 也需要一个文件夹存放文件,在用户目录下新建一个目录 tftp,并赋予权限:
tftp 安装后新建文件 /etc/xinetd.d/tftp,若没有 /etc/xinetd.d 目录则创建一个,然后在配置文件中写入以下内容:
server tftp
{socket_type = dgramprotocol = udpwait = yesuser = rootserver = /usr/sbin/in.tftpdserver_args = -s /home/alientek/linux/tftpboot/disable = noper_source = 11cps = 100 2flags = IPv4
}
启动 TFTP 服务:
sudo service tftpd-hpa start
打开 /etc/default/tftpd-hpa 文件,将其修改为如下内容:
# /etc/default/tftpd-hpaTFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/alientek/linux/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="-l -c -s"
TFTP_DIRECTORY 为之前创建的 /tftp 文件夹的路径,后续将所有需要通过 TFTP 传输的文件放在该文件夹中,并且给这些文件赋予相应的权限
然后重启 tftp 服务:
sudo service tftpd-hpa restart
tftp 服务已经准备就绪,将 zImage 镜像文件拷贝到 tftp 文件夹中,并给予 zImage 相应的权限,命令如下:
cp zImage /home/alientek/linux/tftp/cd /home/alientek/linux/tftp/chmod 777 zImage
UBoot 中 tftp 命令的格式如下:
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
tftp 和 nfs 命令的格式一样 :
-
loadAddress 是文件在 DRAM 中存放的地址;
-
[[hostIPaddr:]bootfilename] 是要从 Ubuntu 中下载的文件;
tftp 和 nfs 命令的区别在于,tftp 命令不需要输入文件在 Ubuntu 中的完整路径,只需要输入文件名即可,比如将 tftp 文件夹中的 zImage 文件下载到开发板 DRAM 的 0x80800000 地址处,命令如下:
tftp 80800000 zImage
下载过程如下所示:
从图中可以看出 zImage 下载成功,网速为 2.6MiB/s,文件大小为 5901752 字节,同样,可以使用 md 命令查看前 0x100 个字节的数据是否正确
有时使用 tftp 命令从 Ubuntu 中下载文件时会出现以下错误提示:
从图中可以看到 “TFTP error: 'Permission denied' (0)” 错误提示,提示没有权限,出现该错误一般有两个原因:
-
在 Ubuntu 中创建 /tftp 文件夹时没有给予 /tftp 文件夹相应的权限;
-
/tftp 文件夹中要下载的文件没有给予相应的权限;
针对上述两个问题,解决方法是使用命令 “chmod 777 xxx” 给予权限,其中 “xxx” 为要给予权限的文件或文件夹
2.6.SD/EMMC 操作
UBoot 支持 EMMC 和 SD 卡,一般认为 EMMC 和 SD 卡是同一类设备,所以没有特殊说明,教程中统一使用 MMC 指代 EMMC 和 SD 卡
UBoot 中用于操作 MMC 设备的命令为 mmc,mmc 是一系列命令,其后可以跟不同的参数,输入 ?mmc 即可查看 mmc 有关的命令,如下图所示:
从图中可以看出,mmc 后面跟不同的参数可以实现不同的功能,如下所示:
2.6.1.查看 MMC 设备信息 - mmc info
mmc info 命令用于输出当前选中的 mmc 设备的信息,输入命令 mmc info 即可,如下所示:
从图中可以看出,当前选中的 MMC 设备为 SD 卡,版本为 3.0,容量为 29.7GiB,速度为 50000000Hz = 50MHz,总线位宽为 4-bit,使用 mmcinfo 命令和 mmc info 命令的效果一致
2.6.2.扫描开发板上所有的 MMC 设备 - mmc rescan
mmc rescan 命令用于扫描当前开发板上所有的 MMC 设备,包括 EMMC 和 SD 卡,输入 mmc rescan 即可
2.6.3.查看开发板上共有几个 MMC 设备 - mmc list
mmc list 命令用于查看当前开发板上一共有几个 MMC 设备,输入 mmc list 结果如下:
可以看到当前开发板有两个 MMC 设备 FSL_SDHC:0 和 FSL_SDHC:1,这是因为开发板为 EMMC 版本的核心板,加上 SD 卡一共有两个 MMC 设备,FSL_SDHC:0 为 SD 卡,FSL_SDHC:1 为 EMMC,UBoot 默认将 EMMC 设置为当前的 MMC 设备
2.6.4.切换 MMC 设备 - mmc dev
mmc dev 命令用于切换当前的 MMC 设备,命令格式如下:
mmc dev [dev] [part]
-
[dev] 为要切换的 MMC 设备号;
-
[part] 为分区号,如果不写分区号则默认为分区 0;
使用如下命令进行切换:
mmc dev 0 // 切换到 SD 卡,0 为 SD 卡,1 为 eMMCmmc dev 1 // 切换到 EMMC,0 为 SD 卡,1 为 eMMC
执行结果如下所示:
输入 mmc info 可查看当前 MMC 设备的相关信息:
从图中可以看出,当前的 MMC 设备已切换为 EMMC,容量为 8G,总线位宽为 8-bit
2.6.5.设备分区 - mmc part
SD 卡或 EMMC 会有多个分区,使用命令 mmc part 查看分区,比如查看 EMMC 的分区情况,输入如下命令:
mmc dev 1 // 切换到 EMMCmmc part // 查看 EMMC 的分区
结果如下所示:
从图中可以看出,此时 EMMC 有两个分区:
-
第一个分区的起始扇区为 20480,长度为 262144 个扇区;
-
第二个分区的起始扇区为 282624,长度为 14594048 个扇区;
如果 EMMC 中烧写了 Linux 系统,则 EMMC 会有 3 个分区:
-
第 0 个分区存放 UBoot;
-
第 1 个分区存放 Linux 镜像和设备树;
-
第 2 个分区存放根文件系统;
图中只有两个分区,这是因为第 0 个分区没有格式化,所以识别不出来,实际上第 0 个分区是存在的
一个新的 SD 卡默认只有一个分区 (分区 0),所以将 UBoot 烧写到 SD 卡就是将 u-boot.bin 烧写到 SD 卡的分区 0 中
使用如下命令将 EMMC 的分区 2 设置为当前的 MMC 设备:
mmc dev 1 2
执行结果如下:
2.6.6.读取 MMC 设备中的数据 - 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 分区 0mmc read 80800000 600 10 // 读取数据
执行结果如下:
通过 md.b 命令查看 0x80800000 处的数据,检查是否正确读取,共查看 16*512=8192 (0x2000) 个字节的数据:
md.b 80800000 2000
结果如下所示:
从图中可以看到 “baudrate=115200.board_name=EVK.” 等字样,这些就是 UBoot 中的环境变量,EMMC 核心板 UBoot 环境变量的存储起始地址为 1536*512=786432
2.6.7.将数据写入 MMC 设备 - mmc write
使用命令 mmc write 将数据写入 MMC 设备中,命令格式如下:
mmc write addr blk# cnt
-
addr 为要写入 MMC 中的数据在 DRAM 中的起始地址;
-
blk 是要写入 MMC 的块起始地址 (十六进制);
-
cnt 是要写入的块大小,一个块为 512 字节;
可以使用命令 mmc write 升级 UBoot,也就是在 UBoot 中更新 UBoot,首先通过 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 拷贝到 Ubuntu 的 tftp 目录下
最后使用 tftp 命令将其下载到 0x80800000 地址处,命令如下:
tftp 80800000 u-boot.imx
下载过程如下所示:
从图中可以看出 u-boot.imx 的大小为 379904 字节,379904/512=742,所以要向 SD 卡中写入 742 个块,如果存在小数则要增加 1 个块
使用命令 mmc write 从 SD 卡分区 0 的第 2 个块 (扇区) 开始烧写,一共烧写 742 (0x2E6) 个块,命令如下:
mmc dev 0 0 // 切换至 SD 卡的扇区 0mmc write 80800000 2 2E6 // 将文件烧写至 SD 卡的扇区 2
烧写过程如下图所示:
烧写成功后重启开发板 (从 SD 卡启动)
重启后输入 version 查看版本号,结果如下图所示:
从图中可以看出,此时的 UBoot 是在 2020 年 10 月 27 号 11:44:31 编译的,说明 UBoot 更新成功,更新 EMMC 中 UBoot 的步骤是一样的,如果要在 UBoot 中更新 EMMC 中的 UBoot,可以使用如下命令:
mmc dev 1 0 // 切换到 EMMC 分区 0tftp 80800000 u-boot.imx // 下载 u-boot.imx 到 DRAMmmc write 80800000 2 32E // 烧写 u-boot.imx 到 EMMC 中mmc partconf 1 1 0 0 // 分区配置,EMMC 需要这一步!
注意:千万不要烧写 SD 卡或 EMMC 的前两个块 (扇区),里面保存着分区表!
2.6.8.擦除 MMC 的指定扇区 - mmc erase
mmc erase 命令用于擦除 MMC 设备指定的扇区,命令格式如下:
mmc erase blk# cnt
-
blk 为要擦除的起始块;
-
cnt 是要擦除的数量;
注意:没事不要用 mmc erase 擦除 MMC 设备!!
2.7.FAT 文件系统操作
在 UBoot 中对 SD 卡或 EMMC 中存储的文件进行操作时,需要用到文件操作命令,与文件操作相关的命令有:fatinfo、fatls、fstype、fatload 和 fatwrite,这些操作命令只支持 FAT 格式的文件系统!!
2.7.1.查看 MMC 使用的文件系统 - fatinfo
fatinfo 命令用于查询指定 MMC 设备分区的文件系统信息,其格式如下:
fatinfo <interface> [<dev[:part]>]
-
interface 表示接口,比如 mmc;
-
dev 是要查询的设备号;
-
part 是要查询的分区;
比如要查询 EMMC 分区 1 的文件系统信息,命令如下:
fatinfo mmc 1:1
结果如下图所示:
从图中可以看出 EMMC 分区 1 的文件系统为 FAT32 格式
2.7.2.查询目录和文件信息 - fatls
fatls 命令用于查询 FAT 格式设备的目录和文件信息,命令格式如下:
fatls <interface> [<dev[:part]>] [directory]
-
interface 是要查询的接口,比如 mmc;
-
dev 是要查询的设备号;
-
part 是要查询的分区;
-
directory 是要查询的目录;
比如要查询 EMMC 分区 1 中所有的目录和文件,输入以下命令:
fatls mmc 1:1
结果如下图所示:
从图中可以看出 EMMC 的分区 1 中存放着 8 个文件
2.7.3.查看 MMC 设备分区的文件系统 - fstype
fstype 用于查看 MMC 设备某个分区的文件系统格式,命令格式如下:
fstype <interface> <dev>:<part>
-
interface 是要查询的接口,比如 mmc;
-
dev 是要查询的设备号;
-
part 是要查询的分区号;
正点原子 EMMC 核心板上的 EMMC 默认有 3 个分区,查看这三个分区的文件系统格式,使用以下命令:
fstype mmc 1:0fstype mmc 1:1fstype mmc 1:2
结果如下所示:
从图中可以看出:
-
分区 0 的格式未知,这是因为分区 0 存放 UBoot 且没有格式化,所以文件系统格式未知;
-
分区 1 的格式为 fat,用于存放 Linux 镜像和设备树;
-
分区 2 的格式为 ext4,用于存放 Linux 的根文件系统 rootfs;
2.7.4.将文件读取至 DRAM - 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
执行结果如下:
从图中可以看出在 222ms 内读取了 6786368 个字节的数据,速度为 29.2MiB/s,速度非常快,因为这是从 EMMC 中读取的,而 EMMC 的数据位宽为 8-bit,所以速度会很快 (SD 卡的数据位宽为 4-bit,速度会比较慢)
2.7.5.将 DRAM 中的数据写入 EMMC - 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 的 /tftp 目录下,拷贝完成后使用 tftp 命令将 zImage 下载到 DRAM 的 0x80800000 地址处,命令如下:
tftp 80800000 zImage
下载过程如下图所示:
zImage 的大小为 6785272 (0x6788F8) 个字节 (注意:由于开发板系统在不断的更新,因此 zImage 的大小不固定,以实际大小为准),接下来使用命令 fatwrite 将其写入到 EMMC 的分区 1 中,文件名为 zImage,命令如下:
fatwrite mmc 1:1 80800000 zImage 6788f8
执行结果如下所示:
执行完成后使用 fatls 命令查看 EMMC 分区 1 中的文件,结果如下图所示:
2.8.EXT 文件系统操作
UBoot 有 ext2、ext4 两种格式的文件系统操作命令,ext 文件系统常用的操作命令有:ext2load、ext2ls、ext4load、ext4ls 和 ext4write,这些命令的含义和使用方法与 fatload、fatls、fatwrite 一样,只是 ext2 和 ext4 均针对 ext 文件系统,比如 ext4ls 命令,EMMC 的分区 2 为 ext4 格式,使用 ext4ls 可以查询 EMMC 分区 2 中的文件和目录,输入命令:
ext4ls mmc 1:2
执行结果如下所示:
ext 格式文件系统的其他命令可以参考 FAT 文件系统的相关命令
2.9.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 核心板的启动信息如下所示:
从图中可以看出,开发板的 NAND 容量为 512MiB,输入 ? nand 即可查看 NAND 相关的命令,如下所示:
2.9.1.查看 NAND Flash 信息 - nand info
nand info 命令用于打印 NAND Flash 的相关信息,执行结果如下图所示:
图中打印了 NAND 的页大小、OOB 域大小,擦除大小等信息,可以对照所使用的 NAND Flash 数据手册查看这些信息是否正确
2.9.2.切换 NAND Flash 设备 - nand device
nand device 命令用于切换 NAND Flash,如果开发板支持多片 NAND 就可以使用该命令设置当前所使用的 NAND,这需要 CPU 至少有两个 NAND 控制器,并且两个 NAND 控制器各接一片 NAND Flash,不过一般情况下 CPU 只有一个 NAND 接口,而且使用中只会接一片 NAND
2.9.3.擦除 NAND Flash - 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
2.9.4.向 NAND 指定位置写入数据 - nand write
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 中,关于 BCB 和 DBBT 的详细信息参考《I.MX6ULL 参考手册》的第 8 章
正点原子的教程目前没有详细研究 BCB 和 DBBT,因此不能在 NAND 版的 UBoot 中更新 UBoot 自身,除非熟悉 I.MX6ULL 的 BCB 和 DBBT,然后在 u-boot.imx 前加上相应的信息,否则即使将 UBoot 烧进去了也不能运行,使用 mfgtool 烧写系统到 NAND 时,mfgtool 会使用一个叫做 kogs-ng 的工具完成 BCB 和 DBBT 信息的添加
但是,可以在 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 (共 1MB);
-
第四个分区存放设备树 dtb,地址范围为 0x520000~0x61FFFF (共 1MB);
-
第五个分区存放 Linux Kernel,地址范围为 0x620000~0xE1FFFF (共 8MB);
-
剩下的所有存储空间全部作为最后一个分区,存放根文件系统 rootfs ;
可以看出 Kernel 从地址 0x620000 开始存放,将 NAND 版 Kernel 对应的 zImage 文件放到 Ubuntu 的 tftp 目录,然后使用 tftp 命令将其下载到开发板的 0x87800000 地址处,最后使用 nand write 将其烧写到 NAND 中,命令如下:
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 的大小,这样连下载都没法下载,更别说更新了
2.9.5.从 NAND 中读取数据到 DRAM - nand read
nand read 命令用于从 NAND 指定地址,读取指定大小的数据到 DRAM 中,命令格式如下:
nand read addr off size
-
addr 为目的地址;
-
off 为要从 NAND 中读取的数据源地址;
-
size 为要读取的数据大小;
比如读取设备树 dtb 文件到 0x83000000 地址处,命令如下:
nand read 0x83000000 0x520000 0x19000
执行过程如下所示:
设备树文件读取到 DRAM 后,就可以使用 fdt 命令对设备树进行操作,首先设置 fdt 的地址,fdt 的地址为 DRAM 中设备树的首地址,命令如下:
fdt addr 83000000
设置后可以使用 fdt header 查看设备树的头信息,输入命令:
fdt header
执行结果如下所示:
输入命令 fdt print 可以查看设备树文件的内容:
fdt print
执行结果如下图所示:
图中的文件就是烧写到 NAND 中的设备树文件
NAND 常用的操作命令就是擦除、读和写,一定不要尝试全片擦除 NAND 的指令!!否则 NAND 会被全部擦除 (包括分区表),什么都没有了,需要重新烧写整个系统
2.10.BOOT 操作
UBoot 的本质工作是引导 Linux,所以 UBoot 相关的 boot (引导) 命令用于启动 Linux,常用的 boot 命令有:bootz、bootm 和 boot
2.10.1.启动系统 - bootz
启动 Linux 前需要将 Linux 镜像文件拷贝到 DRAM 中,如果使用设备树,也需要将设备树文件拷贝到 DRAM 中
可以从 EMMC 或 NAND 等存储设备将 Linux 镜像和设备树拷贝至 DRAM,也可以通过 nfs 或 tftp 将 Linux 镜像和设备树下载到 DRAM 中,然后使用 bootz 命令启动镜像
bootz 命令用于启动 zImage 镜像文件,bootz 命令的格式如下:
bootz [addr [initrd[:size]] [fdt]]
-
addr 是 Linux 镜像文件在 DRAM 中的位置;
-
initrd 是 initrd 文件在 DRAM 中的地址,如果不使用 initrd,使用 - 代替即可;
-
fdt 是设备树文件在 DRAM 中的地址;
使用网络启动 Linux 系统的步骤如下:
-
首先将 I.MX6U-ALPHA 开发板的 Linux 镜像和设备树发送到 Ubuntu 主机的 /tftp 文件夹;
-
然后将设备树文件发送到 Ubuntu 主机的 /tftp 文件夹;
-
赋予设备树文件可执行权限,如:chmod +x imx6ull-14x14-emmc-4.3-480x272- c.dtb;
-
使用 tftp 命令将 zImage 下载到 DRAM 的 0x80800000 地址处;
-
将设备树 imx6ull-14x14-emmc-4.3-480x272- c.dtb 下载到 DRAM 的 0x83000000 地址处;
-
最后使用 bootz 命令启动系统,命令如下:
tftp 80800000 zImagetftp 83000000 imx6ull-14x14-emmc-4.3-480x272-c.dtbbootz 80800000 - 83000000
执行结果如下图所示:
图中就是通过 tftp 和 bootz 命令,从网络启动 Linux 系统的过程
若要从 EMMC 启动 Linux,需要使用命令 fatload 将 zImage 和 imx6ull-14x14-emmc-4.3-480x272-c.dtb 从 EMMC 的分区 1 拷贝到 DRAM 中,然后使用命令 bootz 启动:
-
先使用命令 fatls 查看 EMMC 分区 1 中有没有 Linux 镜像文件和设备树文件,若没有参考 fatwrite 命令将 tftp 中的 zImage 和 imx6ull-14x14-emmc-4.3-480x272-c.dtb 文件烧写到 EMMC 的分区 1 中;
-
然后使用 fatload 命令将 zImage 和 imx6ull-14x14-emmc-4.3-480x272-c.dtb 文件拷贝到 DRAM 中,地址分别为 0x80800000 和 0x83000000;
-
最后使用 bootz 命令启动系统,命令如 下:
fatload mmc 1:1 80800000 zImagefatload mmc 1:1 83000000 imx6ull-14x14-emmc-4.3-480x272-c.dtbbootz 80800000 - 83000000
执行结果如下图所示:
2.10.2.读取环境变量并启动系统 - bootm
bootm 和 bootz 功能类似,但 bootm 用于启动 uImage 镜像文件
不使用设备树启动 Linux 内核的命令如下:
bootm addr // addr 是 uImage 镜像在 DRAM 中的首地址
如果要使用设备树,那么 bootm 命令和 bootz 一样,命令格式如下:
bootm [addr [initrd[:size]] [fdt]]
-
addr 是 uImage 在 DRAM 中的首地址;
-
initrd 是 initrd 的地址,如果 initrd 为空,同样用 - 替代;
-
fdt 是设备树 (.dtb) 文件在 DRAM 中的首地址;
2.10.3.读取引导变量启动系统 - boot
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'saveenvboot
运行结果如下所示:
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'savenevboot
运行结果如下所示:
如果不修改 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(0,0)”
产生该错误的原因是 Linux 内核没有找到根文件系统,这个很正常,因为没有设置 UBoot 的 bootargs 环境变量,关于 bootargs 环境变量后面会讲解!
2.11.其他常用命令
UBoot 中还有其他一些常用命令,如 reset、go、run 和 mtest 等
2.11.1.复位 - reset
reset 命令执行复位操作,和按下开发板上的复位按键效果一致,执行该命令的效果如下:
2.11.2.跳转至指定地址执行 - go
go 命令用于跳转到指定地址执行应用,命令格式如下:
go addr [arg ...]
-
addr 是应用在 DRAM 中的首地址;
可以编译一下裸机例程的实验 13_printf,然后将编译出来的 printf.bin 拷贝到 Ubuntu 的 /tftp 文件夹,注意,这里要拷贝 printf.bin 文件,不需要在前面添加 IVT 信息,因为 UBoot 已完成 DDR 初始化,使用 tftp 命令将 printf.bin 下载到开发板 DRAM 的 0x87800000 地址处,因为裸机例程的链接首地址为 0x87800000,最后使用 go 命令启动 printf.bin 这个应用,命令如下:
tftp 87800000 printf.bingo 87800000
执行结果如下图所示:
从图中可以看出,通过 go 命令就可以在 UBoot 中运行裸机例程
2.11.3.运行环境变量中定义的命令 - run
run 命令用于运行环境变量中定义的命令
例如,可以通过 run bootcmd 来运行 bootcmd 中的启动命令,但 run 命令最大的作用是运行自定义的环境变量
调试 Linux 系统时常常要在网络启动和 EMMC/NAND 启动间来回切换,而 bootcmd 只能保存一种启动方式,如果要切换另外一种启动方式就得重写 bootcmd,会很麻烦,这里就可以通过自定义环境变量实现不同的启动方式
例如,定义环境变量 mybootemmc 表示从 emmc 启动,定义 mybootnet 表示从网络启动,定义 mybootnand 表示从 NAND 启动,如果要切换启动方式,只需要运行 run mybootxxx (xxx 为 emmc、net 或 nand) 即可
创建环境变量 mybootemmc、mybootnet 和 mybootnand,命令如下:
setenv mybootemmc 'fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull14x14-emmc-7-1024x600-c.dtb;bootz 80800000 - 83000000'setenv mybootnand 'nand read 80800000 4000000 800000;nand read 83000000 6000000 100000;bootz 80800000 - 83000000'setenv mybootnet 'tftp 80800000 zImage; tftp 83000000imx6ull-14x14-emmc-7-1024x600-c.dtb; bootz 80800000 - 83000000'saveenv
环境变量创建成功后,就可以使用 run 命令运行 mybootemmc、mybootnet 或 mybootnand 实现不同方式的启动:
run mybootemmc
// 或
run mytoobnand
// 或
run mybootnet
2.11.4.内存读写测试 - mtest
mtest 命令是一个简单的内存读写测试命令,可以用来测试开发板上的 DDR,命令格式如下:
mtest [start [end [pattern [iterations]]]]
-
start 是要测试的 DRAM 开始地址;
-
end 是结束地址;
比如测试 0x80000000~0x80001000 这段内存,输入 mtest 80000000 80001000,结果如下图所示:
从图中可以看出,测试范围为 0x80000000~0x80001000,测试了 486 次,可以通过 Ctrl+C 结束测试