当前位置: 首页 > news >正文

Linux驱动学习day21(GPIO子系统)

一、通用属性 

二、GPIO子系统的作用

管理GPIO,既能支持芯片体身的GPIO,也能支持扩展的GPIO。提供统一的、简便的访问接口,实现:输入、输出、中断。 

设备树中会有节点对应着GPIO控制器,里面含有cell属性,cell属性的意思是,以后如果使用到本组的GPIO,除组别之外还需要两个参数描述这个引脚。

1.1 复习基于设备树的led驱动程序 

#include <linux/module.h>     // 最基本模块宏
#include <linux/kernel.h>     // printk
#include <linux/init.h>       // __init/__exit
#include <linux/fs.h>         // register_chrdev 等
#include <linux/uaccess.h>    // copy_to_user, copy_from_user
#include <linux/types.h>      // dev_t, bool 等类型
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of_device.h>static int major = 0;
static struct class *xupt_led_class;
static struct gpio_desc *gpio;static const struct of_device_id led_based_gpio_dt_ids[] = {{ .compatible = "xupt,led_based_gpio", .data = (void *)NULL},{ /* sentinel */ }
};static ssize_t led_based_gpio_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{int val;val = gpiod_get_value(gpio);printk("%s %s %d get gpio value %d\n" , __FILE__ , __FUNCTION__ , __LINE__ , val);return 0;
}static ssize_t led_based_gpio_write (struct file *file, const char __user *buf, size_t size, loff_t * off)
{int val;char status;val = copy_from_user(&status, buf, size);// if(led_buf[0] == 1){//     gpiod_set_value(gpio, 1);//     printk("led_light OK\n");// }// else{//     gpiod_set_value(gpio, 0);//     printk("led_unlight\n");// }gpiod_set_value(gpio, status);printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);return 0;
}static int led_based_gpio_open (struct inode *inode, struct file *file)
{printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);gpiod_direction_output(gpio, 1); return 0;
}static int led_based_gpio_release (struct inode *inode, struct file *file)
{printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);return 0;
}static struct file_operations led_based_gpio_ops = {.owner     = THIS_MODULE,.read      = led_based_gpio_read,.write     = led_based_gpio_write,.open      = led_based_gpio_open,.release   = led_based_gpio_release,
};static int led_based_gpio_probe(struct platform_device *pdev)
{   /* register chrdev *//* class create */gpio = gpiod_get(&pdev->dev, "led" , 0);return 0;
}static int led_based_gpio_remove(struct platform_device *pdev)
{gpiod_put(gpio);device_destroy(xupt_led_class, MKDEV(major, 0));class_destroy(xupt_led_class);unregister_chrdev(major, "xupt_led");return 0;
}static struct platform_driver led_drv_based_gpio_driver = {.driver		= {.name	= "led_based_gpio",.of_match_table = led_based_gpio_dt_ids,},.probe		= led_based_gpio_probe,.remove		= led_based_gpio_remove,
};static int __init led_init(void)
{major = register_chrdev(0, "xupt_led", &led_based_gpio_ops);/* struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...) */xupt_led_class = class_create(THIS_MODULE, "xupt_led_class");if (IS_ERR(xupt_led_class)) {pr_err("Failed to create class: %ld\n", PTR_ERR(xupt_led_class));return PTR_ERR(xupt_led_class);}/* device create */device_create(xupt_led_class, NULL, MKDEV(major, 0), NULL, "xupt_led_dev"); /* /dev/xupt_led_dev *///p_led_opr = get_board_led_opr();return platform_driver_register(&led_drv_based_gpio_driver);
}/* entry function */
static void __exit led_exit(void)
{/* distroy void device_destroy(struct class *class, dev_t devt)*/gpiod_put(gpio);platform_driver_unregister(&led_drv_based_gpio_driver);device_destroy(xupt_led_class, MKDEV(major, 0));class_destroy(xupt_led_class);if(xupt_led_class){class_destroy(xupt_led_class);}unregister_chrdev(major, "xupt_led");return;
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

三、GPIO子系统的层次与数据结构

3.1 层次

 

构造gpio_chip结构体,构造好之后使用gpiochip_add_data()函数向上层gpiolib注册结构体,该函数会自动构造出gpio_device结构体

3.2 重要数据结构

3.2.1 gpio_chip结构体

 

3.2.2 gpio_device结构体

对于该GPIO控制器中的每个引脚,都会对应一个gpio_desc结构体。

 3.2.3 gpio_desc结构体

使用gpiod_get函数,可以获取gpio_desc结构体。在gpio_device结构体下面有gpio_desc结构体,系统会为该GPIO控制器中的每个引脚设置对应一个gpio_desc结构体。假设设备树是<&gpio3 10 xxx>,那么其构建过程:找到gpio控制器3,并且找到其中gpio_desc数组的第10个gpio_desc并返回。如下图所示。

偏移值的计算如下图所示(数组中指针的偏移计算): 

旧API(gpio_get)需要offset偏移值来得到gpio_desc结构体,比如<&gpio3 10 xxx>,该函数需要的是引脚编号,使用引脚编号来判断是属于哪一组gpio控制器。使用该编号减去gpio控制器中的base值,便可以知道是哪一个引脚。

四、设备树

设备树中的gpio控制器有两个属性是必须要的,一个是gpio-controller属性,表明其是一个GPIO控制器,一个是#gpio-cell属性,表明一个gpio引脚需要用几个整数来表示。

五、编写虚拟的GPIO控制器

5.1 设备树信息

​
virtual_led{compatible = "xupt,led_based_gpio";pinctrl-names = "default";pinctrl-0 = <&myled_pin>;led-gpios = <&gpio_virtual RK_PC1 GPIO_ACTIVE_LOW>;status   = "okay";};virtual_pincontroller {compatible = "xupt,virtual_pinctrl";myled_pin:myled_pin{functions = "gpio";groups     = "pin0";configs   = <0x11223344>;};i2cgrp:i2cgrp{functions = "i2c","i2c";groups = "pin0" , "pin1";configs = <0x11223344 0x55667788>;};};gpio_virtual: gpio_virtual{compatible = "xupt,virtual_gpio";gpio-controller;#gpio-cells = <2>;status = "okay";ngpios = <4>;};​

5.2 GPIO控制器代码 

主要部分还是设置gpio_chip内的成员函数,和属性。


#include <linux/module.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/slab.h>
#include <linux/regmap.h>static struct gpio_chip *g_gpio_chip;
static int g_gpio_val = 0;static const struct of_device_id virtual_gpio_dt_ids[] = {{ .compatible = "xupt,virtual_gpio", .data = (void *)NULL},{ /* sentinel */ }
};static int virtual_gpio_direction_input(struct gpio_chip *chip,unsigned offset)
{printk("set pins:%d direction as input\n" , offset);return 0;
}static int virtual_gpio_direction_output(struct gpio_chip *gc,unsigned int offset, int value)
{printk("set gpio:%d direction output as %s\n" , offset , value ? "high" : "low");return 0;
}static int virtual_gpio_get(struct gpio_chip *gc, unsigned int offset)
{int val;val = (g_gpio_val & (1 << offset)) ? 1 : 0;printk("get pins:%d ,value: %d\n" , offset , val);return 0;
}static void virtual_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{printk("set pins:%d as %d\n" , offset , value);if(value){g_gpio_val |= (1 << offset);}else{g_gpio_val &= ~(1 << offset);}}static int virtual_gpio_probe(struct platform_device *pdev)
{int ret , val;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/*1. alloc gpio_chip */g_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*g_gpio_chip), GFP_KERNEL);/*2. set gpio_chip *//*2.1 set function */g_gpio_chip->direction_input   = virtual_gpio_direction_input;g_gpio_chip->direction_output  = virtual_gpio_direction_output;g_gpio_chip->get               = virtual_gpio_get;g_gpio_chip->set               = virtual_gpio_set;/*2.2 set base and ngpio*/g_gpio_chip->base = -1;ret = of_property_read_u32(pdev->dev.of_node, "ngpios", &val);g_gpio_chip->ngpio = val;g_gpio_chip->label = pdev->name;g_gpio_chip->parent = &pdev->dev;/*3. register gpio_chip */ret = gpiochip_add_data(g_gpio_chip, NULL);return 0;
}static int virtual_gpio_remove(struct platform_device *pdev)
{gpiochip_remove(g_gpio_chip);return 0;
}static struct platform_driver virtual_gpio_driver = {.probe		= virtual_gpio_probe,.remove		= virtual_gpio_remove,.driver		= {.name	= "xupt_virtual_gpio",.of_match_table = virtual_gpio_dt_ids, }
};/* 1. 入口函数 */
static int __init virtual_gpio_init(void)
{	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 1.1 注册一个platform_driver */return platform_driver_register(&virtual_gpio_driver);
}/* 2. 出口函数 */
static void __exit virtual_gpio_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 2.1 反注册platform_driver */platform_driver_unregister(&virtual_gpio_driver);
}module_init(virtual_gpio_init);
module_exit(virtual_gpio_exit);MODULE_LICENSE("GPL");

5.3 结果显示 

echo的用法:

echo "test" > test.txt    # 覆盖写
echo "add" >> test.txt    # 追加

# 导出 GPIO 20
echo 20 > /sys/class/gpio/export

# 设置为输出
echo out > /sys/class/gpio/gpio20/direction

# 设置高电平
echo 1 > /sys/class/gpio/gpio20/value

 

我不知道为什么,我一开始使用.of_match_table = of_match_ptr(virtual_gpio_of_match),用来匹配,加载驱动的时候一直进不去probe函数,但是我换成.of_match_table = virtual_gpio_dt_ids,就可以直接匹配上了(头都大了,原来是这个问题,找了一下午,大坑!)。

六、GPIO子系统与pinctrl子系统的交互(GPIO使用pinctrl)

6.1 设备树信息

使用上一天创建的虚拟pinctrl子系统进行引脚复用,设置功能,结果如下图所示:实现了对GPIO引脚的复用,设置输入输出,设置引脚电平。

 

在设备树中可以建立pinctrl《--》gpio的关系(在gpio控制器的设备树中添加这样一项属性即可)。

分析pinctrl和gpio子系统的联系可以从gpiod_get函数入手,进入到最后会使用pinctrl_get_device_gpio_range()函数找出range结构体,根据range结构体使用gpio_to_pin函数就可以把gpio系统中的引脚编号转换成pinctrl子系统中的引脚编号。 

