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
相关产品推荐
相关产品推荐

