3. SPI设备驱动开发环境搭建:交叉编译工具链配置、内核源码树准备、设备树SPI节点编写

SPI设备驱动的开发与调试,离不开一个稳固且可复现的嵌入式开发环境。本章将聚焦于搭建这一环境的三个核心环节:交叉编译工具链的配置、内核源码树的准备,以及设备树(Device Tree)SPI节点的编写。这三者构成了从代码编译到硬件描述的基础链路,任何一环的缺失或错误,都会导致驱动无法正确加载或运行。

核心原则:工具链版本必须与内核源码版本严格匹配;设备树节点中的寄存器地址、中断号、时钟频率等参数,必须与目标芯片的数据手册(Datasheet)和原理图完全一致。

3.1 交叉编译工具链配置

嵌入式Linux开发中,宿主机(通常是x86架构的PC)无法直接编译出目标机(ARM、RISC-V等架构)可执行的二进制文件,必须使用交叉编译工具链。配置过程分为选择、安装与验证三步。

3.1.1 工具链选择与安装

根据目标SoC架构选择对应的工具链。主流选择包括:

目标架构 常见工具链前缀 典型提供商 适用场景
ARM (32-bit) arm-linux-gnueabihf- Linaro, ARM STM32MP1, i.MX6, AM335x
AArch64 (64-bit) aarch64-linux-gnu- Linaro, ARM RK3588, i.MX8, Jetson
RISC-V (64-bit) riscv64-linux-gnu- SiFive, T-Head K210, TH1520

安装步骤(以ARM 32-bit为例):

  1. 下载:从Linaro官网或芯片厂商SDK中获取预编译工具链压缩包(如 gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz)。
  2. 解压:tar -xvf gcc-linaro-*.tar.xz -C /opt/
  3. 添加至PATH:编辑 ~/.bashrc,添加 export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin,然后执行 source ~/.bashrc。

3.1.2 工具链验证

安装完成后,必须验证工具链是否可用且版本正确:

# 检查编译器版本
arm-linux-gnueabihf-gcc --version

# 编译一个简单的测试程序
echo 'int main(){ return 0; }' > test.c
arm-linux-gnueabihf-gcc -o test test.c

# 查看生成文件的架构信息
file test
# 预期输出: test: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), ...
常见错误:如果执行 file test 显示的是 x86-64 架构,说明宿主机自带的 gcc 被调用,而非交叉编译器。请检查 PATH 环境变量中交叉工具链的路径是否位于系统 gcc 路径之前。

3.2 内核源码树准备

编译SPI设备驱动模块(.ko文件)时,必须依赖完整的内核源码树,尤其是已配置好的 .config 文件和编译生成的 Module.symvers 文件。

3.2.1 获取与解压内核源码

从芯片厂商提供的BSP(Board Support Package)或Linux内核官方仓库获取源码:

# 以Linux 5.10.y 版本为例
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.120.tar.xz
tar -xvf linux-5.10.120.tar.xz
cd linux-5.10.120

3.2.2 配置内核

使用目标平台的默认配置文件生成 .config:

# 假设目标平台为 STM32MP157
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- stm32mp157_defconfig

# 如果需要微调SPI相关选项,使用菜单配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 进入 Device Drivers -> SPI support,确保 CONFIG_SPI_STM32 或对应驱动被选中为 M 或 Y

3.2.3 编译内核与模块

首次编译需要生成必要的头文件和版本信息:

# 编译内核镜像(耗时较长)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage LOADADDR=0xC2000040 -j4

# 编译内核模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules -j4

# 安装模块到临时目录(用于后续打包到根文件系统)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=./modules_install
提示:编译完成后,务必检查 include/generated/autoconf.h 和 Module.symvers 文件是否存在。这两个文件是外部模块编译时自动依赖的关键。

3.3 设备树(Device Tree)SPI节点编写

设备树是描述硬件资源的静态数据结构。SPI控制器及其从设备(Slave Device)的节点编写质量,直接决定了驱动能否正确探测到硬件。

3.3.1 SPI控制器节点结构

SPI控制器节点通常由SoC厂商在 .dtsi 文件中定义,开发者只需在板级 .dts 文件中使能并配置管脚复用。以下是一个典型的SPI控制器节点示例:

/* 在 stm32mp157.dtsi 中定义 */
spi2: spi@4000b000 {
    compatible = "st,stm32h7-spi";
    reg = <0x4000b000 0x400>;
    interrupts = <36>;
    clocks = <&rcc SPI2_K>;
    resets = <&rcc SPI2_R>;
    dmas = <&dmamux1 39 0x400 0x05>,
           <&dmamux1 40 0x400 0x05>;
    dma-names = "rx", "tx";
    status = "disabled";
};

/* 在板级 myboard.dts 中使能并配置管脚 */
&spi2 {
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&spi2_pins_a>;
    pinctrl-1 = <&spi2_pins_sleep>;
    cs-gpios = <&gpioc 1 GPIO_ACTIVE_LOW>;
    status = "okay";

    /* SPI从设备节点 */
    spidev@0 {
        compatible = "rohm,dh2228fv";
        reg = <0>;
        spi-max-frequency = <10000000>;
    };
};

3.3.2 SPI从设备节点关键属性

属性名 类型 说明 示例值
compatible string 驱动匹配字符串,必须与驱动中 of_match_table 一致 "rohm,dh2228fv"
reg u32 片选(CS)线编号,0对应第1条CS <0>
spi-max-frequency u32 该设备支持的最大SPI时钟频率,单位Hz <10000000>
spi-cpha empty 存在时表示使用CPHA=1模式(数据采样沿) (无值)
spi-cpol empty 存在时表示使用CPOL=1模式(时钟极性) (无值)

3.3.3 设备树编译与反编译

编写完成后,需要将 .dts 源文件编译为 .dtb 二进制文件,供U-Boot或内核加载:

# 编译设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

# 或者单独编译指定文件
./scripts/dtc/dtc -I dts -O dtb -o myboard.dtb myboard.dts

# 反编译验证(检查生成的dtb是否正确)
./scripts/dtc/dtc -I dtb -O dts -o myboard_reverse.dts myboard.dtb
调试技巧:如果驱动无法probe,首先使用 dtc -I dtb -O dts 反编译实际加载的dtb,检查SPI节点是否被正确包含、status 是否为 "okay"、compatible 字符串是否与驱动完全匹配(包括大小写)。
SPI设备树节点与驱动匹配流程 SPI设备树节点与驱动匹配流程 设备树节点 (.dts) compatible = "rohm,dh2228fv" reg = <0> spi-max-frequency = <10000000> 内核 OF 核心 of_match_table 匹配 SPI 设备驱动 .driver.of_match_table .probe() 回调 spi_setup() 配置参数 匹配条件: 设备树节点中的 compatible 值必须与驱动中 of_match_table 定义的字符串完全一致(包括厂商前缀)。 ① 解析设备树 of_platform_populate ② 创建SPI设备 spi_alloc_device ③ 驱动匹配 device_spi_driver_match ④ 执行probe spi_drv_probe

3.4 环境验证清单

完成上述配置后,建议按照以下清单逐项确认环境就绪:

检查项 验证命令/方法 预期结果
交叉编译器可用 arm-linux-gnueabihf-gcc --version 显示版本信息,无“command not found”
内核 .config 存在 ls -l .config 文件存在且大小不为0
SPI驱动被选中 grep CONFIG_SPI .config 显示 CONFIG_SPI=y 或 =m
设备树编译无错误 make dtbs 无报错,生成对应的 .dtb 文件
SPI节点 status 为 okay dtc -I dtb -O dts myboard.dtb | grep spi SPI节点下包含 status = "okay"
最佳实践:建议将上述环境搭建步骤写成Shell脚本(如 setup_env.sh),包含工具链路径设置、内核编译、设备树编译等命令。每次在新机器上搭建环境时,只需运行该脚本即可快速复现,避免手动遗漏。