1. U-Boot 移植:技术概述
U-Boot(Universal Boot Loader)是嵌入式 Linux 系统中负责引导内核的启动加载程序。其核心任务包括:初始化关键硬件(如时钟、DDR、总线)、加载操作系统内核与设备树映像至内存、并建立引导参数传递机制。
1.1 为什么要进行移植?
U-Boot 虽然提供了通用的初始化框架,但不同开发板的硬件参数存在差异。当开发板的 DDR 时序、存储器类型(EMMC/NAND)、外设驱动接口(如网络 PHY、LCD 屏幕参数)与 U-Boot 官方提供的参考板(Reference Board)不一致时,必须进行移植。移植的本质是通过修改板级配置文件(Defconfig)、头文件(Header files)和驱动源码,使 U-Boot 正确初始化目标硬件环境。
1.2移植的主要内容
- 构建系统适配:在
configs/和board/目录下添加自定义板级配置,通过修改Makefile和Kconfig建立独立的编译入口。 - 硬件驱动适配:
- 外设驱动:根据硬件原理图修改网络 PHY 寄存器地址及复位 GPIO 时序;调整 LCD 时序参数以适配物理面板。
- 设备树配置:在
.dts文件中更新引脚复用(IOMUX)和外设节点属性。
- 参数传递机制配置:
通过环境变量(bootcmd,bootargs)设定内核加载路径与启动参数,确保 U-Boot 在倒计时结束后,能够将 Linux 内核与设备树搬运至 DDR 对应地址,并将设备树物理首地址写入 CPU 的r2寄存器,完成控制权移交。
2. 移植流程
2.1 了解 U-Boot 的三种来源
| 源码种类 | 维护主体 | 技术侧重 | 定位 |
|---|---|---|---|
| 社区通用版 (Mainline) | U-Boot 社区 | 追求架构的标准化、通用性与前沿特性支持。 | 嵌入式开发的基准线,用于技术预研与架构追踪。 |
| 原厂内核版 (Vendor SDK) | SoC 厂商 (NXP, ST, TI等) | 针对特定 SoC 系列优化,包含完善的内存初始化(DDR)驱动、时钟拓扑及外设控制器驱动。 | 硬件产品的开发基准,确保 CPU 核心特性的稳定性。 |
| 板级工程版 (Board Support) | 开发板/硬件方案商 | 基于原厂代码,进行具体的引脚复用(IOMUX)、存储介质适配及外设时序微调。 | 交付终端用户的实际可用代码,直接匹配物理电路板。 |
社区通用版(Mainline):技术演进的观测窗
核心价值:它是 U-Boot 最新的设计理念来源。开发者研究 Mainline 可以获知最新的驱动模型(DM)、设备树绑定规范(DT Binding)以及安全引导(Secure Boot)技术演进。
局限性:由于适配范围太广它往往不包含特定硬件平台的最新补丁或特定 SoC 的专有 IP 驱动,直接移植难度大。原厂内核版(Vendor SDK):硬件适配的稳定锚点
核心价值:半导体厂商在开发 SoC 的同时,会维护一份与其配套的 U-Boot 源码。它不仅包含核心的芯片驱动,还通常附带了原厂参考板(EVK/EVB)的完整配置。
移植策略:在移植工作中,原厂版本是最稳妥的基准线。绝大多数的内存控制逻辑、SoC 总线驱动均从此版本继承。开发板工程版(Board Support):最终产品的适配层
核心价值:它是对原厂版本进行“裁剪”和“修正”的产物。在工程实践中,开发板厂商的主要工作是物理映射:
引脚映射:确保板卡上的 GPIO 连线与寄存器映射一致。
电气参数校准:调整特定 LCD 屏的时序参数、调整 PHY 芯片的复位时序。
裁剪冗余:移除工程板不需要的调试功能,减小固件体积,缩短冷启动时间。
2.2 U-Boot 源码的选择
在实际工程开发中,这三种 U-Boot 源码的选择依据主要取决于硬件平台与参考设计(Reference Design)的匹配度:
- 社区通用版 (Mainline):在商业项目与定制硬件开发中基本不作为首选。由于其缺乏特定 SoC 厂商的专有补丁与底层高频调校,对具体芯片的外设支持较弱。
- 原厂内核版 (Vendor SDK) 与 板级工程版 (Board Support):
这是工业界最常用的两种源码来源。
- 使用半导体厂商评估板(如 NXP 官方 EVK 板):直接使用半导体厂商维护的 U-Boot 源码。
- 使用第三方标准开发板(如韦东山的 I.MX6ULL 开发板):首选使用开发板厂商提供的 U-Boot 源码。该源码已由厂商基于原厂 U-Boot 针对其特定电路板完成了引脚复用和外设驱动的闭环适配。
- 自主设计板卡(企业产品研发):
通常以半导体厂商提供的 U-Boot 为基础基准线。若硬件电路参考了第三方开发板的设计(如延续了韦东山开发板的电源与网络拓扑),则会结合两者的源码进行裁剪。
在第三方开发板上直接换用原厂 U-Boot,由于部分外设(如网络 PHY 芯片型号、LCD 屏时序、电源管理 PMIC)存在硬件电路差异,部分驱动将无法直接运行,必须进行代码级修改以完成适配——这一过程即标准的U-Boot 移植。
3. 韦东山 IMX6ULL 开发板的 U-Boot 移植
本文采用 NXP 半导体场上的 U-Boot 进行移植,原因如下:
| 维度 | 使用厂商(正点原子/韦东山)U-Boot | 使用 NXP 原厂 U-Boot 移植(你的选择) | 带来的核心学习好处 |
|---|---|---|---|
| 驱动开发深度 | 核心驱动(如 LAN8720A 网络、时钟)已由厂商重写完毕,开发者只需修改 GPIO 引脚编号。 | 必须亲手剔除原厂公版的复杂外设逻辑(如 74LV595 扩展芯片),并根据芯片手册手写物理电平的复位与延时时序。 | 真正掌握物理层驱动编写与硬件总线时序控制。 |
| 构建系统认知 | 直接在已有单板目录下修补代码,较少触及底层的配置与编译规则。 | 必须完整执行大厂标准的“立门户”流程:新建单板目录、配置 Kconfig 菜单、编写 Makefile、定制 defconfig。 | 彻底吃透嵌入式 Linux 系统的条件编译与自动打包机理。 |
| 调试(Debug)锻炼 | 硬件适配度高,编译烧写后基本直接点亮,缺乏排错机会。 | 原厂代码与目标板硬件存在天然冲突,必然高频遭遇网口不通、开机卡死、屏幕花屏等故障。 | 逼迫自己学会对比原理图、分析时钟信号流向、读写底层寄存器,建立解决死机 Bug 的高级排错能力。 |
3.1 NXP 官方 U-Boot 编译
3.1.1 版本确定
由于开发板的资料是 Uboot-2017.03,所以我们去 GitHub 下载对应的版本:NXP官方 uboot 源码下载
3.1.2 编译链确定
从韦东山开发板的参考手册可以得知编译链为:
arm-buildroot-linux-gnueabihf-
3.1.3 默认配置文件选择
进入 configs/ 目录搜索 mx6ull*,列出相关的默认配置文件
- 第一步:核对芯片型号与封装(14x14 还是 9x9?)
14x14 与 9x9 的含义:
这代表 i.MX6ULL 芯片的物理封装尺寸(单位是毫米,即 14mm×14mm 或 9mm×9mm)。不同封装的芯片,其引脚(Pin)数量和球位引脚分布完全不同。
如何选择:
打开韦东山开发板的核心板原理图,查看 CPU 型号(或者直接拿放大镜看你板子中间那颗 NXP 芯片上的丝印)。韦东山的 i.MX6ULL 开发板采用的是最常用的 MAPBGA 289 针脚封装,其物理尺寸正是 14x14 毫米。
结论:
直接过滤掉所有带 9x9 的文件,锁定 mx6ull_14x14_…。
- 第二步:核对板型(evk 还是 arm2?)
evk 的含义:
Evaluation Kit(官方评估板)。NXP 官方公版开发板的名字就叫 i.MX6ULL EVK。
arm2 的含义:
NXP 内部用于特定验证或早期硅片测试的板卡(通常外设极少,不适合用来做应用开发)。
结论:
锁定 evk。
- 第三步:判定启动与存储介质(驱动适配的关键)
这是最关键的一步,U-Boot 需要知道它自己将被烧写到哪里,以及从哪里加载 Linux 内核。
mx6ull_14x14_evk_nand_defconfig:
适用于板载存储器为 NAND Flash 颗粒的开发板。
mx6ull_14x14_evk_emmc_defconfig:
适用于板载存储器为 eMMC 芯片的开发板。
目前市面上百问网(韦东山)主推的 i.MX6ULL 开发板,绝大多数都是 eMMC 版本(板载一颗 8GB 或 4GB 的 eMMC 芯片)。
因此选择mx6ull_14x14_evk_emmc_defconfig
3.1.4 编译过程
#1.彻底清理工程(清除之前由于用错编译器留下的错误缓存,这步非常关键) make ARCH=arm CROSS_COMPILE=arm-buildroot-linux-gnueabihf-distclean #2.配置基线:载入 NXP 官方 I.MX6ULL EMMC 评估板的默认配置文件 make ARCH=arm CROSS_COMPILE=arm-buildroot-linux-gnueabihf-mx6ull_14x14_evk_emmc_defconfig #3.全速编译:调用虚拟机12个线程全速编译 U-Boot 源码 make ARCH=arm CROSS_COMPILE=arm-buildroot-linux-gnueabihf--j12
核心镜像文件(用于烧写启动)
这是编译出来的终极目标,也是唯一需要传到板子上运行的文件:
u-boot-dtb.imx (核心中的核心)
作用:
直接用于烧写到 SD 卡或 eMMC 的最终镜像。
本质:
它是把 u-boot.bin(编译出的可执行文件)和 u-boot.dtb(编译出的设备树文件)合并后,在头部加上了 i.MX6ULL 芯片专属的启动头信息(IVT、DCD 等,用于初始化内存参数)。
3.1.5 烧录 imx 文件至 SD卡
| 维度 | 通用 dd 命令 | imxdownload 工具 |
|---|---|---|
| 安全性 | 高风险。不带设备检查,一旦把盘符 sdb 错打成系统盘 sda,会直接擦除 Ubuntu 系统致其瘫痪。 | 高安全(防呆)。内部自带防误杀机制,会自动校验目标盘符属性,拒绝向系统盘写入数据。 |
| 烧写参数 | 繁琐易错。每次必须手动输入 bs=1k seek=1 来指定跳过前 1KB 字节,漏掉或写错参数板子就无法启动。 | 一键傻瓜化。将“跳过 1KB 绝对扇区”的硬件规则直接写死在源码中,开发者无需记忆复杂的偏移参数。 |
| 文件兼容性 | 单一。只能原封不动地平铺数据,无法处理没有芯片启动头(DCD/IVT)的纯裸机 .bin 文件。 | 智能双模。如果是纯 led.bin,会自动帮其补齐 3KB 的芯片启动头再烧写;如果是已带头的 u-boot.imx,则直接烧写。 |
先手写并编译一个 imxdownload
1.新建 C 文件
cd~/Desktop/uboot_plant/uboot-imx-nxp-imx_v2017.03_4.9.11_1.0.0_ga gedit imxdownload.c2.粘贴通用源码
把下面这段标准的 imxdownload 开源核心 C 语言代码完整复制,粘贴到刚才弹出的 gedit 窗口中,保存并关闭:
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<fcntl.h>#include<sys/stat.h>#include<sys/types.h>intmain(intargc,char*argv[]){FILE*fp_src;intfd_dst;unsignedcharbuf[512];intnread;if(argc!=3){printf("============================================================\n");printf("I.MX6U Embedded Processor Download Tool (Lite)\n");printf("Usage: ./imxdownload <source_bin> <sd_device>\n");printf("Example: ./imxdownload u-boot-dtb.imx /dev/sdb\n");printf("============================================================\n");return-1;}// 打开源镜像文件fp_src=fopen(argv[1],"rb");if(fp_src==NULL){printf("Error: Cannot open source file %s\n",argv[1]);return-1;}// 打开 SD 卡物理设备fd_dst=open(argv[2],O_WRONLY);if(fd_dst<0){printf("Error: Cannot open device %s. Please check sudo permission.\n",argv[2]);fclose(fp_src);return-1;}// 核心逻辑:绝对寻址,定位到物理 SD 卡 1KB 偏移处(第2个扇区)if(lseek(fd_dst,1024,SEEK_SET)<0){printf("Error: lseek failed\n");close(fd_dst);fclose(fp_src);return-1;}printf("Flashing Bootloader %s to %s ...\n",argv[1],argv[2]);// 开始物理扇区平铺写入while((nread=fread(buf,1,sizeof(buf),fp_src))>0){write(fd_dst,buf,nread);}fsync(fd_dst);close(fd_dst);fclose(fp_src);printf("Done! Flash successfully.\n");return0;}3.现场编译成工具
在终端使用 Ubuntu 本地编译器编译它,并赋予执行权限:
gcc imxdownload.c-oimxdownloadchmod+x imxdownload4.使用新生成的工具烧写
现在可以直接使用 imxdownload 进行烧写了:
sudo./imxdownload u-boot-dtb.imx /dev/****** 是 实际 SD 卡盘符
输入 lsblk 查看,我的 TF卡 是16G的,实际容量和sdb的14.6G相符,所以 *** 换成 sdb 进行烧写
3.1.6 启动开发板验证
切换到 SD/TF卡 启动并连接串口,启动开发板查看信息:
📺 LCD 屏幕问题汇总
- 现状体检
U-Boot 识别参数:
480 × 272 480 \times 272480×272分辨率(TFT43AB 4.3寸低清屏)。
实际物理硬件:
通常为1024 × 600 1024 \times 6001024×600(7寸屏)。
物理现象:
上电倒计时 3 秒内,屏幕黑屏、白屏或无任何有效画面。 - 底层根本原因
LCD 驱动的本质是“对表”。U-Boot 驱动此时发出的像素时钟频率(Pixel Clock)以及行同步/场同步时序参数(HBP, HFP, VBP, VFP)完全是按照 4.3 寸低清屏设计的。屏幕控制 IC 收到这些错误的电信号后,无法锁定帧频,导致硬件直接拒绝显示。 - 后续移植修改靶心
核心文件:
board/freescale/mx6ullevk/mx6ullevk.c
修改动作:
在源码中找到 displays 数组下的 struct display_info_t 结构体,将原本 TFT43AB 的参数,依照你物理屏幕的数据手册(Datasheet),精确修改为适配你当前屏幕的分辨率与时序参数。
🌐 网络(Ethernet)问题汇总
现状
U-Boot 报错提示:
Net: No ethernet found.(未找到以太网设备)。
物理现象:
无法使用 ping 命令,无法通过 tftp 或 nfs 远程下载内核,网络功能完全瘫痪。底层根本原因
这是典型的外围控制电路不匹配导致网络物理层(PHY)无法工作。
NXP 官方原厂设计:
使用 KSZ8081 PHY 芯片,且网口的复位引脚由一颗外扩的 74LV595 串转并芯片来间接控制。
百问网实际硬件设计:
使用 LAN8720A PHY 芯片,且网口的复位引脚直接连接在 i.MX6ULL CPU 的物理 GPIO 上(无 74LV595 芯片)。
冲突点:
U-Boot 启动时试图通过 I2C/SPI 去驱动那个压根不存在的 74LV595 芯片来释放网口复位,导致 LAN8720A 芯片始终处于死机复位状态,MDIO 总线根本找不到网络物理层设备。后续移植修改靶心
核心文件:
board/freescale/mx6ullevk/mx6ullevk.c(引脚复用与物理复位逻辑)
include/configs/mx6ullevk.h(网络宏定义与 PHY 地址配置)
修改动作:
在 mx6ullevk.c 中彻底剔除 74LV595 的驱动代码。
写入纯粹的 GPIO 操作代码,在上电时给 LAN8720A 的复位引脚拉低再拉高,完成硬件复位。
在 mx6ullevk.h 中将 PHY 芯片的地址(CONFIG_FEC_MXC_PHYADDR)修改为你的单板实际物理地址(通常是 0x0 或 0x1)。