2. GPIO驱动开发环境搭建:交叉编译工具链配置、内核源码树准备、设备树编译与烧录

嵌入式Linux GPIO驱动开发与普通PC上的驱动开发有本质区别:目标硬件(ARM/RISC-V/MIPS)资源受限,无法直接运行编译工具链。因此,交叉编译是嵌入式驱动开发的基石。本章将手把手搭建完整的开发环境,涵盖工具链安装、内核源码树准备、设备树编译与烧录验证,确保后续章节的驱动代码能正确编译并运行在目标板上。

💡 环境说明:本章以 ARM Cortex-A7(STM32MP157) 为目标平台,主机为 Ubuntu 22.04 x86_64。若使用其他平台(如i.MX6ULL、Raspberry Pi),工具链名称和路径需相应调整,但原理完全一致。

2.1 交叉编译工具链配置

交叉编译工具链由 编译器(gcc)、链接器(ld)、汇编器(as)、库(glibc/uClibc) 及 调试器(gdb) 组成。针对ARM目标,常用工具链有:

工具链名称 目标架构 提供商 前缀 适用场景
arm-linux-gnueabihf-gcc ARMv7-A (硬浮点) Linaro / 芯片厂商 arm-linux-gnueabihf- STM32MP1、i.MX6ULL、AM335x
aarch64-linux-gnu-gcc ARMv8-A (64位) Linaro / 芯片厂商 aarch64-linux-gnu- 树莓派4/5、RK3588、Jetson
riscv64-linux-gnu-gcc RISC-V 64位 SiFive / 社区 riscv64-linux-gnu- K210、全志D1、StarFive

2.1.1 下载与解压工具链

以STM32MP157为例,从 ST官方 或 Linaro 获取预编译工具链:

# 创建开发目录
mkdir -p ~/gpio_workspace/toolchain
cd ~/gpio_workspace/toolchain

# 下载Linaro ARM gcc (7.5.0版本,稳定可靠)
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 解压
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 查看解压后的目录
ls gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/
# 输出: arm-linux-gnueabihf  bin  include  lib  libexec  share

2.1.2 配置环境变量

将工具链的 bin 目录加入 PATH,并设置 CROSS_COMPILE 前缀:

# 编辑 ~/.bashrc 或 ~/.zshrc
export PATH=$PATH:~/gpio_workspace/toolchain/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
# 输出: arm-linux-gnueabihf-gcc (Linaro GCC 7.5-2019.12) 7.5.0
⚠️ 常见错误:若执行 arm-linux-gnueabihf-gcc 提示 No such file or directory,通常是因为 32位库缺失(主机为64位系统)。执行 sudo apt install libc6:i386 lib32ncurses5 lib32stdc++6 解决。

2.1.3 测试交叉编译

编写一个简单的C程序,验证工具链能否正确生成ARM可执行文件:

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

int main(void) {
    printf("GPIO Driver Development Environment OK!\n");
    return 0;
}
# 交叉编译
arm-linux-gnueabihf-gcc test_gpio.c -o test_gpio_arm

# 查看生成文件的架构
file test_gpio_arm
# 输出: test_gpio_arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, not stripped

看到 ARM, EABI5 字样,说明交叉编译工具链配置成功。

📌 关键点:CROSS_COMPILE 环境变量在后续编译内核模块时会被Makefile自动使用,务必正确设置。建议将上述 export 命令写入 ~/.bashrc 永久生效。

2.2 内核源码树准备

GPIO驱动需要依赖内核头文件、模块编译框架和Kconfig配置。因此必须准备一份与目标板运行内核版本一致的 内核源码树,并完成至少一次编译。

2.2.1 获取内核源码

从芯片厂商或内核官网获取源码:

# 创建内核源码目录
mkdir -p ~/gpio_workspace/kernel
cd ~/gpio_workspace/kernel

# 以STM32MP157的Linux 5.10.10为例(ST官方分支)
git clone -b v5.10.10-stm32mp https://github.com/STMicroelectronics/linux.git stm32mp1-kernel

# 进入源码目录
cd stm32mp1-kernel

2.2.2 配置内核

使用目标板对应的 默认配置文件(defconfig):

# 清理之前的编译产物
make mrproper

# 加载STM32MP157的默认配置
make ARCH=arm stm32mp1_defconfig

# 可选:使用menuconfig进行微调(如开启GPIO调试)
make ARCH=arm menuconfig

在menuconfig中,建议确认以下选项已开启:

配置项 路径 说明
CONFIG_GPIOLIB Device Drivers → GPIO Support GPIO核心库,必须开启
CONFIG_GPIO_SYSFS Device Drivers → GPIO Support → /sys/class/gpio/… 用户空间GPIO访问接口(调试用)
CONFIG_GPIO_CDEV Device Drivers → GPIO Support → GPIO character device 字符设备接口(推荐)
CONFIG_OF_GPIO Device Drivers → GPIO Support → OF GPIO support 设备树GPIO解析支持

2.2.3 编译内核与模块

# 设置并行编译(根据CPU核心数调整,例如4核用-j4)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage

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

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

# 安装模块到临时目录(可选)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=./modules modules_install

编译成功后,会在 arch/arm/boot/ 下生成 zImage,在 arch/arm/boot/dts/ 下生成 *.dtb 文件。

💡 加速技巧:首次编译内核耗时较长(约10-30分钟)。后续只需编译内核模块时,可使用 make M=drivers/gpio 仅编译GPIO子系统,大幅缩短时间。

2.3 设备树编译与烧录

设备树(Device Tree)是描述硬件资源的 数据结构,Linux内核通过它来识别GPIO控制器、中断控制器、外设地址等。驱动开发中,修改设备树后必须重新编译并烧录到目标板。

2.3.1 设备树源文件(.dts)结构

一个典型的GPIO节点定义如下:

// stm32mp157.dtsi (SoC级)
&gpioa {
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    reg = <0x50002000 0x400>;
    clocks = <&rcc GPIOA>;
};

// 板级文件(如stm32mp157d-atk.dts)
&gpioa {
    status = "okay";
};

// 用户自定义外设节点
my_gpio_device {
    compatible = "mycompany,gpio-sample";
    gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>;  // PA5
    interrupt-parent = <&gpioa>;
    interrupts = <5 IRQ_TYPE_EDGE_RISING>;
};
📌 关键字段解释:
  • gpio-controller:标记该节点为GPIO控制器
  • #gpio-cells:GPIO描述符需要的参数个数(通常为2:引脚号 + 标志)
  • interrupt-controller:标记该节点为中断控制器
  • #interrupt-cells:中断描述符需要的参数个数(通常为2:中断号 + 触发类型)
  • gpios / interrupts:引用具体的GPIO引脚和中断配置

2.3.2 编译设备树

设备树编译器(dtc)将 .dts 编译为二进制 .dtb:

# 方法一:通过内核Makefile编译(推荐)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

# 方法二:手动使用dtc编译
cd ~/gpio_workspace/kernel/stm32mp1-kernel
./scripts/dtc/dtc -I dts -O dtb -o arch/arm/boot/dts/stm32mp157d-atk.dtb arch/arm/boot/dts/stm32mp157d-atk.dts

# 验证dtb文件
file arch/arm/boot/dts/stm32mp157d-atk.dtb
# 输出: Device Tree Blob version 17, size=..., boot CPU=0x0

2.3.3 烧录设备树到目标板

根据目标板的启动方式(SD卡、eMMC、NAND Flash、TFTP),烧录方法不同。以 SD卡启动 为例:

# 1. 将SD卡插入主机,查看设备节点
lsblk
# 假设SD卡为 /dev/sdb

# 2. 挂载SD卡的boot分区(通常为FAT32格式)
sudo mount /dev/sdb1 /mnt/boot

# 3. 备份原设备树(可选)
sudo cp /mnt/boot/stm32mp157d-atk.dtb /mnt/boot/stm32mp157d-atk.dtb.bak

# 4. 复制新编译的设备树
sudo cp arch/arm/boot/dts/stm32mp157d-atk.dtb /mnt/boot/

# 5. 同步并卸载
sync
sudo umount /mnt/boot
⚠️ 烧录注意事项:
  • 确保设备树文件名与uboot中设置的 fdtfile 环境变量一致,否则内核无法加载。
  • 若使用TFTP网络启动,将 .dtb 文件复制到TFTP服务器目录即可。
  • 烧录后务必 sync 并安全卸载,避免数据损坏。

2.4 开发环境整体架构

下图展示了交叉编译环境、内核源码树、设备树与目标板之间的数据流关系:

GPIO驱动开发环境架构图 主机开发环境 (Ubuntu x86_64) • 交叉编译工具链 • 内核源码树 (Linux 5.10) • 设备树编译器 (dtc) • 烧录工具 (dd / scp / tftp) 内核源码树 • arch/arm/boot/zImage • arch/arm/boot/dts/*.dtb • drivers/gpio/gpio-stm32.c • include/linux/gpio.h 目标板 (STM32MP157) • ARM Cortex-A7 双核 • 运行Linux内核 • 加载设备树 • GPIO控制器 (PA/PB/PC...) 配置/编译 烧录 设备树编译流程 *.dts (源文件) → dtc → *.dtb (二进制) 内核通过OF API解析dtb 💡 开发流程总结 1. 主机上配置交叉编译工具链 → 2. 准备内核源码树并编译 → 3. 修改设备树并编译 → 4. 烧录zImage+dtb到目标板 → 5. 目标板启动,加载驱动 每次修改设备树或驱动代码后,重复步骤3-5即可快速迭代。

2.5 环境验证清单

完成上述步骤后,使用以下清单逐项确认环境就绪:

# 验证项 验证方法 预期结果
1 交叉编译器可用 arm-linux-gnueabihf-gcc --version 显示版本号,无报错
2 内核源码树完整 ls arch/arm/boot/zImage 文件存在(已编译)
3 设备树编译成功 file arch/arm/boot/dts/stm32mp157d-atk.dtb 显示 Device Tree Blob
4 烧录后目标板启动 串口终端查看内核日志 内核启动,无设备树错误
5 GPIO子系统加载 ls /sys/class/gpio/ 显示 gpiochip0 等目录
💡 下一步:环境搭建完成后,第3章将进入GPIO驱动核心——字符设备驱动框架,实现第一个可以控制GPIO引脚的驱动模块。请确保本章所有验证项通过,否则后续实验将无法正常进行。