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

做BSP开发,环境搭建是第一步。这一步要是没弄好,后面调试起来会非常痛苦。我见过不少新手,花了一周时间在环境上,结果发现是工具链版本不对。

这一章,我把开发环境拆成四个核心部分来讲。你跟着我的节奏走,基本不会踩坑。

3.1 交叉编译工具链安装

交叉编译,说白了就是在PC上编译出能在ARM/MIPS/RISC-V上跑的程序。你的PC是x86架构,目标板是ARM架构,编译器必须知道怎么生成ARM的机器码。

3.1.1 工具链的选择

我个人习惯用Linaro提供的工具链。为什么?因为Linaro跟ARM深度合作,工具链的优化做得很好。当然,你也可以用芯片原厂提供的工具链,比如NXP的、TI的。

工具链类型 适用场景 下载地址
arm-linux-gnueabihf ARM Cortex-A系列,硬件浮点 linaro.org/releases
aarch64-linux-gnu ARMv8 64位平台 linaro.org/releases
riscv64-linux-gnu RISC-V 64位平台 riscv.org/software
gcc-arm-none-eabi 裸机开发,无操作系统 arm.com/developer
注意: 工具链版本必须与内核版本匹配。比如Linux 5.10以上,建议用gcc 9.x或10.x。我曾经用gcc 4.9编译5.15内核,结果编译到一半报了一堆语法错误。

3.1.2 安装步骤

以arm-linux-gnueabihf为例,安装其实就三步:

  1. 下载工具链压缩包
  2. 解压到指定目录
  3. 配置环境变量
# 下载工具链
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-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.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

# 验证安装
arm-linux-gnueabihf-gcc --version
小技巧: 我习惯把工具链放在 /opt 目录下,而不是 /usr/local。这样方便管理多个版本。切换版本时,只需要改 PATH 就行。

3.2 TFTP/NFS服务器配置

嵌入式开发中,你不可能每次都把镜像烧写到板子上。太慢了。TFTP用来传输内核镜像,NFS用来挂载根文件系统。这两个服务配合使用,开发效率能提升好几倍。

3.2.1 TFTP服务器

TFTP是个轻量级的文件传输协议。UDP端口69,没有认证,简单粗暴。U-Boot里直接就能用。

# 安装TFTP服务器(Ubuntu)
sudo apt-get install tftpd-hpa

# 配置TFTP目录
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp

# 修改配置文件 /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure"

# 重启服务
sudo systemctl restart tftpd-hpa

把编译好的zImage、dtb文件丢到 /srv/tftp 目录下。在U-Boot里这样加载:

# U-Boot命令行
setenv serverip 192.168.1.100    # 主机IP
setenv ipaddr 192.168.1.10       # 板子IP
tftp 0x82000000 zImage
tftp 0x83000000 board.dtb
bootz 0x82000000 - 0x83000000
关键点: TFTP目录权限一定要给对。我遇到过好几次,文件明明在目录里,但就是传不过去。最后发现是目录权限是755,但文件是600。TFTP用户读不了。

3.2.2 NFS服务器

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 文件
/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)

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

内核启动参数里加上NFS挂载:

# 内核命令行参数
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
避坑指南: 我曾经在NFS上栽过跟头。板子启动后,ls命令能执行,但一运行程序就报"Permission denied"。查了半天,发现是 no_root_squash 没加。不加这个选项,NFS会把root用户映射成nobody,导致权限不足。

3.3 串口终端工具使用

串口是嵌入式开发中最基本的调试手段。没有串口,你连内核启动信息都看不到。我常用的工具有三个:minicom、screen、putty。

工具 优点 缺点
minicom 功能全面,支持串口参数配置 配置稍复杂
screen 轻量级,支持分屏 默认没有彩色输出
putty 图形界面,操作直观 需要X11支持

3.3.1 minicom配置

# 安装minicom
sudo apt-get install minicom

# 启动配置
sudo minicom -s

