种类 | 描述 |
---|---|
uboot 官方的 uboot 代码 | 由 uboot 官方维护开发的 uboot 版本,版本更新快,基本包含所有常用的芯片。 |
半导体厂商的 uboot 代码 | 半导体厂商维护的一个 uboot,专门针对自家的芯片,在对自家芯片支持上要比 uboot 官方的好。 |
开发板厂商的 uboot 代码 | 开发板厂商在半导体厂商提供的 uboot 基础上加入了对自家开发板的支持。 |
新建名为 mx6ull_alientek_emmc.sh 的 shell 脚本文件,然后在里面输入如下内容:
#!/bin/bash #必须是“ #!/bin/bash”或者“ #!/bin/sh”
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean #清理工程,ARCH指定架构,CROSS_COMPILE指定编译器
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig #配置 uboot,配置文件都在 uboot/configs目录中
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12 # V=1 用于设置编译过程的信息输出级别; -j 用于设置主机使用多少线程编译uboot,最好设置成我们虚拟机所设置的核心数
chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可
./imxdownload u-boot.bin /dev/sdd
bdinfo 用于查看板子信息
printenv 用于输出环境变量信息
version 用于查看 uboot 的版本号
setenv 用于设置或者修改环境变量的值
saveenv 用于保存修改后的环境变量
一般环境变量是存放在外部 flash 中的,uboot 启动的时候会将环境变量从 flash 读取到 DRAM 中。所以使用命令 setenv 修改的是 DRAM中的环境变量值,修改以后要使用 saveenv 命令将修改后的环境变量保存到 flash 中,否则的话
uboot 下一次重启会继续使用以前的环境变量值。
比如我们要将环境变量 bootdelay 改为 5,就可以使用如下所示命令:
setenv bootdelay 5
saveenv
有时候我们修改的环境变量值可能会有空格, 比如 bootcmd、 bootargs 等, 这个时候环境变
量值就得用单引号括起来,比如下面修改环境变量 bootcmd 的值:
setenv bootcmd 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv
命令 setenv 也可以用于新建命令,用法就是修改环境变量一样,比如我们新建一个环境变
量 author, author 的值为我的名字拼音: zuozhongkai,那么就可以使用如下命令:
setenv author zuozhongkai
saveenv
删除环境变量也是使用命令 setenv,
要删除一个环境变量只要给这个环境变量赋空值即可,比如我们删除掉上面新建的 author 这个
环境变量,命令如下:
setenv author
saveenv
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 为起始地址的内存数据
md.b 80000000 10 //以.b 格式显示,长度为 0x10,也就是 16 个字节;
md.w 80000000 10 //以.w 格式显示,长度为 0x10,也就是 16*2=32个字节;
md.l 80000000 10 //以.l 格式显示,长度也是 0x10,也就是 16*4=64 个字节。
nm 命令用于修改指定地址的内存值,命令格式如下:
nm [.b, .w, .l] address
nm 命令同样可以以.b、 .w 和.l 来指定操作格式,比如现在以.l 格式修改 0x80000000 地址
的数据为 0x12345678。输入命令:
nm.l 80000000
mm 命令也是修改指定地址内存值的,使用 mm 修改内存值的时候地址会自增,而使用命
令 nm 的话地址不会自增。
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
cp 是数据拷贝命令,用于将 DRAM 中的数据从一段内存拷贝到另一段内存中,或者把 Nor
Flash 中的数据拷贝到 DRAM 中。命令格式如下:
cp [.b, .w, .l] source target count
cp 命令同样可以以.b、 .w 和.l 来指定操作格式, source 为源地址, target 为目的地址, count
为拷贝的长度。我们使用.l 格式将 0x80000000 处的地址拷贝到 0X80000100 处,长度为 0x10 个
内存块(0x10 * 4=64 个字节),命令如下所示:
cp.l 80000000 80000100 10
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
网络相关环境变量
环境变量 | 描述 |
---|---|
ipaddr | 开发板 ip 地址,可以不设置,使用 dhcp 命令来从路由器获取 IP 地址。 |
ethaddr | 开发板的 MAC 地址,一定要设置。 |
gatewayip | 网关地址。 |
netmask | 子网掩码。 |
serverip | 服务器 IP 地址,也就是 Ubuntu 主机 IP 地址,用于调试代码。 |
表 30.4.4.1 中环境变量设置命令如下所示:
setenv ipaddr 192.168.1.50
setenv ethaddr 00:04:9f:04:d2:35
setenv gatewayip 192.168.1.1
setenv netmask 255.255.255.0
setenv serverip 192.168.1.250
saveenv
注意,网络地址环境变量的设置要根据自己的实际情况,确保 Ubuntu 主机和开发板的 IP
地址在同一个网段内,比如我现在的开发板和电脑都在 192.168.1.0 这个网段内,所以设置开
发板的 IP 地址为 192.168.1.50,我的 Ubuntu 主机的地址为 192.168.1.250,因此 serverip 就是
192.168.1.250。 ethaddr 为网络 MAC 地址,是一个 48bit 的地址,如果在同一个网段内有多个
开发板的话一定要保证每个开发板的 ethaddr 是不同的,否则通信会有问题!设置好网络相关
的环境变量以后就可以使用网络相关命令了。
ping
开发板的网络能否使用,是否可以和服务器(Ubuntu 主机)进行通信,通过 ping 命令就可
以验证,直接 ping 服务器的 IP 地址即可,比如我的服务器 IP 地址为 192.168.1.250,命令如
下:ping 192.168.1.250
dhcp 用于从路由器获取 IP 地址,前提得开发连接到路由器上的,如果开发板是和电脑直
连的,那么 dhcp 命令就会失效。直接输入 dhcp 命令即可通过路由器获取到 IP 地址。
同时在图 30.4.4.3 中可以看到“ warning: no boot file name;”、“ TFTP from
server 192.168.1.1”这样的字样。这是因为 DHCP 不单单是获取 IP 地址,其还会通过 TFTP 来
启动 linux 内核,输入“ ? dhcp”即可查看 dhcp 命令详细的信息。
nfs(Network File System)网络文件系统,通过 nfs 可以在计算机之间通过网络来分享资源,
比如我们将 linux 镜像和设备树文件放到 Ubuntu 中,然后在 uboot 中使用 nfs 命令将 Ubuntu 中
的 linux 镜像和设备树下载到开发板的 DRAM 中。这样做的目的是为了方便调试 linux 镜像和
设备树,也就是网络调试,通过网络调试是 Linux 开发中最常用的调试方法。
我们一般使用 uboot 中的 nfs 命令将 Ubuntu 中的文件下载到开发板的 DRAM 中,在使用
之前需要开启 Ubuntu 主机的 NFS 服务,并且要新建一个 NFS 使用的目录,以后所有要通过
NFS 访问的文件都需要放到这个 NFS 目录中。
uboot 中的 nfs 命令格式如下所
示:
nfs [loadAddress] [[hostIPaddr:]bootfilename]
loadAddress 是要保存的 DRAM 地址, [[hostIPaddr:]bootfilename]是要下载的文件地址。这
里我们将正点原子官方编译出来的 Linux 镜像文件 zImage 下载到开发板 DRAM 的 0x80800000
这个地址处。将文件 zImage 通过 FileZilla 发送到 Ubuntu 中的 NFS 目录下,比如我的就
是放到/home/zuozhongkai/linux/nfs 这个目录下。
准备好以后就可以使用 nfs 命令来将 zImage 下载到开发板 DRAM 的 0X80800000 地址处,
命令如下:
nfs 80800000 192.168.1.250:/home/zuozhongkai/linux/nfs/zImage
命 令 中 的 “ 80800000 ” 表 示 zImage 保 存 地 址 ,
“ 192.168.1.250:/home/zuozhongkai/linux/nfs/zImage”表示 zImage 在 192.168.1.250 这个主机中,
路径为/home/zuozhongkai/linux/nfs/zImage。
下载完成以后查看 0x80800000 地址处的数据,使用命令 md.b 来查看前 0x100 个字节的数
据。
再使用 winhex 软件来查看 zImage,检查一下前面的数据是否和图 30.4.4.8 只的一致。
tftp 命令的作用和 nfs 命令一样,都是用于通过网络下载东西到 DRAM 中,只是 tftp 命令
使用的 TFTP 协议, Ubuntu 主机作为 TFTP 服务器。因此需要在 Ubuntu 上搭建 TFTP 服务器,
需要安装 tftp-hpa 和 tftpd-hpa,命令如下:
sudo apt-get install tftp-hpa tftpd-hpa
sudo apt-get install xinetd
和 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 目录的话自行
创建, 然后在里面输入如下内容:
示例代码 30.4.4.1 /etc/xinetd.d/tftp 文件内容
1 server tftp
2 {
3 socket_type = dgram
4 protocol = udp
5 wait = yes
6 user = root
7 server = /usr/sbin/in.tftpd
8 server_args = -s /home/zuozhongkai/linux/tftpboot/
9 disable = no
10 per_source = 11
11 cps = 100 2
12 flags = IPv4
13 }
完了以后启动 tftp 服务,命令如下:
sudo service tftpd-hpa start
打开/etc/default/tftpd-hpa 文件,将其修改为如下所示内容:
示例代码 30.4.4.2 /etc/default/tftpd-hpa 文件内容
1 # /etc/default/tftpd-hpa
2 3
TFTP_USERNAME="tftp"
4 TFTP_DIRECTORY="/home/zuozhongkai/linux/tftpboot"
5 TFTP_ADDRESS=":69"
6 TFTP_OPTIONS="-l -c -s"
TFTP_DIRECTORY 就是我们上面创建的 tftp 文件夹目录,以后我们就将所有需要通过
TFTP 传输的文件都放到这个文件夹里面,并且要给予这些文件相应的权限。
最后输入如下命令, 重启 tftp 服务器:
sudo service tftpd-hpa restart
tftp 服务器已经搭建好了,接下来就是使用了。将 zImage 镜像文件拷贝到 tftpboot 文件夹
中,并且给予 zImage 相应的权限,命令如下:
cp zImage /home/zuozhongkai/linux/tftpboot/
cd /home/zuozhongkai/linux/tftpboot/
chmod 777 zImage
万事俱备,只剩验证了, uboot 中的 tftp 命令格式如下:
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
看 起 来 和 nfs 命 令 格式 一 样 的 , loadAddress 是 文 件 在 DRAM 中 的存 放 地 址 ,
[[hostIPaddr:]bootfilename]是要从 Ubuntu 中下载的文件。但是和 nfs 命令的区别在于, tftp 命令
不需要输入文件在 Ubuntu 中的完整路径,只需要输入文件名即可。比如我们现在将 tftpboot 文
件夹里面的 zImage 文件下载到开发板 DRAM 的 0X80800000 地址处,命令如下:
tftp 80800000 zImage
uboot 支持 EMMC 和 SD 卡,因此也要提供 EMMC 和 SD 卡的操作命令。一般认为 EMMC
和 SD 卡是同一个东西,所以没有特殊说明,本教程统一使用 MMC 来代指 EMMC 和 SD 卡。
uboot 中常用于操作 MMC 设备的命令为“ mmc”。
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 bootpart…… | 设置指定 MMC 设备的 boot 和 RPMB 分区的大小。 |
mmc partconf…… | 设置指定 MMC 设备的 PARTITION_CONFG 域的值。 |
mmc rst | 复位 MMC 设备 |
mmc setdsr | 设置 DSR 寄存器的值。 |
mmc info 命令用于输出当前选中的 mmc info 设备的信息。
mmc rescan 命令用于扫描当前开发板上所有的 MMC 设备,包括 EMMC 和 SD 卡。
mmc list 命令用于来查看当前开发板一共有几个 MMC 设备。可以看出当前开发板有两个 MMC 设备: FSL_SDHC:0 和 FSL_SDHC:1 (eMMC)。
mmc dev 命令用于切换当前 MMC 设备,命令格式如下:
mmc dev [dev] [part]
[dev]用来设置要切换的 MMC 设备号,[part]是分区号。如果不写分区号的话默认为分区 0。
使用如下命令切换到 SD 卡:
mmc dev 0 //切换到 SD 卡, 0 为 SD 卡, 1 为 eMMC
mmc part 查看分区,比如
查看 EMMC 的分区情况,输入如下命令:
mmc dev 1 //切换到 EMMC
mmc part //查看 EMMC 分区
如果要将 EMMC 的分区 2 设置为当前 MMC 设置,可以使用如下命令:
mmc dev 1 2
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 80800000 600 10 //读取数据
要将数据写到 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 是 2019 年 4 月 15 日 12:52:04 编译的。我们现在重新编译
一下 uboot,然后将编译出来的 u-boot.imx(u-boot.bin 前面加了一些头文件)拷贝到 Ubuntu 中的
tftpboot 目录下。最后使用 tftp 命令将其下载到 0x80800000 地址处,命令如下:
tftp 80800000 u-boot.imx
可以看出, u-boot.imx 大小为 416768 字节, 416768/512=814,所以我们要向 SD 卡中写入
814 个块,如果有小数的话就要加 1 个块。使用命令“ mmc write”从 SD 卡分区 0 第 2 个块(扇
区)开始烧写,一共烧写 814(0x32E)个块,命令如下:
mmc dev 0 0
mmc write 80800000 2 32E
烧写成功,重启开发板(从 SD 卡启动),重启以后再输入 version 来查看版本号。
此时的 uboot 是 2019 年 4 月 21 号 18:05:59 编译的,这个时间就
是我刚刚编译 uboot 的时间,说明 uboot 更新成功。
这里我们就学会了如何在 uboot 中更新 uboot
了,如果要更新 EMMC 中的 uboot 也是一样的。
千万不要写 SD 卡或者 EMMC 的前两个块(扇区),里面保存着分区表!
如果要擦除 MMC 设备的指定块就是用命令“ mmc erase”,命令格式如下:
mmc erase blk# cnt
blk 为要擦除的起始块, cnt 是要擦除的数量。
有时候需要在 uboot 中对 SD 卡或者 EMMC 中存储的文件进行操作,这时候就要用到文件
操作命令,跟文件操作相关的命令有: fatinfo、 fatls、 fstype、 fatload 和 fatwrite,但是这些文件
操作命令只支持 FAT 格式的文件系统!!
fatinfo 命令用于查询指定 MMC 设置指定分区的文件系统信息,格式如下:
fatinfo <interface> [<dev[:part]>]
interface 表示接口,比如 mmc, dev 是查询的设备号, part 是要查询的分区。比如我们要查
询 EMMC 分区 1 的文件系统信息,命令如下:
fatinfo mmc 1:1
fatls 命令用于查询 FAT 格式设备的目录和文件信息,命令格式如下:
fatls <interface> [<dev[:part]>] [directory]
interface 是要查询的接口,比如 mmc,dev 是要查询的设备号,part 是要查询的分区,directory
是要查询的目录。比如查询 EMMC 分区 1 中的所有的目录和文件,输入命令:
fatls mmc 1:1
fstype 用于查看 MMC 设备某个分区的文件系统格式,命令格式如下:
fstype <interface> <dev>:<part>
正点原子 EMMC 核心板上的 EMMC 默认有 3 个分区,我们来查看一下这三个分区的文件
系统格式,输入命令:
fstype mmc 1:0
fstype mmc 1:1
fstype mmc 1:2
从上图可以看出,分区 0 格式未知,因为分区 0 存放的 uboot,并且分区 0 没有格式化,
所以文件系统格式未知。分区 1 的格式为 fat,分区 1 用于存放 linux 镜像和设备树。分区 2 的
格式为 ext4,用于存放 Linux 的根文件系统(rootfs)。
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 zImag
注意! 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 镜像文件放到了开发板光盘中,路径为: 开发板光盘->8、开发板系统
镜像->zImage。
使用命令 tftp 将 zImage 下载到 DRAM 的 0X80800000 地址处,命令如下:
tftp 80800000 zImage
zImage 大小为 6039328(0X5C2720)个字节,接下来使用命令 fatwrite 将其写入到 EMMC 的
分区 1 中,文件名字为 zImage,命令如下:
fatwrite mmc 1:1 80800000 zImage 0x5c2720
uboot 有 ext2 和 ext4 这两种格式的文件系统的操作命令,常用的就四个命令,分别为:
ext2load、 ext2ls、 ext4load、 ext4ls 和 ext4write。这些命令的含义和使用与 fatload、 fatls 和 fatwrit
一样,只是 ext2 和 ext4 都是针对 ext 文件系统的。比如 ext4ls 命令, EMMC 的分区 2 就是 ext4
格式的,使用 ext4ls 就可以查询 EMMC 的分区 2 中的文件和目录,输入命令:
ext4ls mmc 1:2
输入“ ? nand”即可查看所有有关 NAND 令
nand info 命令打印 NAND Flash 信息。
nand device 用于切换 NAND Flash,如果你的板子支持多片 NAND 的话就可以使用此命令
来设置当前所使用的 NAND。这个需要你的 CPU 有两个 NAND 控制器,并且两个 NAND 控制
器各接一片 NAND Flash。就跟 I.MX6U 有两个 SDIO 接口,这两个 SDIO 接口可以接两个 MMC
设备一样。不过一般情况下 CPU 只有一个 NAND 接口,而且在使用中只接一片 NAND。
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 write 命令用于向 NAND 指定地址写入指定的数据,一般和“ nand erase”命令配置使用来更新
NAND 中的 uboot、 linux kernel 或设备树等文件,命令格式如下:
nand write addr off size
addr 是要写入的数据首地址, off 是 NAND 中的目的地址, size 是要写入的数据大小。
以更新 NAND 中的 uboot 为例,讲解一下如何使用此命令。先编译出来 NAND 版本的 uboot.imx 文件,在烧写之前要先对 NAND 进行分区,也就是规划好 uboot、 linux kernel、设备树
和根文件系统的存储区域, I.MX6U-ALPHA 开发板的 NAND 分区如下:
0x000000000000-0x000004000000 : "boot"
0x000004000000-0x000006000000 : "kernel"
0x000006000000-0x000007000000 : "dtb"
0x000007000000-0x000020000000 : "rootfs"
一共有四个分区,第一个分区存放 uboot,地址范围为 0x0~0x4000000(共 64MB);第二个
分区存放 kernel(也就是 linux kernel),地址范围为 0x4000000~0x6000000(共 32MB);第三个分
区存放 dtb(设备树),地址范围为 0x6000000~0x7000000(共 16MB);剩下的所有存储空间全部作
为最后一个分区,存放 rootfs(根文件系统)。
uboot 是从地址 0 开始存放的,其实用不了这么大的区域,但是为了好管理才分配了这么大
的,将 NAND 版本的 u-boot.imx 文件放到 Ubuntu 中的 tftpboot 目录中,然后使用 tftp 命令将其
下载到开发板的 0X87800000 地址处,最终使用“ nand write”将其烧写到 NAND 中,命令如下:
tftp 0x87800000 u-boot.imx //下载 u-boot.imx 到 DRAM 中
nand erase 0x0 0x100000 //从地址 0 开始擦除 1MB 的空间
nand write 0x87800000 0x0 0x100000 //将接收到的 u-boot.imx 写到 NAND 中
u-boot.imx 很小,一般就是 4,5 百 KB,所以擦除 1MB 的空间就可以了。写入的时候也是
按照 1M 的数据写入的,所以肯定会写入一些无效的数据。你也可以将写入的大小改为 uboot.imx 这个文件的大小,这样写入的数据量就是 u-boot.imx 的实际大小了。
同理我们也可以更新 NAND 中的 linux kernel 和设备树(dtb)文件,命令如下:
tftp 0x87800000 zImage //下载 zImage 到 DRAM 中
nand erase 0x4000000 0xA00000 //从地址 0x4000000 开始擦除 10MB 的空间
nand write 0x87800000 0x4000000 0xA00000 //将接收到的 zImage 写到 NAND 中
这里我们擦出了 10MB 的空间,因为一般 zImage 就是 6,7MB 左右, 10MB 肯定够了,如
果不够的话就将在多擦除一点就行了。
最后烧写设备树(dtb)文件文件,命令如下:
tftp 0x87800000 imx6ull-alientek-nand.dtb //下载 dtb 到 DRAM 中
nand erase 0x6000000 0x100000 //从地址 0x6000000 开始擦除 1MB 的空间
nand write 0x87800000 0x6000000 0x100000 //将接收到的 dtb 写到 NAND 中
dtb 文件一般只有几十 KB,所以擦除 1M 是绰绰有余的了。
根文件系统(rootfs)就不要在 uboot 中更新了,还是使用 NXP 提供的 MFGTool 工具来烧写,
因为根文件系统太大!很有可能超过开发板 DRAM 的大小,这样连下载都没法下载,更别说更
新了。
nand read 用于从 NAND 中的指定地址读取指定大小的数据到 DRAM 中,命令格式如下:
nand read addr off size
addr 是目的地址, off 是要读取的 NAND 中的数据源地址, size 是要读取的数据大小。比
如我们读取设备树(dtb)文件到 0x83000000 地址处,命令如下:
nand read 0x83000000 0x6000000 0x19000
设备树文件读取到 DRAM 中以后就可以使用 fdt 命令来对设备树进行操作了,首先设置 fdt
的地址, fdt 地址就是 DRAM 中设备树的首地址,命令如下:
fdt addr 83000000
设置好以后可以使用“ fdt header”来查看设备树的头信息
输入命令“ fdt print”就可以查看设备树文件的内容
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 文件夹里面。以 EMMC 核心板为
例,将开发板光盘->8、开发板系统镜像->imx6ull-alientek-emmc.dtb 文件发送到 Ubuntu 主机中
的 tftpboot 文件夹里面。
下载 Linux 镜像文件和设备树都准备好了,我们先学习如何通过网络启动 Linux,使用 tftp
命令将 zImage 下载到 DRAM 的 0X80800000 地址处,然后将设备树 imx6ull-alientek-emmc.dtb
下载到 DRAM 中的 0X83000000 地址处,最后之后命令 bootz 启动,命令如下:
tftp 80800000 zImage
tftp 83000000 imx6ull-alientek-emmc.dtb
bootz 80800000 – 83000000
如果我们要从 EMMC 中启动 Linux 系统的话只需要使用命令 fatload 将 zImage 和 imx6ull-alientek-emmc.dtb 从 EMMC 的
分区 1 中拷贝到 DRAM 中,然后使用命令 bootz 启动即可。先使用命令 fatls 查看要下 EMMC
的分区 1中有没有 Linux镜像文件和设备树文件,如果没有的话参考 30.4.6小节中讲解的 fatwrite
命令将 tftpboot 中的 zImage 和 imx6ull-alientek-emmc.dtb 文件烧写到 EMMC 的分区 1 中。然后
使用命令 fatload 将 zImage 和 imx6ull-alientek-emmc.dtb 文件拷贝到 DRAM 中,地址分别为
0X80800000 和 0X83000000,最后使用 bootz 启动,命令如下:
fatload mmc 1:1 80800000 zImage
fatload mmc 1:1 83000000 imx6ull-alientek-emmc.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-alientek-emmc.dtb; bootz 80800000 - 83000000”,然后使
用 saveenv 将 bootcmd 保存起来。然后直接输入 boot 命令即可从网络启动 Linux 系统,命令如
下:
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz
80800000 - 83000000'
saveenv
boot
前面说过 uboot 倒计时结束以后就会启动 Linux 系统,其实就是执行的 bootcmd 中的启动
命令。只要不修改 bootcmd 中的内容,以后每次开机 uboot 倒计时结束以后都会使用 tftp 命令
从网络下载 zImage 和 imx6ull-alientek-emmc.dtb,然后启动 Linux。
如果想从 EMMC 启动那就设置 bootcmd 为“ fatload mmc 1:1 80800000 zImage; fatload mmc
1:1 83000000 imx6ull-alientek_emmc.dtb; bootz 80800000 - 83000000”,然后使用 boot 命令启动即
可,命令如下:
setenv bootcmd 'fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ullalientek_emmc.dtb; bootz 80800000 - 83000000'
savenev
boot
如果不修改 bootcmd 的话,每次开机 uboot 倒计时结束以后都会自动从 EMMC 里面读取
zImage 和 imx6ull-alientek-emmc.dtb,然后启动 Linux。
在启动 Linux 内核的时候可能会遇到如下错误:
“ Kernel panic – not Syncing: VFS: Unable to mount root fs on unknown-block(0,0)”
这个错误的原因是 linux 内核没有找到根文件系统,这个很正常,因为没有设置 uboot 的
bootargs 环境变量,关于 bootargs 环境变量后面会讲解!此处我们重点是验证 boot 命令, linux
内核已经成功启动了,说明 boot 命令工作正常。
reset 命令顾名思义就是复位的。
go 命令用于跳到指定的地址处执行应用,命令格式如下:
go addr [arg ...]
addr 是应用在 DRAM 中的首地址,我们可以编译一下裸机例程的实验 13_printf,然后将编
译出来的 printf.bin 拷贝到 Ubuntu 中的 tftpboot 文件夹里面,注意,这里要拷贝 printf.bin 文件,
不需要在前面添加 IVT 信息,因为 uboot 已经初始化好了 DDR 了。使用 tftp 命令将 printf.bin
下载到开发板 DRAM 的 0X87800000 地址处,因为裸机例程的链接首地址就是 0X87800000,
最后使用 go 命令启动 printf.bin 这个应用,命令如下:
tftp 87800000 printf.bin
go 87800000
通过 go 命令我们就可以在 uboot 中运行裸机例程。
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 imx6ullalientek-emmc.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 83000000 imx6ull-alientek-emmc.dtb; bootz
80800000 - 83000000'
saveenv
创建环境变量成功以后就可以使用 run 命令来运行 mybootemmc、 mybootnet 或 mybootnand
来实现不同的启动:
run mybootemmc
或
run mytoobnand
或 r
un mybootnet
mtest 命令是一个简单的内存读写测试命令,可以用来测试自己开发板上的 DDR,命令格
式如下:
mtest [start [end [pattern [iterations]]]]
start是要测试的 DRAM 开始地址,end 是结束地址,比如我们测试 0X80000000~0X80001000
这段内存,输入“ mtest 80000000 80001000”,从图 30.4.10.3 可以看出,测试范围为 0X80000000~0X80001000,已经测试了 486 次,如果要结束测试就按下键盘上的“ Ctrl+C”键。
【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.3.pdf