第二章:开发环境搭建——交叉编译工具链配置、目标板系统镜像烧录、SSH/NFS调试环境搭建、串口终端与日志系统配置
说实话,很多刚入行的朋友觉得AI芯片驱动开发最难的是算法,是底层寄存器操作。但我个人经验告诉我,真正卡住你进度的,往往是开发环境没搭好。我见过太多人花了两周调一个驱动,最后发现是交叉编译工具链版本不对——这种坑,咱们今天一次性填平。
2.1 交叉编译工具链配置
为什么需要交叉编译?你想想看,你的开发机是x86架构,目标板是ARM或RISC-V。你不可能在目标板上装个GCC去编译——那玩意儿跑起来慢得让人抓狂。所以我们在PC上编译,生成目标板能跑的二进制文件。
我个人习惯用Linaro GCC,它对ARM架构支持最好。当然,如果你用的是华为昇腾或寒武纪的芯片,它们通常会提供定制版的工具链。
核心步骤:
- 下载对应架构的交叉编译工具链(比如gcc-arm-linux-gnueabihf)
- 解压到指定目录,例如
/opt/toolchains/ - 配置环境变量
PATH,让系统能找到交叉编译器 - 验证:运行
arm-linux-gnueabihf-gcc --version
# 以ARM Cortex-A72为例
export CROSS_COMPILE=arm-linux-gnueabihf-
export CC=${CROSS_COMPILE}gcc
export CXX=${CROSS_COMPILE}g++
export LD=${CROSS_COMPILE}ld
export AR=${CROSS_COMPILE}ar
# 验证
${CC} --version
# 输出类似:arm-linux-gnueabihf-gcc (Linaro GCC 7.5-2019.12) 7.5.0
我曾经踩过的坑:工具链的glibc版本必须与目标板系统镜像的glibc版本匹配。否则编译出来的程序一运行就报 FATAL: kernel too old 或者段错误。解决办法:用 readelf -l 你的程序 | grep INTERP 查看动态链接器路径,确认与目标板一致。
2.2 目标板系统镜像烧录
拿到一块新的AI开发板,第一件事就是给它灌系统。常见的烧录方式有:
- SD卡烧录:用
dd命令或Etcher工具,把镜像写入SD卡 - USB烧录:通过厂商提供的烧录工具(如Rockchip的
RKDevTool) - 网络烧录:通过TFTP/NFS从服务器加载内核和根文件系统
我个人最常用的是SD卡烧录,简单粗暴。但要注意:烧录前一定要确认镜像的校验和。我遇到过镜像下载过程中损坏,结果板子死活起不来,折腾了一天才发现是镜像本身的问题。
# 查看SD卡设备名(千万别搞错!)
lsblk
# 烧录镜像(假设SD卡是 /dev/sdb)
sudo dd if=ubuntu-20.04-arm64.img of=/dev/sdb bs=4M status=progress
# 同步并弹出
sync
sudo eject /dev/sdb
小技巧:烧录完成后,先别急着拔卡。挂载SD卡的boot分区,检查一下 config.txt 或 extlinux.conf,确认内核启动参数是否正确。特别是 console=ttyS0,115200 这个参数,决定了你能不能通过串口看到启动日志。
2.3 SSH/NFS调试环境搭建
板子跑起来之后,你总不能每次都插着显示器和键盘吧?SSH和NFS就是你的远程调试利器。
SSH配置
说白了就是让板子开一个远程登录服务。我建议你一开始就配好静态IP,否则每次重启IP变了,你还得去查路由器。
# 在目标板上(通过串口或显示器登录后)
sudo apt update
sudo apt install openssh-server
# 配置静态IP(以Ubuntu为例)
sudo vi /etc/netplan/01-netcfg.yaml
# 内容示例:
network:
version: 2
ethernets:
eth0:
dhcp4: no
addresses: [192.168.1.100/24]
gateway4: 192.168.1.1
nameservers:
addresses: [8.8.8.8, 114.114.114.114]
sudo netplan apply
NFS挂载
为什么要用NFS?因为你在PC上改完代码,不用重新烧录,直接让板子通过网络挂载你的开发目录。这对于驱动开发来说,效率提升不是一点半点。
# 在PC上(NFS服务器)
sudo apt install nfs-kernel-server
sudo mkdir -p /home/yourname/nfs_root
sudo chmod 777 /home/yourname/nfs_root
# 编辑 /etc/exports,添加:
/home/yourname/nfs_root *(rw,sync,no_subtree_check,no_root_squash)
sudo exportfs -a
sudo systemctl restart nfs-kernel-server
# 在目标板上(客户端)
sudo mount -t nfs -o nolock 192.168.1.10:/home/yourname/nfs_root /mnt
注意:NFS挂载时加上 nolock 参数,很多嵌入式板子的NFS客户端不支持文件锁。不加这个参数,你可能会看到 mount.nfs: Stale file handle 错误。
2.4 串口终端与日志系统配置
串口是嵌入式开发的最后一道防线。当网络不通、系统崩溃时,串口是你唯一能跟板子对话的方式。
串口终端配置
PC端我用 minicom 或 screen。我个人更推荐 screen,因为它轻量、支持滚屏回看。
# 查看串口设备
ls /dev/ttyUSB* # USB转串口
ls /dev/ttyS* # 原生串口
# 用screen连接(波特率115200,8N1)
sudo screen /dev/ttyUSB0 115200
# 退出screen:Ctrl+A 然后按 K,再按 Y 确认
日志系统配置
驱动开发中,日志就是你的眼睛。我习惯在驱动代码里用 printk 分级输出:
// 驱动代码中的日志分级
#define pr_fmt(fmt) "my_driver: " fmt
#include <linux/printk.h>
// 不同级别的日志
pr_emerg("系统崩溃了!\n"); // 级别0,紧急
pr_alert("必须立即处理\n"); // 级别1,警报
pr_crit("严重错误\n"); // 级别2,临界
pr_err("出错了\n"); // 级别3,错误
pr_warn("警告一下\n"); // 级别4,警告
pr_notice("注意\n"); // 级别5,通知
pr_info("信息\n"); // 级别6,信息
pr_debug("调试信息\n"); // 级别7,调试
查看内核日志:
dmesg:查看所有内核日志dmesg -w:实时监控日志输出cat /proc/kmsg:直接读取内核日志缓冲区tail -f /var/log/kern.log:查看持久化的内核日志
调试技巧:如果你发现 pr_debug 不输出,别慌。内核默认编译时会把调试信息去掉。你需要重新编译内核,打开 CONFIG_DYNAMIC_DEBUG 选项,或者直接在启动参数里加 loglevel=8 来提升日志级别。
知识体系总览
下面这张图,是我自己总结的开发环境搭建全流程。你照着这个顺序走,基本不会漏掉什么。
嗯,到这里,开发环境这块基本就齐了。你可能会问:这些配置一次搞定以后就不用管了吧?其实不是。每次换芯片型号、换内核版本,工具链和镜像都得重新确认一遍。我自己的习惯是,每换一个项目,就把环境搭建的步骤写成脚本,下次直接跑脚本就行。
最后送你一句话:环境搭建花的时间,会在后面调试的时候十倍百倍地省回来。别嫌麻烦,一步到位才是最快的。
公众号:蓝海资料掘金营,微信deep3321