Linux驱动学习—pinctl和gpio子系统

2024-01-01 20:36

本文主要是介绍Linux驱动学习—pinctl和gpio子系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、pinctl和gpio子系统(一)

1.1pinctrl 子系统主要工作内容

<1>获取设备树中 pin 信息,管理系统中所有的可以控制的 pin, 在系统初始化的时候, 枚举所有可以控制的 pin, 并标识这些 pin。
<2>根据获取到的 pin 信息来设置 pin 的复用功能,对于 SOC 而言, 其引脚除了配置成普通的 GPIO 之外,若干个引脚还可以组成一个 pin group, 形成特定的功能。
<3>根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。

对应使用者来说,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成。

1.2gpio子系统主要工作内容

当使用 pinctrl 子系统将引脚的复用设置为 GPIO,可以使用 GPIO 子系统来操作GPIO,Linux 内核提供了 pinctrl 子系统和 gpio 子系统用于 GPIO 驱动。

通过 GPIO 子系统功能要实现:

<1>引脚功能的配置(设置为 GPIO,GPIO 的方向, 输入输出模式,读取/设置 GPIO 的值)
<2>实现软硬件的分离(分离出硬件差异, 有厂商提供的底层支持; 软件分层。 驱动只需要调用接口 API 即可操作 GPIO)
<3>iommu 内存管理(直接调用宏即可操作 GPIO)

gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO, Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO。

1.3 不同soc厂家的pin contrller的节点

这些节点都是把某些引脚复用成功能。

1.4 不同soc厂家的pin contrller的节点里面的属性都是什么意思

可以通过在Documentation/devicetree/bindings/下的txt文档查看。

1.5 怎么在代码中使用pin contrller里面定义好的节点?

例1:

pinctrl-names = "default";//设备的状态,可以有多种状态,default为状态0
pinctrl-0 = <&pinctrl_hog_1>;/*第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1里面的管脚配置。*/

例2:

pinctrl-names = "default","wake up";//设备的状态,可以有多种状态,default为状态0,wake up为状态1,
pinctrl-0 = <&pinctrl_hog_1>;/*第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1里面的管脚配置。*/
pinctrl-1 = <&pinctrl_hog_2>;/*第1个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_2里面的管脚配置。*/

例3:

pinctrl-names = "default";//设备的状态,可以有多种状态,default为状态0,wake up为状态1,
pinctrl-0 = <&pinctrl_hog_1   &pinctrl_hog_2>;/*第0个状态所对应的引脚配置,也就是default状态对应的引脚在pin controller里面定义好的节点pinctrl_hog_1和pinctrl_hog_2这两个节点的管脚配置。*/

1.6 总结

总结:之前控制引脚的方法都是操作配置寄存器:

现在可以不用这种方法,linux有现成的框架,这个框架就是pinctl子系统和gpio子系统,可以pinctl子系统设置引脚的复用功能,设置引脚的电气属性。

2、pinctl和gpio子系统(二)

上一个小节我们学习了pinctrl子系统,Linux内核提供了pinctrl子系统和gpio子系统用于GPIO驱动,当然pinctrl子系统负责不仅仅是GPIO的驱动,而是所有pin脚配置。pinctrl子系统是随着设备树的加入而加入的,依赖设备树。GPIO子系统在之前的内核也是存在的,但是pinctrl子系统的加入使得GPIO子系统有很大的改变。

在以前的内核版本中,如果要配置GPIO的话一般要使用SOC厂家实现的配置函数,例如三星的配置函数s3c_gpio_cfgpin等,这样带来的问题就是各家有个家的接口函数与是实现方式,不但内核的代码复用率低而且开发者很难记住这么多的函数,如果要使用多种平台的话背函数都是很麻烦的,所以在引入设备树后对GPIO子系统进行大的改造,使用设备树来实现并提供统一的接口。通过GPIO子系统功能主要实现引脚功能的配置,如设置为GPIO,特殊功能,GPIO的方向,设置为中断等。

那么我们先来看一下怎么在设备树中pinctrl和gpio子系统描述一个gpio。

2.1 设备树使用pinctrl和gpio子系统描述一个gpio

test1:test{#address-cells = <1>;#size-cells = <1>;compatible = "test";reg = <0x20ac000 0x00000004>;//描述数据寄存器的地址pinctrl-names = "default";pintrl-0 = <&pinctrl_test>;test-gpio = <gpio1 3 GPIO_ACTICE_LOW>;//gpio 表示第一组,3表示第一组第三个引脚,GPIO_ACTICE_LOW表示低电平
};

2.2 常用的gpio子系统提供的api函数

这些函数的定义在include\linux\gpio.h

2.2.1 gpio_request函数

作用: gpio_request函数用于申请一个gpio管脚。

int gpio_request(unsigned gpio, const char *label)
参数:
gpio:要申请的gpio标号,使用of_get_named_gpio函数从设备树获取指定的GPIO属性信息,此函数会返回这个GPIO标号。
label:给gpio设置个名字。
返回值:0,申请成功,其他值申请失败。
2.2.2 gpio_free函数

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

void gpio_free(unsigned gpio);
参数:
gpio:要释放的gpio标号。
返回值:无
2.2.3 gpio_direction_input函数

作用:此函数用于设置某个GPIO为输入。

int gpio_direction_input(unsigned gpio);
参数:
gpio:要设置为输入的GPIO标号。
返回值:0,设置成功,其他值设置失败。
2.2.4gpio_direction_output函数

作用:此函数用于设置某个GPIO为输出,并且设置默认输出值。

