Tinkerboard2/GPIO

From wiki
Jump to: navigation, search


简介

GPIO(英语:General-purpose input/output),用于输入输出高低电平信号以及中断触发,方便控制电路器件。每个GPIO端口可通过软件配置成输入输出以及中断信号。在Linux中,由于操作系统的限制,在Linux上又无法在应用程序层直接对底层的硬件进行操作,因此利用字符设备驱动程序对其gpio管脚进行控制。

Linux申请gpio并操作有两种方法

分别是:

  1. sysfs文件系统方式
  2. 设备树驱动的方式

配置流程:分三步

  1. 在/sys/class/gpio/生成gpio相关的文件夹
  2. 设置gpio输入输出方向
  3. 读写gpio的值

第一步:确定使用的gpio引脚:

  • Android系统要通过adb来使用下面的命令
  • Ubuntu、debian等系统在终端输入
:/ # ls /sys/class/gpio/
export     gpiochip128  gpiochip32   gpiochip64  unexport
gpiochip0  gpiochip255  gpiochip500  gpiochip96
#########################################################
GPIO分为5组bank :GPIO0~GPIO4,每组又以 A0~A7, B0~B7, C0~C7, D0~D7 作为编号区分,常用以下公式计算引脚:
GPIO pin脚计算公式:pin = bank * 32 + number
GPIO 小组编号计算公式:number = group * 8 + X
以GPIO4_D5 pin脚为例:
bank = 4;      //GPIO4_D5 => 4, bank => [0,4]
group = 3;      //GPIO4_D5 => 3, group => {(A=0), (B=1), (C=2), (D=3)}
X = 5;       //GPIO4_D5 => 5, X => [0,7]
number = group * 8 + X = 3 * 8 + 5 = 29
pin = bank*32 + number= 4 * 32 + 29 = 157;
根据这里的方法确定gpio,这里没有定义的引脚才可以使用
#########################################################

sysfs文件系统方式控制GPIO

将gpio映射到sysfs文件系统中,也就是操作/sys/class/gpio里的文件来对GPIO进行相关的配置。应用程序也可以直接操作这个文件对GPIO进行设置。 在/sys/class/gpio/生成gpio相关的文件夹,终端输入如下命令(这里以生成157号管脚为):

echo 157 > /sys/class/gpio/export
ls /sys/class/gpio/
ls /sys/class/gpio/gpio157
cat /sys/class/gpio/gpio157/direction #查看输入还是输出模式
cat /sys/class/gpio/gpio157/value #查看电平值

设备树驱动的方式

设备树配置

以Android源码为例,修改设备树文件:
kernel/arch/arm64/boot/dts/rockchip/tinker_board_2.dtsi
找到&pinctrl {这个节点。在&pinctrl {下添加如下内容,例如:
&pinctrl {
    gpiodmeo: gpiodmeo{
        rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>,
                        <0 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>;
    };
其中rockchip,pins是不能自定义,gpiodmeo: gpiodmeo可以自定义,这里0表示GPIO0,RK_PA5 表示A5,pcfg_pull_up表示上拉,其他值参考rk3399.dsi文件里,pinctrl 配置好以后就是设置 gpio 了。

设备树中的 gpio

gpio属于字符设备驱动,所以可以通过字符设备驱动程序的框架来完善gpio控制驱动。

先写出模板
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/gpio.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/poll.h>

/*DEV INIT*/
static int __init gpio_init(void)
{
}
/*DEV EXIT*/
static void __exit gpio_exit(void)
{
}

module_init(gpio_init);
module_exit(gpio_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("作者名及联系方式");
MODULE_DESCRIPTION("GPIO driver for test");

完整的模板

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/gpio.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/poll.h>

#define DEVICE_NAME            "gpiodrv"
#define GPIO_MAJOR            0
#define IOCTL_MAGIC            'g'
#define GPIO_OUT_LOW        _IOW(IOCTL_MAGIC, 0x00, unsigned long)
#define GPIO_OUT_HIG        _IOW(IOCTL_MAGIC, 0x01, unsigned long)
#define GPIO_INPUT            _IOR(IOCTL_MAGIC, 0x02, unsigned long)

static struct cdev cdev;
static struct class *gpio_class;
static dev_t devno;

/*OPEN*/
static int gpio_open(struct inode *inode, struct file *filp)
{
   int ret = ;

   filp->private_data = &cdev;

   return ret;
}

/*RELEASE*/
static int gpio_release(struct inode *inode, struct file *filp)
{
   return ;
}

/*READ*/
static ssize_t gpio_read(struct file *filp, char __user *buff,
               size_t count, loff_t *offp)
{
    return ;
}

/*IOCTL*/
static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
   unsigned int ret = ,err = ;

   if (_IOC_TYPE(cmd) != IOCTL_MAGIC)
       return -EINVAL;

   if (arg > )
       return -EINVAL;

   //申请gpio引脚
   err = gpio_request(arg,NULL);
   if(err)
   {
       //printk("gpio_ioctl request err!\n");
   }

   switch(cmd) {
   case GPIO_OUT_LOW:
       gpio_direction_output(arg,);
       break;

   case GPIO_OUT_HIG:
       gpio_direction_output(arg,);
       break;

   case GPIO_INPUT:
       gpio_direction_input(arg);
       ret = gpio_get_value(arg);
       break;

   default:
       ret = -EINVAL;
       break;
   }

   return ret;
}

static struct file_operations gpio_fops = {
   .owner = THIS_MODULE,
   .open = gpio_open,
   .release = gpio_release,
   .read = gpio_read,
   .unlocked_ioctl = gpio_ioctl,
};

/*DEV SETUP*/
static int gpio_setup(struct cdev *cdevp, dev_t dev)
{
   int ret = 0;

   cdev_init(cdevp, &gpio_fops);
   cdevp->owner = THIS_MODULE;
   cdevp->ops = &gpio_fops;
   ret = cdev_add(cdevp, dev, );
   if (ret)
       printk(KERN_ALERT"add gpio setup failed!\n");
   return ret;
}

/*DEV INIT*/
static int __init gpio_init(void)
{
   struct device *dev;
   int ret;
   unsigned int gpio_major;

   printk("init gpio driver module...\n");
   //1.申请主次设备号
   devno = MKDEV(GPIO_MAJOR, );
   gpio_major = MAJOR(devno);
   if (gpio_major)
       ret = register_chrdev_region(devno, , DEVICE_NAME);
   else
       ret = alloc_chrdev_region(&devno, , , DEVICE_NAME);

   if (ret < ) {
       printk(KERN_ALERT"failed in registering dev.\n");
       return ret;
   }
   //2.加入字符设备结构体
   ret = gpio_setup(&cdev, devno);
   if (ret < ) {
       printk(KERN_ALERT"failed in setup dev.\n");
       return ret;
   }
   //3.在class目录中创建文件
   gpio_class = class_create(THIS_MODULE, DEVICE_NAME);
   if (IS_ERR(gpio_class)) {
       printk(KERN_ALERT"failed in creating class.\n");
       return -1;
   }
   //4.生成设备节点
   dev = device_create(gpio_class, NULL, devno, NULL, DEVICE_NAME "%d", );
   if (IS_ERR(dev)) {
       printk(KERN_ALERT"failed in creating class.\n");
       return -1;
   }
   return ret;
}

/*DEV EXIT*/
static void __exit gpio_exit(void)
{
   cdev_del(&cdev);
   unregister_chrdev_region(devno, );
   device_destroy(gpio_class, devno);
   class_destroy(gpio_class);
}
module_init(gpio_init);
module_exit(gpio_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZFJ");
MODULE_DESCRIPTION("GPIO driver for test");

测试程序

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

#define GPIO(X)   X
#define GPIO_IOC_MAGIC 'g'

/* general APIs - GPIO_IOC_MAGIC */
enum {
    IOC_OUTPUT_CLR,
    IOC_OUTPUT_SET,
    IOC_SET_INPUT,
};

#define GPIO_IOC_OUTPUT_LOW        _IOW(GPIO_IOC_MAGIC, IOC_OUTPUT_CLR, unsigned int)
#define GPIO_IOC_OUTPUT_HIG        _IOW(GPIO_IOC_MAGIC, IOC_OUTPUT_SET, unsigned int)
#define GPIO_IOC_INPUT            _IOR(GPIO_IOC_MAGIC, IOC_SET_INPUT, unsigned int)

int main(int argc, char **argv)
{
   int gpiofd = , gpio = ;
   int gpio_state = ;

   if (argc != ) { 
       printf("Usage: gpio-pin <on/off>\n\n"); 
       printf("gpio test\n"); 
       exit(-1); 
   } 

   gpio = atoi(argv[]);

   if ((gpiofd = open("/dev/gpiodrv0", O_RDWR)) < ) {
       perror("open");
       return -1;
   }

   if(strcmp(argv[],"on")==)
   {
       gpio_state = GPIO_IOC_OUTPUT_HIG;
   }
   else if(strcmp(argv[],"off")==)
   {
       gpio_state = GPIO_IOC_OUTPUT_LOW;
   }
   else
   {
       gpio_state = GPIO_IOC_INPUT;
   }

   if ((gpio_state = ioctl(gpiofd, gpio_state, gpio)) < ) {
       perror("ioctl err");
       return -1;
   }

   printf("GPIO state:%d\n", gpio_state);
   close(gpiofd);

   return ;
}