Linux通用GPIO驱动写法与应用_道一23的博客-程序员秘密_gpio驱动 linux

技术标签: linux  

Linux通用GPIO驱动写法与应用

1. 说明

在Linux中,可以对GPIO进行相关的控制,具体的做法就是利用字符设备驱动程序对相关的gpio进行控制。由于操作系统的限制,在Linux上又无法直接在应用程序的层面上对底层的硬件进行操作。本文主要通过一个点亮红外灯的实例,再次理解Linux下的应用程序与驱动程序的交互,同时加深驱动程序编写流程的理解。

2.方法一:采用通用sysfs文件系统的方式

这种方式是利用内核配置sysfs文件系统
在这里插入图片描述
这种方式是将gpio映射到sysfs文件系统中,也就是操作/sys/class/gpio里的文件来对GPIO进行相关的配置。应用程序可以直接操作这个文件对GPIO进行设置。

如果采用脚本的方式:

#bin/bash
echo 87 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio87/direction
echo 1 > /sys/class/gpio/gpio87/value

以上的脚本中首先需要计算GPIO的编号,比如需要采用PC(23),那么C组是第三组那么可以利用公式
在这里插入图片描述
其中num是GPIO的编号,n是第几组gpio,m是当前的gpio的序号。经过计算PC23的GPIO编号为87。

所以当执行

echo 87 > /sys/class/gpio/export

会在/sys/class/gpio/文件夹中生成gpio87这个目录,里面有些文件可以设置GPIO的值。

执行echo out > /sys/class/gpio/gpio87/direction表示设置该GPIO为输出,最后向GPIO写值即可。

echo  > /sys/class/gpio/gpio87/value

以上的方式实践起来比较的容易,应用程序完全不需要关注底层驱动做了哪些事情,只是按照步骤进行操作即可,程序的可预知性不强。但是操作简单。

如果要用在C程序中,也可以分为以下几步:

第一步:在/sys/class/gpio/生成gpio相关的文件夹

第二步:设置gpio输入输出方向

第三步:写gpio的值

具体操作代码可以参考附录1:采用sysfs文件系统的方式控制GPIO

3. 方法二:自己编写GPIO驱动的方式

该方式主要利用字符设备驱动程序,通过ioctl函数进行控制。相比用sysfs文件系统的方式,这种方式的操作流程更加的清晰。但是需要完成的工作量较大,既要理解驱动又要熟悉Linux应用编程。下面来介绍这种方式。

3.1 什么是ioctl

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制。其函数原型如下:

#include <sys/ioctl.h>
int ioctl(int fd, int cmd, ...);

在这里插入图片描述
ioctl()执行成功时返回0,失败则返回-1并设置全局变量errorno值。

其中函数中的参数cmd交互协议可以划分为四个位段:
在这里插入图片描述
对于cmd的宏的定义如下:

// include/uapi/asm-generic/ioctl.h
/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

对于实际gpio驱动的编写,我们可以做如下的交互协议

#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)

3.2 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("ZFJ");
MODULE_DESCRIPTION("GPIO driver for test");

然后完善里面的功能。需要申请字符设备驱动,并且提供write,read和ioctl函数。

安装字符设备驱动函数的通用写法

第一步:申请设备号

可以采用register_chrdev_region进行静态申请或者采用alloc_chrdev_region动态申请设备号。

第二步:注册字符设备

在这一步中,需要向内核注册设备,并且填充fops结构体,完善read,write及ioctl函数,由于这里只是控制gpio,所以只会用到ioctl函数。

第三步:向sysfs文件系统注册设备

通过调用class_create函数,可以向sysfs注册设备。

第四步:生成设备节点

通过调用device_create生成设备节点,应用程序通过控制设备节点来对gpio进行控制。

以上的具体代码可以参考附录2:GPIO驱动程序

3.3 编译及验证

程序编写完成后,编译内核驱动程序需要编写Makefile文件。具体的程序代码可以参考附录。

obj-m:=gpio.o
KDIR:=/home/xxx/xxx/xxx/kernel #内核的具体目录
PWD:=$(shell pwd)

all:
    make ARCH=mips CROSS_COMPILE=mips-linux-gnu- -C $(KDIR) M=$(PWD) modules

clean:
    rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.order *symvers *Module.markers

在宿主机]上交叉编译后会生成.ko文件,将该文件传到开发板即可。
在这里插入图片描述
在开发板上,输入insmod gpio.ko看到挂载完成表示成功。
在这里插入图片描述
如果要测试该驱动程序是否成功,可以写一个测试程序来进行测试。

