pinctrl和gpio子系统

文章目录

  • 一、pinctrl 子系统简介
  • 二、pinctrl子系统的配置形式分析
    • 1.主要功能
    • 2.配置格式
    • 3.pinctrl驱动匹配
  • 三、gpio子系统
    • 1.gpio系统使用流程
  • 四、程序举例-led
  • 五、总结


一、pinctrl 子系统简介

在led操作设备树的实验中,对于gpio的初始化是直接操作的寄存器,包括引脚复用,引脚的电气属性(上下拉,速度等),输入/输出都是操作的寄存器,在stm32中是使用库函数实现引脚的初始化,也没有直接去配置寄存器,所以在Linux中,提供了pinctrl子系统来实现IO的配置的。

二、pinctrl子系统的配置形式分析

1.主要功能

①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成,pinctrl 子系统源码目录为 drivers/pinctrl。

2.配置格式

举例说明:以imx6ull.dtsi文件(是imx6ull芯片的公共节点配置文件)中的iomuxc节点为例。
在这里插入图片描述
手册内容如下:IOMUXC控制器的起始地址就是0x20e0000
在这里插入图片描述

向节点iomuxc节点追加内容如下:在设备树中进行的信息追加

在这里插入图片描述
在配置GPIO的时候分为两部分,第一部分是引脚的复用功能,第二部分是电气属性信息,现在看下:

fsl,pins中的宏定义含义:打开imx6ull-pinfunc.h文件
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19	0x17059 /* SD1 CD */
在文件中定义了宏定义MX6UL_PAD_UART1_RTS_B__GPIO1_IO19内容,其格式如下
/*
 * The pin function ID is a tuple of
 * <mux_reg      conf_reg      input_reg    mux_mode    input_val>
 */
  #define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19                        
    0x0090        0x031C        0x0000        0x5         0x0
  mux_reg:含义是复用寄存器地址偏移地址:0x20e0000+ 0x0090的值(iomuxc节点的地址+0x0090),对比手册
  也就是UART1_RTS_B这个引脚
  conf_reg:也是寄存器的偏移地址:0x20e0000+0x031C=0x20e031C是UART1_RTS_B这个引脚电气属性配置但在这个conf_reg并没有看见他的值是多少,
  在哪呢,在宏定义MX6UL_PAD_UART1_RTS_B__GPIO1_IO19后边,也就是0x17059这个值
  intput_reg:的值偏移为0表示UART1_RTS_B这个引脚没有input的这个功能.
  mux_mode:复用模式配置:0x5,二进制101看下图0x20e0090的ALT5的值也是101所以引脚被复用为GPIO1_IO19 
  input_val:就是写入到 intput_reg的值

在这里插入图片描述
在这里插入图片描述

3.pinctrl驱动匹配

如何找到imx6ull的pinctrl子系统呢?设备树里的节点又是如何与驱动匹配的呢?
驱动与设备树里的节点是通过compatible属性匹配,原理是在驱动中存在一个驱动兼容表也就是 OF 匹配表,此表是个字符串数组,里边保存着compatible 属性值,如果设备树的节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动.
举例:iomuxc节点compatible属性值为:compatible = “fsl,imx6ul-iomuxc”;
在Linux中搜索可以找到,其pinctrl驱动在pinctrl-imx6ul.c中。当pinctrl驱动匹配后,执行probe函数(这个函数是个总称不是说就要执行这个probe函数)。
在这里插入图片描述
在这里插入图片描述

probe函数获取节点方式:如下代码所示。

imx6ul_pinctrl_probe
	->imx_pinctrl_probe
		->  此函数会初始化imx_pinctrl_desc,为pinctrl_desc类型的结构体。重点是:
		imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
		imx_pinctrl_desc->pmxops = &imx_pmx_ops;
		imx_pinctrl_desc->confops = &imx_pinconf_ops;
		最后通过pinctrl_register函数向系统注册一个imx_pinctrl_desc,也就是ops函数

		->imx_pinctrl_probe_dt
			->imx_pinctrl_parse_functions
				->imx_pinctrl_parse_groups
					->		pin_reg->mux_reg = mux_reg;
						pin_reg->conf_reg = conf_reg;
						pin->input_reg = be32_to_cpu(*list++);
						pin->mux_mode = be32_to_cpu(*list++);
						pin->input_val = be32_to_cpu(*list++);
						config = be32_to_cpu(*list++);
						if (config & IMX_PAD_SION)这里用来设置电气属性值0x17059
						pin->mux_mode |= IOMUXC_CONFIG_SION;
						pin->config = config & ~IMX_PAD_SION;
imx_pinconf_set函数设置PIN的电气属性


imx_pmx_set函数设置PIN的复用

三、gpio子系统

pinctrl 子系统重点是设置 PIN(有的 SOC 叫做 PAD)的复用和电气属性,如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么接下来就要用到 gpio 子系统了。
gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO。

1.gpio系统使用流程

1、gpio在设备树中的表示方法:
&usdhc1 {
	pinctrl-names = "default", "state_100mhz", "state_200mhz";
	pinctrl-0 = <&pinctrl_usdhc1>;在pinctrl_usdhc1节点
	pinctrl-1 = <&pinctrl_usdhc1_100mhz>;在pinctrl_usdhc1_100mhz节点
	pinctrl-2 = <&pinctrl_usdhc1_200mhz>;在pinctrl_usdhc1_200mhz节点
	cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
	keep-power-in-suspend;
	enable-sdio-wakeup;
	vmmc-supply = <&reg_sd1_vmmc>;
	status = "okay";
};

pinctrl_usdhc1: usdhc1grp {
			fsl,pins = <
				MX6UL_PAD_SD1_CMD__USDHC1_CMD     0x17059
				MX6UL_PAD_SD1_CLK__USDHC1_CLK     0x10071
				MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059
				MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059
				MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059
				MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059
			>;
		};

		pinctrl_usdhc1_100mhz: usdhc1grp100mhz {
			fsl,pins = <
				MX6UL_PAD_SD1_CMD__USDHC1_CMD     0x170b9
				MX6UL_PAD_SD1_CLK__USDHC1_CLK     0x100b9
				MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9
				MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9
				MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9
				MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9
			>;
		};

		pinctrl_usdhc1_200mhz: usdhc1grp200mhz {
			fsl,pins = <
				MX6UL_PAD_SD1_CMD__USDHC1_CMD     0x170f9
				MX6UL_PAD_SD1_CLK__USDHC1_CLK     0x100f9
				MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9
				MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9
				MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9
				MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9
			>;
		};

定义了一个cd-gpios属性,这里引用了gpio1节点(gpio控制器):关于gpio控制器内的格式,见devicetree\bindings\gpio\fsl-imx-gpio.txt

	gpio1: gpio@0209c000 {
				compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
				reg = <0x0209c000 0x4000>;
				interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
					     <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
				gpio-controller;
				#gpio-cells = <2>;
				interrupt-controller;
				#interrupt-cells = <2>;
			};

这样只要读取cd-gpios 属性,就可以操作gpio了,那如何从设备树中获取信息呢,使用of函数获取。
流程如下:

驱动中对gpio的操作函数

	1、首先,获取到GPIO所处的设备节点,比如of_find_node_by_path。
	2、获取GPIO编号, of_get_named_gpio函数,返回值就是GPIO编号。
	3、请求此编号的GPIO,gpio_request函数(不使用后记得释放gpio)
	4、设置GPIO,输入或输出,gpio_direction_input或gpio_direction_output。
	5、如果是输入,那么通过gpio_get_value函数读取GPIO值,如果是输出,通过gpio_set_value设置GPIO值。

四、程序举例-led

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/export.h>
#include <linux/types.h>
#include <linux/uaccess.h>			//  copy_to_user() & copy_from_user
#include <asm/io.h>      //ioremap
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
MODULE_DESCRIPTION("NatSemi/Winbond PC-8736x GPIO Pin Driver");
MODULE_LICENSE("GPL");
#define DEVNAME "gpioled"
/*设备结构体*/
struct gpioled_dev{
    dev_t devid;/*设备号*/
    int major; /*主设备号*/
    int minor; /*次设备号*/
    struct cdev cdev;/*字符设备结构体*/
    struct class *class;/*类*/
    struct device *device;/*设备*/
    struct device_node *node;/*设备节点*/
    int led_gpio;
};

struct gpioled_dev gpioled;/*led结构体变量*/
static int gpioled_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &gpioled;
    printk("gpioled_open\n\r");
    return 0;
}
static int gpioled_release(struct inode *inode, struct file *filp)
{
    //struct gpioled *gpioled = (struct gpioled *)filp->private_data;
    //printk("gpioled_open\n\r");
    return 0;
}
static ssize_t gpioled_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    //struct gpioled *gpioled = (struct gpioled *)filp->private_data;
    //printk("gpioled_open\n\r");
    return 0;
}

static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) 
{
    struct gpioled_dev *dev = (struct gpioled_dev *)filp->private_data;
    int ret = 0;
    unsigned char databuff[1];
    ret = copy_from_user(databuff, buf, count);
    if(ret < 0)
    {
        printk("copy_from_user error\n\r");
        return -EPERM;
    }
    if(databuff[0] == 0)
    {
        gpio_set_value(dev->led_gpio, 0);
    }
    if(databuff[0] == 1)
    {
        gpio_set_value(dev->led_gpio, 1);
    }
    //printk("gpioled_open\n\r");
    return 0;
}

const struct file_operations gpioled_fops = {
    //.owner = THIS_MODULE,
   .open = gpioled_open,
   .release = gpioled_release,
   .read = gpioled_read,
   .write = gpioled_write,
};


static int __init gpioled_init(void)
{
    /*注册*/
    int ret = -1;
    /*设备号申请*/   
    if(gpioled.major){
        gpioled.devid = MKDEV(gpioled.major,0);
        ret = register_chrdev_region(gpioled.devid, 1, DEVNAME);
        

    }else{
        ret = alloc_chrdev_region(&gpioled.devid, 0, 1, DEVNAME);
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
    }
    if (ret < 0)
    {
        printk("alloc_chrdev_region failed\n");
        goto devid_fail;
    }
    
    /*字符设备初始化*/
    cdev_init(&gpioled.cdev,&gpioled_fops);
    gpioled.cdev.owner = THIS_MODULE;
    gpioled.cdev.ops = &gpioled_fops;
    ret = cdev_add(&gpioled.cdev, gpioled.devid,1);
    if (ret < 0)
    {
        printk("cdev_add failed\n");
        goto cdev_add_fail;
    }
    /*类创建*/
    gpioled.class = class_create(THIS_MODULE, DEVNAME);
    if (IS_ERR(gpioled.class))
    {
        printk("class_create failed\n");
        goto class_create_fail;
    }
    /*设备创建*/
    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, DEVNAME);
    if (IS_ERR(gpioled.device))
    {
        printk("device_create failed\n");
        goto device_create_fail;
    }
    //获取设备属性信息
    //1获取节点
    gpioled.node = of_find_node_by_path("/gpioled");
    if(gpioled.node == NULL)
    { 
        printk("of_find_node_by_path error\n\r");
        goto of_find_node_by_path_failed;
    }
   //2获取led灯gpio的编号
   gpioled.led_gpio = of_get_named_gpio(gpioled.node,"led_gpios",0);
   if(gpioled.led_gpio < 0)
   {
       printk("of_get_named_gpio error\n\r");
       goto of_get_named_gpio_failed;
   }
    printk("of_get_named_gpio=%d\n\r",gpioled.led_gpio);
    //3申请gpio,申请成功要记得释放
    ret = gpio_request(gpioled.led_gpio, "led-gpio");
	if (ret) {
		printk("gpio_request failed\n");
		goto gpio_request_failed;
	}
    //4使用io,设置输出
    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if (ret) {
        printk("gpio_direction_output failed\n");
        goto gpio_direction_output_failed;
    }
    //5设置io为低电平
    gpio_set_value(gpioled.led_gpio, 0);
    return 0;
gpio_direction_output_failed:
    gpio_free(gpioled.led_gpio);
gpio_request_failed:
of_get_named_gpio_failed:
of_find_node_by_path_failed:
device_create_fail:
    class_destroy(gpioled.class);
class_create_fail:
    cdev_del(&gpioled.cdev);
cdev_add_fail:
    unregister_chrdev_region(gpioled.devid, 1);
devid_fail:
    return ret;

}
static void __exit gpioled_exit(void)
{   
     gpio_set_value(gpioled.led_gpio, 1);
    /*注销*/
    device_destroy(gpioled.class, gpioled.devid);
    class_destroy(gpioled.class);
    cdev_del(&gpioled.cdev);
    unregister_chrdev_region(gpioled.devid, 1);
    printk(KERN_INFO "gpioled_exit\n");
    /*释放io*/
    gpio_free(gpioled.led_gpio);

}


/*驱动入口函数和出口函数*/
module_init(gpioled_init);
module_exit(gpioled_exit);

五、总结

1,添加pinctrl信息,
2,检查当前设备树中要使用的IO有没有被其他设备使用,如果有的话要处理。
分两个地方检查:
1)查看引脚的定义是否有被使用(pinctrl系统)。
2)查看gpio属性,led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;是否被使用。
3,添加设备节点,在设备节点中创建一个属性,此属性描述所使用的gpio。
4,编写驱动,获取对应的gpio编号,并申请IO,成功以后即可使用此Io。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

idea No versioned directories to update were found

idea如何配置svn以及svn安装时需要注意什么 下载地址&#xff1a;https://112-28-188-82.pd1.123pan.cn:30443/download-cdn.123pan.cn/batch-download/123-820/3ec9445a/1626635-0/3ec9445a25ba365a23fc433ce0c16f34?v5&t1714358478&s171435847804276f7d9249382ba512…

使用Mybatis映射时间 DateTime ==> LocalDateTime

首先查看&#xff0c;数据库字段&#xff1a; 书写映射实体类对象VO&#xff1a; Data public class OrderListVO implements Serializable {private Integer orderId;private String memberName;private String orderNumber;private BigDecimal orderPrice;private String l…

【数据结构与算法】力扣 239. 滑动窗口最大值

题干描述 给你一个整数数组 nums&#xff0c;有一个大小为 k **的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a; nums [1,3,-1,-3,5,3…

C/C++实现高性能并行计算——1.pthreads并行编程(中)

系列文章目录 pthreads并行编程(上)pthreads并行编程(中)pthreads并行编程(下)使用OpenMP进行共享内存编程 文章目录 系列文章目录前言一、临界区1.1 pi值估计的例子1.2 找到问题竞争条件临界区 二、忙等待三、互斥量3.1 定义和初始化互斥锁3.2 销毁。3.3 获得临界区的访问权&…

安卓中对象序列化面试问题及回答

1. 什么是对象的序列化&#xff1f; 答&#xff1a; 序列化是将对象转换为字节流的过程&#xff0c;以便将其存储在文件、数据库或通过网络传输。反序列化则是将字节流重新转换为对象的过程。 2. 为什么在 Android 开发中需要对象的序列化&#xff1f; 答&#xff1a; 在 An…

ctfshow——JWT

文章目录 web 345web 346——算法改为Noneweb 347-348——爆破密匙web 349——非对称加密算法RS256私钥泄漏web 350——泄漏公钥、非对称密码算法改为对称密码算法 web 345 抓个包&#xff0c;可以看到cookie部分使用JWT&#xff08;Json Web Token&#xff09;。 JWT实际上是…

Django后台项目开发实战一

开发环境使用 Anaconda, IDE 使用 pycharm 第一阶段 创建 Django 项目 在 Anaconda Prompt 中逐步输入下面的命令&#xff08;之后的所有命令都在这个&#xff09; 首先创建一个虚拟环境&#xff0c;名称自拟&#xff0c;python 版本我这里使用 3.9.18 关于 python 版本和…

STM32中断之TIM定时器详解

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. TIM简述 2. 定时器类型 2.1 基本定时器 2.2 通用定时器 2.3 高级定时器 3. 定时中断 4. 代码示例1 5. 代码示例2 1. TIM简述 定时器的基本功能&#xff1a;定时器可以在预定的时间间隔内产生周…

经典机器学习法---感知模型机

优质博文&#xff1a;IT-BLOG-CN 1、模型形式 感知机模型主要用于解决二分类问题&#xff0c;即响应变量Y是个二分类变量&#xff08;如性别&#xff09;。其基本思想是拟找出一个超平面S&#xff0c;将样本空间中的训练集分为两个部分&#xff0c;使得位于超平面S合一侧的点具…

启发式搜索算法4 -遗传算法实战:吊死鬼游戏

相关文章: 启发式搜索算法1 – 最佳优先搜索算法 启发式搜索算法2 – A*算法 启发式搜索算法2 – 遗传算法 有一个小游戏叫吊死鬼游戏&#xff08;hangman&#xff09;&#xff0c;在学习英语的时候&#xff0c;大家有可能在课堂上玩过。老师给定一个英文单词&#xff0c;同学们…

2024深圳杯数学建模竞赛A题(东三省数学建模竞赛A题):建立火箭残骸音爆多源定位模型

更新完整代码和成品完整论文 《2024深圳杯&东三省数学建模思路代码成品论文》↓↓↓&#xff08;浏览器打开&#xff09; https://www.yuque.com/u42168770/qv6z0d/zx70edxvbv7rheu7?singleDoc# 2024深圳杯数学建模竞赛A题&#xff08;东三省数学建模竞赛A题&#xff0…

前端性能优化知识梳理

1.重要性 当我们面试的时候&#xff0c;前端性能优化方面算是必考的知识点&#xff0c;但是工作中我们又很少会重点的对项目进行前端优化&#xff0c;它真的不重要吗&#xff1f; 如果我们可以将后端响应时间缩短一半&#xff0c;整体响应时间只能减少5%~10%。而如果关注前端…

手把手实现一个简约酷美美的版权声明模块

1. 导语 版权声明在很多网站都有用到&#xff0c;出场率还是很高的。所以今天就实现一个属于自己分风格的版权声明模块&#xff0c;技术上采用原生的前端三剑客: HTMLCSSJavaScript(可能会用到) 比如CSDN的版权声明是这样的 2. 需求分析 先看看成品吧&#xff0c;这篇文字结…

翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一

合集 ChatGPT 通过图形化的方式来理解 Transformer 架构 翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习二翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深…

强网杯 2019]随便注解题方式

先来看题 这里只有一个提交&#xff0c;那我们就先提交看看情况找找思路 这里提交以后发现也没有什么有用的信息 这样我们只能根据我们的经验先试试 输入1发现有报错 有报错信息就可以试试报错注入了&#xff0c;但是这种ctf题通常会有限制字符所以我使用 1 select 1,2# 来…

c#数据库: 4.修改学生成绩

将4年级的学生成绩全部修改为100分,。修改前的学生信息表如图所示: using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks;namespace StudentUpdate {internal class Program{s…

Apache SeaTunnel k8s 集群模式 Zeta 引擎部署指南

SeaTunnel提供了一种运行Zeta引擎(cluster-mode)的方法&#xff0c;可以让Kubernetes在本地运行Zeta引擎&#xff0c;实现更高效的应用程序部署和管理。在本文中&#xff0c;我们将探索SeaTunnel k8s运行zeta引擎(cluster-mode模式)的更多信息&#xff0c;了解如何更好地利用Ze…

JavaScript基础(二)

JS语法结构——引入方式 js很明显可以是一个后缀名为js的文件&#xff0c;js的引入方式和css一样&#xff0c;也有三种方式。 1.外部 使用script表现&#xff0c;只不过增加一个src属性&#xff0c;把js文件的路径src属性中。 <script src "js文件路径">&l…

h5+Vant左滑删除

介绍&#xff1a;这是一个上拉加载下拉刷新的列表&#xff0c;外加左滑删除。废话不多说&#xff0c;直接上代码&#xff01;&#xff01;&#xff01;&#xff01; <template><div class"info-list"><div class"top-bar"><van-nav-…

深入解析Floyd Warshall算法:原理、Java实现与优缺点

Floyd Warshall算法的简介 在我们的日常生活中&#xff0c;常常会遇到需要找出两点之间最短路径的问题。比如&#xff0c;从家到公司的最短路线&#xff0c;或者在旅行时&#xff0c;从一个景点到另一个景点的最快路线。 为了解决这类问题&#xff0c;科学家们设计出了许多算法…