外设与接口:按键输入 (libgpiod)

1 API 介绍

以下是处理 GPIO 中断事件的核心 API。

1.1 事件请求与监听
c 复制代码
int gpiod_line_request_both_edges_events(struct gpiod_line *line, const char *consumer);
  • 参数
    • linegpiod_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);
  • 参数
    • linegpiod_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;
}
相关推荐
sdm0704271 小时前
yum和开发工具vim/gcc
linux·服务器·centos
如意.7597 小时前
【Linux开发工具实战】Git、GDB与CGDB从入门到精通
linux·运维·git
爱编码的小八嘎7 小时前
C语言完美演绎4-7
c语言
Thera7777 小时前
C++ 高性能时间轮定时器:从单例设计到 Linux timerfd 深度优化
linux·开发语言·c++
炘爚8 小时前
C语言(文件操作)
c语言·开发语言
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ8 小时前
Linux 查询某进程文件所在路径 命令
linux·运维·服务器
W.D.小糊涂9 小时前
gpu服务器安装windows+ubuntu24.04双系统
c语言·开发语言·数据库
安当加密10 小时前
无需改 PAM!轻量级 RADIUS + ASP身份认证系统 实现 Linux 登录双因子认证
linux·运维·服务器
内卷焦虑人士10 小时前
Windows安装WSL2+Ubuntu 22.04
linux·windows·ubuntu
C羊驼10 小时前
C语言:两天打鱼,三天晒网
c语言·经验分享·笔记·算法·青少年编程