测试程序可以让其输入两个参数,第一个是传入的GPIO的编号,第二个是GPIO的电平,用字符串on/off来表示。

核心操作就是

第一步:打开设备

gpiofd = open("/dev/gpiodrv0", O_RDWR)

第二步:通过ioctl进行引脚设置

ioctl(gpiofd, gpio_state, gpio)

目前设置的引脚状态如下
在这里插入图片描述
第三步:关闭设备

close(gpiofd);

经过以上几步,即可编写一个完整的测试程序。

最后是进行交叉编译生成可执行文件即可。下面是TFM_V2上点亮红外灯的操作。
在这里插入图片描述
测试程序的代码可以参考附录3:测试程序

4. 将GPIO驱动集成到内核中

由于前面已经将问题驱动模块单独编译,此时若想集成到内核中,则需要做以下几件事:

4.1 向内核中添加文件

由于GPIO驱动属于字符设备驱动,所以应该放在kernel/drivers/char目录中。
在这里插入图片描述

4.2 修改Kconfig

如果要通过配置manuconfig配置是否选择gpio,则需要配置Kconfig。这样可以通过宏来控制是否加载驱动模块。
在这里插入图片描述
这里选择在头部添加这一条。此时查看图形配置界面

在这里插入图片描述

4.3 让驱动编译到内核中

通过Kconfig只是选择了编译的宏,如果让驱动正真编译到内核中,还需要修改Makefile。也就是修改kernel/drivers/char/Makefile
在这里插入图片描述
这个宏表示当配置了TFM_V2_GPIO宏时,tfmv2_gpio.c将会编译成驱动,内核启动时,该驱动自动加载。

下图是Linux启动后自动加载的tfm_v2的gpio驱动。
在这里插入图片描述

同时启动后再dev目录中可以看到生成的设备
在这里插入图片描述

5. 总结

由于应用层不能直接操作gpio,但是应用程序可以调用驱动程序的接口来操作gpio。这也是为什么控制gpio这么麻烦的原因。

文章中叙述了两种操作gpio的办法,第一种是利用sysfs文件系统的方式,这种方式操作起来简单,方便应用程序的调用,第二种是写一个驱动函数的方式,通过ioctl进行控制,这种办法虽然操作起来比较麻烦,但是app调用起来也比较容易。并且可以知道调用过程,思路清晰。

通过这次的总结,对Linux的驱动的内核层与应用层要区分清楚,同时也加深对驱动程序编写流程的理解。

附录1:采用sysfs文件系统的方式控制GPIO

/* Copyright (c) 2011, RidgeRun
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the RidgeRun.
 * 4. Neither the name of the RidgeRun nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>

 /****************************************************************
 * Constants
 ****************************************************************/

#define SYSFS_GPIO_DIR "/sys/class/gpio"
#define POLL_TIMEOUT (3 * 1000) /* 3 seconds */
#define MAX_BUF 64

/****************************************************************
 * gpio_export
 ****************************************************************/
int gpio_export(unsigned int gpio)
{
    
    int fd, len;
    char buf[MAX_BUF];

    fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
    if (fd < ) {
    
        perror("gpio/export");
        return fd;
    }

    len = snprintf(buf, sizeof(buf), "%d", gpio);
    write(fd, buf, len);
    close(fd);

    return ;
}

/****************************************************************
 * gpio_unexport
 ****************************************************************/
int gpio_unexport(unsigned int gpio)
{
    
    int fd, len;
    char buf[MAX_BUF];

    fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
    if (fd < ) {
    
        perror("gpio/export");
        return fd;
    }

    len = snprintf(buf, sizeof(buf), "%d", gpio);
    write(fd, buf, len);
    close(fd);
    return ;
}

/****************************************************************
 * gpio_set_dir
 ****************************************************************/
int gpio_set_dir(unsigned int gpio, unsigned int out_flag)
{
    
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR  "/gpio%d/direction", gpio);

    fd = open(buf, O_WRONLY);
    if (fd < ) {
    
        perror("gpio/direction");
        return fd;
    }

    if (out_flag)
        write(fd, "out", );
    else
        write(fd, "in", );

    close(fd);
    return ;
}

/****************************************************************
 * gpio_set_value
 ****************************************************************/
int gpio_set_value(unsigned int gpio, unsigned int value)
{
    
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

    fd = open(buf, O_WRONLY);
    if (fd < ) {
    
        perror("gpio/set-value");
        return fd;
    }

    if (value)
        write(fd, "1", );
    else
        write(fd, "0", );

    close(fd);
    return ;
}

/****************************************************************
 * gpio_get_value
 ****************************************************************/
