外设与接口:按键输入 (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;
}
相关推荐
C_心欲无痕8 小时前
ts - tsconfig.json配置讲解
linux·前端·ubuntu·typescript·json
冰西瓜6009 小时前
国科大2025操作系统高级教程期末回忆版
linux
HIT_Weston10 小时前
93、【Ubuntu】【Hugo】搭建私人博客:面包屑(一)
linux·运维·ubuntu
cuijiecheng201810 小时前
Linux下Beyond Compare过期
linux·运维·服务器
HIT_Weston10 小时前
92、【Ubuntu】【Hugo】搭建私人博客:侧边导航栏(六)
linux·运维·ubuntu
CodeAllen嵌入式10 小时前
Windows 11 本地安装 WSL 支持 Ubuntu 24.04 完整指南
linux·运维·ubuntu
Felven11 小时前
A. Helmets in Night Light
c语言
码农小韩12 小时前
基于Linux的C++学习——指针
linux·开发语言·c++·学习·算法
wdfk_prog12 小时前
[Linux]学习笔记系列 -- [fs]seq_file
linux·笔记·学习
Jay Chou why did12 小时前
wsl安装完无法进入wsl
linux