2. 开发环境搭建:交叉编译工具链安装、TFTP/NFS服务器配置、串口终端与ADB调试

做嵌入式开发,第一步就是把“窝”搭好。环境配不对,后面全是坑。我见过不少新手,代码写得挺漂亮,结果卡在编译不过、烧录失败上,一查全是工具链版本不对、服务器没配好这类基础问题。

这一章,咱们就把全志T113的开发环境从头捋一遍。说白了,就四件事:交叉编译工具链、TFTP/NFS服务器、串口终端、ADB调试。每样我都会结合自己的踩坑经历来讲。

T113 开发环境整体架构 开发主机(Ubuntu 20.04/22.04) 交叉编译工具链 TFTP 服务器 NFS 服务器 T113 目标板(Linux 内核/根文件系统) 串口终端(UART0) ADB 调试(USB) 串口:内核启动日志 + Uboot ADB:文件传输 + 应用调试

2.1 交叉编译工具链安装

T113是ARM Cortex-A7内核,你的PC是x86架构。在x86上编译出ARM能跑的程序,就得靠交叉编译工具链。说白了,就是一套“跨架构的编译器+库+头文件”。

2.1.1 获取工具链

全志官方推荐的是 gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf 这个版本。我个人习惯用Linaro的版本,稳定,社区支持好。

核心要点:工具链版本必须与内核版本匹配。T113的官方BSP基于Linux 5.4,用gcc 7.5最稳。我曾经试过用gcc 9.x编译,结果内核模块加载时直接报Unknown symbol,折腾了两天才发现是工具链ABI不兼容。

安装步骤很简单,就三步:

  1. 下载:从Linaro官网或全志网盘获取压缩包。
  2. 解压:放到 /opt/ 目录下。
  3. 配置环境变量:加到 ~/.bashrc 里。
# 解压到 /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

# 使生效
source ~/.bashrc

# 验证
arm-linux-gnueabihf-gcc --version

小技巧:我习惯在项目根目录下放一个 setenv.sh 脚本,每次开新终端就 source setenv.sh,避免多个项目环境变量冲突。

2.1.2 验证工具链

写一个最简单的Hello World,交叉编译后放到板子上跑,这是最直接的验证方式。

// hello.c
#include <stdio.h>

int main(void)
{
    printf("Hello from T113!\n");
    return 0;
}

// 编译
arm-linux-gnueabihf-gcc -o hello hello.c

// 查看文件信息
file hello
// 输出: hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), ...

看到 ARM 字样,说明编译出来的确实是ARM架构的可执行文件。嗯,这一步别跳过,我见过有人配了半天,结果 file 一看还是x86的。

2.2 TFTP/NFS服务器配置

开发阶段,你不可能每次修改都重新烧录整个镜像。TFTP用来网络加载内核和设备树,NFS用来挂载根文件系统。这两个配好了,开发效率能翻一倍。

2.2.1 TFTP服务器

TFTP说白了就是一个极简的文件传输协议,Uboot用它从主机拉取内核镜像。

# 安装
sudo apt-get install tftp-hpa tftpd-hpa

# 创建tftp根目录
sudo mkdir -p /tftpboot
sudo chmod 777 /tftpboot

# 配置文件 /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-l -c -s"

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

注意:防火墙要放行UDP 69端口。我在项目中遇到过Ubuntu的ufw默认开启,结果TFTP一直超时,查了半天才发现是防火墙拦了。执行 sudo ufw allow 69/udp 即可。

测试方法:在 /tftpboot 下放一个文件,然后在板子的Uboot中执行:

# Uboot端
setenv serverip 192.168.1.100   # 主机IP
setenv ipaddr 192.168.1.50      # 板子IP
tftp 0x42000000 zImage

能正常下载,说明TFTP通了。

2.2.2 NFS服务器

NFS用于挂载根文件系统。这样你修改了文件系统内容,板子重启后直接生效,不用反复烧录。

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

# 创建导出目录,比如 /home/t113/rootfs
sudo mkdir -p /home/t113/rootfs

# 配置文件 /etc/exports,添加一行
/home/t113/rootfs *(rw,sync,no_subtree_check,no_root_squash)

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

避坑指南:no_root_squash 这个参数很重要。不加的话,板子上的root用户会被映射成nobody,很多操作会权限不足。我曾经因为这个,折腾了一下午才发现是权限问题。

板端挂载测试:

# 板子上执行
mount -t nfs -o nolock 192.168.1.100:/home/t113/rootfs /mnt
ls /mnt

能列出文件,说明NFS配置成功。

2.3 串口终端与ADB调试

这两个是调试的“眼睛”和“手”。串口看日志,ADB传文件、跑命令。

2.3.1 串口终端配置

T113的调试串口默认是UART0,通常用3.3V电平的USB转串口模块连接。波特率一般是 115200,8N1。

主机端我用 minicom 或 screen,个人更推荐 screen,轻量、稳定。

# 安装
sudo apt-get install screen

# 查看串口设备
ls /dev/ttyUSB*   # 或 /dev/ttyACM*

# 连接
sudo screen /dev/ttyUSB0 115200

# 退出:Ctrl+A 然后按 K,再按 Y 确认

关键参数:波特率115200、数据位8、停止位1、无校验、无流控。这五个参数错一个,出来的就是乱码。我刚开始做驱动时,有次串口全是乱码,还以为是硬件坏了,结果发现是minicom的流控没关。

板子上电后,串口应该能输出Uboot的启动信息,然后是内核的log。如果没有任何输出,先检查:

  • TX/RX有没有接反?
  • GND有没有共地?
  • 电平是不是3.3V?

2.3.2 ADB调试

ADB(Android Debug Bridge)本来是Android的调试工具,但全志的Linux BSP也集成了ADB守护进程。有了ADB,你可以通过USB传输文件、执行shell命令,比串口快得多。

主机端安装:

sudo apt-get install adb

# 查看设备
adb devices

# 进入shell
adb shell

# 传输文件
adb push local_file /target_path
adb pull /remote_file local_path

我的习惯:调试驱动时,我通常开两个终端:一个串口看内核log,一个ADB shell用来insmod/rmmod模块、测试应用。串口负责“看”,ADB负责“做”,分工明确。

如果 adb devices 找不到设备,检查:

  • USB线是不是数据线?有些充电线只能充电。
  • 板子上有没有启动ADB守护进程?执行 adbd &。
  • 主机有没有添加USB设备权限?可以 sudo adb kill-server 然后 sudo adb start-server。

2.4 环境验证清单

所有配置完成后,我建议你按这个表格逐项检查。每一项都确认通过,再开始写代码。

序号 验证项 验证方法 预期结果
1 交叉编译工具链 arm-linux-gnueabihf-gcc --version 显示版本号,无报错
2 TFTP下载 Uboot中执行 tftp 0x42000000 zImage 下载成功,显示传输速率
3 NFS挂载 板端执行 mount -t nfs ... /mnt 挂载成功,能列出文件
4 串口输出 板子上电,观察串口终端 正常显示Uboot和内核log
5 ADB连接 adb devices 显示设备序列号

重要提醒:开发主机的IP地址建议设置为静态IP,比如 192.168.1.100。板子的IP也固定下来。DHCP虽然方便,但每次重启IP一变,Uboot里的serverip就得改,很烦。我吃过这个亏,后来一律用静态IP。

环境搭好了,后面写驱动、调bug才能顺风顺水。嗯,这一章的内容就这些,都是实打实的操作。你跟着配一遍,遇到问题再回头看,基本都能解决。