1. 项目背景与核心目标
最近在为一个工业控制项目做定制化开发,选用了NXP的i.MX6ULL这颗性价比极高的处理器。在完成硬件设计后,面临的首要任务就是构建完整的嵌入式Linux系统。这个过程中,U-Boot的移植是关键的第一步,它直接决定了后续内核和根文件系统能否正常加载运行。
i.MX6ULL作为i.MX6系列中的低功耗版本,保留了丰富的外设接口(如双网口、CAN总线等),但主频降低到900MHz,更适合对功耗敏感的场景。官方提供的BSP虽然功能完整,但包含大量冗余功能,我们需要精简优化以适应工业现场的特殊需求。
2. 开发环境准备
2.1 工具链选择
经过对比测试,最终选择了Linaro GCC 7.5.0作为交叉编译工具链。这个版本在ARMv7架构上表现出色,与i.MX6ULL的Cortex-A7内核完美匹配。下载后解压并设置环境变量:
bash复制wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
tar xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
注意:务必验证工具链是否生效,执行
arm-linux-gnueabihf-gcc -v应显示正确的版本信息。遇到过环境变量未生效导致编译失败的情况,建议写入~/.bashrc永久生效。
2.2 源码获取
直接从NXP官方GitHub仓库获取U-Boot源码,选择与芯片匹配的分支:
bash复制git clone https://github.com/nxp-imx/uboot-imx.git -b imx_v2020.04_5.4.70_2.3.0
这个版本是经过NXP官方验证的稳定分支,包含了针对i.MX6ULL的特定优化。相比主线U-Boot,它额外支持了以下关键特性:
- 优化的DDR初始化时序配置
- 增强的eMMC/SD卡启动兼容性
- 专有的PMIC电源管理驱动
3. U-Boot配置与编译
3.1 板级配置选择
i.MX6ULL有多个官方开发板配置,我们需要选择最接近目标硬件的配置作为基础。通过分析configs/目录下的文件:
code复制mx6ull_14x14_evk_defconfig # 官方评估板配置
mx6ull_14x14_evk_emmc_defconfig # 带eMMC的版本
mx6ull_9x9_evk_defconfig # 小型封装版本
我们的硬件采用14x14mm封装但未使用eMMC,因此选择第一个配置作为起点:
bash复制make mx6ull_14x14_evk_defconfig
3.2 关键参数调整
通过make menuconfig进入配置界面,需要特别关注以下选项:
-
Boot Device:
- CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ullevk/imximage.cfg"
- CONFIG_BOOTCOMMAND="mmc dev ${mmcdev}; if mmc rescan; then ..."
-
DDR Configuration:
- CONFIG_SYS_DDR_RAW_TIMING=1
- CONFIG_SYS_MEMTEST_START=0x80000000
- CONFIG_SYS_MEMTEST_END=0x88000000
-
Environment Storage:
- CONFIG_ENV_IS_IN_MMC=1
- CONFIG_ENV_OFFSET=0x400000
- CONFIG_ENV_SIZE=0x2000
经验分享:DDR参数对系统稳定性影响极大。我们曾遇到随机死机问题,最终发现是CONFIG_SYS_DDR_RAW_TIMING未正确设置导致。建议先用官方评估板的默认值,稳定后再优化。
3.3 编译与生成镜像
执行编译命令:
bash复制make -j$(nproc)
成功编译后会生成以下关键文件:
u-boot.bin:原始二进制文件u-boot.imx:包含IVT头的可烧写镜像u-boot.srec:Motorola S-record格式文件
验证文件大小:
bash复制ls -lh u-boot.imx
# 正常应约为400KB左右,过大可能包含调试符号
4. 硬件适配与定制
4.1 时钟树配置
i.MX6ULL的时钟系统非常灵活但也复杂。需要修改arch/arm/cpu/armv7/mx6/clock.c中的初始化代码:
c复制/* 设置ARM内核时钟为792MHz */
void set_arm_clk(void)
{
...
writel(0x00000000, &ccm->ccr); // 先切回24MHz参考时钟
writel(0x1C000000, &ccm->ccr); // 设置PLL1为792MHz
while (!(readl(&ccm->ccsr) & 0x1)); // 等待锁定
...
}
4.2 GPIO引脚复用
在board/freescale/mx6ullevk/mx6ullevk.c中配置IOMUX:
c复制static iomux_v3_cfg_t const uart1_pads[] = {
MX6_PAD_UART1_TX_DATA__UART1_DCE_TX | MUX_PAD_CTRL(UART_PAD_CTRL),
MX6_PAD_UART1_RX_DATA__UART1_DCE_RX | MUX_PAD_CTRL(UART_PAD_CTRL),
};
static void setup_iomux_uart(void)
{
imx_iomux_v3_setup_multiple_pads(uart1_pads, ARRAY_SIZE(uart1_pads));
}
避坑指南:GPIO复用配置错误是常见问题。建议先用
imx_iomux_v3_setup_pad()逐个测试引脚,确认无误后再改用批量配置函数。
4.3 设备树修改
关键设备树节点示例(arch/arm/dts/imx6ull-14x14-evk.dts):
dts复制&usdhc1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usdhc1>;
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
bus-width = <4>;
no-1-8-v;
status = "okay";
};
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
5. 烧写与启动测试
5.1 烧录工具选择
推荐使用NXP官方提供的mfgtools,其特点包括:
- 支持DDR初始化配置
- 自动处理IVT和DCD头
- 提供恢复模式
配置mfgtool2-yocto-mx-evk-mx6ull14x14.ini文件:
ini复制[profiles]
chip = MX6ULL
[platform]
board = MX6ULL
[LIST]
name = MX6ULL-14x14
5.2 启动参数优化
在U-Boot环境中设置合理的bootargs:
bash复制setenv bootargs console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw
setenv bootcmd 'mmc dev 1; fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ull-14x14-evk.dtb; bootz 80800000 - 83000000'
saveenv
5.3 常见启动问题排查
-
DDR初始化失败:
- 现象:卡在"Starting kernel..."或直接死机
- 解决:检查
include/configs/mx6ullevk.h中的DDR参数
-
SD卡无法识别:
- 现象:
mmc dev命令无响应 - 解决:确认设备树中usdhc节点配置正确,特别是pinctrl设置
- 现象:
-
环境变量丢失:
- 现象:每次重启参数恢复默认
- 解决:检查CONFIG_ENV_OFFSET是否与存储介质分区对齐
6. 性能优化技巧
6.1 启动时间优化
通过分析启动流程,我们发现以下优化点:
-
禁用不必要的驱动:
在configs/mx6ullevk_defconfig中移除:code复制-CONFIG_DM_VIDEO=y -CONFIG_VIDEO_LCD=y -
简化环境变量:
bash复制
setenv bootdelay 1 setenv fdt_file imx6ull-14x14-evk.dtb -
预加载DDR参数:
修改board/freescale/mx6ullevk/spl.c,提前初始化DDR。
6.2 内存布局调整
优化include/configs/mx6ullevk.h中的内存分配:
c复制#define CONFIG_SYS_LOAD_ADDR 0x82000000
#define CONFIG_LOADADDR 0x82000000
#define CONFIG_SYS_MEMTEST_START 0x80000000
#define CONFIG_SYS_MEMTEST_END 0x88000000
实测数据:经过优化后,冷启动时间从原来的2.1秒缩短到1.3秒,满足工业现场快速启动的需求。
7. 扩展功能实现
7.1 添加自定义命令
示例:实现一个读取温度传感器的命令:
- 创建
cmd_temp.c:
c复制U_BOOT_CMD(
temp, 1, 1, do_temp,
"Read temperature sensor",
""
);
static int do_temp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
uint32_t val = read_temp_sensor();
printf("Current temperature: %d°C\n", val);
return 0;
}
- 修改
Kconfig和Makefile添加编译选项。
7.2 安全启动支持
配置HAB(High Assurance Boot):
- 生成PKI树:
bash复制openssl genrsa -out SRK_priv.pem 2048
openssl req -new -key SRK_priv.pem -out SRK.csr
- 修改
include/configs/mx6ullevk.h:
c复制#define CONFIG_SECURE_BOOT
#define CONFIG_IMX_HAB
8. 后续开发建议
在实际项目中,我们还需要考虑:
-
生产烧录方案:
- 开发批量烧录工具
- 实现序列号自动注入
-
现场升级机制:
- 设计可靠的OTA更新流程
- 实现A/B分区切换
-
长期维护:
- 建立版本管理系统
- 记录每个版本的硬件兼容性矩阵
移植过程中最深的体会是:i.MX6ULL的灵活性既是优势也是挑战。每次修改配置后,建议通过diffconfig工具记录变更,这对排查问题非常有帮助。另外,官方论坛和errata文档是解决疑难问题的金矿,遇到问题时不妨先搜索已知问题列表。
