如何使用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 = <&pinctrl_gpioled>;led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;status = "okay";};

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

 pinctrl_gpioled: ledgrp {fsl,pins = <MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10b0>;};

我的板子原理图对应的led引脚是gpio1 io03,根据这个信息在自己设备树对应的pinfunc.h头文件里面搜索GPIO1_IO03对应的引脚定义

表示io03可以复用为如上图所示那么多的功能。
而在右边数值的定义是:<mux_reg conf_reg input_reg mux_mode input_val>。IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000 ,所以

fsl,pins = <MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 (0x020e0000 + mux_reg) >;

之后,烧写设备树到开发板,结果如图

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 = <&pinctrl_hog_1>”这一行,也就是说并没有指定 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>#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 << 3);  //bit3清0 打开ledwritel(val,GPIO1_DR);}else if(sta == LEDOFF){val = readl(GPIO1_DR);val |= (1 << 3);  //bit3清0 关闭ledwritel(val,GPIO1_DR);}
}static int led_open(struct inode *inode, struct file *filp)
{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 < 0){printk("kernel write failed!\r\n");return -EFAULT;}/*判断开灯还是关灯*/led_switch(databuf[0]);return 0;
}//字符设备操作集
static const struct file_operations led_fops = {.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 << 26); /*先清除以前的配置bit26,27*/val |= 3 << 26;  //bit 26.27置1writel(val,IMX6U_CCM_CCGR1);writel(0x5,SW_MUX_GPIO1_IO03);//设置复用writel(0x10B0,SW_PAD_GPIO1_IO03);//设置电气属性val = readl(GPIO1_GDIR);val |= 1 << 3;  //bit 3置1 设置为输出writel(val,GPIO1_GDIR);val = readl(GPIO1_DR);val &= ~(1 << 3);  //bit3清0 打开ledwritel(val,GPIO1_DR); /*注册字符设备*/ret = register_chrdev(LED_MAJOR,LED_NAME,&led_fops);if (ret < 0){printk("register chardev failed!\r\n");return -EIO;};printk("led_init\r\n");return 0;
}/*出口*/
static void __exit led_exit(void)
{   unsigned int val = 0;val = readl(GPIO1_DR);val |= (1 << 3);  //bit3清0 关闭ledwritel(val,GPIO1_DR);/*取消地址映射*/iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);/*注销字符设备*/unregister_chrdev(LED_MAJOR,LED_NAME);printk("led_exit\r\n");
}/*注册驱动的加载和卸载*/module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("guozhijian");

编写应用程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char *argv[])
{int fd,retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error usage!\r\n");return -1;};filename = argv[1];fd = open(argv[1], O_RDWR);if(fd < 0){printf("file %s open failed!\r\n",filename);return -1;};databuf[0] = atoi(argv[2]);  //字符转换成数字retvalue = write(fd, databuf,sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}close(fd);return 0;
}

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

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

  1. linux 查找只读文件夹,Linux系统中查找命令find的使用方法(二)

    今天达内Linux培训小编要继续跟大家分享关于Linux系统中中查找命令find的使用方法的文章.在上文中小编提到,Linux查找命令是Linux系统中很重要也是很常用的命令之一.Linux的查找命令 ...

  2. centos 卸载软件_一篇看懂!详解-Linux系统中安装软件的三种方法

    Linux系统中安装软件的三种方法 注:本文主要以CentOS为例介绍常用的安装方式,其他版本linux在文章底部 Linux系统中怎么安装软件,首先说一下应用程序与系统命令的区别: 1.文件位置 系 ...

  3. linux系统中find怎么用,Linux系统中查找命令find的使用方法(一)

    今天达内Linux培训小编要跟大家分享的文章是关于Linux系统中中查找命令find的使用方法.熟悉Linux系统的小伙伴们都知道,Linux查找命令是Linux系统中很重要也是很常用的命令之一.Li ...

  4. linux分区变为空闲,分析linux系统中磁盘空闲空间的管理方法

    分析linux系统中磁盘空闲空间的管理方法 汪建国 摘要:要把文件信息存放在存储介质上,必须先找出存储介质上可供使用的空闲块.如何实现存储空间的分配和收回,取决于对空闲块的管理方法,主要有两种对磁盘存 ...

  5. linux怎么重复命令,Linux系统中重复执行历史命令的方法有哪些?

    今天小编要跟大家分享的文章是关于Linux系统中重复执行历史命令的方法有哪些?各位正在从事Linux运维工作的小伙伴们,如果要执行一条或多条之前输过的指令,要怎么处理?很多人会想到使用上下箭头去翻查历 ...

  6. Linux系统怎么使用扫描仪,Linux系统中Nmap扫描命令的使用方法 -电脑资料

    我们可以使用ping扫描的方法(-sP),与fping的工作方式比较相似,它发送icmp回送请求到指定范围的ip地址并等待响应,证明主机正在运行,反之,则无法判断主机是否开机或者是否在网络上互连. 扫 ...

  7. linux查看pid对应的进程,linux系统中快速查看进程pid的方法

    linux系统中快速查看进程pid的方法 一个很简单的'命令,pgrep,可以迅速定位包含某个关键字的进程的pid:使用这个命令,再也不用ps aux 以后去对哪个进程的pid了 用法: pgrep ...

  8. linux如何生成tar文件内容,在Linux系统中创建tar.gz文件的方法及实例讲解

    本文介绍在Linux系统中创建tar.gz文件的方法,及实例讲解. 介绍 tar存档是一个文件,用于存储其他文件的集合,包括有关它们的信息,例如所有权.权限和时间戳. 在Linux操作系统中,可以使用 ...

  9. linux系统怎么安装钉钉,在deepin linux系统中安装钉钉DingTalk的方法

    本文介绍在深度deepin linux操作系统中安装钉钉DingTalk的方法,安装的版本为官方最新版钉钉,并非web版本.playonlinux重新安装钉钉,发现大部分功能都可以使用,就是无法显示光 ...

最新文章

  1. Linux下多播的配置【十全十美】
  2. Embedding Lua, in Scala, using Java(转)
  3. DevExpress v17.2新版亮点—ASP.NET篇(二)
  4. 【Java】Servlet 工作原理解析
  5. 正则只能输入数字,一个小数点,第一位不能为小数点,保留两位小数,为正数,比较全的正则...
  6. 2.3.1 进程的同步与互斥
  7. 光猫的分类及应用范围有哪些?
  8. python自动补全库_这个库厉害了,自动补全Python代码,节省50%敲码时间
  9. LeetCode 540. 有序数组中的单一元素(位运算二分查找)
  10. (转)51单片机C中关于.c文件和.h文件
  11. vector容器详细介绍
  12. JWT Token在线编码生成
  13. multisim安装后无法连接数据库_Kepserver连接Mysql教程(一)MySQL5.5数据库安装
  14. python difflib 编辑距离_LeetCode--072--编辑距离(python)
  15. 【C语言】18-变量类型
  16. c语言 字符串转浮点型函数
  17. LEACH算法仿真实验
  18. 面试中单例模式有几种写法
  19. 关于恢复synaptics触摸板手势
  20. 常见服务器故障有哪些?如何预防服务器发生故障?服务器故障后如何恢复数据?

热门文章

  1. 世界安全生产与健康日 国美零售这些家电与你息息相关
  2. 解决:steps/make_fbank.sh: line 132: run.pl: command not found
  3. MySQL 报错:Could not acquire management access for administration 不能正确登录怎么办?
  4. 社科院与杜兰大学金融管理硕士项目——与优秀的人同行,做更好的自己!
  5. Activiti6.0流程引擎学习——(22)activiti的任务管理服务(TaskService)
  6. 体育技术机器学习金钱和灵感的圣杯
  7. mongo启动报错:ERROR: child process failed, exited with error number 1
  8. Python练习题024:分数相加
  9. 9820E ClassicHome分析总结
  10. 排列组合问题 “n个球放入m个盒子(8种)”