03、基于描述符的GPIO接口
一般工作流程:linux/gpio/consumer.h
头文件:
获取描述:
- gpiod_get(最基础的函数)
- gpiod_count(计算有几个GPIO)
- gpiod_get_optional (判NULL为依据)
- gpiod_get_index(索引到对应的GPIO)
方向:
- gpiod_direction_output
- gpiod_direction_input(struct gpio_desc *desc)
value值:
- gpiod_get_value
- gpiod_set_value
整数型 与 描述符型的转换:
Int 与 gpio_desc 对转换
- desc_to_gpio
- gpio_to_desc
一、GPIO函数介绍
gpiod 开头的 ---- 基于描述符
gpio 开头 ---- 基于整数型 (最早的API)
1.1、获取 GPIO 描述符函数
功能 这个函数用来在Linux系统中获取GPIO(通用输入输出)的描述符(即GPIO的"标识"),方便后续操作。
函数原型
struct gpio_desc *__must_check gpiod_get(struct device *dev,
const char *con_id,
enum gpiod_flags flags);
2
3
需包含的头文件
#include <linux/gpio/consumer.h>
参数说明
设备指针(dev)
- 需要操作GPIO的设备的结构体指针。比如,如果你在驱动某个LED模块,这里的设备指针就是这个LED模块的设备结构。
GPIO名称(con_id)
- 一个字符串,用于在设备树(Device Tree)中匹配具体的GPIO。
- 例如:"reset"、"power"或"led",这个名字需要和设备树中定义的GPIO名称一致。
模式标志(flags)
通过标志设置GPIO的工作模式:
GPIOD_INPUT
:设置为输入模式(如读取按键状态)。GPIOD_OUTPUT
:设置为输出模式(如控制LED亮灭)。GPIOD_ACTIVE_LOW
:表示低电平有效(如信号线反转)。GPIOD_OPEN_DRAIN
:开漏输出模式(需外接上拉电阻)。GPIOD_OPEN_SOURCE
:开源输出模式(需外接下拉电阻)。
返回值
- 成功:返回一个GPIO描述符(
struct gpio_desc
指针),后续操作(如读写电平)都通过它进行。 - 失败:返回
NULL
,表示找不到对应的GPIO或配置错误。
1.2、其他获取 GPIO 描述符函数
在 Linux 内核中还有另外三个同样是获取 GPIO 描述符资源的函数, 三个函数内容如下所示:
struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags
flags);
struct gpio_desc *gpiod_get_optional(struct device *dev, const char *con_id, enum gpiod_flags flags);
struct gpio_desc *gpiod_get_index_optional(struct device *dev, const char *con_id, unsigned int index, enum
gpiod_flags flags);
2
3
4
5
相比之前的gpiod_get函数,这三个新函数增加了两个特点:
- index参数:当设备树中某个GPIO属性包含多个引脚时,用这个参数指定具体要操作的哪个引脚(比如第0个、第1个)。
- 可选的optional后缀:函数名可能带或不带"optional()",功能相同(都是获取GPIO描述符),但失败时的返回值不同。
返回值区别:
- 带optional()的函数:如果找不到GPIO,直接返回
NULL
。 - 不带optional的函数:如果找不到GPIO,返回一个特殊值(表示获取失败,而不是
NULL
)。
1.3、释放 GPIO 描述符
作用: 释放之前申请的 GPIO 资源。
函数定义:
void gpiod_put(struct gpio_desc *desc);
使用前需包含头文件:
#include <linux/gpio/consumer.h>
参数说明:
desc
:要释放的 GPIO 描述符(即之前通过gpiod_get()
等函数获取的 GPIO 标识)。
功能描述: 当你不再需要某个 GPIO 引脚时,调用此函数释放它,以便系统可以重新分配该资源。
返回值: 不返回任何值(无意义返回)。
1.4、获取 GPIO 的方向函数:
作用:
该函数的作用是查看某个GPIO引脚是输入还是输出。
函数定义:
void gpiod_put(struct gpio_desc *desc);
使用前需包含头文件:
#include <linux/gpio/consumer.h>
参数说明:
要使用这个函数:
- 需要包含头文件:#include <linux/gpio/consumer.h>
- 函数需要传入GPIO的描述符(即该GPIO的标识信息)
调用方式: int result = gpiod_get_direction(你的_gpio描述符);
返回结果说明:
- 如果返回0,说明该GPIO设置为输入模式
- 如果返回1,说明该GPIO设置为输出模式
- 如果返回负数(比如-1),说明操作失败(可能GPIO未正确初始化)
简单来说,这个函数就像询问GPIO当前是"接收信号"状态还是"发送信号"状态。得到结果后,你可以根据需要进行下一步操作,比如确认配置是否正确,或决定如何处理该引脚的信号。
1.5、配置 GPIO 的方向函数:
::: 函数原型:
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
头文件:
#include <linux/gpio/consumer.h>
参数:
desc: 指向 GPIO 描述符的指针。
value(仅适用于 gpiod_direction_output) : 初始输出值, 可以是 0 或 1。
函数功能:
gpiod_direction_input 函数用于配置 GPIO 的方向为输入。
gpiod_direction_output 函数用于配置 GPIO 的方向为输出, 并可指定初始输出值。
返回值:
返回值为整型, 表示配置 GPIO 方向的结果。
如果成功配置 GPIO 方向, 返回值为 0。
如果配置失败, 返回值为负数, 表示错误码。 :::
这两个函数用于配置 GPIO 的方向。 gpiod_direction_input 将给定的 GPIO 描述符所代表的GPIO 配置为输入模式。 而 gpiod_direction_output 将 GPIO 配置为输出模式, 并可以指定初始输出值。
1.6、读取 GPIO 的电平状态函数
::: 函数原型:
int gpiod_get_value(const struct gpio_desc *desc);
头文件:
#include <linux/gpio/consumer.h>
参数:
desc: 指向 GPIO 描述符的指针。
函数功能:
gpiod_get_value 函数用于读取 GPIO 的电平状态。
返回值:
返回值为整型, 表示 GPIO 的电平状态。
如果成功读取到 GPIO 的电平状态, 返回值为 0 或 1, 分别表示低电平和高电平。
如果读取失败, 返回值为负数, 表示错误码。 :::
该函数用于读取给定 GPIO 描述符所代表的 GPIO 的电平状态。 通过调用该函数, 可以获取 GPIO 当前的电平状态, 以便进一步处理和判断 GPIO 的状态。
1.7、读取 GPIO 的电平状态函数
::: 函数原型:
void gpiod_set_value(struct gpio_desc *desc, int value);
头文件:
#include <linux/gpio/consumer.h>
参数:
desc: 指向 GPIO 描述符的指针。
value: 要设置的 GPIO 的电平状态, 可以是 0 或 1。
函数功能:
gpiod_set_value 函数用于设置 GPIO 的电平状态。
返回值: 无(void)
该函数用于设置给定 GPIO 描述符所代表的 GPIO 的电平状态。 通过调用该函数, 您可以将 GPIO设置为特定的电平状态, 以便控制外部设备或执行其他相关操作。 :::
value 参数表示要设置的 GPIO 的电平状态, 可以是 0 或 1。 当 value 为 0 时, 表示设置GPIO 为低电平; 当 value 为 1 时, 表示设置 GPIO 为高电平。
该函数没有返回值, 因为它只是执行设置操作而不需要返回任何结果。
在使用该函数之前, 需要确保 GPIO 已经被正确地配置为输出模式。
1.8、GPIO 描述符转换为中断编号函数
::: 函数原型:
int gpiod_to_irq(const struct gpio_desc *desc);
头文件:
#include <linux/gpio/consumer.h>
参数:
desc: 指向 GPIO 描述符的指针。
函数功能:
gpiod_to_irq 函数用于将 GPIO 描述符转换为中断号。
返回值:
返回值为整型, 表示中断号。
如果成功将 GPIO 描述符转换为中断号, 返回值为大于等于 0 的中断号。
如果转换失败, 返回值为负数, 表示错误码。 :::
该函数用于将给定 GPIO 描述符所代表的 GPIO 转换为对应的中断号。
二、实验
2.1、设备树
my_gpio:gpiol_a0 {
compatible = "mygpio";
my-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_HIGH>,<&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&mygpio_ctrl>;
};
2
3
4
5
6
compatible: 用于指定设备的兼容性字符串, 与驱动程序中的值相匹配。
my-gpios: 指定了与该设备相关联的 GPIO。
- &gpio1 表示 GPIO 控制器的句柄(handle)
- RK_PA0 是与该 GPIO 相关的资源描述符(resource specifier)
- GPIO_ACTIVE_HIGH 表示 GPIO 的默认电平为高电平。
pinctrl-names 和 pinctrl-0: 用于指定引脚控制器(pinctrl) 的配置。 pinctrl-names 表示引脚控制器配置的名称, 这里为 "default"。 pinctrl-0 指定了与该配置相关联的引脚控制器句柄, 这里为 &mygpio_ctrl。
mygpio {
mygpio_ctrl: my-gpio-ctrl {
rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
2
3
4
5
在第三行的内容中,
1 表示引脚索引(x :哪个大组GPIO1)
RK_PA0: (GPIOx_A0)表示资源描述符, 用于标识与该引脚相关联的物理资源, 表示引脚所属的功能组,
RK _FUNC_GPI0 表示将引脚的功能设置为 GPIO,
&pcfg_pull_none 表示引脚配置为无上下拉。
- pcfg_pull_up
- pcfg_pull_down
- pcfg_pull_none
- pcfg_pull_none_drv_level_0
2.2、驱动
#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");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
Makefile
export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += gpio_api.o #platform_device.c对应.o文件的名称。名称要保持一致。
KDIR :=/kernel #内核源码所在虚拟机ubuntu的实际路径
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules #make操作
clean:
make -C $(KDIR) M=$(PWD) clean #make clean操作
2
3
4
5
6
7
8
9