前言:gpio 三级节点获取和控制相关API函数
文章目录
- [一、 需求](#一、 需求)
- 二、参考文档
- [三、多级 GPIO 节点操作核心函数](#三、多级 GPIO 节点操作核心函数)
- 四、关键操作流程与技巧
- [五、GPIO 三级节点获取和控制相关API 总结](#五、GPIO 三级节点获取和控制相关API 总结)
-
- [主要 API](#主要 API)
- 关键API详解
- 三级节点操作辅助函数
- 总结
一、 需求
gpio 配置在三级目录下,如何获取和控制,相关API了解熟悉。
gpio 配置,并不是都是配置在二级目录下,而是配置在三级目录下,下面列举二级目录的配置和三级目录的配置,如下:
- 二级目录配置,gpio 配置在设备树根节点二级目录节点下

- 可能存在三级目录配置,gpio 配置在设备树根节点三级目录节点下,如下:

二、参考文档
三、多级 GPIO 节点操作核心函数
| 函数名 | 作用描述 | 所属头文件 | 关键参数说明 | 返回值说明 |
|---|---|---|---|---|
| device_get_child_node_count | 统计设备节点的直接子节点数量 | <linux/device.h> | dev: 父设备节点 | 子节点数量 (失败返回0) |
| device_get_next_child_node | 遍历获取下一个子节点的对象地址 | <linux/device.h> | dev: 父设备节点 child: 当前子节点 | 下一个子节点的 fwnode_handle (没有则返回NULL) |
| fwnode_get_named_gpiod | 从指定节点和属性名获取GPIO描述符 | <linux/gpio/consumer.h> | fwnode: 节点对象 propname: 属性名 index: 属性中GPIO的索引 dflags: 初始化配置 label: GPIO描述标签 | GPIO描述符指针 (失败返回NULL) |
四、关键操作流程与技巧
-
**遍历子节点:**通常先用
device_get_child_node_count了解子节点数量,然后结合device_get_next_child_node循环遍历所有子节点。 -
**获取GPIO描述符:
fwnode_get_named_gpiod功能强大,可通过 index 参数指定获取属性中的第几个GPIO引脚,dflags 参数能直接设置GPIO的初始状态(如输入、输出高/低电平)。 -
**GPIO配置与读写: 获取到
struct gpio_desc *后,可以使用gpiod_direction_input/output、gpiod_get/set_value等函数进行配置和读写。操作完成后,记得用gpiod_put释放资源。
设备树节点更改
位置:kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi,两点:
- 在根节点下定义
led1/led2两个二级节点,在二级节点下创建my-gpios三级节点。其中一个pinctrl,设置pinctrl-0的引用 - 在pin-ctrl 设备树节点中定义
my_gpio节点。
代码如下:
java
/ {
model = "Rockchip RK3568 EVB1 DDR4 V10 Board";
compatible = "rockchip,rk3568-evb1-ddr4-v10", "rockchip,rk3568";
....................................
my_gpio:gpio1_a0 {
compatible = "mygpio";
led1{
my-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_HIGH>, <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&my_gpio_ctrl>;
};
led2{
my-gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
};
};
};
java
&pinctrl {
.......................
mygpio{
my_gpio_ctrl:my-gpio-ctrl {
rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
..................................
};
注意点:
- 节点已经定义了
gpio1_a0,标签为my_gpio,为什么还要在&pinctrl里面定义关联的my_gpio_ctrl呢? - 根节点里面的设备树配置如何和
pin-ctrl里面关联的呢? 就是通过pinctrl-0属性值关联的。
驱动代码
java
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio.h>
unsigned int count;
struct fwnode_handle *child_fw = NULL;
struct gpio_desc *led[2];
int i = 0;
int num = 0;
// 平台设备初始化函数
static int my_platform_probe(struct platform_device *dev)
{
printk("This is my_platform_probe\n");
// 获取父设备节点的子设备节点数量
count = device_get_child_node_count(&dev->dev);
printk("count is %d\n", count);
for (i = 0; i < count; i++)
{
// 获取下一个子设备节点
child_fw = device_get_next_child_node(&dev->dev, child_fw);
if (child_fw)
{
// 获取子设备节点中名为 "my-gpios" 的 GPIO 描述
led[i] = fwnode_get_named_gpiod(child_fw, "my-gpios", 0, 0, "LED");
}
// 将 GPIO 描述转换为 GPIO 号
num = desc_to_gpio(led[i]);
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)
{
// 注销平台驱动
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");
实验结果

五、GPIO 三级节点获取和控制相关API 总结
在 Linux 内核中,操作 GPIO(通用输入输出)主要有两套 API:一套是基于描述符(descriptor-based) 的现代接口,另一套是基于整数(legacy) 的传统接口。
主要 API
下面这个表格汇总了基于描述符的 GPIO 三级节点获取和控制相关的主要 API:
| 类别 | 函数名 | 功能描述 |
|---|---|---|
| 获取GPIO描述符 gpiod_get() 基础函数,通过设备节点和名称获取GPIO描述符 | ||
| gpiod_get_index() | 获取属性中包含多个GPIO引脚时的指定索引引脚 | |
| gpiod_get_optional() | 可选获取,GPIO不存在时返回NULL而不报错 | |
| fwnode_get_named_gpiod() | 通过节点对象地址和属性名获取GPIO结构描述 | |
| 释放GPIO描述符 | gpiod_put() | 释放之前申请的GPIO资源 |
| 方向控制 | gpiod_direction_input() | 配置GPIO为输入模式 |
| gpiod_direction_output() | 配置GPIO为输出模式,并可指定初始输出值 | |
| 电平读写 | gpiod_get_value() | 读取GPIO的当前电平状态 |
| gpiod_set_value() | 设置GPIO的输出电平状态 | |
| 状态查询 | gpiod_get_direction() | 查询GPIO当前是输入还是输出模式 |
关键API详解
-
获取GPIO描述符:这是控制GPIO的第一步。
gpiod_get()是最基础的函数,你需要提供设备结构体指针、在设备树中定义的GPIO名称以及初始标志。如果设备树属性包含多个GPIO引脚,可以使用gpiod_get_index()指定索引。fwnode_get_named_gpiod()则更底层,直接通过节点对象地址和属性名获取。 -
配置GPIO方向:获取描述符后,需要设置方向。使用
gpiod_direction_input()设置为输入(如读取按键),使用gpiod_direction_output()设置为输出(如控制LED)并可同时设置初始电平。 -
电平读写与控制:配置好方向后,对于输入GPIO,使用
gpiod_get_value()读取当前电平;对于输出GPIO,使用gpiod_set_value()设置输出电平。 -
释放资源:当不再使用某个GPIO时,务必调用
gpiod_put()释放资源。
三级节点操作辅助函数
-
在设备树中,GPIO控制器可能包含多级子节点。以下辅助函数有助于遍历和操作这些节点:
-
device_get_next_child_node():用于遍历父设备节点下的所有子设备节点。传入父设备指针和当前子节点(首次调用可传入NULL),函数返回下一个子节点的指针,方便逐个处理。 -
device_get_child_node_count():用于统计指定设备节点下直接子节点的数量,有助于在遍历前了解子设备规模。
总结
- 核心内容,对于二级节点下的三级节点的gpio 属性相关获取的基本方法,讨论、验证 api.
- 获取到了三级节点下的gpio 描述符,剩下的就是递归、基本gpio 调用方法了。