
一般工作流程
头文件:linux/gpio/consumer.h
获取描述:
gpiod_get(最基础的函数)gpiod_count(计算有几个GPIO)gpiod_get_optional(以判NULL为依据)gpiod_get_index(索引到对应的GPIO)
方向:
gpiod_direction_outputgpiod_direction_input(struct gpio_desc *desc)
读写值:
gpiod_get_valuegpiod_set_value
整数型与描述符型的转换
Int 与 gpio_desc 的转换:
desc_to_gpiogpio_to_desc
一、GPIO函数介绍
gpiod开头:基于描述符gpio开头:基于整数型(最早的API)
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的工作模式:
返回值
- 成功:返回一个GPIO描述符(
struct gpio_desc指针),后续操作(如读写电平)都通过它进行。 - 失败:返回
NULL,表示找不到对应的GPIO或配置错误。
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
相比之前的 gpiod_get 函数,这三个新函数增加了两个特点:
- index参数:当设备树中某个GPIO属性包含多个引脚时,用这个参数指定具体要操作的哪个引脚(比如第0个、第1个)。
- 可选的optional后缀:函数名可能带或不带"optional()",功能相同(都是获取GPIO描述符),但失败时的返回值不同。
返回值区别:
- 带optional()的函数:如果找不到GPIO,直接返回
NULL。 - 不带optional的函数:如果找不到GPIO,返回一个特殊值(表示获取失败,而不是
NULL)。
3、释放 GPIO 描述符
作用: 释放之前申请的 GPIO 资源。
函数定义:
void gpiod_put(struct gpio_desc *desc);使用前需包含头文件:
#include <linux/gpio/consumer.h>参数说明:
desc:要释放的 GPIO 描述符(即之前通过gpiod_get()等函数获取的 GPIO 标识)。
功能描述: 当你不再需要某个 GPIO 引脚时,调用此函数释放它,以便系统可以重新分配该资源。
返回值: 不返回任何值(无意义返回)。
4、获取 GPIO 的方向函数
作用:
该函数的作用是查看某个GPIO引脚是输入还是输出。
函数定义:
int gpiod_get_direction(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当前是"接收信号"状态还是"发送信号"状态。得到结果后,你可以根据需要进行下一步操作,比如确认配置是否正确,或决定如何处理该引脚的信号。
5、配置 GPIO 的方向函数
INFO
函数原型:
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);2
头文件:
#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 配置为输出模式,并可以指定初始输出值。
6、读取 GPIO 的电平状态函数
INFO
函数原型:
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 的状态。
7、设置 GPIO 的电平状态函数
INFO
函数原型:
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 已经被正确地配置为输出模式。
8、GPIO 描述符转换为中断编号函数
INFO
函数原型:
int gpiod_to_irq(const struct gpio_desc *desc);头文件:
#include <linux/gpio/consumer.h>参数:
desc:指向 GPIO 描述符的指针。
函数功能:
gpiod_to_irq 函数用于将 GPIO 描述符转换为中断号。
返回值:
返回值为整型,表示中断号。
- 如果成功将 GPIO 描述符转换为中断号,返回值为大于等于 0 的中断号。
- 如果转换失败,返回值为负数,表示错误码。
该函数用于将给定 GPIO 描述符所代表的 GPIO 转换为对应的中断号。
二、实验
- 源代码下载:
git clone git@gitee.com:yangxuesong314/linux-driver.git(若之前已git拉取代码可以忽略) - 代码位于:
linux-driver/09.GPIO子系统/01.基于描述符的GPIO接口
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_GPIO表示将引脚的功能设置为 GPIO,&pcfg_pull_none表示引脚配置为无上下拉。pcfg_pull_uppcfg_pull_downpcfg_pull_nonepcfg_pull_none_drv_level_0
2、驱动
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/atomic.h>
static struct task_struct *blink_thread;
static struct gpio_desc *gpio;
static atomic_t keep_blinking = ATOMIC_INIT(1);
static int blink_thread_func(void *data) {
int value = 0;
while (atomic_read(&keep_blinking)) {
gpiod_set_value(gpio, value);
msleep(500);
value = !value;
gpiod_set_value(gpio, value);
printk(KERN_INFO "GPIO state: %d\n", value);
if (kthread_should_stop())
break;
}
return 0;
}
static int my_probe(struct platform_device *pdev) {
gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
if (IS_ERR(gpio)) {
dev_err(&pdev->dev, "Failed to get GPIO: %ld\n", PTR_ERR(gpio));
return PTR_ERR(gpio);
}
blink_thread = kthread_run(blink_thread_func, NULL, "gpio_blinker");
if (IS_ERR(blink_thread)) {
return PTR_ERR(blink_thread);
}
dev_info(&pdev->dev, "GPIO control initialized\n");
return 0;
}
static int my_remove(struct platform_device *pdev) {
atomic_set(&keep_blinking, 0);
if (blink_thread) {
kthread_stop(blink_thread);
blink_thread = NULL;
}
gpiod_set_value(gpio, 0);
dev_info(&pdev->dev, "GPIO control released\n");
return 0;
}
static const struct of_device_id my_of_match[] = {
{ .compatible = "my_platform_gpio" },
{}
};
MODULE_DEVICE_TABLE(of, my_of_match);
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_platform_driver",
.of_match_table = my_of_match,
},
};
module_platform_driver(my_driver);
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
Makefile 我们编译到内核里面
obj-y += gpiodesc.o实验:内核启动后对应的打印如下: