嵌入式Linux驱动开发——设备树中的 GPIO 配置实战

嵌入式Linux驱动开发------设备树中的 GPIO 配置实战

仓库已经开源!所有教程,主线内核移植,跑新版本imx-linux/uboot都在这里,或者一起来尝试跑7.0的Linux!欢迎各位大佬观摩!喜欢的话点个⭐!
仓库地址:https://github.com/Awesome-Embedded-Learning-Studio/imx-forge
静态网页:https://awesome-embedded-learning-studio.github.io/imx-forge/

PS: 笔者5.28号到6.6号出差,不过的话,更新应该正常,会把压在草稿箱的存货一步一步慢慢发掉了!所以存在任何错误的修复,只能等到回到自己的小窝后才能修!

前言:GPIO 属性的两种写法

在上一章我们分析了 GPIO 子系统的源码,理解了它是如何工作的。现在让我们回到设备树,看看怎么正确配置 GPIO。

说实话,设备树里的 GPIO 配置有一个容易让人踩坑的地方:属性名有两种写法 ------led-gpioled-gpios。这两个都是对的,但用法稍有不同。我们后面会详细讲。

GPIO 控制器节点

首先,GPIO 控制器本身也需要在设备树里定义。在 i.MX 6ULL 的设备树里(imx6ull.dtsi),GPIO1 的定义大致是这样的:

dts 复制代码
gpio1: gpio@0209c000 {
    compatible = "fsl,imx7d-gpio", "fsl,imx35-gpio";
    reg = <0x0209c000 0x4000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
};

这里有几个关键属性:

  • compatible:用于驱动匹配。fsl,imx7d-gpio 是具体的兼容字符串。
  • reg:GPIO 控制器的寄存器地址范围。
  • gpio-controller:标记这是一个 GPIO 控制器。
  • #gpio-cells:指定引用这个 GPIO 时需要几个参数。对于 i.MX,通常是 2 个:控制器内的编号和标志位。

⚠️ 注意#gpio-cells = <2> 表示引用这个 GPIO 时需要 2 个参数。第一个参数是控制器内的编号(0-31),第二个参数是标志位(比如 GPIO_ACTIVE_LOW)。

GPIO 属性的两种写法

现在我们来看设备节点里怎么引用 GPIO。有两种写法:

写法一:单数形式 xxx-gpio

dts 复制代码
imx_aes_led {
    led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
};

这种写法适用于只有一个 GPIO 的情况。属性名是单数形式,值是单个 GPIO 引用。

写法二:复数形式 xxx-gpios

dts 复制代码
imx_aes_led {
    led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
};

这种写法更通用,适用于有多个 GPIO 的情况。比如一个设备有 3 个 LED:

dts 复制代码
my_device {
    led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>,
                <&gpio1 4 GPIO_ACTIVE_LOW>,
                <&gpio1 5 GPIO_ACTIVE_LOW>;
};

⚠️ 注意 :虽然两种写法都可以,但内核推荐使用复数形式 xxx-gpios。为什么?因为这样更一致,而且便于扩展。

GPIO 引用的格式

不管用哪种写法,GPIO 引用的格式都是一样的:

dts 复制代码
<&gpio_controller pin_number flags>
  • &gpio_controller:GPIO 控制器的引用(通过标签)
  • pin_number:控制器内的编号(0-31)
  • flags:标志位,常用的有 GPIO_ACTIVE_LOWGPIO_ACTIVE_HIGH

我们的例子:

dts 复制代码
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;

表示:使用 GPIO1 控制器的第 3 号引脚,低电平有效。

GPIO_ACTIVE_LOW vs GPIO_ACTIVE_HIGH

这两个标志位告诉内核 GPIO 的极性:

  • GPIO_ACTIVE_LOW (0):低电平有效。写 0 时设备激活,写 1 时设备关闭。
  • GPIO_ACTIVE_HIGH (1):高电平有效。写 1 时设备激活,写 0 时设备关闭。

默认值是 GPIO_ACTIVE_HIGH,所以如果忽略这个参数,内核会认为是高电平有效。

我们的 LED 是低电平有效的,所以必须指定 GPIO_ACTIVE_LOW。这样内核会自动反转逻辑,我们就可以用正常的思维(1 表示开,0 表示关)来编程了。

驱动代码如何解析 GPIO

让我们看看驱动代码是怎么从设备树解析 GPIO 的:

c 复制代码
// 方法一:使用 of_get_named_gpio(旧 API)
int gpio = of_get_named_gpio(dev->of_node, "led-gpio", 0);
if (gpio < 0) {
    pr_err("Failed to get GPIO\n");
    return gpio;
}

// 方法二:使用 gpiod_get(新 API,推荐)
struct gpio_desc *desc;
desc = gpiod_get(dev, "led", GPIOD_OUT_LOW);
if (IS_ERR(desc)) {
    pr_err("Failed to get GPIO\n");
    return PTR_ERR(desc);
}

of_get_named_gpio 的第二个参数是属性名("led-gpio" 或 "led-gpios"),第三个参数是索引(如果有多个 GPIO,用索引选择)。

gpiod_get 的第二个参数是属性名的后缀。如果属性名是 "led-gpios",后缀就是 "led"。

⚠️ 注意 :新代码推荐使用 gpiod_get 系列 API,而不是 of_get_named_gpio。新 API 有更好的错误处理和资源管理。

查找引脚冲突的方法

当你添加新的 GPIO 配置时,一定要检查引脚是否已经被其他设备使用了。

方法一:grep 设备树文件

bash 复制代码
grep -r "gpio1" arch/arm/boot/dts/

这会显示所有引用 GPIO1 的地方。你可以逐个检查,看看有没有冲突。

方法二:查看内核日志

如果你的 GPIO 已经被其他驱动占用了,当你尝试申请时会看到类似的错误:

复制代码
gpio-3 (led-gpio) hogged, cannot claim

方法三:查看 sysfs

GPIO 子系统会在 sysfs 下导出信息:

bash 复制代码
ls /sys/class/gpio/

如果一个 GPIO 已经被导出,你会看到 gpiochip0gpio3 这样的节点。

完整的设备树配置示例

现在让我们来看一个完整的设备树配置,包含 pinctrl 和 GPIO:

dts 复制代码
/dts-v1/;
#include "imx6ull.dtsi"
#include "imx6ull-aes.dtsi"

/ {
    model = "Awesome Embedded Studio IMX6ULL Example Driver";
    compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

    imx_aes_led {
        compatible = "imxaes_led";

        // pinctrl 配置:引脚复用和电气特性
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_aes_led>;

        // GPIO 配置:哪个 GPIO,极性是什么
        led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;

        status = "okay";
    };
};

&iomuxc {
    // pinctrl 配置:把 GPIO1_IO03 配置成 GPIO 功能
    pinctrl_aes_led: led_grp {
        fsl,pins = <
            MX6UL_PAD_GPIO1_IO03__GPIO1_IO03    0x10B0
        >;
    };
};

这个配置做了两件事:

  1. pinctrl 子系统把 GPIO1_IO03 配置成 GPIO 功能
  2. 设备树告诉驱动:这个设备用的是 gpio1 的第 3 号引脚,而且是低电平有效的

其他外设的 GPIO 使用示例

为了让你对 GPIO 配置有更全面的理解,让我们看看其他外设是怎么用 GPIO 的。

SD 卡的 CD 引脚

dts 复制代码
&usdhc1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_usdhc1>;
    cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
    status = "okay";
};

这里 cd-gpios 表示卡检测(Card Detect)引脚。当 SD 卡插入时,这个引脚会被拉低(因为是 GPIO_ACTIVE_LOW)。

I2C 的 SDA/SDL 引脚

I2C 不需要单独的 GPIO 配置,因为引脚复用已经处理了:

dts 复制代码
&i2c1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";
};

&iomuxc {
    pinctrl_i2c1: i2c1grp {
        fsl,pins = <
            MX6UL_PAD_UART4_TX_DATA__I2C1_SCL    0x4001b8b0
            MX6UL_PAD_UART4_RX_DATA__I2C1_SDA    0x4001b8b0
        >;
    };
};

I2C 的引脚被配置成 I2C 功能,驱动会直接使用 I2C API,不需要 GPIO API。

命名规范建议

虽然设备树对命名没有严格要求,但遵循一些规范会让代码更易读:

  1. GPIO 属性名 :使用 <功能>-gpios<功能>-gpio。比如 led-gpiosreset-gpio
  2. pinctrl 标签 :使用 pinctrl_<设备>。比如 pinctrl_aes_led
  3. pinctrl 节点名 :使用 <功能>_grp。比如 led_grp

调试技巧

当你修改了设备树后,怎么验证 GPIO 配置是否正确?

查看 sysfs

bash 复制代码
# 查看 GPIO 控制器
ls /sys/class/gpio/

# 查看特定 GPIO 的信息
cat /sys/kernel/debug/gpio

查看设备树

bash 复制代码
ls /proc/device-tree/
cat /proc/device-tree/imx_aes_led/led-gpio

使用 libgpio

你可以用 libgpio 工具来测试 GPIO:

bash 复制代码
# 导出 GPIO
echo 3 > /sys/class/gpio/export

# 设置方向
echo out > /sys/class/gpio/gpio3/direction

# 设置值
echo 1 > /sys/class/gpio/gpio3/value
echo 0 > /sys/class/gpio/gpio3/value

⚠️ 注意:在手动操作 GPIO 之前,确保它没有被驱动占用。否则你会看到 "Device or resource busy" 的错误。

小结

设备树中的 GPIO 配置就几个关键点:

  1. GPIO 控制器节点需要 gpio-controller#gpio-cells 属性
  2. 设备节点通过 <&gpioX N FLAGS> 格式引用 GPIO
  3. GPIO_ACTIVE_LOWGPIO_ACTIVE_HIGH 指定 GPIO 的极性
  4. 属性名可以是单数 (xxx-gpio) 或复数 (xxx-gpios) 形式
  5. 配置前一定要检查引脚冲突

说实话,GPIO 配置比 pinctrl 配置简单多了。你只需要知道是哪个 GPIO、极性是什么,就完了。pinctrl 那边的引脚复用和电气特性配置才是真正复杂的地方。


相关阅读

  1. 嵌入式Linux嵌入式Linux驱动开发:设备树驱动改造------从硬编码到设备树的实战之旅 - 相似度 100%
  2. 嵌入式Linux驱动开发pinctrl篇(1)------从寄存器到子系统:驱动演进之路 - 相似度 100%
  3. 通用GUI编程技术------图形渲染实战(四十五)------D3D12资源与堆管理:从上传到驻留 - 相似度 100%
相关推荐
Hani_971 小时前
Code Coverage系列(七)Code Coverage 原理详细说明
linux·代码覆盖率
Upsy-Daisy1 小时前
OpenClaw 源码解析(九):Channel 接入机制与消息路由流程
linux·运维·网络
x***r1511 小时前
linux安装 apache-tomcat-7.0.42.tar.gz 详细步骤(解压、配置、启动)
linux·tomcat·apache
ch_ziyuan1 小时前
安卓APP报毒自动化解决方案处理系统:动态包名+证书随机+360加固集成(后台源码)
android·运维·自动化
诸神缄默不语2 小时前
Linux shell脚本教程
linux·bash·shell·sh
技术不好的崎鸣同学2 小时前
Windows 命令提示符(CMD)内容补缺&输入输出重定向及管道
运维·windows
读创商闻2 小时前
解锁强劲算力,数聚红芯 AI 智算服务器甄选指南
运维·服务器·人工智能
小熊officer2 小时前
Debian与Linux与Ubuntu的关系
linux
feng_you_ying_li2 小时前
liunx之信号量与进程信号的基本介绍(1)
linux