3. 开发环境搭建:交叉编译工具链安装、TFTP/NFS服务器配置、串口终端工具使用
做嵌入式开发,环境搭建是绕不开的第一道坎。很多初学者喜欢一上来就啃代码,结果编译报错半天找不到原因——最后发现是工具链版本不对。我自己的经验是,花半天时间把环境理清楚,后面能省下几天的排查时间。
这一章我们就把三样东西搞定:交叉编译工具链、TFTP/NFS服务器、串口终端工具。这三样是Bootloader移植和内核调试的基石,缺一不可。
核心思路:开发机(x86_64)上编译ARM代码,通过TFTP把镜像传到目标板,通过NFS挂载根文件系统,通过串口看输出。说白了就是“交叉编译 + 网络传输 + 串口调试”三板斧。
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 环境验证:一条龙测试
三个工具都装好了,我们来做个完整的验证:
- 编译一个测试程序:用交叉编译工具链编译一个简单的hello world。
- 放到TFTP目录:把编译好的二进制文件放到
/srv/tftp/。 - 板子上通过uboot下载:在uboot命令行输入
tftp 0x42000000 hello.bin。 - 验证传输成功:用
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让你共享文件系统,串口让你看到板子在干什么。这四个东西配合起来,就是嵌入式开发的“四件套”。