linux系统中pinctrl 和gpio子系统使用方法(教你点灯) 您所在的位置:网站首页 北京时间根据什么来算 linux系统中pinctrl 和gpio子系统使用方法(教你点灯)

linux系统中pinctrl 和gpio子系统使用方法(教你点灯)

2023-11-16 12:27| 来源: 网络整理| 查看: 265

如何使用pinctrl和gpio子系统点亮led pinctrl 子系统作用设备树PIN配置gpio子系统介绍配置gpio相关编写驱动程序编写应用程序

pinctrl 子系统作用

pinctrl 子系统主要工作内容如下: ①、获取设备树中 pin 信息。 ②、根据获取到的 pin 信息来设置 pin 的复用功能 ③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。 对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成, pinctrl 子系统源码目录为 drivers/pinctrl。使用这个子系统代替了我们直接去配置寄存器的步骤,这就是linux内核集成的优势了。

设备树PIN配置

要使用 pinctrl 子系统,我们需要在设备树里面设置 PIN 的配置信息,毕竟 pinctrl 子系统要根据你提供的信息来配置 PIN 功能,一般会在设备树里面创建一个节点来描述 PIN 的配置信息。打开 imx6ull.dtsi 文件,找到一个叫做 iomuxc 的节点,如下所示: 在这里插入图片描述 别看这个节点比较少内容,他的补充内容在设备树.dts的 &iomuxc {} 节点里。 在这里插入图片描述

如图是子系统对我们平时使用到的例如gpio功能的基础应用的pin集合定义,如果需要在 iomuxc 中添加我们自定义外设的 PIN,那么需要新建一个子节点,然后将这个自定义外设的所有 PIN 配置信息都放到这个子节点中。上图中的ledgrp节点就是我创建的一个用于驱动led的pinctrl节点

我们还需要在/{}根目录下创建一个用于描述led的设备子节点(在设备树的根目录节点下创建设备子节点可以方便我们后续开发)

gpioled{ compatible = "guozhijiang,gpioled"; pinctrl-names = "default"; pinctrl-0 = ; led-gpios = ; status = "okay"; };

pinctrl-0 = ;代表我接下来要创建的pinctrl节点 led-gpios = ;代表gpio子节点,gpio1 表示第一组,3代表引脚编号为3,GPIO_ACTIVE_LOW代表低电平有效。这个信息在驱动编写的时候使用gpio函数会用到。 记得在&iomuxc {}设置对应的pinctrl子节点用于描述在pinctrl子系统的引脚描述,下图右侧值代表的是 conf_reg 寄存器值!此值由用户自行设置,通过此值来设置一个 IO 的上/下拉、驱动能力和速度等,由用户自行定义。

pinctrl_gpioled: ledgrp { fsl,pins = ; };

我的板子原理图对应的led引脚是gpio1 io03,根据这个信息在自己设备树对应的pinfunc.h头文件里面搜索GPIO1_IO03对应的引脚定义 在这里插入图片描述 表示io03可以复用为如上图所示那么多的功能。 而在右边数值的定义是:。IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000 ,所以

fsl,pins = ;

之后,烧写设备树到开发板,结果如图 在这里插入图片描述

gpio子系统介绍

pinctrl 子系统重点是设置 PIN(有的 SOC 叫做 PAD)的复用和电气属性,如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么接下来就要用到 gpio 子系统了。 gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。 gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO, Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO。

配置gpio相关

