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 |
3.1.2 安装步骤
以arm-linux-gnueabihf为例,安装其实就三步:
- 下载工具链压缩包
- 解压到指定目录
- 配置环境变量
# 下载工具链
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
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
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
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 就行。
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
3.4.3 调试技巧
- 断点设置: 硬件断点有限(通常4-6个),软件断点无限。但软件断点需要修改内存,在Flash上不能用。
- 单步调试: 内核代码中,中断上下文不能单步。否则会触发看门狗复位。
- 查看寄存器:
info registers查看所有寄存器,monitor reg查看外设寄存器。
3.5 本章知识体系
下面这张图,把开发环境搭建的四个核心部分串起来了。你对照着看,心里就有谱了。
环境搭建这块,说白了就是四个工具的组合使用。每个工具单独验证没问题,再串起来用。我刚开始做BSP时,总想一步到位,结果出了问题都不知道是哪个环节的锅。后来学乖了,一个一个测,稳得很。