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

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;
}
相关推荐
承渊政道16 小时前
Linux网络学习【五种IO模型与⾮阻塞IO】
linux·网络·学习·ubuntu·io模型·非阻塞io
wuminyu17 小时前
JVM执行引擎学习总结:热点字节码编译成本地机器码
java·linux·c语言·jvm·c++
kiros_wang17 小时前
通用工具模块封装:防抖节流、参数校验公共工具类
linux·运维·ubuntu
佳得动17 小时前
编译和链接的认识(C语言)
c语言·开发语言
honsor17 小时前
以太网温湿度传感器|动环监控核心采集设备,协议、组网、Zabbix接入实战
linux·服务器·ubuntu
Android系统攻城狮17 小时前
Linux FFmpeg深度解析之avcodec_default_get_buffer2调用流程与实战(二十)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
深圳恒讯17 小时前
香港云主机硬盘满了怎么办?Linux和Windows在线扩容操作教程
linux·运维·windows
Android系统攻城狮17 小时前
Linux FFmpeg深度解析之av_parser_init调用流程与实战(二十一)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
Navigator_Z17 小时前
LeetCode //C - 1313. Decompress Run-Length Encoded List
c语言·算法·leetcode
HideF18 小时前
Linux 查看用户和组
linux