结合设备树,了解gpio 部分控制方法
文章目录
一、参考文档:
基础相关:驱动相关知识、gpio 这一块,基本知识,常识需要了解的,可以参考之前 体系文章。
驱动GPIO-获取单个gpio描述符
使用C程序通过sysfs文件系统控制gpio
相关扩展知识内容:
命令行控制 - sysfs控制方法
在Linux驱动中使用gpio子系统
GPIO使用总结
二、学习目标-了解哪些控制方法
1.获取GPIO
在之前篇章已经了解过 驱动GPIO-获取单个gpio描述符,新版本gpio 体系中,最基本你需要先获取得到gpio 描述符吧。
gpiod_get;
gpiod_get_index;
gpiod_get_array;
devm_gpiod_get;
devm_gpiod_get_index;
devm_gpiod_get_array;
2.设置方向
gpiod_direction_input;
gpiod_direction_output;
3.读值、写值
gpiod_get_value;
gpiod_set_value;
4. 设为中断(如果必要)
request_irq(gpiod_to_irq(gpio_desc)...); //将gpio转为对应的irq,然后注册该irq的中断handler
5.释放GPIO
gpiod_put;
gpiod_put_array;
devm_gpiod_put;
devm_gpiod_put_array;
备注:这里先从学习目标上面列出需要掌握的内容。 说得高大上,其实就是理解几个api,然后会用。
三、必备基础 - sysfs 基础知识再熟悉
在不了解gpio 前提下,很多基础知识必须掌握的,一步一步方便理解。这里再次列出sysfs 里面的基础知识点,方便下文后续继续理解。
强烈建议参考之前文章: GPIO 控制和操作-使用命令通过sysfs文件系统控制GPIO
这里最核心的就是需要了解:基础文件active_low、direction、edge、value 描述 ,如下图:

这里再简要说明,方便理解,个人觉得,理解了这些,你才知道为什么需要我们gpio 相关的api
active_low:该文件用于设置GPIO引脚的电平触发方式。写入"1"意味着将该引脚的电平触发逻辑翻转(即,硬件上的高电平被视作逻辑上的低电平,硬件上的低电平被视作逻辑上的高电平)。写入"0"则表示使用正常的电平触发逻辑。direction:通过这个文件可以设置GPIO引脚是作为输入还是输出。写入"out"将GPIO配置为输出模式,写入"in"则将其配置为输入模式。edge:这个文件用于配置中断触发边缘,只对设置为输入模式的GPIO有效。可写入的值通常有:“none”(无触发),“rising”(上升沿触发),“falling”(下降沿触发),或"both"(上升沿和下降沿都触发)。uevent:这个文件允许用户向用户空间发送事件,通知关于GPIO引脚状态的变化。value:这个文件表示GPIO引脚的当前值。对于输出模式的GPIO,写入"1"或"0"可以改变引脚的电平状态。对于输入模式的GPIO,读取这个文件可以获取当前引脚的电平状态("1"表示高电平,"0"表示低电平)。
四、实验测试
定义设备树
位置: kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi ,两点:
- 在根节点下定义gpio 节点-my_gpio
- 在pin-ctrl 设备树节点中定义my_gpio
抛出问题-为什么定义一个自己的节点,需要在设备树根节点里面创建一个并在pin-ctrl 里面在创建一个节点呢?
简单来说,这两个步骤分别完成了两个不同的、但都必不可少的任务:
- 在根节点定义:声明一个GPIO资源。意思是“系统中存在这么一组GPIO引脚,它的控制权在这里定义”。
- 在pinctrl节点中定义:配置这些GPIO引脚的电气特性和复用状态。意思是“当我的设备要使用这些引脚时,请把它们设置为以下具体状态(如上下拉、驱动强度、复用为GPIO功能等)”。


为什么必须分两步?—— 总结
职责分离 (Separation of Concerns):
- GPIO控制器节点:描述硬件资源本身。内核需要知道有哪些GPIO端口、它们的地址在哪里,才能进行底层的读写操作。
- Pinctrl配置:描述资源的使用方式。一个引脚在被用作GPIO之前,必须通过SoC的复用寄存器将其功能切换到GPIO模式,并设置好电气特性。这是 pinctrl 子系统的核心工作。
资源复用 (Resource Sharing):
- 一个GPIO控制器(如 gpio1)提供了32个引脚资源。你的LED可能只用其中一个,其他人的按键可能用另一个。大家共享同一个资源库,但各自通过不同的 pinctrl 配置项来申请和使用自己需要的那部分。
动态配置 (Dynamic Configuration):
- 设备可以在不同状态下使用不同的引脚配置(通过 pinctrl-names 和 pinctrl-0, pinctrl-1 等实现)。但无论怎么配置,它操作的底层硬件控制器始终是 gpio1。
如下, 我把两处 根节点、pin-ctrl 定义放到一起,方便查看:
my_gpio:gpio1_a0{
compatible = "mygpio";
my-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&my_gpio_ctrl>;
};
mygpio{
my_gpio_ctrl:my-gpio-ctrl {
rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
实验代码
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
struct gpio_desc *mygpio1; // GPIO 描述符指针
int dir, value, irq;
// 平台设备初始化函数
// 平台设备初始化函数
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;
}
gpiod_direction_output(mygpio1, 0);
gpiod_set_value(mygpio1, 1);
dir = gpiod_get_direction(mygpio1);
if (dir == 0)
{
printk("dir is GPIO OUT\n");
}
else if (dir == 1)
{
printk("dir is GPIO IN \n");
}
value = gpiod_get_value(mygpio1);
printk("gpio value %d \n", value);
irq = gpiod_to_irq(mygpio1);
printk("gpio irq %d \n", irq);
// 释放GPIO描述符
gpiod_put(mygpio1);
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(mygpio1);
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 篇章,了解、使用 gpio 相关的api 使用方法。 其实还是围绕 高低电平设置、方向、读值、中断号获取等 基本API
- 实验简单,实际工作中会经常用到
191

被折叠的 条评论
为什么被折叠?



