【正点原子FPGA连载】第二十六章gpio子系统简介 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Linux开发指南

news2024/10/10 10:29:37

1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html

第二十六章gpio子系统简介

上一章我们编写了基于设备树的LED驱动,但是驱动的本质还是没变,都是配置LED灯所使用的GPIO寄存器,驱动开发方式和裸机基本没啥区别。在驱动程序用到了GPIO就直接去读写GPIO相关的寄存器,这样会引发一个问题,大家有没有想过,如果另外一个驱动工程师写了一个驱动也用到这个相同的管脚,那么它也会去操作这些GPIO寄存器,也就是说多个驱动代码中都用了这个GPIO,那么这会乱套的,对于linux系统来说是绝对不允许的事情!
内核维护者在内核中设计了一些统一管控系统资源的体系,这些体系让内核能够对系统资源在各个驱动之间的使用统一协调和分配,保证整个内核的稳定健康运行。例如系统中所有的GPIO就属于系统资源,每个驱动模块如果要使用某个GPIO就要先调用特殊的接口先申请,申请到后使用,使用完后要释放。又譬如中断号也是一种资源,驱动在使用前也必须去申请,只能申请成功之后才能使用,否则不能使用。
所以本章就引入了gpio子系统,那么gpio子系统其实就是内核中管控GPIO资源的一套体系。

22.1gpio子系统

22.1.1gpio子系统简介
gpio子系统是linux内核当中用于管理GPIO资源的一套系统,它提供了很多GPIO相关的API接口。驱动程序中使用GPIO之前需要向gpio子系统申请,申请成功之后才可以使用,例如设置GPIO的输入、输出方向,设置GPIO输出高或低电平、读取GPIO输入电平等等。
gpio子系统的主要目的就是方便驱动开发者使用gpio,驱动开发者在设备树中添加gpio相关信息,然后就可以在驱动程序中使用gpio子系统提供的API函数来操作GPIO,Linux内核向驱动开发者屏蔽掉了GPIO的设置过程,极大的方便了驱动开发者使用GPIO。

22.1.2ZYNQ MPSoC的gpio子系统驱动

gpio子系统虽然方便了驱动开发者使用gpio,但是最终还是得去操作硬件寄存器;所以在使用gpio子系统之前,我们需要向内核gpio子系统注册这一套操作硬件寄存器的“方法”,关于这个后面再说,我们先来看看在设备树中是如何描述gpio信息。
1、设备树中的gpio信息
我们来看个示例,如下所示:
示例代码22.1.2.1 gpio信息

11 gpio-led0 {
12     label = "ps_led1";
13     gpios = <&gpio 38 GPIO_ACTIVE_HIGH>;
14     linux,default-trigger = "timer";
15 };
 ……
41 gpio-key1 {
42     label = "ps_key1";
43     gpios = <&gpio 40 GPIO_ACTIVE_LOW>;
44     linux,code = <KEY_UP>;
45 };

在上面的示例当中,定义了两个节点gpio-led0和gpio-key1,其中gpio-led0节点是对应了一个led设备,gpio-key1节点对应了一个按键设备;第13行,gpio-led0节点中定义了一个属性“gpios”,该属性用于描述LED设备由那个GPIO控制,属性值一共有三个,我们来看一下这三个属性值的含义,“&gpio”表示led引脚所使用的IO属于gpio,“38”表示gpio的第38号IO,通过这两个值led驱动程序就知道led引脚使用了GPIO MIO38。“GPIO_ACTIVE_HIGH”表示高电平有效,如果改为“GPIO_ACTIVE_LOW”就表示低电平有效(GPIO_ACTIVE_HIGH和GPIO_ACTIVE_LOW其实是宏定义,GPIO_ACTIVE_HIGH等于0,GPIO_ACTIVE_LOW等于1);高电平有效的意思就是,当GPIO MIO38管脚输出高电平时led才会被点亮。
根据上面这些信息,LED驱动程序就可以使用GPIO MIO38来控制LED灯的亮灭状态了,打开zynqmp.dtsi,在里面找到如下所示内容:
示例代码22.1.2.2 gpio0节点

648 gpio: gpio@ff0a0000 {
649         compatible = "xlnx,zynqmp-gpio-1.0";
650         status = "disabled";
651         #gpio-cells = <0x2>;
652         interrupt-parent = <&gic>;
653         interrupts = <0 16 4>;
654         interrupt-controller;
655         #interrupt-cells = <2>;
656         reg = <0x0 0xff0a0000 0x0 0x1000>;
657         gpio-controller;
658         power-domains = <&zynqmp_firmware 46>;
659 };

gpio节点信息描述了ZYNQ MPSoC器件PS GPIO控制器的所有信息,重点就是GPIO外设寄存器基地址以及兼容属性。关于ZYNQ MPSoC的PS GPIO控制器绑定信息请查看内核源码中的文档Documentation/devicetree/bindings/gpio/gpio-zynq.txt。
第649行,设置gpio节点的compatible属性为“xlnx,zynqmp-gpio-1.0”,在Linux内核中搜索这两个字符串就可以找到ZYNQ MPSoC的GPIO驱动程序。
第656行,reg属性设置了GPIO控制器的寄存器基地址为0xFF0A0000,大家可以打开ug1085-zynq-ultrascale-trm.pdf手册,找到“Ch.10 System Addresses”章节的PS I/O Peripherals Registers小节,如图 22.1.1所示:
在这里插入图片描述

图 22.1.1 ZYNQ MPSoC GPIO寄存器基地址
从图 22.1.1可以看出,GPIO控制器的基地址就是0xFF0A0000。
第657行,“gpio-controller”表示gpio节点是个GPIO控制器,表示这个节点对应的驱动程序是gpio驱动。
第651行,“#gpio-cells”属性和“#address-cells”类似,在gpio节点中#gpio-cells的值等于0x2,表示一共有两个cell,大家可以这样理解,使用gpio的时候,需要传递2个参数过去,第一个参数为GPIO编号,比如“&gpio 38”就表示GPIO MIO38。第二个参数表示GPIO极性,如果为0(GPIO_ACTIVE_HIGH)的话表示高电平有效,如果为1(GPIO_ACTIVE_LOW)的话表示低电平有效。
所以在led节点中“led-gpio = <&gpio 38 GPIO_ACTIVE_HIGH>;”就表示使用了GPIO Bank1 MIO38这个管脚,并且是高电平有效。
以上就讲解了在设备树如何指定、描述一个gpio。
2、GPIO驱动程序简介
在前面给大家说过,gpio子系统虽然方便了驱动开发者使用gpio,但是最终还是得去操作硬件寄存器;所以在使用gpio子系统之前,我们需要向内核gpio子系统注册这一套操作硬件寄存器的“方法”,那么这套“方法”就是由GPIO驱动程序去实现、并注册到gpio子系统,所以GPIO驱动程序就负责实现GPIO操控硬件寄存器的代码,并注册到内核gpio子系统中由gpio子系统进行统一管控。
所以怎么去控制GPIO的高低电平、怎么去设置输入输出方向,怎么读取GPIO高低电平等这些代码都是在GPIO驱动程序中实现的。
gpio节点的compatible属性描述了兼容性,在Linux内核中搜索“xlnx,zynqmp-gpio-1.0”这个字符串,找到我们ZYNQ MPSoC的GPIO驱动程序代码。drivers/gpio/gpio-zynq.c就是ZYNQ MPSoC的GPIO驱动程序,在此文件中有如下所示of_device_id匹配表:
示例代码22.1.2.3 zynq_gpio_of_match配置表

870 static const struct of_device_id zynq_gpio_of_match[] = {
871         { .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def },
872         { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
873         { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def },
874         { .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def },
875         { /* end of table */ }
876 };

第872行的compatible值为“xlnx,zynqmp-gpio-1.0”,和gpio节点的compatible属性匹配,因此gpio-zynq.c就是ZYNQ MPSoC的GPIO控制器驱动文件。gpio-zynq.c所在的目录为drivers/gpio,进入到这个目录下可以看到很多芯片的gpio驱动文件,“gpiolib”开始的文件是gpio驱动的核心文件,如下图所示:
在这里插入图片描述

图 22.1.2 gpio驱动核心文件
gpio-zynq.c文件的内容这里先不分析,等后面时机成熟的时候会专门介绍gpio-zynq.c程序以及如何编写一个GPIO驱动程序。

22.1.3gpio子系统API函数

对于驱动开发人员,设置好设备树以后就可以使用gpio子系统提供的API函数来操作指定的GPIO,gpio子系统向驱动开发人员屏蔽了具体的读写寄存器过程。这就是驱动分层与分离的好处,大家各司其职,做好自己的本职工作即可。gpio子系统提供的常用的API函数有下面几个:
1、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设置个名字。
返回值:0,申请成功;其他值,申请失败。
2、gpio_free函数
如果不使用某个GPIO了,那么就可以调用gpio_free函数进行释放。函数原型如下:
void gpio_free(unsigned gpio)
函数参数和返回值含义如下:
gpio:要释放的gpio标号。
返回值:无。
3、gpio_direction_input函数
此函数用于设置某个GPIO为输入,函数原型如下所示:
int gpio_direction_input(unsigned gpio)
函数参数和返回值含义如下:
gpio:要设置为输入的GPIO标号。
返回值:0,设置成功;负值,设置失败。
4、gpio_direction_output函数
此函数用于设置某个GPIO为输出,并且设置默认输出值,函数原型如下:
int gpio_direction_output(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置为输出的GPIO标号。
value:GPIO默认输出值。
返回值:0,设置成功;负值,设置失败。
5、gpio_get_value函数
此函数用于获取某个GPIO的值(0或1),此函数是个宏,定义所示:
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
函数参数和返回值含义如下:
gpio:要获取的GPIO标号。
返回值:非负值,得到的GPIO值;负值,获取失败。
6、gpio_set_value函数
此函数用于设置某个GPIO的值,此函数是个宏,定义如下
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置的GPIO标号。
value:要设置的值。
返回值:无
关于gpio子系统常用的API函数就讲这些,这些是我们用的最多的。
22.1.4与gpio相关的OF函数
示例代码22.1.2.1中使用gpios属性指定了LED灯对应的GPIO,那么在驱动程序中就需要去读取这些属性的内容,Linux内核提供了几个与GPIO有关的OF函数,常用的几个OF函数如下所示:
1、of_gpio_named_count函数
of_gpio_named_count函数用于获取设备树某个属性里面定义了几个GPIO信息,要注意的是空的GPIO信息也会被统计到,比如:
gpios = <0
&gpio1 1 2
0
&gpio2 3 4>;
上述代码的“gpios”节点一共定义了4个GPIO,但是有2个是空的,没有实际的含义。通过of_gpio_named_count函数统计出来的GPIO数量就是4个,此函数原型如下:
int of_gpio_named_count(struct device_node *np, const char *propname)
函数参数和返回值含义如下:
np:设备节点。
propname:要统计的GPIO属性。
返回值:正值,统计到的GPIO数量;负值,失败。
2、of_gpio_count函数
和of_gpio_named_count函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属性的GPIO数量,而of_gpio_named_count函数可以统计任意属性的GPIO信息,函数原型如下所示:
int of_gpio_count(struct device_node *np)
函数参数和返回值含义如下:
np:设备节点。
返回值:正值,统计到的GPIO数量;负值,失败。
3、of_get_named_gpio函数
此函数获取GPIO编号,gpio子系统为了方便管理系统中的GPIO资源,每一个GPIO管脚都有一个对应的编号,Linux内核中关于GPIO的API函数都要使用GPIO编号,此函数会将设备树中类似<&gpio 38 GPIO_ACTIVE_LOW>的属性信息转换为对应的GPIO编号,此函数在驱动中使用很频繁!函数原型如下:
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
函数参数和返回值含义如下:
np:设备节点。
propname:包含要获取GPIO信息的属性名。
index:GPIO索引,因为一个属性里面可能包含多个GPIO,此参数指定要获取哪个GPIO的编号,如果只有一个GPIO信息的话此参数为0。
返回值:正值,获取到的GPIO编号;负值,失败。
22.2pinctrl子系统
gpio子系统是用于管理系统中的GPIO资源的,那么pinctrl子系统又是做什么的呢?pinctrl其实就是PIN control的一个缩写形式。
如果使用过STM32的话应该都记得,STM32也是要先设置某个PIN的复用功能以及电气特性,例如IO速率、上下拉等,其实对于大多数SOC而言,PIN都是需要设置复用功能和电气特性,因为大多数SOC的pin都是支持复用的,同一个PIN可以作为多种功能,例如vivado中配置SD1时,可以使用MIO4651,也可以选择MIO7176等,以及配置它们的电气特性,如所示:
在这里插入图片描述

图 22.2.1 vivado中配置SD1
因此Linux内核针对PIN的配置推出了pinctrl子系统,对于GPIO的配置推出了gpio子系统,所以说到这里就知道了,pinctrl子系统是内核中专门用于管理、配置PIN的一套子系统。
配置PIN的复用功能和电器特性也是通过控制硬件寄存器来实现的,所以pinctrl子系统最终也是如此。有gpio驱动程序,那必然也有pinctrl驱动程序,pinctrl驱动程序中实现了PIN的配置方法并,并注册到pinctrl子系统,所以pinctrl驱动程序就负责实现配置PIN的底层代码(主要就是寄存器控制),并注册到内核pinctrl子系统中由pinctrl子系统进行统一管理。
在内核源码drivers/pinctrl目录下有很多的pinctrl-xxx.c文件,它们都是不同SoC所对应的pinctrl驱动程序源文件,例如对于ZYNQ MPSoC来说,pinctrl-zynqmp.c就是它的pinctrl驱动程序文件。pinctrl-zynqmp.c文件的内容这里先不分析,等后面时机成熟的时候会专门介绍pinctrl-zynqmp.c程序以及如何编写一个pinctrl驱动程序。
对于ZYNQ MPSoC来说,我们使用了vivado图形化完成了对PIN的配置并在fsbl阶段将配置信息写入了硬件寄存器中(具体的过程就不分析了),所以不需要在内核阶段进行配置,所以就不详细介绍pinctrl子系统了,本小节的只是告诉大家pinctrl子系统的存在以及它的作用,对于ZYNQ MPSoC来说,我们可以忽略它!

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

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

相关文章

NVDLA Xilinx FPGA Mapping

Lei WangLeiWang1999要当世界第一&#xff01;78357联系我常用的链接1. 1. 硬件系统设计概述1.1. 1.1 RTL 生成1.2. 1.2 IP Package1.2.1. 1.2.1 csb2apb1.2.2. 1.2.2 关闭 Clock Gating1.2.3. 1.2.3 IP Package1.3. 1.3 Block Design1.4. 1.4 Generate Bit HDF1.5. 1.5 Sanity…

java基础一JVM之JRE、JDK、解释器、编译器详解

1.JVM、JRE和JDK区别 1.JVM&#xff08; Java Virtual Machine &#xff09;&#xff1a; Java虚拟机&#xff0c;它是整个 Java 实现跨平台的最核心的部分&#xff0c;所有的 Java 程序会首先被编译为 .class 的类文件&#xff0c;这种类文件可以在虚拟机上执行&#xff0c;…

3.10-动态规划-01背包问题

问题描述&#xff0c;给定n种物品和一个背包。物品 i 的重量是 wi &#xff0c;其价值为 vi &#xff0c;背包的容量为 c &#xff0c;问应该如何选择装入背包中的物品&#xff0c;使得装入背包的物品总价值最大&#xff1f; 写在前面 dp数组的含义--dp[i][j]表述容量为j 已经…

【计算机体系结构-03】ISA (Instruction Set Architecture) 指令集架构特性

1. 指令的类型 上一篇文章里主要介绍了几种机器模型&#xff0c;有机器模型后需要知道计算机有什么样的基本指令&#xff0c;接下来就来看看指令都有哪些类型。 [注]&#xff1a;以下指令主要为 MIPS 指令。 类型指令数据传输LD、ST、MFC1、MTC1、MFC0、MTC0计算ADD、SUB、AN…

Vue3和Vue2的slot-scope插槽用法

目录 &#x1f9e8;&#x1f9e8;&#x1f9e8;第一种插槽&#xff08;匿名插槽&#xff09; &#x1f9e8;&#x1f9e8;&#x1f9e8;第二种插槽&#xff08;具名插槽&#xff09;以及插槽简写 具名插槽的使用 &#x1f9e8;&#x1f9e8;&#x1f9e8;第三种插槽(作用域插…

使用HTTP代理后,网速反而变慢是什么原因?

如今越来越多的人利用HTTP代理开展业务&#xff0c;但在实际使用时&#xff0c;经常会有用户发现使用了HTTP代理后&#xff0c;网速非但没有变快&#xff0c;反而还更慢了。我们今天就来说说&#xff0c;这是什么原因造成的&#xff0c;从根本入手对于我们能更有利的解决问题。…

2.Spring IOC

目录 一.如何进行注册 二、如何进行注入 三、扫描注解的原理 反射文件操作 四、什么是IOC/DI&#xff1f; 五、演示使用Spring开发的案例&#xff1a;用户管理|登陆、注册 五、常见错误总结 1、注解使用Repository 2、UserController的构造方法注入&#xff1a; 3、…

【FreeRTOS】第一章:介绍

FreeRTOS是什么&#xff1f; Free和RTOS,Free就是免费的、自由的意思&#xff0c;RTOS 全称是 Real Time Operating System。中文名就是实时操作系统。可以看出FreeROTS 就是一个免费的 RTOS 类系统。这里要注意&#xff0c;RTOS 不是指某一个确定的系统&#xff0c;而是指一类…

联想电脑安装ubuntu18.04双系统超详细教程(23年最新教程,99%成功率)

文章目录前言电脑配置制作系统盘安装ubuntu系统更新显卡驱动安装wifi驱动完成前言 ubtuntu的长期支持版本现在应该已经出道21.04版本了&#xff0c;如果你对于版本没有要求的话&#xff0c;建议直接安装最新版ubuntu&#xff0c;因为新版的系统驱动都会进行更新&#xff0c;也…

自学 Java 怎么入门?

玩Java多年的老司机带你上车全面系统学习Java&#xff0c;并且还能教你如何学习才能在今年拿到一份不错的offer。 说到系统全面&#xff0c;就是以目前绝大部分公司招聘要求的知识内容为基准&#xff0c;毕竟我们学习Java都是为了高薪工作&#xff0c;《史记》中说”天下熙熙皆…

2.【SpringBoot源码】SpringBoot核心启动流程

目录 一、简介 二、创建SpringApplication对象 1)、推导出当前启动的项目的类型 2)、设置Initializer初始化器 3)、初始化Listener监听器 4)、反推出main方法所在的Class对象 三、运行SpringApplication#run(java.lang.String...)方法 1)、获取运行监听器 2)、发布…

unity使用对象池实现冲锋留下的残影效果

目录 效果展示 实现思路 残影代码 对象池代码 控制冲刺产生残影 CD冷却图标 效果展示 实现思路 对象池&#xff0c;有想要用的物体时可以从池子里取&#xff0c;用完再放回去。 因为在生成残影再销毁&#xff0c;这个过程中创建和销毁都需要耗费大量资源&#xff0c;因此…

shell 条件测试详解

目录 shell条件测试 一&#xff0c;条件测试的基本语法 1&#xff0c;test 2&#xff0c;[ ] 3&#xff0c;[[ ]] 二&#xff0c;文件测试表达式 1&#xff0c;判断目录是否存在&#xff1a; 2&#xff0c;判断文件file1是否有写的权限&#xff0c;结果为有 3&#xf…

重学MySQL基础(一)

文章目录重学MySQL基础&#xff08;一&#xff09;MySQL 连接管理MySQL字符编码InnoDB 记录存储结构InnoDB 表的主键生成策略&#xff1a;InnoDB 数据页结构页目录页的效验和索引事务报错记录在MySQL中创建函数时出现这种错误恶补SQL语句SQL中的条件语句SQL中的字符串函数SQL中…

python调用go语言踩坑记录

目录 基本操作 1 在go文件中加注释&#xff0c;设置为导出方法,导出C依赖 2 导出so文件&#xff08;mac或者linux下只需要so&#xff09; 3 进行调用 报错记录 踩坑1 关于结构体 2 cannot use (_Cfunc_CString)("12345") (value of type *_Ctype_char) as ty…

spring中事务失效场景

文章目录spring中事务失效场景一、权限访问问题二、方法用final修饰三、无事务嵌套有事务的方法四、没有被spring管理五、设计的表不支持事务六、没有开启事务七、错误的事务传播八、自己捕获了异常九、手动抛出别的异常十、自定义回滚异常spring中事务失效场景 一、权限访问问…

软件研发管理经验总结 - 事务管理

软件研发管理经验总结 - 事务管理 相关系列文章 软件产品研发管理经验总结-管理细分 软件研发管理经验总结 - 事务管理 目录软件研发管理经验总结 - 事务管理一、概述二、事务管理过程1、制定开发计划2、启动会议3、阅读前一天的日报4、例会/早会5、调整计划6、协调资源7、日报…

LeetCode——2325. 解密消息

一、题目 给你字符串 key 和 message &#xff0c;分别表示一个加密密钥和一段加密消息。解密 message 的步骤如下&#xff1a; 使用 key 中 26 个英文小写字母第一次出现的顺序作为替换表中的字母 顺序 。 将替换表与普通英文字母表对齐&#xff0c;形成对照表。 按照对照表…

vue全家桶(三)前端路由

vue全家桶&#xff08;三&#xff09;前端路由1.路由的概念1.1路由1.2vue Router2.vue-router的基本使用步骤2.1基本使用步骤2.2路由重定向3.vue-router的嵌套路由用法3.1嵌套路由的用法4.vue-router动态路由匹配用法5.vue-router命名路由用法6.vue-router编程式导航用法6.1 页…

06 Sentinel控制台规则配置讲解 (2)

1、实时监控 监控接口的通过的QPS和拒绝的QPS 2、簇点链路 用来显示微服务的所监控的API 3、流控规则 流量控制&#xff08;flow control&#xff09;&#xff0c;其原理是监控应用流量的 QPS 或并发线程数等指标&#xff0c;当达到指定的阈值时对流量进行控制&#xff0c;以…