1. ARM Linux 驱动开发环境搭建

做 ARM Linux 驱动开发,第一件事就是把环境搭好。我见过不少新手,代码写得挺漂亮,结果卡在环境配置上,一卡就是好几天。说白了,环境就是你的工具箱——工具不称手,活就干不顺。

这一章,我们重点搞定三件事:交叉编译工具链、内核源码树、NFS 根文件系统挂载。这三样配齐了,你就能在 PC 上编译、在开发板上跑,调试效率翻倍。

1.1 交叉编译工具链配置

ARM 开发板和 PC 的 CPU 架构不同。PC 是 x86,ARM 板是 ARM。你不能直接在 PC 上编译出 ARM 能跑的程序——除非你用交叉编译。

交叉编译工具链,说白了就是一套能在 x86 上生成 ARM 可执行文件的工具集。里面包含编译器、链接器、库文件等等。

1.1.1 工具链的选择

市面上常见的 ARM 交叉编译工具链有几种:

工具链名称 适用场景 我的建议
arm-linux-gnueabihf- ARM Cortex-A 系列,硬件浮点 大多数 ARM 板首选
arm-linux-gnueabi- ARM Cortex-A 系列,软浮点 老内核或特殊板子用
aarch64-linux-gnu- ARM64 (Cortex-A53/A72 等) 64 位 ARM 板必选
arm-none-eabi- 裸机开发,无操作系统 驱动开发一般不用

我个人习惯用 arm-linux-gnueabihf-。为什么?因为现在主流的 ARM 板(比如 i.MX6、AM335x、STM32MP1)都带硬件浮点单元,用 hf 版本性能更好。我在项目中遇到过有人用错工具链版本,结果内核启动到一半就崩了——查了两天才发现是浮点指令不兼容。

1.1.2 安装与验证

以 Ubuntu 系统为例,安装命令很简单:

sudo apt-get install gcc-arm-linux-gnueabihf
sudo apt-get install g++-arm-linux-gnueabihf

装完后验证一下:

arm-linux-gnueabihf-gcc --version

能看到版本号就说明装好了。嗯,这里要注意:有些教程会让你从 Linaro 官网下载压缩包手动解压。我个人觉得没必要——apt 源里的版本足够稳定,除非你有特殊需求。

小技巧: 把工具链路径加到 ~/.bashrc 里,省得每次都要写全路径。加一行 export PATH=$PATH:/usr/bin 就行——其实 apt 安装的默认就在 /usr/bin,一般不用加。

1.1.3 测试编译

写个简单的 hello.c 试试:

#include <stdio.h>

int main() {
    printf("Hello ARM Linux!\n");
    return 0;
}

编译:

arm-linux-gnueabihf-gcc -o hello hello.c

用 file 命令看看生成的文件:

file hello
# 输出: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)

看到 "ARM" 字样就对了。我曾经犯过一个低级错误——在 PC 上直接跑这个 hello 文件,结果报 "cannot execute binary file"。废话,ARM 的程序在 x86 上当然跑不了。

1.2 内核源码树构建

驱动开发离不开内核源码。你写的驱动模块,需要依赖内核头文件和编译规则。所以你得先有一套完整的内核源码树。

1.2.1 获取内核源码

有两种方式:

  • 从 kernel.org 下载主线内核:版本新,但可能和你的板子不完全匹配
  • 从芯片厂商获取 BSP 内核:比如 NXP 的 Linux BSP、TI 的 Processor SDK——这些是定制好的,开箱即用

我建议新手直接用厂商的 BSP。为什么?因为主线内核需要你自己打补丁、配设备树,厂商 BSP 已经帮你做好了。我在项目中用过几次主线内核,每次都要花一两天适配板级支持,得不偿失。

以 NXP i.MX6 为例,下载 BSP 后解压:

tar -xvf linux-imx.tar.bz2
cd linux-imx

1.2.2 配置内核

内核配置有三种方式:

命令 说明 适用场景
make menuconfig 文本菜单界面 最常用,推荐
make xconfig 图形界面 需要 Qt 库,不常用
make defconfig 默认配置 快速开始,但可能缺模块

我个人习惯用 make menuconfig。先加载厂商的默认配置:

make ARCH=arm imx_v7_defconfig
make ARCH=arm menuconfig

在 menuconfig 里,你可以按需开关驱动模块。比如你要写一个 I2C 驱动,就确保 Device Drivers → I2C support 是选中的。

注意: 配置内核时,ARCH=arm 这个参数不能少。我见过有人忘了指定架构,结果编译出来的内核是 x86 的——烧到板子上当然起不来。

1.2.3 编译内核

配置好之后,开始编译:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 modules

-j4 表示用 4 个线程并行编译,能快不少。编译完成后,在 arch/arm/boot/ 下会生成 zImage——这就是我们要的内核镜像。

编译内核模块:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=./modules

模块会安装到 ./modules 目录下,后面挂载 NFS 时会用到。

1.3 NFS 根文件系统挂载

驱动开发最烦什么?反复烧写文件系统。每次改个驱动都要重新烧写、重启,一天下来大半时间都在等。NFS 挂载能解决这个问题——开发板通过网络,把 PC 上的目录当作根文件系统来用。

1.3.1 为什么用 NFS

你想想看,传统方式是这样的:

  1. 在 PC 上编译驱动
  2. 把驱动拷贝到 SD 卡或烧写到 Flash
  3. 插卡、重启开发板
  4. 测试、发现问题
  5. 回到第 1 步

用 NFS 之后:

  1. 在 PC 上编译驱动
  2. 驱动直接出现在开发板的文件系统里
  3. 立即测试

省掉了烧写和重启的步骤。我在项目中用过 NFS 之后,就再也不想回到烧写模式了——效率提升不是一点半点。

1.3.2 搭建 NFS 服务器

在 PC 上安装 NFS 服务器:

sudo apt-get install nfs-kernel-server

创建一个目录作为根文件系统的导出点:

sudo mkdir -p /srv/nfs/rootfs

把你之前编译好的根文件系统(比如用 Buildroot 或 Yocto 生成的)解压到这个目录:

sudo tar -xvf rootfs.tar -C /srv/nfs/rootfs

配置 NFS 导出:编辑 /etc/exports,添加一行:

/srv/nfs/rootfs *(rw,sync,no_subtree_check,no_root_squash)

参数说明:

  • rw:读写权限
  • sync:同步写入,保证数据一致性
  • no_subtree_check:禁用子树检查,提高性能
  • no_root_squash:允许开发板以 root 权限访问——这个很重要,驱动开发经常需要 root 权限

重启 NFS 服务:

sudo exportfs -a
sudo systemctl restart nfs-kernel-server
验证方法: 在 PC 上自己挂载自己,看看能不能访问:sudo mount -t nfs localhost:/srv/nfs/rootfs /mnt。能成功就说明 NFS 服务器没问题。

1.3.3 配置开发板启动参数

开发板通过 U-Boot 启动时,需要告诉内核用 NFS 作为根文件系统。在 U-Boot 命令行中设置:

setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/rootfs ip=192.168.1.10:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off'
saveenv
boot

参数拆解:

  • root=/dev/nfs:告诉内核使用 NFS 根文件系统
  • nfsroot=192.168.1.100:/srv/nfs/rootfs:NFS 服务器 IP 和导出路径
  • ip=192.168.1.10:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off:开发板 IP、服务器 IP、网关、掩码

嗯,这里要注意:IP 地址要和你 PC 的网络环境匹配。我建议用静态 IP,别用 DHCP——万一 DHCP 分配了不同的 IP,NFS 就挂不上了。

1.3.4 常见问题排查

我曾经遇到过 NFS 挂载失败,卡在 "VFS: Unable to mount root fs" 的错误。排查步骤:

  1. 先确认网络通不通——在 U-Boot 里 ping 一下 PC 的 IP
  2. 检查 PC 的防火墙——有时候防火墙会拦截 NFS 端口
  3. 确认 /etc/exports 配置正确——特别是 no_root_squash 有没有加
  4. 查看 PC 上的 NFS 日志:sudo tail -f /var/log/syslog

大部分问题都是网络或权限导致的,按这个顺序查,基本都能解决。

1.4 本章知识体系

下面这张图把本章的核心逻辑串起来了:

ARM Linux 驱动开发环境搭建 交叉编译工具链 内核源码树 NFS 根文件系统 arm-linux-gnueabihf-gcc 验证:file hello make menuconfig make zImage modules 配置 /etc/exports U-Boot bootargs 三件套配齐后,PC 上编译、开发板上运行,调试效率翻倍 无需反复烧写,驱动修改立即可测

核心要点:

  • 交叉编译工具链选 arm-linux-gnueabihf-,匹配硬件浮点
  • 内核源码用厂商 BSP,省去适配时间
  • NFS 挂载是驱动开发调试的利器,必须掌握