pinctrl子系统和gpio子系统

pinctrl子系统和gpio子系统简化了我们驱动工程师的开发,将芯片的底层操作和驱动分割开来了,底层由bsp工程师完成,他来实现pinctrl子系统和gpio子系统,而我们只需要使用该系统就可完成相关的设备的初始化和配置。pinctrl完成的是引脚的配置,比如引脚的复用,引脚属性(上下拉,电阻大小等等)。gpio完成的是gpio的输入输出功能,给驱动提供接口来控制引脚的输出电平或读取引脚的值。这两者的配置我们只需要在设备树中添加就OK了。


1. pinctrl子系统使用步骤

1.1 节点的基本结构

该节点用于定义
pinctrl的节点应该添加到&iomuxc节点下, GPIO5相关的添加到&iomuxc_snvs下

// 基本结构如下:
pinctrl_test: testgrp { 
	fsl,pins = < 
		3 /* 设备所使用的 PIN 配置信息 */
	>;
};

// eg: 
pinctrl_rgb_led:rgb_led {
	fsl,pins = <
		MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
		MX6UL_PAD_CSI_HSYNC__GPIO4_IO20  0x000010B1
		MX6UL_PAD_CSI_VSYNC__GPIO4_IO19  0x000010B1
	>;
};
  • pinctrl_test: 为pinctrl节点的别名,注意:前缀pinctrl_是必须有的
  • testgrp: 为pinctrl节点的名字
  • fsl, pins = <> , 规则如此没什么说的。里面的PIN配置信息结构为复用功能宏定义 设置pad的值。这些宏定义定义在文件源码根目录/arch/arm/boot/dts/imx6ul-pinfunc.h中,pad的值一般设置为10b1就好了。

1.2 需要在设备节点下调用前面定义的节点

需要添加的属性有:

  • pinctrl-nams = “default” , 该属性用于指定该设备节点的状态
  • pinctrl-n = <&pinctrl_test>, 该属性指定该设备的第n个状态的引脚配置魏pinctrl_test对应的配置
// eg: 
my_rgb_led {
		#address-cells = <1>;
		#size-cells = <1>;
		pinctrl-names = "default";
		compatible = "fire,my_rgb_led";
		pinctrl-0 = <&pinctrl_rgb_led>;
		// 后面的与GPIO子系统有关
		rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
		rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
		rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

2. GPIO子系统使用步骤

2.1 需要在对应的设备树节点添加

属性:gpios = <>,属性值有三个,第一个指定使用的引脚属于那一组(格式为&gpio1, &gpio2,…),第二个为引脚号,第三个为一个宏定义,指定什么电平有效(GPIO_ACTIVE_HIGH, GPIO_ACTIVE_LOW) 。

// eg: 
my_rgb_led {
		...
		rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
		rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
		rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

2.2 GPIO子系统提供的API

2.2.1 gpio_request

用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request进行申请

int gpio_request(unsigned gpio, const char *label)
  • gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
  • label:给 gpio 设置个名字。

2.2.2 gpio_free

对申请的gpio进行释放

void gpio_free(unsigned gpio)

2.2.3 gpio_direction_input

设置某个GPIO为输入

int gpio_direction_input(unsigned gpio)

2.2.4 gpio_direction_output

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

int gpio_direction_output(unsigned gpio, int value)

2.2.5 gpio_get_value

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

#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)

2.2.6 gpio_set_value

此函数用于设置某个 GPIO 的值

#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)

3. 与gpio相关的of函数

3.1 of_gpio_named_count

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

int of_gpio_named_count(struct device_node *np, const char *propname)

3.2 of_gpio_count

与上一个函数功能相同,不同的是该函数指定了统计属性为"gpios"

int of_gpio_count(struct device_node *np)

3.3 of_get_named_gpio

此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号

int of_get_named_gpio(struct device_node *np, const char *propname, int index)

4. 实例(野火i.MX6ULL PRO开发板)

4.1 设备树添加的内容

// 根节点下添加
	my_rgb_led {
		#address-cells = <1>;
		#size-cells = <1>;
		pinctrl-names = "default";
		compatible = "fire,my_rgb_led";
		pinctrl-0 = <&pinctrl_rgb_led>;
		rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
		rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
		rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

// &iomuxc节点下添加
	pinctrl_rgb_led:rgb_led {
		fsl,pins = <
			MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
			MX6UL_PAD_CSI_HSYNC__GPIO4_IO20  0x000010B1
			MX6UL_PAD_CSI_VSYNC__GPIO4_IO19  0x000010B1
		>;
	};

4.2 驱动文件

/* 编写要点
 1. 完成平台驱动(platform_driver_register())的注册,probe, remove函数的实现
 2. 完成驱动(driver_register())的注册,增加设备节点,实现file_operation结构体的相关函数
*/

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>

#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <asm/io.h>



static int major = 0;  // 主设备号
static struct class *led_class;

static int rgb_led_red;
static int rgb_led_green;
static int rgb_led_blue;

static int current_led;

static int led_drv_open(struct inode *node, struct file *file)
{
	int minor = iminor(node);

	switch(minor) {
		case 0:
			current_led = rgb_led_red;
			break;
		case 1:
			current_led = rgb_led_green;
			break;
		case 2:
			current_led = rgb_led_blue;
			break;
		default:
			printk("========error: not minor=========\n");
	}
	printk("current_led: %d\n", current_led);
	printk("===============%s===============\n", __FUNCTION__);
	return 0;
}
static int led_drv_close(struct inode *node, struct file *file)
{
	
	printk("%s\n", __FUNCTION__);
	return 0;
}


static ssize_t led_drv_read(struct file *file, char __user * buf, size_t size, loff_t *offset)
{
	
	printk("%s\n", __FUNCTION__);
	return 0;
}

static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int minor;
	char status;
	
	struct inode *node = file_inode(file);
	minor = iminor(node);

	copy_from_user(&status, buf, 1);

	if (status) {
		// 开灯
		gpio_direction_output(current_led, 0);
	} else {
		// 关灯
		gpio_direction_output(current_led, 1);
	}
	printk("%s\n", __FUNCTION__);
	return 0;
}


static struct file_operations led_drv = {
	.owner = THIS_MODULE,  //gcc用法
	.open = led_drv_open,
	.read = led_drv_read,
	.write = led_drv_write,
	.release = led_drv_close,
};

static int rgb_led_probe(struct platform_device *pdev)
{
	int err;
	// 获取资源
	struct device_node *rgb_device_node = NULL;

	// led节点的父节点
	rgb_device_node = of_find_node_by_path("/my_rgb_led");
	if (rgb_device_node == NULL) {
		printk("%s,%s,%d error: not find /my_reg_led\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}

	// 获取引脚编号
	rgb_led_red = of_get_named_gpio(rgb_device_node, "rgb_led_red", 0);
	rgb_led_green = of_get_named_gpio(rgb_device_node, "rgb_led_green", 0);
	rgb_led_blue = of_get_named_gpio(rgb_device_node, "rgb_led_blue", 0);

	printk("========red_red: %d=========\n", rgb_led_red);
	printk("========red_green: %d=========\n", rgb_led_green);
	printk("========red_blue: %d=========\n", rgb_led_blue);

	gpio_direction_output(rgb_led_red, 1);
	gpio_direction_output(rgb_led_green, 1);
	gpio_direction_output(rgb_led_blue, 1);
	
	// 完成字符设备的注册
	major = register_chrdev(0, "rgb_led", &led_drv);

	// 注册设备类
	led_class = class_create(THIS_MODULE, "hxdled_class");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class)) {
		unregister_chrdev(major, "rgb_led");
		return -1;
	}

	// 注册设备节点,/dev/rgb_led%
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "led_red");
	device_create(led_class, NULL, MKDEV(major, 1), NULL, "led_green");
	device_create(led_class, NULL, MKDEV(major, 2), NULL, "led_blue");
	
	printk("=======================%s========================\n", __FUNCTION__);
	return 0;
}

static int rgb_led_remove(struct platform_device *dev)
{
	// 释放设备节点
	device_destroy(led_class, MKDEV(major, 0));
	device_destroy(led_class, MKDEV(major, 1));
	device_destroy(led_class, MKDEV(major, 2));

	// 释放设备类
	class_destroy(led_class);
	
	// 释放注册字符设备
	unregister_chrdev(major, "rgb_led");

	
	printk("%s\n", __FUNCTION__);
	return 0;
}

static const struct of_device_id rgb_led[] = {
	{.compatible = "fire,my_rgb_led"},
};

static struct platform_driver rgb_led_platform_driver = {
	.probe = rgb_led_probe,
	.remove = rgb_led_remove,
	.driver = {
		.name = "rgb-leds-device-tree",
		.owner = THIS_MODULE,
		//.remove = rgb_led_remove,
		.of_match_table = rgb_led,   // match匹配的时候会用到这个列表里面的compatible和设备树里面的compatible对比
	}
};

// 平台驱动入口函数
static int __init  platform_rgb_led_init(void)
{

	int ret = 0;
	// 完成平台驱动的注册
	ret = platform_driver_register(&rgb_led_platform_driver);
	
	printk("%s\n", __FUNCTION__);
	return 0;
}

// 平台驱动出口函数
static void __exit platform_rgb_led_exit(void)
{
	// 完成平台驱动的移除
	platform_driver_unregister(&rgb_led_platform_driver);

	printk("%s\n", __FUNCTION__);
	return;
}

module_init(platform_rgb_led_init);
module_exit(platform_rgb_led_exit);
MODULE_LICENSE("GPL");

4.3 应用程序

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>


int main(int argc, char **argv)
{
	int fd;
	char status;
	if (argc != 3) {
		printf("Usage %s <dev-path> <on/off>\n", argv[0]);
		return -1;
	}

	fd = open(argv[1], O_RDWR);
	if (fd < 0) {
		perror("open error");
		return -1;
	}

	if (strcmp(argv[2], "on") == 0) {
		// 开灯
		status = 1;
		write(fd, &status, 1);
	} else if (strcmp(argv[2], "off") == 0) {
		// 关灯
		status = 0;
		write(fd, &status, 1);
	} else {
		printf("Usage %s <dev-path> <on/off>\n", argv[0]);
		return -1;
	}

	close(fd);
	return 0;
}

4.4 Makefile

ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE

KERN_DIR = /home/hxd/workdir/ebf_linux_kernel_6ull_depth1/build_image/build

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o led_test led_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f hello_drv_test

obj-m	+= led_driver.o
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值