Prerequisites
| Condition | Description |
|---|---|
| System | Debian12 or Ubuntu24 |
| Development library | sudo apt install libgpiod-dev |
| Demo pin | Pin 31 (GPIO2_B0 = gpiochip2 line 8) |
Compilation method
The examples in this article are compiled and run directly on the development board (native compilation), which is simple and straightforward. For cross-compilation, see the Cross-Compilation section at the end.
API Overview
Core objects and basic workflow of libgpiod:
gpiod_chip_open_by_name() -> Get chip
|
gpiod_chip_get_line() -> Get line
|
gpiod_line_request_output() -> Request usage (specify direction)
gpiod_line_request_input()
|
gpiod_line_set_value() -> Read/write operations
gpiod_line_get_value()
|
gpiod_line_release() -> Release
gpiod_chip_close() -> Close2
3
4
5
6
7
8
9
10
11
12
| Object | Description |
|---|---|
struct gpiod_chip | GPIO controller, corresponds to /dev/gpiochipN |
struct gpiod_line | A single GPIO line |
struct gpiod_line_bulk | Collection of multiple lines for batch operations |
Output Control (LED Blinking)
Create file gpio_output.c:
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
struct gpiod_chip *chip;
struct gpiod_line *line;
chip = gpiod_chip_open_by_name("gpiochip2");
if (!chip) {
perror("gpiod_chip_open_by_name");
return 1;
}
line = gpiod_chip_get_line(chip, 8);
if (!line) {
perror("gpiod_chip_get_line");
gpiod_chip_close(chip);
return 1;
}
/* Request as output, initial level is 0 (low) */
if (gpiod_line_request_output(line, "gpio-output-demo", 0) < 0) {
perror("gpiod_line_request_output");
gpiod_chip_close(chip);
return 1;
}
printf("Start blinking Pin 31 (GPIO2_B0)...\n");
for (int i = 0; i < 5; i++) {
gpiod_line_set_value(line, 1);
printf(" HIGH\n");
usleep(500000);
gpiod_line_set_value(line, 0);
printf(" LOW\n");
usleep(500000);
}
gpiod_line_release(line);
gpiod_chip_close(chip);
printf("Done\n");
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Compile and run:
gcc gpio_output.c -o gpio_output -lgpiod
sudo ./gpio_output2
Run output
Start blinking Pin 31 (GPIO2_B0)...
HIGH
LOW
HIGH
LOW
HIGH
LOW
HIGH
LOW
HIGH
LOW
Done2
3
4
5
6
7
8
9
10
11
12
If Pin 31 has an external LED connected (with a 330 ohm current-limiting resistor in series to GND), you can see the LED blinking.
Input Reading
Create file gpio_input.c:
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
struct gpiod_chip *chip;
struct gpiod_line *line;
chip = gpiod_chip_open_by_name("gpiochip2");
if (!chip) {
perror("gpiod_chip_open_by_name");
return 1;
}
line = gpiod_chip_get_line(chip, 8);
if (!line) {
perror("gpiod_chip_get_line");
gpiod_chip_close(chip);
return 1;
}
/* Request as input, enable internal pull-up */
if (gpiod_line_request_input_flags(line, "gpio-input-demo",
GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP) < 0) {
perror("gpiod_line_request_input_flags");
gpiod_chip_close(chip);
return 1;
}
printf("Reading Pin 31 (GPIO2_B0) level, 10 times...\n");
for (int i = 0; i < 10; i++) {
int val = gpiod_line_get_value(line);
printf(" [%d] Level: %s\n", i + 1, val ? "HIGH" : "LOW");
sleep(1);
}
gpiod_line_release(line);
gpiod_chip_close(chip);
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Compile and run:
gcc gpio_input.c -o gpio_input -lgpiod
sudo ./gpio_input2
With internal pull-up configured, the pin reads HIGH (1) when floating, and LOW (0) when connected to GND.
Common request function reference
| Function | Direction | Bias |
|---|---|---|
gpiod_line_request_input() | Input | None |
gpiod_line_request_input_flags() | Input | Can specify pull-up/pull-down |
gpiod_line_request_output() | Output | None |
gpiod_line_request_output_flags() | Output | Can specify open-drain, etc. |
Common flags values:
GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP-- Internal pull-upGPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN-- Internal pull-downGPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN-- Open-drain output
Edge Event Detection (Interrupts)
Edge detection is the biggest advantage of libgpiod over sysfs -- no polling is needed, the kernel notifies user space when the level changes.
Create file gpio_event.c:
#include <gpiod.h>
#include <stdio.h>
int main(void)
{
struct gpiod_chip *chip;
struct gpiod_line *line;
struct gpiod_line_event event;
struct timespec timeout = { 5, 0 }; /* 5 second timeout */
chip = gpiod_chip_open_by_name("gpiochip2");
if (!chip) {
perror("gpiod_chip_open_by_name");
return 1;
}
line = gpiod_chip_get_line(chip, 8);
if (!line) {
perror("gpiod_chip_get_line");
gpiod_chip_close(chip);
return 1;
}
/* Request both-edge event monitoring, enable internal pull-up */
if (gpiod_line_request_both_edges_events_flags(line, "gpio-event-demo",
GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP) < 0) {
perror("gpiod_line_request_both_edges_events_flags");
gpiod_chip_close(chip);
return 1;
}
printf("Waiting for Pin 31 (GPIO2_B0) edge events (5s timeout)...\n");
while (1) {
int ret = gpiod_line_event_wait(line, &timeout);
if (ret < 0) {
perror("gpiod_line_event_wait");
break;
}
if (ret == 0) {
printf(" Timeout, continuing to wait...\n");
continue;
}
/* Read event */
if (gpiod_line_event_read(line, &event) < 0) {
perror("gpiod_line_event_read");
break;
}
printf(" %s @ %ld.%09ld\n",
event.event_type == GPIOD_LINE_EVENT_RISING_EDGE ? "RISING " : "FALLING",
event.ts.tv_sec, event.ts.tv_nsec);
}
gpiod_line_release(line);
gpiod_chip_close(chip);
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Compile and run:
gcc gpio_event.c -o gpio_event -lgpiod
sudo ./gpio_event2
After running, the program blocks waiting. Connect Pin 31 to GND and then release it, and the terminal will print edge events with timestamps.
Event request function reference
| Function | Edge Monitored |
|---|---|
gpiod_line_request_rising_edge_events() | Rising edge only |
gpiod_line_request_falling_edge_events() | Falling edge only |
gpiod_line_request_both_edges_events() | Both edges |
gpiod_line_request_both_edges_events_flags() | Both edges + flags |
Operating Multiple Lines Simultaneously
Use gpiod_line_bulk to control multiple lines in a single operation, suitable for scenarios requiring synchronized control of multiple GPIOs.
Create file gpio_multi.c:
#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
struct gpiod_chip *chip;
struct gpiod_line_bulk bulk;
struct gpiod_line *line8, *line16;
int values[2];
chip = gpiod_chip_open_by_name("gpiochip2");
if (!chip) {
perror("gpiod_chip_open_by_name");
return 1;
}
/* Pin 31 (GPIO2_B0 = line 8) and Pin 29 (GPIO2_C0 = line 16) */
line8 = gpiod_chip_get_line(chip, 8);
line16 = gpiod_chip_get_line(chip, 16);
if (!line8 || !line16) {
perror("gpiod_chip_get_line");
gpiod_chip_close(chip);
return 1;
}
gpiod_line_bulk_init(&bulk);
gpiod_line_bulk_add(&bulk, line8);
gpiod_line_bulk_add(&bulk, line16);
/* Batch request as output, initial levels both 0 */
int defaults[] = {0, 0};
if (gpiod_line_request_bulk_output(&bulk, "gpio-multi-demo", defaults) < 0) {
perror("gpiod_line_request_bulk_output");
gpiod_chip_close(chip);
return 1;
}
/* Alternately blink two pins */
printf("Alternately blinking Pin 31 and Pin 29...\n");
for (int i = 0; i < 10; i++) {
values[0] = (i % 2 == 0) ? 1 : 0;
values[1] = (i % 2 == 0) ? 0 : 1;
gpiod_line_set_value_bulk(&bulk, values);
usleep(300000);
}
gpiod_line_release_bulk(&bulk);
gpiod_chip_close(chip);
printf("Done\n");
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Compile and run:
gcc gpio_multi.c -o gpio_multi -lgpiod
sudo ./gpio_multi2
Cross-Compilation
Recommendation
For small programs like libgpiod examples, compiling directly on the development board is the simplest approach (the board has sufficient performance). Cross-compilation is suitable for scenarios that need to be integrated into automated build workflows.
# Execute on Ubuntu x86_64 host
sudo dpkg --add-architecture arm64
sudo apt update
sudo apt install gcc-aarch64-linux-gnu libgpiod-dev:arm64
# Compile
aarch64-linux-gnu-gcc gpio_output.c -o gpio_output -lgpiod
# Transfer to board and run
scp gpio_output root@<board-IP>:/root/2
3
4
5
6
7
8
9
10
# SDK toolchain path
# TaishanPi-3-Linux/prebuilts/gcc/linux-x86/aarch64/
# gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/
# aarch64-none-linux-gnu-gcc
# SDK sysroot does not have libgpiod pre-installed, need to copy from board
mkdir -p /tmp/gpiod-sysroot/usr/{include,lib}
scp root@<board-IP>:/usr/include/gpiod.h /tmp/gpiod-sysroot/usr/include/
scp root@<board-IP>:/usr/lib/aarch64-linux-gnu/libgpiod.* \
/tmp/gpiod-sysroot/usr/lib/
# Compile
export SDK=~/TaishanPi-3-Linux
export CC=$SDK/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc
$CC gpio_output.c -o gpio_output \
-I/tmp/gpiod-sysroot/usr/include \
-L/tmp/gpiod-sysroot/usr/lib \
-lgpiod2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Common Issues
Permission denied
GPIO operations require root privileges. Please run with sudo. Or add the user to the gpio group (requires re-login):
sudo usermod -aG gpio $USERDevice or resource busy
The pin is already occupied by another program or kernel driver. Troubleshooting:
gpioinfo gpiochip2 | grep "line 8"If it shows [used], it means a kernel driver is occupying it, and you need to disable the corresponding function in the device tree.
Cannot find gpiod.h
sudo apt install libgpiod-devReferences
- libgpiod official repository: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/
- Kernel GPIO documentation:
Documentation/gpio/directory