3. 开发环境搭建:交叉编译工具链配置、TFTP/NFS 网络启动环境搭建

说实话,嵌入式开发的第一步往往最劝退。很多新手拿着开发板,第一周全耗在「环境没配好,代码跑不起来」上。我自己带团队时,新人入职第一件事就是搭环境——不是折腾他们,而是这步不过关,后面全是白费。

这一章,咱们就把交叉编译工具链、TFTP 和 NFS 这三个核心工具彻底搞定。你跟着我的步骤走,半小时内应该能跑通第一个网络启动。

3.1 交叉编译工具链:为什么非它不可?

你的 PC 是 x86 架构,开发板是 ARM(或 RISC-V、MIPS)。PC 上编译出的程序,开发板不认识。反过来也一样。所以我们需要一个「翻译官」——交叉编译工具链。

说白了,就是在 PC 上运行一个编译器,它生成的目标代码是开发板 CPU 能执行的指令。

3.1.1 工具链的三大件

组件 典型名称 作用
编译器 arm-linux-gnueabihf-gcc 将 C 源码编译为 ARM 机器码
链接器 arm-linux-gnueabihf-ld 将目标文件链接成可执行文件
调试器 arm-linux-gnueabihf-gdb 在 PC 上远程调试开发板程序

我习惯用 arm-linux-gnueabihf- 这个前缀。它代表:目标架构 ARM、Linux 系统、支持硬浮点。如果你的板子是 Cortex-A 系列,这个基本通用。

3.1.2 两种获取方式

方式一:从芯片厂商获取预编译包

这是最省事的。NXP、TI、Rockchip 都会提供官方工具链。比如 NXP 的 gcc-arm-none-eabi-* 系列。直接解压,配置 PATH 即可。

我的建议: 优先用厂商提供的工具链。我自己踩过坑——用 Linaro 通用工具链编译 U-Boot,结果网卡驱动死活不工作,换了 NXP 官方版一次通过。厂商的版本经过充分验证,省心。

方式二:从 Linaro 或 ARM 官方下载

如果你用的 SoC 比较新,或者厂商没提供,可以去 Linaro 官网下载。命令如下:

# 下载 ARM 64位工具链(以 gcc-12.2 为例)
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 解压到 /opt 目录
sudo tar -xvf gcc-linaro-*.tar.xz -C /opt/

# 配置环境变量(写入 ~/.bashrc)
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm

# 使配置生效
source ~/.bashrc
注意: 环境变量 CROSS_COMPILE 和 ARCH 必须设置。U-Boot 和 Linux 内核的 Makefile 依赖这两个变量。我曾经忘记设 ARCH=arm,结果编译出来的 U-Boot 是 x86 格式,烧进去直接黑屏。

3.1.3 验证工具链是否可用

# 查看编译器版本
arm-linux-gnueabihf-gcc --version

# 编译一个简单测试程序
echo 'int main(){return 0;}' > test.c
arm-linux-gnueabihf-gcc -o test test.c

# 查看生成的文件类型
file test
# 输出应为:ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)

看到 ARM, EABI5 就对了。如果显示 x86-64,说明你用的还是本地 gcc,PATH 没生效。

3.2 TFTP 网络启动环境:让开发板从网络加载镜像

为什么要用 TFTP?你想想看,每次修改 U-Boot 或内核都要插拔 SD 卡、烧写 eMMC,多麻烦。网络启动只需在 PC 上改文件,开发板重启就能加载新镜像。调试效率提升 10 倍不止。

3.2.1 在 PC 上搭建 TFTP 服务器

我用的是 Ubuntu 22.04,其他发行版大同小异。

# 安装 TFTP 服务器
sudo apt-get update
sudo apt-get install tftpd-hpa

# 创建 TFTP 根目录
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp

# 修改配置文件 /etc/default/tftpd-hpa
sudo vim /etc/default/tftpd-hpa

配置文件内容如下:

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure --create"

然后重启服务:

sudo systemctl restart tftpd-hpa
sudo systemctl enable tftpd-hpa

# 检查服务状态
sudo systemctl status tftpd-hpa
避坑指南: 防火墙要放行 UDP 69 端口。我曾经在服务器上配了半天,客户端一直超时,最后发现是 ufw 没关。执行 sudo ufw allow 69/udp 即可。

3.2.2 在 U-Boot 中配置 TFTP 启动

开发板上电,进入 U-Boot 命令行。设置以下环境变量:

# 设置开发板 IP(根据你的网络环境修改)
setenv ipaddr 192.168.1.100

# 设置服务器 IP(PC 的 IP)
setenv serverip 192.168.1.10

# 设置网关(可选)
setenv gatewayip 192.168.1.1

# 设置网络掩码
setenv netmask 255.255.255.0

# 保存环境变量
saveenv

测试网络连通性:

# Ping 服务器
ping 192.168.1.10

如果看到 host 192.168.1.10 is alive,说明网络通了。接下来就可以从 TFTP 加载镜像:

# 从 TFTP 下载 U-Boot 镜像到内存地址 0x42000000
tftp 0x42000000 u-boot.bin

# 从 TFTP 下载 Linux 内核
tftp 0x42000000 zImage

# 从 TFTP 下载设备树
tftp 0x43000000 imx6ull-14x14-evk.dtb

3.3 NFS 网络文件系统:让开发板共享 PC 的根文件系统

TFTP 解决了镜像加载问题,但调试应用程序时,每次都要重新烧写根文件系统,太慢了。NFS 可以让你在 PC 上修改文件,开发板直接通过网络访问,实时生效。

3.3.1 在 PC 上搭建 NFS 服务器

# 安装 NFS 服务器
sudo apt-get install nfs-kernel-server

# 创建共享目录(存放根文件系统)
sudo mkdir -p /srv/nfs/rootfs

# 将你的根文件系统解压到该目录
sudo tar -xvf rootfs.tar.gz -C /srv/nfs/rootfs

# 修改配置文件 /etc/exports
sudo vim /etc/exports

在 /etc/exports 中添加一行:

/srv/nfs/rootfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

参数说明:

  • rw:读写权限
  • sync:同步写入,保证数据一致性
  • no_subtree_check:提高性能
  • no_root_squash:允许开发板以 root 权限访问,调试时很有用

重启 NFS 服务:

sudo exportfs -a
sudo systemctl restart nfs-kernel-server

# 验证共享是否生效
showmount -e localhost

3.3.2 在 U-Boot 中配置 NFS 启动

U-Boot 启动内核时,通过 bootargs 参数告诉内核使用 NFS 根文件系统:

# 设置 NFS 启动参数
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.10:/srv/nfs/rootfs,v3,tcp ip=192.168.1.100:192.168.1.10:192.168.1.1:255.255.255.0::eth0:off'

# 完整的启动命令
setenv bootcmd 'tftp 0x42000000 zImage; tftp 0x43000000 imx6ull-14x14-evk.dtb; bootz 0x42000000 - 0x43000000'

# 保存并重启
saveenv
boot
注意: nfsroot 参数中的路径必须与 /etc/exports 中配置的路径一致。另外,v3 协议兼容性最好,我遇到过 NFSv4 在某些老内核上挂载失败的情况。

3.4 整体流程:一张图看懂

下面这张图展示了开发板从加电到进入 NFS 根文件系统的完整流程:

TFTP + NFS 网络启动完整流程 开发板上电 U-Boot 启动 TFTP 下载内核 TFTP 下载设备树 内核启动 NFS 挂载根文件系统 PC 服务器 TFTP 服务 (udp:69) 提供 zImage 提供 .dtb 文件 NFS 服务 (tcp:2049) 共享 /srv/nfs/rootfs 以太网 U-Boot 通过 DHCP 或静态 IP 获取网络配置 内核启动后自动挂载 NFS

3.5 常见问题与排查思路

环境搭好了,但第一次启动大概率会出问题。别慌,我列几个最常见的坑:

现象 可能原因 解决方法
TFTP 下载超时 防火墙未放行 UDP 69 端口 sudo ufw allow 69/udp
TFTP 下载成功但内核不启动 内核镜像格式不对(如 uImage 与 zImage 混淆) 检查 U-Boot 的 bootm 或 bootz 命令是否匹配
NFS 挂载失败:VFS: Unable to mount root fs 内核未开启 NFS 客户端支持 重新编译内核,选中 CONFIG_NFS_FS=y
NFS 挂载失败:Network is unreachable U-Boot 设置的 IP 与内核启动参数中的 IP 不一致 检查 bootargs 中的 ip= 参数
开发板能 ping 通 PC,但 TFTP 无响应 TFTP 根目录权限不足 sudo chmod 777 /srv/tftp

我的调试习惯: 先确保 TFTP 能下载文件,再搞 NFS。TFTP 通了,说明网络层没问题。NFS 挂不上,多半是内核配置或 bootargs 写错了。别同时排查两个问题,逐个击破。

3.6 小结

这一章我们搞定了三件事:交叉编译工具链、TFTP 服务器、NFS 服务器。工具链让你能在 PC 上编译出 ARM 代码,TFTP 让你通过网络加载内核和设备树,NFS 让你共享根文件系统。这三板斧砍下来,你的开发效率至少翻一倍。