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