int gpio_get_value(unsigned int gpio, unsigned int *value)
{
    
    int fd, len;
    char buf[MAX_BUF];
    char ch;

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

    fd = open(buf, O_RDONLY);
    if (fd < ) {
    
        perror("gpio/get-value");
        return fd;
    }

    read(fd, &ch, );

    if (ch != '0') {
    
        *value = ;
    } else {
    
        *value = ;
    }

    close(fd);
    return ;
}


/****************************************************************
 * gpio_set_edge
 ****************************************************************/

int gpio_set_edge(unsigned int gpio, char *edge)
{
    
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);

    fd = open(buf, O_WRONLY);
    if (fd < ) {
    
        perror("gpio/set-edge");
        return fd;
    }

    write(fd, edge, strlen(edge) + );
    close(fd);
    return ;
}

/****************************************************************
 * gpio_fd_open
 ****************************************************************/

int gpio_fd_open(unsigned int gpio)
{
    
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

    fd = open(buf, O_RDONLY | O_NONBLOCK );
    if (fd < ) {
    
        perror("gpio/fd_open");
    }
    return fd;
}

/****************************************************************
 * gpio_fd_close
 ****************************************************************/

int gpio_fd_close(int fd)
{
    
    return close(fd);
}

/****************************************************************
 * Main
 ****************************************************************/
int main(int argc, char **argv, char **envp)
{
    
    struct pollfd fdset[2];
    int nfds = ;
    int gpio_fd, timeout, rc;
    char *buf[MAX_BUF];
    unsigned int gpio;
    int len;



    if (argc < ) {
    
        printf("Usage: gpio-int <gpio-pin>\n\n");
        printf("Waits for a change in the GPIO pin voltage level or input on stdin\n");
        exit(-1);
    }

    gpio = atoi(argv[]);

    gpio_export(gpio);
    gpio_set_dir(gpio, );
    gpio_set_edge(gpio, "rising");
    gpio_fd = gpio_fd_open(gpio);

    timeout = POLL_TIMEOUT;

    while () {
    
        memset((void*)fdset, , sizeof(fdset));

        fdset[].fd = STDIN_FILENO;
        fdset[].events = POLLIN;

        fdset[].fd = gpio_fd;
        fdset[].events = POLLPRI;

        rc = poll(fdset, nfds, timeout);     

        if (rc < ) {
    
            printf("\npoll() failed!\n");
            return -1;
        }

        if (rc == ) {
    
            printf(".");
        }

        if (fdset[].revents & POLLPRI) {
    
            len = read(fdset[].fd, buf, MAX_BUF);
            printf("\npoll() GPIO %d interrupt occurred\n", gpio);
        }

        if (fdset[].revents & POLLIN) {
    
            (void)read(fdset[].fd, buf, );
            printf("\npoll() stdin read 0x%2.2X\n", (unsigned int) buf[]);
        }

        fflush(stdout);
    }

    gpio_fd_close(gpio_fd);
    return ;
}

附录2:GPIO驱动程序

/**
 * drviers/char/tfmv2_gpio.c
 *
 * GPIO driver
 *
 */
#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 = ;

    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");

附录三:测试程序

/**
 *  test.c
 *
 *  Copyright (C) 2014 W.J, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#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 ;
}
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 ;
}

转发连接:https://cloud.tencent.com/developer/article/1599571?from=14588

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/scy518/article/details/120220926

智能推荐

C#基础之Assembly_julianboom的博客-程序员秘密_assembly c#

 一直以来,我们都在用C#编写程序,编写程序的时候,我们用到继承、多态、接口以及泛型,我们也都明白子类可以继承抽象类,并能够重写父类的抽象方法,可是大家是否想过,如下几个问题:  1、凡树必有根和叶,类的继承也如此,如何通过程序集查找所有继承父类的之类的程序集名称?  2、如果程序B被其他程序调用,如何通过程序集查询调用B的所有程序?  3、如何查询当前项目通过添加引用了哪些程序集?...

使用Assembly打包Jar,可直接使用_此处一淌水的博客-程序员秘密_assembly jar

Pom文件 &lt;build&gt; &lt;resources&gt; &lt;resource&gt; &lt;directory&gt;${project.basedir}/src/main/resources&lt;/directory&gt; &lt;filtering&gt;true&lt;/filtering&gt; &lt;excludes&gt;

Pycharm和Python关系_CC_Lsh的博客-程序员秘密_pycharm与python的关系

Pycharm和Python关系简单来说:Pycharm是一个代码编辑器,是目前最流行的代码编辑器之一,用于编写python代码。Python是一个代码解释器,用于将Python代码翻译成计算机可以理解的指令。Pycharm下载地址:PyCharm: the Python IDE for Professional Developers by JetBrainsThe Python &amp; Django IDE with intellig...

pycharm怎样编写java_Pycharm改进和编写代码_伊利心情的博客-程序员秘密

PyCharm包含用于编写代码的各种标准,其中包含适用于Python的适当缩进。 这有助于提高代码标准并在PyCharm编辑器中编写完整的代码。改进代码完成PyCharm中的代码完成非常独特。 您可以使用许多其他功能进一步增强它。 请注意,编辑器提供了代码块的开始和结束。 以下代码编写一个名为demo.py的文件中 -message = 'GIEWIVrGMTLIVrHIQS' #encrypte...

STM32F207(4) 上电关中断_烂笔_头的博客-程序员秘密

环境:STM32F207 内容:上电关闭中断前面我们又提到过,设置时钟的时候我们关闭了一次中断,但是请注意,这里的中断并不是什么定时器啊,外部中断什么的,这个只是针对于时钟树摄制过程中产生的针对于时钟相关的终端。所以这个和我们平时用的中断没有一毛钱关系,真正上电关中断是使用下面代码实现的:INT32S main(void){ // SystemInit(void) CPU_

台达HMI 笔记_Knight_Chester_Sun的博客-程序员秘密

1.需要注意的是,screen edit软件编的程序能用DOP SOFT 打开,但是DOPSOFT打开后再保存,文件就会变成 .DPS格式,无法再用Scredit打开。2. HMI TAGScredit里面的数据代号里面是可以使用DM的bit的,比如DM1023.01, 但是再次点击进去时就会出现数据类型又变成了word型,没关系,忽略就行。DOPSOFT的数据代号里面已经增加

随便推点

bitset 应用实例-埃拉托斯特尼筛法_DWonderOO的博客-程序员秘密

bitset 应用实例-埃拉托斯特尼筛法bitset在C++容器中,是一个特殊容器,用来处理二进制位。那么在这里讲一个非常有趣的例子,这个例子是用来查找质数,又称为素数,只能够被1和它自身整除的整数。1既不是质数,也不是合数。那么如何使用一个算法,高效率地查找质数呢? 这里介绍一下 埃拉托斯特尼筛法。原理:先把N个自然数按次序排列起来。1不是质数,也不是合数,要划去。第二个数是2是质数留下来,而吧2后面所有能被2整除的数都划去。2后面第一个没被划去的数是3,把3留,再把3后面所有能被3整除的数都划去

埃拉托斯特尼筛法(埃筛)_hambaga的博客-程序员秘密_埃拉托斯特尼筛法

埃筛的作用是找出区间内的所有素数,复杂度是O(nloglogn)。其基本思想是:素数的倍数一定是合数。#include &lt;bits/stdc++.h&gt;using namespace std;const int Max = 1e5;int n;int prime[Max]; // 1表示是素数void eratos() { memset(prime, 1, sizeof(...

删除chrome浏览器记住密码input自动填充背景色_DenggLin的博客-程序员秘密

/* Change the white to any color ;) */@-webkit-keyframes autofill { to { color: #333; //input中文字的颜色 background: transparent; }}.login-input input:-webkit-autofill { ...

芯片如何储存信息_简单通俗谈信息储存原理_weixin_39986027的博客-程序员秘密

简单通俗谈信息储存原理:网上有个塑料杯留声机的实验,即人对着纸杯喊话,纸杯底部连接一根钢针,钢针的另一头刻在塑料杯上,塑料杯安装在一个滚轴上。即喊话的声音频率刻在滚动的塑料杯上,然后回放滚动的塑料杯就可以还原原声。其实它这个也就是信息储存原理:1,刻下或保存信息能量频率,也即刻下或保存信息。2,刻下,记下,保存的信息能量频率可以振动回放,回放即输出原声。那我们人脑和电脑其实信息储存也是这个原理。人...

(数据结构)图的邻接表存储结构_是我来晚了!的博客-程序员秘密_图的邻接表存储

图的邻接表存储结构一般来说,图更多的是采用链表存储,具体的存储方法有 3 种,分别是邻接表、邻接多重表和十字链表本篇文章将优先介绍邻接表!!!邻接点:在图中,如果两个点相互连通,且通过其中一个顶点,可直接找到另一个顶点,则称它们互为邻接点邻接:指图中顶点之间有边或者弧的存在邻接表存储图的实现方式:给图中的各个顶点独自建立一个链表,用节点存储该顶点,用另一个链表中的节点存储其邻接点特殊之处是,为了便于管理这些链表,通常会将链表的头节点存储到数组中,也正因为各个链表的头节点存储的是各个顶