目录
一、u-boot使用实验
1.u-boot简介
Linux系统要启动就必须需要一个bootloader程序,也就说芯片上电以后先运行一段bootloader程序。这段bootloader程序会先初始化DDR等外设,然后将Linux内核flash(NAND,NOR FLASH,SD,MMC等)拷贝到DDR中,最后启动Linux内核。当然了,bootloader的实际工作要复杂的多,但是它最主要的工作就是启动Linux内核,bootloader和Linux内核的关系就跟PC上的BIOS和Windows的关系一样,bootloader就相当于BIOS。目前有很多现成的bootloader软件可以使用,比如U-Boot、vivi、RedBoot等等,其中以U-Boot使用最为广泛。(DDR是"Double Data Rate"的缩写,指的是双倍数据速率。)uboot官网为 http://www.denx.de/wiki/U-Boot/。
2.u-boot初次编译
首先在Ubuntu中安装ncurses 库,否则编译会报错,安装命令如下:
sudo apt-get install libncurses5-dev
将正点原子uboot和linux出厂源码uboot-imx-2016.03-2.1.0-g0ae7e33-v1.7.tar.bz2拷贝到ubuntu中并解压
tar -vxjf uboot-imx-2016.03-2.1.0-g0ae7e33-v1.7.tar.bz2
解压参数说明
tar
:是一个用于创建、查看和提取归档文件的命令行工具。-v
:表示"verbose",即在解压缩过程中显示详细信息,包括被解压缩的文件名。-x
:表示"extract",即从归档文件中提取文件。-j
:表示使用bzip2
算法进行解压缩。bzip2
是一种高压缩比的压缩算法,通常用于解压缩.bz2
格式的压缩文件。-f
:表示后面紧跟着要解压缩的归档文件的名称。
路径如下:
在alientek_uboot文件夹创建shell脚本文件mx6ull_alientek_emmc.sh进行uboot编译
#!/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- -j4
第 1行是 shell脚本要求的,必须是“#!/bin/bash”或者“#!/bin/sh”
第 2行使用了 make命令,用于清理工程,也就是每次在编译 uboot之前都清理一下工程。这里的 make命令带有三个参数,第一个是 ARCH,也就是指定架构,这里肯定是 arm;第二个参数CROSS_COMPILE,用于指定编译器,只需要指明编译器前缀就行了,比如arm-linux- gnueabihf-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目录中。
第4行有4个参数,用于编译 uboot,通过第3行配置好 uboot 以后就可以直接“make”编译 uboot 了。其中 V=1用于设置编译过程的信息输出级别;-j用于设置主机使用多少线程编译uboot,最好设置成我们虚拟机所设置的核心数,如果在VMware里面给虚拟就分配了4个核,那么使用-j4是最合适的,这样4个核都会一起编译。
使用chmod命令给予mx6ull_alientek_emmc.sh 文件可执行权限
chmod 777 mx6ull_alientek_emmc.sh
chmod命令介绍:
在 Unix、Linux 和类似的操作系统中,文件和目录都有权限属性,用于控制对它们的访问和操作。这些权限属性分为三个类别:所有者(Owner)、所属组(Group)和其他用户(Others)。
chmod
命令通过指定不同的权限参数来修改文件或目录的权限。常用的权限参数包括:
u
:表示所有者(Owner)的权限。g
:表示所属组(Group)的权限。o
:表示其他用户(Others)的权限。a
:表示所有用户(包括所有者、所属组和其他用户)的权限。权限参数后面可以跟上符号操作和权限操作的组合,常见的符号操作包括:
+
:添加权限。-
:移除权限。=
:设置权限。常见的权限操作包括:
r
:读权限(Read)。w
:写权限(Write)。x
:执行权限(Execute)。
chmod
命令还可以使用数字表示权限,每个权限用一个数字表示,其中读权限为 4,写权限为 2,执行权限为 1。通过将这些数字相加,可以得到所需的权限组合。例如,chmod 755 file.txt
表示将file.txt
文件的权限设置为-rwxr-xr-x
,即所有者具有读、写和执行权限,而所属组和其他用户具有读和执行权限。
然后就可以使用这个shell脚本文件来重新编译uboot,命令如下:
./mx6ull_alientek_emmc.sh
3.u-boot烧写与启动
uboot编译好以后就可以烧写到板子上使用了,将uboot烧写到SD卡中,然后通过SD卡启动来运行 uboot。使用imxdownload软件烧写,命令如下:
chmod 777 imxdownload //给予imxdownload可执行权限,一次即可
./imxdownload u-boot.bin /dev/sdb //烧写到SD卡,不能烧写到/dev/sda或sda1设备里面!
烧写完成后,将SD卡插到开发板上,设置拨码开关为SD卡启动,将USB线将USB_TTL与电脑连接,打开MobaXterm软件。复位开发板,在3秒倒计时期间按下回车键进入uboot的命令行模式。
4.u-boot命令使用
进入 uboot 的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前 uboot 所支持的命令。
输入如下命令即可查看“bootz”这个命令的用法:
4.1 信息查询命令
常用的和信息查询有关的命令有 3 个:bdinfo、printenv 和 version。先来看一下 bdinfo 命令,此命令用于查看板子信息,直接输入“bdinfo”即可
从上图 中可以得出 DRAM 的起始地址和大小、启动参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息。
命令“printenv”用于输出环境变量信息,uboot 也支持 TAB 键自动补全功能,输入“print” 然后按下 TAB 键就会自动补全命令,直接输入“print”也可以。输入“print”,然后按下回车键,环境变量如下图所示:
命令 version 用于查看 uboot 的版本号
从上图可以看出,当前 uboot 版本号为 2016.03,2023 年 8 月 27 日编译的,编译器为arm-linux-gnueabihf-gcc,这是 NXP 官方提供的编译器,正点原子出厂系统用的此编译器编译的,但是本教程我们统一使用 arm-linux-gnueabihf-gcc。
4.2 环境变量操作命令
(1)修改环境变量
环境变量的操作涉及到两个命令:setenv 和 saveenv,命令 setenv 用于设置或者修改环境变量的值。命令 saveenv 用于保存修改后的环境变量,一般环境变量是存放在外部 flash中的,uboot 启动的时候会将环境变量从flash 读取到DRAM 中。所以使用命令 setenv 修改的是DRAM中的环境变量值,修改以后要使用 saveenv 命令将修改后的环境变量保存到flash中,否则的话uboot 下一次重启会继续使用以前的环境变量值。
比如我们要将环境变量 bootdelay 改为 5,就可以使用如下所示命令:
setenv bootdelay 5
saveenv
执行结果如下:
在上图中,当我们使用命令 saveenv 保存修改后的环境变量的话会有保存过程提示信息,根据提示可以看出环境变量保存到了 MMC(0)中,也就是 SD 卡中。因为我们现在将 uboot 烧写到了 SD 卡里面,所以会保存到 MMC(0)中。如果烧写到 EMMC 里面就会提示保存到MMC(1),也就是 EMMC 设备。
修改 bootdelay 以后,重启开发板,uboot 就是变为 5 秒倒计时,如下图所示:
有时候修改的环境变量值可能会有空格,比如 bootcmd、bootargs 等,这个时候环境变量值就得用单引号括起来,比如下面修改环境变量 bootargs 的值:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv
(2)新建环境变量
命令 setenv 也可以用于新建命令,用法和修改环境变量一样,比如我们新建一个环境变量author,author 的值为我的名字拼音:ssz,那么就可以使用如下命令:
setenv author ssz
saveenv
(3)删除环境变量
既然可以新建环境变量,肯定也可以删除环境变量,删除环境变量也是使用命令 setenv, 要删除一个环境变量只要给这个环境变量赋空值即可,比如删除掉上面新建的author 这个环境变量,命令如下:
setenv author
saveenv
4.3 内存操作命令
内存操作命令就是用于直接对 DRAM(是动态随机存取存储器(Dynamic Random Access Memory)的缩写,是一种计算机内存类型。它是一种用于临时存储数据的半导体存储器,广泛应用于计算机系统、移动设备和其他电子设备中。) 进行读写操作的,常用的内存操作命令有md、nm、mm、mw、cp 和cmp。
(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
(2)nm命令
nm 命令用于修改指定地址的内存值,命令格式如下:
nm [.b, .w, .l] address
nm命令同样可以以.b、.w 和.l 来指定操作格式,比如现在以.l 格式修改0x80000000地址的数据为 0x12345678。输入命令:
bfffffff表示地址 0x80000000 现在的数据,?后面就可以输入要修改后的数据 0x12345678,输入完成以后按下回车,然后再输入‘q’即可退出.
修改完成以后在使用命令 md 来查看一下有没有修改成功,如上图所示。
(3)mm命令
mm 命令也是修改指定地址内存值的,使用 mm 修改内存值的时候地址会自增,而使用命令 nm 的话地址不会自增。比如以.l 格式修改从地址 0x80000000 开始的连续 3 个内存块(3*4=12 个字节)的数据为 0X05050505,操作如下图所示:
从上图可以看出,修改了地址 0X80000000、0X80000004 和 0X80000008 的内容为0x05050505。使用命令 md 查看修改后的值,结果如下图 所示:
(4)mw命令
命令 mw 用于使用一个指定的数据填充一段内存,命令格式如下:
mw [.b, .w, .l] address value [count]
mw 命令同样可以以.b、.w 和.l来指定操作格式,address 表示要填充的内存起始地址,value为要填充的数据,count 是填充的长度。比如使用.l 格式将以 0X80000000 为起始地址的 0x10 个内存块(0x10 * 4=64 字节)填充为 0X0A0A0A0A,命令如下:
(5)cp命令
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 个字节),命令如下所示:
(6)cmp命令
cmp 是比较命令,用于比较两段内存的数据是否相等,命令格式如下:
cmp [.b, .w, .l] addr1 addr2 count
cmp 命令同样可以以.b、.w 和.l 来指定操作格式,addr1 为第一段内存首地址,addr2 为第二段内存首地址,count 为要比较的长度。我们使用.l 格式来比较 0x80000000 和 0X80000100这两个地址数据是否相等,比较长度为 0x10 个内存块(16 * 4=64 个字节),命令如下所示:
4.4 网络操作命令
uboot是支持网络的,我们在移植 uboot 的时候一般都要调通网络功能,因为在移植 linux kernel 的时候需要使用到uboot 的网络功能做调试。uboot 支持大量的网络相关命令,比如 dhcp、ping、nfs 和 tftpboot。
开发板和PC主机用网线连接,连接网口2,设置环境变量
环境变量 | 描述 |
ipaddr | 开发板ip地址,可以不设置,使用dhcp命令来从路由器获取IP地址。 |
ethaddr | 开发板的MAC地址,一定要设置。 |
gatewayip | 网关地址 |
netmask | 子网掩码 |
serverip | 服务器IP地址,也就是Ubuntu主机IP地址,用于调试代码。 |
setenv ipaddr 192.168.1.55
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.66
saveenv
(1)ping命令
开发板的网络能否使用,是否可以和服务器(Ubuntu 主机)进行通信,通过 ping 命令就可以验证,直接 ping 服务器的 IP 地址即可,比如我的服务器 IP 地址为 192.168.1.66,命令如下:
(2)dhcp命令
dhcp 用于从路由器获取 IP 地址,前提得开发板连接到路由器上的,如果开发板是和电脑直连的,那么 dhcp 命令就会失效,直接输入 dhcp 命令即可通过路由器获取到 IP 地址,我的开发板与电脑连接,这里不予展示
(3)nfs命令
后面进行Linux 驱动开发的时候需要NFS 启动,因此要先安装并开启Ubuntu 中的NFS 服务,使用如下命令安装NFS 服务:
先检查看是否安装:
dpkg -s nfs-kernel-server
如果没安装,执行下面命令进行安装:
sudo apt-get install nfs-kernel-server rpcbind
等待安装完成,安装完成以后在用户根目录下创建一个名为“linux”的文件夹,以后所有的东西都放到这个“linux”文件夹里面,在“linux”文件夹里面新建一个名为“nfs”的文件夹,如下图所示:
上图创建的nfs 文件夹供nfs 服务器使用,以后我们可以在开发板上通过网络文件系统来访问nfs 文件夹,要先配置nfs,使用如下命令打开nfs 配置文件/etc/exports:
sudo vi /etc/exports
打开/etc/exports 以后在后面添加如下所示内容:
/home/ssz/linux/nfs *(rw,sync,no_root_squash)
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/home/ssz/linux/nfs *(rw,sync,no_root_squash)
重启NFS 服务,使用命令如下:
sudo /etc/init.d/nfs-kernel-server restart
注:ubuntu17.10之后的版本nfs默认只支持协议3和4,但后续我们使用的uboot默认使用协议2,所以需要在/etc/default/nfs-kernel-server文件末尾加一句内容:
RPCNFSDOPTS=‘“--nfs-version 2,3,4 --debug --syslog”
然后重启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目录中,uboot 中的 nfs 命令格式如下所示:
nfs [loadAddress] [[hostIPaddr:]bootfilename]
loadAddress 是要保存的 DRAM 地址,[[hostIPaddr:]bootfilename]是要下载的文件地址,将出厂系统镜像的kernel镜像文件zimage文件放到nfs目录下
准备好以后就可以使用 nfs 命令来将 zImage 下载到开发板 DRAM 的 0X80800000 地址处, 命令如下:
(4)tftp命令
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/ssz/linux/tftpboot
chmod 777 /home/ssz/linux/tftpboot
最后配置 tftp,安装完成以后新建文件/etc/xinetd.d/tftp,如果没有/etc/xinetd.d 目录的话自行创建,然后在里面输入如下内容:
server tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /home/ssz/linux/tftpboot/
disable = no
per_source = 11
cps = 100 2
flags = IPv4
}
完了以后启动 tftp 服务,命令如下:
sudo service tftpd-hpa start
打开/etc/default/tftpd-hpa 文件,将其修改为如下所示内容:
# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/ssz/linux/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="-l -c -s"
TFTP_DIRECTORY 就是我们上面创建的 tftp 文件夹目录,以后我们就将所有需要通过TFTP 传输的文件都放到这个文件夹里面,并且要给予这些文件相应的权限。最后输入如下命令, 重启 tftp 服务器:
sudo service tftpd-hpa restart
将 zImage 镜像文件拷贝到 tftpboot 文件夹中,并且给予 zImage 相应的权限
uboot 中的 tftp 命令格式如下:
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
看起来和nfs命令格式一样的,loadAddress是文件在DRAM中的存放地址,[[hostIPaddr:]bootfilename]是要从Ubuntu 中下载的文件。但是和 nfs 命令的区别在于,tftp 命令不需要输入文件在Ubuntu 中的完整路径,只需要输入文件名即可。比如我们现在将 tftpboot 文件夹里面的zImage 文件下载到开发板 DRAM 的 0X80800000 地址处,命令如下:
4.5 EMMC和SD卡操作命令
uboot 支持 EMMC 和 SD 卡,因此也要提供 EMMC 和 SD 卡的操作命令。一般认为 EMMC和 SD 卡是同一个东西,所以没有特殊说明,本教程统一使用 MMC 来代指 EMMC 和 SD 卡。uboot 中常用于操作 MMC 设备的命令为“mmc”。
命令 | 描述 |
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 寄存器的值。 |
(1)mmc info命令
mmc info 命令用于输出当前选中的 mmc info 设备的信息
(2)mmc rescan命令
mmc rescan 命令用于扫描当前开发板上所有的 MMC 设备,包括 EMMC 和 SD 卡,输入“mmc rescan”即可。
(3)mmc list命令
mmc list 命令用于来查看当前开发板一共有几个 MMC 设备
(4)mmc dev命令
mmc dev 命令用于切换当前 MMC 设备,命令格式如下:
mmc dev [dev] [part]
[dev]用来设置要切换的 MMC 设备号,[part]是分区号。如果不写分区号的话默认为分区 0。使用如下命令切换到 SD 卡:
mmc dev 0 //切换到 SD 卡,0 为 SD 卡,1 为 eMMC
(5)mmc part命令
有时候 SD 卡或者 EMMC 会有多个分区,可以使用命令“mmc part”来查看其分区,比如查看 EMMC 的分区情况,输入如下命令:
mmc dev 1 //切换到 EMMC
mmc part //查看 EMMC 分区
从上图中可以看出,此时 EMMC 有两个分区,第一个分区起始扇区为 20480,长度为1024000个扇区;第二个分区起始扇区为 1228800,长度为 14041088个扇区。
如果 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
(6)mmc read命令
mmc read 命令用于读取 mmc 设备的数据,命令格式如下:
mmc read addr blk# cnt
addr 是数据读取到 DRAM 中的地址,blk 是要读取的块起始地址(十六进制),一个块是 512字节,这里的块和扇区是一个意思,在 MMC 设备中我们通常说扇区,cnt 是要读取的块数量(十六进制)。比如从 EMMC 的第 1536(0x600)个块开始,读取 16(0x10)个块的数据到 DRAM 的
0X80800000 地址处,命令如下:
这里我们还看不出来读取是否正确,通过 md.b 命令查看 0x80800000 处的数据就行了,查看 16*512=8192(0x2000)个字节的数据,命令如下
md.b 80800000 2000
(7)mmc write命令
mmc read命令用于读取 mmc 设备的数据,命令格式如下:
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 版本号,注意编译时间,输入命令:
可以看出当前 SD 卡中的 uboot 是 2023 年 8 月 27 日 17:38:18 编译的。我们现在重新编译一下 uboot,然后将编译出来的 u-boot.imx(u-boot.bin 前面加了一些头文件)拷贝到 Ubuntu 中的tftpboot 目录下。最后使用 tftp 命令将其下载到 0x80800000 地址处,命令如下:
可以看出,u-boot.imx 大小为 363520 字节,363520/512=710,所以我们要向 SD 卡中写入742 个块,如果有小数的话就要加 1 个块。使用命令“mmc write”从 SD 卡分区 0 第 2 个块(扇区)开始烧写,一共烧写 710(0x2C6)个块,命令如下:
烧写成功,重启开发板(从 SD 卡启动),重启以后再输入 version 来查看版本号,结果如下图所示:
从上图可以看出,此时的 uboot 是 20203年 8月 27 号 22:56:39 编译的,说明 uboot 更新成功。这里我们就学会了如何在SD卡 uboot 中更新 uboot 了,如果要更新 EMMC 中的 uboot 也是一样的。
同理,如果要在 uboot 中更新 EMMC 对应的 uboot,可以使用如下所示命令:
把拨码开关移动至EMMC启动,重启开发板就可以看到uboot日期改变
(8)mmc erase命令
如果要擦除 MMC 设备的指定块就是用命令“mmc erase”,命令格式如下:
mmc erase blk# cnt
blk 为要擦除的起始块,cnt 是要擦除的数量。没事不要用 mmc erase 来擦除 MMC 设备!!!
4.6 FAT 格式文件系统操作命令
有时候需要在 uboot 中对 SD 卡或者 EMMC 中存储的文件进行操作,这时候就要用到文件操作命令,跟文件操作相关的命令有:fatinfo、fatls、fstype、fatload 和 fatwrite,但是这些文件操作命令只支持 FAT 格式的文件系统!!
(1)fatinfo命令
fatinfo 命令用于查询指定 MMC 设备分区的文件系统信息,格式如下:
fatinfo <interface> [<dev[:part]>]
interface 表示接口,比如 mmc,dev 是查询的设备号,part 是要查询的分区。比如我们要查
询 EMMC 分区 1 的文件系统信息,命令如下:
从上图可以看出,EMMC 分区 1 的文件系统为 FAT32 格式的。
(2)fatls命令
fatls 命令用于查询 FAT 格式设备的目录和文件信息,命令格式如下:
fatls <interface> [<dev[:part]>] [directory]
interface 是要查询的接口,比如 mmc,dev 是要查询的设备号,part 是要查询的分区,directory
是要查询的目录。比如查询 EMMC 分区 1 中的所有的目录和文件,输入命令:
从上图可以看出,emmc 的分区 1 中存放着 2 个文件。
(3)fstype命令
fstype 用于查看 MMC 设备某个分区的文件系统格式,命令格式如下:
fstype <interface> <dev>:<part>
正点原子 EMMC 核心板上的 EMMC 默认有 3 个分区,我们来查看一下这三个分区的文件系统格式,输入命令:
从上图可以看出,分区 0 格式未知,因为分区 0 存放的 uboot,并且分区 0 没有格式化,所以文件系统格式未知。分区 1 的格式为 fat,分区 1 用于存放 linux 镜像和设备树。分区 2 的格式为 ext4,用于存放 Linux 的根文件系统(rootfs)。
(4)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 地址处,命令如下:
(5) 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 目录下。
拷贝完成以后使用命令 tftp 将zImage 下载到DRAM 的 0X80800000 地址处,命令如下:
zImage 大小为 6785480(0X6789c8)个字节(注意,由于开发板系统在不断的更新中,因此zImage 大小不是固定的,一切以实际大小为准),接下来使用命令 fatwrite 将其写入到 EMMC 的分区 1 中,文件名字为 zImage,命令如下:
完成以后使用“fatls”命令查看一下EMMC 分区 1 里面的文件
4.7 EXT 格式文件系统操作命令
uboot 有 ext2 和 ext4 这两种格式的文件系统的操作命令,常用的就四个命令,分别为ext2load、ext2ls、ext4load、ext4ls 和ext4write。这些命令的含义和使用与 fatload、fatls 和 fatwrite 一样,只是 ext2 和 ext4 都是针对 ext 文件系统的。比如 ext4ls 命令,EMMC 的分区 2 就是ext4 格式的,使用 ext4ls 就可以查询 EMMC 的分区 2 中的文件和目录,输入命令:
4.8 BOOT操作命令
uboot 的本质工作是引导 Linux,所以 uboot 肯定有相关的 boot(引导)命令来启动 Linux。常用的跟 boot 有关的命令有:bootz、bootm 和 boot。
(1)bootz命令
要启动 Linux,需要先将 Linux 镜像文件拷贝到 DRAM 中,如果使用到设备树的话也需要将设备树拷贝到 DRAM 中。可以从 EMMC存储设备中将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 文件夹里面。
Linux 镜像文件和设备树都准备好了,我们先学习如何通过网络启动 Linux,使用 tftp 命令将zImage 下载到DRAM 的0X80800000 地址处,然后将设备树imx6ull-14x14-emmc-4.3-800x480- c.dtb 下载到 DRAM 中的 0X83000000 地址处,最后之后命令 bootz 启动,命令如下:
上图就是我们通过 tftp 和 bootz 命令来从网络启动 Linux 系统,如果我们要从 EMMC 中启动Linux 系统的话只需要使用命令fatload 将zImage 和imx6ull-14x14-emmc-4.3-800x480-c.dtb 从EMMC 的分区 1 中拷贝到 DRAM 中,然后使用命令 bootz 启动即可。先使用命令 fatls 查看要下 EMMC 的分区 1 中有没有 Linux 镜像文件和设备树文件,如果没有的话参考前面的 fatwrite 命令将 tftpboot 中的 zImage 和 imx6ull-14x14-emmc-4.3-800x480-c.dtb 文件烧写到EMMC 的分区 1 中。
然后使用命令 fatload 将zImage 和imx6ull-14x14-emmc-4.3-800x480-c.dtb 文件拷贝到DRAM 中,地址分别为 0X80800000 和 0X83000000,最后使用 bootz 启动,命令如下:
(2)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 为空的话,同样是用“-”来替代。
(3)boot命令
boot 命令也是用来启动Linux 系统的,只是 boot 会读取环境变量 bootcmd 来启动 Linux 系统,bootcmd 是一个很重要的环境变量!其名字分为“boot”和“cmd”,也就是“引导”和“命令”,说明这个环境变量保存着引导命令,其实就是启动的命令集合,具体的引导命令内容是可以修改的。
比如我们要想使用 tftp 命令从网络启动 Linux 那么就可以设置 bootcmd 为“tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-4.3-800x480-c.dtb; bootz 80800000 - 83000000”,然后使用 saveenv 将 bootcmd 保存起来。然后直接输入 boot 命令即可从网络启动Linux 系统,命令如下
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-4.3-800x480-c.dtb; bootz 80800000 - 83000000'
saveenv
boot
前面说过 uboot 倒计时结束以后就会启动Linux 系统,其实就是执行的 bootcmd 中的启动命令。只要不修改 bootcmd 中的内容,以后每次开机uboot 倒计时结束以后都会使用 tftp 命令从网络下载zImage 和 imx6ull-14x14-emmc-4.3-800x480-c.dtb,然后启动 Linux。
如果想从 EMMC 启动那就设置 bootcmd 为“fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-14x14-emmc-4.3-800x480-c.dtb; bootz 80800000 - 83000000”,然后使用 boot命令启动即可,命令如下
如果不修改 bootcmd 的话,每次开机 uboot 倒计时结束以后都会自动从 EMMC 里面读取zImage 和 imx6ull-14x14-emmc-4.3-800x480-c.dtb,然后启动 Linux。
在启动 Linux 内核的时候可能会遇到如下错误
“Kernel panic – not Syncing: VFS: Unable to mount root fs on unknown-block(0,0)”
这个错误的原因是 linux 内核没有找到根文件系统,这个很正常,因为没有设置 uboot 的bootargs 环境变量,配置了根文件系统就没有了
4.9 其他常用操作命令
uboot 中还有其他一些常用的命令,比如reset、go、run 和 mtest 等。
(1)reset命令
(2) go命令
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 这个应用,命令如下:
从上图 可以看出,通过go命令我们就可以在uboot 中运行裸机例程。
(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 imx6ull-
14x14-emmc-4.3-800x480-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-4.3-800x480-c.dtb; bootz 80800000 - 83000000'
saveenv
创建环境变量成功以后就可以使用 run 命令来运行 mybootemmc、mybootnet 或 mybootnand来实现不同的启动:
run mybootemmc
run mytoobnand
run mybootnet
(4)mtest命令
mtest 命令是一个简单的内存读写测试命令,可以用来测试自己开发板上的 DDR,命令格式如下:
mtest [start [end [pattern [iterations]]]]
start 是要测试的DRAM 开始地址,end 是结束地址,比如我们测试 0X80000000~0X80001000这段内存,输入“mtest 80000000 80001000”