int gpio_direction_output(unsigned gpio, int value);
参数:
gpio:要设置为输出的GPIO标号。
value:GPIO默认输出值。
返回值:0,设置成功,设置失败返回负值。
2.2.5 gpio_get_value函数

作用:此函数用于获取某个GPIO的值(0或1)

int gpio_get_value(unsigned int gpio);
gpio:要获取的gpio标号
返回值:0,成功,失败返回负值。
2.2.5 gpio_set_value函数

作用:此函数用于获取某个GPIO的值(0或1)

void gpio_set_value(unsigned int gpio, int value);
gpio:要设置的gpio标号
value:要设置的值。
返回值:无。

2.3 总结

pinctl子系统的作用就是设置引脚的复用功能和电气属性。gpio子系统就是当pinctl子系统把引脚设置成GPIO功能以后就可以使用gpio子系统来操作我们引脚了,比如说设置输入、输出或者引脚的高低电平等等。

3、pinctl和gpio子系统(三)

pinctrl子系统就是设置引脚的复用关系和电气属性,gpio子系统就是当pinctrl把引脚设置成设置为gpio以后我们使用gpio子系统来操作gpio。

3.1 引脚的宏定义是在哪里找的

在arch/arm/boot/dts/imx6ul-pinfunc.h:

每个宏定义都对应一个管脚的复用功能。一个引脚有怎么多复用功能,但是只能使能一个,所以在设备树下需要检察是否有其他复用功能被使用,有就需要在设备树文件其他使用的地方注释掉:

3.2 实验

Linux驱动学习—设备树及设备树下的platform总线-CSDN博客

实现设备树学习中的7.3未实现的部分,即在probe函数注册一个杂项设备驱动用于对蜂鸣器的操作。这里对引脚的操作不是相之前一样对地址寄存器的操作实现gpio电平值的改变,而是通过gpio子系统的api函数是实现。

3.2.1 设备树文件修改

3.2.2 实验代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> 
#include <linux/of.h>
#include <linux/of_address.h>
​
struct device_node *test_device_node;
struct property *test_node_property;
int size;
u32 out_values[2]={0};
const char *str=NULL;
unsigned int *vir_gpio_dr;
int beep_gpio = 0;
​
static const of_device_id of_match_table_test[] = {//匹配表{.compatible = "test1234"},
};
​
static const platform_device_id beep_id_table ={.name = "beep_test",
};
​
int misc_open(struct inode *inode, struct file *file)
{printk("misc_open\n");return 0;
}
​
int misc_release(struct inode *inode, struct file *file)
{printk("misc_relese\n");return 0;
}
​
ssize_t misc_read(struct file *file,char __user *ubuf, size_t size, loff_t *loff_t)
{char kbuf[64] = "heheh";if(copy_to_user(ubuf, kbuf, strlen(kbuf)) != 0) {printk("copy_to_user error\n");return -1;}return 0;
}
​
ssize_t misc_wirie(struct file *file,char __user *ubuf, size_t size, loff_t *loff_t)
{char kbuf[64] = {0};if(copy_form_user(kbuf, ubuf, strlen(kbuf)) != 0) {printk("copy_form_user error\n");return -1;}printk("kbuf is %s\n",kbuf);if(kbuf[0] == 1)get_set_value(beep_gpio, 1);else if(kbuf[0] == 0)get_set_value(beep_gpio, 0);return 0;
}
​
struct file_operations misc_fops = {.owner      = THIS_MODULE,.open       = misc_open,.release    = misc_release,.write      = misc_wirie,.read       = misc_read
};
​
struct miscdevice misc_dev = {.minor = MISC_DYNAMIC_MINOR,.name  = hello_misc,.fops  = &misc_fops
};
​
/*设备树节点compatible属性与of_match_table_test的compatible相匹配就会进入该函数,pdev是匹配成功后传入的设备树节点*/
int beep_probe(struct platform_device *pdev)
{int ret = 0;printk("beep_probe\n");//查找要查找的节点test_device_node = of_find_node_by_path("/test");if (test_device_node == NULL) {printk("test_device_node find error\n");return -1;}printk("test_device_node name is %s\n",test_device_node->name);//testbeep_gpio = of_get_named_gpio(test_device_node, "beep-gpio", 0);if (beep_gpio < 0) {printk("of_get_named_gpio error\n");return -1;}printk("beep_gpio name is %d\n",beep_gpio);ret = gpio_request(beep_gpio, "beep");if (ret < 0) {printk("gpio_request error\n");return -1;}ret = misc_register(&misc_dev);if (ret < 0) {printk("misc_register error\n");return -1;}return 0;
}
​
int beep_remove(struct platform_device *pdev)
{pritnk("beep_remove \n");return 0;
}
​
strcut platform_driver beep_device = {.probe = beep_probe,.remove = beep_remove,.driver = {.owner = THIS_MODULE,.name  = "123",.of_match_table = of_match_table_test,//匹配表 },.id_table = &beep_id_table,
};
​
static int beep_driver_init(void)
{int ret = -1;ret = platform_driver_register(&beep_device);if(ret < 0) {printk("platform_driver_register error \n");}printk("platform_driver_register ok\n");return 0;
}
​
static void  beep_driver_exit(void)
{platform_driver_unregister(&beep_device);printk("beep_driver_exit \n");
}
​
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");

加载驱动,可以看到杂项设备节点生成,对这个设备节点写入1就表示引脚电平设置为高,,对这个设备节点写入0就表示引脚电平设置为低,

echo 1 > /dev/hello_misc
echo 0 > /dev/hello_misc

这篇关于Linux驱动学习—pinctl和gpio子系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss