前置条件
| 条件 | 说明 |
|---|---|
| 系统 | Debian12 或 Ubuntu24 |
| 开发库 | sudo apt install libgpiod-dev |
| 演示引脚 | Pin 31(GPIO2_B0 = gpiochip2 line 8) |
编译方式
本文示例直接在开发板上编译运行(native 编译),简单直接。交叉编译方式见文末 交叉编译 章节。
API 概览
libgpiod 的核心对象和基本流程:
text
gpiod_chip_open_by_name() → 获取 chip
↓
gpiod_chip_get_line() → 获取 line
↓
gpiod_line_request_output() → 申请使用(指定方向)
gpiod_line_request_input()
↓
gpiod_line_set_value() → 读写操作
gpiod_line_get_value()
↓
gpiod_line_release() → 释放
gpiod_chip_close() → 关闭1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
| 对象 | 说明 |
|---|---|
struct gpiod_chip | GPIO 控制器,对应 /dev/gpiochipN |
struct gpiod_line | 单条 GPIO 线 |
struct gpiod_line_bulk | 多条线的集合,用于批量操作 |
输出控制(点灯)
创建文件 gpio_output.c:
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;
}
/* 申请为输出,初始电平为 0(低) */
if (gpiod_line_request_output(line, "gpio-output-demo", 0) < 0) {
perror("gpiod_line_request_output");
gpiod_chip_close(chip);
return 1;
}
printf("开始闪烁 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("完成\n");
return 0;
}1
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
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
编译运行:
bash
gcc gpio_output.c -o gpio_output -lgpiod
sudo ./gpio_output1
2
2
运行输出
text
开始闪烁 Pin 31 (GPIO2_B0)...
HIGH
LOW
HIGH
LOW
HIGH
LOW
HIGH
LOW
HIGH
LOW
完成1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
如果 Pin 31 外接了 LED(串联 330Ω 限流电阻到 GND),可以看到 LED 闪烁。
输入读取
创建文件 gpio_input.c:
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;
}
/* 申请为输入,启用内部上拉 */
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("读取 Pin 31 (GPIO2_B0) 电平,共 10 次...\n");
for (int i = 0; i < 10; i++) {
int val = gpiod_line_get_value(line);
printf(" [%d] 电平: %s\n", i + 1, val ? "HIGH" : "LOW");
sleep(1);
}
gpiod_line_release(line);
gpiod_chip_close(chip);
return 0;
}1
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
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
编译运行:
bash
gcc gpio_input.c -o gpio_input -lgpiod
sudo ./gpio_input1
2
2
配置了内部上拉,引脚悬空时读到 HIGH(1),接 GND 后读到 LOW(0)。
常用 request 函数对照
| 函数 | 方向 | 偏置 |
|---|---|---|
gpiod_line_request_input() | 输入 | 无 |
gpiod_line_request_input_flags() | 输入 | 可指定上拉/下拉 |
gpiod_line_request_output() | 输出 | 无 |
gpiod_line_request_output_flags() | 输出 | 可指定开漏等 |
flags 常用值:
GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP— 内部上拉GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN— 内部下拉GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN— 开漏输出
边沿事件检测(中断)
边沿检测是 libgpiod 相比 sysfs 最大的优势——不需要轮询,内核在电平变化时通知用户空间。
创建文件 gpio_event.c:
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 秒超时 */
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;
}
/* 申请双边沿事件监听,启用内部上拉 */
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("等待 Pin 31 (GPIO2_B0) 边沿事件(5秒超时)...\n");
while (1) {
int ret = gpiod_line_event_wait(line, &timeout);
if (ret < 0) {
perror("gpiod_line_event_wait");
break;
}
if (ret == 0) {
printf(" 超时,继续等待...\n");
continue;
}
/* 读取事件 */
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;
}1
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
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
编译运行:
bash
gcc gpio_event.c -o gpio_event -lgpiod
sudo ./gpio_event1
2
2
运行后程序阻塞等待。将 Pin 31 接 GND 再松开,终端会打印边沿事件和时间戳。
事件请求函数对照
| 函数 | 监听边沿 |
|---|---|
gpiod_line_request_rising_edge_events() | 仅上升沿 |
gpiod_line_request_falling_edge_events() | 仅下降沿 |
gpiod_line_request_both_edges_events() | 双边沿 |
gpiod_line_request_both_edges_events_flags() | 双边沿 + flags |
同时操作多条线
通过 gpiod_line_bulk 在一次操作中控制多条线,适合需要同步控制多个 GPIO 的场景。
创建文件 gpio_multi.c:
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) 和 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);
/* 批量申请为输出,初始电平都为 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;
}
/* 交替闪烁两个引脚 */
printf("交替闪烁 Pin 31 和 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("完成\n");
return 0;
}1
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
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
编译运行:
bash
gcc gpio_multi.c -o gpio_multi -lgpiod
sudo ./gpio_multi1
2
2
交叉编译
推荐
对于 libgpiod 这类小程序,直接在开发板上编译是最简单的方式(板子性能足够)。交叉编译适合需要集成到自动化构建流程的场景。
bash
# 在 Ubuntu x86_64 主机上执行
sudo dpkg --add-architecture arm64
sudo apt update
sudo apt install gcc-aarch64-linux-gnu libgpiod-dev:arm64
# 编译
aarch64-linux-gnu-gcc gpio_output.c -o gpio_output -lgpiod
# 传输到板子运行
scp gpio_output root@<板子IP>:/root/1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
bash
# SDK 工具链路径
# 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 中没有预装 libgpiod,需要从板子拷贝
mkdir -p /tmp/gpiod-sysroot/usr/{include,lib}
scp root@<板子IP>:/usr/include/gpiod.h /tmp/gpiod-sysroot/usr/include/
scp root@<板子IP>:/usr/lib/aarch64-linux-gnu/libgpiod.* \
/tmp/gpiod-sysroot/usr/lib/
# 编译
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 \
-lgpiod1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
常见问题
Permission denied
GPIO 操作需要 root 权限,请使用 sudo 运行。或者将用户加入 gpio 组(需重新登录):
bash
sudo usermod -aG gpio $USER1
Device or resource busy
引脚已被其他程序或内核驱动占用。排查方法:
bash
gpioinfo gpiochip2 | grep "line 8"1
如果显示 [used],说明有内核驱动占用,需要在设备树中禁用对应功能。
找不到 gpiod.h
bash
sudo apt install libgpiod-dev1
参考资料
- libgpiod 官方仓库:https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/
- 内核 GPIO 文档:
Documentation/gpio/目录