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从源码到运行的完整流程:
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启动到一半就挂了的案例。
总结一下本章核心:
- 从官方Git仓库获取U-Boot源码
- 用
make *_defconfig生成基础配置 - 用
make menuconfig按需微调 - 设置交叉编译器后
make编译 - 根据启动介质选择烧录方式(SD卡/NAND/eMMC/SPI Flash)
- 上电验证,检查串口输出