【正点原子Linux连载】第三章 U-Boot使用 摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第三章 U-Boot使用

U-Boot用于引导Linux系统,所以要先使用一下U-Boot,体验一下U-Boot是个什么东西。正点原子ATK-DLRK3568开发板出厂系统已经烧写了U-Boot,所以可以直接拿开发板体验U-Boot。当然了,提供的SDK包里面也有U-Boot源码,大家也可以自行编译U-Boot源码烧写测试。本章我们主要学习使用U-Boot的相关命令。

3.1 U-Boot简介
Linux 系统要启动需要通过bootloader 程序引导,也就说芯片上电以后先运行一段bootloader程序。这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(NAND,NOR FLASH,SD,EMMC 等)拷贝到 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 官网为https://www.denx.de/project/u-boot/,如图3.1.1所示:
在这里插入图片描述

图3.1.1 uboot官网
我们可以在 uboot 官方FTP下载服务器地址为https://ftp.denx.de/pub/u-boot/,进入此 FTP 服务器即可看到 uboot 源码,如图3.1.2所示:
在这里插入图片描述

图3.1.2 uboot源码
图3.1.2中就是 uboot 原汁原味的源码文件,目前最新的版本是 2023.01-rc2。但是我们一般不会直接用 uboot 官方的 U-Boot 源码的。uboot 官方的 uboot 源码是给半导体厂商准备的,半导体厂商会下载 uboot 官方的 uboot 源码,然后将自家相应的芯片移植进去。也就是说半导体厂商会自己维护一个版本的 uboot,这个版本的 uboot 相当于是他们定制的。既然是定制的,那么肯定对自家的芯片支持会很全,虽然 uboot 官网的源码中一般也会支持他们的芯片,但是绝对是没有半导体厂商自己维护的 uboot 全面。瑞芯微的SDK包提供了2017.09版本的uboot。
Linux SDK包中uboot源码如图3.1.3所示:(请按开发板光盘10、用户手册【正点原子】ATK-DLRK3568嵌入式Linux统开发手册.pdf中的第四章节安装SDK,并检索出源码)
在这里插入图片描述

图3.1.3 RK3568官方uboot源码
图3.1.3中的“u-boot”就是RK3568官方uboot源码包,它支持RK3568芯片,而且支持各种启动方式,比如EMMC、NAND 等等,这些都是 uboot 官方所不支持的。但是图3.1.3中的 uboot 是针对正点原子根据自家ATK-DLRK3568开发板移植好的。如果直接使用的瑞芯微原厂的SDK包,那么可能需要根据自己所使用的平台进行移植,使其支持自己做的板子,本教程不涉及uboot的移植,后面的所有实验均基于正点原子出厂SDK中已经移植好的uboot。
3.2 U-Boot初次编译
3.2.1 编译与烧写
RK3568的U-Boot的编译与烧写请参考开发板光盘10、用户手册【正点原子】ATK-DLRK3568嵌入式linux系统开发手册.pdf中的“4.4.1 单独编译U-Boot”和“5.3.5使用rkflash.sh脚本烧写(找到单独烧写uboot指令,自行烧写)”小节。
3.2.2 U-Boot启动过程
烧写完成以后用USB Type-C线将开发板上的UART接口与电脑连接起来,因为我们要在串口终端里面输入命令来操作uboot。
打开MobaXterm,设置好串口参数,最后复位开发板。在 MobaXterm 上出现“Hit key to stop autoboot(‘CTRL+C’):”倒计时的时候按下键盘上“CTRL+C”按键。为了加快系统启动速度,开发板默认是 0秒倒计时,所以大家一般很难卡准时间按下按键。所以建议大家按下开发板复位按键的时候,就一直按CTRL+C,这样就能很轻松的进入到uboot的命令行。
注意!一般uboot在倒计时的时候随便按下键盘上哪个按键都可以进入uboot命令行,但是RK3568的uboot应该是被瑞芯微改过,只能按“CTRL+C”组合键!按其他按键是不能打断启动过程计入命令行的。
进入 uboot 的命令行模式以后如图3.2.2.1所示:
在这里插入图片描述

图3.2.2.1 uboot启动log信息
从图3.2.2.1可以看出,当进入到 uboot 的命令行模式以后,左侧会出现一个“=>”
简单介绍一下uboot启动过程中都打印出了哪些信息:
1、uboot版本号,比如图3.2.2.1中当前uboot版本号是2017.09,编译时间为2023年5月11号11:59:33。
2、板子信息,当前板子是瑞芯微的RK3568 Evaluation开发板,这个信息是可以改的,因为正点原子ATK-DLRK3568开发板是直接参考瑞芯微官方的RK3568开发板移植的uboot,所以这部分信息也就没改。
3、DDR大小为4GB(请参考根据个人开发板的配置,有可能为2G/4G等)
4、EMMC启动设备信息。
5、PMIC芯片(RK809)信息。
6、DRM信息,也就是屏幕信息。
7、RK3568芯片时钟信息。
8、……
最后是“Hit key to stop autoboot(‘CTRL+C’):”倒计时提示,倒计时结束之前按下“CTRL+C”组合键就会进入 Uboot命令行模式。如果在倒计时结束以后没有按下“CTRL+C”组合键,那么 Linux 内核就会启动,Linux 内核一旦启动,uboot 就会寿终正寝。
uboot的主要作用是引导kernel,我们现在已经进入 uboot 的命令行模式了,进入命令行模式以后就可以给 uboot 发号施令了。当然了,不能随便发号施令,得看看 uboot 支持哪些命令,然后使用这些uboot 所支持的命令来做一些工作,下一节就讲解 uboot 命令的使用。
3.3 U-Boot命令使用
进入uboot的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前uboot所支持的命令,如图3.3.1所示:
在这里插入图片描述

图3.3.1 uboot的命令列表(部分)
图3.3.1中只是 uboot 的一部分命令,具体的命令列表以实际为准。图3.3.1中的命令并不是 uboot 所支持的所有命令,uboot 是可配置的,需要什么命令就使能什么命令。所以图3.3.1中的命令是正点原子提供的 uboot 中使能的命令,uboot 支持的命令还有很多,而且也可以在 uboot 中自定义命令。这些命令后面都跟有命令说明,用于描述此命令的作用,但是命令具体怎么用呢?我们输入“help(或?) 命令名”就可以查看命令的详细用法,以“boot_fit”这个命令为例,我们输入如下命令即可查看“boot_fit”这个命令的用法:
? boot_fit 或 help boot_fit
结果如图3.3.2所示:
在这里插入图片描述

图3.3.2 boot_fit命令使用说明
图3.3.2列出了“boot_fit”这个命令的详细说明,其它的命令也可以使用此方法查询具体的使用方法。接下来我们学习一下一些常用的 uboot 命令。
3.3.1 查询命令
常用的和信息查询有关的命令有 3 个:bdinfo、printenv 和 version。先来看一下 bdinfo 命令,此命令用于查看板子信息,直接输入“bdinfo”即可,结果如图3.3.1.1示:
在这里插入图片描述

图3.3.1.1 bdinfo 命令
从图3.3.1.1中可以看出 DRAM 的起始地址和大小、BOOT参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息。命令“printenv”用于输出环境变量信息,uboot 也支持 TAB 键自动补全功能,输入“print”然后按下 TAB 键就会自动补全命令。直接输入“print”也可以,因为整个uboot命令中只有printenv的前缀是“print”,所以当输入print以后就只有printenv命令了。输入“print”,然后按下回车键,环境变量如图3.3.1.2所示:
在这里插入图片描述

图3.3.1.2 printenv 命令部分结果
图3.3.1.2只是printenv命令的部分内容,RK3568的uboot环境变量有很多。uboot 中的环境变量都是字符串,既然叫做环境变量,那么它的作用就和“变量”一样。比如 bootdelay 这个环境变量就表示 uboot 启动延时时间,如果将 bootdelay 改为 5 的话就会倒计时 5s了。uboot 中的环境变量是可以修改的,有专门的命令来修改环境变量的值,稍后我们会讲解。
命令 version 用于查看 uboot 的版本号,输入“version”,uboot 版本号如图3.3.1.3所示:
在这里插入图片描述

图3.3.1.3 version命令结果
3.3.2 环境变量操作命令
1、修改环境变量
环境变量的操作涉及到两个命令:setenv 和 saveenv,setenv命令用于设置或者修改环境变量的值。命令 saveenv 用于保存修改后的环境变量,一般环境变量存放在外部 flash 中,uboot 启动的时候会将环境变量从 flash 读取到 DRAM 中。所以使用命令 setenv 修改的是 DRAM中的环境变量值,修改以后要使用 saveenv 命令将修改后的环境变量保存到 flash 中,否则uboot下一次重启会继续使用以前的环境变量值。
瑞芯微官方SDK中的uboot默认没有使能saveenv命令,但是正点原子出厂SDK里面已经使能了,所以如果你用的是其他品牌的开发板,可能无法使用saveenv命令。
命令saveenv使用起来很简单,格式为:
saveenv
比如我们要将环境变量 bootdelay 改为 5,就可以使用如下所示命令:
setenv bootdelay 5
saveenv
上述命令执行过程如图3.3.2.1所示:
在这里插入图片描述

图3.3.2.1 环境变量修改
在图3.3.2.1中,当我们使用命令 saveenv 保存修改后的环境变量会有保存过程提示信息,根据提示可以看出环境变量保存到了 MMC(0)中,也就是 EMMC中。修改 bootdelay以后,重启开发板,uboot 就是变为 5 秒倒计时,如图3.3.2.2所示:
在这里插入图片描述

图3.3.2.2 5秒倒计时
从图3.3.2.2可以看出,此时uboot启动倒计时变为了5秒。
2、新建环境变量
命令setenv也可以用于新建命令,用法和修改环境变量一样,比如我们新建一个环境变量author,author的值为‘alientek’,那么就可以使用如下命令:
setenv author ‘alientek’
saveenv
author命令创建完成以后重启 uboot,然后使用命令 printenv 查看当前环境变量,如图3.3.2.3所示:
在这里插入图片描述

图3.3.2.3 新建的author环境变量值
3、删除环境变量
既然可以新建环境变量,那么就可以删除环境变量,删除环境变量也是使用命令 setenv,
要删除一个环境变量只要给这个环境变量赋空值即可,比如我们删除掉上面新建的 author 环境变量,命令如下:
setenv author
saveenv
上面命令中通过 setenv 给 author 赋空值,也就是什么都不写来删除环境变量 author。重启uboot 就会发现环境变量 author 没有了。
3.3.3 内存操作命令
内存操作命令就是用于直接对 DRAM 进行读写操作的,常用的内存操作命令有 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,也就是 202=40个字节;如果显示格式为.l 的话就表示 20个long,也就是204=80个字节,另外要注意:
uboot命令中的数字都是十六进制的!不是十进制的!
比如你想查看0X08300000开始的20个字节的内存值,显示格式为.b的话,应该使用如下所示命令:
md.b 8300000 14
而不是:
md.b 8300000 20
上面说了,uboot命令里面的数字都是十六进制的,所以可以不用写“0x”前缀,十进制的 20对应的十六进制为 0x14,所以命令md后面的个数应该是14,如果写成20的话就表示查看32(十六进制为0x20)个字节的数据。分析下面三个命令的区别:
md.b 8300000 10
md.w 8300000 10
md.l 8300000 10
上面这三个命令都是查看以0X08300000为起始地址的内存数据,第一个命令以.b格式显示,长度为0x10,也就是16个字节;第二个命令以.w 格式显示,长度为 0x10,也就是 162=32个字节;最后一个命令以.l 格式显示,长度也是 0x10,也就是164=64个字节。这三个命令的执行结果如图3.3.3.1所示:
在这里插入图片描述

图3.3.3.1 md命令使用示例
2、nm命令
nm 命令用于修改指定地址的内存值,命令格式如下:
nm [.b, .w, .l] address
nm命令同样可以以.b、.w 和.l 来指定操作格式,比如现在以.l格式修改0X08300000地址的数据为0x12345678。输入命令:
nm.l 8300000
输入上述命令以后如图3.3.3.2所示:
在这里插入图片描述

图3.3.3.2 nm命令
在图3.3.3.2中,08300000表示现在要修改的内存地址,edfe0dd0表示地址0X08300000 现在的数据,‘?’后面就可以输入要修改后的数据 0x12345678,输入完成以后按下回车,然后再输入‘q’即可退出(大小写不敏感),如图3.3.3.3所示:
在这里插入图片描述

图3.3.3.3 修改内存数据
修改完成以后再使用md命令来查看一下有没有修改成功,如图3.3.3.4所示:
在这里插入图片描述

图3.3.3.4 查看修改后的值
从图3.3.3.4可以看出,此时地址 0X08300000的值变为了0X12345678。
3、nm命令
mm命令也是修改指定地址内存值的,使用mm修改内存值的时候地址会自增,而使用nm 命令的话地址不会自增。比如以.l格式修改从地址0X08300000开始的连续3个内存块(3*4=12个字节)的数据为0X05050505,操作如图3.3.3.5所示:
在这里插入图片描述

图3.3.3.5 命令mm
从图3.3.3.5可以看出,修改了地址0X08300000、0X08300004和 0X0830000C 的内容为0x05050505。使用命令 md 查看修改后的值,结果如图3.3.3.6所示:
在这里插入图片描述

图3.3.3.6 查看修改后的内存数据
从图3.3.3.6可以看出内存数据修改成功。
4、mw命令
命令mw用于使用一个指定的数据填充一段内存,命令格式如下:
mw [.b, .w, .l] address value [count]
mw命令同样以.b、.w和.l来指定操作格式,address表示要填充的内存起始地址,value为要填充的数据,count是填充的长度。比如使用.l格式将以0X08300000为起始地址的0x10 个内存块(0x10 * 4=64字节)填充为0X0A0A0A0A,命令如下:
mw.l 8300000 0A0A0A0A 10
然后使用命令md来查看,如图3.3.3.7所示:
在这里插入图片描述

图3.3.3.7 查看修改后的内存数据
从图3.3.3.7以看出内存数据修改成功。
5、cp命令
cp是数据拷贝命令,用于将DRAM中的数据从一段内存拷贝到另一段内存中,或者把 NorFlash中的数据拷贝到DRAM中。命令格式如下:
cp [.b, .w, .l] source target count
cp命令同样以.b、.w和.l来指定操作格式,source为源地址,target为目的地址,count为拷贝的长度。我们使用.l格式将0x08300000处的地址拷贝到0x08300100处,长度为0x10个内存块(0x10 * 4=64个字节),命令如下所示:
cp.l 8300000 8300100 10
结果如图3.3.3.8所示:
在这里插入图片描述

图3.3.3.8 cp命令操作结果
在图3.3.3.8中,先使用md.l命令打印出地址0x08300000和0x08300100处的数据,然后使用命令cp.l将0x08300000处的数据拷贝到0x08300100处。最后使用md.l查看0x08300100处的数据有没有变化,检查拷贝是否成功。
6、cmp命令
cmp是比较命令,用于比较两段内存的数据是否相等,命令格式如下:
cmp [.b, .w, .l] addr1 addr2 count
cmp命令同样以.b、.w和.l来指定操作格式,addr1为第一段内存首地址,addr2为第二段内存首地址,count为要比较的长度。我们使用.l格式来比较0x08300000和0x08300100这两个地址数据是否相等,比较长度为0x10个内存块(16 * 4=64个字节),命令如下所示:
cmp.l 8300000 8300100 10
结果如图3.3.3.9所示:
在这里插入图片描述

图3.3.3.9 cmp命令比较结果
从图3.3.3.9可以看出两段内存的数据相等。我们再随便挑两段内存比较一下,比如地址0x08300000和0x08300200,长度为0X10,比较结果如图3.3.3.10所示:
在这里插入图片描述

图3.3.3.10 cmp命令比较结果
从图3.3.3.10可以看出,0x08300000处的数据和0x08300200处的数据就不一样。
3.3.4 网络操作命令
uboot是支持网络的,我们在移植uboot的时候一般都要调通网络功能,因为在移植linux kernel的时候需要使用到uboot的网络功能做调试。uboot支持大量的网络相关命令,比如dhcp、ping、nfs和tftpboot,我们接下来依次学习一下这几个和网络有关的命令。
建议开发板和主机PC都连接到同一个路由器上!最后设置表3.3.4.1所示的几个环境变量:
在这里插入图片描述

表3.3.4.1 网络相关环境变量
表3.3.4.1中环境变量设置命令如下所示:下面指令尽量手敲,复制有可能提示找不到指令!
setenv ipaddr 192.168.6.38
setenv ethaddr b8:ae:1d:01:01:00 //有的uboot会默认设置ethaddr,然后禁止修改,RK3568是禁止修改的,所以这个我们可以不用设置
setenv gatewayip 192.168.6.1
setenv netmask 255.255.255.0
setenv serverip 192.168.6.227
saveenv

注意,网络地址环境变量的设置要根据自己的实际情况,确保Ubuntu主机和开发板的IP地址在同一个网段内,比如我现在的开发板和电脑都在192.168.6.0 这个网段内,所以设置开发板的IP地址为192.168.6.38,我的Ubuntu主机的地址为192.168.6.227,因此serverip就是192.168.6.227。ethaddr为网络MAC地址,是一个48bit的地址,如果在同一个网段内有多个开发板的话一定要保证每个开发板的ethaddr是不同的,否则通信会有问题!设置好网络相关的环境变量以后就可以使用网络相关命令了。
1、ping命令
开发板的网络能否使用,是否可以和服务器(Ubuntu主机)进行通信,通过ping命令就可以验证,直接ping服务器的IP地址即可,比如我的服务器IP地址为192.168.6.227,命令如下:
ping 192.168.6.227
结果如图3.3.4.1所示:
在这里插入图片描述

图3.3.4.1 ping命令
从图3.3.4.1可以看出,192.168.6.222这个主机存在,说明ping成功,uboot的网络工作正常。
注意!只能在uboot中ping其他的机器,其他机器不能ping uboot,因为uboot没有对ping命令做处理,如果用其他的机器ping uboot的话会失败!
2、dhcp 命令
dhcp用于从路由器获取IP地址,前提是开发板得连接到路由器上的,如果开发板是和电脑直连的,那么dhcp命令就会失效。直接输入dhcp命令即可通过路由器获取到IP地址,如图3.3.4.2所示:
在这里插入图片描述

图3.3.4.2 dhcp 命令
从图3.3.4.2可以看出,开发板通过dhcp获取到的IP地址为192.168.6.38。从图3.3.4.2可以看出,dhcp命令不单单是获取IP地址,其还会通过TFTP来启动linux内核,输入“? dhcp”即可查看dhcp命令详细的信息,如图3.3.4.3所示:
在这里插入图片描述

图3.3.4.3 dhcp命令使用查询
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目录,要先安装并开启Ubuntu中的NFS服务,使用如下命令安装NFS服务:
sudo apt-get install nfs-kernel-server rpcbind
等待安装完成,安装完成以后在用户根目录下创建一个名为“linux”的文件夹,以后所有的东西都放到这个“linux”文件夹里面,在“linux”文件夹里面新建一个名为“nfs”的文件夹,如图3.3.4.4所示:
在这里插入图片描述

图3.3.4.4创建linux工作目录
图3.3.4.4中创建的nfs文件夹供nfs服务器使用,以后我们可以在开发板上通过网络文件系统来访问nfs文件夹,要先配置nfs,使用如下命令打开nfs配置文件/etc/exports:
sudo vi /etc/exports
打开/etc/exports以后在后面添加如下所示内容:
/home/alientek/linux/nfs *(rw,sync,no_root_squash)
添加完成以后的/etc/exports如图3.3.4.5所示:
在这里插入图片描述

图3.3.4.5 修改文件/etc/exports
Ubuntu20.04中默认关闭了NFS V2版本,uboot里面使用nfs命令下载可能失败,所以需要打开NFS V2版本。打开/etc/default/nfs-kernel-server,加入下面这行:
RPCNFSDOPTS=“–nfs-version 2,3,4 --debug --syslog”
如图3.3.4.6所示:
在这里插入图片描述

图3.3.4.6 使能NFS V2
最后重启NFS服务,使用命令如下:
sudo /etc/init.d/nfs-kernel-server restart
至此,Ubuntu下的NFS服务搭建完毕。
uboot中的nfs命令格式如下所示:
nfs [loadAddress] [[hostIPaddr:]bootfilename]
loadAddress是要保存的DRAM地址,[[hostIPaddr:]bootfilename]是要下载的文件地址。这里我们将正点原子官方编译出来的Linux内核镜像文件boot.img下载到开发板DRAM的0X30000000这个地址处(这个地址是笔者自己随便定义的)。正点原子编译出来的boot.img文件已经放到了开发板光盘中,未替换路径为:开发板光盘A 09、系统镜像01、出厂系统SDK镜像boot.img。将boot.img文件通过FileZilla发送到Ubuntu中的NFS目录下:/home/alientek/linux/nfs,完成以后的NFS目录如图3.3.4.7所示:
在这里插入图片描述

图3.3.4.7 nfs目录
准备好以后就可以使用nfs命令来将boot.img下载到开发板DRAM的0X7B3C5880地址处,命令如下:
nfs 30000000 192.168.6.227:/home/alientek/linux/nfs/boot.img
命令中的“30000000”表示boot.img在DDR中的首地址,“192.168.6.227:/home/alientek/linux/nfs/boot.img”表示boot.img在192.168.6.227这个主机中,路径为/home/alientek/linux/nfs/boot.img。下载过程如图3.3.4.8所示:
在这里插入图片描述

图3.3.4.8 nfs命令下载uImage过程
图3.3.4.8中通过网络下载boot.img的时候会打印出‘#’表示正在下载,如果网络环境不好,下载有干扰的话就会打印出一个‘T’,这个没事的,只要它能正常下载成功,最终打印出“done”即可。
下载完成以后查看0x30000000地址处的数据,使用命令md.b来查看前0x100(256)个字节的数据,如图3.3.4.9所示:
在这里插入图片描述

图3.3.4.9 下载的数据
再使用winhex软件来查看刚刚下载的boot.img,检查一下前面的数据是否和图3.3.4.9中的一致,结果如图3.3.4.10所示:
在这里插入图片描述

图3.3.4.10 winhex查看uImage
可以看出图3.3.4.9和图3.3.4.10中的前0x100个字节的数据一致,说明nfs命令下载到的uImage是正确的。
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/alientek/linux/tftpboot
chmod 777 /home/alientek/linux/tftpboot
这样我就在我的电脑上创建了一个名为tftpboot的目录(文件夹),路径为/home/alientek /linux/tftpboot。注意!我们要给tftpboot文件夹权限,否则的话uboot不能从tftpboot文件夹里面下载文件。
最后配置tftp,新建文件/etc/xinetd.d/tftp,如果没有/etc/xinetd.d目录的话自行创建,然后在里面输入如下内容:
示例代码 3.3.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/alientek/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文件,将其修改为如下所示内容:
示例代码3.3.4.2 /etc/default/tftpd-hpa文件内容

1 # /etc/default/tftpd-hpa
2 
3 TFTP_USERNAME="tftp"
4 TFTP_DIRECTORY="/home/alientek/linux/tftpboot" 
5 TFTP_ADDRESS=":69"                                
6 TFTP_OPTIONS="-l -c -s"                           
TFTP_DIRECTORY就是我们上面创建的tftp文件夹目录,以后我们就将所有需要通过TFTP传输的文件都放到这个文件夹里面,并且要给予这些文件相应的权限。

最后输入如下命令, 重启tftp服务器:
sudo service tftpd-hpa restart
tftp服务器已经搭建好了,接下来就是使用了。将boot.img镜像文件拷贝到tftpboot文件夹中,并且给予boot.img相应的权限,命令如下:
cp boot.img /home/alientek/linux/tftpboot/
cd /home/alientek/linux/tftpboot/
chmod 777 boot.img
万事俱备,只剩验证了,uboot中的tftp命令格式如下:
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
看起来和nfs命令格式一样的,loadAddress是文件在DRAM中的存放地址,[[hostIPaddr:]bootfilename]是要从Ubuntu中下载的文件。但是和nfs命令的区别在于,tftp命令不需要输入文件在Ubuntu中的完整路径,只需要输入文件名即可。比如我们现在将tftpboot文件夹里面的boot.img文件下载到开发板DRAM的0X30000000地址处,命令如下:
tftp 30000000 boot.img
下载过程如图3.3.4.11所示:
在这里插入图片描述

图3.3.4.11 tftp命令下载过程
从图3.3.4.11可以看出,boot.img下载成功了,网速为3.4MiB/s,文件大小为23536128字节。同样的,可以使用md.b命令来查看前0x100个字节的数据是否和图3.3.4.10中的相等。
好了,uboot中关于网络的命令就讲解到这里,我们最常用的就是ping、nfs和tftp这三个命令。使用ping命令来查看网络的连接状态,使用nfs和tftp命令来从Ubuntu主机中下载文件。
3.3.5 EMMC和SD卡操作命令
uboot支持EMMC和SD卡,因此也要提供EMMC和SD卡的操作命令。一般认为EMMC和SD卡是同一个东西,所以没有特殊说明,本教程统一使用MMC来代指EMMC和SD卡。uboot中常用于操作MMC设备的命令为“mmc”。
mmc是一系列的命令,其后可以跟不同的参数,输入“?mmc”即可查看mmc有关的命令,如图3.3.5.1所示:
在这里插入图片描述

图3.3.5.1 mmc 命令
从图3.3.5.1可以看出,mmc后面跟不同的参数可以实现不同的功能,如表3.3.5.1所示:
在这里插入图片描述

表3.3.5.1 mmc命令
1、mmc info命令
mmc info命令用于输出当前选中的mmc info设备的信息,输入命令“mmc info”即可,如图3.3.5.2所示:
在这里插入图片描述

图3.3.5.2 mmc info命令
从图3.3.5.2可以看出,当前选的MMC版本为5.1,容量为57.6GiB(EMMC为64GB),速度为200000000Hz=200MHz,8位宽的总线。还有一个与mmc info命令相同功能的命令:mmcinfo,“mmc”和“info”之间没有空格。
2、mmc rescan命令
mmc rescan命令用于扫描当前开发板上所有的MMC设备,包括EMMC和SD卡,输入“mmc rescan”即可。
3、mmc list命令
mmc list命令用于来查看当前开发板一共有几个MMC设备,输入“mmc list”,结果如图3.3.5.3所示:
在这里插入图片描述

图3.3.5.3 扫描MMC设备
可以看出当前开发板有三个MMC设备:sdhci@fec310000: 0 (eMMC)、dwmmc@fe2c0000: 2和dwmmc@fe2b0000:1,一共有三个个MMC设备,sdhci@fec310000: 0 (eMMC)是EMMC,dwmmc@fe2b0000:1 (SD)是SD卡,另一个应是SDIO WIFI设备(若没有接MMC设备,这里就代表SDIO总线)。默认会将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卡(请插上你的SD卡,如果你有的话):
mmc dev 1 //切换到SD卡,0为EMMC,1为SD卡
结果如图3.3.5.4所示:
在这里插入图片描述

图3.3.5.4 切换到SD卡
从图3.4.5.4可以看出,切换到SD卡成功,mmc1为当前的MMC设备,输入命令“mmc info”即可查看SD卡的信息(要插入SD卡),结果如图3.3.5.5所示:
在这里插入图片描述

图3.3.5.5 SD信息
从图3.3.5.5可以看出当前SD卡版本号为3.0,容量为29.1GiB(32GB的SD卡),4位宽的总线。
5、mmc part命令
有时候SD卡或者EMMC会有多个分区,可以使用命令“mmc part”来查看其分区,比如查看EMMC的分区情况,输入如下命令:
mmc dev 0 //切换到EMMC
mmc part //查看EMMC分区
结果如图3.3.5.6所示:
在这里插入图片描述

图3.3.5.6 查看EMMC分区
从图3.3.5.6中可以看出,此时EMMC是EFI类型并且有8个分区,这个是烧写了正点原子出厂系统以后才会有的分区。
如果要将图3.3.5.6中EMMC的分区6设置为当前MMC设置分区,可以使用如下命令:
mmc dev 0 6
结果如图3.3.5.7所示:
在这里插入图片描述

图3.3.5.7 设置EMMC分区6为当前设备
6、mmc read命令
mmc read命令用于读取mmc设备的数据,命令格式如下:
mmc read addr blk# cnt
addr是数据读取到DRAM中的地址,blk是要读取的块起始地址(十六进制),一个块是512字节,这里的块和扇区是一个意思,在MMC设备中我们通常说扇区,cnt是要读取的块数量(十六进制)。比如从EMMC的第1024(0x400)个块开始,读取16(0x10)个块的数据到DRAM的0XC0000000地址处,命令如下:
mmc dev 0 //切换到EMMC
mmc read 8300000 400 10 //读取数据
结果如图3.3.5.7所示:
在这里插入图片描述

图3.3.5.7 mmc read命令
可以通过md命令来查看0X08300000处的数据,也就是读取到的MMC设备里面的数据,在这里,这些数据是随便读的,没什么意义,就不看了。
3.3.6 EXT格式文件系统操作命令
uboot有ext2和ext4这两种格式的文件系统的操作命令,RK3568的系统镜像都是ext4格式的,所以我们重点讲解一下和ext4有关的三个命令:ext4ls、ext4load和ext4write。注意,由于只有linux内核、设备树和根文件系统是以ext4格式存放在EMMC中的,因此在测试ext4相关命令的时候要先确保EMMC里面烧写了完整的出厂系统!
1、ext4ls
ext4ls命令用于查询EXT4格式设备的目录和文件信息,命令格式如下:
ext4ls [<dev[:part]>] [directory]
interface是要查询的接口,比如mmc,dev是要查询的设备号,part是要查询的分区,directory是要查询的目录。比如查询EMMC分区6中的所有的目录和文件,输入命令:
ext4ls mmc 0:6
结果如图3.3.6.1所示:
在这里插入图片描述

图3.3.6.1 EMMC分区2文件查询
从上图可以看出,emmc的分区6存放的就是我们的根文件系统。
2、ext4load命令
extload命令用于将指定的文件读取到DRAM中,命令格式如下:
fatload [<dev[:part]> [ [ [bytes [pos]]]]]
interface为接口,比如mmc,dev是设备号,part是分区,addr是保存在DRAM中的起始地址,filename是要读取的文件名字。bytes表示读取多少字节的数据,如果bytes为0或者省略的话表示读取整个文件。pos是要读的文件相对于文件首地址的偏移,如果为0或者省略的话表示从文件首地址开始读取。我们将图3.3.6.1中EMMC分区6中的linuxrc文件读取到DRAM中的0X08300000地址处,命令如下:
ext4load mmc 0:6 8300000 linuxrc
操作过程如图3.3.6.2所示:
在这里插入图片描述

图3.3.6.2 读取过程
从上图可以看出在10ms内读取了754880个字节的数据,速度为72MiB/s,速度是非常快的,因为这是从EMMC里面读取的,而EMMC是8位的,速度肯定会很快。
3.3.7 BOOT操作命令
uboot的本质工作是引导Linux,所以uboot肯定有相关的boot(引导)命令来启动Linux。常用的跟boot有关的命令有:boot_fit和boot。
1、boot_fit命令
大家如果学过I.MX6U或者STM32MP1的话,应该知道uboot使用bootm或者bootz这两个命令启动内核,需要提供Linux编译出来的zImage或uImage以及设备树文件,然后使用bootm或bootz启动。但是RK3568最终的系统烧写文件只有一个boot.img,Image和设备树文件全部打包进boot.img这一个文件里面,所以就不能用bootm或bootz,要用到boot_fit命令。
boot_fit命令格式如下:
boot_fit [addr]
addr是可选的,也就是boot.img在DRAM中的位置。可以不需要,boot_fit默认会从相应的boot分区里面读取boot.img然后解析启动Linux内核,比如当我们把系统烧写到EMMC里面以后,只需要一个boot_fit命令就可以完成Linux内核和设备树的提取、启动。
如果想要从网络启动系统,那么先把boot.img放到Ubuntu的tftpboot文件夹中,然后用tftp命令将boot.img下载到开发板的DRAM中,然后使用boot_fit命令启动。首先是要先将boot.img通过tftp下载到合适的DRAM地址处,这里需要用到sysmem_search命令找到一个合适的存储起始地址,sysmem_search命令格式如下:
sysmem_search size
size就是要获取的内存大小,为十六进制的。一般来说就是boot.img大小,但是每次重新编译Linux内核以后boot.img大小都会变,每次都要获取内存起始地址太麻烦了。我们就直接获取一个25MB的空间就行了,所以25MB=0X1900000,输入如下命令:
sysmem_serach 1900000
结果如图3.3.7.1所示:
在这里插入图片描述

图3.3.7.1 得到的内存起始地址
从图3.3.7.1可以看出,我当前得到的地址为0XE9EF6400,大家以自己实际得到的地址为准!接下来就是通过tftp将boot.img下载到0XE9EF6400地址处,然后通过boot_fit命令启动,整个的命令如下:
tftp E9EF6400 boot.img
boot_fit E9EF6400
命令运行结果如图3.3.7.2所示:
在这里插入图片描述

图3.3.7.2 通过网络启动Linux
从图3.3.7.2可以看出,此时Linux内核已经启动成功,相关的log信息已经打印出来了。
2、boot和bootd命令
boot和bootd其实是一个命令,它们最终执行的是同一个函数。为了方便起见,后面就统一使用boot命令,此命令也是用来启动Linux系统的,只是boot会读取环境变量bootcmd来启动Linux系统,bootcmd是一个很重要的环境变量!其名字分为“boot”和“cmd”,也就是“引导”和“命令”,说明这个环境变量保存着引导命令,其实就是多条启动命令的集合,具体的引导命令内容是可以修改的。
RK3568开发板bootcmd默认值如图3.3.7.3所示:

在这里插入图片描述
在这里插入图片描述

图3.3.7.3 bootcmd默认值
可以看出bootcmd默认值为“boot_fit;boot_android ${devtype} ${devnum};bootrkp;run distro_bootcmd”,其中devtype为mmc,devnum为0,所以简化一下就是“boot_fit;boot_android mmc 0;”。也就是有两种启动内核的方式:boot_fit和boot_android,其中boot_android在RK3568里面无效,因此实际有效的只有一个boot_fit。后面的bootrkp;run distro_bootcmd未定义,所以无效。
因此在RK3568中,bootcmd就是直接调用boot_fit命令来启动Linux系统的。uboot倒计时结束以后会默认运行bootcmd环境变量里面的命令,所以boot_fit也就会默认执行。
3.3.8 其他常用命令
uboot中还有其他一些常用的命令,比如reset、go、run和mtest等。
1、reset命令
reset命令顾名思义就是复位的,输入“reset”即可复位重启,如图3.3.8.1所示:
在这里插入图片描述

图3.3.8.1 reset命令运行结果
2、go命令
go命令用于跳到指定的地址处执行应用,命令格式如下:
go addr [arg …]
addr是应用在DRAM中的首地址。
3、run命令
run命令用于运行环境变量中定义的命令,比如可以通过“run bootcmd”来运行bootcmd中的启动命令,但是run命令最大的作用在于运行我们自定义的环境变量。
至此,uboot常用的命令就讲解完了,如果要使用uboot的其他命令,可以查看uboot中的帮助信息,或者上网查询一下相应的资料。
3.3.9 MII命令使用说明
MII命令是网络相关命令,主要用于读取网络PHY芯片寄存器,在uboot中调试网络PHY芯片的时候非常有用!MII是一系列命令,如图3.3.9.1所示:
在这里插入图片描述

图3.3.9.1 mii系列命令
我们使用“mii info”命令,“mii info”命令格式如下:
mii info
其中addr就是PHY芯片地址,RK3568开发板上网络PHY地址为0X01,输入如下命令:
mii info 0x1
结果如图3.3.9.2所示:
在这里插入图片描述

图3.3.9.2 mii info命令
也可以使用“mii dump”直接打印出PHY的0~5寄存器值,输入如下命令:
mii dump 1 0-5
其中1表示PHY地址为1,0-5表示读取0-5这6个寄存器,结果如图3.3.9.3所示:
在这里插入图片描述

大家也可以使用“mii read”命令读取PHY芯片的其他寄存器。或者使用“mii write”命令向指定的寄存器写入一个数据。
  • 13
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值