行情
HOME
行情
正文内容
uboot标记nand 为什么要有uboot带你全面分析嵌入式中uboot的作用
发布时间 : 2024-11-24
作者 : 小编
访问数量 : 23
扫码分享至微信

为什么要有uboot带你全面分析嵌入式中uboot的作用

1.为什么要有uboot

1.1、计算机系统的主要部件

(1)计算机系统就是以CPU为核心来运行的系统。典型的计算机系统有:PC机(台式机+笔记本)、嵌入式设备(手机、平板电脑、游戏机)、单片机(家用电器像电饭锅、空调)

(2)计算机系统的组成部件非常多,不同的计算机系统组成部件也不同。但是所有的计算机系统运行时需要的主要核心部件都是3个东西:

CPU + 外部存储器(Flash/硬盘) + 内部存储器(DDR SDRAM/SDRAM/SRAM)

1.2、PC机的启动过程

(1)部署:典型的PC机的BIOS程序部署在PC机主板上(随主板出厂时已经预制了),操作系统部署在硬盘上,内存在掉电时无作用,CPU在掉电时不工作。

(2)启动过程:PC上电后先执行BIOS程序(实际上PC的BIOS就是NorFlash),BIOS程序负责初始化DDR内存,负责初始化硬盘,然后从硬盘上将OS镜像读取到DDR中,然后跳转到DDR中去执行OS直到启动(OS启动后BIOS就无用了)

1.3、典型嵌入式linux系统启动过程

(1)典型嵌入式系统的部署:uboot程序部署在Flash(能作为启动设备的Flash)上、OS部署在FLash(嵌入式系统中用Flash代替了硬盘)上、内存在掉电时无作用,CPU在掉电时不工作。

(2)启动过程:嵌入式系统上电后先执行uboot、然后uboot负责初始化DDR,初始化Flash,然后将OS从Flash中读取到DDR中,然后启动OS(OS启动后uboot就无用了)

总结:嵌入式系统和PC机的启动过程几乎没有两样,只是BIOS成了uboot,硬盘成了Flash。

1.4、android系统启动过程

(1)Android系统的启动和Linux系统(前面讲的典型的嵌入式系统启动)几乎一样。几乎一样意思就是前面完全一样,只是在内核启动后加载根文件系统后不同了。

(2)可以认为启动分为2个阶段:第一个阶段是uboot到OS启动;第二个阶段是OS启动后到rootfs加载到命令行执行;现在我们主要研究第一个阶段,android的启动和linux的差别在第二阶段。

1.5、总结:uboot到底是干嘛的

(1)uboot主要作用是用来启动操作系统内核。

(2)uboot还要负责部署整个计算机系统。

(3)uboot中还有操作Flash等板子上硬盘的驱动。

(4)uboot还得提供一个命令行界面供人来操作。

2.为什么是uboot

2.1、uboot从哪里来的?

(1)uboot是SourceForge上的开源项目

(2)uboot项目的作者:一个德国人最早发起的项目

(3)uboot就是由一个人发起,然后由整个网络上所有感兴趣的人共同维护发展而来的一个bootloader。

2.2、uboot的发展历程

(1)自己使用的小开源项目。

(2)被更多人认可使用

(3)被SoC厂商默认支持。

总结:uboot经过多年发展,已经成为事实上的业内bootloader标准。现在大部分的嵌入式设备都会默认使用uboot来做为bootloader。

2.3、uboot的版本号问题

(1)早期的uboot的版本号类似于这样:uboot1.3.4。后来版本号便成了类似于uboot-2010.06。

(2)uboot的核心部分几乎没怎么变化,越新的版本支持的开发板越多而已,对于一个老版本的芯片来说,新旧版本的uboot并没有差异。

2.4、uboot的可移植性的正确理解

(1)uboot就是universal bootloader(通用的启动代码),通用的意思就是在各种地方都可以用。所以说uboot具有可移植性。

(2)uboot具有可移植性并不是说uboot在哪个开发板都可以随便用,而是说uboot具有在源代码级别的移植能力,可以针对多个开发板进行移植,移植后就可以在这个开发板上使用了。

3.uboot必须解决哪些问题

3.1、自身可开机直接启动

(1)一般的SoC都支持多种启动方式,譬如SD卡启动、NorFlash启动、NandFlash启动等·····uboot要能够开机启动,必须根据具体的SoC的启动设计来设计uboot。

(2)uboot必须进行和硬件相对应的代码级别的更改和移植,才能够保证可以从相应的启动介质启动。uboot中第一阶段的start.S文件中具体处理了这一块。

3.2、能够引导操作系统内核启动并给内核传参

(1)uboot的终极目标就是启动内核。

(2)linux内核在设计的时候,设计为可以被传参。也就是说我们可以在uboot中事先给linux内核准备一些启动参数放在内存中特定位置然后传给内核,内核启动后会到这个特定位置去取uboot传给他的参数,然后在内核中解析这些参数,这些参数将被用来指导linux内核的启动过程。

3.3、能提供系统部署功能

(1)uboot必须能够被人借助而完成整个系统(包括uboot、kernel、rootfs等的镜像)在Flash上的烧录下载工作。

(2)裸机教程中刷机(ARM裸机第三部分)就是利用uboot中的fastboot功能将各种镜像烧录到iNand中,然后从iNand启动。

3.4能进行soc级和板级硬件管理

(1)uboot中实现了一部分硬件的控制能力(uboot中初始化了一部分硬件),因为uboot为了完成一些任务必须让这些硬件工作。譬如uboot要实现刷机必须能驱动iNand,譬如uboot要在刷机时LCD上显示进度条就必须能驱动LCD,譬如uboot能够通过串口提供操作界面就必须驱动串口。譬如uboot要实现网络功能就必须驱动网卡芯片。

(2)SoC级(譬如串口)就是SoC内部外设,板级就是SoC外面开发板上面的硬件(譬如网卡、iNand)

3.5、uboot的“生命周期”

(1)uboot的生命周期就是指:uboot什么时候开始运行,什么时候结束运行。

(2)uboot本质上是一个裸机程序(不是操作系统),一旦uboot开始SoC就会单纯运行uboot(意思就是uboot运行的时候别的程序是不可能同时运行的),一旦uboot结束运行则无法再回到uboot(所以uboot启动了内核后uboot自己本身就死了,要想再次看到uboot界面只能重启系统。重启并不是复活了刚才的uboot,重启只是uboot的另一生)

(3)uboot的入口和出口。uboot的入口就是开机自动启动,uboot的唯一出口就是启动内核。uboot还可以执行很多别的任务(譬如烧录系统),但是其他任务执行完后都可以回到uboot的命令行继续执行uboot命令,而启动内核命令一旦执行就回不来了。

总结:一切都是为了启动内核

4.uboot的工作方式

4.1、从裸机程序镜像uboot.bin说起

(1)uboot的本质就是一个裸机程序,和我们裸机全集中写的那些裸机程序xx.bin并没有本质区别。如果非说要有区别,那就是:我们写的大部分小于16KB,而uboot大于16KB(一般uboot在180k-400k之间)

(2)uboot本身是一个开源项目,由若干个.c文件和.h文件组成,配置编译之后会生成一个uboot.bin,这就是uboot这个裸机程序的镜像文件。然后这个镜像文件被合理的烧录到启动介质中拿给SoC去启动。也就是说uboot在没有运行时表现为uboot.bin,一般躺在启动介质中。

(3)uboot运行时会被加载到内存中然后一条指令一条指令的拿给CPU去运行。

4.2、uboot的命令式shell界面

(1)普通的裸机程序运行起来就直接执行了,执行时效果和代码有关。

(2)有些程序需要和人进行交互,于是乎程序中就实现了一个shell(shell就是提供人机交互的一个界面,回想ARM裸机全集第十六部分),uboot就实现了一个shell。

注意:shell并不是操作系统,和操作系统一点关系都没有。linux中打开一个终端后就得到了一个shell,可以输入命令回车执行。uboot中的shell工作方式和linux中的终端shell非常像(其实几乎是一样的,只是命令集不一样。譬如linux中可以ls,uboot中ls就不识别)

4.3、掌握uboot使用的2个关键点:命令和环境变量

(1)uboot启动后大部分时间和工作都是在shell下完成的(譬如uboot要部署系统要在shell下输命令、要设置环境变量也得在命令行地下,要启动内核也要在命令行底下敲命令)

(2)命令就是uboot的shell中可以识别的各种命令。uboot中有几十个命令,其中有一些常用另一些不常用(我们还可以自己给uboot添加命令),后面会用几节课时间来依次学习uboot中常用命令。

(3)uboot的环境变量和操作系统的环境变量工作原理和方式几乎完全相同。uboot在设计时借助了操作系统的设计理念(命令行工作方式借鉴了linux终端命令行,环境变量借鉴了操作系统的环境变量,uboot的驱动管理几乎完全照抄了linux的驱动框架)。

(4)环境变量可以被认为是系统的全局变量,环境变量名都是系统内置的(认识就认识,不认识就不认识,这部分是系统自带的默认的环境变量,譬如PATH;但是也有一部分环境变量是自己添加的,自己添加的系统就不认识但是我们自己认识)。系统或者我们自己的程序在运行时可以通过读取环境变量来指导程序的运行。这样设计的好处就是灵活,譬如我们要让一个程序更改运行方法,不用去重新修改程序代码再重新编译运行,而只要修改相应的环境变量就可以了。

(5)环境变量就是运行时的配置属性。

5.uboot的常用命令1

5.1、类似linux终端的行缓冲命令行

(1)行缓冲的意思就是:当我们向终端命令行输入命令的时候,这些命令没有立即被系统识别,而是被缓冲到一个缓存区(也就是系统认为我们还没有输入完),当我们按下回车键(换行)后系统就认为我们输入完了,然后将缓冲区中所有刚才输入的作为命令拿去分析处理。

(2)linux终端设计有3种缓冲机制:无缓冲、行缓冲、全缓冲

(3)有些命令有简化的别名,譬如printenv命令可以简化为print,譬如setenv可以简化为set

(4)有些命令会带参数(注意格式是固定的),uboot的每个命令都有事先规定好的各种格式。有些命令就是不带参数的,譬如printenv/print命令;有些命令带可选的参数(可以带也可以不带,当然带不带参数的执行结果是不同的);有些命令带必须的参数(譬如setenv/set命令)

(5)采用“help+命令名”来查询命令的详细信息,只输入help时,则打印出命令列表。

5.2、命令中的特殊符号(譬如单引号)

(1)uboot的有些命令带的参数非常长,为了告诉uboot这个非常长而且中间有好多个空格的东西是给他的一整个参数,所以用单引号将这个很长且中间有空格隔开的参数引起来。

