最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

使用pinctrl和gpio子系统开发GPIO驱动正点原子IMX6ULL阿尔法板的LED灯

来源:博客园

前言

在linux内核中,提供了pinctrl和gpio子系统,用于简化GPIO驱动开发。

pinctrl子系统

作用:根据设备树中的pin信息自动设置pin的复用功能和电气特性


(资料图片)

模板:

/* 在设备树文件(如阿尔法板的imx6ull-alientek-emmc.dts文件)的iomuxc节点的imx6ull-evk子节点下添加一个"pinctrl_test"节点并在该节点下添加fsl,pins属性,完整如下:*/&iomuxc {...imx6ull-evk {...pinctrl_test: testgrp {fsl,pins = ;};};};/* 注意: 1、对于IMX6ULL,pinctrl_test的test表示节点名字,testgrp表示test节点的集合,grp是group的缩写2、对于IMX6ULL,pinctrl子系统是通过fsl,pins来获取pin信息,所以该属性名不能有误3、MX6UL_PAD_GPIO1_IO00__GPIO1_IO00表示将GPIO1 IO0复用为普通IO,定义在imx6ul-pinfunc.h函数中4、config表示设置pin的电气特性,比如上/下拉,速度,驱动能力等*/

gpio子系统

作用:在设备树文件中添加gpio相关信息后,就可以在驱动程序中使用gpio的API函数来操作GPIO

模板:

/* 1、在设备树源文件(.dts后缀)的根节点下创建test设备子节点 *// {...test {/* 2、向test设备子节点中添加pinctrl信息 */pinctrl-name = "default";pinctrl-0 = <&pinctrl-test>;/* 使用保存在pinctrl-test节点的PIN信息 *//* 3、添加GPIO属性信息 */gpio = <&gpio1 0 GPIO_ACTIVE_LOW>;/* gpio表示test设备所使用的GPIO,该GPIO为gpio1的io0,在低电平时有效 */};};

常用的gpio API函数:

/*获取GPIO编号将设备树中类似<&gpio1 0 GPIO_ACTIVE_LOW>的属性转换为对应的GPIO编号*/int of_get_named_gpio(struct device_node *np, const char *propname, int index)参数:np:设备节点propname:包含GPIO信息的属性名index:GPIO索引,如果只有一个GPIO信息,此参数为0返回值:正值:获取到的GPIO编号负值:获取失败

例程

实现效果:点亮IMX6ULL阿尔法开发板的LED1、添加pinctrl节点

/* 在imx6ull-alientek-emmc.dts的iomuxc节点中添加如下内容 */&iomuxc {...imx6ull-evk {...pinctrl_led: ledgrp {fsl,pins = ;};};};

2、添加LED设备节点

/* 在imx6ull-alientek-emmc.dts的根节点中添加如下内容 *// {...gpioled {#address-cells = <1>;#size-cells = <1>;/* compatible = "atkalpha-gpioled"; *//* 该属性现阶段可以不用,文章后面我会提到为什么 */pinctrl-names = "default";pinctrl-0 = <&pinctrl_led>;led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;status ="okay";};};

3、编写LED设备的driver文件

/***************************************************************************** * 文件名:gpioled.c * 作者:xxx * 版本:v1.0 * 描述:LED驱动文件 * 其他:无 * 日志:v1.0 23/3/29 xxx创建 * 备注:该驱动使用了pinctrl和gpio子系统 * 要使用pinctrl子系统,需要在设备树中设置PIN的配置信息 */#include #include #include #include #include #include #include #include //注册设备文件#include #include //添加到设备树#include #include //添加gpio子系统API函数#include #include #include #include #defineNEWCHRLED_CNT1/* 设备号个数 */#define NEWCHRLED_NAME"gpioled"/* 名字 */#defineLED_OFF0/* 关灯 */#defineLED_ON1/* 开灯 *//* 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设备 *//* * @description : 打开设备 * @param - inode : 传递给驱动的inode * @param - filp : 设备文件,在open时将file结构体的private_data成员变量指向设备结构体 * @return : 0:成功;其他:失败 */static int led_open(struct inode *inode, struct file *filp){filp->private_data = &gpioled;/* 设置私有数据 */return 0;}/* * @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符) * @param - buf : 返回给用户空间的数据缓冲区 * @param - cnt : 要读取的数据数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 读取的字节数,若为负,表示读取失败 */static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){return 0;}/* * @description : 向设备写数据 * @param - filp : 设备文件,表示打开的文件描述符 * @param - buf : 要给设备写入的数据 * @param - cnt : 要写入的数据长度 * @param - offt : 写入的字节数,若为负,表示写入失败 */static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt){int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if (retvalue < 0){printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];/* 获取状态值 */if (ledstat == LED_ON){gpio_set_value(dev->led_gpio, 0);/* 打开LED */} else if (ledstat == LED_OFF){gpio_set_value(dev->led_gpio, 1);/* 关闭LED */}return 0;}/* * @description : 关闭或释放设备 * @param - inode : 设备的inode * @param - filp : 要关闭的设备文件(文件描述符) * @return : 0 成功;其他 失败 */static int led_release(struct inode *inode, struct file *filp){return 0;}/* * 设备操作函数结构体 */static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release,};/* * @description : 驱动入口函数 * @param : 无 * @return : 0 成功;其他 失败 */static int __init led_init(void){unsigned int ret = 0;/* 设置LED所使用的GPIO *//* 1、获取设备节点:gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if (gpioled.nd == NULL){printk("gpioled node can not found!\r\n");return -EINVAL;} else {printk("gpioled node has been found!\r\n");}/* 2、获取设备树中的gpio属性,得到LED所使用的LED编号*/gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if (gpioled.led_gpio < 0){printk("can not get led-gpio!\r\n");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */ret = gpio_direction_output(gpioled.led_gpio, 1);if (ret < 0)printk("can not set gpio!\r\n");/* 注册字符设备驱动 *//* 1、创建设备号 */if (gpioled.major){/* 定义了设备号 */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);} else {/* 没有定义设备号 */alloc_chrdev_region(&gpioled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME);/* 申请设备号 */gpioled.major = MAJOR(gpioled.devid);/* 获取主设备号 */gpioled.minor = MINOR(gpioled.devid);/* 获取此设备号 */}printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor);/* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一个cdev */cdev_add(&gpioled.cdev, gpioled.devid, NEWCHRLED_CNT);/* 4、创建类 */gpioled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);if (IS_ERR(gpioled.class)){return PTR_ERR(gpioled.class);}/* 5、创建设备 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, NEWCHRLED_NAME);if (IS_ERR(gpioled.device)){return PTR_ERR(gpioled.device);}return 0;}/* * @description : 驱动出口函数 * @param : 无 * @return : 无 */static void __exit led_exit(void){/* 注销字符设备驱动 */cdev_del(&gpioled.cdev);/* 删除cdev */unregister_chrdev_region(gpioled.devid, NEWCHRLED_CNT);/*删除设备号*/device_destroy(gpioled.class, gpioled.devid);/*删除设备*/class_destroy(gpioled.class);/*删除类*/}/* * 将上面两个函数指定为驱动的入口和出口函数 */module_init(led_init);module_exit(led_exit);/* * LICENSE和作者信息 */MODULE_LICENSE("GPL v2");MODULE_AUTHOR("xxx");

4、编译设备树文件(编译完后需要把对应的.dtb文件烧录到开发板上)

/* 在内核源码中根目录下,使用如下命令编译所有设备树文件 */make debs

5、编译LED设备模块(.ko后缀文件),并将编译出来的.ko文件复制到根文件系统的/lib/modules/4.1.15/下6、重启开发板7、重启后首次加载模块前,需要开发板的命令行中输入depmode命令8、加载模块,输入"modprobe xxx.ko"命令,示例如下:

问题

为什么在设备树的gpioled节点,没有使用compatible属性也可以驱动LED模块?1、compatible是Linux用来绑定节点和设备驱动用的,在linux上电后,如果compatible匹配,就会调用对应驱动的probe函数,一般,在probe函数中,通常也包含了init函数。但,对于目前的学习,我们并没有学习到使用probe函数,并且当前采用的方式是手动加载模块。在模块加载时,会调用驱动里的init函数,因此当前阶段,有没有使用compatible都没有关系。2、由于linux初始化时会初始化platform总线上的设备,会根据设备节点compatible属性和驱动中of_match_table对应的值,匹配就加载对应的驱动,所以compatible在使用platform框架的驱动中就比较重要

关键词: