1. 项目概述从网络启动到独立运行的跨越折腾嵌入式Linux的朋友相信对“网络文件系统”这个阶段都不陌生。前几篇文章里我们完成了uboot、内核和设备树的移植并且把根文件系统挂载在Ubuntu虚拟机的NFS服务器上。这种方式对于开发和调试来说简直太方便了——改一行内核代码重新编译zImage开发板重启就能生效在根文件系统里增删一个文件主机上操作板子上立刻可见。但这种便利性背后是两根“脐带”的束缚一根网线连着NFS一根串口线连着终端。板子离了网络就是个“砖”啥也干不了。所以把系统“固化”到板载的eMMC存储里是每个嵌入式项目从原型走向产品的必经之路。这就像给一个一直靠输液维持生命的人教会了他自己吃饭喝水。本篇要解决的就是如何将我们精心调教好的uboot、内核、设备树和根文件系统这四个核心部件打包烧录进i.MX6ULL开发板的eMMC中让它彻底摆脱对宿主机的依赖成为一个能独立上电运行的嵌入式Linux设备。我们将使用NXP官方提供的MfgTool工具通过USB OTG接口来完成这个“灌装”过程。这个工具看似一键操作但背后涉及从内存启动到存储设备分区的完整流程理解其原理对于排查烧写失败、定制自己的烧写方案至关重要。2. MfgTool工具深度解析与准备工作2.1 MfgTool是什么为什么是它MfgTool全称Manufacturing Tool是NXP为其i.MX系列处理器量身定做的量产烧写工具。它的设计初衷是在工厂生产线上快速、批量地将系统镜像烧录到设备的eMMC、NAND Flash或SD卡中。对于我们开发者而言它提供了一个稳定、可靠的途径将我们编译好的系统文件从电脑“灌入”板载存储。选择MfgTool而非其他方式如SD卡烧写或TFTP网络烧写主要基于几个考量第一可靠性。这是官方工具对自家芯片的启动流程、eMMC控制器操作最为熟悉能处理各种底层细节。第二便利性。它通过USB OTG接口通信无需依赖板载网络或额外的SD卡一根线搞定。第三功能完整。它并非简单地将文件复制到存储设备而是模拟了一个完整的“两步走”启动过程先加载最小系统到内存运行再用这个运行中的系统去格式化、分区并烧写eMMC这确保了烧写过程的可控性和兼容性。我使用的是从正点原子资料中获取的L4.1.15_2.0.0-ga_mfg-tools.tar.gz工具包。解压后你会发现两个核心的压缩包mfgtools-with-rootfs.tar.gz和mfgtools-without-rootfs.tar.gz。顾名思义带rootfs的版本包含了预编译的根文件系统用于烧写完整系统不带rootfs的则只烧写uboot、内核等适用于后续通过其他方式部署文件系统。我们的目标是制作一个完整的、可独立运行的系统因此选择mfgtools-with-rootfs.tar.gz将其解压。进入解压后的mfgtools-with-rootfs/mfgtools目录你会看到以下关键内容Profiles/目录这是整个工具的核心我们待会儿要替换的系统文件就放在这里。MfgTool2.exe图形化烧写程序的主程序但我们通常不直接双击它。一系列.vbs文件这些是Visual Basic脚本文件它们才是我们启动烧写任务的入口。每个脚本对应不同的板型和存储介质。例如对于eMMC版本的板子我们关注mfgtool2-yocto-mx-evk-emmc.vbs如果是NAND版本则对应不同的脚本。用文本编辑器打开这个vbs文件可以看到它本质上是用特定参数调用MfgTool2.exe指明了板型(sabresd)、存储设备(eMMC)和具体的uboot/设备树配置。这一步的选择至关重要选错了脚本可能导致烧写失败或烧写到错误的位置。2.2 剖析烧写目录结构files与firmware的使命理解MfgTool如何工作必须深入Profiles/Linux/OS Firmware/这个目录。它下面主要有三个关键部分files/、firmware/和ucl2.xml。firmware/目录存放着第一阶段烧写所需的文件。这个阶段的目标是在板子的eMMC还不可用或内容不正确的时候通过USB OTG接口将一个小型的、可运行的系统直接加载到板子的DDR内存中并启动。因此这个目录下的文件必须是板子能够直接从其内存启动的。通常包括u-boot-imx6ull14x14evk_emmc.imx一个包含了SPLSecondary Program Loader和完整uboot的镜像文件格式为NXP的.imx。zImage压缩的内核镜像。zImage-imx6ull-14x14-evk-emmc.dtb与上述内核匹配的设备树二进制文件。 注意这里没有根文件系统。第一阶段加载的是一个极简的、可能基于initramfs内存文件系统的Linux环境其唯一任务就是为第二阶段的烧写做准备。files/目录存放着第二阶段烧写所需的文件。当第一阶段的小系统在内存中运行起来后它已经具备了访问和操作eMMC的能力。此时MfgTool会通过USB将files/目录下的文件传输给这个运行中的系统并由该系统执行烧写命令。因此files/目录包含了一个完整系统所需的全部文件u-boot-imx6ull14x14evk_emmc.imx与firmware目录下相同的uboot文件将被烧写到eMMC的boot分区。zImage内核镜像将被烧写到eMMC的kernel分区。zImage-imx6ull-14x14-evk-emmc.dtb设备树文件通常与内核放在同一分区或紧邻。rootfs_nogpu.tar.bz2根文件系统的压缩包。nogpu表示不含GPU驱动体积更小。这个压缩包将被解压到eMMC的rootfs分区。ucl2.xml文件这是整个烧写过程的“剧本”或“流程控制器”。它是一个XML格式的配置文件详细定义了烧写的每一个步骤先加载哪个文件到内存的哪个地址然后跳转到哪里执行接着如何在运行中的系统里执行分区、格式化、解压等命令。它会根据.vbs脚本传递进来的参数如boardsabresd,mmc1动态地决定使用files/和firmware/目录下的哪些具体文件以及执行哪些针对eMMC的烧写命令。阅读这个文件虽然很长是理解MfgTool内部机制的最佳方式。2.3 硬件连接与模式切换实际操作前需要正确设置硬件连接USB OTG找到开发板上的USB OTG接口通常是Micro-USB或Type-C口用数据线连接到电脑。关键一步务必在连接前将启动方式选择拨码开关拨到USB模式并拔掉SD卡。这是因为uboot在启动时会优先检测SD卡如果SD卡中有可启动镜像它将从SD卡启动从而干扰USB烧写模式。供电与串口给开发板上电。同时建议连接串口调试工具到板子的UART接口通常是UART1并打开串口终端软件如MobaXterm、SecureCRT。串口输出是烧写过程最直接的“黑匣子”任何错误信息都会在这里打印对于排查问题不可或缺。进入下载模式在USB线连接好、电源开启的情况下按下板子的复位键。此时芯片的Boot ROM会检测到拨码开关处于USB模式从而进入USB下载等待状态。此时在电脑的设备管理器中你应该能看到一个名为“HID-compliant vendor-defined device”或类似描述的设备出现这表明板子已被正确识别为MfgTool可操作的设备。注意有些开发板可能需要先按住某个“下载键”再上电或者上电后需要短接某些测试点才能进入USB下载模式。请务必查阅你手头开发板的用户手册确认进入USB烧写模式的具体操作。操作不当是导致“No Device Found”错误的最常见原因。3. 烧写实践从官方镜像到自定义系统3.1 试烧官方系统验证工具链与硬件在烧写我们自己的系统之前强烈建议先用MfgTool烧写一次NXP官方的原始系统。这一步的目的有三个验证工具链你的Windows系统驱动、MfgTool软件是否正常、验证硬件连接USB线、供电、拨码开关是否正确、熟悉流程。官方的镜像通常经过充分测试成功率很高。操作步骤很简单确保Profiles/Linux/OS Firmware/files/和firmware/目录下是官方的原始文件。双击对应的.vbs文件例如mfgtool2-yocto-mx-evk-emmc.vbs。MfgTool图形界面弹出后如果左下角状态栏显示“符合 HID 标准的供应商定义设备”并且设备列表中出现一个设备点击“Start”按钮即可。烧写过程中观察串口终端和MfgTool日志窗口串口会打印uboot、内核启动信息以及后续的分区、格式化、文件复制等操作日志。MfgTool窗口会显示进度条和状态提示如“Jumping to OS image”标志着第一阶段完成第二阶段开始。烧写完成后MfgTool会显示“Done”状态。实操心得我第一次烧写官方大体积镜像时耗时很长约10分钟且中途遇到了失败。但改用资料包里另一个较小的演示镜像如野火提供的却能快速成功。这提示我们第一烧写时间与镜像大小尤其是rootfs直接相关第二首次烧写失败可能是eMMC原有分区表或数据异常。用一个小镜像成功烧写一次相当于对eMMC进行了一次完整的擦除和格式化之后再烧大镜像往往就顺利了。如果始终失败请检查USB线质量必须使用数据线而非仅充电线、电脑USB端口供电是否充足或尝试以管理员身份运行MfgTool。3.2 准备自定义烧写文件烧写自己的系统核心就是替换掉files/和firmware/目录下的文件。你需要准备以下四个文件它们来自你之前的移植工作U-Boot镜像 (u-boot.imx)来源在U-Boot源码根目录下执行make后生成。注意必须是.imx格式它包含了IVTImage Vector Table等头信息是i.MX系列ROM能直接识别的格式。检查点确认你的u-boot.imx是针对eMMC启动编译的。这通常在make时通过make mx6ull_myboard_emmc_defconfig这样的配置来指定。Linux内核镜像 (zImage)来源位于Linux内核源码的arch/arm/boot/目录下。这是编译内核后得到的压缩镜像。检查点确保内核配置中包含了你的板子所需的全部驱动特别是eMMC控制器驱动、你的网卡驱动、文件系统支持如EXT4等。设备树二进制文件 (*.dtb)来源位于Linux内核源码的arch/arm/boot/dts/目录下例如imx6ull-myboard.dtb。检查点设备树必须与你的硬件完全匹配特别是eMMC所在的节点通常是usdhc2、GPIO、网络PHY等。一个错误的引脚定义就可能导致设备无法识别。根文件系统 (rootfs.tar.bz2)来源将你构建好的根文件系统目录例如/home/yourname/nfs_rootfs进行打包。打包命令tar -cjf rootfs.tar.bz2 -C /path/to/your/rootfs .。注意-C参数指定目录并切换到该目录最后的.表示打包当前目录所有内容。务必在打包前确认根文件系统是完整、可用的。3.3 文件替换与重命名策略准备好四个文件后有两种策略替换到MfgTool目录中策略一简单重命名替换推荐初学者这是最直接的方法即将自己的文件按照NXP官方文件的命名规则进行重命名然后覆盖原文件。这样做的好处是无需修改任何配置文件ucl2.xml,.vbs,cfg.iniMfgTool会按照原有逻辑找到并烧写它们。你的文件原名重命名为覆盖原文件放入的目录u-boot.imxu-boot-imx6ull14x14evk_emmc.imxfiles/和firmware/zImagezImage(名称不变)files/和firmware/imx6ull-myboard.dtbzImage-imx6ull-14x14-evk-emmc.dtbfiles/和firmware/rootfs.tar.bz2rootfs_nogpu.tar.bz2files/关键操作u-boot.imx、zImage和.dtb文件需要同时放入files/和firmware/两个目录因为两个阶段都需要它们。根文件系统只需要放入files/目录。策略二修改配置文件适配进阶如果你希望保持自己文件的原始命名或者需要同时管理多个不同板型的镜像则可以修改配置文件。主要修改两个地方修改ucl2.xml找到其中引用文件名的行例如filefirmware/u-boot-imx6ul%lite%%6uluboot%_emmc.imx/file将其中的文件名部分替换为你的文件名。但要注意%lite%和%6uluboot%是变量其值在cfg.ini中定义。修改cfg.ini在[variable]段可以修改变量的值。例如你可以将6uluboot14x14evk改为6ulubootmyboard然后在ucl2.xml中对应的文件名也改为u-boot-imx6ullmyboard_emmc.imx。这种方式更灵活但需要对配置文件结构有一定了解且要确保所有相关引用都同步修改否则容易出错。对于大多数个人开发者和单一项目策略一的简单重命名法足够且可靠。我强烈建议先从这种方法开始。3.4 执行烧写与结果验证文件替换完成后重复3.1节的硬件连接步骤双击.vbs脚本启动MfgTool点击“Start”。此时你应该在串口终端看到与你之前编译的内核相匹配的启动信息而不是官方的内核信息。烧写过程同样分为两个阶段可以在MfgTool日志中观察到。烧写完成后务必先将拨码开关从USB模式切换回eMMC启动模式然后复位或重新上电开发板。如果一切顺利你应该能看到uboot的启动倒计时然后是内核解压和启动信息最终成功挂载eMMC中的根文件系统出现登录提示符。一个至关重要的检查点成功启动后立即检查eMMC的分区情况。在Linux命令行下执行lsblk或fdisk -l /dev/mmcblk1eMMC通常是mmcblk1SD卡是mmcblk0。你应该能看到类似下面的分区信息这表明MfgTool已经正确地对eMMC进行了分区和格式化NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT mmcblk1 179:0 0 7.3G 0 disk ├─mmcblk1p1 179:1 0 1M 0 part /boot ├─mmcblk1p2 179:2 0 32M 0 part / └─mmcblk1p3 179:3 0 7.2G 0 part /home4. 疑难杂症排查与深度优化4.1 内核启动失败uboot环境变量之殇按照上述步骤我最开始烧写后切换回eMMC启动内核却卡住了串口报错“MMC: no card present”或类似无法找到MMC设备的错误。这让人非常困惑明明刚刚才通过USB向eMMC烧写了数据怎么启动时就找不到了呢问题根源不在烧写过程而在我们自己编译的uboot上。回顾我们之前移植uboot时为了从网络启动可能修改了include/configs/mx6ull_myboard.h或对应的头文件中的CONFIG_SYS_MMC_ENV_DEV和CONFIG_SYS_MMC_ENV_PART等环境变量存储位置相关的宏。更常见的是在板级配置头文件中关于板子型号的宏定义被错误修改。以我遇到的错误为例在mx6ull_myboard.h中第一行可能是#define CONFIG_MX6ULL_EVK_EMMC_REWORK。 这个宏MX6ULL_EVK_EMMC_REWORK非常关键它通常用于在代码中条件编译一些与eMMC硬件初始化相关的特定操作。如果我们在移植时盲目地将其改为自己板子的名字例如MYBOARD就可能丢失了针对这块板子eMMC芯片所需的特定初始化序列。解决方案检查你的uboot板级配置文件。如果其中有类似CONFIG_MX6ULL_EVK_EMMC_REWORK的宏并且你的板子硬件设计与NXP EVK板在eMMC部分相似很多国产开发板都是基于EVK设计那么不要改动这个宏保持原样。重新编译uboot生成新的u-boot.imx再次烧写问题大概率会解决。深度解析这个宏通常关联到底层board_init()函数中对USDHCeMMC控制器引脚的复用、上电时序、信号延迟等参数的配置。错误的配置会导致uboot在早期初始化阶段就无法正确识别eMMC硬件从而后续所有依赖于MMC的操作包括加载内核都会失败。这提醒我们移植uboot时对于不理解的宏尤其是与具体硬件初始化强相关的切忌随意改名或删除。4.2 网络接口失活开机自启动配置从eMMC成功启动后我发现使用ifconfig命令看不到eth0或eth1网卡网络不通。这与之前使用NFS挂载根文件系统时的情况不同。原因是当内核通过NFS挂载根文件系统时它必须在挂载阶段就初始化网络以便访问NFS服务器因此网络驱动会被自动加载和启用。而当根文件系统位于本地的eMMC时内核没有这个强制需求网络接口默认是down的状态。临时解决方案可以手动启动网卡并配置IP。ifconfig eth1 up # 启动eth1网卡设备名可能是eth0 ifconfig eth1 192.168.5.108 netmask 255.255.255.0 # 配置静态IP route add default gw 192.168.5.1 # 添加默认网关但这只是临时生效重启后失效。永久解决方案将网络配置添加到开机自动执行的脚本中。在Buildroot构建的根文件系统中这个脚本通常是/etc/init.d/rcS在Ubuntu Base或Debian中可能是/etc/network/interfaces或使用systemd-networkd。这里以常见的rcS为例使用串口或后续配置好的网络登录到板子的Linux系统。编辑/etc/init.d/rcS文件vi /etc/init.d/rcS。在文件末尾或在调用其他网络脚本附近添加以下内容# Configure eth1 on boot /sbin/ifconfig eth1 up /sbin/ifconfig eth1 192.168.5.108 netmask 255.255.255.0 /sbin/route add default gw 192.168.5.1注意eth1需要替换为你实际的网络接口名IP地址和网关根据你的局域网环境修改。保存文件并退出。执行sync命令确保写入eMMC。重启开发板reboot。重启后使用ifconfig检查网卡应该已经启动并配置好IP。更优的实践对于产品化环境建议使用DHCP动态获取IP或者使用更强大的网络管理工具如connman、NetworkManager。可以将DHCP客户端命令加入rcSudhcpc -i eth1。这样板子就能自动从路由器获取IP地址无需硬编码。4.3 烧写过程卡住或报错排查表烧写过程中可能遇到各种问题以下是一个快速排查指南现象可能原因排查步骤MfgTool显示“No Device Connected”1. 板子未进入USB下载模式。2. USB线或电脑端口问题。3. 电脑驱动未安装。1. 确认拨码开关在USB模式SD卡已拔出按复位键。2. 更换USB线或电脑USB口尝试USB2.0口。3. 检查设备管理器看是否有未知设备尝试手动安装MfgTool目录下的驱动。烧写开始后很快失败提示“HID…”设备断开1. 供电不足。2.firmware/目录下的文件不正确或损坏。1. 使用外部电源适配器给开发板供电而非仅靠USB供电。2. 检查firmware/下的.imx,zImage,.dtb文件是否完整尤其是.imx文件是否针对你的板子。第一阶段成功第二阶段卡在“正在格式化…”或文件复制1. eMMC有坏块或硬件故障。2.files/目录下的rootfs压缩包损坏。3. 运行中的小系统内核缺少eMMC驱动或文件系统支持。1. 尝试用官方小镜像先完整烧写一次格式化eMMC。2. 在Ubuntu下检查rootfs.tar.bz2的完整性tar -tjf rootfs.tar.bz2。3. 确认你编译的内核包含了CONFIG_MMC_SDHCI_ESDHC_IMX(eMMC驱动) 和CONFIG_EXT4_FS等必要选项。烧写成功但无法从eMMC启动1. uboot环境变量错误如上述MMC: no card。2. 烧写脚本.vbs选错烧到了错误存储设备。3. eMMC分区表损坏。1. 检查uboot配置确保eMMC初始化宏正确。2. 确认使用的.vbs脚本是针对eMMC的文件名含emmc。3. 在uboot命令行下尝试用mmc dev 1切换到eMMCmmc part查看分区。串口有输出但MfgTool无反应串口线连接到了错误的UART口。MfgTool通过USB通信串口仅用于输出日志。确保串口连接正确通常是UART1但不影响烧写流程。烧写逻辑由USB通信控制。4.4 性能与空间优化进阶思考当系统成功运行后我们还可以从eMMC的利用效率上进行优化调整分区大小MfgTool默认的分区方案如boot分区1MBrootfs分区几百MB可能不适合你的需求。你可以通过修改ucl2.xml中第二阶段的烧写命令来调整。找到包含partition和mkfs命令的CMD标签可以修改分区起始扇区、大小以及文件系统类型。例如将rootfs分区扩大到占用大部分eMMC空间。使用更压缩的根文件系统rootfs.tar.bz2的压缩率已经不错但如果你极度追求启动速度可以考虑使用initramfs内嵌内核将最小的根文件系统直接编译进内核实现极速启动但无法保存数据。使用 squashfs 只读文件系统它具有极高的压缩比适合内容不变的系统部分搭配 overlayfs 实现可写层。uboot优化检查uboot中是否禁用了不必要的驱动和命令以减小uboot体积加快启动速度。同时可以优化uboot的环境变量设置好默认的bootcmd使其能无缝地从eMMC特定分区加载内核和设备树。将系统烧录进eMMC标志着你的嵌入式Linux板卡从一个依赖宿主机的“开发原型”转变为了一个可以独立工作的“嵌入式产品”。这个过程虽然会遇到诸如uboot配置、网络配置等小坑但每一个坑的排查和解决都让你对系统启动链条的理解加深一层。掌握MfgTool这个工具不仅是为了完成烧写更是理解i.MX6ULL从USB下载模式到正常启动模式的完整上下文。当你下次再遇到启动问题时你会清楚地知道问题是出在uboot对硬件的初始化、内核的设备树匹配还是根文件系统的完整性上。这种系统级的视角是嵌入式Linux开发者最宝贵的财富。