一、前言
在上一次的教程中,带大家了解了什么是UBoot,以及也为大家演示了如何移植一个Uboot。之前我们提到了,Uboot就是一个功能非常强大的裸机程序,我们可以使用Uboot来拉起操作系统。当然除了这些以外Uboot还具有非常强大的功能。比如从网络启动操作系统,直接修改DDR的内容以及直接操作EMMC或者SD卡,也可以从指定的存储设备启动操作系统。总的来说Uboot功能是非常强大的。那么,本次教程就来教大家如何使用Uboot的命令以及完成一些基础操作。当然因为Uboot的网络配置比较麻烦,并且情况可能也比较多,所以我们这次就不讲解Uboot的网络配置和网络相关的一些命令,后面我会专门出一篇教程来讲解Uboot的网络和网络相关的命令。本次教程使用的开发板依然是正点原子的STM32MP157开发板,这里还是强烈建议使用和我一样的开发板,当然,如果你有一定的经验,也可以使用别的开发板。如果你准备好了,那就让我们开始吧!
二、谁适合本次教程
本次教程是比较偏上层的教程,不涉及底层的代码修改或者驱动移植。所以,不管你会不会移植Uboot,不管你了不了解STM32MP157的启动流程,都可以看本次教程。如果你使用的是自己移植的Uboot,本次教程中使用的命令你刚好可以验证移植的完整性。如果你使用的是正点原子的原厂Uboot,也可以跟着本次教程感受命令的使用。总的来说,本次教程没有太多限制,初学者和有一定了解的人都可以学习。
三、资料的准备
这里我们同样使用正点原子的官方资料,下载的方法在之前的STM32MP157环境搭建教程中已经讲过了,大家自行查看即可:
STM32MP157开发环境搭建:[Linux]从零开始的STM32MP157交叉编译环境配置_stm32mp157 linux 开发 单片机开发-CSDN博客
资料准备好以后如图:
四、U-Boot基础命令的使用以及功能验证
上次教程中我们已经移植好了一份Uboot。我们可以直接将其烧录到开发板中验证其功能,下面大家可以跟着我一边学习Uboot中的基础命令一边验证移植的功能。
1.进入Uboot的终端
这里可能要分两种情况,一种是烧录了Uboot也烧录了Linux内核;还有一种就是自己烧录了Uboot并且没有烧录内核,并且开发板中也不存在内核。首先是第一种情况,我们烧录了Uboot和内核以后Uboot可能会在启动时直接拉起内核,导致我们不能进入Uboot的终端。对于这种情况我们可以在按下复位后,反复按键盘的回车来解决,不知道大家注意到没有,在Uboot启动后会有一个倒计时,并且有提示我们“按下任意键停止自动引导”:
这里的自动引导就会引导Linux内核,我们只需要按下键盘上的任意一个键将这个引导停止即可。这里推荐大家按回车,即在复位以后反复按回车键。这里的倒计时只有一秒钟,如果这一秒钟内没有按下键盘上的键,就会启动Linux内核。后面也会教大家如何修改这个倒计时的时间。
然后就是第二种情况,开发板中只烧录了Uboot,并没有烧录内核并且开发板中原本也不存在内核,这种情况就不用担心别的了,因为本来就没有内核,Uboot想引导也引导不了,最后还是只能进入Uboot的终端。经过测试,就算是正点原子的原厂系统,我们将TF-A和Uboot都烧录成自己编译的以后,都不会启动Linux内核。
当我们顺利进入Uboot的终端以后,就可以使用Uboot终端中的命令了。
2.查询命令
常用的和信息查询有关的命令有 3 个:bdinfo、printenv 和 version。这些命令可以让我们查询到开发板的基础信息,环境变量以及版本号等。
首先是bdinfo命令,它在执行以后会打印我们开发板的基础信息,如下图所示:
bdinfo
这些输出中,打印了我们开发板的基础信息,比如内存的类型,内存的起始地址和总的内存大小,从打印的信息中,可以看出我们的内存种类为DDR RAM,并且物理起始地址为C0000000,大小为0x40000000个字节也就是1GB的内存。这里需要注意的是,在Uboot中显示的大部分数字都是十六进制的,尽管它的前面没有加0x,它也是十六进制的。再往下我们可以看到“baudrate”的参数是115200,这也表示我们使用串口调试Uboot的波特率为115200。再往下我们还可以看到堆栈指针的起始地址等等。这里先就讲这么多,这些也是我们使用开发板时要用到的一些关键信息。如果大家对其他的参数感兴趣可以自行查阅。
下面是printenv,这条命令会打印Uboot下的环境变量,如下图:
printenv
当我们输入命令以后,可以看到一大堆内容跑过去了,这些就是Uboot中的环境变量,既然是变量那就意味着,这些内容的值都是可变的。我们可以修改这些环境变量的值达到配置Uboot的目的。比如我们可以通过修改“baudrate”环境变量从而修改串口调试的波特率。我们可以通过修改“bootdelay”环境变量从而修改自动引导的延时,之前也提到了这里自动引导延时默认为1秒。假如我们将“bootdelay”环境变量设置为5,那么自动引导的延时就变为了5秒。当然,后面也会为大家演示环境变量的修改命令。
下面是最后一条查询命令——version,version用于查询Uboot的版本信息,如下图所示:
version
这里Uboot打印出了自己的版本,可以看到的是,我们这里使用的Uboot为2020.1版,同时Uboot也打印出了被编译的时间,以及我们所使用的交叉编译器。
上面就是Uboot中所有的查询命令了,我们可以利用这些查询命令来查看我们系统中的基础信息,环境变量等等。
3.环境变量操作命令
在上面的命令中,教了大家如何查询环境变量的值,那么现在就来教大家如何修改和新建环境变量。
首先是修改环境变量,我们可以使用setenv来修改环境变量,格式如下:
setenv 环境变量名称 值
下面我来演示一下,这里还是拿自动引导延时来举例,假如我们想将自动引导延时的时间修改为10秒,我们只需要将“bootdelay”环境变量的值修改为10即可。我们按照上面的格式,命令如下:
setenv bootdelay 10
当我们在终端输入命令以后,是没有任何提示的:
那我们到底有没有设置成功呢?当然我们查询一下就知道有没有成功了。我们使用查询命令可以看到,“bootdelay”变量的值确实变为10了:
这样我们就已经修改成功了吗?当然没有,我们只是在内存中修改了“bootdelay”的值,当我们再次启动Uboot时就会失效。这时我们就要用到环境变量的保存命令——saveenv,使用saveenv以后我们的环境变量就会被写入EMMC中,当我们下次重启开发板时也会生效:
saveenv
输入保存环境变量的命令以后,如果出现下面这样的提示就表示保存成功了:
这里我们保存好环境变量以后,按下开发板的复位键,就可以发现,自动引导延时变为了10秒:
这就是我们的环境变量的修改和保存命令。
下面再来为大家修改一个环境变量吧!这里我们来修改一下串口波特率的环境变量。这里我们将串口波特率修改为9600,我们就需要将“baudrate”环境变量修改为9600 ,使用下面的命令:
setenv baudrate 9600
这里我们输入命令以后,Uboot的串口波特率就被改为了9600,我们使用115200波特率的串口工具就无法远程到Uboot了,这里Uboot提示我们按下回车将波特率切换到9600,按下回车以后,这个终端就没反应了:
这里大家可以和我一样,新建一个9600波特率的终端,就又可以远程到Uboot了:
同样的,我们现在的环境变量配置只是被写到了内存中,我们还需要使用saveenv将环境变量保存到EMMC中,这样我们每次启动波特率就都是9600了:
在我们复位开发板以后,就会发现,Uboot的波特率已经变为9600了:
前面的那一段乱码来自TF-A,因为TF-A的波特率没有被修改所以会乱码。
以上就是我们Uboot中的环境变量修改命令了。大家每次在修改完Uboot中的环境变量以后,都要记得保存。这一步很重要。
下面来教大家如何新建环境变量。新建环境变量我们同样使用setenv命令。格式如下:
setenv 新的环境变量名 值
这里的命令比较简单,我就举一个简单的例子吧,假如我想创建一个名为“CLX”的变量并且赋值为555,那我就可以这样写:
setenv CLX 555
这里新建了环境变量并且赋值以后,我们使用环境变量查询命令就可以查询到我们新建的环境变量了:
这里大家同样可以使用saveenv命令将我们自己新建的环境变量保存到EMMC中。
下面时环境变量删除命令,删除环境变量我们可以使用“setenv”命令,我们只需要将某个环境变量的值设置为空值,那么这个环境变量就被删除了。假如我们想删除刚刚创建的“CLX”环境变量,就可以这样写:
setenv CLX
这里我们为“CLX”这个变量赋为空以后,这个变量就查询不到了。如果某个环境变量被保存到了EMMC中,大家执行删除命令后还需要将执行“saveenv”保存一下,不然就只是在内存中将这个变量清除了。
以上就是Uboot中常用的环境变量操作命令。
4.内存操作命令
在Uboot中允许我们直接对DDR进行操作,也提供了相关的命令,下面我们就来学习一下这些命令吧!
首先是md命令,用于显示指定内存中的值,格式如下:
md[.b, .w, .l] address [size]
这里的.b表示以一个字节的形式显示内存的值,.w表示以一个字的形式,.l表示long也就是以四个字节的形式显示。这里的address表示要查看内存的起始地址,[size]表示要查看内存的长度,这里的长度是根据前面选的.b ,.w,.l决定的。然后是size,需要注意的是,这里的size是十六进制的。假如我前面选择的是.b当我的size为10时,就会打印从起始地址开始16字节长度的内存的值。假如我选择的是.w,size同样是10,就会打印从起始地址开始32字节长度的内存的值。假如我选择的是.l,size同样是10,就会打印从起始地址开始64字节长度的内存的值。下面我来将上面这几种情况演示一下:
这里的内存查询命令比较简单,所以就不进行多的讲解了。大家只要知道如何查询指定区域的内存就行。
下面我们来看看修改内存的命令——nm,这个命令主要用于修改指定内存的值,格式如下:
nm[.b, .w, .l] address
这里的.b。.w,.l与上面是一样的,都表示,一个字节,两个字节和四个字节。后面的address表示我们要修改的内存的地址。下面我来演示一下这条命令,假如这里我想修改地址为C0100000内存的值,就可以这样写命令:
nm.b C0100000
这里我们使用的.b表示就只修改从C0100000地址开始,一个字节内存的值,当我们输入命令回车以后,终端会出现下面这样的提示:
这种状态就是这块内存的修改模式,这里的fa就表示这块内存中原本的值,我们可以输入一个值回车,这个原本的值就被改变了,假如我们输入“ff”:
可以看到这块内存中的值就变为了ff,我们这里需要结束修改,直接输入“q”回车即可:
这样就退出了内存的编辑模式,下面我们查询一下这块内存看看修改成功没有:
可以看到的是,这里我们确实已经修改成功了。
下面为大家介绍下一条内存操作命令——mm。当我们使用mm修改内存时,内存地址会自增,mm的用法和nm时一样的:
mm[.b, .w, .l] address
这里我们同样使用C0100000这个地址来举例:
这里我们可以看到,每当我们回车一次,前面被操作的内存地址都发生了变化,这就是mm命令的自增特性。
下面要为大家介绍——“mw”命令,这个命令用于使用一个指定的值,填充一段指定的内存,命令用法如下:
mw[.b, .w, .l] address value [count]
这里的b,w,l,同样都是为了确定操作格式,后面的address表示要操作的内存的起始地址,value表示为下面这一块内存填充的值,[count]表示从address开始要填充的内存数量,同样的[count]参数也是十六进制的。下面我们就用一段命令为大家演示一下:
mw.b C0100000 ab 10
上面的命令就表示,将从C0100000开始的连续16个字节的内存的值修改为“ab”,命令运行完成以后,我们可以查询这段内存的值,可以发现我们已经修改成功了:
这就是mw命令。
下面为大家介绍内存拷贝命令——cp,它用于将一段内存的值拷贝到另一段内存中。用法如下:
cp[.b, .w, .l] source target count
cp命令的b,w,l,同样都是为了确定操作格式。后面的source表示源地址,target表示要拷贝到的目的地址,count表示要拷贝的长度,同样为十六进制。下面就为大家演示一下这条命令,因为我们上面修改了C0100000开始连续16个字节的内存的地址,现在我们就将这一段内存拷贝到C0100100这一块内存中,命令如下:
cp.b C0100000 C0100100 10
拷贝完成以后,我们查询一下这块内存的值:
可以看到这段内存的值已经被我们成功拷贝过来了。
下面是内存比较命令——cmp,用于比较两段内存是否相等,用法如下:
cmp[.b, .w, .l] addr1 addr2 count
cmp命令的b,w,l,同样都是为了确定操作格式。这里的addr1表示第一段内存地址,addr2表示第二段内存地址count同样是比较的长度,同样为16进制。这里我会为大家演示相等和不相等的两种情况,首先是相等的情况,因为一开始我们就将C0100000开始的连续16个字节的内存数据复制到了C0100100处,所以这两段地址的连续16个字节的内容都是相等的。我们可以直接比较,命令如下:
cmp.b C0100000 C0100100 10
这里可以看到,它告诉我们,总计16字节,内容都是一样的。
下面我们再来看看不一样的情况,这里只需要我们随意比较两段地址,内容大概率是不一样的,使用下面的命令:
cmp.b C0100000 C0100200 10
执行结果如下,它告诉我们,总计0个字节相同。当然,如果有时候运气好也可能会遇到一两个一样的值。
5.EMMC和SD卡操作命令
再Uboot中支持了EMMC和SD卡,同样也为这两种设备提供了操作命令。不管是EMMC还是SD卡我们都可以使用mmc命令来操作,mmc的全部命令如下:
这里是不是觉得非常多,是的,虽然看起来多,但是都不难,下面我就来为大家将这些命令都演示一下。
首先是“mmc info”命令它用于输出当前选中的mmc设备的信息,顺带一提,我们的EMMC和SD卡都属于MMC设备,输入命令回车以后,我们就能看到下面的输出了:
mmc info
可以看到,这里输出了SD卡的信息,表示当前选中的mmc设备是SD卡,我们后面可以通过命令修改选中的设备,这个留到后面再介绍。通过输出我们可以看到,我们的SD处于高速模式,版本号为3.0,并且大小为59.5GB。
下面是用于扫描开发板所有mmc设备的命令——mmc rescan,这里我们直接执行即可:
mmc rescan
扫描完开发板mmc设备以后,我们就可以使用mmc list来列出开发板上的所有mmc设备:
mmc list
可以看到这里我有两个mmc设备,分别是EMMC和SD卡,我们记住它们的编号,在后面切换设备时会用到。
现在我们就来介绍一下切换当前mmc设备的命令——mmc dev ,这条命令的语法如下:
mmc dev [dev] [part]
这里的[dev]表示要切换的mmc的编号,这里的编号就是我们使用mmc list命令查询到的编号。后面的[part]表示对应的mmc设备的分区,如果我们的EMMC和SD卡都没有进行专门的分区操作,这里可以不用写,它会默认选择分区0。
假如我这里想将默认设备切换到EMMC,就可以这样写:
mmv dev 1
切换mmc设备以后,我们使用mmc info查询到的信息也是这个设备的。
下面是查询mmc设备分区设备的命令——mmc part,它会显示当前选择mmc设备的分区信息:
mmc part
因为我这里选中的是EMMC所以它打印了两个分区,一个是ssbl也就是我们uboot存在的分区,还有一个就是Linux内核所在的区域。
我们再来看看SD卡的分区情况:
SD卡我们并没有做什么特别的操作,所以就只有一个分区。
下面是mmc设备的读取命令——mmc read。它用于从mmc中读取数据,命令格式如下:
mmc read addr blk# cnt
这里的addr表示读取出来的数据存放在内存中的起始地址,后面面的blk的起始地址,这里引入了一个块的概念,我们读取mmc设备是读取一整个块,一个块是512字节,这里的块就是我们常说的扇区。最后的cnt表示要读取的块的数量。下面就来演示一下这条命令,这里我们使用下面的命令对mmc设备进行读取:
mmc read c0000000 400 10
这里的命令就表示,读取当前选中的mmc,并且从mmc设备的第0x400个块开始,连续读取0x10个块到起始地址为c0000000的内存中。注意哈,这里的400和10是十六进制的。命令执行完成以后,我们可以使用下面的命令查询一下对应内存中的内容:
md.b c0000000 2000
这里大家可能会好奇为什么是2000,因为一开始我们复制了16个扇区,一个扇区又是512字节,总共8192个字节,所以,写成16进制就是2000。这里查询的内容太长了,就不截图了,大家自行查看即可。
6.EXT文件格式操作命令
uboot有ext2和ext4这两种格式的文件系统的操作命令, STM32MP1的系统镜像都是ext4 格式的,所以我们重点讲解一下和ext4有关的三个命令:ext4ls、ext4load和ext4write。注意,由于只有linux内核、设备树和根文件系统是以ext4格式存放在EMMC中的,因此在测试ext4 相关命令的时候要先确保EMMC里面烧写了完整的正点原子出厂系统!
ext4ls 命令用于查询EXT4格式设备的目录和文件信息,命令格式如下:
ext4ls <interface> [<dev[:part]>] [directory]
首先是<interface>表示要查询的设备接口,我们这里要查询mmc设备所以<interface>直接写成mmc即可。dev表示要查询的设备编号,这个编号同样是我们使用mmc list查询到的编号。后面的port表示要查询的分区,[directory]表示要查询的目录,如果不写[directory] 的话就表示查询这个分区的所有目录,下面用一条命令为大家演示一下,我们在终端中输入下方的命令:
ext4ls mmc 1:2
这条命令就表示我们查询EMMC中的第二个分区的所以文件,执行以后,得到以下结果:
这里我们可以看到在emmc的第二个分区中保存成了设备树文件,以及Linux内核等文件。
下面是将指定文件读取到DDR中的命令——ext4load,它的命令格式如下:
extload <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]
这里的interface同样表示设备接口,我们写mmc即可。dev表示设备的编号,port表示设备的分区。addr表示要读取到DDR的起始地址,filename表示要读取的文件的名字。pos 是要读的文件相对于文件首地址的偏移,如果为0或者省略的 话表示从文件首地址开始读取。下面就用一条命令为大家演示一下extload的用法:
ext4load mmc 1:2 C2000000 uImage
上面的命令就表示,将EMMC中第二个分区中的uimage读取到DDR的C2000000地址起始处。
执行以后,如图所示:
我们可以看到,这里的读取速度非常快。
下面为大家介绍将DDR中的数据保存到mmc设备中的命令——ext4write,命令格式如下:
ext4write <interface> <dev[:part]> <addr> <absolute filename path> [sizebytes] [file offset]
这里的interface,dev,part参数都是和前面一样的,就不多说了,然后是addr,这里addr表示要读取DDR的起始地址,absolute filename path是写入的数据文件名字,注意是要带有绝对路径,以‘/’ 开始;sizebytes表示要写入多少字节的数据;file offset表示文件偏移,不写这个参数的话,就表示从文件起始地址开始,不偏移。下面我们就来将我们写入DDR中的数据,再次写回到EMMC中,我们可以使用下面的命令:
ext4write mmc 1:2 c2000000 /test_uImage 0x7fd9a8
下面我就来解释一下这条命令,首先是c2000000在上一条命令中,我们将EMMC的第二个分区的uimage写入到了这个地址,我们现在也从这个地址开始读取。然后是/test_uImage这里表示这个分区根节点下的test_uimage文件,这也是我们要向EMMC中写入的文件名。最后0x7fd9a8表示我们要写入的长度,因为一开始我们读取文件读取了8378792个字节,所以写入我也要写入这么多,换成十六进制就是0x7fd9a8。命令执行完成以后如图所示:
我们再使用文件查询命令查询这个分区的文件,既可以看到我们刚刚拷贝过来的文件了:
它的大小和原本的文件是一样的。
7.BOOT相关的操作命令
还记得Uboot的作用吗?Uboot的本质目的就是为了启动Linux内核,所以在Uboot中也预留了相关的命令来启动Linux。下面就来为大家介绍一下boot相关的命令。
首先是Linux的启动命令——bootm。我们要启动一个Linux内核首先要将Linux的相关文件拷贝到DDR中,不管是直接使用EXT命令进行拷贝,还是使用nfs或者tftp进行网络拷贝,都需要将Linux的相关文件拷贝到DDR中,然后使用bootm命令启动Linux。下面我们来看看bootm命令的基本格式:
bootm [addr [arg ...]]
首先是第一个参数addr,这里的addr表示Linux镜像在DDR中的位置。后面的arg ...表示可选的其他参数,也就表示这些参数不是必须的。第二个参数就是initrd在DRAM中的地址。如 果Linux 内核使用设备树的话还需要第三个参数,用来指定设备树在DRAM中的地址,如果不需要initrd的话第二个参数就用‘-’来代替。
因为我们没有配置网络,这里就不讲解网络启动了,后面会有一期教程专门讲解Uboot网络相关的内容。这里我们就讲解使用bootm命令从EMMC启动Linux系统。这里首先我们需要将Linux运行所需要的文件拷贝到DDR中,我们直接使用前面的EXT相关的命令即可:
ext4load mmc 1:2 c2000000 uImage
ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb
最后我们就可以使用bootm命令来启动Linux内核:
bootm c2000000 - c4000000
这里可以看到我们的内核已经成功启动起来了并且输出了相关的日志。
在内核启动时可能会遇到下面这样的错误:
“---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---”
这里的错误内容表示没有找到根文件系统。这是正常的,根文件系统的路径需要使用bootargs环境变量指定。后面会进行讲解,还有一种情况就是没烧录根文件系统。
下面是bootz命令,bootz命令主要用于启动zimage文件,但是我们stm32mp157编译出来的Linux镜像为uimage,所以本次教程中,我们不会用到bootz命令。
然后是boot和bootd命令,其实这两条命令的功能都是一样的,我们用哪个都可以。但是请注意,请确保在编译Uboot时将这两条命令都使能了。后面为了方便,我们就统一叫做boot命令。
boot命令只会做一件事,运行“bootcmd”环境变量的内容,下面我们去看一下“bootcmd”环境变量:
我们可以看到bootcmd环境变量中运行了run bootcmd_stm32mp命令,我们再看到bootcmd_stm32mp变量:
它执行了一些启动相关的代码。当我们输入boot命令后,就会执行这些代码。还记得启动时的倒计时吗?倒计时结束以后,就会执行bootcmd中的代码,从而启动Linux内核。
下面我们可以通过修改bootcmd的内容从而实现执行boot命令以后自动从EMMC启动Linux内核,我们可以使用下面的命令修改bootcmd环境变量的值:
setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
设置好以后,记得保存到EMMC中:
saveenv
我们修改完以后,可以查看一下bootcmd的内容:
确定已经修改成功以后,我们就可以直接输入boot命令:
boot
输入boot并且回车以后,就可以看到我们的内核已经启动起来了:
8.UMS命令
在Uboot中,我们可以将开发板虚拟成U盘,我们可以直接在虚拟机中访问开发板的文件,这就需要使用到UMS命令,命令格式如下:
ums <USB_controller> [<devtype>] <dev[:part]>
其中USB_controller 是 usb 接口索引,有的开发板有多个USB SLAVE接口,具体要使用哪 个就可以通过USB_controller参数指定。正点原子STM32MP157开发板的只有一个USB_OTG 口可以作为USB SLAVE,对应的索引为0。Devtype是要挂载的设备,默认为mmc,dev[:part] 是要挂载的Flash设备,part是要挂载的分区。
这里我将开发板的SD卡挂载到电脑上,使用如下命令:
ums 0 mmc 0
输入命令以后,我们就可以直接将OTG接口连接到电脑,OTG接口就是我们平常下载程序的那个接口,这样我们就能看到一个磁盘了:
因为这张卡插到过安卓设备上所有才是这个名字。
当然,如果你在这里看不到磁盘,可以去磁盘管理看看,因为Linux和Windows的文件系统不同,可能磁盘不会显示:
9.其他命令的讲解
除了上面的命令,这里继续为大家介绍一下别的常用的命令。
首先是reset命令,reset 命令顾名思义就是复位的,输入“reset”即可复位重启:
reset
然后是go命令,go 命令用于跳到指定的地址处执行应用,命令格式如下:
go addr [arg ...]
addr是应用在DDR中的地址。
总之,在Uboot中有上面这些常用的命令,如果遇到了不会的命令,大家也可以通过浏览器或者AI搜索其用法。
五、结语
本次为大家介绍了Uboot中常用的命令,当然,网络相关的命令后面会单独有一期教程来讲,那么最后,感谢大家的观看!