pinctrl 配置好以后就是设置 gpio 了,驱动程序怎么知道 引脚连接的是 GPIO1_IO19 呢?肯定是需要设备树告诉驱动啊!在设备树中的节点下添加一个属性来描述 引脚就行了, 驱动直接读取这个属性值就知道引脚使用的是哪个 GPIO 了。举个如图所示sd卡的例子: 在这里插入图片描述 如图中 pinctrl-0~2 都是 SD 卡其他 PIN 的 pincrtl 节点信息。但是大家会发现,其实在 usdhc1 节点中并没有“pinctrl-3 = ”这一行,也就是说并没有指定 CD 引脚的 pinctrl 信息,那么 SD 卡驱动就没法设置 CD 引脚的复用功能啊?这个不用担心,因为在“iomuxc”节点下引用了 pinctrl_hog_1 这个节点,所以 Linux 内核中的 iomuxc 驱动就会自动初始化pinctrl_hog_1节点下的所有 PIN。 属性“cd-gpios”描述了 SD 卡的 CD 引脚使用的哪个 IO。属性值一共有三个,我们来看一下这三个属性值的含义,“&gpio1”表示 CD 引脚所使用的 IO 属于 GPIO1 组,“19”表示 GPIO1 组的第 19 号 IO,通过这两个值 SD 卡驱动程序就知道 CD 引脚使用了 GPIO1_IO19这 GPIO。“GPIO_ACTIVE_LOW”表示低电平有效,如果改为“GPIO_ACTIVE_HIGH”就表示高电平有效。根据上面这些信息, SD 卡驱动程序就可以使用 GPIO1_IO19 来检测 SD 卡的 CD 信号了## 编写驱动 根据上面这些信息, SD 卡驱动程序就可以使用 GPIO1_IO19 来检测 SD 卡的 CD 信号了, 打开 imx6ull.dtsi,在里面找到如下所示内容: 在这里插入图片描述 gpio1 节点信息描述了 GPIO1 控制器的所有信息,重点就是 GPIO1 外设寄存器基地址以及兼 容 属 性 。 设置 gpio1 节点的 compatible 属性有两个,分别为“fsl,imx6ul-gpio”和“fsl,imx35-gpio”,在 Linux 内核中搜索这两个字符串就可以找到 I.MX6UL 的 GPIO 驱动程序。reg 属性设置了 GPIO1 控制器的寄存器基地址为 0X0209C000。《I.MX6ULL 参考手册》找到“Chapter 28:General Purpose Input/Output(GPIO)”章节第 28.5 小节,有如图 45.2.2.1 所示的寄存器地址表: 在这里插入图片描述 可以看出, GPIO1 控制器的基地址就是 0X0209C000。 gpio-controller”表示 gpio1 节点是个 GPIO 控制器。 #gpio-cells”属性和“#address-cells”类似, #gpio-cells 应该为 2,表示一共有两个 cell,第一个 cell 为 GPIO 编号,比如“&gpio1 3”就表示 GPIO1_IO03。第二个 cell 表示GPIO 极 性 , 如 果 为 0(GPIO_ACTIVE_HIGH) 的 话 表 示 高 电 平 有 效 , 如 果 为1(GPIO_ACTIVE_LOW)的话表示低电平有效。

编写驱动程序 #include #include #include #include #include #include #include #define LED_MAJOR 200 #define LED_NAME "led" /*寄存器物理地址*/ #define CCM_CCGR1_BASE (0x020C406C) #define SW_MUX_GPIO1_IO03_BASE (0x020E0068) #define SW_PAD_GPIO1_IO03_BASE (0x020E02F4) #define GPIO1_DR_BASE (0X0209C000) #define GPIO1_GDIR_BASE (0X0209C004) static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO03; static void __iomem *SW_PAD_GPIO1_IO03; static void __iomem *GPIO1_DR; static void __iomem *GPIO1_GDIR; #define LEDOFF 0 #define LEDON 1 /*led灯的打开或者关闭*/ static void led_switch(u8 sta) { u32 val = 0; if(sta == LEDON){ val = readl(GPIO1_DR); val &= ~(1 return 0; } static int led_release(struct inode *inode, struct file *filp) { return 0; } static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { int retvalue; unsigned char databuf[1]; retvalue = copy_from_user(databuf,buf,count); if(retvalue .owner = THIS_MODULE, .write = led_write, .open = led_open, .release= led_release, }; /*入口*/ static int __init led_init(void) { int ret = 0; unsigned int val = 0; /*初始化LED灯 地址映射*/ IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4); SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4); SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4); GPIO1_DR = ioremap(GPIO1_DR_BASE,4); GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4); /* 2.初始化*/ val = readl(IMX6U_CCM_CCGR1); val &= ~(3 unsigned int val = 0; val = readl(GPIO1_DR); val |= (1 printf("Error usage!\r\n"); return -1; }; filename = argv[1]; fd = open(argv[1], O_RDWR); if(fd printf("LED Control Failed!\r\n"); close(fd); return -1; } close(fd); return 0; }

使用gcc编译应用函数然后在开发板上面驱动led的节点就可以控制led灯亮灭啦!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有