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
Autoreconf execution, there is an undefined macro problem - linuxarmsummary's blog - CSDN blog
Why 'Possibly Undefined Macro' Means Install pkg-config | AE1020: Lazy Notebook