# 配置串口参数
Serial Device: /dev/ttyUSB0
Bps/Par/Bits: 115200 8N1
Hardware Flow Control: No
Software Flow Control: No

保存配置后,下次直接运行 sudo minicom 就行。

个人经验: 我习惯把minicom的配置文件保存为 ~/.minirc.dfl。这样换电脑时,直接把配置文件拷过去就能用。另外,记得把用户加入dialout组,不然每次都要sudo。

3.3.2 串口调试技巧

  • 波特率匹配: 板子和PC的波特率必须一致。常见的是115200,但也有用9600或57600的。
  • 流控关闭: 硬件流控(RTS/CTS)默认关闭。如果开了,串口可能无法正常通信。
  • 日志保存: minicom可以用 Ctrl+A L 开启日志记录。调试时一定要开,方便回溯。

3.4 JTAG/SWD调试器连接

串口只能看打印信息,但遇到内核崩溃、死锁、中断异常,串口就无能为力了。这时候需要JTAG或SWD调试器。

JTAG是5线接口(TMS、TCK、TDI、TDO、TRST),SWD是2线接口(SWDIO、SWCLK)。现在主流调试器都支持SWD,因为引脚少,布线方便。

3.4.1 常用调试器

调试器 接口 价格 适用场景
J-Link JTAG/SWD 中高 ARM Cortex全系列
ST-Link SWD 低 STM32系列
OpenOCD + FT2232 JTAG/SWD 低 通用,开源
DAPLink SWD 低 ARM Cortex调试

3.4.2 OpenOCD + GDB调试

我个人比较喜欢OpenOCD + GDB的组合。开源、免费、支持芯片多。

# 安装OpenOCD
sudo apt-get install openocd

# 启动OpenOCD(以STM32MP1为例)
openocd -f interface/stlink.cfg -f target/stm32mp1.cfg

# 另一个终端启动GDB
arm-linux-gnueabihf-gdb vmlinux

# 在GDB中连接OpenOCD
(gdb) target remote localhost:3333
(gdb) monitor reset halt
(gdb) load
(gdb) continue
核心思路: OpenOCD作为中间层,把JTAG/SWD协议转换成GDB远程调试协议。GDB通过TCP 3333端口连接OpenOCD,实现对目标板的调试。

3.4.3 调试技巧

  • 断点设置: 硬件断点有限(通常4-6个),软件断点无限。但软件断点需要修改内存,在Flash上不能用。
  • 单步调试: 内核代码中,中断上下文不能单步。否则会触发看门狗复位。
  • 查看寄存器: info registers 查看所有寄存器,monitor reg 查看外设寄存器。
避坑指南: 我曾经调试一个中断嵌套的问题。在中断处理函数里设了断点,结果一单步就死机。后来发现是中断处理函数里关了全局中断,单步时无法响应其他中断,导致系统卡死。所以,中断上下文里尽量别单步。

3.5 本章知识体系

下面这张图,把开发环境搭建的四个核心部分串起来了。你对照着看,心里就有谱了。

BSP开发环境搭建知识体系 BSP开发环境搭建知识体系 交叉编译工具链 TFTP/NFS服务器 串口终端工具 JTAG/SWD调试器 子模块 Linaro / 原厂工具链 环境变量配置 版本匹配检查 子模块 TFTP目录配置 NFS导出配置 内核命令行参数 子模块 minicom/screen/putty 波特率/流控配置 日志记录 子模块 J-Link / ST-Link / OpenOCD GDB远程调试 断点/单步/寄存器 开发环境 = 工具链 + 网络服务 + 串口 + 调试器 核心原则 1. 工具链版本与内核版本匹配 2. TFTP/NFS权限正确 3. 串口参数一致 4. 调试器驱动正常 5. 先验证单项功能,再组合使用 6. 遇到问题先检查基础配置

环境搭建这块,说白了就是四个工具的组合使用。每个工具单独验证没问题,再串起来用。我刚开始做BSP时,总想一步到位,结果出了问题都不知道是哪个环节的锅。后来学乖了,一个一个测,稳得很。