【IMX6ULL驱动开发学习】13.Pinctrl子系统与GPIO子系统

news2024/11/17 3:25:57

上一篇博客中,已经实现了设备树的添加
【IMX6ULL驱动开发学习】12.Linux驱动之设备树

这篇博客介绍Pinctrl子系统与GPIO子系统的使用
Pinctrl子系统参考文档:
内核文档链接:https://www.kernel.org/doc/Documentation/
内核源码doc:Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

GPIO子系统参考文档:
内核文档链接:https://www.kernel.org/doc/Documentation/
内核源码doc:Documentation/devicetree/bindings/gpio/各个芯片厂商文件

代码自取【13.led_button_drv_tree_gpio_pinctrl】:
https://gitee.com/chenshao777/imx6-ull_-drivers


为什么要用Pinctrl子系统与GPIO子系统?

驱动开发中,我们可以使用寄存器对外设进行操作,但是那样太麻烦了
所以引入了 Pinctrl子系统与GPIO子系统

Pinctrl子系统: 可以用它来复用引脚、配置引脚的电气属性等等。
GPIO子系统: 控制引脚,设置输入输出。


之前在设备树中定义硬件资源的方法
(1)设备树节点中自定义 pin 属性
(2)在驱动代码中通过 of_property_read_u32 函数读取属性值,得到具体引脚
(3)根据GPIO组和pin对寄存器地址进行映射 (ioremap)
在这里插入图片描述
弊端: 还是需要在驱动代码中进行寄存器的操作,进行地址映射操作 ( ioremap )
在这里插入图片描述


引入Pinctrl子系统和GPIO子系统

总结图片,一览无遗
在这里插入图片描述

1、Pinctrl子系统
Pinctrl子系统是负责引脚的复用,和属性定义
可以认为它对应IOMUX──用来复用引脚,还可以配置引脚(比如上下拉电阻等)

//client端:
@节点名字 {
    pinctrl-names = "default, sleep";    // 定义有几个状态
    pinctrl-0 = <&pinctrl_自定义名字A>;   // 第一个状态对应的引脚属性
    pinctrl-1 = <&pinctrl_自定义名字B>;   // 第二个状态对应的引脚属性
    xxx-gpio  = <&gpiox  n  flag>;       // 
    status = "okay";
};

//pincontroller服务端
pinctrl_自定义名字A: 自定义名字 {
    fsl,pins = <
            引脚复用宏定义   PAD(引脚)属性, // 引脚 A
    >;
};
pinctrl_自定义名字B: 自定义名字 {
    fsl,pins = <
            引脚复用宏定义   PAD(引脚)属性; // 引脚 B
    >;
};

PS:
一个引脚可以有多种状态 ,例如可以配置成串口模式,睡眠的时候为了省点配置成GPIO模式

2、添加gpio属性,指定引脚和有效状态

@节点名字 {
    pinctrl-names = "default, sleep";    // 定义有几个状态
    pinctrl-0 = <&pinctrl_自定义名字A>;   // 第一个状态对应的引脚属性
    led-gpio  = <&gpio1  3  GPIO_ACTIVE_LOW>;   //指定GPIO1_3引脚,模式低电平有效 
    status = "okay";
};

使用实例:

设备节点

在这里插入图片描述
pinctrl端

在这里插入图片描述
提问:

为什么设备节点中 led-gpio 后面是<&gpio1 3 flag>
&gpio1 是 组
为什么后面要加上两个位呢?这是因为在 imx6ull.dtsi 文件中定义了 gpio1节点,且定义了 pinctrl ,其中 #gpio-cells = <2>; ,这个属性就表明了,GPIO组后面要跟两个参数

在这里插入图片描述
提问:

pinctrl端 “MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0” 是怎么得来的?

想要获得IMX6ULL 的pinctrl 代码,可以使用官方提供的工具Pins_Tool_for_i.MX_Processors_v6_x64 来获取,下载链接,使用方式自行百度吧嘿嘿
这个 0x10B0 其实就是 参考手册中 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 寄存器设置的值

MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 是一个宏,在 imx6ul-pinfunc.h 文件中定义

3、GPIO子系统API使用

在这里插入图片描述
推荐使用新的一套GPIO子系统

(1)获得GPIO (gpiod_get)

struct gpio_desc *my_dev_gpio;
........
//从设备树中获取GPIO引脚,并设置成默认输出模式,无效(LOW表示无效逻辑)
my_dev_gpio = gpiod_get(dev, "led", GPIOD_OUT_LOW);

第二个参数对应设备树中 xxx-gpio 中的 xxx

led-gpio   = <&gpio1 3 GPIO_ACTIVE_LOW>;

(2)【可选】获取自定义设备名称属性(of_property_read_string

char a[20];
const char *str = a;
of_property_read_string(np, "my_name", &str);

(3)创建设备节点(device_create)

//创建设备节点 /dev/xxx
device_create(my_dev_class, NULL, MKDEV(major, 0), NULL, str);

(4)写GPIO(gpiod_set_value)

gpiod_set_value(my_dev_gpio, status);

(5)释放GPIO,销毁设备节点

//释放GPIO
gpiod_put(my_dev_gpio);
//销毁设备
device_destroy(my_dev_class, MKDEV(major, 0));

全部代码:

我将led和beep都加进来了,所以打开设备节点、销毁节点时要区分是哪一个,所以将下面这两个该成了数组,通过设备树中自定义的 my_name 属性区分设备

char dev_names[10][20]={};    //保存设备树中的名字
struct gpio_desc *my_dev_gpio[10];
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/string.h>

int major;				//设备号
static struct class *my_dev_class;
int dev_cnt;
char dev_names[10][20]={};    //保存设备树中的名字
struct gpio_desc *my_dev_gpio[10];


/*=============================file_operations ==============================*/
static ssize_t my_drv_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
	printk("drv_read function run....\n");
	return 1;
}

static ssize_t my_drv_write (struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
	struct inode *inode = file_inode(filp);
	int minor = iminor(inode);
	char status;
	int err;
	err = copy_from_user(&status, buf, 1);
	gpiod_set_value(my_dev_gpio[minor], status);
	
	printk("drv_write function run....\n");
	return 0;
}

static int my_drv_open (struct inode *node, struct file *filp)
{
	struct inode *inode = file_inode(filp);
	int minor = iminor(inode);

	printk("drv_open function run....\n");
	gpiod_set_value(my_dev_gpio[minor], 0);
	return 0;
}

static int my_drv_release (struct inode *node, struct file *filp)
{
	printk("drv_release function run....\n");
	return 0;
}

/* operations结构体:为应用层提供驱动接口 */
static struct file_operations my_dev_ops = {
	.owner		= 	THIS_MODULE,
	.read 		=	my_drv_read,
	.write		=	my_drv_write,
	.open		=	my_drv_open,
	.release	=	my_drv_release,
};


/*=============================platform_driver==============================*/
/*  如果匹配到了内核根据设备树生成的platform_device,
	该函数会被调用,如果有多个匹配的设备节点,该函数
	会被多次调用
*/
static int my_probe(struct platform_device *pdev)
{
	/*  从内核根据设备树生成的 platform_device 
		结构体中获取到设备节点
	*/
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	
	int gpio_pin;
	char a[20];
	const char *str = a;
	
	of_property_read_string(np, "my_name", &str);
	//保存设备的名字
	strcpy(dev_names[dev_cnt], str);
	
	//从设备树中获取GPIO引脚,并设置成默认输出模式,无效(LOW表示无效逻辑)
	my_dev_gpio[dev_cnt] = gpiod_get(dev, str, GPIOD_OUT_LOW);
	
	//从struct desc结构体转成GPIO子系统标号
	gpio_pin = desc_to_gpio(my_dev_gpio[dev_cnt]);  

	//创建设备节点 /dev/xxx
	device_create(my_dev_class, NULL, MKDEV(major, dev_cnt), NULL, str);
	dev_cnt++;

	printk("my_probe run, my_name = %s\n", str);
	
	return 0;
}

static int my_remove(struct platform_device *pdev)
{
	/*  从内核根据设备树生成的 platform_device 
		结构体中获取到设备节点
	*/
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	
	int gpio_pin, i;
	char a[20];
	const char *str = a;
	int flag = 0;
	
	of_property_read_string(np, "my_name", &str);

	for(i = 0; i < dev_cnt; i++){
		if(strcmp(dev_names[i],str) == 0){
			strcpy(dev_names[i], "");
			gpio_pin = desc_to_gpio(my_dev_gpio[i]); 
			//释放GPIO
			gpiod_put(my_dev_gpio[i]);
			//销毁设备
			device_destroy(my_dev_class, MKDEV(major, i));
			printk("my_remove run, device_destroy %s, my_gpio = %d\n", str, gpio_pin);
		}
		if(dev_names[i])
			flag = 1;
	}
	if(flag == 0){
		dev_cnt = 0;
		printk("all removed\n");
	}
	return 0;
}