总结:函数调用gpiod_get,会导致gpio_chip中的request被调用,他会把gpio number转化为pinctrl中的pins,然后request会导致pinctrl_desc中的pinmux_ops中的gpio_set_direction或者request函数被调用。

6.2 编程思路

在GPIO子系统中提供gpio_chip->request函数,在pinctrl子系统中提供 pinctrl_desc->pinmux_ops中的gpio_set_direction或者request函数。

6.3 设备树文件

virtual_led{compatible = "xupt,led_based_gpio";led-gpios = <&gpio_virtual RK_PC1 GPIO_ACTIVE_LOW>;status   = "okay";};pinctrl_virtual: virtual_pincontroller {compatible = "xupt,virtual_pinctrl";i2cgrp:i2cgrp{functions = "i2c","i2c";groups = "pin0" , "pin1";configs = <0x11223344 0x55667788>;};};gpio_virtual: gpio_virtual{compatible = "xupt,virtual_gpio";gpio-controller;#gpio-cells = <2>;status = "okay";ngpios = <4>;gpio-rangs = <&pinctrl_virtual 0 0 4>;};

6.4 实验结果

 解释:从led_drv驱动中的gpiod_get函数进入-->gpiod_get_index(获得gpio_desc结构体从of_find_gpio函数)-->gpiod_request-->gpiod_request_commit(得到gpio_chip)-->chip->request函数-->gpiochip_generic_request-->pinctrl_gpio_request(获得range, pinctrl_get_device_gpio_range)-->gpio_to_pin(使用range建立gpio 引脚编号到 pinctrl子系统的pin编号)。

七、GPIO子系统的sysfs接口(可以简单的调试GPIO)

进入/sys/bus/gpio/device可以查看有多少组gpio控制器。

进入/sys/class/gpio可以看到每一组gpio控制器的最小引脚编号。

 

常用命令 

 

/sys/class/gpio/export : 将GPIO控制器从内核空间导出到用户空间,用户可以通过对导出的文件进行读写操作来操作gpio。

/sys/class/gpio/unexport:取消导出。

http://www.dtcms.com/a/272130.html

相关文章:

  • 【保姆级喂饭教程】GitLab创建用户规范,分支开发规范,提交日志规范
  • 标题:2025游戏反外挂终极指南:从DMA对抗到生态治理的全面防御体系
  • 【博文汇项目全维度测试报告:功能与自动化双轨验证】
  • 在指定conda 环境里安装 jupyter 和 python kernel的方法
  • 【LeetCode 热题 100】2. 两数相加——(解法二)迭代法
  • MyBatis-Plus 中使用 Wrapper 自定义 SQL
  • 专题:2025供应链数智化与效率提升报告|附100+份报告PDF、原数据表汇总下载
  • 【2025/07/10】GitHub 今日热门项目
  • JavaEE——线程池
  • 嵌入式开发:云端仿真赋能WS2812创意灯光教学
  • PyTorch随机擦除:提升模型抗遮挡能力
  • 【会员专享数据】2013-2024年我国省市县三级逐日SO₂数值数据(Shp/Excel格式)
  • Houdini 分布式解算效率瓶颈突破:渲染 101 云集群实战指南
  • Transformer江湖录 第一章:江湖前传 - 神经网络门派纷争
  • 微服务架构下某汽车APP电商模块订单服务自动化测试方案(Python蹭个场)
  • YOLO11 目标检测从安装到实战
  • [论文阅读]LLMZip: Lossless Text Compression using Large Language Models
  • qemu vcpu的创建过程
  • 智慧气象新范式:人工智能如何重构城市级气象服务生态?
  • AI技术通过智能缺陷检测正在深度重构多个行业的生产模式、质量管理体系和人才结构,其影响已超越单纯的技术升级,正在引发系统性变革。
  • Windows 11 安装过程中跳过微软账户创建本地账户
  • 大模型 Agent(智能体)技术简介
  • 静默的田野革命—人工智能重构农业生态的技术风暴与文明悖论
  • 蛋白质序列-omega参数计算算法解读
  • 「按键精灵安卓/ios辅助工具」动态验证码该怎么得到完整的图片
  • 20250710解决KickPi的K7的SDK编译异常:rk3576-android14.0-25250704.tar.gz【降低BUILD_JOBS】
  • 微软365 PDF导出功能存在本地文件包含漏洞,可泄露敏感服务器数据
  • 【办公类-107-01】20250710视频慢速与视频截图
  • 用 ngrok + SSH 实现公网远程控制电脑
  • Linux Vim 编辑器详解:从入门到进阶(含图示+插件推荐)