设备树pinctrl子系统

news2024/10/11 17:00:30

1.pinctrl子系统

大多数 SOC 的 pin 都是支持复用的,比如 IMX6ULL 的 GPIO1_IO03 既可以作为普通的GPIO 使用,也可以作为 I2C1 的 SDA 等等。此外我们还需要配置 pin 的电气特性,比如上/下拉、速度、驱动能力等等。传统的配置 pin 的方式就是直接操作相应的寄存器,但是这种配置方式比较繁琐、而且容易出问题(比如 pin 功能冲突)。pinctrl 子系统就是为了解决这个问题而引入的,pinctrl 子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。

 

要使用 pinctrl 子系统,我们需要在设备树里面设置 PIN 的配置信息,毕竟 pinctrl 子系统要
根据你提供的信息来配置 PIN 功能,一般会在设备树里面创建一个节点来描述 PIN 的配置信
息。在imx6ull.dtsi中有者三个节点表示imx6ull的pin引脚:

分别是iomuxc,iomuxc-gpr,iomuxc-snvs节点,可以在这三个节点下添加GPIO节点信息:

iomuxc: iomuxc@020e0000 {
	compatible = "fsl,imx6ul-iomuxc";
	reg = <0x020e0000 0x4000>;
};
 
gpr: iomuxc-gpr@020e4000 {
	compatible = "fsl,imx6ul-iomuxc-gpr",
	"fsl,imx6q-iomuxc-gpr", "syscon";
	reg = <0x020e4000 0x4000>;
};
 
iomuxc_snvs: iomuxc-snvs@02290000 {
	compatible = "fsl,imx6ull-iomuxc-snvs";
	reg = <0x02290000 0x10000>;
};

2.引脚定义解析

开发板中MX6UL_PAD_UART1_RTS_B__GPIO1_IO19的定义如下:


#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0


0x0090 0x031C 0x0000 0x5 0x0,这 5 个值的含义如下所示对应关系:
< 0x0090    0x031C    0x0000     0x5       0x0      >
< mux_reg   conf_reg  input_reg  mux_mode  input_val>

地址解析:

0x0090:mux_reg 寄存器偏移地址;dtsi中的 iomuxc 节点就是 IOMUXC 外设对应的节点 , 根 据 其 reg 属 性 可 知 IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000 。 因 此
0x020e0000+0x0090=0x020e0090。

0x031C:conf_reg 寄存器偏移地址,和 mux_reg 一样,0x020e0000+0x031c=0x020e031c,这个就是寄存器 IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 的地址。

0x0000:input_reg 寄存器偏移地址,有 input_reg 寄存器的外设需要配置 input_reg 寄存器。没有的话就不需要设置。

0x5 : mux_reg 寄 存 器 值 , 相当于设置IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B 寄存器为 0x5,也即是设置 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19。
0x0:input_reg 寄存器值,在这里无效。

注意上面没有config_reg的值,因此0x17059 就是他的值,此值由用户自行设置,通过此值来设置一个 IO 的上/下拉、驱动能力和速度等。

==========================

3.设备GPIO树节点

gpioled {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "atkalpha-gpioled";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_led>;
    led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
    status = "okay";
};

设备树节点描述信息

inctrl-names:用来表示设备的状态,这里有1个,为默认状态(default),也可以有其他的状态,还可以自定义状态,比如state_100mhz:表示这个gpio运行为100mhz状态。

pinctrl-0:表示第0个状态对应于"default"状态,对应的引脚在pinctrl-0里面定义。

led-gpios:描述led使用到的IO引脚信息     
“&gpio1”表示 引脚所使用的 IO 属于 GPIO1 组,“19” 表示 GPIO1 组的第 19 号 IO,“GPIO_ACTIVE_LOW” 表示低电平有效,如果改为“GPIO_ACTIVE_HIGH”就表示高电平有效。

============================

4.gpio 子系统 API 函数

gpio_request 函数

gpio_request 函数用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request进行申请,函数原型如下:

int gpio_request(unsigned gpio, const char *label)

@gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信
息,此函数会返回这个 GPIO 的标号。
@label:给 gpio 设置个名字。

gpio_free 函数

如果不使用某个 GPIO 了,那么就可以调用 进行释放。

void gpio_free(unsigned gpio)
@gpio:要释放的 gpio 标号。

gpio_direction_input 函数

用于设置某个 GPIO 为输入:

int gpio_direction_input(unsigned gpio)
gpio:要设置为输入的 GPIO 标号。
返回值:0,设置成功;负值,设置失败。

gpio_direction_output 函数

用于设置某个 GPIO 为输出,并且设置默认输出值

int gpio_direction_output(unsigned gpio, int value)
gpio:要设置为输出的 GPIO 标号。
value:GPIO 默认输出值。

gpio_get_value 函数

用于获取某个 GPIO 的值(0 或 1),此函数是个宏

#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)

gpio_set_value 函数

用于设置某个 GPIO 的值,此函数是个宏

#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
@gpio:要设置的 GPIO 标号。
@value:要设置的值。

=========================

5.驱动代码编写:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ide.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/err.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
 
#define GPIOLED_CNT      1
#define GPIOLED_NAME     "gpioled"
 
#define LED_ON      1
#define LED_OFF     0
 
 
 
/* gpioled设备结构体 */
typedef struct gpioled_dev{
    dev_t devid;                    /* 设备号   */
    int major;                      /* 主设备号 */
    int minor;                      /* 次设备号 */
    struct cdev cdev;               /* 字符设备 */
    struct class *class;            /* 类      */
    struct device *device;          /* 类的设备 */
    struct device_node *nd;         /* 节点 */
    int led_gpio;                   /* LED的gpio编号 */
}gpioled_dev;
gpioled_dev gpioled;
 
 
 
/* LED等控制函数 */
static int Led_Switch(unsigned char Led_Status)
{
 
    if(Led_Status == LED_ON){
        gpio_set_value(gpioled.led_gpio,0);
    }else if(Led_Status == LED_OFF){
        gpio_set_value(gpioled.led_gpio,1);
    }else{
        return -1;
    }
    return 0;
}
 
/* 打开驱动文件 */
static int gpioled_open (struct inode *Inode, struct file *File)
{
    File->private_data = &gpioled;   /* 设置私有数据 */
    return 0;
}
/* 关闭驱动文件 */
static int gpioled_release (struct inode *Inode, struct file *File)
{
 
    return 0;
}
/* 对驱动文件进行写操作 */
static ssize_t gpioled_write (struct file *File, const char __user *buf,
                            size_t Count, loff_t *Loff)
{
    int ret = 0;
    unsigned char databuf[1];
 
    ret = copy_from_user(databuf,buf,Count);
    if(ret < 0){
        return ret;
    }
 
    Led_Switch(databuf[0]);
    return 0;
}
 
/* 字符设备的文件操作集合 */
static const struct file_operations gpioled_opts = {
    .owner = THIS_MODULE,
    .open = gpioled_open,
    .release = gpioled_release,
    .write = gpioled_write,
};
 
/* 模块入口 */
static int __init gpioled_init(void)
{
    int ret = 0;
 
    /* 获取设备节点:gpioled */
    gpioled.nd = of_find_node_by_path("/gpioled");
    if(gpioled.nd == NULL){
        ret = -EINVAL;
        goto fail_nd;
    }
    /* 获取设备树中的gpio属性,得到LED所使用的GPIO编号 */
    gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-gpio", 0);
    if(gpioled.led_gpio < 0){
        ret = -EINVAL;
        goto fail_nd;
    }
    printk("led-gpio num = %d\r\n",gpioled.led_gpio);
 
    /* 申请GPIO */
    ret = gpio_request(gpioled.led_gpio, "led-gpio");
    if(ret){
        goto fail_nd;
    }
 
    /* 将LED的GPIO设置为输出,默认关闭LED灯(低电平点亮) */
    ret = gpio_direction_output(gpioled.led_gpio, 0);
    if(ret < 0){
        goto fail_gpio;
    }
 
    /* 注册字符设备 */
    /* 1、注册设备号 */
    gpioled.major = 0;
    if(gpioled.major){               /* 指定了设备号 */
        gpioled.devid = MKDEV(gpioled.major,0);
        ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
    }else{                          /* 没有指定设备号 */
        ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
        
    }
    if(ret < 0){                    /* 注册设备号失败 */
        goto fail_devid;
    }
    printk("major=%d,minor=%d\r\n",gpioled.major,gpioled.minor);
 
    /* 2、注册字符设备 */
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_opts);               /* 初始化 */
    ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
    if(ret < 0){
        goto fail_cdev;
    }
 
    /* 自动添加节点 */
    /* 1、添加类 */
    gpioled.class = class_create(THIS_MODULE,GPIOLED_NAME);
    if(IS_ERR(gpioled.class)){
        ret = PTR_ERR(gpioled.class);
        goto fail_class;
    }
    /* 2、添加类的设备 */
    gpioled.device = device_create(gpioled.class, NULL,gpioled.devid, NULL, GPIOLED_NAME);
    if(IS_ERR(gpioled.device)){
        ret = PTR_ERR(gpioled.device);
        goto fail_device;
    }
 
 
    return 0;
 
fail_device:
    class_destroy(gpioled.class);
fail_class:
    cdev_del(&gpioled.cdev);
fail_cdev:
    unregister_chrdev_region(gpioled.devid,GPIOLED_CNT);
fail_devid:
fail_gpio:
    gpio_free(gpioled.led_gpio);
fail_nd:
    return ret;
}
 
/* 模块出口 */
static void __exit gpioled_exit(void)
{
    /* 关灯 */
    Led_Switch(LED_OFF);
    
    /* gpio释放 */
    gpio_free(gpioled.led_gpio);
 
    /* 删除设备 */
    cdev_del(&gpioled.cdev);
    /* 注销设备号 */
    unregister_chrdev_region(gpioled.devid,GPIOLED_CNT);
    printk("gpioled_exit\r\n");
 
    /* 销毁类的设备 */
    device_destroy(gpioled.class,gpioled.devid);
    /* 销毁类 */
    class_destroy(gpioled.class);
 
}
 
/* 注册模块入口和出口函数 */
module_init(gpioled_init);
module_exit(gpioled_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZhangXueGuo");

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

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

相关文章

一个月学会Java 第9天 构造器与访问修饰符

Day9 构造器与访问修饰符 本来其实是想在Day8里面把这个构造器给讲掉的&#xff0c;但是发现篇幅有点长的&#xff0c;后面的内容也是稍微有一点点超纲了&#xff0c;所以就先把这个留着&#xff0c;现在再讲。 第一章 构造器 构造器&#xff0c;我们之前在讲类的时候其实是说过…

CANoe_调用C#控件的方法_DEMO方法演示

1、DEMO存放位置 D:\Users\Public\Documents\Vector\CANoe\Sample Configurations 11.0.96\CAN\MoreExamples\ActiveX_DotNET_Panels 每个人的电脑因为有区别存放位置不一样 2、控件制作--使用C#控件可以直接制作 3、控件代码 using System; using System.Collections; usi…

探索血糖人工智能预测可穿戴设备

导言 论文地址&#xff1a;https://arxiv.org/abs/2404.12605 数字生物标志物是一种新型医疗指标&#xff0c;其数据来源于可穿戴设备和智能手机等数字设备收集的数据。与从传统生物样本中提取的病理标志物不同&#xff0c;数字生物标志物能提供行为模式、生理节奏和环境因素的…

DBeaver的安装与使用

文章目录 前言一、DBeaver 的安装二、DBeaver 的基本使用三、DBeaver 的高级功能四、使用小技巧五、总结 前言 DBeaver 是一款广泛应用的数据库管理工具&#xff0c;支持多种数据库系统。以下是它的安装与使用方法。 一、DBeaver 的安装 下载 DBeaver: 前往 DBeaver 官方网站…

Vue工程化结构环境安装及搭建教程 : 之nvm

vue需要的环境&#xff1a; node.js : Node.js和Vue.js通常会一起使用。Node.js作为后端服务器&#xff0c;处理服务器端的逻辑和数据访问&#xff0c;而Vue.js则负责前端用户界面的构建和交互。通过Ajax通信&#xff0c;Vue.js应用程序向Node.js服务器发送请求&#xff0c;并…

Collection 集合框架

Collection 集合框架 各类集合 Set TreeSet 基于红黑树实现&#xff0c;支持有序性操作&#xff0c;例如根据一个范围查找元素的操作。但是查找效率不如 HashSet&#xff0c;HashSet 查找的时间复杂度为 O(1)&#xff0c;TreeSet 则为 O(logN)。 HashSet 基于哈希表实现&…

Python入门笔记(四)

文章目录 第九章 集合set9.1 创建集合&#xff1a;set()、集合生成式9.2 集合性质9.3 一些函数&#xff1a;issubset()、issuperset()、isdisjoint()9.4 集合增加元素&#xff1a;add()、update()9.5 集合删除元素&#xff1a;remove()、discard()、pop()、clear()9.6 创建不能…

ELM分类预测 | MATLAB实现ELM极限学习机多特征分类预测(二分类)

分类预测 | MATLAB实现ELM极限学习机多特征分类预测(二分类) 目录 分类预测 | MATLAB实现ELM极限学习机多特征分类预测(二分类)效果一览基本介绍程序设计学习总结参考资料效果一览 训练集正确率Accuracy = 89%(445/500) 测试集正确率Accuracy = 86.9565%(60/69) 基本介绍 MATLA…

《RabbitMQ篇》消费者轮询消费消息

当有多个消费者都在同一个队列中拿取消息时&#xff0c;会轮询从队列中拿取消息消费。 RabbitMQUtil类为工具类&#xff0c;获取Channel。 import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory;public…

基于SSM的线上学习网站【附源码】

基于SSM的线上学习网站&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统结构设计 4.2系统结构 4.3.数据库设计 4.3.1数据库实体 4.3.2数据库设计表 5系统详细实现 5.1 管理员模块的实现 5.1.1 学生信息管理 5.1.2 教…

亚马逊云科技生成式 AI 认证正式上线!

为了更好帮助大家在人工智能领域入门及快速成长&#xff0c;助力企业发掘优秀人工智能人才&#xff0c;亚马逊云科技推出 AWS Certified AI Practitioner 认证&#xff0c;涵盖人工智能领域的必备技能、前沿技术和实践经验。 通过该认证&#xff0c;你将掌握设计考虑、RAG&…

大傻工具提示:没有找到c:\windows\system32\msrd3x43.dll

解决办法&#xff1a; 1、不用理会它&#xff0c;对串口工具运行没有任何影响。就算你下载了也没用&#xff0c;依然会有提示。 2、右键以管理员身份运行就不报错了。

医学大模型微调·数据处理全流程:炼丹,是自我超越的方法

医学大模型微调数据处理全流程&#xff1a;炼丹&#xff0c;是自我超越的方法 数据清洗脚本数据标注数据核验转为微调格式随机化 数据清洗脚本 HTML标签移除 去除文本中的所有HTML标签&#xff0c;保留纯文本内容。 特殊字符处理 替换特殊数字符号&#xff08;如①②③&#x…

如何保护源代码?十种有效方法实现源代码防泄密

在数字化时代&#xff0c;源代码的安全保护对于企业来说至关重要。源代码不仅是企业技术创新的成果&#xff0c;更是其核心竞争力的体现。一旦源代码泄露&#xff0c;不仅可能导致企业丧失市场优势&#xff0c;还可能引发知识产权纠纷、增加竞争对手的市场竞争力&#xff0c;甚…

k8s 1.28.2 集群部署 MinIO 分布式集群

文章目录 [toc]MinIO 介绍MinIO 生产硬件要求MinIO 存储要求MinIO 内存要求MinIO 网络要求MinIO 部署架构分布式 MinIO复制的 MinIO 部署 MinIO创建目录节点打标签创建 namespace创建 pv创建 MinIO配置 ingress问题记录通过代理服务器访问 MinIO 的 Object Browser 界面一直显示…

用echarts画天气预报

如图 上代码 <template><div id"temp15day"></div> </template><script setup> import * as echarts from "echarts"; const initChart () > {const machart echarts.init(document.getElementById("temp15day&q…

如何选择最合适的华为云数据库:指南与建议

在数字化转型的浪潮中&#xff0c;选择合适的数据库是企业成功的关键。华为云提供了多种数据库服务&#xff0c;以满足不同业务需求。以下是九河云总结的一些指南和建议&#xff0c;帮助您选择最合适的华为云数据库。 1. 了解业务需求 在选择数据库之前&#xff0c;首先需要了…

西米:未来的支付还能做吗?

未来支付行业还能做吗&#xff1f;一直是在洗牌&#xff0c;一直让你有上场的机会&#xff0c;做一个行业&#xff0c;最好的时间是行业刚刚开始&#xff0c;市场相对空白&#xff0c;跑马圈地&#xff0c;广撒网&#xff0c;利用时差赚钱&#xff0c;这是最好的时间。 另外&a…

【Kubernets】容器网络基础二:通讲CNI(Container Network Interface)容器网络接口实现方案

文章目录 背景知识Underlay网络Overlay网络一、基本概念二、工作原理三、实现方案四、应用场景 两者对比示意图 CNI实现有哪些&#xff1f;FlannelFlannel 的工作原理Flannel 的主要组件数据传输机制总结 Calico一、架构基础二、核心组件与功能三、路由与数据包转发四、安全策略…

Java微信支付接入(4) - API V3 API字典和相关工具

1. API列表 Native下单 - Native支付 | 微信支付商户文档中心 (qq.com) 以下是微信提供的 Native 支付的相关 API 微信提供了详细的请求接口和参数 2.接口规则 概述 - 通用规则 | 微信支付商户文档中心 (qq.com) 微信支付 APIv3 使用 JSON 作为消息体的数据交换格式。 JSO…