static struct of_device_id my_dev_match[] = {
	{.compatible = "hc-led-beep"}, 
	{.compatible = "hc-led-beep"}, 
//	{.compatible = "hc-key"}, 
	{},
};

static struct platform_driver dev_driver = {
	.probe		=	my_probe,	
	.remove		= 	my_remove,
	.driver		= {
		.name	= "my_platform_driver",
		.of_match_table = my_dev_match,
	},
};

/*=============================驱动出入口函数==============================*/
/* 驱动入口函数:insmod xx.ko 时会被调用 */
static int dev_init(void)
{	
	major = register_chrdev(0, "hc_dev_drv", &my_dev_ops);
	if(major < 0){
		printk("register_chrdev famy\n");
		return major;
	}

	my_dev_class = class_create(THIS_MODULE, "my_dev_class");
	if(IS_ERR(my_dev_class)){
		printk("class_create failed\n");
		return 1;
	}

	platform_driver_register(&dev_driver);

	return 0;
}

/* 驱动出口函数: rmmod xx.ko 时会被调用 */
static void dev_exit(void)
{
	platform_driver_unregister(&dev_driver);
	class_destroy(my_dev_class);
	unregister_chrdev(major, "hc_dev_drv");
	printk("my_dev driver exit\n");
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

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

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

相关文章

AD23 原理图选中元件在PCB中高亮显示

概述 项目需要&#xff0c;再次使用AD&#xff0c;在此做个笔录。 1、原理图界面 2、在原理图界面选中电容后&#xff0c;对应的PCB界面该电容高亮显示 3、总结 希望能帮助到有需要的攻城狮。

Linux查看日志常用操作整理

项目出现异常&#xff0c;要定位问题&#xff0c;查看日志是最常用的方法&#xff0c;在Linux系统查看一些日志文件&#xff0c;我们一般会使用tail、cat等命令&#xff0c;下面总结归纳一下这些常用的命令。 1、查看日志的方法 tail&#xff1a;tail命令应该是使用最多的&am…

探讨绿色照明与智能照明节能控制系统应用

张心志 安科瑞电气股份有限公司 上海嘉定 201801 【摘 要】随着社会经济的不断发展&#xff0c;人们对生活质量、环境品质越发重视。积极推广绿色智能照明&#xff0c;提高城市照明质量&#xff0c;对于改善人们居住环境意义重大。文章简要介绍了绿色照 明的基本要求、室内智…

互联网编程之简单邮箱发送程序

需求是使用Java写一个简单的邮箱发送程序。 注意需要到QQ邮箱的设置-账户中开启服务。 package org.example;import org.apache.commons.mail.Email; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.SimpleEmail;public class Main {public …

ospf-interface-fsm-and-neighbor-fsm

/* Interface State Machine */ struct {int (*func) (struct ospf_interface *);int next_state; } ISM [OSPF_ISM_STATE_MAX][OSPF_ISM_EVEN

网络安全技术入门(1):简介

文章目录 1.前言2.什么是网络安全技术&#xff1f;3.列举一些常见的网络安全技术3.1 防火墙3.2 加密技术3.3 身份认证和访问控制3.4 恶意软件防护3.5 网络监控和日志管理3.6 威胁情报和漏洞管理3.7 安全培训和意识教育 4.网络安全研究的关键技术5.网络安全防护技术有哪些&#…

计算机网络 - 应用层http协议 - http报文格式介绍(1)

前言 本篇认识和理解应用层中的http协议&#xff0c;了解抓包工具并进行使用&#xff0c;认识请求报文与响应报文&#xff0c;了解报文中基本键值对意思例如&#xff1a;Set-Cookie, 状态码等&#xff0c;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#…

segement and remove-SAM一键清除物体(代码安装实战项目)

结果展示 去除图片中前景物体的步骤: 1.框选 2.分割 3.分离 4.去除 项目介绍 一键帮你剔除视频内的物体,现在只需要一句话。使用Meta的SAM技术,你现在可以让视频内任意物体消失! 特点 按照提示进行分割:只需输入“黑色狗”,您就可以将您的黑色狗分割出来; 修复图…

JAVA 初识序列化与反序列化

JAVA 初识序列化与反序列化 目录 JAVA 初识序列化与反序列化初识序列化与反序列化1 概述2 特点/应用场景3 涉及到的流对象4 代码实现序列化与反序列化4.1 步骤1&#xff1a;创建学生类Student4.2 步骤2&#xff1a;创建序列化测试类 5 测试报错NotSerializableException:6 测试…

yarn与npm的区别(yarn的安装报错问题)

一、yarn 是什么&#xff0c;yarn 与 npm 的区别是什么&#xff1f; yarn 是一个软件包管理系统&#xff0c;Yarn 和 npm 都是包管理工具&#xff0c;用于管理用 JavaScript 编写的软件包&#xff0c;yarn的出现是为了弥补 npm的一些缺陷。yarn 与 npm 的区别 &#xff1a; 性能…

Matplotlib---3D图

1. 3D图 # 3D引擎 from mpl_toolkits.mplot3d.axes3d import Axes3D fig plt.figure(figsize(8, 5)) x np.linspace(0, 100, 400) y np.sin(x) z np.cos(x)# 三维折线图 axes Axes3D(fig, auto_add_to_figureFalse) fig.add_axes(axes) axes.plot(x,y,z) plt.savefi…

arduino uno r3 机械臂

1、硬件 arduino uno r3 改进板、sg90舵机&#xff08;180度 x 4&#xff09;、JoyStick Shield PS2游戏摇杆扩展板&#xff08;或者按键 遥杆模块&#xff09;、3D打印件、M3螺丝螺母&#xff08;10mm 15mm 25mm &#xff09;以及M2螺丝螺母。 2、接线 开发版使用DC口7V-12V 供…

AI 绘画风格迁移之青铜模型训练

前情提要 2023-07-01 周六 杭州 阴 小记: mmp&#xff0c;上周吃烤面筋&#xff0c;不小心牙签扎到口腔&#xff0c;结果这几天吃什么都不舒服&#xff0c;可是计划还是要做的&#xff0c;下半年要把当下 AI 的应用梳理下&#xff0c;自己还是喜欢那种先使用再学习相关原理&a…

C语言之数组初阶(1-4)

目录 1. 一维数组的创建和初始化 2. 一维数组的使用 3. 一维数组在内存中的存储 4. 二维数组的创建和初始化 5. 二维数组的使用 6. 二维数组在内存中的存储 7. 数组越界 8. 数组作为函数参数 数组概念:数组是一组相同类型元素的集合 1.一维数组的创建和初始化 在C语言中…

【已解决】nacos新增配置报错: 发布失败。请检查参数是否正确

解决方法&#xff1a; sql文件直接采取容器默认自带的创建表&#xff0c;复制出来即可 非容器自带sql&#xff0c;有多余encrypted_data_key字段 1.删掉config_info 和 his_config_info 表中的encrypted_data_key字段 2.config_info 和 his_config_info 表中非空encrypted_data…

Android12之ServiceManager::addService注册服务的本质(一百五十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

如何用smardaten90天快速开发并上线智慧空间loT物联平台?

前言 大家好&#xff0c;我是小白白&#xff0c;前段时间一位好友接手了一个“烫手山芋”开发任务&#xff0c;77万㎡的科技园区需要打造智慧空间物联平台。要求接入600园区设备&#xff0c;处理15000日数据量&#xff0c;在打造整体IOT物联底座之上&#xff0c;构建起整个园区…

三电阻采样的劣势分析

最近控制过程中&#xff0c;被下三桥采样小坑了一下。 如下图所示为下桥臂三电阻采样方式&#xff0c;该方法的采样器件是高精度电阻&#xff0c;电阻两端电压经过采样调理电路输出至单片机ADC&#xff0c;三电阻采样成本低、结构简单&#xff0c;并且采样信号和输出强电之间是…

【Unity3D】素描特效

1 非真实渲染 法线贴图和凹凸映射中讲述了普通光照的渲染原理&#xff0c;实现的效果比较贴近真实世界&#xff08;照相写实主义&#xff0c;Photorealism&#xff09;&#xff0c;非真实渲染&#xff08;Non-Photorealism Rendering&#xff0c;NPR&#xff09;在照相写实主义…

7-数组创建函数还有哪些?【视频版】

目录 问题视频解答 问题 视频解答 点击观看&#xff1a; 7-数组创建函数还有哪些&#xff1f;