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

网友投稿 304 2022-11-04

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

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

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

1 Pinctrl子系统

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

1.1 设备树中iomuxc节点

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

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

1.2 宏定义的含义解析

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

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

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

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

0x0090 mux_reg寄存器偏移地址

0x031C conf_reg寄存器偏移地址

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

0x5 mux_reg寄存器的值

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

2 GPIO子系统

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

2.1 设备树中gpio信息

仍以热插拔节点为例:

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

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

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

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

19 表示GPIO1组的第19号IO

GPIO_ACTIVE_LOW 表示低电平有效

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)

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)

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

3.1 修改设备树文件

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

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 低转换率 */

在根节点下创建名为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,低电平有效

3.2 检查引脚是否使用冲突

本次添加的这个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函数来操作:

4 实验测试

4.1 编译程序

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

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

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

4.2 测试

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

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

5 总结

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

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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java oshi如何查看cpu信息
下一篇:Kubernets 污点与容忍
相关文章

 发表评论

暂时没有评论,来抢沙发吧~