(2)别的符号也许也有,而且有特定的意义。当碰到uboot的命令行有特殊符号时要注意不是弄错了,而是可能有特别的含义。

5.3、有些命令是一个命令族(譬如movi)

(1)命令族意思就是好多个命令开头都是用同一个命令关键字的,但是后面的参数不一样,这些命令的功能和作用也不同。这就叫一个命令族。

(2)同一个命令族中所有的命令都有极大的关联,譬如movi开头的命令族都和moviNand(EMMC、iNand)操作有关。

5.4、第一个命令:printenv/print

(1)print命令不用带参数,作用是打印出系统中所有的环境变量。

(2)环境变量就好像程序的全局变量一样。程序中任何地方都可以根据需要去调用或者更改环境变量(一般都是调用),环境变量和全局变量不同之处在于:全局变量的生命周期是在程序的一次运行当中,开始运行时诞生程序结束时死亡,下次运行程序时从头开始;但是环境变量被存储在Flash的另一块专门区域(Flash上有一个环境变量分区),一旦我们在程序中保存了该环境变量,那么下次开机时该环境变量的值将维持上一次更改保存后的值。

6.uboot的常用命令2

1、设置(添加/更改)环境变量:setenv/set

用法:set name value

2、保存环境变量的更改:saveenv/save

saveenv/save命令不带参数,直接执行,作用是将内存中的环境变量的值同步保存到Flash中环境变量的分区。注意:环境变量的保存是整体的覆盖保存,也就是说内存中所有的环境变量都会整体的将Flash中环境变量分区中原来的内容整体覆盖。

总结:彻底更改一个环境变量的值,需要2步:

第一步:set命令来更改内存中的环境变量

第二步:用save命令将其同步到Flash中环境变量的分区。

有时候我们只是想测试下这个环境变量,不希望影响到下一次开机,那就只set不save,这样set后当前本次运行的uboot已经起效果了,只不过没save下一次开机还是会恢复到原来的状况。

3、网络测试指令:ping

(1)命令用法: ping ip地址

注意:ping是测试开发板和主机之间的网络链接,注意以下步骤:

1)首先要插上网线。

2)先试图ping通主机windows。注意Windows中有线网卡的地址设置(设置本地连接)。设置主机windows的本地连接IPv4地址为192.168.1.10

3)第三步确认开发板中uboot里几个网络相关的环境变量的值对不对。最重要的是ipaddr(这个环境变量表示当前开发板的IP地址),这个地址必须和主机windows的IP地址在同一个网段。

网段的概念:一个IP地址分为2部分,一部分是网段地址,另一部分是网段内的主机地址(由子网掩码来区分哪一部分是网段地址,哪一部分是IP地址)。在子网掩码是255.255.255.0的情况下,192.168.1.10这个IP地址的前三部分(192.168.1.)属于网段地址,第4部分(10)属于主机地址。

7.开发板和主机的ping通

上节课最后的结果是:uboot中的ipaddr和主机windows本地连接地址已经设置到一个网段,但是实际还ping不通。

还发现了这样的现象:1、我把2个的网段都从192.168.1.x改到192.168.0.x时会ping通一次,第二次开始就ping不通了;2、有同学说ping不通可能是因为uboot中gatewayip没设置,我就实际测试设置网管为同网段.1,再次测试结论是第一次ping通了,第二次开始又不通了。

7.1、开发板运行linux下和主机Windows的ping通

(1)先将开发板刷机成linux+QT镜像(刷机见裸机教程第三部分),然后启动进入linux命令行终端下。

(2)在linux下使用ifconfig命令将开发板中linux系统的IP地址设置为和主机windows同一网段(为了上课方便,以后就固定:主机windows地址192.168.1.10,开发板uboot或linux的地址为192.168.1.20,虚拟机ubuntu地址为192.168.1.141)

(3)此时开发板端ping windows通的。

(4)windows中ping开发板也是通的。

说明:首先开发板和主机的网络部分硬件都是好的,网络连接也是好的,主机windows中的网络软件设置是好的。

7.2、开发板运行linux下和虚拟机ubuntu的ping通

(1)在linux基础课中讲过:虚拟机的网卡设置可以选择好几种方式,常用的就是NAT和桥接(bridged)。

(2)虚拟机要和开发板进行网络通信,只能通过桥接方式连接。

(3)虚拟机要想被开发板ping通,设置步骤如下:

第一步:虚拟机设置成桥接方式。

第二步:虚拟机的菜单中有个“虚拟网络编辑器”,这里面要设置为桥接到有线网卡。(默认是自动的,自动的一般会影响ping通。因为电脑现在一般都有2个网卡:一个有线的一个无线的。如果选了自动,那么虚拟机会自动桥接到无线网卡上,但是我们却是通过有线网卡来连接开发板的,自然ping不通)

第三步:在虚拟机ubuntu中设置IP地址为192.168.1.141(可以通过/etc/network/interfaces文件来设置静态的然后重启;也可以直接命令行ifconfig去设置)

(4)此时开发板ping虚拟机ubuntu应该就通了。

(5)此时虚拟机ubuntu中ping开发板也是通的。

7.3、开发板运行uboot下和主机Windows的ping通

(1)刚才开发板运行linux时和主机windows、虚拟机ubuntu都ping通了,说明硬件和连接和主机设置没错。

(2)此时开发板重启进入uboot,设置好ipaddr、gatewayip,然后去ping windows发现还是不通。 怀疑uboot本身网络驱动有问题。

(3)然后同样情况下尝试去ping通虚拟机ubuntu,理论分析应该也不通,但是实际发现是通的。

7.4、开发板运行uboot下和虚拟机ubuntu的ping通

uboot和虚拟机ubuntu互相ping通(前提是虚拟机ubuntu设置为桥接,且桥接到有线网卡,且ip地址设置正确的情况下)

结论:开发板中运行的uboot有点小bug,ping windows就不通,ping虚拟机ubuntu就通。

8.uboot常用命令3

8.1、tftp下载指令:tftp

(1)uboot本身主要目标是启动内核,为了完成启动内核必须要能够部署内核,uboot为了部署内核就需要将内核镜像从主机中下载过来然后烧录到本地flash中。uboot如何从主机(windows或者虚拟机ubuntu)下载镜像到开发板上?有很多种方式,主流方式是:fastboot和tftp。

fastboot的方式是通过USB线进行数据传输。

tftp的方式是通过有线网络的。典型的方式就是通过网络,fastboot是近些年才新发展的。

(2)tftp方式下载时实际上uboot扮演的是tftp客户端程序角色,主机windows或虚拟机ubuntu中必须有一个tftp服务器,然后将要下载的镜像文件放在服务器的下载目录中,然后开发板中使用uboot的tftp命令去下载即可。

(3)有些人习惯在windows中搭建tftp服务器,一般是用一些软件来搭建(譬如tftpd32,使用起来比较简单);有些人习惯在linux下搭建tftp服务器,可以参考网盘中的虚拟机下载目录下的一个教程《嵌入式开发环境搭建-基于14.04.pdf》,这里面有ubuntu中搭建tftp服务器的教程,也可以自己上网搜索教程尝试。(如果你直接就用我的虚拟机,那就已经搭建好了,不用再搭建了;如果是自己新装的那就参考文档搭建;如果你的版本和我的不一样那搭建过程可能不一样)

(4)我的虚拟机搭建的时候设置的tftp下载目录是/tftpboot,将要被下载的镜像复制到这个目录下。

(5)检查开发板uboot的环境变量,注意serverip必须设置为虚拟机ubuntu的ip地址。(serverip这个环境变量的意义就是主机tftp服务器的ip地址)

(6)然后在开发板的uboot下先ping通虚拟机ubuntu,然后再尝试下载:tftp 0x30000000 zImage-qt(意思是将服务器上名为zImage-qt的文件下载到开发板内存的0x30000000地址处。)

(7)镜像下载到开发板的DDR中后,uboot就可以用movi指令进行镜像的烧写了。

注意:

如果你是用的windows下的tftp服务器,那uboot的serverip就要设置为和windwos下tftp服务器的ip地址一样(windows下的tftp服务器软件设置的时候就有个步骤是让你设置服务器的ip地址,这个ip地址和主机windows必须在一个网段)。

9.uboot的常用命令4

9.1、SD卡/iNand操作指令movi

(1)开发板如果用SD卡/EMMC/iNand等作为Flash,则在uboot中操作flash的指令为movi(或mmc)

(2)movi指令是一个命令集,有很多子命令,具体用法可以help movi查看。

(3)movi的指令都是movi read和movi write一组的,movi read用来读取iNand到DDR上,movi write用来将DDR中的内容写入iNand中。理解这些指令时一定要注意涉及到的2个硬件:iNand和DDR内存。

(4)movi read {u-boot | kernel} {addr} 这个命令使用了一种通用型的描述方法来描述:movi 和read外面没有任何标记说明每一次使用这个指令都是必选的;一对大括号{}括起来的部分必选1个;大括号中的竖线表是多选一;中括号[]表示可选参数

(5)指令有多种用法,譬如 movi read u-boot 0x30000000,意思就是把iNand中的u-boot分区读出到DDR的0x30000000起始的位置处。(uboot代码中将iNand分成了很多个分区,每个分区有地址范围和分区名,uboot程序操作中可以使用直接地址来操作iNand分区,也可以使用分区名来操作分区。);注意这里的0x30000000也可以直接写作30000000,意思是一样的(uboot的命令行中所有数字都被默认当作十六进制处理,不管你加不加0x都一样)。

9.2、NandFlash操作指令nand

理解方法和操作方法完全类似于movi指令

9.3、内存操作指令:mm、mw、md

(1)DDR中是没有分区的(只听说过对硬盘、Flash进行分区,没听说过对内存进行分区····),但是内存使用时要注意,千万不能越界踩到别人了。因为uboot是一个裸机程序,不像操作系统会由系统整体管理所有内存,系统负责分配和管理,系统会保证内存不会随便越界。然后裸机程序中uboot并不管理所有内存,内存是散的随便用的,所以如果程序员(使用uboot的人)自己不注意就可能出现自己把自己的数据给覆盖了。(所以你思考下我们为什么把uboot放在23E00000地址处)

(2)md就是memory display,用来显示内存中的内容。

(3)mw就是memory write,将内容写到内存中

(4)mm就是memory modify,修改内存中的某一块,说白了还是写内存(如果需要批量的逐个单元的修改内存,用mm最合适)

9.4、启动内核指令:bootm、go

(1)uboot的终极目标就是启动内核,启动内核在uboot中表现为一个指令,uboot命令行中调用这个指令就会启动内核(不管成功与否,所以这个指令是一条死路)。

(2)差别:bootm启动内核同时给内核传参,而Go命令启动内核不传参。bootm其实才是正宗的启动内核的命令,一般情况下都用这个;go命令本来不是专为启动内核设计的,go命令内部其实就是一个函数指针指向一个内存地址然后直接调用那个函数,go命令的实质就是PC直接跳转到一个内存地址去运行而已。go命令可以用来在uboot中执行任何的裸机程序(有一种调试裸机程序的方法就是事先启动uboot,然后在uboot中去下载裸机程序,用go命令去执行裸机程序)

10.uboot的常用环境变量1

10.1、环境变量如何参与程序运行

(1)环境变量有2份,一份在Flash中,另一份在DDR中。uboot开机时一次性从Flash中读取全部环境变量到DDR中作为环境变量的初始化值,然后使用过程中都是用DDR中这一份,用户可以用saveenv指令将DDR中的环境变量重新写入Flash中去更新Flash中环境变量。下次开机时又会从Flash中再读一次。

(2)环境变量在uboot中是用字符串表示的,也就是说uboot是按照字符匹配的方式来区分各个环境变量的。因此用的时候一定要注意不要打错字了。

1、自动运行倒数时间:bootdelay

2、网络设置:ipaddr serverip

(1)ipaddr是开发板的本地IP地址

(2)serverip是开发板通过tftp指令去tftp服务器下载东西时,tftp服务器的IP地址。

(3)gatewayip是开发板的本地网关地址

(4)netmask是子网掩码

(5)ethaddr是开发板的本地网卡的MAC地址。

11.uboot的常用环境变量2

11.1、自动运行命令设置:bootcmd

(1)uboot启动后会开机自动倒数bootdelay秒,如果没有人按下回车打断启动,则uboot会自动执行启动命令来启动内核。

(2)uboot开机自动启动时实际就是在内部执行了bootcmd这个环境变量的值所对应的命令集:bootcmd=movi read kernel 30008000; bootm 30008000

意思是:将iNand的kernel分区读取到DDR内存的0x30008000地址处,然后使用bootm启动命令从内存0x30008000处去启动内核。

(3)set bootcmd printenv,然后saveenv;然后重启则会看到启动倒数后自动执行printenv命令打印出环境变量。这个小实验说明开机自动执行了bootcmd。

(4)设置bootcmd环境变量:set bootcmd 'movi read kernel 30008000; bootm 30008000'

11.2、uboot给kernel传参:bootargs

(1)linux内核启动时可以接收uboot给他传递的启动参数,这些启动参数是uboot和内核约定好的形式、内容,linux内核在这些启动参数的指导下完成启动过程。这样的设计是为了灵活,为了内核在不重新编译的情况下可以用不同的方式启动。

(2)我们要做的事情就是:在uboot的环境变量中设置bootargs,然后bootm命令启动内核时会自动将bootargs传给内核。

(3)环境变量bootargs=console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3

意义解释:

console=ttySAC2,115200 控制台使用串口2,波特率115200.

root=/dev/mmcblk0p2 rw 根文件系统在SD卡端口0设备(iNand)第2分区,根文件系统是可读可写的

init=/linuxrc linux的进程1(init进程)的路径

rootfstype=ext3 根文件系统的类型是ext3

(4)内核传参非常重要。在内核移植的时候,新手经常因为忘记给内核传参,或者给内核传递的参数不对,造成内核启动不起来。

11.3、新建、更改、删除一个环境变量的方法

(1)新建一个环境变量,使用set var value

(2)更改一个环境变量,使用set var value

(3)删除一个环境变量,使用set var

注意:修改完成环境变量后一定要保存,否则下次开机更改就又没了。

12.uboot中对Flash和DDR的管理

12.1、uboot阶段Flash的分区

(1)所谓分区,就是说对Flash进行分块管理。

(2)PC机等产品中,因为大家都是在操作系统下使用硬盘的,整个硬盘由操作系统统一管理,操作系统会使用文件系统帮我们管理硬盘空间。(管理保证了文件之间不会互相堆叠),于是乎使用者不用自己太过在意分区问题。

(3)在uboot中是没有操作系统的,因此我们对Flash(相当于硬盘)的管理必须事先使用分区界定(实际上在uboot中和kernel中都有个分区表,分区表就是我们在做系统移植时对Flash的整体管理分配方法)。有了这个界定后,我们在部署系统时按照分区界定方法来部署,uboot和kernel的软件中也是按照这个分区界定来工作,就不会错。

(4)分区方法不是一定的,不是固定的,是可以变动的。但是在一个移植中必须事先设计好定死,一般在设计系统移植时就会定好,定的标准是:

uboot:uboot必须从Flash起始地址开始存放(也许是扇区0,也许是扇区1,也许是其他,取决于SoC的启动设计),uboot分区的大小必须保证uboot肯定能放下,一般设计为512KB或者1MB(因为一般uboot肯定不足512KB,给再大其实也可以工作,但是浪费);

环境变量:环境变量分区一般紧贴着uboot来存放,大小为32KB或者更多一点。

kernel:kernel可以紧贴环境变量存放,大小一般为3MB或5MB或其他。

rootfs:······

剩下的就是自由分区,一般kernel启动后将自由分区挂载到rootfs下使用

总结:一般规律如下:

(1)各分区彼此相连,前面一个分区的结尾就是后一个分区的开头。

(2)整个flash充分利用,从开头到结尾。

(3)uboot必须在Flash开头,其他分区相对位置是可变的。

(4)各分区的大小由系统移植工程师自己来定,一般定为合适大小(不能太小,太小了容易溢出;不能太大,太大了浪费空间)

(5)分区在系统移植前确定好,在uboot中和kernel中使用同一个分区表。将来在系统部署时和系统代码中的分区方法也必须一样。

12.2、uboot阶段DDR的分区

(1)DDR的分区和Flash的分区不同,主要是因为Flash是掉电存在的,而DDR是掉电消失,因此可以说DDR是每次系统运行时才开始部署使用的。

(2)内存的分区主要是在linux内核启动起来之前,linux内核启动后内核的内存管理模块会接管整个内存空间,那时候就不用我们来管了。

(3)注意内存分区关键就在于内存中哪一块用来干什么必须分配好,以避免各个不同功能使用了同一块内存造成的互相踩踏。譬如说我们tftp 0x23E00000 zImage去下载zImage到内存的0x23E00000处就会出错,因为这个内存处实际是uboot的镜像所在。这样下载会导致下载的zImage把内存中的uboot给冲掉。

广告商务合作,请联系0755-33248146

「正点原子Linux连载」第三十章U-Boot使用实验(二)

1)实验平台:正点原子Linux开发板

2)摘自《正点原子 I.MX6U嵌入式Linux驱动开发指南

关注官方微信号公众号,获取更多资料:正点原子

30.4.7 EXT格式文件系统操作命令

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

结果如图30.4.7.1所示:

图30.4.7.1ext4ls命令

关于ext格式文件系统其他命令的操作参考30.4.6小节的即可,这里就不讲解了。

30.4.8 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版核心板启动信息如图30.4.8.1所示:

图30.4.8.1 NAND核心板启动信息

从图30.4.8.1可以看出,当前开发板的NAND容量为512MiB。输入“? nand”即可查看所有有关NAND令,如图30.4.8.2所示:

图30.4.8.2 NAND相关操作命令

可以看出,NAND相关的操作命令少,本节我们讲解一些常用的命令。

1、nand info命令

此命令用户打印NAND Flash信息,输入“nandinfo”,结果如图30.4.8.3所示:

图30.4.8.3 nand信息

图30.4.8.3中给出了NAND的页大小、OOB域大小,擦除大小等信息。可以对照着所使用的NAND Flash数据手册来查看一下这些信息是否正确。

2、nanddevice命令

nanddevice用于切换NAND Flash,如果你的板子支持多片NAND的话就可以使用此命令来设置当前所使用的NAND。这个需要你的CPU有两个NAND控制器,并且两个NAND控制器各接一片NAND Flash。就跟I.MX6U有两个SDIO接口,这两个SDIO接口可以接两个MMC设备一样。不过一般情况下CPU只有一个NAND接口,而且在使用中只接一片NAND。

3、nanderase命令

nanderase命令用于擦除NAND Flash,NAND Flash的特性决定了在向NAND Flash写数据之前一定要先对要写入的区域进行擦除。“nanderase”命令有三种形式:

nand erase[.spread] [clean] off size //从指定地址开始(off)开始,擦除指定大小(size)的区域。

nand erase.part [clean] partition //擦除指定的分区

nand erase.chip [clean] //全篇擦除

NAND的擦除命令一般是配合写命令的,后面讲解NAND写命令的时候在演示如何使用“nand erase”。

4、nandwrite命令

此命令用于向NAND指定地址写入指定的数据,一般和“nanderase”命令配置使用来更新NAND中的uboot、linuxkernel或设备树等文件,命令格式如下:

nand write addr off size

addr是要写入的数据首地址,off是NAND中的目的地址,size是要写入的数据大小。

以更新NAND中的uboot为例,讲解一下如何使用此命令。先编译出来NAND版本的u-boot.imx文件,在烧写之前要先对NAND进行分区,也就是规划好uboot、linuxkernel、设备树和根文件系统的存储区域,I.MX6U-ALPHA开发板的NAND分区如下:

0x000000000000-0x000004000000 : "boot"

0x000004000000-0x000006000000 : "kernel"

0x000006000000-0x000007000000 : "dtb"

0x000007000000-0x000020000000 : "rootfs"

一共有四个分区,第一个分区存放uboot,地址范围为0x0~0x4000000(共64MB);第二个分区存放kernel(也就是linuxkernel),地址范围为0x4000000~0x6000000(共32MB);第三个分区存放dtb(设备树),地址范围为0x6000000~0x7000000(共16MB);剩下的所有存储空间全部作为最后一个分区,存放rootfs(根文件系统)。

uboot是从地址0开始存放的,其实用不了这么大的区域,但是为了好管理才分配了这么大的,将NAND版本的u-boot.imx文件放到Ubuntu中的tftpboot目录中,然后使用tftp命令将其下载到开发板的0X87800000地址处,最终使用“nandwrite”将其烧写到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的数据写入的,所以肯定会写入一些无效的数据。你也可以将写入的大小改为u-boot.imx这个文件的大小,这样写入的数据量就是u-boot.imx的实际大小了。

