出识:获取单个gpio 描述符,初步使用gpio函数
文章目录
- 前言
- 环境准备
- 一、结构体gpio_desc
- [二、gpiod_get 、 gpiod_get_index 、 gpiod_get_optional 、 piod_get_index_optional 方法的含义、区别和联系](#二、gpiod_get 、 gpiod_get_index 、 gpiod_get_optional 、 piod_get_index_optional 方法的含义、区别和联系)
- [三、配合设备树验证 获取gpio 函数](#三、配合设备树验证 获取gpio 函数)
- 总结
前言
通过获取gpio ,初步认识几个函数
- gpiod_get
- gpiod_get_index
- gpiod_get_optional
- piod_get_index_optional
环境准备
参考前文 将原理图中的一个引脚复用为gpio功能 的功能提供的环境,就用自己复用的gpio 配置来测试验证。
一、结构体gpio_desc
gpio_desc 结构体在 Linux 内核中代表一个 GPIO 引脚的描述符。它取代了旧的基于整数的 GPIO 编号方式,提供了更安全、更面向对象的 GPIO 管理方式。每个 GPIO 引脚在系统中都有一个对应的 gpio_desc 实例。
结构体定义
(基于 Linux 内核 5.x 版本,位置通常在 include/linux/gpio/consumer.h 和 drivers/gpio/gpiolib.h)
java
struct gpio_desc {
struct gpio_device *gdev; // 指向所属的GPIO设备
unsigned long flags; // 状态标志位
/* 关键标志位包括: */
#define FLAG_REQUESTED 0 // 引脚已被请求
#define FLAG_IS_OUT 1 // 引脚配置为输出
#define FLAG_EXPORT 2 // 引脚已导出到sysfs
#define FLAG_SYSFS 3 // 在sysfs中显示
#define FLAG_ACTIVE_LOW 6 // 低电平有效
#define FLAG_OPEN_DRAIN 7 // 开漏输出
#define FLAG_OPEN_SOURCE 8 // 开源输出
#define FLAG_USED_AS_IRQ 9 // 用作中断引脚
#define FLAG_IS_HOGGED 10 // 引脚被预分配
#define FLAG_TRANSITORY 11 // 短暂状态变化
/* 其他可能的标志... */
const char *label; // GPIO的标签/名称
struct irq_data *irqdata; // 中断相关数据
char *name; // GPIO名称
struct list_head node; // 用于链表管理
};
主要成员详解
结构体变量 struct gpio_device *gdev
- 作用:指向管理这个 GPIO 的 GPIO 设备
- 包含信息:GPIO 控制器的寄存器基地址、IRQ、支持的 GPIO 数量等
- 重要成员
java
struct gpio_chip *chip; // GPIO控制器芯片
unsigned base; // 这个设备的GPIO编号基值
int ngpio; // GPIO数量
flag 标志 unsigned long flags
- 作用:记录 GPIO 的当前状态和配置
- 重要标志:
java
FLAG_REQUESTED:表示 GPIO 已被 gpio_request() 或 gpiod_get() 请求
FLAG_IS_OUT:GPIO 被配置为输出模式
FLAG_ACTIVE_LOW:逻辑反转,读取/写入的值会被反转
FLAG_OPEN_DRAIN:开漏输出模式
FLAG_USED_AS_IRQ:GPIO 被用作中断源
标志 const char *label
- 作用:标识谁在使用这个 GPIO
- 示例:"led-red", "reset-button" 等
二、gpiod_get 、 gpiod_get_index 、 gpiod_get_optional 、 piod_get_index_optional 方法的含义、区别和联系
方法 gpiod_get
含义
这是最基础、最常用的函数。它通过指定设备指针和 GPIO 的功能标签(Consumer Label)来获取一个 GPIO 描述符。
"功能标签" 用于说明这个 GPIO 在驱动中的用途,例如 "reset"、"power-enable"、"led" 等。它在设备树中对应 gpios 属性的标签部分。
函数原型
java
struct gpiod_desc *gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags);
参数
-
dev: 使用这个 GPIO 的设备指针。
-
con_id: 功能标签(Consumer Label)。如果设备树中只定义了一个 GPIO,这个参数可以为 NULL。
-
flags: 指定 GPIO 的初始方向,是一个枚举值:
java
GPIOD_ASIS: 不改变方向,保持原样(通常需要后续调用 gpiod_direction_ 函数来设置)。
GPIOD_IN: 初始化为输入。
GPIOD_OUT_LOW: 初始化为输出,并设置输出低电平。
GPIOD_OUT_HIGH: 初始化为输出,并设置输出高电平。
示例
假设设备树中有:
java
my_device {
compatible = "my-gpio-device";
enable-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
};
驱动代码中:
java
struct gpiod_desc *enable_gpio, *reset_gpio;
enable_gpio = gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
if (IS_ERR(enable_gpio)) {
return PTR_ERR(enable_gpio); // 必须处理错误
}
reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset_gpio)) {
gpiod_put(enable_gpio); // 释放之前申请的GPIO
return PTR_ERR(reset_gpio);
}
特别注意:第二个参数 label ,其实是定义 gpio 的前缀。
方法 gpiod_get_index
含义
当某个功能标签(con_id)对应一组(多个)GPIO 时,使用这个函数来获取其中的某一个。它通过索引(index)来区分同组中的不同 GPIO。
函数原型
java
struct gpiod_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags);
参数
- index: 要获取的 GPIO 在该组中的索引(从 0 开始)。
示例
假设设备树中定义了一组 LED:
java
my_device {
compatible = "my-led-device";
led-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>, // Index 0
<&gpio1 11 GPIO_ACTIVE_HIGH>, // Index 1
<&gpio1 12 GPIO_ACTIVE_HIGH>; // Index 2
};
驱动代码中获取第二个 LED(索引 1):
java
struct gpiod_desc *led_gpio;
led_gpio = gpiod_get_index(dev, "led", 1, GPIOD_OUT_LOW);
if (IS_ERR(led_gpio)) {
return PTR_ERR(led_gpio);
}
方法 gpiod_get_optional 与 gpiod_get_index_optional
含义
这两个函数是上面两个函数的"可选"版本。它们的核心区别在于:如果请求的 GPIO 不存在,它们不会返回错误,而是返回 NULL。
函数原型
java
struct gpiod_desc *gpiod_get_optional(struct device *dev, const char *con_id, enum gpiod_flags flags);
struct gpiod_desc *gpiod_get_index_optional(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags);
示例
java
struct gpiod_desc *status_led_gpio;
// 这个状态灯是可选的,没有也没关系
status_led_gpio = gpiod_get_optional(dev, "status-led", GPIOD_OUT_LOW);
if (IS_ERR(status_led_gpio)) {
// 处理真正的错误(非不存在)
return PTR_ERR(status_led_gpio);
}
if (status_led_gpio) {
// 如果 GPIO 存在,就使用它
gpiod_set_value(status_led_gpio, 1);
} else {
// GPIO 不存在,驱动可以正常继续,只是没有这个功能
dev_info(dev, "No status LED available\n");
}
区别与联系总结
| 特性 | gpiod_get | gpiod_get_index | gpiod_get_optional | gpiod_get_index_optional |
|---|---|---|---|---|
| 核心用途 | 获取单个/默认GPIO | 获取同标签组中的特定GPIO | 可选地获取单个/默认GPIO | 可选地获取同标签组中的特定GPIO |
| 索引支持 | 不支持 | 支持 (index参数) | 不支持 | 支持 (index参数) |
| GPIO不存在时的行为 | 返回错误指针 (如 -ENOENT) | 返回错误指针 (如 -ENOENT) | 返回 NULL | 返回 NULL |
| 错误处理 | 必须用 IS_ERR() 检查 | 必须用 IS_ERR() 检查 | 检查 IS_ERR() 和 == NULL | 检查 IS_ERR() 和 == NULL |
| 适用场景 | 系统中必须存在的GPIO | 系统中必须存在的GPIO组 | 可选、可缺失的GPIO | 可选、可缺失的GPIO组 |
| 联系 | 是基础函数 | 是gpiod_get的带索引版本 | 是gpiod_get的可选版本 | 是gpiod_get_index的可选版本 |
函数家族关系
可以这样理解它们的联系:
基础对:
gpiod_get(dev, con_id, flags) 等价于 gpiod_get_index(dev, con_id, 0, flags)
可选对:
gpiod_get_optional(dev, con_id, flags) 等价于 gpiod_get_index_optional(dev, con_id, 0, flags)
索引扩展:
gpiod_get_index 扩展了 gpiod_get,使其能处理GPIO组。
gpiod_get_index_optional 扩展了 gpiod_get_optional,使其能处理GPIO组。
可选性扩展:
gpiod_get_optional 是 gpiod_get 的"宽松"版本。
gpiod_get_index_optional 是 gpiod_get_index 的"宽松"版本。
三、配合设备树验证 获取gpio 函数
程序源码
java
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
struct gpio_desc *mygpio1; // GPIO 描述符指针
struct gpio_desc *mygpio2; // GPIO 描述符指针
int num; // GPIO 编号
//平台设备初始化函数
// 平台设备初始化函数
static int my_platform_probe(struct platform_device *dev)
{
printk("This is my_platform_probe\n");
// 获取可选的GPIO描述符
mygpio1 = gpiod_get_optional(&dev->dev, "my", 0);
if (mygpio1 == NULL) {
printk("gpiod_get_optional error\n");
return -1;
}
num = desc_to_gpio(mygpio1);
printk("num is %d\n", num);
// 释放GPIO描述符
gpiod_put(mygpio1);
// 获取指定索引的GPIO描述符
mygpio2 = gpiod_get_index(&dev->dev, "my", 0, 0);
if (IS_ERR(mygpio2)) {
printk("gpiod_get_index error\n");
return -2;
}
num = desc_to_gpio(mygpio2);
printk("num is %d\n", num);
return 0;
}
// 平台设备的移除函数
static int my_platform_remove(struct platform_device *pdev)
{
printk(KERN_INFO "my_platform_remove: Removing platform device\n");
// 清理设备特定的操作
// ...
return 0;
}
const struct of_device_id of_match_table_id[] = {
{.compatible="mygpio"},
};
// 定义平台驱动结构体
static struct platform_driver my_platform_driver = {
.probe = my_platform_probe,
.remove = my_platform_remove,
.driver = {
.name = "my_platform_device",
.owner = THIS_MODULE,
.of_match_table = of_match_table_id,
},
};
// 模块初始化函数
static int __init my_platform_driver_init(void)
{
int ret;
// 注册平台驱动
ret = platform_driver_register(&my_platform_driver);
if (ret) {
printk(KERN_ERR "Failed to register platform driver\n");
return ret;
}
printk(KERN_INFO "my_platform_driver: Platform driver initialized\n");
return 0;
}
// 模块退出函数
static void __exit my_platform_driver_exit(void)
{
// 注销平台驱动
gpiod_put(mygpio2);
platform_driver_unregister(&my_platform_driver);
printk(KERN_INFO "my_platform_driver: Platform driver exited\n");
}
module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("fangchen");
编译-加载驱动-验证结果

得到的pin脚标号是32 ,我们测试的是引脚是GPIO1_A0,计算不就是:1*32+(A-1)*8+0=32
部分知识点和注意实现
- 这里的驱动测试程序其实就是之前验证平台驱动的部分代码,函数
.of_match_table = of_match_table_id,配置的 compatible="mygpio" 不就是 上一篇将原理图中的一个引脚复用为gpio功能 中的添加节点的属性compatible 配置嘛。
java
my_gpio:gpio1_a0{
compatible="mygpio"
my-gpios= <&gpio1 RK_PA0 GPIO_ACTIVE_HIGH>;
pinctrl-0 = <&my_gpio_ctrl>;
};
- 在方法
my_platform_probe回调中,去验证gpiod_get_optional和gpiod_get_index方法,
注意点my_platform_probe(struct platform_device中的参数是 平台设备platform_device,但是gpiod_get_optional和gpiod_get_index方法中的设备是device。 这个参数不正好是platform_device的成员变量struct device dev;嘛。 - 释放描述结构体指针:
gpiod_put,释放 结构体描述指针 - 注意点,带
optional的获取gpio描述符的,判断是否获取成功可以通过 是否是NULL 来判断,不包含optional的方法,通过IS_ERR函数方法来判断是否获取成功。
总结
- 这里在前面复用gpio引脚,配置环境的基础上,学习了解4个获取
gpio_desc结构体的方法知识点。 - 通过实验 , 结合设备数,再一次了解设备数配置和gpio获取的联系。
- 本人简单的知识,但是还是务必掌握。