1. 程式人生 > 其它 >Linux應用程式——使用者層操作GPIO

Linux應用程式——使用者層操作GPIO

技術標籤:Linux

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/gpiochip0GPIOA
/dev/gpiochip1GPIOB
/dev/gpiochip2GPIOC
/dev/gpiochip3GPIOD
/dev/gpiochip4GPIOE
/dev/gpiochip5GPIOF
/dev/gpiochip6GPIOG
/dev/gpiochip7GPIOH
/dev/gpiochip8GPIOI
/dev/gpiochip9GPIOJ
/dev/gpiochip10GPIOK
/dev/gpiochip11GPIOZ

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;

__u32id;

};

#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: gpiochip11forGPIO bank Z */

fd =open(chrdev_name, 0);

if(fd == -1) {

ret = -errno;

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

returnret;

}

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

}

returnret;

}

將以上程式碼儲存為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

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

http://blog.chinaunix.net/uid-31087949-id-5793457.html