Linux应用程序——用户层操作GPIO

Linux应用程序——用户层操作GPIO_放羊娃的博客-CSDN博客_linux 操作gpio

stm32mp157  盘古开发板  Linux内核版本4.19

目录

1、拜兔核i2SOM的wiki上关于gpio应用的说明

2、gpio操作相关的数据结构

3、测试代码:

4、参考链接

1、拜兔核i2SOM的wiki上关于gpio应用的说明
链接:http://wiki.i2som.com/pages/viewpage.action?pageId=22479311

以下是链接中的复制内容:

GPIO操作在Linux系统上是常用功能,STM32MP1芯片平台也同样支持。从Linux 4.8版本开始,Linux引入了新的gpio操作方式,GPIO字符设备。不再使用以前SYSFS方式在"/sys/class/gpio"目录下来操作GPIO,而是,基于"文件描述符"的字符设备,每个GPIO组在"/dev"下有一个对应的gpiochip文件,例如"/dev/gpiochip0, /dev/gpiochip1"。使用常规的LInux文件操作方式即可操作这些gpiochip文件,基本可以概括为open() + ioctl() + poll() + read() + close()。

STM32MP1平台的gpiochip文件描述

字符设备

描述

/dev/gpiochip0    GPIOA
/dev/gpiochip1    GPIOB
/dev/gpiochip2    GPIOC
/dev/gpiochip3    GPIOD
/dev/gpiochip4    GPIOE
/dev/gpiochip5    GPIOF
/dev/gpiochip6    GPIOG
/dev/gpiochip7    GPIOH
/dev/gpiochip8    GPIOI
/dev/gpiochip9    GPIOJ
/dev/gpiochip10    GPIOK
/dev/gpiochip11    GPIOZ
gpiochip 操作

gpio申请

flags: desired flags for the desired GPIO lines, such as GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed * together. Note that even if multiple lines are requested, the same flags * must be applicable to all of them, if you want lines with individual * flags set, request them one by one. It is possible to select * a batch of input or output lines, but they must all have the same * characteristics, i.e. all inputs or all outputs, all active low etc

lines: number of lines requested in this request, i.e. the number of valid fields in the above arrays, set to 1 to request a single line

struct gpiohandle_request {

    __u32 lineoffsets[GPIOHANDLES_MAX];

    __u32 flags;

    __u8 default_values[GPIOHANDLES_MAX];

    char consumer_label[32];

    __u32 lines;

    int fd;

};

#define GPIOHANDLES_MAX 64

#define GPIOHANDLE_REQUEST_INPUT (1UL << 0)

#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1)

#define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)

#define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)

#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)

gpio 操作数据

struct gpiohandle_data {

    __u8 values[GPIOHANDLES_MAX];

};

gpio event的申请

handleflags: desired handle flags for the desired GPIO line, such as GPIOHANDLE_REQUEST_ACTIVE_LOW or GPIOHANDLE_REQUEST_OPEN_DRAIN

eventflags: desired flags for the desired GPIO event line, such as GPIOEVENT_REQUEST_RISING_EDGE or GPIOEVENT_REQUEST_FALLING_EDGE

struct gpioevent_request {

    __u32 lineoffset;

    __u32 handleflags;

    __u32 eventflags;

    char consumer_label[32];

    int fd;

};

#define GPIOEVENT_REQUEST_RISING_EDGE (1UL << 0)

#define GPIOEVENT_REQUEST_FALLING_EDGE (1UL << 1)

#define GPIOEVENT_REQUEST_BOTH_EDGES ((1UL << 0) | (1UL << 1))

gpio event

struct gpioevent_data {

        __u64 timestamp;

        __u32 id;

};

#define GPIOEVENT_EVENT_RISING_EDGE 0x01

#define GPIOEVENT_EVENT_FALLING_EDGE 0x02

以下是使用C语言操作PZ0管脚的示例代码。

#include

#include

#include

#include

#include

#include

#include

#include

int main(int argc, char **argv)

{

struct gpiohandle_request req;

struct gpiohandle_data data;

char chrdev_name[20];

int fd, ret;

strcpy(chrdev_name, "/dev/gpiochip11");

/* Open device: gpiochip11 for GPIO bank Z */

fd = open(chrdev_name, 0);

if (fd == -1) {

ret = -errno;

fprintf(stderr, "Failed to open %s\n", chrdev_name);

return ret;

}

/* request GPIO line: GPIO_Z_0 */

req.lineoffsets[0] = 0;

req.flags = GPIOHANDLE_REQUEST_OUTPUT;

memcpy(req.default_values, &data, sizeof(req.default_values));

strcpy(req.consumer_label, "led_gpio_z_0");

req.lines = 1;

ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);

if (ret == -1) {

ret = -errno;

fprintf(stderr, "Failed to issue GET LINEHANDLE IOCTL (%d)\n",

ret);

}

if (close(fd) == -1)

perror("Failed to close GPIO character device file");

/* Start led blinking */

while(1) {

data.values[0] = !data.values[0];

ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);

if (ret == -1) {

ret = -errno;

fprintf(stderr, "Failed to issue %s (%d)\n",

"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret);

}

sleep(1);

}

/* release line */

ret = close(req.fd);

if (ret == -1) {

perror("Failed to close GPIO LINEHANDLE device file");

ret = -errno;

}

return ret;

}

将以上代码保存为gpio.c文件,然后加载SDK环境变量后,就可以编译了。

$CC gpio.c -o gpio

编译后的gpio文件,可以拷贝到开发板上运行。

Libgpiod

由于gpiochip的方式,基于C语言,所以开发者实现了Libgpiod,提供了一些工具和更简易的C API接口。

Libgpiod (Library General Purpose Input/Output device)  提供了完整的API 给开发者,同时还提供了一些用户空间下的应用来操作GPIO。

gpiodetect – list all gpiochips present on the system, their names, labels and number of GPIO lines
gpioinfo – list all lines of specified gpiochips, their names, consumers, direction, active state and additional flags
gpioget – read values of specified GPIO lines
gpioset – set values of specified GPIO lines, potentially keep the lines exported and wait until timeout, user input or signal
gpiofind – find the gpiochip name and line offset given the line name
gpiomon – wait for events on GPIO lines, specify which events to watch, how many events to process before exiting or if the events should be reported to the console
仓库地址 https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/

 API https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/tree/include/gpiod.h

libgpiod introduction video: https://www.youtube.com/watch?v=76j3TIqTPTI

2、gpio操作相关的数据结构
内核目录:linux-st\include\uapi\linux\gpio.h

/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
 * - userspace ABI for the GPIO character devices
 *
 * Copyright (C) 2016 Linus Walleij
 *
 * 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.
 */
#ifndef _UAPI_GPIO_H_
#define _UAPI_GPIO_H_
 
#include
#include
 
/**
 * struct gpiochip_info - Information about a certain GPIO chip
 * @name: the Linux kernel name of this GPIO chip
 * @label: a functional name for this GPIO chip, such as a product
 * number, may be NULL
 * @lines: number of GPIO lines on this chip
 */
struct gpiochip_info {
    char name[32];
    char label[32];
    __u32 lines;
};
 
/* Informational flags */
#define GPIOLINE_FLAG_KERNEL        (1UL << 0) /* Line used by the kernel */
#define GPIOLINE_FLAG_IS_OUT        (1UL << 1)
#define GPIOLINE_FLAG_ACTIVE_LOW    (1UL << 2)
#define GPIOLINE_FLAG_OPEN_DRAIN    (1UL << 3)
#define GPIOLINE_FLAG_OPEN_SOURCE    (1UL << 4)
 
/**
 * struct gpioline_info - Information about a certain GPIO line
 * @line_offset: the local offset on this GPIO device, fill this in when
 * requesting the line information from the kernel
 * @flags: various flags for this line
 * @name: the name of this GPIO line, such as the output pin of the line on the
 * chip, a rail or a pin header name on a board, as specified by the gpio
 * chip, may be NULL
 * @consumer: a functional name for the consumer of this GPIO line as set by
 * whatever is using it, will be NULL if there is no current user but may
 * also be NULL if the consumer doesn't set this up
 */
struct gpioline_info {
    __u32 line_offset;
    __u32 flags;
    char name[32];
    char consumer[32];
};
 
/* Maximum number of requested handles */
#define GPIOHANDLES_MAX 64
 
/* Linerequest flags */
#define GPIOHANDLE_REQUEST_INPUT    (1UL << 0)
#define GPIOHANDLE_REQUEST_OUTPUT    (1UL << 1)
#define GPIOHANDLE_REQUEST_ACTIVE_LOW    (1UL << 2)
#define GPIOHANDLE_REQUEST_OPEN_DRAIN    (1UL << 3)
#define GPIOHANDLE_REQUEST_OPEN_SOURCE    (1UL << 4)
 
