2. 开发环境搭建:交叉编译工具链安装、TFTP/NFS服务配置、串口终端使用
说实话,嵌入式Linux开发的第一步,往往就把不少人卡住了。不是代码多难写,而是环境搭不起来。我见过太多人拿着开发板,串口没输出、文件传不上去、编译出来的程序跑不了——然后就开始怀疑人生。
这一章,咱们就把这些基础环境一次性搞定。说白了,嵌入式开发环境就三样东西:交叉编译工具链(让PC编译出ARM能跑的程序)、TFTP/NFS服务(让开发板能跟PC传文件)、串口终端(让你能跟开发板“说话”)。
核心思路:PC是“生产车间”,开发板是“目标机器”。工具链负责生产,TFTP/NFS负责运输,串口负责沟通。三者缺一不可。
2.1 交叉编译工具链安装
交叉编译,说白了就是在x86的PC上,编译出ARM架构能执行的二进制文件。为什么不能直接在开发板上编译?你想想看,开发板的CPU性能、内存、存储都有限,编译个Linux内核可能要几个小时——谁等得起?
我个人习惯用Linaro GCC,它是ARM官方推荐的交叉编译器,支持ARMv7、ARMv8等主流架构。当然,你也可以用芯片厂商提供的工具链,比如NXP的、TI的,但Linaro的通用性最好。
安装步骤
- 下载工具链:从Linaro官网下载对应架构的gcc-linaro-xxx-x86_64_arm-linux-gnueabihf.tar.xz
- 解压到指定目录:我习惯放在
/opt/toolchains/下 - 配置环境变量:将工具链的bin目录加入PATH
- 验证安装:运行
arm-linux-gnueabihf-gcc --version
# 下载(以ARMv7 Cortex-A9为例)
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
# 解压
sudo mkdir -p /opt/toolchains
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/toolchains/
# 配置环境变量(写入 ~/.bashrc)
export PATH=/opt/toolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# 使生效
source ~/.bashrc
# 验证
arm-linux-gnueabihf-gcc --version
小技巧:我习惯把 export CROSS_COMPILE=arm-linux-gnueabihf- 也写进 ~/.bashrc。这样编译内核、U-Boot时,直接 make 就行,不用每次手动指定。省事不少。
验证交叉编译
写一个简单的hello.c,交叉编译后放到开发板上跑。如果能在ARM板上打印出"Hello, Embedded!",说明工具链没问题。
// hello.c
#include <stdio.h>
int main(void)
{
printf("Hello, Embedded!\n");
return 0;
}
// 交叉编译
arm-linux-gnueabihf-gcc -o hello hello.c
// 查看文件信息
file hello
// 输出: hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)
注意:交叉编译出来的程序,在PC上跑不了。别试,会报"Exec format error"。我刚开始学的时候就犯过这个傻——在PC上直接运行ARM程序,然后一脸懵。
2.2 TFTP服务配置
TFTP(Trivial File Transfer Protocol)是个轻量级的文件传输协议。在嵌入式开发中,它主要用来在U-Boot阶段把内核镜像(zImage)、设备树(dtb)从PC传到开发板的内存里。
为什么不用FTP或SCP?因为U-Boot里没有完整的TCP/IP协议栈,TFTP基于UDP,实现简单,U-Boot原生支持。
安装与配置
# 安装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
避坑指南:我曾经遇到过TFTP传文件总是超时,折腾了半天才发现是防火墙没关。Ubuntu默认开了ufw,记得放行UDP 69端口:sudo ufw allow 69/udp。或者干脆在开发阶段关掉防火墙。
测试TFTP
在PC上放一个测试文件,然后在开发板的U-Boot里用tftp命令下载:
# PC端:把内核镜像放到TFTP目录
cp arch/arm/boot/zImage /srv/tftp/
cp arch/arm/boot/dts/your-board.dtb /srv/tftp/
# 开发板U-Boot端
setenv serverip 192.168.1.100 # PC的IP
setenv ipaddr 192.168.1.10 # 开发板的IP
tftp 0x82000000 zImage # 下载到内存地址0x82000000
tftp 0x83000000 your-board.dtb
2.3 NFS服务配置
NFS(Network File System)比TFTP更进一步。它让开发板通过网络挂载PC上的一个目录,当作自己的根文件系统来用。好处是:你在PC上修改文件,开发板立即可见,不用反复烧写。
我个人的开发习惯是:前期调试阶段100%用NFS启动,等所有驱动和应用都调通了,再打包成镜像烧到板子的Flash里。
安装与配置
# 安装NFS服务器
sudo apt-get install nfs-kernel-server
# 创建NFS共享目录(存放根文件系统)
sudo mkdir -p /srv/nfs/rootfs
sudo chmod 777 /srv/nfs/rootfs
# 编辑 /etc/exports
sudo vim /etc/exports
在 /etc/exports 中添加:
/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)
| 参数 | 含义 |
|---|---|
* |
允许所有IP访问(开发阶段方便,生产环境要限制) |
rw |
读写权限 |
sync |
同步写入,保证数据一致性 |
no_subtree_check |
关闭子树检查,提升性能 |
no_root_squash |
允许开发板以root权限访问,否则会有权限问题 |
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
# 验证共享
showmount -e localhost
U-Boot中设置NFS启动
# 在U-Boot命令行设置
setenv serverip 192.168.1.100
setenv ipaddr 192.168.1.10
setenv netmask 255.255.255.0
setenv gatewayip 192.168.1.1
# 设置NFS启动参数
setenv bootargs 'console=ttyS0,115200 root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/rootfs rw ip=192.168.1.10:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off'
# 下载内核和设备树
tftp 0x82000000 zImage
tftp 0x83000000 your-board.dtb
# 启动
bootz 0x82000000 - 0x83000000
注意:NFS启动时,根文件系统目录 /srv/nfs/rootfs 里必须包含完整的Linux根文件系统结构(bin、sbin、etc、lib、usr、dev、proc、sys等)。否则内核启动到挂载根文件系统时会崩溃。我见过有人只放了一个空目录,然后问为什么启动不了——嗯,内核也需要吃饭的。
2.4 串口终端使用
串口是嵌入式开发中最基础、最可靠的调试接口。不管网络通不通、屏幕亮不亮,只要串口线接对了,你就能看到U-Boot的启动信息,就能跟开发板交互。
我个人推荐用minicom,轻量、稳定、配置简单。当然,你也可以用screen、putty(Windows下),但minicom在Linux下最顺手。
安装与配置
# 安装minicom
sudo apt-get install minicom
# 查看串口设备
ls -l /dev/ttyUSB* # USB转串口通常是ttyUSB0
ls -l /dev/ttyS* # 原生串口通常是ttyS0
# 配置minicom
sudo minicom -s
在minicom的配置菜单中,进入"Serial port setup",设置:
| 参数 | 典型值 | 说明 |
|---|---|---|
| Serial Device | /dev/ttyUSB0 |
串口设备文件,根据实际情况修改 |
| Bps/Par/Bits | 115200 8N1 |
波特率115200,8数据位,无校验,1停止位 |
| Hardware Flow Control | No |
关闭硬件流控,否则可能无法输入 |
| Software Flow Control | No |
关闭软件流控 |
小技巧:配置完成后,选择"Save setup as dfl"保存为默认配置。下次直接运行 sudo minicom 就行。我习惯再按 Ctrl+A 然后 Z 查看帮助,Ctrl+A 然后 X 退出——这些快捷键记熟了,操作起来飞快。
常见问题排查
- 串口没输出:检查波特率是不是115200?检查串口线是不是交叉线(Tx接Rx,Rx接Tx)?我犯过最蠢的错误是用了直连线,折腾了半小时才发现。
- 乱码:波特率不匹配。开发板一般是115200,但也有用57600或9600的,看芯片手册。
- 无法输入:检查硬件流控是否关闭。有些USB转串口模块的RTS/CTS引脚悬空会导致输入被阻塞。
- 权限问题:
sudo usermod -a -G dialout $USER把当前用户加入dialout组,然后注销重新登录,就不用每次sudo了。
2.5 环境验证:一条龙测试
所有服务配置完成后,我建议做一次完整的验证测试。确保工具链能编译、TFTP能传文件、NFS能挂载、串口能交互。这四样都通了,后面的开发才能顺利进行。
# 1. 验证交叉编译
arm-linux-gnueabihf-gcc -o hello hello.c
file hello # 确认是ARM ELF
# 2. 验证TFTP
cp hello /srv/tftp/
# 在开发板U-Boot中: tftp 0x82000000 hello
# 3. 验证NFS
# 在开发板Linux中:
mount -t nfs -o nolock 192.168.1.100:/srv/nfs/rootfs /mnt
ls /mnt # 应该能看到PC上的文件
# 4. 验证串口
# 如果能看到U-Boot启动信息,能在终端输入命令,说明串口正常
我的经验:环境搭建这一步,宁可多花一小时验证,也不要急着写代码。我见过太多人工具链没装对就编译内核,编译报错后怀疑人生,最后发现是交叉编译器版本不对。基础打牢,后面才走得快。