You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux GPIO用户空间API v2引脚轮询实现问题咨询

树莓派GPIO v2 API引脚轮询实现修复

你的代码存在几个关键问题,导致无法正确检测GPIO引脚状态变化,下面是问题分析和修复后的实现:

问题点分析

  • 结构体未完整初始化:struct gpio_v2_line_request的多个必填字段未设置(比如num_offsets、consumer),GPIO v2 API要求必须明确指定这些参数才能完成引脚请求。
  • 未读取事件数据:触发POLLIN事件后,必须从req.fd读取struct gpio_v2_line_event结构体数据,否则内核会持续触发POLLIN,且无法捕获后续事件。
  • 事件类型与打印不匹配:代码配置的是上升沿(GPIO_V2_LINE_EVENT_RISING_EDGE),但打印信息标注的是下降沿,逻辑混淆。
  • 未检查ioctl调用结果:如果GPIO_V2_GET_LINE_IOCTL调用失败,后续操作都是无效的,必须先验证返回值。

修复后的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <string.h>
#include <linux/gpio.h>

#define DEV_NAME "/dev/gpiochip0"
#define GPIO_OFFSET 17

void gpio_poll(int fd)
{
    struct gpio_v2_line_request req = {0}; // 初始化结构体为0
    struct pollfd pfd;
    struct gpio_v2_line_event event;

    // 填充请求结构体必填字段
    req.num_offsets = 1;
    req.offsets[0] = GPIO_OFFSET;
    strncpy(req.consumer, "gpio-poll-example", sizeof(req.consumer) - 1);
    req.config.flags = GPIO_V2_LINE_EVENT_RISING_EDGE | GPIO_V2_LINE_EVENT_FALLING_EDGE; // 同时监听升降沿(根据按键类型调整)
    req.config.attrs[0].attr = GPIO_V2_LINE_ATTR_ID_PULL_UP; // 启用上拉电阻(如果按键是接地触发)

    // 请求引脚事件监听
    int ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
    if (ret == -1) {
        fprintf(stderr, "GPIO line request failed: %s\n", strerror(errno));
        return;
    }

    pfd.fd = req.fd;
    pfd.events = POLLIN;

    while (1) {
        ret = poll(&pfd, 1, -1);
        if (ret == -1) {
            fprintf(stderr, "Poll error: %s\n", strerror(errno));
            close(req.fd);
            return;
        }

        if (pfd.revents & POLLIN) {
            // 读取事件数据,必须执行此操作以清除事件状态
            ret = read(req.fd, &event, sizeof(event));
            if (ret != sizeof(event)) {
                fprintf(stderr, "Failed to read event: %s\n", strerror(errno));
                continue;
            }

            // 根据事件类型打印信息
            if (event.id == GPIO_V2_LINE_EVENT_RISING_EDGE) {
                printf("Rising edge detected on GPIO %d (%s)\n", GPIO_OFFSET, DEV_NAME);
            } else if (event.id == GPIO_V2_LINE_EVENT_FALLING_EDGE) {
                printf("Falling edge detected on GPIO %d (%s)\n", GPIO_OFFSET, DEV_NAME);
            }
        }
    }

    close(req.fd);
}

int main()
{
    int fd = open(DEV_NAME, O_RDONLY);
    if (fd == -1) {
        fprintf(stderr, "Failed to open GPIO chip: %s\n", strerror(errno));
        return EXIT_FAILURE;
    }

    gpio_poll(fd);
    close(fd);
    return EXIT_SUCCESS;
}

关键修复说明

  • 完整初始化请求结构体:
    • 设置num_offsets为1(因为只请求一个引脚)
    • 指定consumer名称(GPIO v2 API要求标识请求方)
    • 根据按键触发方式配置上拉/下拉电阻(示例中启用上拉,适合接地型按键)
  • 读取事件数据:每次POLLIN触发后,必须调用read()读取事件结构体,否则内核会一直标记该fd为可读状态,无法正常捕获后续事件。
  • 匹配事件类型与打印信息:明确区分上升沿和下降沿事件的打印内容,避免逻辑混淆。
  • 添加错误处理:检查ioctl、read、poll的返回值,及时处理错误并清理资源。
  • 移除无用的usleep:poll()本身是阻塞等待事件,不需要额外睡眠延迟。

编译与测试

使用gcc编译:

gcc gpio_poll.c -o gpio_poll

运行程序(需要root权限):

sudo ./gpio_poll

内容的提问来源于stack exchange,提问作者MrForwardMotion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 02:06:01