/**
 * struct gpiohandle_request - Information about a GPIO handle request
 * @lineoffsets: an array desired lines, specified by offset index for the
 * associated GPIO device
 * @flags: desired flags for the desired GPIO lines, such as
 * GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed
 * together. Note that even if multiple lines are requested, the same flags
 * must be applicable to all of them, if you want lines with individual
 * flags set, request them one by one. It is possible to select
 * a batch of input or output lines, but they must all have the same
 * characteristics, i.e. all inputs or all outputs, all active low etc
 * @default_values: if the GPIOHANDLE_REQUEST_OUTPUT is set for a requested
 * line, this specifies the default output value, should be 0 (low) or
 * 1 (high), anything else than 0 or 1 will be interpreted as 1 (high)
 * @consumer_label: a desired consumer label for the selected GPIO line(s)
 * such as "my-bitbanged-relay"
 * @lines: number of lines requested in this request, i.e. the number of
 * valid fields in the above arrays, set to 1 to request a single line
 * @fd: if successful this field will contain a valid anonymous file handle
 * after a GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value
 * means error
 */
struct gpiohandle_request {
    __u32 lineoffsets[GPIOHANDLES_MAX];
    __u32 flags;
    __u8 default_values[GPIOHANDLES_MAX];
    char consumer_label[32];
    __u32 lines;
    int fd;
};
 
/**
 * struct gpiohandle_data - Information of values on a GPIO handle
 * @values: when getting the state of lines this contains the current
 * state of a line, when setting the state of lines these should contain
 * the desired target state
 */
struct gpiohandle_data {
    __u8 values[GPIOHANDLES_MAX];
};
 
#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x08, struct gpiohandle_data)
#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x09, struct gpiohandle_data)
 
/* Eventrequest flags */
#define GPIOEVENT_REQUEST_RISING_EDGE    (1UL << 0)
#define GPIOEVENT_REQUEST_FALLING_EDGE    (1UL << 1)
#define GPIOEVENT_REQUEST_BOTH_EDGES    ((1UL << 0) | (1UL << 1))
 
/**
 * struct gpioevent_request - Information about a GPIO event request
 * @lineoffset: the desired line to subscribe to events from, specified by
 * offset index for the associated GPIO device
 * @handleflags: desired handle flags for the desired GPIO line, such as
 * GPIOHANDLE_REQUEST_ACTIVE_LOW or GPIOHANDLE_REQUEST_OPEN_DRAIN
 * @eventflags: desired flags for the desired GPIO event line, such as
 * GPIOEVENT_REQUEST_RISING_EDGE or GPIOEVENT_REQUEST_FALLING_EDGE
 * @consumer_label: a desired consumer label for the selected GPIO line(s)
 * such as "my-listener"
 * @fd: if successful this field will contain a valid anonymous file handle
 * after a GPIO_GET_LINEEVENT_IOCTL operation, zero or negative value
 * means error
 */
struct gpioevent_request {
    __u32 lineoffset;
    __u32 handleflags;
    __u32 eventflags;
    char consumer_label[32];
    int fd;
};
 
/**
 * GPIO event types
 */
#define GPIOEVENT_EVENT_RISING_EDGE 0x01
#define GPIOEVENT_EVENT_FALLING_EDGE 0x02
 
/**
 * struct gpioevent_data - The actual event being pushed to userspace
 * @timestamp: best estimate of time of event occurrence, in nanoseconds
 * @id: event identifier
 */
struct gpioevent_data {
    __u64 timestamp;    // 应该是以纳秒为单位的时间戳
    __u32 id;
};
 
#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info)
#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info)
#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request)
#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)
 
#endif /* _UAPI_GPIO_H_ *
3、测试代码:
该测试代码位于

#include
#include
#include
#include
#include
#include
#include
#include
 
#include
 
