4. 硬件平台选型

好,咱们进入第四章节。Bootloader,说白了就是板子上电后第一个跑起来的程序。它负责初始化硬件、加载操作系统内核。在嵌入式Linux世界里,U-Boot几乎是事实标准。我这些年经手的项目,从ARM9到Cortex-A72,清一色用的都是它。

这一章,咱们就手把手把U-Boot从源码变成能在目标板上运行的二进制文件。别急,一步步来。

4.1 获取U-Boot源码

U-Boot的源码托管在多个地方,我个人习惯从官方Git仓库拉取。这样能保证是最新、最干净的版本。

官方仓库地址:

  • 主仓库:https://source.denx.de/u-boot/u-boot.git
  • GitHub镜像:https://github.com/u-boot/u-boot

克隆命令很简单:

git clone https://source.denx.de/u-boot/u-boot.git
cd u-boot

这里有个小建议。你不需要每次都克隆完整的历史。如果只是做开发,加个 --depth=1 参数能省不少时间:

git clone --depth=1 https://source.denx.de/u-boot/u-boot.git

嗯,这里要注意。不同版本的U-Boot对编译器版本有要求。太新的编译器编译旧版U-Boot可能会报错。我一般会先看看 Makefile 里写的 VERSION 和 PATCHLEVEL,心里有个数。

4.2 配置U-Boot

源码拿到手,下一步就是配置。U-Boot支持几百种开发板,每种板子都有自己的配置文件。配置文件的命名规则通常是 <板子名称>_defconfig。

先看看你的板子支持哪些配置:

ls configs/ | grep <你的板子名>

比如,我用的是QEMU模拟的ARM虚拟板(vexpress-a9),配置命令就是:

make vexpress_ca9x4_defconfig

执行完这条命令,U-Boot会根据 configs/vexpress_ca9x4_defconfig 文件生成 .config。这个 .config 就是整个编译过程的“宪法”,所有模块的开关都在里面。

我的经验: 如果你需要微调配置,别直接改 .config。用 make menuconfig 打开图形界面,在里面改。这样能保证依赖关系不乱。我曾经手改 .config 导致某个驱动没选上,调试了整整两天。

执行 make menuconfig 后,你会看到一个类似Linux内核配置的界面。常用的配置项包括:

  • Boot media:从SD卡、NAND Flash还是eMMC启动
  • Network support:是否支持TFTP/NFS网络启动
  • Device Tree:是否使用设备树(现在基本都用了)
  • Environment:环境变量保存在哪里(SPI Flash、MMC分区还是FAT文件系统)

4.3 编译U-Boot

配置搞定,接下来就是编译。编译前先确认交叉编译器已经装好,并且加到了 PATH 里。

设置交叉编译环境变量:

export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm

然后直接 make:

make -j4

-j4 表示用4个线程并行编译。如果你的CPU核心多,可以设大一点,比如 -j8。

编译完成后,你会看到几个关键文件:

文件 说明
u-boot.bin 纯二进制镜像,可直接烧录到存储介质
u-boot ELF格式文件,可用于调试(GDB)
u-boot.img 带有U-Boot头信息的镜像,通常用于网络启动
u-boot.srec Motorola S-record格式,用于串口下载

避坑指南: 我曾经在编译时遇到 undefined reference to `raise' 错误。原因是交叉编译器版本太新,而U-Boot版本太老。解决办法是换一个匹配的编译器版本,或者给 Makefile 加 -Wno-error 临时绕过。但后者不推荐,治标不治本。

4.4 烧录到目标板

编译出 u-boot.bin 后,最后一步就是把它烧到板子上。烧录方式取决于你的硬件平台和启动介质。

下面这张图展示了U-Boot从源码到运行的完整流程:

U-Boot构建与烧录流程图 U-Boot 构建与烧录流程 获取源码 配置 (make *_defconfig) menuconfig 微调(可选) 编译 (make -j4) 烧录到目标板 烧录方式:SD卡 dd命令 / USB OTG / JTAG / 串口Xmodem 烧录前务必确认启动介质地址和分区表正确

4.4.1 烧录到SD卡

这是最常用的方式。把SD卡插入电脑,找到设备节点(比如 /dev/sdb),然后:

sudo dd if=u-boot.bin of=/dev/sdb bs=1024 seek=1
sync

seek=1 表示跳过第一个1KB块。为什么?因为SD卡的第0块通常是MBR或分区表,U-Boot一般从第1块开始放。具体偏移量要看你的SoC手册。

我的习惯: 烧录前先用 sudo fdisk -l /dev/sdb 确认设备名。千万别搞错,否则会把电脑硬盘给写了。别问我怎么知道的……

4.4.2 烧录到NAND Flash / eMMC

如果板子已经有一个能用的Bootloader(比如厂家预装的),你可以通过网络或串口把新U-Boot传进去。

在U-Boot命令行下:

# 通过TFTP下载
tftp 0x60000000 u-boot.bin

# 擦除NAND分区
nand erase 0x0 0x80000

# 写入NAND
nand write 0x60000000 0x0 0x80000

对于eMMC,命令类似:

mmc dev 0
mmc write 0x60000000 0x0 0x800

4.4.3 烧录到SPI Flash

有些板子用SPI Flash存Bootloader。烧录命令:

sf probe 0
sf erase 0x0 0x80000
sf write 0x60000000 0x0 0x80000

4.5 验证烧录结果

烧完后,上电。串口终端应该能看到U-Boot的启动信息。如果打印出 U-Boot 2024.01 之类的版本号,并且能进入 => 提示符,恭喜你,成了。

如果没反应,别慌。检查这几样:

  • 启动介质选择:板子上的拨码开关或电阻配置对不对?
  • 烧录地址:U-Boot是否烧到了正确的偏移位置?
  • 串口参数:波特率、数据位、停止位是否匹配?通常是115200 8N1。
  • 电源:供电是否稳定?我遇到过因为电源纹波大导致U-Boot启动到一半就挂了的案例。

总结一下本章核心:

  1. 从官方Git仓库获取U-Boot源码
  2. 用 make *_defconfig 生成基础配置
  3. 用 make menuconfig 按需微调
  4. 设置交叉编译器后 make 编译
  5. 根据启动介质选择烧录方式(SD卡/NAND/eMMC/SPI Flash)
  6. 上电验证,检查串口输出