同理我们也可以更新NAND中的linuxkernel和设备树(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的大小,这样连下载都没法下载,更别说更新了。

4、nand read命令

此命令用于从NAND中的指定地址读取指定大小的数据到DRAM中,命令格式如下:

nand read addr off size

addr是目的地址,off是要读取的NAND中的数据源地址,size是要读取的数据大小。比如我们读取设备树(dtb)文件到0x83000000地址处,命令如下:

nand read 0x83000000 0x6000000 0x19000

过程如图30.4.8.4所示:

图30.4.8.4 nandread读取过程

设备树文件读取到DRAM中以后就可以使用fdt命令来对设备树进行操作了,首先设置fdt的地址,fdt地址就是DRAM中设备树的首地址,命令如下:

fdtaddr 83000000

设置好以后可以使用“fdtheader”来查看设备树的头信息,输入命令:

fdtheader

结果如图30.4.8.5所示:

图30.4.8.5 设备树头信息

输入命令“fdtprint”就可以查看设备树文件的内容,输入命令:

fdtprint

结果如图30.4.8.6所示:

图30.4.8.6 设备树文件

图30.4.8.6中的文件就是我们写到NAND中的设备树文件,至于设备树文件的详细内容我们后面会有专门的章节来讲解,这里大家知道这个文件就行了。

NAND常用的操作命令就是擦除、读和写,至于其他的命令大家可以自行研究一下,一定不要尝试全片擦除NAND的指令!!否则NAND就被全部擦除掉了,什么都没有了,又得重头烧整个系统。

30.4.9 BOOT操作命令

uboot的本质工作是引导Linux,所以uboot肯定有相关的boot(引导)命令来启动Linux。常用的跟boot有关的命令有:bootz、bootm和boot。

1、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文件夹里面,完成以后的tftpboot文件夹如图30.4.9.1所示:

图30.4.9.1tftpboot文件夹

下载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

命令运行结果如图30.4.9.2所示:

图30.4.9.2通过网络启动Linux

上图就是我们通过tftp和bootz命令来从网络启动Linux系统,如果我们要从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

命令运行结果如图30.4.9.3所示:

图30.4.6.3从EMMC中启动Linux

2、bootm命令

bootm和bootz功能类似,但是bootm用于启动uImage镜像文件。如果不使用设备树的话启动Linux内核的命令如下:

bootmaddr

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-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

运行结果如图30.4.6.4所示:

图30.4.6.4设置bootcmd从网络启动Linux

前面说过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 imx6ull-alientek_emmc.dtb; bootz 80800000 - 83000000'

savenev

boot

运行结果如图30.4.6.5所示:

图30.4.6.5设置bootcmd从EMMC启动Linux

如果不修改bootcmd的话,每次开机uboot倒计时结束以后都会自动从EMMC里面读取zImage和imx6ull-alientek-emmc.dtb,然后启动Linux。

30.4.10其他常用命令

uboot中还有其他一些常用的命令,比如reset、go、run和mtest等。

1、reset命令

reset命令顾名思义就是复位的,输入“reset”即可复位重启,如图30.4.10.1所示:

图30.4.10.1reset命令运行结果

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这个应用,命令如下:

tftp 87800000 printf.bin

go 87800000

结果如图30.4.10.2所示:

图30.4.10.2go命令运行裸机例程

从图30.4.10.2可以看出,通过go命令我们就可以在uboot中运行裸机例程。

3、run命令

run命令用于运行环境变量中定义的命令,比如可以通过“runbootcmd”来运行bootcmd中的启动命令,但是run命令最大的作用在于运行我们自定义的环境变量。在后面调试Linux系统的时候常常要在网络启动和EMMC/NAND启动之间来回切换,而bootcmd只能保存一种启动方式,如果要换另外一种启动方式的话就得重写bootcmd,会很麻烦。这里我们就可以通过自定义环境变量来实现不同的启动方式,比如定义环境变量mybootemmc表示从emmc启动,定义mybootnet表示从网络启动,定义mybootnand表示从NAND启动。如果要切换启动方式的话只需要运行“runmybootxxx(xxx为emmc、net或nand)”即可。

说干就干,创建环境变量mybootemmc、mybootnet和mybootnand,命令如下:

setenv mybootemmc 'fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-alientek-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来实现不同的启动:

runmybootemmc

run mytoobnand

run mybootnet

4、mtest命令

mtest命令是一个简单的内存读写测试命令,可以用来测试自己开发板上的DDR,命令格式如下:

mtest [start [end [pattern [iterations]]]]

start是要测试的DRAM开始地址,end是结束地址,比如我们测试0X80000000~0X80001000这段内存,输入“mtest8000000080001000”,结果如图30.4.10.3所示:

图30.4.10.3mtest命令运行结果

从图30.4.10.3可以看出,测试范围为0X80000000~0X80001000,已经测试了486次,如果要结束测试就按下键盘上的“Ctrl+C”键。

至此,uboot常用的命令就讲解完了,如果要使用uboot的其他命令,可以查看uboot中的帮助信息,或者上网查询一下相应的资料。

图30.1.1uboot官网

我们可以在uboot官网下载uboot源码,点击图30.1.1中左侧Topics中的“Source Code”,打开如图30.1.2所示界面:

图30.1.2uboot源码界面

点击图30.1.2中的“FTP Server”,进入其FTP服务器即可看到uboot源码,如图30.1.3所示:

图30.1.3uboot源码

图30.1.3中就是uboot原汁原味的源码文件,目前最新的版本是2019.04。但是我们一般不会直接用uboot官方的U-Boot源码的。uboot官方的uboot源码是给半导体厂商准备的,半导体厂商会下载uboot官方的uboot源码,然后将自家相应的芯片移植进去。也就是说半导体厂商会自己维护一个版本的uboot,这个版本的uboot相当于是他们定制的。既然是定制的,那么肯定对自家的芯片支持会很全,虽然uboot官网的源码中一般也会支持他们的芯片,但是绝对是没有半导体厂商自己维护的uboot全面。

NXP就维护的2016.03这个版本的uboot,下载地址为:http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tag/?h=imx_v2016.03_4.1.15_2.0.0_ga&id=

rel_imx_4.1.15_2.1.0_ga,下载界面如图30.1.4所示:

图30.1.4 NXP官方uboot下载界面

图30.1.4中的uboot-imx_rel_imx4.1.15_2.1.0_ga.xx(xx为zip、tar.gz或tar.bz2)就是NXP官方维护的uboott,后面我们学习uboot移植的时候就是使用的图30.1.4中的uboot,下载uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。我们已经放到了开发板光盘中,路径为:开发板光盘->1、程序源码->4、NXP官方原版Uboot和Linux->uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。图30.1.4中的uboot基本支持了NXP当前所有可以跑Linux的芯片,而且支持各种启动方式,比如EMMC、NAND、NOR FLASH等等,这些都是uboot官方所不支持的。但是图30.1.4中的uboot是针对NXP自家评估板的,如果是我们自己做的板子就需要修改NXP官方的uboot,使其支持我们自己做的板子,正点原子的I.MX6U开发板就是自己做的板子,虽然大部分都参考了NXP官方的I.MX6ULL EVK开发板,但是还是有很多不同的地方,所以需要修改NXP官方的uboot,使其适配正点原子的I.MX6U开发板。所以当我们拿到开发板以后,是有三种uboot的,这三种uboot的区别如表30.1.1所示:

种类

描述

uboot官方的uboot代码

由uboot官方维护开发的uboot版本,版本更新快,基本包含所有常用的芯片。

半导体厂商的uboot代码

半导体厂商维护的一个uboot,专门针对自家的芯片,在对自家芯片支持上要比uboot官方的好。

开发板厂商的uboot代码

开发板厂商在半导体厂商提供的uboot基础上加入了对自家开发板的支持。

表30.1.1三种uboot的区别

那么这三种uboot该如何选择呢?首先uboot官方的基本是不会用的,因为支持太弱了。最常用的就是半导体厂商或者开发板厂商的uboot,如果你用的半导体厂商的评估板,那么就使用半导体厂商的uboot,如果你是购买的第三方开发板,比如正点原子的I.MX6ULL开发板,那么就使用正点原子提供的uboot源码(也是在半导体厂商的uboot上修改的)。当然了,你也可以在购买了第三方开发板以后使用半导体厂商提供的uboot,只不过有些外设驱动可能不支持,需要自己移植,这个就是我们常说的uboot移植。

本节是uboot的使用,所以就直接使用正点原子已经移植好的uboot,这个已经放到了开发板光盘中了,路径为:开发板光盘->1、程序源码->3、正点原子修改后的Uboot和Linux->uboot-imx-rel_imx_4.1.15_2.1.0_ga_alientek.tar.bz2。

30.2U-Boot初次编译

在Ubuntu中创建存放uboot的目录,比如我的是/home/$USER/linux/uboot,然后在此目录下新建一个名为“alientek_uboot”的文件夹用于存放正点原子提供的uboot源码。alientek_uboot文件夹创建成功以后使用FileZilla软件将正点原子提供的uboot源码拷贝到此目录中,正点原子提供的uboot源码已经放到了开发板光盘中,路径为:开发板光盘->1、例程源码->3、正点原子修改后的Uboot和Linux-> uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2。将其拷贝到Ubuntu中新建的alientek_uboot文件夹下,完成以后如图30.2.1所示:

图30.2.1将uboot拷贝到Ubuntu中

使用如下命令对其进行解压缩:

tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2

解压完成以后alientek_uboot文件夹内容如图30.2.2所示:

图30.2.2 解压后的uboot

图30.2.2中除了uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2这个正点原子提供的uboot源码压缩包以外,其他的文件和文件夹都是解压出来的uboot源码。

1、512MB(DDR3)+8GB(EMMC)核心板

如果使用的是512MB+8G的EMMC核心板,使用如下命令来编译对应的uboot:

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

这三条命令中ARCH=arm设置目标为arm架构,CROSS_COMPILE指定所使用的交叉编译器。第一条命令相当于“makedistclean”,目的是清除工程,一般在第一次编译的时候最好清理一下工程。第二条指令相当于“make mx6ull_14x14_ddr512_emmc_defconfig”,用于配置uboot,配置文件为mx6ull_14x14_ddr512_emmc_defconfig。最后一条指令相当于“make -j12”也就是使用12核来编译uboot。当这三条命令执行完以后uboot也就编译成功了,如图30.2.3所示:

图30.2.3 编译完成

编译完成以后的alentek_uboot文件夹内容如图30.2.4所示:

图30.2.4 编译后的uboot源码

可以看出,编译完成以后uboot源码多了一些文件,其中u-boot.bin就是编译出来的uboot二进制文件。uboot是个裸机程序,因此需要在其前面加上头部(IVT、DCD等数据)才能在I.MX6U上执行,图30.2.4中的u-boot.imx文件就是添加头部以后的u-boot.bin,u-boot.imx就是我们最终要烧写到开发板中的uboot镜像文件。

每次编译uboot都要输入一长串命令,为了简单起见,我们可以新建一个shell脚本文件,将这些命令写到shell脚本文件里面,然后每次只需要执行shell脚本即可完成编译工作。新建名为mx6ull_alientek_emmc.sh的shell脚本文件,然后在里面输入如下内容:

示例代码30.2.1 mx6ull_alientek_emmc.sh文件代码

1 #!/bin/bash

2 make ARCH= arm CROSS_COMPILE= arm- linux- gnueabihf- distclean

3 make ARCH= arm CROSS_COMPILE= arm- linux- gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig

4 make V= 1 ARCH= arm CROSS_COMPILE= arm- linux- gnueabihf-- j12

第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_alientek_emmc_defconfig。前面说了uboot是bootloader的一种,可以用来引导Linux,但是uboot除了引导Linux以外还可以引导其它的系统,而且uboot还支持其它的架构和外设,比如USB、网络、SD卡等。这些都是可以配置的,需要什么功能就使能什么功能。所以在编译uboot之前,一定要根据自己的需求配置uboot。mx6ull_alientek_emmc_defconfig就是正点原子针对I.MX6U-ALPHA的EMMC核心板编写的配置文件,这个配置文件在uboot-imx-rel_imx_4.1.15_2.1.0_ga_alientek /configs目录中。在uboot中,通过“makexxx_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文件可执行权限,然后就可以使用这个shell脚本文件来重新编译uboot,命令如下:

./mx6ull_alientek_emmc.sh

1、256MB(DDR3)+256MB/512MB(NAND)核心板

如果用的256MB+256MB/512MB的NAND核心板,新建名为mx6ull_alientek_nand.sh的shell脚本文件,然后在里面输入如下内容:

示例代码30.2.2 mx6ull_alientek_nand.sh文件代码

1 #!/bin/bash

2 make ARCH= arm CROSS_COMPILE= arm- linux- gnueabihf- distclean

3 make ARCH= arm CROSS_COMPILE= arm- linux- gnueabihf- mx6ull_14x14_ddr256_nand_defconfig

4 make V= 1 ARCH= arm CROSS_COMPILE= arm- linux- gnueabihf-- j12

完成以后同样使用chmod指令给予mx6ull_alientek_nand.sh可执行权限,然后输入如下命令即可编译NAND版本的uboot:

./mx6ull_alientek_nand.sh

mx6ull_alientek_nand.sh和mx6ull_alientek_emmc.sh类似,只是uboot配置文件不同,这里就不详细介绍了。

30.3 U-Boot烧写与启动

uboot编译好以后就可以烧写到板子上使用了,这里我们跟前面裸机例程一样,将uboot烧写到SD卡中,然后通过SD卡来启动来运行uboot。使用imxdownload软件烧写,命令如下:

chmod 777 imxdownload //给予imxdownload可执行权限,一次即可

./imxdownload u-boot.bin /dev/sdd

等待烧写完成,完成以后将SD卡插到I.MX6U-ALPHA开发板上,BOOT设置从SD卡启动,使用USB线将USB_TTL和电脑连接,也就是将开发板的串口1连接到电脑上。打开SecureCRT,设置好串口参数并打开,最后复位开发板。在SecureCRT上出现“Hit any key to stop autoboot: ”倒计时的时候按下键盘上的回车键,默认是3秒倒计时,在3秒倒计时结束以后如果没有按下回车键的话uboot就会使用默认参数来启动Linux内核了。如果在3秒倒计时结束之前按下回车键,那么就会进入uboot的命令行模式,如图30.3.1所示:

图30.3.1 uboot启动过程

从图30.3.1可以看出,当进入到uboot的命令行模式以后,左侧会出现一个“=>”标志。uboot启动的时候会输出一些信息,这些信息如下所示:

示例代码30.3.1 uboot输出信息

1 U-Boot 2016.03 (Apr 12 2019 - 02:33:00 +0800)

2

3 CPU: Freescale i.MX6ULL rev1.1 69 MHz (running at 396 MHz)

4 CPU: Industrial temperature grade (-40C to 105C) at 46C

5 Reset cause: POR

6 Board: MX6ULL 14x14 EVK

7 I2C: ready

8 DRAM: 512 MiB

9 MMC: FSL_SDHC: 0, FSL_SDHC: 1

10 Display: ATK-LCD-7-1024x600 (1024x600)

11 Video: 1024x600x24

12 ** Unrecognized filesystem type **

13 In: serial

14 Out: serial

15 Err: serial

16 switch to partitions #0, OK

17 mmc0 is current device

18 Net: FEC1

19 Normal Boot

20 Hit any key to stop autoboot: 0

21 =>

第1行是uboot版本号和编译时间,可以看出,当前的uboot版本号是2016.03,编译时间是2019年4月12日凌晨2点33(没错!为了赶教程和例程,我这一年多以来基本每天晚上工作到凌晨两三点!看到这里一定要记得到论坛夸我一下!)。

第3和第4行是CPU信息,可以看出当前使用的CPU是飞思卡尔的I.MX6ULL(I.MX以前属于飞思卡尔,然而飞思卡尔被NXP收购了),如果使用528MHz的I.MX6ULL,此处会显示主频为528MHz。但是如果使用800MHz的I.MX6ULL的话此处会显示69MHz,这个是uboot内部主频读取错误,但是不影响运行,可以不用管。不管是528MHz还是800MHz的I.MX6ULL,此时都运行在396MHz。这颗芯片是工业级的,可以工作在-40°C~105°C。

第5行是复位原因,当前的复位原因是POR。I.MX6ULL芯片上有个POR_B引脚,将这个引脚拉低即可复位I.MX6ULL。

第6行是板子名字,当前的板子名字为“MX6ULL 14x14 EVK”。

第7行提示I2C准备就绪。

第8行提示当前板子的DRAM(内存)为512MB,如果是NAND版本的话内存为256MB。

第9行提示当前有两个MMC/SD卡控制器:FSL_SDHC(0)和FSL_SDHC(1)。I.MX6ULL支持两个MMC/SD,正点原子的I.MX6ULL EMMC核心板上FSL_SDHC(0)接的EMMC,FSL_SDHC(1)接的SD(TF)卡。

第10和第11行是LCD型号,当前的LCD型号是ATK-LCD-7-1024x600 (1024x600),分辨率为1024x600,格式为RGB888(24位)。

第13~15是标准输入、标准输出和标准错误所使用的终端,这里都使用串口(serial)作为终端。

第16和17行是切换到emmc的第0个分区上,因为当前的uboot是emmc版本的,也就是从emmc启动的。我们只是为了方便将其烧写到了SD卡上,但是它的“内心”还是EMMC的。所以uboot启动以后会将emmc作为默认存储器,当然了,你也可以将SD卡作为uboot的存储器,这个我们后面会讲解怎么做。

第18行是网口信息,提示我们当前使用的FEC1这个网口,I.MX6ULL支持两个网口。

第19行提示正常启动,也就是说uboot要从emmc里面读取环境变量和参数信息启动Linux内核了。

第20行是倒计时提示,默认倒计时3秒,倒计时结束之前按下回车键就会进入Linux命令行模式。如果在倒计时结束以后没有按下回车键,那么Linux内核就会启动,Linux内核一旦启动,uboot就会寿终正寝。

这个就是uboot默认输出信息的含义,NAND版本的uboot也是类似的,只是NAND版本的就没有EMMC/SD相关信息了,取而代之的就是NAND的信息,比如NAND容量大小信息。

uboot是来干活的,我们现在已经进入uboot的命令行模式了,进入命令行模式以后就可以给uboot发号施令了。当然了,不能随便发号施令,得看看uboot支持哪些命令,然后使用这些uboot所支持的命令来做一些工作。下一节就讲解uboot命令的使用。

30.4U-Boot命令使用

进入uboot的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前uboot所支持的命令,如图30.4.1所示:

图30.4.1 uboot命令列表

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

? bootz 或 helpbootz

结果如图30.4.2所示:

图30.4.2bootz命令使用说明

图30.4.2中就详细的列出了“bootz”这个命令的详细,其它的命令也可以使用此方法查询具体的使用方法。接下来我们学习一下一些常用的uboot命令。

30.4.1信息查询命令

常用的和信息查询有关的命令有3个:bdinfo、printenv和version。先来看一下bdinfo命令,此命令用于查看板子信息,直接输入“bdinfo”即可,结果如图30.4.1.1所示:

图30.4.1.1bdinfo命令

从图30.4.1.1中可以得出DRAM的其实地址和大小、启动参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息。

命令“printenv”用于输出环境变量信息,uboot也支持TAB键自动补全功能,输入“print”然后按下TAB键就会自动补全命令,直接输入“print”也可以。输入“print”,然后按下回车键,环境变量如图30.4.1.2所示:

图30.4.1.2printenv命令结果

在图30.4.1.2中有很多的环境变量,比如baudrate、board_name、board_rec、boot_fdt、bootcmd等等。uboot中的环境变量都是字符串,既然叫做环境变量,那么它的作用就和“变量”一样。比如bootdelay这个环境变量就表示uboot启动延时时间,默认bootdelay=3,也就默认延时3秒。前面说的3秒倒计时就是由bootdelay定义的,如果将bootdelay改为5的话就会倒计时5s了。uboot中的环境变量是可以修改的,有专门的命令来修改环境变量的值,稍后我们会讲解。

命令version用于查看uboot的版本号,输入“version”,uboot版本号如图30.4.1.3所示:

图30.4.1.3version命令结果

从图30.4.1.3可以看出,当前uboot版本号为2016.03,2019年4月12日编译的,编译器为arm-linux-gnueabihf-gcc等信息。

30.4.2环境变量操作命令

1、修改环境变量

环境变量的操作涉及到两个命令:setenv和saveenv,命令setenv用于设置或者修改环境变量的值。命令saveenv用于保存修改后的环境变量,一般环境变量是存放在外部flash中的,uboot启动的时候会将环境变量从flash读取到DRAM中。所以使用命令setenv修改的是DRAM中的环境变量值,修改以后要使用saveenv命令将修改后的环境变量保存到flash中,否则的话uboot下一次重启会继续使用以前的环境变量值。

命令saveenv使用起来很简单,格式为:

saveenv命令值

saveenv命令‘值1值2值3’

比如我们要将环境变量bootdelay改为5,就可以使用如下所示命令:

setenvbootdelay5

saveenv

上述命令执行过程如图30.4.2.1所示:

图30.4.2.1环境变量修改

在图30.4.2.1中,当我们使用命令saveenv保存修改后的环境变量的话会有保存过程提示信息,根据提示可以看出环境变量保存到了MMC(1)中,也就是EMMC中。因为我用的EMMC版本的核心板,所以会保存到MMC(1)中,如果是NAND版本核心板的话就会提示保存到NAND中。

修改bootdelay以后,重启开发板,uboot就是变为5秒倒计时,如图30.4.2.2所示:

图30.4.2.25秒倒计时

有时候我们修改的环境变量值可能会有空格,比如bootcmd、bootargs等,这个时候环境变量值就得用单引号括起来,比如下面修改环境变量bootcmd的值:

setenv bootcmd 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'

saveenv

上面命令设置bootcmd的值为“console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw”,其中“console=ttymxc0,115200”、“root=/dev/mmcblk1p2”、“rootwait”和“rw”相当于四组“值”,这四组“值”之间用空格隔开,所以需要使用单引号‘’将其括起来,表示这四组“值”都属于环境变量bootcmd。

2、新建环境变量

命令setenv也可以用于新建命令,用法就是修改环境变量一样,比如我们新建一个环境变量author,author的值为我的名字拼音:zuozhongkai,那么就可以使用如下命令:

setenv author zuozhongkai

saveenv

新建命令author完成以后重启uboot,然后使用命令printenv查看当前环境变量,如图30.4.2.3所示:

图30.4.2.3环境变量

从图30.4.2.3可以看到新建的环境变量:author,其值为:zuozhongkai。

3、删除环境变量

既然可以新建环境变量,那么就可以删除环境变量,删除环境变量也是使用命令setenv,要删除一个环境变量只要给这个环境变量赋空值即可,比如我们删除掉上面新建的author这个环境变量,命令如下:

setenv author

saveenv

上面命令中通过setenv给author赋空值,也就是什么都不写来删除环境变量author。重启uboot就会发现环境变量author没有了。

30.4.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,也就是20*2=40个字节;如果显示格式为.l的话就表示20个long,也就是20*4=80个字节。另外要注意:

uboot命令中的数字都是十六进制的!不是十进制的!

比如你想查看以0X80000000开始的20个字节的内存值,显示格式为.b的话,应该使用如下所示命令:

md.b 80000000 14

而不是:

md.b 80000000 20

上面说了,uboot命令里面的数字都是十六进制的,所以可以不用写“0x”前缀,十进制的20其十六进制为0x14,所以命令md后面的个数应该是14,如果写成20的话就表示查看32(十六进制为0x20)个字节的数据。分析下面三个命令的区别:

md.b 80000000 10

md.w 80000000 10

md.l 80000000 10

上面这三个命令都是查看以0X80000000为起始地址的内存数据,第一个命令以.b格式显示,长度为0x10,也就是16个字节;第二个命令以.w格式显示,长度为0x10,也就是16*2=32个字节;最后一个命令以.l格式显示,长度也是0x10,也就是16*4=64个字节。这三个命令的执行结果如图30.4.3.1所示:

图30.4.3.1md命令使用示例

2、nm命令

nm命令用于修改指定地址的内存值,命令格式如下:

nm [.b, .w, .l] address

nm命令同样可以以.b、.w和.l来指定操作格式,比如现在以.l格式修改0x80000000地址的数据为0x12345678。输入命令:

nm.l 80000000

输入上述命令以后如图30.4.3.2所示:

图30.4.3.2nm命令

在图30.4.3.2中,80000000表示现在要修改的内存地址,ffffff00表示地址0x80000000现在的数据,?后面就可以输入要修改后的数据0x12345678,输入完成以后按下回车,然后再输入‘q’即可退出,如图30.4.3.3所示:

图30.4.3.3修改内存数据

修改完成以后在使用命令md来查看一下有没有修改成功,如图30.4.3.4所示:

图30.4.3.4查看修改后的值

从图30.4.3.4可以看出,此时地址0X80000000的值变为了0x12345678。

3、mm命令

mm命令也是修改指定地址内存值的,使用mm修改内存值的时候地址会自增,而使用命令nm的话地址不会自增。比如以.l格式修改从地址0x80000000开始的连续3个内存块(3*4=12个字节)的数据为0X05050505,操作如图30.4.3.5所示:

图30.4.3.5命令mm

从图30.4.3.5可以看出,修改了地址0X80000000、0X80000004和0X8000000C的内容为0x05050505。使用命令md查看修改后的值,结果如图30.4.3.6所示:

图30.4.3.6查看修改后的内存数据

从图30.4.3.6可以看出内存数据修改成功。

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来查看,如图30.4.3.7所示:

图30.4.3.7查看修改后的内存数据

从图30.4.3.7可以看出内存数据修改成功。

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个字节),命令如下所示:

cp.l 80000000 80000100 10

结果如图30.4.3.8所示:

图30.4.3.8cp命令操作结果

在图30.4.3.8中,先使用md.l命令打印出地址0x80000000和0x80000100处的数据,然后使用命令cp.l将0x80000100处的数据拷贝到0x80000100处。最后使用命令md.l查看0x80000100处的数据有没有变化,检查拷贝是否成功。

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

结果如图30.4.3.9所示:

图30.4.3.9 cmp命令比较结果

从图30.4.3.9可以看出两段内存的数据相等。我们再随便挑两段内存比较一下,比如地址0x80002000和0x800003000,长度为0X10,比较结果如图30.4.3.10所示:

图30.4.3.10cmp命令比较结果

从图30.4.3.10可以看出,0x80002000处的数据和0x80003000处的数据就不一样。

30.4.4网络操作命令

uboot是支持网络的,我们在移植uboot的时候一般都要调通网络功能,因为在移植linuxkernel的时候需要使用到uboot的网络功能做调试。uboot支持大量的网络相关命令,比如dhcp、ping、nfs和tftpboot,我们接下来依次学习一下这几个和网络有关的命令。

在使用uboot的网络功能之前先用网线将开发板的ENET2接口和电脑或者路由器连接起来,I.MX6U-ALPHA开发板有两个网口:ENET1和ENET2,一定要连接ENET2,不能连接错了,ENET2接口如图30.4.4.1所示。

图30.4.4.1 ENET2网络接口

建议开发板和主机PC都连接到同一个路由器上!最后设置表30.4.4.1中所示的几个环境变量。

环境变量

描述

ipaddr

开发板ip地址,可以不设置,使用dhcp命令来从路由器获取IP地址。

ethaddr

开发板的MAC地址,一定要设置。

gatewayip

网关地址。

netmask

子网掩码。

serverip

服务器IP地址,也就是Ubuntu主机IP地址,用于调试代码。

表30.4.4.1网络相关环境变量

表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是不同的,否则通信会有问题!设置好网络相关的环境变量以后就可以使用网络相关命令了。

1、ping命令

开发板的网络能否使用,是否可以和服务器(Ubuntu主机)进行通信,通过ping命令就可以验证,直接ping服务器的IP地址即可,比如我的服务器IP地址为192.168.1.250,命令如下:

ping192.168.1.250

结果如图30.4.4.2所示:

图30.4.4.2ping命令

从图30.4.4.2可以看出,192.168.1.250这个主机存在,说明ping成功,uboot的网络工作正常。

2、dhcp命令

dhcp用于从路由器获取IP地址,前提得开发板连接到路由器上的,如果开发板是和电脑直连的,那么dhcp命令就会失效。直接输入dhcp命令即可通过路由器获取到IP地址,如图30.4.4.3所示:

图30.4.4.3dhcp命令

从图30.4.4.3可以看出,开发板通过dhcp获取到的IP地址为192.168.1.50,和我们手动设置的一样,这很正常。同时在图30.4.4.3中可以看到“warning:nobootfilename;”、“TFTP fromserver192.168.1.1”这样的字样。这是因为DHCP不单单是获取IP地址,其还会通过TFTP来启动linux内核,输入“? dhcp”即可查看dhcp命令详细的信息,如图30.4.4.4所示:

图30.4.4.4dhcp命令使用查询

3、nfs命令

nfs也就是网络文件系统,通过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服务开启我们在4.2.1小节已经详细讲解过了,包括NFS文件目录的创建,如果忘记的话可以去查看一下4.2.1小节。我设置的/home/zuozhongkai/linux/nfs这个目录为我的NFS文件目录。uboot中的nfs命令格式如下所示:

nfs [loadAddress] [[hostIPaddr:]bootfilename]

loadAddress是要保存的DRAM地址,[[hostIPaddr:]bootfilename]是要下载的文件地址。这里我们将正点原子官方编译出来的Linux镜像文件zImage下载到开发板DRAM的0x80800000这个地址处。正点原子编译出来的zImage文件已经放到了开发板光盘中,路径为:8、开发板系统镜像->zImage。将文件zImage通过FileZilla发送到Ubuntu中的NFS目录下,比如我的就是放到/home/zuozhongkai/linux/nfs这个目录下,完成以后的NFS目录如图30.4.4.5所示:

图30.4.4.5 NFS目录中的zImage文件

准备好以后就可以使用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。下载过程如图30.4.4.6所示:

图30.4.4.6nfs命令下载zImage过程

在图30.4.4.6中会以“#”提示下载过程,下载完成以后会提示下载的数据大小,这里下载的6071136字节,而zImage的大小就是6071136字节,如图30.4.4.7所示:

图30.4.4.7zImage大小

下载完成以后查看0x80800000地址处的数据,使用命令md.b来查看前100个字节的数据,如图30.4.4.8所示:

图30.4.4.8下载的数据

在使用winhex软件来查看zImage,检查一下前面的数据是否和图30.4.4.8只的一致,结果如图30.4.4.9所示:

图30.4.4.9winhex查看zImage

可以看出图30.4.4.8和图30.4.4.9中的前100个字节的数据一致,说明nfs命令下载到的zImage是正确的。

4、tftp命令

tftp命令的作用和nfs命令一样,都是用于通过网络下载东西到DRAM中,只是tftp命令使用的TFTP协议,Ubuntu主机作为TFTP服务器。因此需要在Ubuntu上搭建TFTP服务器,需要安装tftp-hpa和tftpd-hpa,命令如下:

sudo apt-get install tftp-hpa tftpd-hpa

和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,然后在里面输入如下内容:

示例代码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 = 1002

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

下载过程如图30.4.4.10所示:

图30.4.4.10 tftp命令下载过程

从图30.4.4.10可以看出,zImage下载成功了,网速为1.4MibB/s,文件大小为6071136字节。同样的,可以使用md.b命令来查看前100个字节的数据是否和图30.4.4.9中的相等。有时候使用fttp命令从Ubuntu中下载文件的时候会出现如图30.4.4.11所示的错误提示:

图30.4.4.11 tftp下载出错

在图30.4.4.11中可以看到“TFTP error: 'Permission denied' (0)”这样的错误提示,提示没有权限,出现这个错误一般有两个原因:

①、在Ubuntu中创建tftpboot目录的时候没有给予tftboot相应的权限。

②、tftpboot目录中要下载的文件没有给予相应的权限。

针对上述两个问题,使用命令“chmod777xxx”来给予权限,其中“xxx”就是要给予权限的文件或文件夹。

好了,uboot中关于网络的命令就讲解到这里,我们最常用的就是ping、nfs和tftp这三个命令。使用ping命令来查看网络的连接状态,使用nfs和tftp命令来从Ubuntu主机中下载文件。

30.4.5 EMMC和SD卡操作命令

uboot支持EMMC和SD卡,因此也要提供EMMC和SD卡的操作命令。一般认为EMMC和SD卡是同一个东西,所以没有特殊说明,本教程统一使用MMC来代指EMMC和SD卡。uboot中常用于操作MMC设备的命令为“mmc”。

mmc是一系列的命令,其后可以跟不同的参数,输入“?mmc”即可查看mmc有关的命令,如图30.4.5.1所示:

图30.4.5.1mmc命令

从图30.4.5.1可以看出,mmc后面跟不同的参数可以实现不同的功能,如表30.4.5.1所示:

命令

描述

mmc info

输出MMC设备信息

mmc read

读取MMC中的数据。

mmc wirte

向MMC设备写入数据。

mmc rescan

扫描MMC设备。

mmcpart

列出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寄存器的值。

表30.4.5.1mmc命令

1、mmcinfo命令

mmcinfo命令用于输出当前选中的mmcinfo设备的信息,输入命令“mmcinfo”即可,如图30.4.5.2所示:

图30.4.5.2mmcinfo命令

从图30.4.5.2可以看出,当前选中的MMC设备是EMMC,版本为4.5,容量为3.7GiB(EMMC为4GB),速度为52000000Hz=52MHz,8位宽的总线。还有一个与mmcinfo命令相同功能的命令:mmcinfo,“mmc”和“info”之间没有空格。

2、mmcrescan命令

mmcrescan命令用于扫描当前开发板上所有的MMC设备,包括EMMC和SD卡,输入“mmcrescan”即可。

3、mmclist命令

mmclist命令用于来查看当前开发板一共有几个MMC设备,输入“mmclist”,结果如图30.4.5.3所示:

图30.4.5.3扫描MMC设备

可以看出当前开发板有两个MMC设备:FSL_SDHC:0和FSL_SDHC:1 (eMMC),这是因为我现在用的是EMMC版本的核心板,加上SD卡一共有两个MMC设备,FSL_SDHC:0是SD卡,FSL_SDHC:1(eMMC)是EMMC,。默认会将EMMC设置为当前MMC设备,这就是为什么输入“mmcinfo”查询到的是EMMC设备信息,而不是SD卡。要想查看SD卡信息,就要使用命令“mmcdev”来将SD卡设置为当前的MMC设备。

4、mmcdev命令

mmcdev命令用于切换当前MMC设备,命令格式如下:

mmc dev [dev] [part]

[dev]用来设置要切换的MMC设备号,[part]是分区号。如果不写分区号的话默认为分区0。使用如下命令切换到SD卡:

mmc dev 0 //切换到SD卡,0为SD卡,1为eMMC

结果如图30.4.5.4所示:

图30.4.5.4切换到SD卡

从图30.4.5.4可以看出,切换到SD卡成功,mmc0为当前的MMC设备,输入命令“mmcinfo”即可查看SD卡的信息,结果如图30.4.5.5所示:

图30.4.5.5SD信息

从图30.4.5.5可以看出当前SD卡为3.0版本的,容量为14.8GiB(16GB的SD卡),4位宽的总线。

5、mmcpart命令

有时候SD卡或者EMMC会有多个分区,可以使用命令“mmcpart”来查看其分区,比如查看EMMC的分区情况,输入如下命令:

mmcdev1 //切换到EMMC

mmcpart //查看EMMC分区

结果如图30.4.5.6所示:

图30.4.5.6查看EMMC分区

从图30.4.5.6中可以看出,此时EMMC有两个分区,扇区20480~1024000为第一个分区,扇区1228800~6504448为第二个分区。如果EMMC里面烧写了Linux系统的话,EMMC是有3个分区的,第0个分区存放uboot,第1个分区存放Linux镜像文件和设备树,第2个分区存放根文件系统。但是在图30.4.5.6中只有两个分区,那是因为第0个分区没有格式化,所以识别不出来,实际上第0个分区是存在的。一个新的SD卡默认只有一个分区,那就是分区0,所以前面讲解的uboot烧写到SD卡,其实就是将u-boot.bin烧写到了SD卡的分区0里面。后面学习Linux内核移植的时候再讲解怎么在SD卡中创建并格式化第二个分区,并将Linux镜像文件和设备树文件存放到第二个分区中。

如果要将EMMC的分区2设置为当前MMC设置,可以使用如下命令:

mmcdev12

结果如图30.4.5.7所示:

图30.4.5.7设置EMMC分区2为当前设备

6、mmcread命令

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 //读取数据

结果如图30.4.5.8所示:

图30.4.5.8mmcread命令

这里我们还看不出来读取是否正确,通过md.b命令查看0x80800000处的数据就行了,查看16*512=8192(0x2000)个字节的数据,命令如下:

md.b 80800000 2000

结果如图30.4.5.9所示:

图30.4.5.9读取到的数据(部分截图)

从图30.4.5.9可以看到“D$..baudrate=115200.board_name=EVK.board_rev=14X14.”等字样,这个就是uboot中的环境变量。EMMC核心板uboot环境变量的存储起始地址就是1536*512=786432。

7、mmcwrite命令

要将数据写到MMC设备里面,可以使用命令“mmcwrite”,格式如下:

mmc write addr blk# cnt

addr是要写入MMC中的数据在DRAM中的起始地址,blk是要写入MMC的块起始地址(十六进制),cnt是要写入的块大小,一个块为512字节。我们可以使用命令“mmcwrite”来升级uboot,也就是在uboot中更新uboot。这里要用到nfs或者tftp命令,通过nfs或者tftp命令将新的u-boot.bin下载到开发板的DRAM中,然后再使用命令“mmcwrite”将其写入到MMC设备中。我们就来更新一下SD中的uboot,先查看一下SD卡中的uboot版本号,注意编译时间,输入命令:

mmcdev0 //切换到SD卡

version //查看版本号

结果如图30.4.5.10所示:

图30.4.5.10uboot版本查询

可以看出当前SD卡中的uboot是2019年4月15日12:52:04编译的。我们现在重新编译一下uboot,然后将编译出来的u-boot.imx(u-boot.bin前面加了一些头文件)拷贝到Ubuntu中的tftpboot目录下。最后使用tftp命令将其下载到0x80800000地址处,命令如下:

tftp 80800000 u-boot.imx

下载过程如图30.4.5.11所示:

图30.4.5.11u-boot.imx下载过程

可以看出,u-boot.imx大小为 416768字节,416768/512=814,所以我们要向SD卡中写入814个块,如果有小数的话就要加1个块。使用命令“mmcwrite”从SD卡分区0第2个块(扇区)开始烧写,一共烧写814(0x32E)个块,命令如下:

mmc dev 0 0

mmc write 80800000 2 32E

烧写过程如图30.4.5.12所示:

图30.4.5.12烧写过程

烧写成功,重启开发板(从SD卡启动),重启以后再输入version来查看版本号,结果如图30.4.5.13所示:

图30.4.5.13uboot版本号

从图30.4.5.13可以看出,此时的uboot是2019年4月21号18:05:59编译的,这个时间就是我刚刚编译uboot的时间,说明uboot更新成功。这里我们就学会了如何在uboot中更新uboot了,如果要更新EMMC中的uboot也是一样的。

千万不要写SD卡或者EMMC的前两个块(扇区),里面保存着分区表!

千万不要写SD卡或者EMMC的前两个块(扇区),里面保存着分区表!

千万不要写SD卡或者EMMC的前两个块(扇区),里面保存着分区表!

8、mmcerase命令

如果要擦除MMC设备的指定块就是用命令“mmcerase”,命令格式如下:

mmc erase blk# cnt

blk为要擦除的起始块,cnt是要擦除的数量。没事不要用mmcerase来擦除MMC设备!!!

关于MMC设备相关的命令就讲解到这里,表30.4.5.1中还有一些跟MMC设备操作有关的命令,但是很少用到,这里就不讲解了,感兴趣的可以上网查一下,或者在uboot中查看这些命令的使用方法。

30.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的文件系统信息,命令如下:

fatinfo mmc 1:1

结果如图30.4.6.1所示:

图30.4.6.1emmc分区1文件系统信息

从上图可以看出,EMMC分区1的文件系统为FAT16格式的。

2、fatls命令

fatls命令用于查询FAT格式设备的目录和文件信息,命令格式如下:

fatls <interface> [<dev[:part]>] [directory]

interface是要查询的接口,比如mmc,dev是要查询的设备号,part是要查询的分区,directory是要查询的目录。比如查询EMMC分区1中的所有的目录和文件,输入命令:

fatls mmc 1:1

结果如图30.4.6.2所示:

图30.4.6.2 EMMC分区1文件查询

从上图可以看出,emmc的分区1中存放着两个文件:zimage和imx6ull-14x14-evk.dtb,这两个文件分别是linux镜像文件和设备树。并且在emmc的分区1中有两个文件,没有目录

3、fstype命令

fstype用于查看MMC设备某个分区的文件系统格式,命令格式如下:

fstype <interface><dev>:<part>

正点原子EMMC核心板上的EMMC默认有3个分区,我们来查看一下这三个分区的文件系统格式,输入命令:

fstypemmc1:0

fstype mmc 1:1

fstype mmc 1:2

结果如图30.4.6.3所示:

图30.4.6.3fstype命令

从上图可以看出,分区0格式未知,因为分区0存放的uboot,并且分区0没有格式化,所以文件系统格式未知。分区1的格式为fat,分区1用于存放linux镜像和设备树。分区2的格式为ext4,用于存放Linux的跟文件系统。

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地址处,命令如下:

fatload mmc 1:1 80800000 zImage

操作过程如图30.4.6.4所示:

图30.4.6.4读取过程

从上图可以看出在153ms内读取了6071136个字节的数据,速度为37.8MiB/s,速度是非常快的,因为这是从EMMC里面读取的,而EMMC是8位的,速度肯定会很快的。

5、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。拷贝完成以后的tftpboot目录如图30.4.6.5所示:

图30.4.6.5zImage放到tftpboot目录中。

使用命令tftp将zImage下载到DRAM的0X80800000地址处,命令如下:

tftp 80800000 zImage

下载过程如图30.4.6.6所示:

图30.4.6.6zImage下载过程

zImage大小为6039328(0X5C2720)个字节,接下来使用命令fatwrite将其写入到EMMC的分区1中,文件名字为zImage,命令如下:

fatwrite mmc 1:1 80800000 zImage 0x5c2720

结果如图30.4.6.7所示:

图30.4.6.7将zImage烧写到EMMC扇区1中

完成以后使用“fatls”命令查看一下EMMC分区1里面的文件,结果如图30.4.6.8所示:

图30.4.6.8 EMMC分区1里面的文件

30.4.7 EXT格式文件系统操作命令

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

结果如图30.4.7.1所示:

图30.4.7.1ext4ls命令

关于ext格式文件系统其他命令的操作参考30.4.6小节的即可,这里就不讲解了。

30.4.8 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版核心板启动信息如图30.4.8.1所示:

图30.4.8.1 NAND核心板启动信息

从图30.4.8.1可以看出,当前开发板的NAND容量为512MiB。输入“? nand”即可查看所有有关NAND令,如图30.4.8.2所示:

图30.4.8.2 NAND相关操作命令

可以看出,NAND相关的操作命令少,本节我们讲解一些常用的命令。

1、nand info命令

此命令用户打印NAND Flash信息,输入“nandinfo”,结果如图30.4.8.3所示:

图30.4.8.3 nand信息

图30.4.8.3中给出了NAND的页大小、OOB域大小,擦除大小等信息。可以对照着所使用的NAND Flash数据手册来查看一下这些信息是否正确。

2、nanddevice命令

nanddevice用于切换NAND Flash,如果你的板子支持多片NAND的话就可以使用此命令来设置当前所使用的NAND。这个需要你的CPU有两个NAND控制器,并且两个NAND控制器各接一片NAND Flash。就跟I.MX6U有两个SDIO接口,这两个SDIO接口可以接两个MMC设备一样。不过一般情况下CPU只有一个NAND接口,而且在使用中只接一片NAND。

3、nanderase命令

nanderase命令用于擦除NAND Flash,NAND Flash的特性决定了在向NAND Flash写数据之前一定要先对要写入的区域进行擦除。“nanderase”命令有三种形式:

nand erase[.spread] [clean] off size //从指定地址开始(off)开始,擦除指定大小(size)的区域。

nand erase.part [clean] partition //擦除指定的分区

nand erase.chip [clean] //全篇擦除

NAND的擦除命令一般是配合写命令的,后面讲解NAND写命令的时候在演示如何使用“nand erase”。

4、nandwrite命令

此命令用于向NAND指定地址写入指定的数据,一般和“nanderase”命令配置使用来更新NAND中的uboot、linuxkernel或设备树等文件,命令格式如下:

nand write addr off size

addr是要写入的数据首地址,off是NAND中的目的地址,size是要写入的数据大小。

以更新NAND中的uboot为例,讲解一下如何使用此命令。先编译出来NAND版本的u-boot.imx文件,在烧写之前要先对NAND进行分区,也就是规划好uboot、linuxkernel、设备树和根文件系统的存储区域,I.MX6U-ALPHA开发板的NAND分区如下:

0x000000000000-0x000004000000 : "boot"

0x000004000000-0x000006000000 : "kernel"

0x000006000000-0x000007000000 : "dtb"

0x000007000000-0x000020000000 : "rootfs"

一共有四个分区,第一个分区存放uboot,地址范围为0x0~0x4000000(共64MB);第二个分区存放kernel(也就是linuxkernel),地址范围为0x4000000~0x6000000(共32MB);第三个分区存放dtb(设备树),地址范围为0x6000000~0x7000000(共16MB);剩下的所有存储空间全部作为最后一个分区,存放rootfs(根文件系统)。

uboot是从地址0开始存放的,其实用不了这么大的区域,但是为了好管理才分配了这么大的,将NAND版本的u-boot.imx文件放到Ubuntu中的tftpboot目录中,然后使用tftp命令将其下载到开发板的0X87800000地址处,最终使用“nandwrite”将其烧写到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的数据写入的,所以肯定会写入一些无效的数据。你也可以将写入的大小改为u-boot.imx这个文件的大小,这样写入的数据量就是u-boot.imx的实际大小了。

同理我们也可以更新NAND中的linuxkernel和设备树(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的大小,这样连下载都没法下载,更别说更新了。

4、nand read命令

此命令用于从NAND中的指定地址读取指定大小的数据到DRAM中,命令格式如下:

nand read addr off size

addr是目的地址,off是要读取的NAND中的数据源地址,size是要读取的数据大小。比如我们读取设备树(dtb)文件到0x83000000地址处,命令如下:

nand read 0x83000000 0x6000000 0x19000

过程如图30.4.8.4所示:

图30.4.8.4 nandread读取过程

设备树文件读取到DRAM中以后就可以使用fdt命令来对设备树进行操作了,首先设置fdt的地址,fdt地址就是DRAM中设备树的首地址,命令如下:

fdtaddr 83000000

设置好以后可以使用“fdtheader”来查看设备树的头信息,输入命令:

fdtheader

结果如图30.4.8.5所示:

图30.4.8.5 设备树头信息

输入命令“fdtprint”就可以查看设备树文件的内容,输入命令:

fdtprint

结果如图30.4.8.6所示:

图30.4.8.6 设备树文件

图30.4.8.6中的文件就是我们写到NAND中的设备树文件,至于设备树文件的详细内容我们后面会有专门的章节来讲解,这里大家知道这个文件就行了。

NAND常用的操作命令就是擦除、读和写,至于其他的命令大家可以自行研究一下,一定不要尝试全片擦除NAND的指令!!否则NAND就被全部擦除掉了,什么都没有了,又得重头烧整个系统。

30.4.9 BOOT操作命令

uboot的本质工作是引导Linux,所以uboot肯定有相关的boot(引导)命令来启动Linux。常用的跟boot有关的命令有:bootz、bootm和boot。

1、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文件夹里面,完成以后的tftpboot文件夹如图30.4.9.1所示:

图30.4.9.1tftpboot文件夹

下载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

命令运行结果如图30.4.9.2所示:

图30.4.9.2通过网络启动Linux

上图就是我们通过tftp和bootz命令来从网络启动Linux系统,如果我们要从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

命令运行结果如图30.4.9.3所示:

图30.4.6.3从EMMC中启动Linux

2、bootm命令

bootm和bootz功能类似,但是bootm用于启动uImage镜像文件。如果不使用设备树的话启动Linux内核的命令如下:

bootmaddr

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-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

运行结果如图30.4.6.4所示:

图30.4.6.4设置bootcmd从网络启动Linux

前面说过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 imx6ull-alientek_emmc.dtb; bootz 80800000 - 83000000'

savenev

boot

运行结果如图30.4.6.5所示:

图30.4.6.5设置bootcmd从EMMC启动Linux

如果不修改bootcmd的话,每次开机uboot倒计时结束以后都会自动从EMMC里面读取zImage和imx6ull-alientek-emmc.dtb,然后启动Linux。

30.4.10其他常用命令

uboot中还有其他一些常用的命令,比如reset、go、run和mtest等。

1、reset命令

reset命令顾名思义就是复位的,输入“reset”即可复位重启,如图30.4.10.1所示:

图30.4.10.1reset命令运行结果

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这个应用,命令如下:

tftp 87800000 printf.bin

go 87800000

结果如图30.4.10.2所示:

图30.4.10.2go命令运行裸机例程

从图30.4.10.2可以看出,通过go命令我们就可以在uboot中运行裸机例程。

3、run命令

run命令用于运行环境变量中定义的命令,比如可以通过“runbootcmd”来运行bootcmd中的启动命令,但是run命令最大的作用在于运行我们自定义的环境变量。在后面调试Linux系统的时候常常要在网络启动和EMMC/NAND启动之间来回切换,而bootcmd只能保存一种启动方式,如果要换另外一种启动方式的话就得重写bootcmd,会很麻烦。这里我们就可以通过自定义环境变量来实现不同的启动方式,比如定义环境变量mybootemmc表示从emmc启动,定义mybootnet表示从网络启动,定义mybootnand表示从NAND启动。如果要切换启动方式的话只需要运行“runmybootxxx(xxx为emmc、net或nand)”即可。

说干就干,创建环境变量mybootemmc、mybootnet和mybootnand,命令如下:

setenv mybootemmc 'fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-alientek-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来实现不同的启动:

runmybootemmc

run mytoobnand

run mybootnet

4、mtest命令

mtest命令是一个简单的内存读写测试命令,可以用来测试自己开发板上的DDR,命令格式如下:

mtest [start [end [pattern [iterations]]]]

start是要测试的DRAM开始地址,end是结束地址,比如我们测试0X80000000~0X80001000这段内存,输入“mtest8000000080001000”,结果如图30.4.10.3所示:

图30.4.10.3mtest命令运行结果

从图30.4.10.3可以看出,测试范围为0X80000000~0X80001000,已经测试了486次,如果要结束测试就按下键盘上的“Ctrl+C”键。

至此,uboot常用的命令就讲解完了,如果要使用uboot的其他命令,可以查看uboot中的帮助信息,或者上网查询一下相应的资料。

相关问答

UBOOT 移植时,不同型号的NANDFLASH存储的起始地址怎么得到?

你只能通过NandFlash控制器访问NandFlash,即是只要知道Nand控制器的寄存器地址即可。NandFlash不是一个RamLike的器件。Uboot放入nand中,在nand的0地址开...

cpu的spl是什么?

SPLSPL是uboot第一阶段执行的代码.主要负责搬移uboot第二阶段的代码到内存中运行.SPL是由固化在芯片内部的ROM引导的.我们知道很多芯片厂商固化的ROM支持...

 香港金像  厨艺比赛 
王经理: 180-0000-0000(微信同号)
10086@qq.com
北京海淀区西三旗街道国际大厦08A座
©2024  上海羊羽卓进出口贸易有限公司  版权所有.All Rights Reserved.  |  程序由Z-BlogPHP强力驱动
网站首页
电话咨询
微信号

QQ

在线咨询真诚为您提供专业解答服务

热线

188-0000-0000
专属服务热线

微信

二维码扫一扫微信交流
顶部