0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【i.MX6ULL】驱动开发6——GPIO子系统点亮LED

码农爱学习 来源:码农爱学习 作者:码农爱学习 2022-05-21 21:50 次阅读

前面的两篇文章(寄存器配置点亮LED与设备树版的点亮LED),其本质都是通过寄存器配置,来控制LED的亮灭。

使用直接操作寄存器的方式,是将与LED有关的寄存器信息,直接写到了LED的驱动代码中,这也是一种比较常规的控制方式。但当芯片的寄存器发了变动,就要对底层的驱动进行重写。

使用设备树的方式,是将与LED有关的寄存器信息,写到了设备树文件中,这样,当设备的信息修改了,还可以通过设备树的接口函数,来获取设备信息,提高了驱动代码的复用能力。

本篇介绍的Pinctrl子系统与GPIO子系统的方式,不需要再直接操作寄存器了,因为这两个子系统已经替我们实现了对寄存器的操作,我们只需要操作这两个子系统提供的API函数即可。

1 Pinctrl子系统

Pintrl子系统,顾名思义,就是管理pin引脚的一个系统,比如要点亮LED,即要控制LED对应引脚的高低电平,就要先通过Pintrl子系统将LED对应的引脚复用为GPIO功能(这一点是不是和之前寄存器配置时使用的MUX寄存器的功能有点像)。

1.1 设备树中iomuxc节点

如何使用Pintrl子系统呢?其实它也是要依赖设备树的,先来了解一下设备树里的iomuxc节点,这个节点是IOMUXC外设对应的节点,负责IO功能的复用。

打开自己开发板对应的设备树文件(我的是imx6ull-myboard.dts),然后找到iomuxc节点,先来看一下其基本结构:

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	imx6ul-evk {
		pinctrl_hog_1: hoggrp-1 {
			fsl,pins = <
				MX6UL_PAD_UART1_RTS_B__GPIO1_IO19	    0x17059 /* SD1 CD */
				MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT	0x17059 /* SD1 VSELECT */
				MX6UL_PAD_GPIO1_IO09__GPIO1_IO09        0x17059 /* SD1 RESET */
			>;
		};
        
		pinctrl_csi1: csi1grp {
			fsl,pins = <
				MX6UL_PAD_CSI_MCLK__CSI_MCLK		0x1b088
				MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK	0x1b088
				MX6UL_PAD_CSI_VSYNC__CSI_VSYNC		0x1b088
				MX6UL_PAD_CSI_HSYNC__CSI_HSYNC		0x1b088
				MX6UL_PAD_CSI_DATA00__CSI_DATA02	0x1b088
				MX6UL_PAD_CSI_DATA01__CSI_DATA03	0x1b088
				MX6UL_PAD_CSI_DATA02__CSI_DATA04	0x1b088
				MX6UL_PAD_CSI_DATA03__CSI_DATA05	0x1b088
				MX6UL_PAD_CSI_DATA04__CSI_DATA06	0x1b088
				MX6UL_PAD_CSI_DATA05__CSI_DATA07	0x1b088
				MX6UL_PAD_CSI_DATA06__CSI_DATA08	0x1b088
				MX6UL_PAD_CSI_DATA07__CSI_DATA09	0x1b088
			>;
		};
        //省略...

这里以pinctrl_hog_1插拔子节点为例进行分析,它是和热插拔有关的Pin集合,比如USB OTG的ID引脚,pinctrl_csi1子节点是csi外设所使用的PIN,本篇需要控制LED的亮灭,就需要新建一个对应的节点,然后将这个自定义外设的所有Pin配置信息都放到这个子节点中。

1.2 宏定义的含义解析

对于pinctrl_hog_1这个字节点,注意其中的:

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19	    0x17059 /* SD1 CD */

这就是对Pin引脚的配置,配置包括两方面:一是设置Pin的复用功能,二是设置Pin的电气特性

前面的MX6UL_PAD_UART1_RTS_B__GPIO1_IO19这个宏定义, 定义在arch/arm/boot/dts/imx6ul-pinfunc.h中(注意imx6ull.dtsi会引用imx6ull-pinfunc.h,而imx6ull-pinfunc.h又会引用imx6ul-pinfunc.h

pYYBAGKI7KCAX6zqAAJnWJhOezU948.png

这里一共有8 个以MX6UL_PAD_UART1_RTS_B开头的宏定义,分别代表这个IO的8种不同的功能。

另外,这个宏定义的值,被分为了5段,每段的值都有具体的含义:

0x0090 mux_reg寄存器偏移地址

pYYBAGKI7KiAfUv1AAF6aSF6lvg586.png

0x031C conf_reg寄存器偏移地址

pYYBAGKI7K6AXIfiAAGNVxBLOL0527.png

0x0000 input_reg寄存器偏移地址(这里无效)

0x5 mux_reg寄存器的值

poYBAGKI7LWAYSWXAANuxScJUAM153.png

0x0 input_reg寄存器值(这里无效)

2 GPIO子系统

GPIO子系统,顾名思义,就是管理GPIO功能的一个系统,其作用是初始化配置GPIO(这一点是不是和之前寄存器配置时使用的PAD寄存器的功能有点像),并提供对外的API接口。使用GPIO子系统后,就不需要自己操作寄存器,通过调用GPIO子系统提供的API函数即可实现对GPIO的控制。

2.1 设备树中gpio信息

仍以热插拔节点为例:

poYBAGKI7LuAQ-JjAADyIuPUmdQ560.png

UART1_RTS_B复用为GPIO1_IO19,通过读取其高低电平来判断SD卡有没有插入。

那SD卡驱动程序怎么知道CD引脚连接的GPIO1_IO19呢?还是需要设备树告诉驱动,在设备树中SD卡节点下添加一个属性来描述SD卡的 CD 引脚就行了:

poYBAGKI7MKAbYQ6AAEsBfUxedI148.png

cd-gpios描述了SD卡的CD引脚使用的哪个IO,属性值一共有三个:

&gpio1 表示CD引脚所使用的IO属于GPIO1组

19 表示GPIO1组的第19号IO

GPIO_ACTIVE_LOW 表示低电平有效

根据上面这些信息,SD卡驱动程序就可以使用GPIO1_IO19来检测SD卡的CD信号

2.2 gpio子系统API函数

2.2.1 gpio_request/free

gpio_request

用于申请一个GPIO管脚

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

gpio_free

用于释放一个GPIO管脚

/**
 * gpio: 要释放的gpio标号
 * return
 */
void gpio_free(unsigned gpio) 

2.2.2 gpio_direction_input/output

gpio_direction_input

用于设置某个GPIO为输入

/**
 * gpio: 要设置为输入的GPIO标号
 * return: 0-设置成功 负值-设置失败
 */
int gpio_direction_input(unsigned gpio)

gpio_direction_output

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

/**
 * gpio: 要设置为输出的GPIO标号
 * value: GPIO默认输出值
 * return 0-设置成功 负值-设置失败
 */
int gpio_direction_output(unsigned gpio, int value) 

2.2.3 gpio_get_value/set_value

gpio_get_value

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

#define gpio_get_value  __gpio_get_value
/**
 * gpio: 要获取的gpio标号
 * return: 非负值-得到的gpio值 负值-获取失败
 */
int __gpio_get_value(unsigned gpio)

gpio_set_value

用于设置某个GPIO的值

#define gpio_set_value  __gpio_set_value 
/**
 * gpio: 要设置的gpio标号
 * value: 要设置的值
 * return
 */
void __gpio_set_value(unsigned gpio, int value)

2.3 与gpio相关的OF函数

2.3.1 of_gpio_named_count

用于获取设备树某个属性里面定义了几个GPIO信息

/**
 * np: 设备节点
 * propname: 要统计的gpio属性
 * return: 正值-统计到的gpio数量 负值-失败
 */
int of_gpio_named_count(struct device_node *np, const char  *propname) 

2.3.2 of_gpio_count

统计“gpios”这个属性的gpio数量

/**
 * np: 设备节点
 * return: 正值-统计到的gpio数量 负值-失败
 */
int of_gpio_count(struct device_node *np) 

2.3.3 of_get_named_gpio

获取GPIO编号

/**
 * np: 设备节点
 * propname: 包含要获取gpio信息的属性名
 * index: gpio索引(一个属性里面可能包含多个gpio)
 * return: 正值-获取到的gpio编号 负值-失败
 */
int of_get_named_gpio(struct device_node *np, 
                              const char *propname,   
                                     int index) 

3 Pinctr版LED驱动程序

上面介绍了Pinctrl子系统与GPIO子系统的基本情况,下面就来使用它们来实现LED的亮灭控制。

3.1 修改设备树文件

修改imx6ull-myboard.dts,在iomuxc节点的imx6ull-evk字节点下创建一个名为pinctrl_led的子节点,节点内容如下:

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

MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 表示将该io复用为GPIO

0x10b0 表示对PAD寄存器的配置值,具体含义为如下,之前的文章(驱动开发4--点亮LED(寄存器版))介绍过。

/*寄存器SW_PAD_SNVS_TAMPER3设置IO属性
     *bit 16:0 HYS关闭
     *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
     */
poYBAGKI7M-ACpQxAAE0T6VcuZ4075.png

在根节点下创建名为gpioled的LED节点,内容如下:

/*pinctrl led*/
gpioled {
    compatible = "myboard,gpioled";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_gpioled>;
    led-gpios = <&gpio5 3 GPIO_ACTIVE_LOW>;
    status = "okay";
};

pinctrl-0 设置 LED所使用的PIN对应的pinctrl节点

led-gpio 指定了LED所使用的GPIO,这里是GPIO5的IO03,低电平有效

pYYBAGKI7NaARIqgAAGYrHgGx_w745.png

3.2 检查引脚是否使用冲突

因为我的开发板使用的设备树文件(imx6ull-myboard.dts)是从NXP官方提供的设备树文件(imx6ull-14x14-evk.dts)上修改而来的,可能某些引脚的配置与自己的开发板不一样,需要检查一下是否有使用冲突。

本次添加的这个MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03与文件中的其它引脚没有出现冲突,因此无需修改。

3.3 修改LED驱动文件

在上一篇的设备树版的驱动文件上进行修改,主要修改内容如下。

头文件需要添加一个:

#include 

设备结构体改为gpio_led:

/* gpioled设备结构体 */
struct gpioled_dev{
    dev_t         devid;    /* 设备号   */
    struct cdev   cdev;     /* cdev     */
    struct class  *class;   /* 类       */
    struct device *device;  /* 设备     */
    int           major;    /* 主设备号 */
    int           minor;    /* 次设备号 */
    struct device_node *nd; /* 设备节点 */
    int           led_gpio; /* led使用的GPIO编号*/
};

struct gpioled_dev gpioled;    /* led设备 */

硬件初始化部分是主要修改的内容,这次就不需要从设备树读取寄存器操作了,也不需要自己再进行I/O内存映射了,而实使用gpio子系统的API函数来对LED的GPIO进行配置:

static int gpioled_hardware_init(void)
{
    int ret;

    /* 获取设备树中的属性数据 */
    /* 1、获取设备节点:gpioled */
    gpioled.nd = of_find_node_by_path("/gpioled");
    if(gpioled.nd == NULL) 
    {
        printk("gpioled node not find!\r\n");
        return -EINVAL;
    } 
    else 
    {
        printk("gpioled node find!\r\n");
    }

    /* 2、获取gpio属性, 得到LED编号 */
    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    if(gpioled.led_gpio < 0) 
    {
        printk("can't get led-gpio!\r\n");
        return -EINVAL;
    } 
    else 
    {
        printk("led-gpio num = %d\r\n", gpioled.led_gpio);
    }

    /* 3、设置GPIO为输出, 并默认关闭LED */
    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if(ret < 0)
    {
        printk("can't set led-gpio!\r\n");
    }
    
    return 0;
}

开关LED时,也不需要再直接操作寄存器了,也是使用API函数来操作:

static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	//省略...

    if(ledstat == LEDON) 
    {    
        gpio_set_value(dev->led_gpio, 0);         /* 打开LED灯 */
        printk("led on!\n");
    } 
    else if(ledstat == LEDOFF) 
    {
        gpio_set_value(dev->led_gpio, 1);        /* 关闭LED灯 */
        printk("led off!\n");
    }
    
    return 0;
}

4 实验测试

4.1 编译程序

编译设备树文件(.dtb),和上篇设备树点亮LED的实验一样,先将设备树文件复制到nfs文件系统位置,再从网络启动开发板,串口中查看设备树中是否有添加的gpioled节点:

pYYBAGKI7OKAaZFuAABvQHK0vHw773.png

编译LED驱动文件(.ko),复制到rootfs/lib/modules/4.1.15目录中:

pYYBAGKI7OyAIPOwAACPQEKA2AU916.png

LED应用程序不需要改,仍使用之前寄存器版点亮LED实验时使用的程序即可。

4.2 测试

测试方式与之前的一样,都是先加载驱动文件,然后调用应用程序来控制LED的亮灭:

pYYBAGKI7PSAbdXZAADcDOZLJgU848.png

效果和之前的寄存器版点亮LED设备树版点亮LED的效果一样

pYYBAGKI60aAYOiNAAC-QqGhKlk901.png

5 总结

本篇介绍了使用Pinctrl子系统与GPIO子系统的方式来点亮LED,与之前的寄存器版点亮LED设备树版点亮LED的最大区别在于不需要直接操作寄存器了,而是使用API函数来配置GPIO,具体操作寄存器在过程在API函数内部实现,我们无需在进行繁琐的寄存器操作。

本篇与上一篇的设备树版点亮LED的程序编写流程基本一致,因为都是要使用设备树,与上一篇的主要区别就在于,不需要将寄存器信息写入设备树,再从设备树获取出来手动配置寄存器了。

审核编辑:符乾江

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5068

    文章

    19008

    浏览量

    302981
  • 驱动
    +关注

    关注

    12

    文章

    1824

    浏览量

    85169
  • Linux
    +关注

    关注

    87

    文章

    11219

    浏览量

    208872
收藏 人收藏

    评论

    相关推荐

    i.MX6ULL 驱动开发7—按键输入捕获与GPIO输入配置与高低电平读取

    本篇主要介绍了i.MX6ULL的按键检测的使用,主要的知识点是设备树的修改,以及GPIO的输入配置与高低电平的读取。
    的头像 发表于 05-24 09:11 6125次阅读
    <b class='flag-5'>i.MX6ULL</b> <b class='flag-5'>驱动</b><b class='flag-5'>开发</b>7—按键输入捕获与<b class='flag-5'>GPIO</b>输入配置与高低电平读取

    i.MX6ULL嵌入式Linux开发1-uboot移植初探

    本系列教程以i.MX6ULL处理器的ARM开发板为实验基础,学习记录嵌入式Linux开发的各种知识与经验,主要内容包括嵌入式Linux移植,嵌入式Linux驱动
    的头像 发表于 03-07 08:57 3843次阅读
    <b class='flag-5'>i.MX6ULL</b>嵌入式Linux<b class='flag-5'>开发</b>1-uboot移植初探

    使用i.MX6ULL开发板进行Linux根文件系统的完善

    上一篇推文讲了怎么移植根文件系统,并在i.MX6ULL开发板中运行起来,但是会出现一些提示,现在来进行根文件的完善。
    发表于 10-17 11:13 762次阅读

    移植NXP官方linux 5.4内核到i.MX6ULL开发

    本文描述移植NXP官方 linux 5.4 内核到i.MX6ULL开发板。
    发表于 12-19 11:10 2002次阅读

    I.MX6ULL终结者开发板裸机仿真jlink调试

    I.MX6ULL‘终结者’开发板预留了JTAG仿真接口,并给出了开发文档,可以实现在JLINK仿真器条件下的单步跟踪、断点调试等功能,使得开发研究i
    发表于 07-07 10:56

    i.MX6ULL开发板硬件资源

    迅为i.MX6ULL 终结者开发板硬件资源非常丰富,几乎将 i.MX6ULL 芯片的所有资源都扩展引出到底板上了,底板提供了丰富的外设接口,开发板的尺寸是 190mm*125mm,充分
    发表于 12-29 06:18

    初识 i.MX6ULL 寄存器

    裸机开发_L1_汇编LED实验0. 本节目标1. 硬件层电路2. 初识 i.MX6ULL 寄存器2.1 i.MX6ULL 时钟控制寄存器2.2 i.
    发表于 12-20 07:13

    关于i.MX6ULL配置GPIO

    正如学习C语言时写的第一段代码都是“HelloWorld!”,接触一款新的处理器时往往是从点亮一个LED开始;而点亮一个LED,则需要操作这款芯片的
    发表于 08-05 10:37

    飞凌i.MX6ULL开发板的评测,再次进阶拥有更高的性价比

    处理器MCIMX6Y2开发设计,采用先进的ARMCortex-A7内核,运行速度高达800MHz。i.MX6ULL应用处理器包括一个集成的电源管理模块,降低了外接电源的复杂性,并简化了上电时序。
    发表于 10-27 11:55 1461次阅读
    飞凌<b class='flag-5'>i.MX6ULL</b><b class='flag-5'>开发</b>板的评测,再次进阶拥有更高的性价比

    基于NXP i.MX6ULL处理器的FETMX6ULL-C核心板

    合作伙伴,飞凌不负美誉,基于i.MX6ULL匠心打造的FETMX6ULL-S核心板一经问世便好评不断,且已有数百家来自工业、医疗、电力、物联网等行业的用户采用此款核心板快速完成了整机产品的开发上市。
    发表于 04-11 15:05 1135次阅读
    基于NXP <b class='flag-5'>i.MX6ULL</b>处理器的FETMX<b class='flag-5'>6ULL</b>-C核心板

    i.MX6ULL驱动开发4——点亮LED(寄存器版)

    本篇主要介绍了如何通过操作寄存器来点亮i.MX6ULL开发板上的led,通过编写LED对应的驱动
    的头像 发表于 05-21 21:26 2937次阅读
    【<b class='flag-5'>i.MX6ULL</b>】<b class='flag-5'>驱动</b><b class='flag-5'>开发</b>4——<b class='flag-5'>点亮</b><b class='flag-5'>LED</b>(寄存器版)

    基于i.MX6ULL点亮LED

    都说入门一款芯片的第一步是点亮LED,但是i.MX6ULL入门门槛比较高,特别是通过自学入门的,这个系列已经写了好久了,最近打算在项目不急的时候加快一下学习进度,现在就开始学习一下怎么点亮
    的头像 发表于 03-06 09:09 781次阅读

    使用pinctrl和gpio子系统实现LED驱动

    前边已经学了两种点灯,本质依然还是通过配置寄存器;在学习STM32的时候除了学习配置一下寄存器,基本都是使用库来开发,那么在i.MX6ULL还使用寄存器开发明显是不太适合,那么i.MX6ULL
    的头像 发表于 04-03 10:17 1282次阅读

    【北京迅为】i.MX6ULL开发板移植 Debian 文件系统

    【北京迅为】i.MX6ULL开发板移植 Debian 文件系统
    的头像 发表于 02-10 15:34 1107次阅读
    【北京迅为】<b class='flag-5'>i.MX6ULL</b><b class='flag-5'>开发</b>板移植 Debian 文件<b class='flag-5'>系统</b>

    基于i.MX6ULL的掉电检测设计与软件测试

    基于i.MX6ULL的掉电检测设计与软件测试基于i.MX6ULL平台设计实现掉电检测功能,首先选择一路IO,利用IO电平变化触发中断,在编写驱动时捕获该路GPIO的中断,然后在中断响应
    的头像 发表于 11-09 10:40 807次阅读
    基于<b class='flag-5'>i.MX6ULL</b>的掉电检测设计与软件测试