GPIO子系统---(1)应用下操作gpio

前言

为了了解以及更好的使用linux世界里gpio是个什么样的,特地用几篇文章来挖掘挖掘这部分的内容。希望能够让自己更好的认识kernel里面的gpio的操作。
gpio是一个非常常用和简单的一个操作,其主要功能输出一个高地电平,输入能感知一个高地电平或者中断。

通常 我们认识一个系统,自上而下的认识和学习比较是比较能够聚焦相关的内容,可以摒弃一些与之联系较少的内容,比较能够快速的认识了解这个事物。

首先我们从应用层面然后到驱动层面来全方位的了解gpio

应用层面的gpio

一般在Linux里面,应用和驱动的联系一般有file opt来衔接。也就是在应用下open一个设备节点,然后由驱动注册opt操作从而操作寄存器达到相关目的。gpio模块也是如此,只不过内部驱动没有直接操作gpio的寄存器,而是通过一组api间接操作寄存器,为什么要这么做我们下一篇文章介绍

目录
kernel\drivers\gpio 提供了gpiolib的api以及不同厂家的驱动实现

SYSFS控制GPIO

这种方式优点不需要写demo,直接在命令行下就可以直接操作GPIO,非常方便。
每个厂家针对他们的soc,对每个gpio进行编号,每个gpio的管脚都有一个独一无二的编号。比如常用的编号方式GPIO10_6 – > 10*8 + 6 == 86,每个厂家的编号方式不一定同意,他们的编码规则体现在厂商gpio驱动里,也可以咨询fae。
sysfs控制gpio的方式参考我的另一片博客,里面有详细的介绍、操作等内容
sysfs控制GPIO

gpioctl控制gpio

如上sysfs方式确实调试方便,命令行交互即可实现gpio的输入等操作,但在我们的项目应用中终究还是要回归代码。在代码中不能总用system系统调用来控制gpio吧,这又不是最优的方式。kernel开发人员写了一个通用是设备节点操作gpio,同样前提是需要知道gpio的编号。

demo

前提是需要知道gpio的num,以及系统中有/dev/gpio_ctrl设备节点

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define GPIO_NUM   228

#define UNINIT_CMD_MAGIC 0xdb
#define INIT_CMD_MAGIC   0xdc
#define RESET_CMD_MAGIC  0xdd
#define GET_CMD_MAGIC    0xde
#define SET_CMD_MAGIC    0xdf
#define GET_CMD         _IOWR(GET_CMD_MAGIC, 0, unsigned int)
#define SET_CMD         _IOW(SET_CMD_MAGIC, 0, unsigned int)
#define INIT_CMD        _IOWR(INIT_CMD_MAGIC, 0, unsigned int)
#define RESET_CMD       _IOW(RESET_CMD_MAGIC, 0, unsigned int)
#define UNINIT_CMD      _IOWR(UNINIT_CMD_MAGIC, 0, unsigned int)
struct gpio_ioctl_data{
    
    
	int gpio_num;
	int value;
};

int main(void)
{
    
    
	int fd = 0;
	int ret = 0;
	int cnt = 5;
	struct gpio_ioctl_data val = {
    
    0};
	val.gpio_num = GPIO_NUM;
	fd = open("/dev/gpio_ctrl", O_RDWR, 0644);
	if (fd < 0)
	{
    
    
		printf("open err!");
	}
	while(cnt--)
	{
    
    
		val.value = 1;
		ioctl(fd, SET_CMD, &val);
		sleep(1);
		val.value = 0;
		ioctl(fd, SET_CMD, &val);
		sleep(1);
	}
	ioctl(fd, UNINIT_CMD, &val);
	close(fd);
	return 0;
}

gpio ctl驱动

drivers\gpio\gpioioctl.c

static const struct file_operations gpio_fops = {
    
    
	.owner          = THIS_MODULE,
	.unlocked_ioctl	= gpio_ioctl,
};
static struct miscdevice gpio_miscdev = {
    
    
	.minor          = MISC_DYNAMIC_MINOR,
	.name           = "gpio_ctrl",
	.fops           = &gpio_fops,
};
static void __exit gpio_ctrl_dev_exit(void)
{
    
    
	misc_deregister(&gpio_miscdev);
}
module_init(gpio_ctrl_init);

驱动实现非常简单注册了misc驱动

主要实现功能是还是这个ioctl gpio_ioctl c
删掉了一些代码

static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    
    
switch(cmd) {
    
    
		case SET_CMD:
			...
			gpio_request(val.gpio_num, "GPIOCTRL");
			gpio_direction_output(val.gpio_num, val.value);
			break;
		case GET_CMD:
			if(copy_from_user(&val, (struct gpio_ioctl_data *)arg, \
				sizeof(struct gpio_ioctl_data)))
				return -EFAULT;

                        val.value = gpio_get_value(val.gpio_num);
                        if(copy_to_user((struct gpio_ioctl_data *)arg, &val, \
				sizeof(struct gpio_ioctl_data)))
				return -EFAULT;
			break;
		case INIT_CMD:
			...
			gpio_request(val.gpio_num, "GPIOCTRL");
			break;
		case RESET_CMD:
			...
			gpio_direction_input(val.gpio_num);
			break;
		case UNINIT_CMD:
			...
			gpio_free(val.gpio_num);
			break;
		default:
			return -EINVAL;
}

可以看出这个驱动调用了一系列 的gpiolib的函数接口用来操作gpio。
其中好处我认为有这几点

  1. 把各个平台的gpio通过gpionum 都统一管理起来
  2. 各个驱动需要调用的时候必须申请,保证gpio用处的唯一性,防止一个口被多个驱动使用,状态被意外修改
  3. 可供其他驱动调用灵活使用,比如485,控制收发脚,以及其他芯片的使能脚,如果不用gpio的库也能用,mmap,直接控制寄存器也可以,只不过更加繁琐而已。

猜你喜欢

转载自blog.csdn.net/weixin_41884251/article/details/120604596
今日推荐