[Linux_IMX6ULL驱动开发]-GPIO子系统和Pinctrl子系统

目录

Pinctrl子系统的概念

GPIO子系统的概念

定义自己的GPIO节点

GPIO子系统的函数

引脚号的确定

基于GPIO子系统的驱动程序

驱动程序

设备树修改


之前我们进行驱动开发的时候,对于硬件的操作是依赖于ioremap对寄存器的物理地址进行映射,以此来达到对操控硬件的目的,但是在实际的开发中,如果对引脚一个个的进行物理地址的映射并不现实,我们在这里使用Pinctrl子系统来完成对引脚的配置以及复用等。pinctrl子系统由BSP工程师来完成,驱动工程师使用这个子系统

Pinctrl子系统的概念

pinctrl中引入两个概念,一个是client device 一个是 controller,client device表示客户,也就是子系统的使用者,在这里主要指定状态,也就是引脚在不同的状态下使用的不同配置,而controller则主要是对应状态的具体详细配置

假如此时引脚的状态为“default”,那么就是对应pinctrl-0这个节点,那么引脚的功能(function)设置为uart0,groups设置为“u0rxtx”,“u0rtscts”。

client device的格式一般是一样的,但是controller这里的格式,不同的开发板有不同的格式,格式虽然不同,但是概念是一样的


GPIO子系统的概念

当我们使用pinctrl子系统设置了引脚为GPIO模式的时候,我们就可以使用GPIO子系统来设置引脚方向(输入还是输出)、读值──获得电平状态,写 值──输出高低电平

在之前,我们一般是使用寄存器操控来控制引脚,而在这里我们使用GPIO子系统,完全是使用API来控制引脚,而不是设计寄存器操作,这样,不管是使用什么板都是兼容的

我们可以在设备树中指定GPIO引脚,然后在驱动程序中,直接使用对应的函数来获得 GPIO、设置 GPIO 方向、读 取/设置 GPIO 值。

几乎所有芯片的GPIO都是分组的,我们需要在自己所需要操控的GPIO分组中,找到自己所需要的。如下就是一个GPIO Controller,也就是一个GPIO分组,为GPIO分组1.一般来说,GPIO Controller是由厂家来实现的,我们只负责使用

我们暂时关注这两个属性 。“gpio-controller”表示这个节点是一个 GPIO Controller,它下面有很多引脚。 “#gpio-cells = ”表示这个控制器下每一个引脚要用 2 个 32 位的数 (cell)来描述。至于为什么需要使用两个cell来表示,一般第一个cell(第一个32位数)表示哪个引脚,第二个表示有效电平。

GPIO_ACTIVE_HIGH : 高电平有效

GPIO_ACTIVE_LOW : 低电平有效

定义自己的GPIO节点

定义 GPIO Controller 是芯片厂家的事,那么我们该如何定义自己的节点呢。

如下图所示,我在的这个节点中,通过gpios = 多少来设置我在这个节点当中使用的是哪个GPIO节点,一般的使用格式为:GPIO组 引脚 什么电平激活 , 下图中的表示为,此节点使用的是GPIO5组当中的3引脚,当电平为低电平的时候激活

如下的设置为设备树当中的例子,只需要看 gpios = 这个属性即可

一般来说,如果我们要定义自己的节点,那么格式如下

compatible属性,作用是与驱动代码中的of_match_table进行匹配,当匹配成功之后,驱动代码才会调用probe,获取到引脚硬件信息。

pinctrl-name和pinctrl-0都是pinctrl的信息,用于设置引脚在不同状态下(pinctrl-name决定)的引脚复用和状态设置(开漏、上拉等)(pinctrl-0.....对应的状态决定)

led-gpio用来决定引脚信息,这个属性的节点为name-gpio,如下代码中可知,name为led,则在驱动代码中可以通过name来获得引脚信息

mynode{compatible = ;pinctrl-name = ;pinctrl-0 = ;led-gpio = ;};

GPIO子系统的函数

当我们通过pinctrl子系统定义完了引脚信息后,我们应该如何在驱动代码中获取设备树中对应的引脚信息呢,我们使用如下这些GPIO 子系统的接口函数。

GPIO 子系统有两套接口:基于描述符的(descriptor-based)、老的(legacy)通常情况下,我们使用前者,也就是基于描述符的。前者使用起来更为简单,后者还要基于引脚号。前者的函数都有前缀“gpiod_”,后者的函数都有前缀“gpio_”。

使用这些函数需要包含如下头文件

#include <linux/gpio/consumer.h>    // descriptor-based
#include <linux/gpio.h>    // legacy

常用函数如下

descriptor-based

legacy

获得 GPIO

 gpiod_get
gpio_request
gpiod_get_index
 gpiod_get_array
 gpio_request_array
 devm_gpiod_get
devm_gpiod_get_index
devm_gpiod_get_array

设置方向

gpiod_direction_input
gpio_direction_input
gpiod_direction_output
gpio_direction_output

读值、写值

gpiod_get_value
gpio_get_value
gpiod_set_value
gpio_set_value

释放 GPIO

gpio_free

gpio_free

gpiod_put
gpio_free_array
gpiod_put_array
devm_gpiod_put
devm_gpiod_put_array

我们假设在设备树中存在如下节点

foo_device {
compatible = "acme,foo";
...
led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */<&gpio 16 GPIO_ACTIVE_HIGH>, /* green */<&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>; };

 那我们我们该如何在驱动程序中获得如下的节点信息呢,使用如下方法

我们看red,第二个参数“led”也就是name,led-gpios,格式为<name>-gpio,那么led就是name,0表示获得第一个name为led的参数信息,GPIOD_OUT_HIGH表示设置为激活状态,但是并不一定是高电平,它的意思是逻辑输出,是一个逻辑值。如果引脚低电平点亮,则为0,反之则为1.

struct gpio_desc *red, *green, *blue, *power;red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH); 
green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH); 
blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH); 
power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);

引脚号的确定

如果我们需要使用旧的GPIO函数才操控引脚,那么我们就需要知道引脚号为多少,才能够操控对应的引脚,那么引脚号一般是怎么计算的呢。

一般来说,假如GPIO1的基引脚号为base,那么GPIO0的第n号引脚号为base+n

进入开发板目录:/sys/class/gpio ,可以看到如下GPIO分组

它表示GPIO的分组,我们进入gpioip128,得到如下

查看label属性

通过如上的地址“20ac000”这个地址通过查阅芯片手册或者设备树即可知道引脚号为128的是哪个GPIO组的基引脚号

如下为imx6ull.dtsi中的信息,可知20ac000这个地址表示为gpio5组的基引脚号,那么gpioip128就表示gpio5组了,那么可知,如果我们需要使用GPIO5的引脚3,那么此引脚的引脚号则为 128+3 = 131

在这里我们可以做一个测试,已知按键的电路图如下

可知它使用的是GPIO4的引脚14,我们可推算GPIO4组的基引脚号为96,那么引脚14就是96+14=110,引脚号110表示这个引脚,键入如下命令

echo 110 > /sys/class/gpio/export 
echo in > /sys/class/gpio/gpio110/direction 
cat /sys/class/gpio/gpio110/value 
echo 110 > /sys/class/gpio/unexport

echo 110 > /sys/class/gpio/export 这个命令将编号为110的GPIO引脚导出为用户空间的设备,使其可以在用户空间进行操作。

echo in > /sys/class/gpio/gpio110/direction 这个命令设置编号为110的GPIO引脚的方向为输入("in")。

cat /sys/class/gpio/gpio110/value 这个命令读取编号为110的GPIO引脚的当前值。如果引脚连接的是一个按钮或传感器,它会输出0或1,表示引脚的状态是低电平或高电平。

echo 110 > /sys/class/gpio/unexport 这个命令将编号为110的GPIO引脚取消导出,使其不再在用户空间可用。

通过如上命令可以在用户空间读取按键值,但是如果此引脚被使用,则会出现如下提醒


基于GPIO子系统的驱动程序

驱动程序

如下函数为probe函数,只有当设备树与驱动成功配对后,才会调用此函数,主要的目的是在驱动设备中获取设备树当中的引脚等信息

如上两个函数主要为驱动和设备树节点对应成功后以及卸载时所需要设置的步骤,往下实现file_operation结构体中的相关提供给应用层的函数(这里实现open和write举例)

gpiod_set_value第二个参数传入的是逻辑值,只要传入的是1,那么它一定是激活引脚的电平,这个在dts的属性中可以设置引脚是低电平有效还是高电平有效,子系统会达到控制效果。
#include <linux/module.h>
#include <linux/platform_device.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
static struct gpio_desc *led_gpio;/* 3. 实现对应的open/read/write等函数,填入file_operations结构�?                  */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int err;char status;//struct inode *inode = file_inode(file);//int minor = iminor(inode);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = copy_from_user(&status, buf, 1);/* 根据次设备号和status控制LED */gpiod_set_value(led_gpio, status);return 1;
}static int led_drv_open (struct inode *node, struct file *file)
{//int minor = iminor(node);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 根据次设备号初始化LED */gpiod_direction_output(led_gpio, 0);return 0;
}static int led_drv_close (struct inode *node, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/* 定义自己的file_operations结构�?                                             */
static struct file_operations led_drv = {.owner	 = THIS_MODULE,.open    = led_drv_open,.read    = led_drv_read,.write   = led_drv_write,.release = led_drv_close,
};/* 4. 从platform_device获得GPIO*    把file_operations结构体告诉内核:注册驱动程序*/
static int chip_demo_gpio_probe(struct platform_device *pdev)
{//int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 4.1 设备树中定义�? led-gpios=<...>;	*/led_gpio = gpiod_get(&pdev->dev, "led", 0);if (IS_ERR(led_gpio)) {dev_err(&pdev->dev, "Failed to get GPIO for led\n");return PTR_ERR(led_gpio);}/* 4.2 注册file_operations 	*/major = register_chrdev(0, "100ask_led", &led_drv);  led_class = class_create(THIS_MODULE, "100ask_led_class");if (IS_ERR(led_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "led");gpiod_put(led_gpio);return PTR_ERR(led_class);}device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0); /* /dev/100ask_led0 */return 0;}static int chip_demo_gpio_remove(struct platform_device *pdev)
{device_destroy(led_class, MKDEV(major, 0));class_destroy(led_class);unregister_chrdev(major, "100ask_led");gpiod_put(led_gpio);return 0;
}static const struct of_device_id ask100_leds[] = {{ .compatible = "100ask,leddrv" },{ },
};/* 1. 定义platform_driver */
static struct platform_driver chip_demo_gpio_driver = {.probe      = chip_demo_gpio_probe,.remove     = chip_demo_gpio_remove,.driver     = {.name   = "100ask_led",.of_match_table = ask100_leds,},
};/* 2. 在入口函数注册platform_driver */
static int __init led_init(void)
{int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);err = platform_driver_register(&chip_demo_gpio_driver); return err;
}/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函�? *     卸载platform_driver*/
static void __exit led_exit(void)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);platform_driver_unregister(&chip_demo_gpio_driver);
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");

设备树修改

imx6ull芯片,有对应的设备树生成的工具,使用 “Pins_Tool_for_i.MX_Processors_v6_x64.exe”可以生成对应的设备树文件。

把自动生成的信息放在设备树 arch/arm/boot/dts/100ask_imx6ull-14x14.dts

然后,在此设备树文件中定义自己的新节点,添加在根节点下

由于GPIO5 3 已经被用来做CPU指示灯,需要再设备树中关闭它

由于都是使用pinctrl来控制,我们在dts中搜索 MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03

在搜索框中的节点名,既可以反推找到使用该节点的节点

设置完后,在内核目录下使用 make dtbs 编译,设备树文件拷贝到网络文件系统中,更新设备树,安装编译后的驱动程序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/10032.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【020】基于JavaWeb实现的批报管理系统

项目介绍 基于jspservlet实现的批报管理系统采用B/S架构,该项目设计了一个角色管理员&#xff0c;管理员实现了我的案件、查询统计、项目维护等三大功能模块 技术栈 开发工具&#xff1a;Idea2020.3 运行环境&#xff1a;jdk1.8tomcat9.0mysql5.7 服务端技术&#xff1a;j…

麦肯锡专访 Mistral AI CEO:三五年后的工作,要比现在更有意义

【编者按】总部位于巴黎的人工智能初创公司 Mistral AI 成立仅一年&#xff0c;就被誉为现有大模型巨头的有力挑战者。 今年 2 月&#xff0c;Mistral AI 正式发布了旗舰级大模型 Mistral Large&#xff0c;直接对标 OpenAI 的 GPT-4&#xff1b;几周前&#xff0c;Mistral AI…

大数据Scala教程从入门到精通第一篇:Scala基本介绍

一&#xff1a;Scala基本介绍 1&#xff1a;Scala相当于Java的增强版和拓展 Scala 基于 JVM和 Java 完全兼容。同样具有跨平台、可移植性好、方便的垃圾回收等特性 Scala 比 Java 更加面向对象&#xff0c;可以说完全面对对象。 Scala 是一门函数式编程语言&#xff0c;Java就…

Element快速上手!

Element是饿了么公司前端团队开发的一套基于Vue的组件库&#xff0c;用于快速构建网页~ 官网链接&#xff1a; Element - The worlds most popular Vue UI frameworkElement&#xff0c;一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库https://element.elem…

UML之用例图

1.用例图 用例图指参与者&#xff0c;用例&#xff0c;边界以及它们之间的关系构成的用于描述系统功能的视图。说明是谁要使用系统&#xff0c;以及可以使用该系统可以做些什么。展示了一个外部用户能够观察到的系统功能模型图 2.用例图的元素 &#xff08;1&#xff09;参与…

Redis 实战之监视器

监视器 成为监视器向监视器发送命令信息总结 成为监视器 发送MONITOR 命令可以让一个普通客户端变为一个监视器&#xff0c; 该命令的实现原理可以用以下伪代码来实现&#xff1a; def MONITOR():# 打开客户端的监视器标志client.flags | REDIS_MONITOR# 将客户端添加到服务器…

解压.cpio.gz文件的步骤

cpio是一种用于创建和恢复文件系统映像的工具&#xff0c;而gz则是gzip压缩工具的扩展名。当你遇到一个名为.cpio.gz的文件时&#xff0c;这意味着这个文件已经被cpio和gzip联合压缩过。解压这样的文件需要两个步骤&#xff1a;首先解压缩&#xff0c;然后解cpio。 以下是解压.…

vtkvolume与vtkactor的区别与联系

参考&#xff1a; https://blog.csdn.net/qq_39966730/article/details/134926850 vtkVolume是vtkProp3D的子类&#xff0c;用于体渲染。类似于vtkActor(用于几何渲染)&#xff0c;vtkVolume保存转换信息&#xff0c;如位置&#xff0c;方向和比例&#xff0c;以及指向映射器和…

C++初阶之stack,queue,priority_queue的使用和模拟以及仿函数的创建和使用

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂 目录 一.stack,queue,priority_queue简介以及代码模拟 1.1 stack …

js设计模式--观察者模式

概述 观察者模式用于在对象之间建立 一对多 的依赖关系&#xff0c;当一个对象状态发生变化时&#xff0c;它的所有依赖者&#xff08;观察者&#xff09;都会收到通知并自动更新。在js中&#xff0c;观察者模式通常由两个角色组成&#xff1a;主体&#xff08;Subject&#x…

uniapp 版本检查更新

总体来说uniapp的跨平台还是很不错的&#xff0c;虽然里面各种坑要去踩&#xff0c;但是踩坑也是开发人员的必修课和成长路。 这不&#xff0c;今天就来研究了一下版本检查更新就踩到坑了。。。先来看看检查更新及下载、安装的实现。 先来看看页面&#xff1a; 从左到右依次为…

推荐3个实用的github开源项目

目录&#xff1a; 1、AI生成高清短视频 2、媒体平台爬虫 3、文本转语音项目

【Linux网络编程】I/O多路转接之select

select 1.初识select2.了解select基本概念和接口介绍3.select服务器4.select特点及优缺点总结 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603;…

Spring Boot基础功能指引

文章目录 引言I 处理全局ExceptionII 定时任务III 引入springboot MVC3.1 添加Spring Web依赖3.2 重写SpringBootServletInitializer3.3 WebMvcConfigurer引言 I 处理全局Exception 类上加入注解@ControllerAdvice 定义controller切面方法上加入注解@ExceptionHandler(value =…

C++中的`this`指针详解

目录标题 1. this指针的基础1.1 this指针的定义1.2 this指针的用途 2. this指针的详细用法2.1 区分同名的成员和参数2.2 链式调用2.3 返回对象自身的地址 3. this在特殊情况下的用途3.1 在构造函数中使用this3.2 与智能指针结合使用 4. this指针的限制和注意事项4.1 静态成员函…

vue2 与vue3的差异汇总

Vue 2 与 Vue 3 之间存在多方面的差异&#xff0c;这些差异主要体现在性能、API设计、数据绑定、组件结构、以及生命周期等方面。以下是一些关键差异的汇总&#xff1a; 数据绑定与响应式系统 Vue 2 使用 Object.defineProperty 来实现数据的响应式&#xff0c;这意味着只有预…

系统架构设计师 - 计算机组成与体系结构(1)

计算机组成与体系结构 计算机组成与体系结构计算机结构 ★CPU 组成结构运算器组成控制器组成 计算机体系结构冯诺依曼结构哈弗结构 嵌入式芯片&#xff08;了解&#xff09; 存储系统 ★★★★概述Cache主存编址磁盘管理磁盘基本结构与存取过程磁盘优化分布存储磁盘管理 大家好…

数据挖掘(二)数据预处理

前言 基于国防科技大学 丁兆云老师的《数据挖掘》 数据挖掘 数据挖掘&#xff08;一&#xff09;数据类型与统计 2、数据预处理 2.1数据清理 缺失值处理&#xff1a; from sklearn.impute import SimpleImputer# 创建一个SimpleImputer对象&#xff0c;指定缺失值的处理策略…

Spring框架学习笔记(二):Spring IOC容器配置 Bean,分别基于XML配置bean 和 基于注解配置 bean

1 Spring 配置/管理 bean 介绍 Bean 管理包括两方面 &#xff1a;创建 bean 对象&#xff1b;给 bean 注入属性 Bean 配置方式&#xff1a;基于 xml 文件配置方式&#xff1b;基于注解方式 2 基于 XML 配置 bean 2.1 通过类型来获取 bean 方法&#xff1a;给getBean传入一…

从零开始学习MySQL 事务处理

事务处理与ACID特性 事务是数据库操作的基本单元&#xff0c;它确保一组操作要么全部成功&#xff0c;要么全部失败&#xff0c;以此来维护数据库的一致性。这四个字母缩写ACID代表了事务的四大特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;**&#xff1a;事务被…