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

2024-05-14 20:52

本文主要是介绍[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 编译,设备树文件拷贝到网络文件系统中,更新设备树,安装编译后的驱动程序

这篇关于[IMX6ULL驱动开发]-GPIO子系统和Pinctrl子系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/989825

相关文章

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark