本文讲解了pinctrl子系统和gpio子系统的API,以及使用示例。传统的管脚配置方式是直接操作对应的寄存器,但这种配置方式繁琐且容易出现问题(如管脚功能冲突)。pinctrl子系统的引入就是为了解决这个问题。pinctrl子系统的主要工作如下:①获取设备树中的pin信息。②根据获取到的管脚信息设置管脚的复用功能③根据获取到的管脚信息设置管脚的电气特性,如上拉/下拉、速度、驱动能力等。对于我们的用户,我们只需要在设备树中设置某个管脚的相关属性,其他初始化工作由pinctrl子系统完成。如果pinctrl将一个引脚初始化为GPIO而不是IIC或SPI,那么gpio子系统的API就可以使用了。gpio子系统是基于pinctrl子系统的!pincontroller和GPIOController不是一回事,前者的控制引脚可用于功能切换,如GPIO功能、I2C功能等;后者只是将pin配置为输入、输出,设置GPIO方向、取值等简单功能(pinctrl的api其实可以满足所有需求,但gpio功能更常用)1.gpio子系统API如下步骤在gpio子系统中操作一个GPIO需要:1.of_find_compatible_node2,of_get_named_gpio3,gpio_request4,controlgpio(gpio_direction_input,gpio_direction_output...…)5.gpio_free1)of_find_compatible_node函数根据device_type这两个属性在设备树中查找指定节点和compatible,这里是获取设备树中设置的GPIO的节点句柄。如果别处有句柄,可以直接使用这个句柄。2)of_get_named_gpio,获取设置的gpio编号。3)gpio_request,请求这个gpio。如果这个gpio是在别处请求的,还没有发布,那我们就请求不到了。4)请求完这个gpio后,我们就可以对其进行操作了,比如获取它的值,设置它的值。5)使用后释放gpio。原理图:博主有一块正点原子imx6ull开发板,查看原理图,发现直接连接蜂鸣器的GPIO是GPIO5_1。我拉下这个IO口,蜂鸣器会响。在设备树中添加以下代码(imx6ull-alientek-emmc.dts)test:test{compatible="Jason_hello";hello=<&gpio51GPIO_ACTIVE_HIGH>;};设置GPIO为GPIO5_1,高电平有效,但实际上第三个参数我没有用。gpio.c#include#include#include#include#include#includestaticint__initmypinctrl_init(void){intgpionum=0;intret=0;structdevice_node*node=NULL;node=of_find_compatible_node(NULL,NULL,"Jason_hello");if(!node){printk("getnodeerror\n");returnret;}gpionum=of_get_named_gpio(node,"hello",0);if(gpionum<0){printk("getgpionumerror\n");returnret;}ret=gpio_request(gpionum,"hello");if(ret){printk("gpio_requesterror\n");returnret;}printk("gpio(%d)value=%d\n",gpionum,ret);ret=gpio_get_value(gpionum);printk("gpio(%d)value=%d\n",gpionum,ret);gpio_direction_output(gpionum,0);//设置gpio输出低电平ret=gpio_get_value(gpionum);printk("gpio(%d)value=%d\n",gpionum,ret);return0;}staticvoid__exitmypinctrl_exit(void){printk("%s\n",__func__);}module_init(mypinctrl_init);module_exit(mypinctrl_exit);MODULE_LICENSE("GPL");MakefileKERNELDIR:=/home/book/linux/tool/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientekCURRENT_PATH:=$(shellpwd)obj-m:=gpio.obuild:kernel_moduleskernel_modules:$(MAKE)-C$(KERNELDIR)M=$(CURRENT_PATH)modulesclean:$(MAKE)-C$(KERNELDIR)M=$(CURRENT_PATH)clean进入根目录Linux内核源码制作dtbs,编译一个设备树,下载到开发板,在kernel/drivers/misc/新建一个文件夹,命名为mygpio,把gpio.c和Makefile放在里面。然后输入make编译gpio.ko。然后复制到板子里,insmod,可以发现蜂鸣器响了。2.Pinctrl子系统APIpinctrl子系统中有很多API。对于驱动工程师来说,pinctrl操作一个GPIO只需要三步:1.devm_pinctrl_get2,pinctrl_lookup_state3,pinctrl_select_state在Linux中,添加devm_开头的函数,表示该函数支持资源管理。一般我们在写驱动程序的时候,都会在程序的开始申请资源,比如内存,中断号等,万一下一步申请出错,就得回滚到第一步释放已经申请的资源,非常方便。麻烦。后来Linux开发了很多以devm_开头的函数,也就是说这个函数有支持资源管理的版本。不管哪一步出错,只要错误退出,请求的资源就会自动释放。1)devm_pinctrl_get:用于获取设备树中pinctrl创建的节点句柄;2)pinctrl_lookup_state:用于选择一个pinctrl的状态,同一个pinctrl可以有多个状态。比如GPIO50在初始化的时候就是I2C。当设备处于待机状态时,希望切换到普通GPIO模式,配置为下拉输入,以节省电量。这时如果pinctrl节点有描述,我们可以在代码中切换pin的功能,从I2C功能切换到普通GPIO功能;3)pinctrl_select_stat:用于真正的设置,在上一步得到某个状态后,这一步才真正设置为这个状态。对于pinctrl子系统的设备树配置,它遵循服务和客户端结构。客户端各个平台基本相同,服务端各个平台不一样,使用的字符串配置也不一样。设备树配置://客户端,设置不一致&test{pinctrl-names="default","test_low","test_high";pinctrl-0=<&test_default>;pinctrl-1=<&test_low>;pinctrl-2=<&test_high>;gpio=<&gpio51GPIO_ACTIVE_LOW>;status="okay";};//服务器即pincontroller端,设置GPIO几种功能状态&gpio5{test_default:test_default{};test_low:test_low{fsl,pins=7IO0059};test_high:test_low{fsl,pins=};};pinctrl.c#include#include#include#include#include#include#includestaticint__initmypinctrl_init(void){intret=0;structpinctrl*pctrl;structplatform_device*pdev;structpinctrl_state*test_high;structpinctrl_state*test_low;pctrl=devm_pinctrl_get(&pdev->dev);if(IS_ERR(pctrl)){ret=PTR_ERR(pctrl);printk("devm_pinctrl_geterror\n");returnret;}test_high=pinctrl_lookup_state(pctrl,"test_high");if(IS_ERR(pctrl)){ret=PTR_ERR(test_high);printk("pinctrl_lookup_statetest_higherror\n");returnret;}test_low=pinctrl_lookup_state(pctrl,"test_low");if(IS_ERR(pctrl)){ret=PTR_ERR(test_low);printk("pinctrl_lookup_statetest_lowerror\n");returnret;}pinctrl_select_state(pctrl,test_low);udelay(200);pinctrl_select_state(pctrl,test_high);return0;}staticvoid__exitmypinctrl_exit(void){printk("%s\n",__func__);}module_init(mypinctrl_init);module_exit(mypinctrl_exit);MUDULE_GPLICENSE("");Makefile同上,只是改变编译输出的名称和加载驱动来切换GPIO口的功能状态。这里我只是控制GPIO输出电平。这取决于您的设备树是如何配置的。比如可以配置某个GPIO一开始是I2C功能,待机时是普通GPIO功能,达到省电的目的。补充:设备树用于描述板卡上的设备信息。不同的设备有不同的信息,设备树中体现的属性也不同。那么我们在设备树中添加硬件对应的节点时,要参考哪里的相关说明呢?Linux内核源码中有详细的.txt文档描述了如何添加节点。这些.txt文件称为绑定文件,路径为:Linux源码目录/Documentation/devicetree/bindings。比如我们要在I.MX6ULLSOC的I2C下添加一个节点,那么可以查看Documentation/devicetree/bindings/i2c/i2c-imx.txt。本文档详细描述了I.MX系列SOC如何在设备树中添加一个I2C设备节点。有时使用的某些芯片在Documentation/devicetree/bindings目录下找不到对应的文档。这时候就需要咨询芯片供应商,让他们给你提供参考设备树文件。提示:很多时候我们看设备树文件的时候,里面的内容是看不懂的。这时候你可以看看一开始.dts所引用的头文件,点进去,你会发现这里定义了这些字符串。参考文档:Documentation\devicetree\bindings\Pinctrl\Pinctrl-bindings.txtDocumentation\gpio\Pinctrl-bindings.txtDocumentation\devicetree\bindings\gpio\gpio.txtDocumentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt【编辑推荐】鸿蒙官方战略合作共建——鸿蒙技术社区已经2021年了,自己做一个Chrome插件吧!Vue3源码分析图的组件渲染,VNode如何转换为真实DOMVue3源码分析图的Setup,组件渲染前的初始化过程是怎样的?chrome年度优秀浏览器插件,用过后为之疯狂。为什么Vue3源码分析计划的计算属性比普通函数好?