正点原子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 */
			>;
		};
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的电气属性配置寄存器值。

pinctrl驱动

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

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

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

imx6ul_pinctrl_probe

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

gpio子系统

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

设备树中添加 pinctrl 节点模板

我们已经对 pinctrl 有了比较深入的了解,接下来我们学习一下如何在设备树中添加某个外
设 的 PIN 信息。关于 I.MX 系 列 SOC 的 pinctrl 设备树绑定信息可以参考文档
Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt。这里我们虚拟一个名为“test”的设
备,test 使用了 GPIO1_IO00 这个 PIN 的 GPIO 功能,pinctrl 节点添加过程如下:

1、创建对应的节点

同一个外设的 PIN 都放到一个节点里面,打开 imx6ull-alientek-emmc.dts,在 iomuxc 节点
中的“imx6ul-evk”子节点下添加“pinctrl_test”节点,注意!节点前缀一定要为“pinctrl_”。添
加完成以后如下所示:

示例代码 45.1.2.10 test 设备 pinctrl 节点
1 pinctrl_test: testgrp {
2 /* 具体的 PIN 信息 */
3 };
2、添加“fsl,pins”属性

设备树是通过属性来保存信息的,因此我们需要添加一个属性,属性名字一定要为“fsl,pins”,
因为对于 I.MX 系列 SOC 而言,pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配
置信息,完成以后如下所示:

示例代码 45.1.2.11 添加"fsl,pins"属性
1 pinctrl_test: testgrp {
2 fsl,pins = <
3 /* 设备所使用的 PIN 配置信息 */
4 >;
5 };
3、在“fsl,pins”属性中添加 PIN 配置信息

最后在“fsl,pins”属性中添加具体的 PIN 配置信息,完成以后如下所示:

示例代码 45.1.2.13 完整的 test 设备 pinctrl 子节点
1 pinctrl_test: testgrp {
2 fsl,pins = <
3 MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config 是具体设置值*/
4 >;
5 };

至此,我们已经在 imx6ull-alientek-emmc.dts 文件中添加好了 test 设备所使用的 PIN 配置信
息。

驱动中对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驱动程序编写:

在这里插入图片描述

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

编写驱动:

修改设备树

1、添加 pinctrl 节点

I.MX6U-ALPHA 开发板上的 LED 灯使用了 GPIO1_IO03 这个 PIN,打开 imx6ull-alientekemmc.dts,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_led”的子节点,节点
内容如下所示:

示例代码 45.4.1.1 GPIO1_IO03 pinctrl 节点
1 pinctrl_led: ledgrp {
2 fsl,pins = <
3 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */
4 >;
5 };

第 3 行,将 GPIO1_IO03 这个 PIN 复用为 GPIO1_IO03,电气属性值为 0X10B0。

2、添加 LED 设备节点

在根节点“/”下创建 LED 灯节点,节点名为“gpioled”,节点内容如下:

示例代码 45.4.1.2 创建 LED 灯节点
1 gpioled {
2 #address-cells = <1>;
3 #size-cells = <1>;
4 compatible = "atkalpha-gpioled";
5 pinctrl-names = "default";
6 pinctrl-0 = <&pinctrl_led>;
7 led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
8 status = "okay";
9 };

第 6 行,pinctrl-0 属性设置 LED 灯所使用的 PIN 对应的 pinctrl 节点。
第 7 行,led-gpio 属性指定了 LED 灯所使用的 GPIO,在这里就是 GPIO1 的 IO03,低电平
有效。稍后编写驱动程序的时候会获取 led-gpio 属性的内容来得到 GPIO 编号,因为 gpio 子系
统的 API 操作函数需要 GPIO 编号。

3、检查 PIN 是否被其他外设使用

修改结果如下:
在这里插入图片描述

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

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、添加蜂鸣器的节点

在这里插入图片描述
注意: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");
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值