ESC
输入关键词搜索文章标题和内容

GPIO驱动开发:从gpiod接口到设备树配置的实战指南

本文由 linuxROS 整理发布,首发于 linuxros.cn,转载请注明出处。

GPIO驱动开发:从gpiod接口到设备树配置的实战指南

GPIO是嵌入式开发里打交道最多的东西——点灯、按键、复位芯片、使能电源,哪样都离不开它。Linux内核的GPIO子系统经历了几轮大改,旧的全局整数接口在6.8里已经标记deprecated,新式gpiod描述符接口才是正道。本文基于Linux 6.8内核,从子系统架构讲起,对比新旧API,再用LED和按键两个实例把gpiod接口和设备树配置串起来。

一、GPIO子系统架构

内核把GPIO操作抽象成四层结构,驱动开发者只需要跟最上面的接口层打交道:

flowchart TB A["用户空间"] --> B["sysfs /sys/class/gpio<br/>(已deprecated"] A --> C["libgpiod<br/>(推荐方式)"] B --> D["GPIO核心<br/>gpiolib"] C --> D D --> E["GPIO控制器驱动<br/>gpio_chip"] E --> F["硬件寄存<br/>GPIO控制"] style A fill:#E3F2FD style B fill:#FFEBEE style C fill:#E8F5E9 style D fill:#FFF8E1 style E fill:#F3E5F5 style F fill:#FFEBEE
  • 用户空间:sysfs方式。.8里默认禁用,libgpiod是新的标准用户态接。- **GPIO核心层(gpiolib):管理所有GPIO控制器的注册和GPIO描述符的分配,提供统一的gpiod API
  • **GPIO控制器驱动(gpio_chip):每个SoC的GPIO控制器实现自己的gpio_chip,注册到gpiolib
  • *硬件寄存:控制器驱动最终操作的就是GPIO控制器的寄存。

gpio_chip结构体关键回调

// linux/gpio/driver.h(简化)
struct gpio_chip {
    const char  *label;
    unsigned int ngpio;          // 该控制器管理的GPIO数量

    // 方向设置
    int (*direction_input)(struct gpio_chip *chip, unsigned offset);
    int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);

    // 读写。    int (*get)(struct gpio_chip *chip, unsigned offset);
    void (*set)(struct gpio_chip *chip, unsigned offset, int value);

    // 中断映射
    int (*to_irq)(struct gpio_chip *chip, unsigned offset);

    // 获取方向
    int (*get_direction)(struct gpio_chip *chip, unsigned offset);

    // 可睡眠版本(I2C/SPI扩展GPIO必须实现。    int (*get_value_cansleep)(struct gpio_chip *chip, unsigned offset);
    void (*set_value_cansleep)(struct gpio_chip *chip, unsigned offset, int value);

    struct gpio_device *gpiodev;  // 内部使用
};

SoC厂商的BSP工程师负责实现这些回调,驱动开发者用gpiod接口就行了,不需要直接碰gpio_chip。

二、新旧API对比

内核GPIO接口重构过一次。旧接口用全局整数编号标识GPIO,新接口用描述符指针。

旧接口(整数型,已deprecated。

#include <linux/gpio.h>  // 6.8中已标记deprecated

int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value);
int gpio_to_irq(unsigned gpio);

旧接口的问题很明显:一个裸整数没有任何类型安全,谁都能传个错值进去,编译器拦不住。

新接口(描述符型。

#include <linux/gpio/consumer.h>

struct gpio_desc *gpiod_get(struct device *dev, const char *con_id, ...);
void gpiod_put(struct gpio_desc *desc);
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
int gpiod_get_value(struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);
int gpiod_to_irq(struct gpio_desc *desc);

对比表格

对比维度 旧接口(整数型) 新接口(描述符型)
GPIO标识 全局整数编号 struct gpio_desc *指针
类型安全 无,裸整数可随意传递 有,编译期检查类型
方向管理 手动调用direction函数 获取时可通过标志位指定方向
设备树集成 需要of_get_gpio手动解析 自动从设备树绑定,con_id匹配
资源管理 手动request/free devm版本自动释放
6.8状态 标记deprecated 推荐使用
代码可读性 gpio_set_value(23, 1)看不出含义 gpiod_set_value(led_gpio, 1)意图明确

新项目直接用gpiod接口,别犹豫。

三、新式gpiod接口详解

头文件

#include <linux/gpio/consumer.h>   // gpiod核心接口
#include <linux/gpio/driver.h>     // gpio_chip(写控制器驱动才需要)

获取描述。

// 按con_id获取单个GPIO(设备树中gpio-xxx的xxx就是con_id。struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
                            enum gpiod_flags flags);

// 获取同一con_id下的第index个GPIO
struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id,
                                  unsigned int index, enum gpiod_flags flags);

// 批量获取同一con_id下的所有GPIO
struct gpio_descs *gpiod_get_array(struct device *dev, const char *con_id,
                                   enum gpiod_flags flags);

// 获取可选GPIO,设备树中没有也不报错,返回NULL
struct gpio_desc *gpiod_get_optional(struct device *dev, const char *con_id,
                                     enum gpiod_flags flags);

gpiod_get内部会根据con_id去设备树找gpio-xxx或xxx-gpios属性,找不到返回-ENOENT。

标志。

enum gpiod_flags {
    GPIOD_ASIS      = 0,    // 不改变当前方向和。    GPIOD_IN        = 1,    // 设置为输。    GPIOD_OUT_LOW   = 2,    // 设置为输出,初始低电。    GPIOD_OUT_HIGH  = 3,    // 设置为输出,初始高电。};

获取时直接指定标志位,省去了单独调direction函数的步骤。

释放

void gpiod_put(struct gpio_desc *desc);
void gpiod_put_array(struct gpio_descs *descs);

devm资源管理版本

// 自动释放,probe失败或remove时自动put
struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
                                 enum gpiod_flags flags);
struct gpio_desc *devm_gpiod_get_index(struct device *dev, const char *con_id,
                                       unsigned int index, enum gpiod_flags flags);
struct gpio_desc *devm_gpiod_get_optional(struct device *dev, const char *con_id,
                                          enum gpiod_flags flags);
struct gpio_descs *devm_gpiod_get_array(struct device *dev, const char *con_id,
                                        enum gpiod_flags flags);

// 手动提前释放(一般不需要,remove时自动释放)
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);

devm版本是首选,省得在错误处理路径和remove里手动释放。

方向设置

int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);

gpiod_direction_output的value参数是逻辑值,受active-low影响。

读写。

// 读值,返回0。(逻辑值)
int gpiod_get_value(struct gpio_desc *desc);

// 写值,0。(逻辑值)
void gpiod_set_value(struct gpio_desc *desc, int value);

// 可睡眠版本,用于I2C/SPI扩展GPIO
int gpiod_get_value_cansleep(struct gpio_desc *desc);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);

注意:如果GPIO控制器挂在I2C或SPI总线上(比如PCAL6416、MCP23008这类扩展芯片),读写操作可能睡眠,必须用_cansleep版本。用错版本会在运行时打印警告栈跟踪。

中断映射

int gpiod_to_irq(struct gpio_desc *desc);

返回对应的Linux IRQ编号,失败返回负数。拿到IRQ号后就可以用request_irq或devm_request_irq注册中断处理函数。

四、设备树GPIO配置

gpiod接口能自动从设备树获取GPIO信息,前提是DTS写对。

gpio-/gpios属性语。

// 单个GPIO
gpio-xxx = <&gpio_controller pin_number flags>;
// 。xxx-gpios = <&gpio_controller pin_number flags>;

// 多个GPIO
xxx-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>,
            <&gpio1 3 GPIO_ACTIVE_HIGH>;

gpiod_get查找规则。- gpiod_get(dev, "xxx", flags) 查找 gpio-xxx xxx-gpios
- gpiod_get_index(dev, "xxx", 1, flags) 查找 xxx-gpios 的第二个

来自 linuxros.cn · linuxROS

GPIO标志

// include/dt-bindings/gpio/gpio.h
#define GPIO_ACTIVE_HIGH    0    // 高电平有效(默认。#define GPIO_ACTIVE_LOW     1    // 低电平有。#define GPIO_OPEN_DRAIN     2    // 开漏输。#define GPIO_OPEN_SOURCE    4    // 开源输。#define GPIO_PULL_UP        8    // 内部上拉(部分SoC支持```

### active-low语义

这是最容易搞混的地方。active-low的意思是"逻辑1对应物理低电平。
| 设备树标志 | gpiod_set_value(desc, 1) | gpiod_set_value(desc, 0) |
|:-----------|:-------------------------|:-------------------------|
| GPIO_ACTIVE_HIGH | 输出高电平 | 输出低电平 |
| GPIO_ACTIVE_LOW | 输出低电平 | 输出高电平 |

举个例子:LED接在GPIO上,低电平点亮。设备树标`GPIO_ACTIVE_LOW`,代码里`gpiod_set_value(led, 1)`就是点亮,`gpiod_set_value(led, 0)`就是熄灭。逻辑值和物理电平的映射交给内核处理,驱动代码不用关心硬件接法。
### 完整DTS示例

```dts
// GPIO控制器节点(SoC厂商提供。&pinctrl {
    gpio0: gpio@ff200000 {
        compatible = "vendor,soc-gpio";
        reg = <0xff200000 0x100>;
        gpio-controller;
        #gpio-cells = <2>;    // pin_number + flags
        ngpios = <32>;
        interrupt-controller;
        #interrupt-cells = <2>;
    };
};

// LED设备节点
my_led {
    compatible = "linuxros,led-gpio";
    gpio-led = <&gpio0 17 GPIO_ACTIVE_LOW>;   // GPIO0_17,低电平点亮
    default-state = "off";
};

// 按键设备节点
my_button {
    compatible = "linuxros,button-gpio";
    gpio-button = <&gpio0 23 GPIO_ACTIVE_LOW>;  // GPIO0_23,按下为。    debounce-ms = <10>;
};

#gpio-cells = <2>表示引用这个控制器时需要两个参数:引脚编号和标志位。有些SoC需要3个cell(加一个bank编号),看具体binding文档。

五、LED控制驱动示例

用Platform驱动 + gpiod接口实现一个LED控制驱动,支持亮灭和闪烁。

// led_gpio.c - 基于gpiod的LED驱动(Linux 6.8。#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/kthread.h>

struct led_gpio_priv {
    struct gpio_desc *led;
    struct task_struct *blink_task;
    bool blinking;
};

/* 点亮LED */
static void led_on(struct led_gpio_priv *priv)
{
    gpiod_set_value(priv->led, 1);
}

/* 熄灭LED */
static void led_off(struct led_gpio_priv *priv)
{
    gpiod_set_value(priv->led, 0);
}

/* 闪烁线程 */
static int blink_thread(void *data)
{
    struct led_gpio_priv *priv = data;

    while (!kthread_should_stop()) {
        gpiod_set_value(priv->led, 1);
        msleep(500);
        gpiod_set_value(priv->led, 0);
        msleep(500);
    }
    return 0;
}

/* 启动闪烁 */
static void led_start_blink(struct led_gpio_priv *priv)
{
    if (priv->blinking)
        return;
    priv->blinking = true;
    priv->blink_task = kthread_run(blink_thread, priv, "led_blink");
    if (IS_ERR(priv->blink_task)) {
        priv->blinking = false;
        dev_err(priv->led->dev, "failed to create blink thread\n");
    }
}

/* 停止闪烁 */
static void led_stop_blink(struct led_gpio_priv *priv)
{
    if (!priv->blinking)
        return;
    kthread_stop(priv->blink_task);
    priv->blinking = false;
    led_off(priv);
}

static int led_gpio_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct led_gpio_priv *priv;

    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    /* 获取GPIO描述符,初始输出低电平(LED熄灭*/
    priv->led = devm_gpiod_get(dev, "led", GPIOD_OUT_LOW);
    if (IS_ERR(priv->led))
        return dev_err_probe(dev, PTR_ERR(priv->led),
                             "failed to get LED GPIO\n");

    platform_set_drvdata(pdev, priv);

    /* 启动时闪3次表示驱动加载成功 */
    int i;
    for (i = 0; i < 3; i++) {
        led_on(priv);
        msleep(200);
        led_off(priv);
        msleep(200);
    }

    dev_info(dev, "LED GPIO driver loaded\n");
    return 0;
}

static void led_gpio_remove(struct platform_device *pdev)
{
    struct led_gpio_priv *priv = platform_get_drvdata(pdev);

    led_stop_blink(priv);
    /* devm_gpiod_get分配的资源自动释放,不需要手动put */
    dev_info(&pdev->dev, "LED GPIO driver removed\n");
}

static const struct of_device_id led_gpio_of_match[] = {
    { .compatible = "linuxros,led-gpio" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, led_gpio_of_match);

static struct platform_driver led_gpio_driver = {
    .probe  = led_gpio_probe,
    .remove = led_gpio_remove,
    .driver = {
        .name = "led-gpio",
        .of_match_table = led_gpio_of_match,
    },
};
module_platform_driver(led_gpio_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("linuxros");
MODULE_DESCRIPTION("GPIO LED driver using gpiod interface");

关键点:

  • devm_gpiod_get(dev, "led", GPIOD_OUT_LOW)自动查找设备树中的gpio-led或led-gpios属。- GPIOD_OUT_LOW获取时直接设置为输出低电平,不用再调gpiod_direction_output
  • devm_err_probe。.8的标准做法,处理EPROBE_DEFER并统一日志格式
  • devm分配的资源在remove时自动释放,不需要手动gpiod_put

六、按键中断驱动示例

按键检测是GPIO中断最典型的应用。用gpiod获取引脚,gpiod_to_irq转换成IRQ号,再注册中断处理函数。

// button_gpio.c - 基于gpiod的按键中断驱动(Linux 6.8。#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/input.h>

struct button_gpio_priv {
    struct gpio_desc *button;
    int irq;
    struct input_dev *input;
    u32 debounce_ms;
    unsigned long last_jiffies;
};

/* 中断处理函数 */
static irqreturn_t button_isr(int irq, void *dev_id)
{
    struct button_gpio_priv *priv = dev_id;
    unsigned long now = jiffies;
    unsigned long diff = now - priv->last_jiffies;

    /* 软件消抖:两次中断间隔太短就忽略 */
    if (diff < msecs_to_jiffies(priv->debounce_ms))
        return IRQ_HANDLED;

    priv->last_jiffies = now;

    int val = gpiod_get_value(priv->button);

    /* 上报按键事件 */
    input_report_key(priv->input, KEY_POWER, val);
    input_sync(priv->input);

    dev_dbg(priv->input->dev.parent, "button %s\n", val ? "pressed" : "released");
    return IRQ_HANDLED;
}

static int button_gpio_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct button_gpio_priv *priv;
    int ret;

    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    /* 获取GPIO描述符,设置为输*/
    priv->button = devm_gpiod_get(dev, "button", GPIOD_IN);
    if (IS_ERR(priv->button))
        return dev_err_probe(dev, PTR_ERR(priv->button),
                             "failed to get button GPIO\n");

    /* 读取消抖时间,默。0ms */
    if (of_property_read_u32(dev->of_node, "debounce-ms", &priv->debounce_ms))
        priv->debounce_ms = 10;

    priv->last_jiffies = 0;

    /* GPIO转IRQ */
    priv->irq = gpiod_to_irq(priv->button);
    if (priv->irq < 0)
        return dev_err_probe(dev, priv->irq, "failed to map GPIO to IRQ\n");

    /* 注册input设备 */
    priv->input = devm_input_allocate_device(dev);
    if (!priv->input)
        return -ENOMEM;

    priv->input->name = "gpio-button";
    priv->input->dev.parent = dev;
    __set_bit(EV_KEY, priv->input->evbit);
    __set_bit(KEY_POWER, priv->input->keybit);

    ret = input_register_device(priv->input);
    if (ret)
        return dev_err_probe(dev, ret, "failed to register input device\n");

    /* 注册中断,双边沿触发 */
    ret = devm_request_irq(dev, priv->irq, button_isr,
                           IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                           "button-irq", priv);
    if (ret)
        return dev_err_probe(dev, ret, "failed to request IRQ\n");

    platform_set_drvdata(pdev, priv);

    dev_info(dev, "button GPIO driver loaded (IRQ=%d, debounce=%ums)\n",
             priv->irq, priv->debounce_ms);
    return 0;
}

static void button_gpio_remove(struct platform_device *pdev)
{
    /* devm自动释放所有资源:gpiod、irq、input_device */
    dev_info(&pdev->dev, "button GPIO driver removed\n");
}

static const struct of_device_id button_gpio_of_match[] = {
    { .compatible = "linuxros,button-gpio" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, button_gpio_of_match);

static struct platform_driver button_gpio_driver = {
    .probe  = button_gpio_probe,
    .remove = button_gpio_remove,
    .driver = {
        .name = "button-gpio",
        .of_match_table = button_gpio_of_match,
    },
};
module_platform_driver(button_gpio_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("linuxros");
MODULE_DESCRIPTION("GPIO button interrupt driver using gpiod interface");

关键点:

  • GPIOD_IN获取时直接设置为输入模式
  • gpiod_to_irq把GPIO描述符转成IRQ编号,gpiod和中断子系统靠它衔接
  • IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING双边沿触发,按下和松开都能捕获
  • 软件消抖用jiffies时间差实现,够用
  • 用input子系统上报按键事件,用户空间通过/dev/input/eventX读取

七、用户空间GPIO操作

旧方式:sysfs(已deprecated。

# 导出GPIO 17
echo 17 > /sys/class/gpio/export

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

# 读写。echo 1 > /sys/class/gpio/gpio17/value
cat /sys/class/gpio/gpio17/value

# 取消导出
echo 17 > /sys/class/gpio/unexport

6.8内核默认编译选项CONFIG_GPIO_SYSFS不再开启,这个接口基本废了。就算手动开启,内核也会在sysfs目录下打印deprecated警告。

新方式:libgpiod

libgpiod是GPIO sysfs的替代方案,直接通过字符设备/dev/gpiochipX和内核通信,不需要export/unexport。

安装

# Ubuntu/Debian
sudo apt install gpiod libgpiod-dev

# 从源码编。git clone https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git
cd libgpiod && mkdir build && cd build
meson setup .. && ninja && sudo ninja install

命令行工。

# 列出所有GPIO控制。gpiodetect
# 输出示例。# gpiochip0 [ff200000.gpio] (32 lines)
# gpiochip1 [ff210000.gpio] (16 lines)

# 查看某个控制器的所有引脚状态gpioinfo gpiochip0
# 输出示例。# line   0: unnamed       unused   input  active-high
# line  17: unnamed       "led"    output active-low  [used]

# 读取GPIO。gpioget gpiochip0 23
# 输出。 。1

# 设置GPIO值(输出高电平)
gpioset gpiochip0 17=1

# 监控GPIO事件(边沿中断)
gpiomon gpiochip0 23
# 按下按键时输出:
# event: FALLING EDGE offset: 23 timestamp: [1234567890.123456789]

C语言调用libgpiod

// gpiod_user.c - 用户空间GPIO操作示例
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    struct gpiod_line *line;
    struct gpiod_chip *chip;

    /* 打开GPIO控制*/
    chip = gpiod_chip_open_by_name("gpiochip0");
    if (!chip) {
        perror("open chip failed");
        return -1;
    }

    /* 获取GPIO 17 */
    line = gpiod_chip_get_line(chip, 17);
    if (!line) {
        perror("get line failed");
        gpiod_chip_close(chip);
        return -1;
    }

    /* 设置为输出,初始低电*/
    if (gpiod_line_request_output(line, "led-test", 0) < 0) {
        perror("request output failed");
        gpiod_chip_close(chip);
        return -1;
    }

    /* 闪烁5*/
    for (int i = 0; i < 5; i++) {
        gpiod_line_set_value(line, 1);
        usleep(500000);
        gpiod_line_set_value(line, 0);
        usleep(500000);
    }

    gpiod_line_release(line);
    gpiod_chip_close(chip);
    return 0;
}

编译:gcc -o gpiod_user gpiod_user.c -lgpiod

八、常见问题

*Q1:gpiod_get返回-ENOENT

驱动里写gpiod_get(dev, "led", flags),内核会去设备树找gpio-led或led-gpios属性。属性名对不上就返回-ENOENT。检查三点:con_id拼写、设备树属性名格式、节点是否和驱动匹配上。
Q2:gpiod_set_value不生效?

先确认方向是不是output——gpiod_get时用了GPIOD_IN或GPIOD_ASIS,引脚可能还停在输入模式。再检查active-low配置:设备树标了GPIO_ACTIVE_LOW时,gpiod_set_value(desc, 1)输出的是物理低电平,拿万用表量的时候别搞反了。最后确认GPIO有没有被其他驱动占用(gpioinfo查看used状态)。
Q3:sysfs方式不可用?

6.8默认不编译CONFIG_GPIO_SYSFS,/sys/class/gpio/目录不存在。装libgpiod,用gpioget/gpioset/gpiomon替代。如果非要sysfs,改内核配置CONFIG_GPIO_SYSFS=y重新编译,但不推荐。
*Q4:gpiod_get_value_cansleep警告

GPIO控制器挂在I2C或SPI总线上时,读写操作可能睡眠。在中断上下文或spinlock保护区内调用了gpiod_get_value而不是gpiod_get_value_cansleep,内核会打警告。把gpiod_get_value换成gpiod_get_value_cansleep,或者不在原子上下文里读GPIO。
Q5:devm_gpiod_get和gpiod_get怎么选?

无脑选devm版本。devm_gpiod_get在probe失败或remove时自动释放GPIO,省去手动gpiod_put的麻烦,也避免忘记释放导致GPIO一直被占用。只有一种情况用裸gpiod_get:GPIO的生命周期需要脱离设备生命周期独立管理,比如热插拔场景:

九、总结

gpiod接口速查看

操作 函数 说明
获取GPIO devm_gpiod_get(dev, con_id, flags) 首选,自动释放
可选获 devm_gpiod_get_optional(dev, con_id, flags) 设备树没有也不报
批量获取 devm_gpiod_get_array(dev, con_id, flags) 多个GPIO一次获
设输 gpiod_direction_input(desc) 或获取时用GPIOD_IN
设输 gpiod_direction_output(desc, val) 或获取时用GPIOD_OUT_LOW/HIGH
读 gpiod_get_value(desc) 返回逻辑。/1
写 gpiod_set_value(desc, val) 写逻辑值,受active-low影响
读。可睡眠 gpiod_get_value_cansleep(desc) I2C/SPI扩展GPIO必须
写。可睡眠 gpiod_set_value_cansleep(desc, val) I2C/SPI扩展GPIO必须
转中 gpiod_to_irq(desc) 返回IRQ编号
释放 devm_gpiod_put(dev, desc) 一般不需要,remove自动释放

设备树GPIO属性速查

属性格 gpiod_get的con_id 说明
gpio-led = <...> "led" 前缀gpio-
led-gpios = <...> "led" 后缀-gpios
reset-gpios = <...> "reset" 常见复位引脚
enable-gpios = <...> "enable" 常见使能引脚

标志位速查

标志 含义 典型场景
GPIOD_ASIS 不改变方 需要保留bootloader设置
GPIOD_IN 输入 按键、传感器
GPIOD_OUT_LOW 输出低电平 LED初始熄灭、复位引
GPIOD_OUT_HIGH 输出高电平 使能引脚、电源控制

本文首发于linuxros.cn,转载请注明出处。

版权声明

作者linuxROS
协议本作品采用 CC BY-NC-SA 4.0 许可协议:署名-非商业性使用-相同方式共享
关注欢迎关注微信公众号 linuxROS,获取更多机器人 / 嵌入式 / Linux 干货
返回首页