1. 项目概述与核心思路最近因为主力笔记本更新换代我把一台闲置的老笔记本从吃灰状态里拯救了出来给它装上了Ubuntu 20.04 LTS系统专门用来做嵌入式Linux开发。实测下来即便是七八年前的i5处理器加8G内存的老机器运行Ubuntu也比在虚拟机里流畅太多了编译内核、uboot的速度提升非常明显。不过从零开始搭建一套完整的i.MX6ULL开发环境涉及到交叉编译器、uboot、内核、根文件系统以及网络启动调试步骤繁琐容易踩坑。这篇文章我就以这次环境搭建为线索把从裸机Ubuntu到能编译、下载、启动、调试i.MX6ULL开发板的完整流程结合我实际操作中的经验和避坑点系统地梳理一遍。目标是让你拿到这份记录能快速复现一个稳定高效的开发环境把时间花在真正的开发上而不是和环境搏斗。整个环境配置的核心思路是构建一个“主机-目标板”协同的开发模式。主机即这台Ubuntu电脑负责所有代码的编辑、编译和构建目标板i.MX6ULL开发板则通过TFTP和NFS服务从主机获取编译好的内核镜像、设备树并挂载根文件系统实现快速迭代开发。这种模式下修改内核驱动或应用程序后在主机上编译目标板重启即可加载新版本极大提升了调试效率。下面我们就从最基础的Ubuntu系统准备开始一步步拆解每个环节。2. 基础系统准备与工具链安装2.1 Ubuntu系统安装与基础配置我选择的是Ubuntu 20.04.6 LTS版本长期支持社区资源丰富稳定性有保障。安装过程采用U盘启动的方式这里有个细节在分区时建议给/home目录分配较大的空间因为后续的Linux内核源码、交叉编译工具链以及各种SDK都会占用不少空间。我的老笔记本是一块256G的固态硬盘我分配了50G给根目录/剩下的近200G全部分给了/home。系统安装完成后第一件事是更新软件源并升级系统确保我们获得最新的安全补丁和软件包。sudo apt update sudo apt upgrade -y接下来安装嵌入式开发必备的基础工具包。这个步骤很多人会忽略但缺少某些库会导致后续编译各种源码时出现千奇百怪的错误。sudo apt install -y build-essential git vim net-tools openssh-server sudo apt install -y libncurses5-dev libssl-dev bison flex sudo apt install -y u-boot-tools device-tree-compiler gcc-arm-linux-gnueabihf这里解释一下几个关键包的作用build-essential: 包含了gcc, g, make等核心编译工具。libncurses5-dev: 编译内核make menuconfig配置界面时需要。libssl-dev: 内核和部分工具编译时需要OpenSSL库。bison和flex: 语法分析器生成工具编译uboot和内核时可能用到。u-boot-tools: 包含了制作U-Boot镜像的工具mkimage。device-tree-compiler: 设备树编译器dtc用于编译.dts文件为.dtb。gcc-arm-linux-gnueabihf: Ubuntu源里自带的ARM交叉编译器可以作为备用或用于编译一些简单的应用。注意系统自带的gcc-arm-linux-gnueabihf版本可能较低Ubuntu 20.04通常是9.x对于编译较新版本的内核或U-Boot可能不够用。因此我们通常需要安装芯片厂商或社区推荐的专用交叉编译工具链。2.2 安装专用ARM交叉编译工具链对于NXP的i.MX6ULL官方推荐使用Linaro或自己提供的GCC工具链。我选择从ARM官方下载Linaro GCC 7.5.0版本这个版本在稳定性和兼容性上经过了大量项目验证。首先创建一个专门的目录来存放工具链并规划好环境。mkdir -p ~/tools/arm-gcc cd ~/tools/arm-gcc然后使用wget下载工具链压缩包。如果下载速度慢可以事先在浏览器中下载好再通过SSH或U盘拷贝到该目录。# 下载 Linaro GCC 7.5.0 (64-bit Host) 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 sudo mv gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf /opt/最后也是最关键的一步将工具链的路径添加到系统的环境变量中这样我们才能在任意目录下使用arm-linux-gnueabihf-gcc这样的命令。我习惯修改用户家目录下的.bashrc文件。vim ~/.bashrc在文件末尾添加以下几行# ARM Cross Compiler Toolchain export ARCHarm export CROSS_COMPILEarm-linux-gnueabihf- export PATH$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin保存退出后执行source ~/.bashrc让配置立即生效然后验证工具链是否安装成功。source ~/.bashrc arm-linux-gnueabihf-gcc -v如果终端打印出GCC的版本信息如gcc version 7.5.0并且ARCH和CROSS_COMPILE环境变量也已设置正确可以通过echo $ARCH和echo $CROSS_COMPILE查看那么交叉编译工具链就准备就绪了。实操心得路径规划将工具链放在/opt或/usr/local下是一个好习惯结构清晰权限管理也方便。环境变量一定要在.bashrc中设置ARCH和CROSS_COMPILE这能省去后续每次编译uboot或内核时都要在make命令后手动指定的麻烦。版本选择不是越新的工具链越好。对于i.MX6ULL这类经典Cortex-A7芯片GCC 7.x或8.x是经过充分验证的稳定选择。使用太新的工具链如GCC 11可能会遇到链接库不兼容或内核编译错误。3. U-Boot的获取、配置与编译3.1 获取U-Boot源码U-Boot是开发板上电后运行的第一段裸机程序负责初始化硬件并引导加载Linux内核。我们需要获取与i.MX6ULL芯片匹配的U-Boot源码。通常有两个来源芯片原厂NXP的官方Git仓库或开发板厂商提供的适配版本。为了获得最好的兼容性我选择从NXP的官方GitHub镜像获取。cd ~ git clone https://github.com/Freescale/u-boot-fslc.git cd u-boot-fslc # 切换到与i.MX6ULL匹配的稳定分支例如2022.04版本 git checkout -b imx_v2022.04 origin/imx_v2022.04如果网络条件不佳也可以直接从开发板资料包中获取已经下载好的源码压缩包。3.2 配置与编译U-Boot进入U-Boot源码目录首先需要指定目标板型号进行配置。i.MX6ULL EVK开发板对应的配置文件通常是mx6ull_14x14_evk_defconfig。# 清理之前的编译中间文件如果是首次编译可跳过 make distclean # 应用默认配置 make mx6ull_14x14_evk_defconfig接下来如果你需要对U-Boot的功能进行裁剪或增删比如使能/禁用某些命令、驱动可以使用图形化配置界面。make menuconfig对于快速搭建环境我们直接使用默认配置即可。然后开始编译。-j后面的数字表示并行编译的作业数通常设置为CPU核心数的1到2倍以加快编译速度。我的老笔记本是4核8线程这里设置为8。make -j8编译过程大约需要1-3分钟。成功后会在当前目录下生成几个关键文件u-boot ELF格式的可执行文件主要用于调试。u-boot.bin 原始的二进制镜像文件可以直接烧写到存储设备的启动区域。u-boot.imx 由u-boot.bin加上NXP i.MX系列芯片所需的IVTImage Vector Table等头部信息生成的文件这是最终需要烧写到开发板eMMC或SD卡中的文件。注意事项编译错误排查如果编译报错首先检查交叉编译工具链路径是否正确、环境变量是否生效。最常见的错误是找不到arm-linux-gnueabihf-gcc。配置文件确认务必确认你使用的defconfig文件与你的开发板型号完全匹配。板子型号通常印在PCB上比如“14x14 EVK”或“9x9 EVK”。输出文件后续我们通过网络启动测试时主要关注的是u-boot.bin或u-boot.imx。烧写工具如uuu通常需要.imx文件。4. 搭建TFTP与NFS服务器为了实现网络启动我们需要在Ubuntu主机上搭建两个服务TFTP和NFS。4.1 搭建TFTP服务器TFTPTrivial File Transfer Protocol是一种简单的文件传输协议U-Boot内置了TFTP客户端可以从TFTP服务器下载内核镜像和设备树文件。安装TFTP服务器sudo apt install -y tftpd-hpatftpd-hpa是Ubuntu下常用的一款TFTP服务器软件。配置TFTP服务器 编辑其配置文件/etc/default/tftpd-hpa。sudo vim /etc/default/tftpd-hpa修改内容如下主要指定TFTP服务的目录和运行参数TFTP_USERNAMEtftp TFTP_DIRECTORY/var/lib/tftpboot # 这是TFTP服务的根目录后续文件都放这里 TFTP_ADDRESS:69 TFTP_OPTIONS--secure --create # --secure限定在根目录内--create允许客户端创建文件然后创建TFTP目录并设置权限sudo mkdir -p /var/lib/tftpboot sudo chown -R tftp:tftp /var/lib/tftpboot sudo chmod -R 777 /var/lib/tftpboot # 为了方便先赋予全部权限生产环境应收紧重启TFTP服务sudo systemctl restart tftpd-hpa sudo systemctl enable tftpd-hpa # 设置开机自启 sudo systemctl status tftpd-hpa # 查看服务状态确认运行正常4.2 搭建NFS服务器NFSNetwork File System允许开发板通过网络挂载Ubuntu主机上的一个目录作为其根文件系统。这样我们在主机上修改了文件系统中的任何内容如应用程序、配置文件开发板下次访问时立即生效无需重新烧写整个根文件系统镜像是驱动和应用开发的利器。安装NFS服务器sudo apt install -y nfs-kernel-server配置NFS共享目录 首先创建一个用于共享的目录比如/home/你的用户名/nfs_rootfs。mkdir -p ~/nfs_rootfs然后编辑NFS配置文件/etc/exports添加共享规则。sudo vim /etc/exports在文件末尾添加一行请将192.168.2.0/24替换为你实际的局域网网段/home/你的用户名/nfs_rootfs *(rw,sync,no_root_squash,no_subtree_check)* 允许所有IP访问。为安全起见可以指定为192.168.2.0/24允许整个网段或192.168.2.50仅允许开发板IP。rw 读写权限。sync 同步写入数据更安全。no_root_squash非常重要它允许客户端的root用户保持root权限否则开发板上的root操作会被映射为nobody用户导致很多权限问题。no_subtree_check 提高性能禁用子树检查。解决NFS版本兼容性问题关键 这是网络启动中最容易踩的坑。现代Ubuntu默认使用NFSv4而很多嵌入式开发板的U-Boot和内核可能只支持到NFSv3甚至v2。直接挂载会导致失败。 我们需要修改NFS服务器配置强制其同时支持v2,v3,v4。编辑/etc/default/nfs-kernel-serversudo vim /etc/default/nfs-kernel-server在文件末尾找到或添加RPCMOUNTDOPTS或RPCNFSDOPTS参数。在Ubuntu 20.04上更有效的方法是直接修改为# 注释掉原来的或确保有以下行 RPCMOUNTDOPTS--manage-gids --no-nfs-version 4 RPCNFSDOPTS--nfs-version 2,3,4 --debug --syslog或者更简单的做法是直接在文件末尾添加RPCNFSDOPTS--nfs-version 2,3,4 --debug --syslog这个配置让NFS服务同时开启v2, v3, v4支持。应用配置并重启服务sudo exportfs -ra # 重新导出共享目录 sudo systemctl restart nfs-kernel-server sudo systemctl enable nfs-kernel-server测试NFS共享 可以在本机测试一下NFS共享是否成功showmount -e localhost应该能看到你共享的目录路径。5. Linux内核的配置与编译5.1 获取与准备内核源码同样从NXP官方Git仓库获取Linux内核源码是推荐的做法。cd ~ git clone https://github.com/Freescale/linux-fslc.git cd linux-fslc # 切换到与你的U-Boot和芯片匹配的分支例如5.10.x版本 git checkout -b 5.10.xfslc origin/5.10.xfslc5.2 内核配置与编译内核的配置比U-Boot更复杂但基本流程相似。我们使用一个针对i.MX6ULL MFGManufacturing工具的默认配置这个配置通常包含了基本的驱动和功能。# 清理 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- distclean # 应用默认配置 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- imx_v7_mfg_defconfig如果需要深度定制内核比如增删驱动、修改系统参数可以运行图形化配置make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- menuconfig配置完成后开始编译内核。-j16指定了并行编译任务数可以根据你的CPU性能调整。make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- all -j16编译过程耗时较长大约5-15分钟。编译成功后生成的关键文件路径如下内核镜像 (zImage):arch/arm/boot/zImage设备树二进制文件 (.dtb):arch/arm/boot/dts/imx6ull-14x14-evk.dtb(请根据你的具体开发板型号选择也可能是imx6ull-14x14-evk-emmc.dtb或imx6ull-9x9-evk.dtb)避坑技巧解决“code model kernel does not support PIC mode”错误如果你在编译某些版本的内核特别是从官方kernel.org下载的纯净源码时遇到cc1: error: code model kernel does not support PIC mode这个错误这是因为工具链的配置与内核编译选项冲突。 解决方法通常是修改内核顶层Makefile找到KBUILD_CFLAGS变量定义的地方。在其中添加-fno-pie选项。修改后可能类似KBUILD_CFLAGS : -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ -fno-strict-aliasing -fno-common \ -Werror-implicit-function-declaration \ -Wno-format-security \ -stdgnu89 -fno-pie同时确保ARCH和CROSS_COMPILE在Makefile开头或通过环境变量正确设置。 使用NXP维护的内核分支通常已经修复了此类问题。6. 构建与配置根文件系统根文件系统是内核启动后挂载的第一个文件系统包含了Linux系统运行所必须的目录结构、基础命令、库文件和配置文件。6.1 使用Buildroot构建根文件系统手动从零构建根文件系统BusyBox 手工制作非常繁琐且容易出错。对于快速搭建开发环境我强烈推荐使用Buildroot。它是一个自动化构建嵌入式Linux系统包括交叉编译工具链、根文件系统、内核镜像的工具。获取并配置Buildrootcd ~ git clone https://git.buildroot.net/buildroot cd buildroot make menuconfig在图形界面中需要进行以下关键配置Target options-Target Architecture-ARM (little endian)Target options-Target Architecture Variant-cortex-A7Toolchain-Toolchain type-External toolchainToolchain-Toolchain-Linaro ARM 2017.11(或选择你安装的版本)Toolchain-Toolchain origin-Toolchain to be downloaded and installedSystem configuration- 设置root密码初始化系统如BusyBox选择/dev管理方式如Dynamic using devtmpfs mdev。Filesystem images- 选中tar the root filesystem用于生成tarball方便我们放到NFS目录。编译Buildrootmake -j8这个过程会下载所需的软件包并编译首次运行时间较长可能超过半小时。编译完成后根文件系统tarball位于output/images/rootfs.tar。部署到NFS目录sudo tar -xvf output/images/rootfs.tar -C ~/nfs_rootfs # 确保NFS目录下的文件有合适的权限 sudo chown -R root:root ~/nfs_rootfs sudo chmod -R 755 ~/nfs_rootfs6.2 配置开发板通过NFS挂载根文件系统现在我们需要将编译好的内核镜像、设备树文件放到TFTP目录并配置U-Boot从网络启动并挂载NFS根文件系统。拷贝启动文件到TFTP目录cp ~/linux-fslc/arch/arm/boot/zImage /var/lib/tftpboot/ cp ~/linux-fslc/arch/arm/boot/dts/imx6ull-14x14-evk.dtb /var/lib/tftpboot/ # 如果需要也可以把u-boot.imx放进去用于网络更新uboot # cp ~/u-boot-fslc/u-boot.imx /var/lib/tftpboot/配置开发板U-Boot环境变量 将开发板通过串口连接到电脑启动进入U-Boot命令行。 首先设置网络参数请根据你的实际网络环境修改# 设置开发板IP地址 setenv ipaddr 192.168.2.50 # 设置开发板MAC地址通常板子有唯一MAC也可自定义避免局域网冲突 setenv ethaddr b8:1d:01:00:00:00 # 设置网关 setenv gatewayip 192.168.2.1 # 设置子网掩码 setenv netmask 255.255.255.0 # 设置TFTP服务器即Ubuntu主机IP地址 setenv serverip 192.168.2.55 # 保存环境变量到存储设备如eMMC saveenv然后设置从网络启动的完整命令# bootargs: 传递给Linux内核的启动参数 # console: 指定串口控制台设备及波特率 # root: 指定根文件系统为NFS并给出服务器路径和NFS版本选项 # ip: 指定开发板IP、服务器IP、网关等off表示不通过DHCP获取 setenv bootargs consolettymxc0,115200 root/dev/nfs rw nfsroot192.168.2.55:/home/你的用户名/nfs_rootfs,prototcp,nfsvers3 ip192.168.2.50:192.168.2.55:192.168.2.1:255.255.255.0::eth0:off # bootcmd: 上电自动执行的命令 # 先从TFTP服务器下载内核(zImage)和设备树(.dtb)到指定内存地址 # 然后使用bootz命令启动内核 setenv bootcmd tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-evk.dtb; bootz 80800000 - 83000000 saveenv关键点在bootargs的nfsroot参数中我显式指定了nfsvers3。这是确保兼容性的关键一步强制内核使用NFSv3协议挂载避免因版本问题导致挂载失败。测试网络启动 在U-Boot命令行中可以手动执行bootcmd中的命令来测试# 测试网络ping通Ubuntu主机 ping 192.168.2.55 # 如果ping不通检查网线、IP设置、主机防火墙sudo ufw disable可以临时关闭 # 下载内核和设备树 tftp 80800000 zImage tftp 83000000 imx6ull-14x14-evk.dtb # 启动内核 bootz 80800000 - 83000000如果一切配置正确你应该能看到内核启动日志并最终成功挂载NFS根文件系统出现登录提示符。7. 驱动开发环境测试与问题排查环境搭建好后最后一步是测试一个最简单的字符设备驱动确保整个“编辑-编译-部署-测试”的闭环是通的。7.1 编写与编译测试驱动在Ubuntu主机上创建一个简单的驱动模块hello.c#include linux/init.h #include linux/module.h MODULE_LICENSE(GPL); MODULE_AUTHOR(Your Name); static int __init hello_init(void) { printk(KERN_INFO Hello, i.MX6ULL Driver World!\n); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO Goodbye, i.MX6ULL Driver World!\n); } module_init(hello_init); module_exit(hello_exit);编写对应的MakefileKDIR : /home/你的用户名/linux-fslc # 指向你的内核源码目录 CURRENT_PATH : $(shell pwd) obj-m : hello.o build: kernel_modules kernel_modules: $(MAKE) -C $(KDIR) M$(CURRENT_PATH) modules ARCHarm CROSS_COMPILEarm-linux-gnueabihf- clean: $(MAKE) -C $(KDIR) M$(CURRENT_PATH) clean在驱动源码目录下执行make生成hello.ko内核模块文件。7.2 部署与测试拷贝驱动到NFS根文件系统cp hello.ko ~/nfs_rootfs/home/root/在开发板上操作 开发板启动进入Linux系统后执行# 进入驱动所在目录 cd /home/root # 加载内核模块 insmod hello.ko # 查看内核日志应该能看到“Hello, i.MX6ULL Driver World!” dmesg | tail -5 # 卸载模块 rmmod hello # 再次查看日志应该能看到“Goodbye...” dmesg | tail -57.3 常见问题排查速查表在整个环境搭建和测试过程中你可能会遇到以下问题。这里提供一个快速排查指南问题现象可能原因排查步骤与解决方案U-Boot无法ping通主机1. 物理连接问题网线、交换机。2. IP地址、网关、子网掩码设置错误。3. 主机防火墙拦截。1. 检查网线尝试直连或更换交换机口。2. 在U-Boot中用printenv确认网络参数确保与主机在同一网段。3. 在Ubuntu上sudo ufw disable临时关闭防火墙或在Windows上关闭公用网络防火墙。TFTP下载失败1. TFTP服务未运行或配置错误。2. 文件路径或权限问题。3. 防火墙阻止了TFTP端口(69)。1.sudo systemctl status tftpd-hpa检查服务状态。2. 确认/var/lib/tftpboot目录存在且zImage等文件已放入权限为777。3. 在U-Boot中尝试tftp 80800000 zImage时观察主机TFTP服务日志sudo tail -f /var/log/syslog。内核启动后卡住无法挂载根文件系统1. NFS服务器配置错误或未启动。2.bootargs中NFS路径或版本错误。3. 内核未包含NFS客户端支持或网络驱动。1. 在主机showmount -e localhost检查共享是否列出。2.重点检查bootargs中的nfsroot参数IP、路径是否正确务必添加nfsvers3。3. 在内核menuconfig中确认已启用CONFIG_NFS_FSy和对应的网络驱动。挂载NFS根文件系统后提示权限错误或只读1. NFS配置中未使用no_root_squash。2. NFS目录或文件权限不正确。1. 检查/etc/exports文件确保共享选项包含no_root_squash。2. 在主机上检查NFS目录的所有者和权限确保root用户有权访问。sudo chown -R root:root ~/nfs_rootfs。编译内核或驱动时出现“Permission denied”编译输出目录权限不足。确保你对内核源码目录有写权限。如果是克隆的一般没问题。也可以尝试用sudo但不推荐最好调整目录所有权。加载驱动模块失败提示“Invalid module format”驱动模块与当前运行的内核版本不匹配如配置、版本号不一致。确保编译驱动时使用的内核源码目录与开发板上正在运行的内核是同一版本、同一配置编译出来的。这是驱动开发中最常见的错误。走到这一步恭喜你一个完整的、可用于i.MX6ULL应用和驱动开发的嵌入式Linux环境就已经在你的老笔记本上成功运行起来了。这个环境的核心价值在于“网络化”和“分离化”开发在性能强大的主机上进行测试在资源受限的目标板上进行通过TFTP和NFS这座桥梁两者高效协同让每一次代码修改都能在秒级内得到验证。后续无论是学习Linux驱动开发还是移植复杂的应用程序这个稳定的基础环境都会让你事半功倍。