1.gpio_set_value(unsigned gpio, int value)用来设置gpio寄存器的值
2.gpio_direction_output(unsigned gpio, int value)用来设置gpio为输出功能,同时设置gpio输出的值。
一般来说,设置一个GPIO口为输出,先执行一次gpio_direction_output,然后接下来只需执行gpio_set_value就行了。

3.gpio_direction_input(unsigned gpio)用来设置gpio为输入功能
4.gpio_get_value(unsigned gpio)用来获取gpio口的输入的值;

5.在使用gpio口之前,先用gpio_request(unsigned gpio, const char* label)申请gpio口的使用,若申请成功,则说明该gpio口未被使用。
6.在使用完gpio口之后,用gpio_free(unsigned gpio)释放gpio口。

7.如何获取gpio口呢,可以查看内核中对应版型的相关文件,也可以自己进行计算,比如GPIOA1的gpio为1,GPIOB2为34。
8.gpio口的通用函数接口定义在gpiolib.c文件中,声明则在gpio.h中。
 



.

.

.



linux内核驱动中通用GPIO函数使用

1.  gpio_request(unsigned gpio, const char *label):向内核申请指定gpio,所申请的IO口会被内核记录

    参数:gpio:申请IO口编号  ,label:申请者的名字,随便。
    返回:int值,成功:0;失败:负数

    注:在使用gpio口之前,应先用gpio_request()申请gpio口。

        若申请成功,则说明该gpio口未被使用。
        若申请失败,则说明该gpio口不存在或未被释放。

2.  gpio_set_value(unsigned gpio, int value):设置gpio口的值

    参数:gpio:要设置的IO口编号 ,value:要设置的值(0或1)
    返回:无

3.  gpio_get_value(unsigned gpio):获取gpio口的值

    参数:gpio:要获取的IO口编号
    返回:int值,IO口状态

4.  gpio_direction_input(unsigned gpio):设置gpio为输入功能

    参数:gpio:要设置的IO口编号
    返回:int值

5.  gpio_direction_output(unsigned gpio, int value):设置gpio为输出功能,同时设置gpio输出的值

    参数:gpio:要设置的IO口编号 ,value:要设置的值(0或1)
    返回:int值

    注:一般来说,设置一个GPIO口为输出,先执行一次gpio_direction_output,然后接下来只需执行gpio_set_value就行了。

6.  gpio_free(unsigned gpio):释放gpio口,释放的IO口会在内核记录消除

    注:在使用完gpio口之后,应及时释放gpio口。释放的io,可以再次被申请。

注意:gpio口的通用函数接口定义在 gpiolib.c 文件中,声明则在 gpio.h 中。
 




int gpio_request(unsigned gpio, const char *label) 

gpio则为你要申请的哪一个管脚,label则是为其取一个名字

1、一般gpio_request封装了mem_request(),起保护作用,最后要调用mem_free之类的。主要是告诉内核这地址被占用了。当其它地方调用同一地址的gpio_request就会报告错误,该地址已被申请。在/proc/mem应该会有地址占用表描述。
这种用法的保护作用前提是大家都遵守先申请再访问,有一个地方没遵守这个规则,这功能就失效了。好比进程互斥,必需大家在访问临界资源的时候都得先获取锁一样,其中一个没遵守约定,代码就废了。

2、__gpio_set_value和gpio_set_value的区别
一般带__这种操作的宏和函数是未保护的,对这中__操作的使用最好不用,除非你知道其中的原理。
你说的这种显然就是地址检测保护了。主要是防止错误地址引用。__gpio_set_vallue是没有地址范围检测的,如果引用非法地址,有可能内核down掉




如何用Linux内核里的gpio_request(),gpio_set_value()等函数编写LED驱动

        

步骤一:打开Linux内核源代码里的Documentation文件夹下的gpio.txt文档

文档里介绍了要用到的头文件和gpio函数介绍,用到的头文件是

#include <linux/gpio.h>

步骤二:打开Linux sourceindight工程搜索gpio.h,打开Linux目录下的那个里面是gpio函数的介绍,还有要包含的头文件,头文件里是gpio引脚号的定义

#include<asm/gpio.h>

 找到arch/arm/include/asm/gpio.h,里面需要包含的头文件是

#include<mach/gpio.h>这个头文件和具体的平台有关,这里我们以S5PC100平台为例

linux/arch/arm/mach-s5pc100/include/mach/gpio.h

按键驱动程序设计

    /*******************************
    *
    *混杂设备驱动:miscdevice
    *majior=10;
    *
    * *****************************/
     
 
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/types.h>
    #include <linux/init.h>
     
    //#include <linux/moduleparam.h>
    //#include <linux/slab.h>//kcalloc,kzalloc等内存分配函数
     
    //---------ioctl------------
    #include <linux/ioctl.h>
     
    //---------misc_register----
    #include <linux/miscdevice.h>
     
    //----------cdev--------------
    #include <linux/cdev.h>
     
    //----------delay-------------
    #include <linux/delay.h>
     
    //----------GPIO---------------
    #include <mach/gpio.h>
    #include <mach/regs-gpio.h>
    #include <plat/gpio-cfg.h>
     
    #define MISC_DYNAMIC_MINOR 0
    #define DEVICE_NAME "leds"
    //静态映射的管脚虚拟地址
    static int led_gpios[] = {
        S5PV210_MP04(4),
        S5PV210_MP04(5),
        S5PV210_MP04(6),
        S5PV210_MP04(7),
    };//4个LED
    #define LED_NUM        ARRAY_SIZE(led_gpios)
     
     
    static long fl210_leds_ioctl(struct file *filp, unsigned int cmd,
                                 unsigned long arg)    //第二个参数是命令号,第三个参数是附加参数
    {
        switch(cmd) {
                    case 0:     //命令码:如果写规范格式如:#define LED2_OFF _IOR(‘L’,0,unsigned char)
                    case 1:     //命令码:如果写规范格式如:#define LED2_ON _IOR(‘L’,1,unsigned char) 也可以把命令码放在一个头文件里,应用和驱动都包含它
                            if (arg > LED_NUM) {
                                return -EINVAL;
                        }
     
                        gpio_set_value(led_gpios[arg], !cmd);//根据cmd设置LED的暗灭
                        printk(DEVICE_NAME": %ld %d\n", arg, cmd);
                        break;
     
                    default:
                        return -EINVAL;
        }
        return 0;
    }
     
    static struct file_operations fl210_led_dev_fops = {
        .owner            = THIS_MODULE,
        .unlocked_ioctl    = fl210_leds_ioctl,
    };
     
    //----------------miscdevice------------------
    static struct miscdevice fl210_led_dev = {
        .minor            = MISC_DYNAMIC_MINOR,
        .name            = DEVICE_NAME,
        .fops            = &fl210_led_dev_fops,
    };
    //--------------------------------------------
     
     
    static int __init fl210_led_dev_init(void) {
        int ret;
        int i;
        //申请gpio,只有在gpio_request后才可以调用gpio_set_value,gpio_get_value等函数
        for (i = 0; i < LED_NUM; i++) {
            ret = gpio_request(led_gpios[i], "LED");//申请GPIO口
            if (ret) {
                printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
                    led_gpios[i], ret);
                return ret;
            }
     
            s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);//设置GPIO口为输出
            //也可以用函数gpio_direction_output(unsigned gpio, int value);
            gpio_set_value(led_gpios[i], 1);//初始化GPIO口的值
        }
     
        ret = misc_register(&fl210_led_dev);//注册混杂设备
     
        printk(DEVICE_NAME"\tinitialized\n");
        printk("led num is: %d\n",LED_NUM);
        return ret;
    }
     
    static void __exit fl210_led_dev_exit(void) {
        int i;
     
        for (i = 0; i < LED_NUM; i++) {
            gpio_free(led_gpios[i]);//释放GPIO口
        }
     
        misc_deregister(&fl210_led_dev);//注销设备
    }
     
    module_init(fl210_led_dev_init);
    module_exit(fl210_led_dev_exit);
     
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("");
     
    按键应用程序设计
     
    #include <stdio.h>
    //#include "sys/types.h"
    #include <sys/ioctl.h>
    #include <stdlib.h>
    #include <unistd.h>//read,write等等
    //#include "termios.h"
    //#include "sys/stat.h"
    #include <fcntl.h>
     
    #define LED2_ON 0x1    //命令码:如果写规范格式如:#define LED2_ON _IOR(‘L’,0,unsigned char)
    #define LED2_OFF 0x0    //命令码:如果写规范格式如:#define LED2_OFF _IOR(‘L’,1,unsigned char)
     
    main(int argc,char *argv[])
    {
        int fd;
     
        if ((fd=open("/dev/leds",O_RDWR /*| O_NDELAY | O_NOCTTY*/)) < 0)
        {
            printf("Open Device  failed.\r\n");
            exit(1);
        }
        else
        {
            printf("Open Device  successed.\r\n");
        }
        if (argc<3)
        {
            /* code */
            printf("Usage: %s <on|off num>\n",argv[0]);
            exit(1);
        }
        if(!strcmp(argv[1],"on"))//命令号为1
        {
            printf("led1 will on!!\n");
            if(ioctl(fd,LED2_ON,atoi(argv[2]))<0)//命令附加参数是atoi(argv[2])
            {
                printf("ioctl err!!\n");     
            }
     
        }
        if(!strcmp(argv[1],"off"))
        {
            printf("led1 will off!!\n");
            if(ioctl(fd,LED2_OFF,atoi(argv[2]))<0)
            {
                printf("ioctl err!!\n");
            }
        }
        close(fd);
    }
     
     
     
    /*应用层里ioctl函数的原型是:
    *#man ioctl
    *int ioctl(int d, int request, ...)
    *内核驱动源码里ioctl函数的原型是:
    *long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    *可以知道虽然应用层是可变参数...,但是实际上应用层和驱动层的ioctl函数是对应的,
    *所以我们知道,虽然应用层是用...,表示ioctl参数,但是我们应该明白
    *应用层的ioctl函数的参数最多只能有3个
    */




Linux中gpio接口的使用方法示例

这篇文章主要给大家介绍了关于Linux中gpio接口的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。

前言

Linux内核中gpio是最简单,最常用的资源(和 interrupt ,dma,timer一样)驱动程序,应用程序都能够通过相应的接口使用gpio,gpio使用0~MAX_INT之间的整数标识,不能使用负数,gpio与硬件体系密切相关的,不过linux有一个框架处理gpio,能够使用统一的接口来操作gpio.在讲gpio核心(gpiolib.c)之前先来看看gpio是怎么使用的

使用gpio

使用gpio接口需要包含#include <linux/gpio.h> ,在驱动中使用延时函数mdelay,需要包含#include <linux/delay.h>文件,Documentation/gpio.txt文件有作详细说明。

判断一个IO是否合法:

1

int gpio_is_valid(int number);

设置GPIO的方向,如果是输出同时设置电平:

1

2

3

/* set as input or output, returning 0 or negative errno */

int gpio_direction_input(unsigned gpio);

int gpio_direction_output(unsigned gpio, int value);

获取输入引脚的电平:

1

2

3

4

5

6

7

8

9

10

11

12

13

/* GPIO INPUT: return zero or nonzero */

int gpio_get_value(unsigned gpio);

/* GPIO OUTPUT */

void gpio_set_value(unsigned gpio, int value);

int gpio_cansleep(unsigned gpio);

/* GPIO INPUT: return zero or nonzero, might sleep */

int gpio_get_value_cansleep(unsigned gpio);

/* GPIO OUTPUT, might sleep */

void gpio_set_value_cansleep(unsigned gpio, int value);

