• 正点原子linux阿尔法开发板使用—pinctrl和gpio子系统


    pinctrl子系统和gpio子系统试验

    6ULL的GPIO的使用

    调用pinctrl子系统完成gpio复用功能和电气属性的设置。

    1、设置PIN的复用和电气属性
    2、配置GPIO

    pinctrl子系统

    使用GPIO子系统来使用GPIO子系统。
    在这里插入图片描述
    打开imx6ull.dts查看复用设备树在这里插入图片描述
    和参考手册对应上了

    在这里插入图片描述

    根据设备的类型,创造对应的子节点,然后设备所用的pin放到子节点。

    如何添加一个pin 的信息。

    	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_IO00__ANATOP_OTG1_ID    0x13058 /* USB_OTG1_ID */
    			>;
    		};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    pinctrl_hog_1: hoggrp-1 {
    			fsl,pins = <
    				MX6UL_PAD_UART1_RTS_B__GPIO1_IO19	0x17059 /* SD1 CD */
    			>;
    		};
    	我们在imx6ul-pinfunc.h中找到:
    MX6UL_PAD_UART1_RTS_B__GPIO1_IO19         0x0090 0x031C 0x0000 0x5 0x0
    <mux_reg 	conf_reg	 input_reg	 mux_mode	 input_val>
    0x0090 		0x031C 	 0x0000 	  0x5 		 0x0
    IOMUXC父节点首地址0x020e0000,因此UART1_RTS_B这个PIN的mux寄存器地址 就是:0x020e0000+0x0090=0x020e 0090。
    
    conf_reg:0x020e0000+0x031C=0x020e 031C,这个寄存器就是UART1_RTS_B的电气属性配置寄存器。
    
    input_reg,便宜为0,表示UART1_RTS_B这个PIN没有input功能。
    
    mux_mode:5表示复用为GPIO1_IO19,将其写入0x020e 0090
    
    input_val:就是写入input_reg寄存器的值。
    
    0x17059:为PIN的电气属性配置寄存器值。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    pinctrl驱动

    如何找到IMX6ULL对应的pinctrl子系统驱动。
    设备树的节点和驱动通过compatible属性匹配起来。此属性是描述一段字符串列表。驱动文件里面有一个描述驱动兼容性的东西。当设备树节点的compatible属性和驱动里面的兼容性匹配,就表示设备和驱动匹配了。

    找到pinctrl-mx6ul.c文件就是找到了。

    当驱动和设备匹配好了之后,执行probe函数。也就是

    imx6ul_pinctrl_probe
    
    • 1

    引脚复用的时候需要用到pinctrl子系统。

    gpio子系统

    引脚作为gpio使用的时候,要用到gpio子系统。
    在这里插入图片描述

    驱动中对gpio的操作函数

    1、首先,获取到GPIO所处的设备节点,比如of_find_node_by_path。
    2、获取GPIO编号, of_get_named_gpio函数,返回值就是GPIO编号。
    3、请求此编号的GPIO,gpio_request函数
    4、设置GPIO,输入或输出,gpio_direction_input或gpio_direction_output。
    5、如果是输入,那么通过gpio_get_value函数读取GPIO值,如果是输出,通过gpio_set_value设置GPIO值。

    在这里插入图片描述

    led驱动程序编写:

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    编写驱动:

    修改设备树

    在这里插入图片描述

    找到需要使用的引脚:
    在这里插入图片描述

    GPIO复用操作:
    在这里插入图片描述
    重新加载设备树:
    新的节点已经生成了。
    在这里插入图片描述

    P30 左忠凯最后一次手撕代码。

    加载驱动之后申请失败了
    在这里插入图片描述注意:申请IO失败很大部分都是这个IO被其他外设占用了,检查设备树。查找有哪些使用同一个IO的。
    1、检查复用设置。
    在这里插入图片描述

    2、查看设备树。
    在这里插入图片描述
    加载驱动成功:

    在这里插入图片描述
    小结:
    1、添加pinctrl信息
    2、检查设备树要使用IO有没有被占用
    3、添加设备节点信息。在设备上创建一个属性,此属性藐视所使用的GPIO
    4、编写驱动、获取GPIO的编号,并申请IO,申请成功可以使用此IO。

    在这里插入图片描述

    驱动源码

    /**
     *my first driver
     * 
    */
    #include <linux/types.h>
    #include <linux/kernel.h>
    #include <linux/delay.h>
    #include <linux/ide.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/errno.h>
    #include <linux/gpio.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/of.h>
    #include <linux/of_address.h>
    #include <linux/of_irq.h>
    #include <linux/gpio.h>
    #include <linux/of_gpio.h>
    
    
    #define	  LED_NAME "gpioled"
    #define   LED_COUNT 1 
    
    #define LEDOFF 0 /* 关灯 */
    #define LEDON 1 /* 开灯 */
    
    struct gpioled_dev {
    	
    	struct cdev cdev;
    	struct class *class;/*类:为了自动创建节点*/
    	struct device *device;/*设备:为了自动创建节点*/
    	dev_t devid; //设备号
    	int major;   //主设备号
    	int minor;   //次设备号
    	struct device_node *nd; //设备节点
    	int led_gpio;
    };
    
    struct gpioled_dev gpioled; //led设备
    
    
    
    static int gpioled_open(struct inode *inode,struct file *filp)
    {
    	filp->private_data = &gpioled;
    	return 0;
    }
    
    static ssize_t gpioled_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
    {
    	struct gpioled_dev *dev = (struct gpioled_dev *)filp->private_data;
    	int retvalue = 0;
    	unsigned char databuf[1];
    	retvalue = copy_from_user(databuf,buf,cnt);
    	if (retvalue < 0)
    	{
    		printk("kernel write failed!\r\n");
    		return -EFAULT;
    	}
    	if (databuf[0] == LEDON)
    	{
    		gpio_set_value(dev->led_gpio,0);
    	}
    	else if(databuf[0] == LEDOFF){
    		gpio_set_value(dev->led_gpio,1);
    	}
    	
    
    	return 0;
    }
    
    static int gpioled_release(struct inode *inode,struct file *filp)
    {
    	//struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;
    	return 0;
    }
    
    static struct file_operations led_fops = {
    	.owner = THIS_MODULE,
    	.write = gpioled_write,
    	.open = gpioled_open,
    	.release = gpioled_release,
    };
    
    static int __init led_init(void)
    {
    
    	int ret = 0; 
    
    	/*注册字符设备*/
    	gpioled.major = 0;
    	if (gpioled.major){	
    		gpioled.devid = MKDEV(gpioled.major,0);//设备号
    		ret = register_chrdev_region(gpioled.devid,LED_COUNT,LED_NAME);
    	}	else{
    		ret = alloc_chrdev_region(&gpioled.devid,0,LED_COUNT,LED_NAME);
    		gpioled.major = MAJOR(gpioled.devid);
    		gpioled.minor = MINOR(gpioled.devid);
    	}printk("led major = %d led minor = %d \r\n",gpioled.major,gpioled.minor);
    	if (ret < 0){
    		goto faile_devid;
    	}
    
    	/*2、添加字符设备*/
    	gpioled.cdev.owner = THIS_MODULE;
    	cdev_init(&gpioled.cdev,&led_fops);
    	ret = cdev_add(&gpioled.cdev,gpioled.devid,LED_COUNT);
    	if(ret<0){
    		goto fail_cdev;
    	}
    	/*3、创建设备类*/
    	gpioled.class = class_create(THIS_MODULE,LED_NAME);
    	if(IS_ERR(gpioled.class)){
    		ret = PTR_ERR(gpioled.class);
    		printk("can't class_create \r\n");
    		goto fail_class;
    	}
    	/*创建设备节点*/
    	gpioled.device = device_create(gpioled.class,NULL,gpioled.devid,NULL,LED_NAME);
    	if(IS_ERR(gpioled.device)){
    		ret = PTR_ERR(gpioled.device);
    		printk("can't device_create \r\n");
    		goto fail_device;
    	}
    	
    	/*4、获取设备属性内容*/
    	gpioled.nd = of_find_node_by_path("/gpioled");
    	if (gpioled.nd == NULL )
    	{
    		ret = -EINVAL;
    		goto fail_findnd;
    	}
    	/*获取LED所对应的GPIO*/
    	gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-goios",0);
    	if (gpioled.led_gpio < 0)
    	{
    		printk("can't find gpio \r\n");
    		goto fail_findnode;
    	}
    	printk("led gpio number = %d\r\n",gpioled.led_gpio);
    	/*申请的GPIO*/
    	ret = gpio_request(gpioled.led_gpio,"led-gpio");
    	if (ret)
    	{
    		printk("gpio_request errno \r\n");
    		ret = -EINVAL;
    	}
    	/*使用IO*/
    	ret = gpio_direction_output(gpioled.led_gpio,1);
    	if (ret)
    	{
    		goto fail_setoutput;
    	}
    	
    	gpio_set_value(gpioled.led_gpio,0);
    
    
    
    	return 0;
    fail_setoutput:
    	gpio_free(gpioled.led_gpio);
    fail_findnode:
    // fail_findu32arr:
    // fail_findrs:
    fail_findnd:
    	device_destroy(gpioled.class,gpioled.devid);
    fail_device:
    	class_destroy(gpioled.class);
    fail_class:
    	cdev_del(&gpioled.cdev);
    fail_cdev:
    	unregister_chrdev_region(gpioled.devid,LED_COUNT);
    faile_devid:
    	return ret;
    
    }
    
    static void __exit led_exit(void)
    {
    	
    	/* 关灯 */
    	gpio_set_value(gpioled.led_gpio,1);
    	/*删除设备*/
    	cdev_del(&gpioled.cdev);
    	/*释放设备号*/
    	unregister_chrdev_region(gpioled.devid,LED_COUNT);
    
    	/*摧毁设备*/
    	device_destroy(gpioled.class,gpioled.devid);
    
    	/*释放类*/
    	class_destroy(gpioled.class);
    
    	/*释放IO*/
    	gpio_free(gpioled.led_gpio);
    }
    
    
    //模块加载函数
    module_init(led_init);
    
    //模块卸载
    module_exit(led_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("qhy");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207

    蜂鸣器驱动:

    在这里插入图片描述1、添加蜂鸣器的节点

    在这里插入图片描述
    注意:6UL和6ULL的SNVS组IO的地址是不同的!
    注意区别:
    在这里插入图片描述
    在这里插入图片描述
    例程驱动源码:

    #include <linux/types.h>
    #include <linux/kernel.h>
    #include <linux/delay.h>
    #include <linux/ide.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/errno.h>
    #include <linux/gpio.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/of.h>
    #include <linux/of_address.h>
    #include <linux/of_gpio.h>
    #include <asm/mach/map.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    
    #define BEEPNAME "beep"
    
    #define BEEPCOUNT 	1
    
    #define BEEPON 		1
    
    #define BEEPOFF 	0
    
    struct beep_dev 
    {
    	dev_t devid;			 	/*设备ID*/	
    
    	struct cdev cdev;		/*字符设备结构体*/
    
    	struct device *device;	 	/*设备*/
    
    	struct class *class;	/* 类 */
    
    	struct device_node *nd;	/*设备节点*/
    
    	int beep_num;			/*beep所使用的GPIO编号*/
    
    	int major; 			 	/*主设备号*/	
    
    	int minor; 			 	/*次设备号*/
    };
    
    struct beep_dev beep_gpio;
    
    /*
     * @description		: 打开设备
     * @param - inode 	: 传递给驱动的inode
     * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
     * 					  一般在open的时候将private_data指向设备结构体。
     * @return 			: 0 成功;其他 失败
     */
    static int beep_open(struct inode *inode, struct file *filp)
    {
    	filp->private_data = &beep_gpio; /* 设置私有数据 */
    	return 0;
    }
    
    /*
     * @description		: 向设备写数据 
     * @param - filp 	: 设备文件,表示打开的文件描述符
     * @param - buf 	: 要写给设备写入的数据
     * @param - cnt 	: 要写入的数据长度
     * @param - offt 	: 相对于文件首地址的偏移
     * @return 			: 写入的字节数,如果为负值,表示写入失败
     */
    static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
    {
    	unsigned char databuf[1];
    
    	unsigned char beepstat;
    
    	int ret;
    
    	ret = copy_from_user(databuf,buf,cnt);
    	if(ret < 0) {
    		printk("kernel write failed!\r\n");
    		return -EFAULT;
    	}
    
    	beepstat = databuf[0];
    	if (beepstat == BEEPON)
    	{
    		gpio_direction_output(beep_gpio.beep_num,0); /*打开蜂鸣器*/
    	}
    	else if (beepstat == BEEPOFF)
    	{
    		gpio_direction_output(beep_gpio.beep_num,1); /*关闭蜂鸣器*/
    	}
    	
    	return 0;
    }
    
    
    /*
     * @description		: 关闭/释放设备
     * @param - filp 	: 要关闭的设备文件(文件描述符)
     * @return 			: 0 成功;其他 失败
     */
    static int beep_release(struct inode *inode, struct file *filp)
    {
    	return 0;
    }
    /*设备操作函数*/
    static struct file_operations beep_fops =
    {
    	.owner = THIS_MODULE,
    	.open = beep_open,
    	.release = beep_release,
    	.write = beep_write,
    };
    
    
    static int __init beep_init(void){
    
    	int ret = 0;
    	/*设置GPIO所使用的GPIO*/
    	/*1、获取设备的节点:beep*/
    	beep_gpio.nd = of_find_node_by_path("/beep");
    	if (beep_gpio.nd == NULL)
    	{
    		printk("beep node not find!\r\n");
    		return -EINVAL;
    	}
    	
    	/*2、获取设备树的GPIO的属性,得到所使用的Beep的编号*/
    	beep_gpio.beep_num = of_get_named_gpio(beep_gpio.nd,"beep-gpio",0);
    	if (beep_gpio.beep_num < 0)
    	{
    		printk("can't get beep-gpio");
    		return -EINVAL;
    	}
    	
    	ret = gpio_requset(beep_gpio.num,"beep-gpio");
    	if(ret)
    	{
    		printk("can't gpio requset!\r\n");
    	}
    	
    
    	/*3、设置GPIO5_IO01为输出,并且输出为高电平,默认关闭GPIO*/
    	ret = gpio_direction_output(beep_gpio.beep_num,1);
    	if (ret < 0)
    	{
    		printk("can't set gpio!\r\n");
    	}
    
    
    	/*注册字符设备驱动*/
    	/*1、创建设备号*/
    	if (beep_gpio.major)
    	{
    		beep_gpio.devid = MKDEV(beep_gpio.major,0);
    		register_chrdev_region(beep_gpio.devid,BEEPCOUNT,BEEPNAME);
    	}else{
    		alloc_chrdev_region(&beep_gpio.devid,0,BEEPCOUNT,BEEPNAME);
    		beep_gpio.major = MAJOR(beep_gpio.devid);
    		beep_gpio.minor = MINOR(beep_gpio.devid);	
    	}
    
    	/*2、初始化字符设备*/
    	beep_gpio.cdev.owner = THIS_MODULE;
    	cdev_init(&beep_gpio.cdev,&beep_fops);
    
    	/*3、添加字符设备*/
    	cdev_add(&beep_gpio.cdev,beep_gpio.devid,BEEPCOUNT);
    
    	/*4、创建类:为了自动生成设备节点*/
    	beep_gpio.class = class_create(THIS_MODULE, BEEPNAME);
    	if(IS_ERR(beep_gpio.class)){
    		return PTR_ERR(beep_gpio.class);
    	}
    
    	/*5、创建设备*/		 
    	beep_gpio.device = device_create(beep_gpio.class,NULL,beep_gpio.devid,NULL,BEEPNAME);
    		if(IS_ERR(beep_gpio.device)){
    		return PTR_ERR(beep_gpio.device);
    	}
    
    	return 0;
    	
    }
    /*
     * @description	: 驱动出口函数
     * @param 		: 无
     * @return 		: 无
     */
    static void __exit beep_exit(void){
    
    	/*关闭蜂鸣器*/
    	gpio_direction_output(beep_gpio.beep_num,0);
    
    	/*销毁设备*/
    	device_destroy(beep_gpio.class,beep_gpio.devid);
    
    	/*销毁类*/
    	class_destroy(beep_gpio.class);
    
    	/*注销设备号*/
    	unregister_chrdev_region(beep_gpio.devid,BEEPCOUNT);
    
    	/*删除cdev*/
    	cdev_del(&beep_gpio.cdev);
    
    	gpio_free(beep_gpio.beep_num);
    
    }
    
    module_init(beep_init);
    
    module_exit(beep_exit);
    
    MODULE_LICENSE("GPL");
    
    MODULE_AUTHOR("qhy");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
  • 相关阅读:
    Crazy Binary String
    【FAQ】视频监控管理平台/视频汇聚平台EasyCVR安全检查相关问题及解决方法3.0
    TCP协议详解
    爬虫学习笔记-数美验证
    JEB反编译器crack版功能和选项
    [附源码]计算机毕业设计springboot健身房信息管理
    LeetCode 622. 设计循环队列
    【C++】特殊类实现——设计一个类、不能被拷贝、只能在堆上创建对象、只能在栈上创建对象、不能被继承、单例模式、饿汉模式、懒汉模式
    MySQL作业:索引、视图、存储、函数
    sklearn【Accuracy】准确度介绍和案例学习!
  • 原文地址:https://blog.csdn.net/m0_46152793/article/details/124940059