iMX6ULL library porting | Cross-compilation and usage guide of Libgpiod library (linux)

The operation of the GPIO port is a very common function. The legacy GPIO sysfs interface has been deprecated. Since Linux 4.8, the kernel provides a new way to operate gpio, libgpiod (C library and tools for interacting with the linux GPIO character device), which is of course more efficient and recommended.

Introduction to libgpiod

libgpiod - C library and tools for interacting with Linux GPIO character devices (gpiod stands for GPIO device)

libgpiod/libgpiod.git - C library and tools for interacting with the linux GPIO character device

The GPIO sysfs interface has been deprecated since Linux 4.8. Userspace should use character devices instead. The library encapsulates ioctl calls and data structures, providing a simple and intuitive API.

The new character device interface guarantees that all allocated resources are freed after closing the device file descriptor, and adds some new functionality not present in the deprecated sysfs interface (like event polling, setting/reading multiple values ​​at once or open-source and open-drain GPIO).

Unfortunately, it is no longer possible to interact with Linux device files using only standard command line tools. That's why a library was created that encapsulates the tedious, ioctl-based kernel-userspace interaction and provides a set of convenience functions and opaque data structures. Additionally, the project contains a set of command line tools to easily convert user scripts to use character devices.

The old way GPIO sysfs interface uses

Before linux4.8, there was no libgpiod, and the traditional GPIO port was controlled by sysfs. Examples of usage are as follows:

1.sysfs exports GPIO 17 and GPIO 18 first

echo 17 > /sys/class/gpio/export
echo 18 > /sys/class/gpio/export

Now go to the ls /sys/class/gpio/ directory, and there will be additional gpio17 and gpio18 directories.

2. Set the gpio mode to output.

echo out > /sys/class/gpio/gpio17/direction
echo out > /sys/class/gpio/gpio18/direction

3. Set the gpio high level to light up the LED and light up the green (G) light.

echo 1 > /sys/class/gpio/gpio18/value
#熄灭写 0 即可
echo 0 > /sys/class/gpio/gpio18/value

How to build libgpiod

This is a fairly standard autotools project. The core C library has no external dependencies other than the standard C library and GNU extensions. The command line tool optionally depends on libedit for interactive functionality. To build the project (including command line tools),

run:

./autogen.sh --enable-tools=yes --prefix=<安装路径>

make

make install

The autogen script will execute ./configure and pass all command line arguments to it.

See: ./configure --help for all configure features.

Bring your own tools

There are currently six command-line tools available:

* gpiodetect - lists all gpiochips present on the system, their names, labels and GPIO line counts

* gpioinfo - lists lines, their gpiochip, offset, name and direction, if in use, user name and any other configured attributes such as active state, bias, drive, edge detection and go Jitter period

* gpioget - read the value of the specified GPIO

* gpioset - set the value of the specified GPIO, keep the line state until the process is terminated or exits

* gpiomon - wait for edge events on GPIO, specify which edges to monitor, how many events to process before exiting, or whether to report events to the console

* gpionotify - wait for changes in GPIO information, specify which changes to monitor, how many events to process before exiting, or whether to report events to the console

Examples of tool usage

# 检测可用的gpiochips。 
$ gpiodetect 
gpiochip0 [pinctrl-bcm2711]
gpiochip1 [raspberrypi-exp-gpio]
 
# 读取gpiochip上所有的信息。 
$ gpioinfo -c 1 
gpiochip1 - 8个: 
  0:“BT_ON” 输出 
  1:“WL_ON” 输出 
  2:“PWR_LED_OFF” 输出低电平 使用者=“led1” 
  3:“GLOBAL_RESET” 输出 
  4:“VDD_SD_IO_SEL” 输出 使用者=“vdd-sd-io” 
  5:“CAM_GPIO” 输出 使用者=“cam1_regulator” 
  6:“SD_PWR_ON” 输出 使用者=“sd_vcc_reg” 
  7:“SD_OC_N” 输入 
 
# 读取特定的信息。 
$ ./gpioinfo PWR_LED_OFF STATUS_LED_G_CLK GLOBAL_RESET 
gpiochip0 42 “STATUS_LED_G_CLK” 输出 使用者=“led0” 
gpiochip1 2 “PWR_LED_OFF” 输出低电平 使用者=“led1” 
gpiochip1 3 “GLOBAL_RESET” 输出 
 
# 按名称读取单个GPIO的值。 
$ gpioget RXD1 
“RXD1” = 激活 
 
# 按芯片和偏移量读取单个GPIO的值。 
$ gpioget -c 0 15 
“15” = 激活 
 
# 以数字值的形式读取单个GPIO的值。 
$ gpioget --numeric RXD1 
1 
 
# 同时读取两个值。将的活动状态设置为低电平,并且不使用带引号的名称。 
$ gpioget --active-low --unquoted GPIO23 GPIO24 
GPIO23 = 激活 GPIO24 = 激活 
 
# 设置的值,并保持该直到被终止。 
$ gpioset GPIO23=1 
 
# 设置两个的值,然后使其成为守护进程并保持。 
$ gpioset --daemonize GPIO23=1 GPIO24=0 
 
# 设置单个的值,保持20毫秒,然后退出。 
$ gpioset --hold-period 20ms -t0 GPIO23=1 
 
# 在GPIO22上以1Hz频率闪烁LED 
$ gpioset -t500ms GPIO22=1 
 
# 在GPIO22上以1Hz频率和20%的工作比闪烁LED 
$ gpioset -t200ms,800ms GPIO22=1 
 
# 以交互方式设置一些(需要--enable-gpioset-interative) 
$ gpioset --interactive --unquoted GPIO23=inactive GPIO24=active 
gpioset> get 
GPIO23 = inactive GPIO24 = active 
gpioset> toggle 
gpioset> get 
GPIO23 = 激活 GPIO24 = inactive 
gpioset> set GPIO24=1 
gpioset> get 
GPIO23 = 激活 GPIO24 = 激活 
gpioset> toggle 
gpioset> get 
GPIO23 = inactive GPIO24 = inactive 
gpioset> toggle GPIO23 
gpioset> get 
GPIO23 = 激活 GPIO24 = inactive 
gpioset> exit 
 
# 等待单个GPIO上的三个上升沿事件,然后退出。 
$ gpiomon --num-events=3 --edges=rising GPIO22 
10002.907638045 上升沿 “GPIO22” 
10037.132562259 上升沿 “GPIO22” 
10047.179790748 上升沿 “GPIO22” 
 
# 在单个GPIO上等待三个边缘事件,使用本地时间和不带引号的名称,然后退出。 
$ gpiomon --num-events=3 --edges=both --localtime --unquoted GPIO22 
2022-11-15T10:36:59.109615508 上升沿 GPIO22 
2022-11-15T10:36:59.129681898 下降沿 GPIO22 
2022-11-15T10:36:59.698971886 上升沿 GPIO22 
 
# 等待下降沿事件并使用自定义输出格式。 
$ gpiomon --format="%e %c %o %l %S" --edges=falling -c gpiochip0 22 
2 gpiochip0 22 GPIO22 10946.693481859 
2 gpiochip0 22 GPIO22 10947.025347604 
2 gpiochip0 22 GPIO22 10947.283716669 
2 gpiochip0 22 GPIO22 10947.570109430 
... 
 
# 阻塞直到发生边缘事件。不打印任何内容。 
$ gpiomon --num-events=1 --quiet GPIO22 
 
# 监视多个,在第一个边缘事件后退出。 
$ gpiomon --quiet --num-events=1 GPIO5 GPIO6 GPIO12 GPIO17 
 
# 监视的信息更改。 
$ gpionotify GPIO23 
11571.816473718 请求 “GPIO23” 
11571.816535124 释放 “GPIO23” 
11572.722894029 请求 “GPIO23” 
11572.722932843 释放 “GPIO23” 
11573.222998598 请求 “GPIO23” 
... 
 
# 监视的请求,报告UTC时间和不带引号的名称。 
$ gpionotify --utc --unquoted GPIO23 
2022-11-15T03:05:23.807090687Z 请求 GPIO23 
2022-11-15T03:05:23.807151390Z 释放 GPIO23 
2022-11-15T03:05:24.784984280Z 请求 GPIO23 
2022-11-15T03:05:24.785023873Z 释放 GPIO23 
... 
 
# 监视多个,在第一个请求后退出。 
$ gpionotify --quiet --num-events=1 --event=requested GPIO5 GPIO6 GPIO12 GPIO17 
 
# 阻塞直到被释放。 
$ gpionotify --quiet --num-events=1 --event=released GPIO6 

 Calculation method of pin

 Such as PG14 == 206 (line) (6x32+14), PG10 == 202 (line)

Check GPIO usage

cat /sys/kernel/debug/gpio

As a result, an error is reported, indicating that there is no such file: cat: can't open '/sys/kernel/debug/gpio': No such file or directory. After checking online, you need to execute this command first:

mount -t debugfs debugfs /sys/kernel/debug

Cross compilation of libgpiod library

