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 */
/*
*
*
* 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