1)实验平台:正点原子STM32MP157开发板
2)购买链接:https://item.taobao.com/item.htm?&id=629270721801
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子STM32MP157技术交流群:691905614
第十章 U-boot使用
在移植U-Boot之前,我们肯定要先使用一下U-Boot,得先体验一下U-Boot是个什么东西。STM32MP157开发板光盘资料里面已经提供了一个正点原子团队已经移植好的U-Boot,本章我们就直接编译这个移植好的U-Boot,然后烧写到EMMC里面启动,启动U-Boot以后就可以学习使用U-Boot的命令。
10.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 官网为http://www.denx.de/wiki/U-Boot/,如图10.1.1所示:
图10.1.1 uboot官网
我们可以在 uboot 官网下载 uboot 源码,点击图10.1.1中左侧 Topics 中的“Source Code”,打开以后如图10.1.2所示:
图10.1.2 uboot源码界面
点击图10.1.2中的“FTP”,进入其 FTP 服务器即可看到 uboot 源码,如图10.1.3所示:
图10.1.3 uboot源码
图10.1.3中就是 uboot 原汁原味的源码文件,目前最新的版本是 2020.10。但是我们一般不会直接用 uboot 官方的 U-Boot 源码的。uboot 官方的 uboot 源码是给半导体厂商准备的,半导体厂商会下载 uboot 官方的 uboot 源码,然后将自家相应的芯片移植进去。也就是说半导体厂商会自己维护一个版本的 uboot,这个版本的 uboot 相当于是他们定制的。既然是定制的,那么肯定对自家的芯片支持会很全,虽然 uboot 官网的源码中一般也会支持他们的芯片,但是绝对是没有半导体厂商自己维护的 uboot 全面。ST提供了2020.01版本的uboot,在6.1.1小节获取ST官方系统源码中我们已经得到了ST官方uboot源码, 进入到如下目录:
/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi
uboot源码如图10.1.4所示:
图10.1.4 ST官方uboot源码
图10.1.4中的“u-boot-stm32mp-2020.01-r0”就是ST官方uboot源码包,它支持了STM32MP1家族全系列芯片(后续ST也会一直更新,添加新的SOC进去),而且支持各种启动方式,比如EMMC、NAND 等等,这些都是 uboot 官方所不支持的。但是图10.1.4中的 uboot 是针对ST 自家评估板的,如果要在我们自己的板子上跑,那么就需要对其进行修改,使其支持我们自己做的板子,正点原子的 STM32MP157开发板就是自己做的板子,虽然大部分都参考了 ST 官方的STM32MP157 EVK开发板,但是还是有很多不同的地方,所以需要修改 ST 官方的 uboot,使其适配正点原子的 STM32MP157开发板。所以当我们拿到开发板以后,是有三种uboot 的,这三种 uboot的区别如表10.1.1所示:
种类 描述
uboot官方的uboot代码 由uboot官方维护开发的uboot版本,版本更新快,基本包含所有常用的芯片。
半导体厂商的uboot代码 半导体厂商维护的一个uboot,专门针对自家的芯片,在对自家芯片支持上要比uboot官方的好。
开发板厂商的uboot代码 开发板厂商在半导体厂商提供的uboot基础上加入了对自家开发板的支持。
表10.1.1 三种uboot的区别
那么这三种uboot该如何选择呢?首先uboot官方的基本是不会用的,因为支持太弱了。最常用的就是半导体厂商或者开发板厂商的 uboot,如果你用的半导体厂商的评估板,那么就使用半导体厂商的 uboot,如果你是购买的第三方开发板,比如正点原子的 STM32MP157开发板,那么就使用正点原子提供的 uboot 源码(也是在半导体厂商的 uboot 上修改的)。当然了,你也可以在购买了第三方开发板以后使用半导体厂商提供的 uboot,只不过有些外设驱动可能不支持,需要自己移植,这个就是我们常说的 uboot 移植。本节是 uboot 的使用,所以就直接使用正点原子已经移植好的 uboot,这个已经放到了开发板光盘中了,路径为:开发板光盘1、程序源码1、正点原子Linux出厂系统源码u-boot-stm32mp-2020.01-gdb8d2374-v1.0.tar.bz2。
10.2 U-Boot初次编译
10.2.1 编译
首先需要在 Ubuntu 中安装一些库,否则编译uboot会报错,安装命令如下:
sudo apt-get install libncurses5-dev bison flex
在 Ubuntu 中创建存放 uboot 的目录,比如我这里新建了一个名为“alientek_uboot”的文件夹用于存放正点原子提供的 uboot 源码。alientek_uboot文件夹创建成功以后使用 FileZilla 软件将正点原子提供的 uboot 源码拷贝到此目录中,正点原子提供的 uboot 源码已经放到了开发板光盘中,路径为:开发板光盘1、程序源码1、正点原子Linux出厂系统源码u-boot-stm32mp-2020.01-xxxxxxxx-v1.0.tar.bz2(“xxxxxxxxx”为uboot打包时候的版本号,每次打包其版本号都不同!所以大家不要纠结于开发板光盘中的uboot源码打包版本号是否和教程里面的一致)。将stm32mp-2020.01-xxxxxxxx-v1.0.tar.bz2拷贝到前面新建的 alientek_uboot 文件夹下,完成以后如图10.2.1.1所示:
图10.2.1.1 正点原子出厂uboot源码
使用如下命令对其进行解压缩:
tar -vxf u-boot-stm32mp-2020.01-gdb8d2374-v1.0.tar.bz2
解压完成以后如图10.2.1.2所示:
图10.2.1.2 解压后的Uboot
图10.2.1.2中除了u-boot-stm32mp-2020.01-gdb8d2374-v1.0.tar.bz2这个正点原子提供uboot源码压缩包以外,其他的文件和文件夹都是解压出来的 uboot 源码。执行以下命令,编译正点原子提供的uboot。
make distclean
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- stm32mp157d_atk_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- DEVICE_TREE=stm32mp157d-atk all
上面命令每次编译的时候都要指定ARCH、CROSS_COMPILE和DEVICE_TREE,这三个含义如下:
ARCH:指定所使用的平台架构,这里肯定是arm。
CROSS_COMPILE:所使用的交叉编译器前缀,本教程使用的是交叉编译器前缀为arm-none-linux-gnueabihf-。
DEVICE_TREE:设备树文件,uboot也支持设备树,所以在编译的时候需要指定设备树文件,不同的硬件其设备树文件肯定不同,这里为stm32mp157d_atk,也就是正点原子的STM32MP157开发板对应的设备树。
编译的时候每次都输入ARCH和CROSS_COMPILE比较麻烦,为了方便起见,我们可以直接修改uboot的Makefile文件,在里面直接对ARCH和CROSS_COMPILE进行赋值,也就是直接将ARCH设置为arm,CROSS_COMPILE设置为arm-none-linux-gnueabihf-,修改完成以后如图10.2.1.3所示:
图10.2.1.3 设置ARCH和CROSS_COMPILE值
注意!不能在Makefile里面对DEVICE_TREE进行复制,因为没用,必须在编译的时候手动输入!
设置好Makefile里面的ARCH和CROSS_COMPILE以后就可以将编译命令简化为如下所示:
make distclean //清除
make stm32mp157d_atk_defconfig //配置uboot
make V=1 DEVICE_TREE=stm32mp157d-atk all //编译
上述命令和前面的相比就要简洁很多,最后的“make V=1”是真正的编译命令,V=1表示编译uboot的时候输出详细的编译过程,方便我们观察uboot编译过程。直接输入“make”命令的话默认使用单线程编译,编译速度会比较慢,可以通过添加“-j”选项来使用多线程编译,比如使用8线程编译,最后的编译命令就是:
make V=1 DEVICE_TREE=stm32mp157d-atk all -j8 //8线程编译
uboot编译完成如图10.2.1.4所示:
图10.2.1.4 uboot编译成功
编译完成以后的 就会在uboot源码目录下生成相应的镜像文件,如图10.2.1.5所示:
图10.2.1.5 编译后生成的uboot可执行文件
可以看出,编译完成以后 uboot 源码多了一些文件,重点是u-boot.bin和u-boot.stm32这两个文件。u-boot.bin是uboot的二进制可执行文件,u-boot.stm32是在u-boot.bin前面添加了256个字节头部信息。STM32MP1内部ROM代码和TF-A在运行uboot的时候要求前面添加头部信息,所以这就是为什么uboot也有这个头部信息的原因。
10.2.2 烧写
使用STM32CubeProgrammer将上面编译出来的u-boot.stm32镜像烧写到开发板的EMMC里面,修改前面创建的tf-a.tsv文件,添加uboot烧写指令(其实在9.3.2小节已经讲过了),在最后面添加下面这行:
示例代码10.2.2.1 uboot烧写指令
P 0x06 ssbl Binary mmc1 0x00080000 u-boot.stm32
修改以后的tf-a.tsv如图10.2.2.1所示:
图10.2.2.1 修改后的tf-a.tsv
最后将上一小节编译出来的u-boot.stm32,拷贝到前面创建的images目录下(在做TF-A实验的就有u-boot.stm32这个文件,我们只要替换就行)。
一切准备就绪以后就可以使用STM32CubeProgrammer软件通过USB OTG将uboot烧写到开发板上的EMMC里面,等到烧写完成。完成以后设置开发板上的拨码开关,设置从EMMC启动,然后用USB Type-C线将开发板上的USB_TTL接口与电脑连接起来,因为我们要在串口终端里面输入命令来操作uboot。
打开MobaXterm,设置好串口参数,最后复位开发板。在 MobaXterm 上出现“Hit any key tostop autoboot: ”倒计时的时候按下键盘上的回车键,默认是 1 秒倒计时,在 1 秒倒计时结束以后如果没有按下回车键的话 uboot 就会使用默认参数来启动 Linux 内核了(如果内核存在的话,如果Linux内核不存在那么就会进入到uboot的命令行模式)。如果在 1 秒倒计时结束之前按下回车键,那么就会进入 uboot 的命令行模式,如图10.2.2.2所示:
图10.2.2.2 uboot启动log信息
从图10.2.2.2可以看出,当进入到 uboot 的命令行模式以后,左侧会出现一个“STM32MP=>”标志。uboot 启动的时候会输出一些信息,这些信息如下所示:
示例代码10.2.2.2 uboot启动log信息
1 U-Boot 2020.01-stm32mp-r1 (Nov 24 2020 - 17:17:20 +0800)
2
3 CPU: STM32MP157DAA Rev.Z
4 Model: STMicroelectronics STM32MP157D eval daughter
5 Board: stm32mp1 in trusted mode (st,stm32mp157d-atk)
6 DRAM: 1 GiB
7 Clocks:
8 - MPU : 800 MHz
9 - MCU : 208.878 MHz
10 - AXI : 266.500 MHz
11 - PER : 24 MHz
12 - DDR : 533 MHz
13 WDT: Started with servicing (32s timeout)
14 NAND: 0 MiB
15 MMC: STM32 SD/MMC: 0, STM32 SD/MMC: 1
16 Loading Environment from MMC... OK
17 In: serial
18 Out: serial
19 Err: serial
20 invalid MAC address in OTP 00:00:00:00:00:00
21 Net:
22 Error: ethernet@5800a000 address not set.
23 No ethernet found.
24
25 Hit any key to stop autoboot: 0
26 STM32MP>
简单讲解一下uboot启动过程的log信息:
第 1 行是 uboot 版本号和编译时间,可以看出,当前的 uboot 版本号是 2020.01,编译时间是 2020 年 11月 24 日 17: 17。
第3行是CPU的信息,可以看出CPU型号为STM32MP157DAA。
第4行是板子信息,当前板子是ST公司的STM32MP157D eval开发板,这个信息是可以改的,因为正点原子是直接参考ST公司的EVK开发板移植的uboot,所以这部分信息也就没改。
第5行是板子的一些信息,比如工作在trusted模式下。
第6行是DDR的大小为1GB。
第7~12行它们的频率分别为,MPU频率、MCU频率、AXI总线频率、PER的频率、DDR频率。
第13行是看门狗信息,喂狗时间为32s。
第14行是NAND的大小,因为正点原子的STM32MP157开发板没有NAND,所以这里就是0MB。
第15行是板子上MMC设备,一共有两个,SD/MMC0 (SD卡)和SD/MMC1 (EMMC)。
第16行是从MMC里获取环境变量。
第17~19行是标准输入、标准输出和标准错误所使用的终端,这里都使用串口(serial)作为终端。
第20~23行是网络相关信息,网络的MAC地址从OTP里获取,因为我们的OTP没有设置MAC地址,所以就获取失败。这里的网络是可以用的,只是因为没有MAC地址所以提示没有找到网络,可以自行添加相关环境变量来设置MAC地址,后面会讲如何设置。
第 25 行是倒计时提示,默认倒计时 1 秒,倒计时结束之前按下回车键就会进入 Linux 命令行模式。如果在倒计时结束以后没有按下回车键,那么 Linux 内核就会启动,Linux 内核一旦启动,uboot 就会寿终正寝。
uboot的主要作用是引导kernel,我们现在已经进入 uboot 的命令行模式了,进入命令行模式以后就可以给 uboot 发号施令了。当然了,不能随便发号施令,得看看 uboot 支持哪些命令,然后使用这些uboot 所支持的命令来做一些工作,下一节就讲解 uboot 命令的使用。
10.3 U-Boot命令使用
进入uboot的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前uboot所支持的命令,如图10.3.1所示:
图10.3.1 uboot的命令列表(部分)
图10.3.1中只是 uboot 的一部分命令,具体的命令列表以实际为准。图10.3.1中的命令并不是 uboot 所支持的所有命令,说过 uboot 是可配置的,需要什么命令就使能什么命令。所以图10.3.1中的命令是正点原子提供的 uboot 中使能的命令,uboot 支持的命令还有很多,而且也可以在 uboot 中自定义命令。这些命令后面都跟有命令说明,用于描述此命令的作用,但是命令具体怎么用呢?我们输入“help(或?) 命令名”既可以查看命令的详细用法,以“bootz”这个命令为例,我们输入如下命令即可查看“bootz”这个命令的用法:
? bootz 或 help bootz
结果如图10.3.2所示:
图10.3.2 bootz 命令使用说明
图10.3.2列出了“bootz”这个命令的详细说明,其它的命令也可以使用此方法查询具体的使用方法。接下来我们学习一下一些常用的 uboot 命令。
10.3.1 查询命令
常用的和信息查询有关的命令有 3 个:bdinfo、printenv 和 version。先来看一下 bdinfo 命令,此命令用于查看板子信息,直接输入“bdinfo”即可,结果如图10.3.1.1示:
图10.3.1.1 bdinfo 命令
从图10.3.1.1中可以看出 DRAM 的起始地址和大小、BOOT参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息。命令“printenv”用于输出环境变量信息,uboot 也支持 TAB 键自动补全功能,输入“print”然后按下 TAB 键就会自动补全命令。直接输入“print”也可以,因为整个uboot命令中只有printenv的前缀是“print”,所以当输入print以后就只有printenv命令了。输入“print”,然后按下回车键,环境变量如图10.3.1.2所示:
图10.3.1.2 printenv 命令部分结果
图10.3.1.2只是printenv命令的部分内容,STM32MP1系列的环境变量有很多,比如 baudrate、board、board_name、boot_device、bootcmd、bootdelay等等。uboot 中的环境变量都是字符串,既然叫做环境变量,那么它的作用就和“变量”一样。比如 bootdelay 这个环境变量就表示 uboot 启动延时时间,默认 bootdelay=1,也就默认延时 1秒。前面说的 1 秒倒计时就是由 bootdelay 定义的,如果将 bootdelay 改为 5 的话就会倒计时 5s了。uboot 中的环境变量是可以修改的,有专门的命令来修改环境变量的值,稍后我们会讲解。
命令 version 用于查看 uboot 的版本号,输入“version”,uboot 版本号如图10.3.1.3所示:
图10.3.1.3 version命令结果