int main(int argc, char **argv)
{
    struct gpiohandle_request req;
    struct gpiohandle_data data;
    char chrdev_name[20];
    int fd, ret;
    
    struct gpiochip_info chip_info;
    struct gpioline_info line_info;
    struct gpioevent_request event_req;
    struct gpioevent_data event_data;
    /*
    poll函数原型:
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    struct pollfd {
        int   fd;         // file descriptor 
        short events;     // requested events 
        short revents;    // returned events 
    };
    fd是要查询的设备,events是期望获得的事件,这里我们将他设置为:POLLIN
    fds定义一个数组,存放需要poll的所有设备。poll操作会同时查询这些设备。
    nfds为查询的文件数量
    timeout为超时时间
    */
    struct pollfd poll_fd;
     
    //如果不知道gpiochipN对应的是GPIO?,可以查看/sys/bus/gpio/devices/gpiochip0/of_node# cat st,bank-name 输出结果是GPIOA
    strcpy(chrdev_name, "/dev/gpiochip11");
     
    /* Open device: gpiochip11 for GPIO bank Z */
    fd = open(chrdev_name, 0);
    if (fd == -1) {
        ret = -errno;
        fprintf(stderr, "Failed to open %s\n", chrdev_name);
         
        return ret;
    }
    
    /*
    获取gpio chip info,即打开的设备文件"/dev/gpiochip11"的chipinfo
    */
    if ((ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip_info)) < 0){
         printf("GPIO_GET_CHIPINFO_IOCTL failed\n");
         return -errno;
     }
     printf("chip_info.name = %s, chip_info.label = %s, chip_info.lines = %d\n", \
            chip_info.name, chip_info.label, chip_info.lines);
     
    /*
    获取gpio line info, 即获取chip"/dev/gpiochip11"里面第0个引脚的info,即pz0的info
    */
    line_info.line_offset = 0;
     if ((ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line_info)) < 0){
         printf("GPIO_GET_LINEINFO_IOCTL failed\n");
         return -errno;
     }
     printf("line_info.line_offset = %d, line_info.flags = %d, line_info.name = %s, line_info.consumer = %s\n", line_info.line_offset, line_info.flags, line_info.name, line_info.consumer);
    
    /* handle request GPIO line: GPIO_Z_0 */
    req.lineoffsets[0] = 0;
    req.flags = GPIOHANDLE_REQUEST_OUTPUT;
    memcpy(req.default_values, &data, sizeof(req.default_values));
    strcpy(req.consumer_label, "led_gpio_z_0");
    req.lines = 1;
 
    /* event request */
    event_req.lineoffset = 0;
    event_req.handleflags = GPIOHANDLE_REQUEST_INPUT;
    event_req.eventflags = GPIOEVENT_REQUEST_RISING_EDGE;
 
    //ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);    // handle测试的时候用这个
    ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &event_req);    // event测试的时候用这个
    if (ret == -1) {
        ret = -errno;
        fprintf(stderr, "Failed to issue GET LINEHANDLE IOCTL (%d)\n", ret);
    }
    if (close(fd) == -1)
        perror("Failed to close GPIO character device file");
    
    poll_fd.fd = event_req.fd;    //注意这个event_req.fd是ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &event_req);返回的
    poll_fd.events = POLLIN;
    
    while(1) {
        /* handle测试,即设置IO口输出高低电平
        data.values[0] = !data.values[0];
        ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
        if (ret == -1) {
            ret = -errno;
            fprintf(stderr, "Failed to issue %s (%d)\n", "GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret);
        }
        sleep(1);
        */
        
        /* event测试,即监控IO输入电平的高低,触发事件的方式有上升沿触发、下降沿触发等 */
        ret = poll(&poll_fd, 1, 3000);
        if(ret == 0)
            printf("time out \n");
        else{
            printf("revents:%d \n", poll_fd.revents);
            event_data.timestamp = 0;
            event_data.id = 0;
            // 这里一定要将中断的事件读出来,否则会一直处于触发状态
            read(event_req.fd, &event_data, sizeof(event_data));
            // event_data.timestamp是以纳秒为单位的时间戳
            printf("event_data.timestamp:%llu, event_data.id:%d \n", event_data.timestamp, event_data.id);
        }
    }
     
    /* release line */
    ret = close(req.fd);
    if (ret == -1) {
        perror("Failed to close GPIO LINEHANDLE device file");
        ret = -errno;
    }
    return ret;
}
对这里面的poll的使用还不是很清楚

4、参考链接
http://blog.chinaunix.net/uid-9672747-id-5788440.html

http://www.voidcn.com/article/p-uaoezkpv-bau.html

http://www.voidcn.com/article/p-gmssshgp-xr.html

https://blog.csdn.net/djinglan/article/details/8302938

https://www.cnblogs.com/wen123456/p/14046890.html

http://blog.chinaunix.net/uid-31087949-id-5793457.html
————————————————
版权声明:本文为CSDN博主「放羊娃」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fang_yang_wa/article/details/113572733

你可能感兴趣的:(Linux,linux)