Linux應用程式——使用者層操作GPIO
技術標籤:Linux
stm32mp157 盤古開發板 Linux核心版本4.19
目錄
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 asGPIOHANDLE_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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/gpio.h>
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(LibraryGeneralPurposeInput/Outputdevice) 提供了完整的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/
APIhttps://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 */
/*
* <linux/gpio.h> - 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 <linux/ioctl.h>
#include <linux/types.h>
/**
* 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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
#include <linux/gpio.h>
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