获取一个GPIO并声明标签:

1

2

3

4

5

6

7

/* request GPIO, returning 0 or negative errno.

* non-null labels may be useful for diagnostics.

*/

int gpio_request(unsigned gpio, const char *label);

/* release previously-claimed GPIO */

void gpio_free(unsigned gpio);

将GPIO映射为IRQ中断:

1

2

3

4

5

/* map GPIO numbers to IRQ numbers */

int gpio_to_irq(unsigned gpio);

/* map IRQ numbers to GPIO numbers (avoid using this) */

int irq_to_gpio(unsigned irq);

设置GPIO的IRQ中断类型:

1

2

3

4

5

6

7

if (!sw->both_edges) {

 if (gpio_get_value(sw->gpio)) {

 set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_FALLING);

 } else {

 set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_RISING);

 }

}

 







GPIO使用总结

一、GPIO重要概念
要想操作GPIO引脚,需要先把所用引脚配置成GPIO功能,这个通过pinctrl子系统来实现。然后可以根据设置的引脚的方向来读取引脚的值和设置输出值。GPIO子系统存在之前,我们驱动需要在代码中配置寄存器来使用GPIO引脚。再BSP工程师实现好GPIO子系统后,我们就可以在设备树中指定GPIO引脚,在驱动中使用GPIO子系统的标准函数来获取GPIO、设置GPIO方向、读取/设置GPIO的值。这样的驱动代码是于单板无关的。

二、GPIO内核相关文档

Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt Documentation\gpio\gpio.txt Documentation\devicetree\bindings\gpio\gpio.txt


三、GPIO设备树配置
1. BSP工程师实现的gpio驱动,驱动工程师直接在设备树中配置使用。

//client节点 device {
     led-gpios = <组, 哪个几个,flag>; 
//"组"是必须要有的元素,为gpio控制器的描述,这里除了组之外还有几个域是由组中的#gpio-cells的值决定的。 
};  

//service端,设备树中对一个gpio控制器的表示: 
gpio1 {
     ......
    gpio-controller;
     #gpio-cells = <2>; 
//表示client使用gpio1这一组中的某个引脚时,除了组之外还需要使用2个整数来表示。 
};


2. 举个例子:

foo_device {
     compatible = "acme,foo";
     ......
     led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH> /*red*/ //一般这里可能为&gpioX 
        <&gpio 16 GPIO_ACTIVE_HIGH> /*green*/
         <&gpio 17 GPIO_ACTIVE_HIGH>; /*blue*/
          power-gpios = <&gpio, 1 GPIO_ACTIVE_LOW>; //注意这里使用了avtive_low属性了 
};


驱动代码中:

gpiod_get_index(dev, "led", 0, GOIOD_OUT_HIGH); //取出设备树中名为led的gpio中的第0个引脚,也就是red。 
gpiod_get_index(dev, "led", 1, GOIOD_OUT_HIGH); //取出第1个

若在设备树中只定义了一个引脚,就可以使用:

gpiod_get(dev, "power", GPIO_OUTPUT_HIGH); //把这个设备下名为power的那个引脚给取出来。



三、在驱动中使用GPIO
1.GPIO子系统有两套接口
(1) 一是基于描述符(descriptor-based)的,相关api函数都是以"gpiod_"为前缀,它使用gpio_desc结构来表示一个引脚。
(2) 另一种是老(legency)的,相关api函数都是以"gpio_"为前缀,它使用一个整数来表示一个引脚。
要操作一个引脚,首先要get引脚,然后设置方向,然后读取、写值。
2.列举操作GPIO常使用的函数

//1.获取GPIO 
gpiod_get  //legency为gpio_request 
gpiod_get_index 
gpiod_get_array //legency为gpio_request_array 
devm_gpiod_get 
devm_gpiod_get_index 
devm_gpiod_get_array  
//2.设置方向 gpiod_direction_input  //legency为gpio_direction_input 
gpiod_direction_output //legency为gpio_direction_input  
//3.读值、写值 
gpiod_get_value //legency为gpio_get_value 
gpiod_set_value //legency为gpio_set_value 
 //4.释放GPIO 
gpio_free //gpio_free 
gpiod_put //gpio_free_array 
gpiod_put_array 
devm_gpiod_put 
devm_gpiod_put_array


  前缀为"devm_"的含义是设备资源管理,这是一种自动释放资源的机制。它的思想是“资源是属于设备的,设备不存在时资源就可以自动释放”。在Linux驱动开发过程中,先申请了GPIO,再申请内存,如果内存申请失败,那么在返回之前就需要先释放GPIO资源。如果使用的是devm相关函数,在内存申请失败时可以直接返回,设备的销毁函数会自动地释放已经申请了的GPIO资源。建议使用devm相关函数操作GPIO。
3.如何通过GPIO号来使用GPIO
比如要通过gpio号来操作原理图上的GPIO5_14这个gpio引脚,那么得先知道这个gpio引脚的number是多少。那么得先找到gpio5组的基gpio number是多少。

/sys/class/gpio/# ls 
export gpio30 gpiochip0 gpiochip32 gpiochip64 gpiochip96 gpiochip128 gpiochip504 unexport //gpiochip96 这一组gpio的基gpio号是96 
/sys/class/gpio/gpiochip128# ls 
base device label ngpio power subsystem uevent 
/sys/class/gpio/gpiochip128# cat label //可以看出设备树节点名为gpio@20ac000,20ac000就是这一组gpio寄存器的基地址,可通过它查看每一组gpio的基gpio号 20ac000.gpio 
/sys/class/gpio/gpiochip128# cat base //表示这一组gpio的基gpio号为128 128 
/sys/class/gpio/gpiochip128# cat ngpio //表示这一组gpio的个数 32


在dtsi文件中检索20ac000就可以看到如下设备树配置,所以知道gpio5这一组gpio的基gpio号是128

gpio5: gpio@20ac000 {
     compatible = "fsl,im6ul-gpio", "fsl,imx35-gpio";
     reg = <0x020ac000 0x4000>;
     interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
     gpio-controller;     #gpio-cells = <0x2>;
     interrupt-controller;
     #interrupt-cells = <0x2>;
 };


那么GPIO5_14,其gpio号就是128+14=142,然后可以将这个gpio给导出来,设置其方向,配置其值,进行验证。

/sys/class/gpio# echo 142 > export 
/sys/class/gpio# ls export unexport gpio142  ... //此时可以看到多了一个gpio142目录 
/sys/class/gpio/gpio142# ls active_low direction power uevent device edge subsystem value //此时可以cat active_low 看是否具有active_low属性 
/sys/class/gpio/gpio142# echo in > direction //设置为输入引脚 /sys/class/gpio/gpio142# cat value //读取其值 1 
/sys/class/gpio# echo 142 > unexport //取消映射


如果某个引脚已经被使用了,再次export就会报错:“resource busy”。算出引脚号后就可以在驱动中使用legency函数来通过gpio号来操作gpio引脚了。
注意: Qcom平台,如果内核级驱动程序通过of_get_named_gpio()函数或类似函数获取,请求并使用该GPIO,则无法将该GPIO导出以进行sysfs控制。

五、active_low属性
  注意,设置的逻辑电平并不一定等于物理电平,因为有active_low属性,若在获取GPIO的时候指定了active_low属性,那么设置为1就是低电平,设置为0才是高电平。但是也有一些函数直接忽略active_low属性,整理如下:

unction(example)               active-low属性        物理电平 
gpiod_set_raw_value(desc, 0)    don't care             0 
gpiod_set_raw_value(desc, 1)    don't care             1 
gpiod_set_value(desc, 0)        是                     1 
gpiod_set_value(desc, 0)        否                     0 
gpiod_set_value(desc, 1)        是                     0 
gpiod_set_value(desc, 1)        否                     1

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