3. 开发环境搭建:交叉编译工具链安装、TFTP/NFS服务器配置、串口终端工具使用

做嵌入式开发,环境搭建是绕不开的第一道坎。很多初学者喜欢一上来就啃代码,结果编译报错半天找不到原因——最后发现是工具链版本不对。我自己的经验是,花半天时间把环境理清楚,后面能省下几天的排查时间。

这一章我们就把三样东西搞定:交叉编译工具链、TFTP/NFS服务器、串口终端工具。这三样是Bootloader移植和内核调试的基石,缺一不可。

核心思路:开发机(x86_64)上编译ARM代码,通过TFTP把镜像传到目标板,通过NFS挂载根文件系统,通过串口看输出。说白了就是“交叉编译 + 网络传输 + 串口调试”三板斧。

开发环境知识体系 开发环境搭建 交叉编译工具链 arm-linux-gnueabihf- gcc / ld / objcopy 版本:gcc 9.3 / 10.2 TFTP / NFS 服务器 TFTP:tftpd-hpa / atftpd NFS:nfs-kernel-server 端口:69 / 2049 串口终端工具 minicom / picocom putty / SecureCRT 波特率:115200 8N1

3.1 交叉编译工具链安装

交叉编译工具链,说白了就是一套能在x86电脑上生成ARM机器码的工具。你想想看,ARM开发板跑不了gcc,就算能跑也慢得让人崩溃。所以我们得在PC上编译好,再把二进制文件扔到板子上跑。

3.1.1 工具链的选择

市面上工具链很多,我列几个常用的:

工具链名称 适用平台 C库 推荐场景
arm-linux-gnueabihf- ARM Cortex-A系列 glibc 通用Linux开发
aarch64-linux-gnu- ARMv8 64位 glibc 树莓派3/4、RK3399
arm-none-eabi- 裸机/Cortex-M newlib uboot早期启动、RTOS
arm-buildroot-linux-uclibcgnueabihf- ARM Cortex-A uClibc 资源受限的嵌入式系统

我个人习惯用arm-linux-gnueabihf-,因为它是Linaro维护的版本,更新及时,社区支持好。如果你做64位开发,就用aarch64-linux-gnu-。

3.1.2 安装步骤

以Ubuntu 20.04为例,安装arm-linux-gnueabihf工具链:

# 方法一:apt直接安装(推荐)
sudo apt update
sudo apt install gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf

# 方法二:从Linaro官网下载预编译包
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
tar -xf gcc-linaro-*.tar.xz -C /opt/
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin

验证安装:

arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc -v 2>&1 | grep Target

输出应该显示 Target: arm-linux-gnueabihf,说明工具链安装成功。

3.1.3 避坑指南

我曾经在一个项目里吃过亏——工具链版本太新,导致编译出来的uboot在旧版内核上跑不起来。后来查了半天,发现是gcc 10默认开启了某些优化,破坏了uboot的链接顺序。

所以我的建议是:uboot和内核用什么版本的工具链编译,你就用什么版本。别盲目追新。一般uboot 2020.04配合gcc 7.5或9.3是最稳的。

3.2 TFTP/NFS服务器配置

Bootloader移植过程中,你不可能每次都把镜像烧到Flash里。太慢了。正确的做法是用TFTP把uboot镜像或内核镜像传到板子的内存里,用NFS挂载根文件系统。这样改完代码,编译,重启板子就能看到效果。

3.2.1 TFTP服务器配置

TFTP(Trivial File Transfer Protocol)是个轻量级的文件传输协议。它没有认证,没有加密,但胜在简单。uboot原生支持tftp命令。

安装和配置:

# 安装tftpd-hpa
sudo apt install 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"  # --secure限制访问范围

# 创建目录并设置权限
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp

# 重启服务
sudo systemctl restart tftpd-hpa
sudo systemctl enable tftpd-hpa

注意:TFTP默认端口是69,需要确保防火墙放行。如果你在虚拟机里跑,记得把网络模式改成桥接,不然板子找不到你的TFTP服务器。

测试TFTP是否正常工作:

# 在/srv/tftp下放一个测试文件
echo "Hello TFTP" | sudo tee /srv/tftp/test.txt

# 用tftp客户端测试(本机)
tftp localhost
tftp> get test.txt
tftp> quit

# 或者用curl测试
curl -o /tmp/test.txt tftp://localhost/test.txt

3.2.2 NFS服务器配置

NFS(Network File System)用来共享根文件系统。调试内核时,板子通过网络挂载PC上的根文件系统,省去了每次修改都要重新烧写的麻烦。

# 安装nfs-kernel-server
sudo apt install nfs-kernel-server

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

# 编辑导出配置文件
sudo vim /etc/exports

# 添加以下内容(假设板子IP为192.168.1.100)
/srv/nfs/rootfs 192.168.1.100(rw,sync,no_subtree_check,no_root_squash)

# 参数说明:
# rw              :读写权限
# sync            :同步写入
# no_subtree_check:提高性能
# no_root_squash  :允许板子上的root用户拥有root权限

# 重启NFS服务
sudo systemctl restart nfs-kernel-server
sudo exportfs -av

小技巧:调试阶段可以把 /srv/nfs/rootfs 的权限设为777,省得权限问题折腾半天。等产品定型了再收紧权限。

3.2.3 网络环境检查清单

我在项目中遇到过很多次“tftp超时”的问题,最后发现都是网络配置的锅。这里列个检查清单:

检查项 命令/方法 预期结果
PC和板子是否在同一网段 ifconfig / ip addr IP地址前3段一致
能否ping通 ping 192.168.1.100 有回复,无丢包
TFTP端口是否开放 netstat -an | grep :69 显示LISTEN状态
NFS导出是否生效 showmount -e localhost 显示共享目录列表
防火墙是否放行 sudo ufw status 69/udp和2049/tcp已放行

3.3 串口终端工具使用

串口是嵌入式开发者的“眼睛”。uboot启动信息、内核打印、应用程序输出,全从串口来。没有串口,你就像蒙着眼睛调程序。

3.3.1 硬件连接

大多数ARM开发板都带UART接口,通常是3.3V电平的TTL串口。你需要一个USB转串口模块(比如CH340、CP2102、FT232)。

连接方式:

  • GND → 开发板的GND
  • TXD → 开发板的RXD
  • RXD → 开发板的TXD

重要:交叉连接!TXD接RXD,RXD接TXD。我第一次做的时候接反了,折腾了半小时以为串口坏了。另外注意电平匹配,大部分开发板是3.3V,别直接怼5V上去,会烧芯片。

3.3.2 常用串口工具

Linux下我推荐两个工具:minicom和picocom。minicom功能全,picocom轻量简洁。

# 安装
sudo apt install minicom picocom

# 查看串口设备
ls -l /dev/ttyUSB*   # USB转串口通常是ttyUSB0
ls -l /dev/ttyACM*   # 某些开发板是ttyACM0

# 使用picocom(推荐,简单)
sudo picocom -b 115200 /dev/ttyUSB0

# 使用minicom
sudo minicom -D /dev/ttyUSB0 -b 115200

# 退出picocom:Ctrl+A, Ctrl+X
# 退出minicom:Ctrl+A, Z, X

3.3.3 串口参数设置

嵌入式设备串口参数几乎都是固定的:

参数 典型值 说明
波特率 115200 也有用57600或9600的,看uboot配置
数据位 8 固定8位
停止位 1 固定1位
校验位 None 无校验
流控 None 无硬件流控

这个组合通常写成 115200 8N1,你记住就行。

3.3.4 实战:用串口看uboot启动

接好串口,打开终端工具,给开发板上电。你会看到类似这样的输出:

U-Boot 2020.04 (Apr 12 2023 - 15:30:00 +0800)

CPU:   ARM Cortex-A7
DRAM:  512 MiB
MMC:   mmc@48000000: 0
Net:   eth0: ethernet@48000000
Hit any key to stop autoboot:  3
=>

看到 => 提示符,说明uboot已经启动成功,串口通信正常。这时候你就可以输入uboot命令了。

我的习惯:在uboot启动时按任意键打断自动启动,进入命令行模式。然后先用 printenv 看看环境变量,确认serverip(TFTP/NFS服务器IP)和ipaddr(板子IP)是否正确。

3.4 环境验证:一条龙测试

三个工具都装好了,我们来做个完整的验证:

  1. 编译一个测试程序:用交叉编译工具链编译一个简单的hello world。
  2. 放到TFTP目录:把编译好的二进制文件放到 /srv/tftp/。
  3. 板子上通过uboot下载:在uboot命令行输入 tftp 0x42000000 hello.bin。
  4. 验证传输成功:用 md 0x42000000 查看内存内容,确认数据正确。
# PC端编译
arm-linux-gnueabihf-gcc -o hello.bin hello.c -static
sudo cp hello.bin /srv/tftp/

# 板端uboot操作
=> setenv serverip 192.168.1.10
=> setenv ipaddr 192.168.1.100
=> tftp 0x42000000 hello.bin
=> md 0x42000000 0x10

如果能看到内存里有ELF文件头(7f 45 4c 46),说明TFTP传输成功,工具链也没问题。

总结一下:交叉编译工具链让你能在PC上编译ARM代码,TFTP让你快速把镜像传到板子,NFS让你共享文件系统,串口让你看到板子在干什么。这四个东西配合起来,就是嵌入式开发的“四件套”。