1. Install the cross-compilation toolchain: Download and install the corresponding cross-compilation toolchain according to the architecture and operating system of the target platform. For example, for ARM-based Linux systems, you can use the arm-linux-gnueabi toolchain.

2. Download libgpiod source code: Download the latest source code compression package from libgpiod's official warehouse or official website.

3. Unzip the source code: Unzip the downloaded tarball into a directory of your choice.

4. Enter the source code directory: use the terminal to enter the libgpiod source code directory after decompression.

5. Set environment variables: According to your cross-compilation toolchain, set the following environment variables:

   #export CROSS_COMPILE=<交叉编译工具链前缀>
   export CROSS_COMPILE=/opt/okt507/buildroot/host/bin/aarch64-linux-gnu-
   export CC=${CROSS_COMPILE}gcc
   export CXX=${CROSS_COMPILE}g++
   export AR=${CROSS_COMPILE}ar
   export AS=${CROSS_COMPILE}as
   export LD=${CROSS_COMPILE}ld
   export RANLIB=${CROSS_COMPILE}ranlib
   export STRIP=${CROSS_COMPILE}strip

A single setting is troublesome, you can save the script file, for example setenv.sh, and then run the following commands in the terminal to set the environment variables and build the libgpiod library: 

source setenv.sh

This will load the environment variables in the script and make them effective for the current terminal session. You can then proceed to the build steps to compile and install the libgpiod library.

Encountered an error

yang@ubuntu:~/okt507/gpiod/libgpiod-1.6.4$ ./autogen.sh --enable-tools=yes
./autogen.sh: 17: ./autogen.sh: autoreconf: not found

error: possibly undefined macro: AC_CHECK_HEADERS

configure.ac:190: the top level
configure.ac:91: error: possibly undefined macro: AC_CHECK_HEADERS
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.
configure.ac:183: error: possibly undefined macro: AC_LANG_PUSH
configure.ac:185: error: possibly undefined macro: AC_LANG_POP
autoreconf: error: /usr/bin/autoconf failed with exit status: 1

Need to install dependencies:

sudo apt-get install -y autoconf automake libtool
sudo apt-get install autoconf-archive
sudo apt-get install m4

sudo apt install pkg-config

The last pkg-config must not be forgotten, it is a very important dependency, otherwise it is easy to report the following error:

configure.ac:91: error: possibly undefined macro: AC_CHECK_HEADERS
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.

The specific reasons are explained here:

Why 'Possibly Undefined Macro' Means Install pkg-config | AE1020: Lazy Notebook

 Finally, start cross-compiling:

#加载环境变量
source setenv.sh
./autogen.sh --enable-tools=yes --host=arm-linux

#--prefix指定make install的安装目录
./configure --enable-tools=yes --host=arm-linux --prefix=/home/yang/okt507/gpiod/build

Ubuntu turns off background updates to solve the problem of too long restart time

The restart of the Ubuntu virtual machine is found to take too long, and the display shows:A stop job is running for Unattended Upgrades Shutdown (10s / 30 min)

After Baidu, I found that Ubuntu will automatically update, but this time is too long, so I disabled it decisively.

Excuting an order:

sudo dpkg-reconfigure unattended-upgrades

Select no and press ENTER to disable unattended upgrades.
Environment variable setenv.sh script

#!/bin/bash
 # 设置交叉编译工具链前缀
export CROSS_COMPILE=<交叉编译工具链前缀>
 # 设置交叉编译工具链
export CC=${CROSS_COMPILE}gcc
export CXX=${CROSS_COMPILE}g++
export AR=${CROSS_COMPILE}ar
export AS=${CROSS_COMPILE}as
export LD=${CROSS_COMPILE}ld
export RANLIB=${CROSS_COMPILE}ranlib
export STRIP=${CROSS_COMPILE}strip
 # 设置安装路径
#export INSTALL_PATH=<安装路径>

6. Configure the build: Run the following command to configure the build process:

./autogen.sh --enable-tools=yes --prefix=<安装路径>

Replace <安装路径>with the path where you wish to install libgpiod.

7. Run the build: Run the following command to start building the libgpiod library:

8. Install the library: Run the following command to install the built library to the specified installation path:

c API use

#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>

#ifndef	CONSUMER
#define	CONSUMER	"Consumer"
#endif

int blend_led(struct gpiod_line *r, int r_val, struct gpiod_line *g, int g_val, struct gpiod_line *b, int b_val);

/**
* GPIO 16 <-> R
* GPIO 17 <-> B
* GPIO 18 <-> G
*/
int main(int argc, char **argv)
{
	char *chipname = "gpiochip0";
	unsigned int line_num_16 = 16;	// GPIO 16
	unsigned int line_num_17 = 17;	// GPIO 17
	unsigned int line_num_18 = 18;	// GPIO 18
	
	struct gpiod_chip *chip;
	struct gpiod_line *line16, *line17, *line18;
	int ret;

	chip = gpiod_chip_open_by_name(chipname);
	if (!chip) {
		printf("Open chip by name failed. name: %s\n", chipname);
		goto end;
	}
	
	line16 = gpiod_chip_get_line(chip, line_num_16);
	if (!line16) {
		printf("Get line failed. line_num: %u\n", line_num_16);
		goto close_chip;
	}

	line17 = gpiod_chip_get_line(chip, line_num_17);
	if (!line17) {
		printf("Get line failed. line_num: %u\n", line_num_17);
		goto release_line16;
	}

	line18 = gpiod_chip_get_line(chip, line_num_18);
	if (!line18) {
		printf("Get line failed. line_num: %u\n", line_num_18);
		goto release_line17;
	}
	
	ret = gpiod_line_request_output(line16, CONSUMER, 0);
	if (ret < 0) {
		printf("Request line16 as output failed\n");
		goto release_line18;
	}

	ret = gpiod_line_request_output(line17, CONSUMER, 0);
	if (ret < 0) {
		printf("Request line17 as output failed\n");
		goto release_line18;
	}

	ret = gpiod_line_request_output(line18, CONSUMER, 0);
	if (ret < 0) {
		printf("Request line18 as output failed\n");
		goto release_line18;
	}
	
	//R
	ret = blend_led(line16, 1, line17, 0, line18, 0);
	if (ret < 0) {
		printf("Set output failed.\n");
		goto release_line18;
	}
	
	sleep(1);
	//G
	ret = blend_led(line16, 0, line17, 0, line18, 1);
	if (ret < 0) {
		printf("Set output failed.\n");
		goto release_line18;
	}
	sleep(1);
	
	//B
	ret = blend_led(line16, 0, line17, 1, line18, 0);
	if (ret < 0) {
		printf("Set output failed.\n");
		goto release_line18;
	}
	sleep(1);
	
	//yellow
	ret = blend_led(line16, 1, line17, 0, line18, 1);
	if (ret < 0) {
		printf("Set output failed.\n");
		goto release_line18;
	}
	sleep(1);
	
	ret = blend_led(line16, 1, line17, 1, line18, 0);
	if (ret < 0) {
		printf("Set output failed.\n");
		goto release_line18;
	}
	sleep(1);
	
	ret = blend_led(line16, 0, line17, 1, line18, 1);
	if (ret < 0) {
		printf("Set output failed.\n");
		goto release_line18;
	}
	sleep(1);
	
	ret = blend_led(line16, 1, line17, 1, line18, 1);
	if (ret < 0) {
		printf("Set output failed.\n");
		goto release_line18;
	}

release_line18:
	gpiod_line_release(line18);
release_line17:
	gpiod_line_release(line17);
release_line16:
	gpiod_line_release(line16);
close_chip:
	gpiod_chip_close(chip);
end:
	return 0;
}

int blend_led(struct gpiod_line *r, int r_val, struct gpiod_line *g, int g_val, struct gpiod_line *b, int b_val){
	int ret = 0;
	ret = gpiod_line_set_value(r, r_val);
	if (ret < 0) {
		printf("Set r output failed. val: %u\n", r_val);
		return ret;
	}
	
	ret = gpiod_line_set_value(g, g_val);
	if (ret < 0) {
		printf("Set g output failed. val: %u\n", g_val);
		return ret;
	}
	
	ret = gpiod_line_set_value(b, b_val);
	if (ret < 0) {
		printf("Set b output failed. val: %u\n", b_val);
		return ret;
	}
	
	return 0;
}

other resources

qlibgpiod/libgpiod.git - C library and tools for interacting with the linux GPIO character device

The use of Libgpiod library to light up the LED - Programmer Sought

Baidu Security Verification

Autoreconf execution, there is an undefined macro problem - linuxarmsummary's blog - CSDN blog

RaspberryPi 4B uses libgpiod to operate gpio_Raspberry Pi 4b Pin Diagram_TYYJ-Hong Wei's Blog-CSDN Blog

Why 'Possibly Undefined Macro' Means Install pkg-config | AE1020: Lazy Notebook

[imx6ull application development] LED lamp device control of GPIO programming --- sysfs mode and libgpiod mode_libgpiod library_WH^2's blog-CSDN blog 

Guess you like

Origin blog.csdn.net/qq8864/article/details/132413624