1 API 介绍
以下是处理 GPIO 中断事件的核心 API。
1.1 事件请求与监听
c
int gpiod_line_request_both_edges_events(struct gpiod_line *line, const char *consumer);
- 参数 :
line:gpiod_line对象指针。consumer:消费者名称(如"button-listener")。
- 作用:将引脚配置为输入模式,同时监听 上升沿(Rising Edge)和 下降沿(Falling Edge)。
- 返回 :成功返回
0,失败返回-1。
1.2 等待事件(阻塞)
c
int gpiod_line_event_wait(struct gpiod_line *line, const struct timespec *timeout);
- 参数 :
line:已请求事件的gpiod_line对象指针。timeout:超时时间。传NULL表示无限等待(直到事件发生才唤醒,CPU 占用率为 0)。
- 作用:阻塞当前进程,等待中断事件。这是实现低功耗按键检测的核心。
- 返回 :
0表示超时,1表示有事件发生,-1表示发生错误。
1.3 读取事件详情
事件数据结构:
c
struct gpiod_line_event {
struct timespec ts; // 事件发生的时间戳(纳秒级精度)
int event_type; // 事件类型
};
- event_type 常用值 :
GPIOD_LINE_EVENT_RISING_EDGE:上升沿。GPIOD_LINE_EVENT_FALLING_EDGE:下降沿。
c
int gpiod_line_event_read(struct gpiod_line *line, struct gpiod_line_event *event);
- 参数 :
line:gpiod_line对象指针。event:指向struct gpiod_line_event的指针,用于接收事件数据。
- 返回 :成功返回
0,失败返回-1。
2 两种实现方案
2.1 轮询模式
代码如下:
c
#include <gpiod.h>
#include <stdio.h>
#include <time.h> // for nanosleep
#define CHIP_NAME "gpiochip3"
#define LINE_OFFSET 5 // GPIO3_A5
#define DEBOUNCE_MS 20 // 消抖时间窗口
// 辅助函数:毫秒级延时
void msleep(long ms) {
struct timespec ts = { .tv_sec = ms / 1000, .tv_nsec = (ms % 1000) * 1000000 };
nanosleep(&ts, NULL);
}
int main() {
struct gpiod_chip *chip = gpiod_chip_open_by_name(CHIP_NAME);
struct gpiod_line *line = gpiod_chip_get_line(chip, LINE_OFFSET);
int val, last_val = 1; // 假设初始状态为高(松开)
// 1. 仅配置为输入模式 (不使用中断)
if (gpiod_line_request_input(line, "debounce-worker") < 0) {
perror("Request input failed");
return -1;
}
while (1) {
// 2. 主动轮询当前值
val = gpiod_line_get_value(line);
// 3. 发现电平变化
if (val != last_val) {
// 4. 暂停 20ms,让信号飞一会儿(过滤噪声)
msleep(DEBOUNCE_MS);
// 5. 再次读取进行确认 (Double Check)
int stable_val = gpiod_line_get_value(line);
if (stable_val == val) { // 如果20ms后电平依然没变,认为是有效按键
if (stable_val == 0)
printf(">>> Button PRESSED (Stable)\n");
else
printf(" Button RELEASED (Stable)\n");
last_val = stable_val; // 更新状态机
}
}
// 6. 降低轮询频率,保护 CPU (至关重要)
msleep(10);
}
gpiod_line_release(line);
gpiod_chip_close(chip);
return 0;
}
2.1 中断模式
利用内核中断机制,程序不需要时刻盯着引脚,而是挂起等待内核唤醒。

代码如下 :
c
#include <gpiod.h>
#include <stdio.h>
#include <stdlib.h> // for system()
#define CHIP_NAME "gpiochip0"
#define LINE_OFFSET 14 // GPIO0_B6 (Group 1 * 8 + 6 = 14)
int main() {
struct gpiod_chip *chip;
struct gpiod_line *line;
struct gpiod_line_event event;
// 1. 基础初始化
chip = gpiod_chip_open_by_name(CHIP_NAME);
line = gpiod_chip_get_line(chip, LINE_OFFSET);
// 2. 请求监测双边沿事件 (关键 API)
if (gpiod_line_request_both_edges_events(line, "button-listener") < 0) {
perror("Request events failed");
return -1;
}
printf("Waiting for events on %s line %d...\n", CHIP_NAME, LINE_OFFSET);
while (1) {
// 3. 阻塞等待事件,无超时 (NULL),CPU 占用 0%
if (gpiod_line_event_wait(line, NULL) > 0) {
// 4. 读取具体事件
gpiod_line_event_read(line, &event);
// 5. 区分按下还是松开
if (event.event_type == GPIOD_LINE_EVENT_FALLING_EDGE) {
printf("[Timestamp: %ld] Button PRESSED\n", event.ts.tv_sec);
// 业务逻辑:例如 system("/usr/bin/restart_app.sh &");
} else {
printf("[Timestamp: %ld] Button RELEASED\n", event.ts.tv_sec);
}
}
}
gpiod_line_release(line);
gpiod_chip_close(chip);
return 0;
}