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

基于Yocto构建的树莓派4镜像无法从用户态读取按键状态

问题:gpio-keys驱动事件无法在用户态读取

现象

  • 内核侧已通过自定义日志确认GPIO17上的微动开关触发时,gpio-keys驱动会上报事件:
[  185.053346] <gpio_keys_gpio_isr>
[  185.076626] <gpio_keys_gpio_report_event> type: 1 | code: 777 | state: 0
[  185.265685] <gpio_keys_gpio_isr>
[  185.268975] <gpio_keys_gpio_isr>
[  185.292253] <gpio_keys_gpio_report_event> type: 1 | code: 777 | state: 1
  • 用户态应用调用read()时挂起,无法获取按键事件。

用户态代码(原错误版本)

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <string.h>
#define DP_EVENT_INPUT "/dev/input/event0"

int main(int argc, char **argv) {

   unsigned int key_states[2];
   struct input_event evt;
   int fd;

   memset(key_states, 0, sizeof(key_states));

   if ((fd = open(DP_EVENT_INPUT, O_RDONLY)) < 0)
   {
       if (errno == EACCES && getuid() != 0) {
           fprintf(stderr, "You do not have access to %s. Try running as root instead.\n",
                DP_EVENT_INPUT);
           exit(EXIT_FAILURE);
       }
   }

   while(1) {

      ioctl(fd, EVIOCGKEYCODE, key_states);
      if(read(fd, &evt, sizeof(struct input_event)) > 0) {

          if(evt.type == EV_KEY) {
             printf("EVENT type: EV_KEY\n");
          }
          else {
             printf("No event!\n");
          }
      }
    }
  }

设备树Overlay

/dts-v1/;
/plugin/;

#include <dt-bindings/pinctrl/bcm2835.h>
#include <dt-bindings/gpio/gpio.h>
#include "dt-bindings/input/linux-event-codes.h"

/ {
   compatible = "raspberrypi,4-model-b", "brcm,bcm2711";

    fragment@0 {
            target = <&gpio>;
            __overlay__ {
        // Configure the gpio pin controller
        buttonpin: button-gpio {
            brcm,pins = <17>;
            brcm,function = <BCM2835_FSEL_GPIO_IN>;
            brcm,pull = <BCM2835_PUD_UP>;
                };
    };
    };

fragment@1 {
    target-path = "/";
    __overlay__ {

        gpio-keys {
            compatible = "gpio-keys";
            pinctrl-names = "default";
            pinctrl-0 = <&buttonpin>;
            status = "okay";
            
            sw1 {
                label = "GPIO Key USER1";
                linux,code = <777>;
                linux,input-type = <EV_KEY>;
                debounce-interval = <20>;
                gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
            };
        };
    };
};
};

问题分析与解决

1. 错误的ioctl调用

你误用了EVIOCGKEYCODE,这个ioctl的作用是查询键码与输入事件的映射关系,而非读取当前按键状态。如果要读取按键状态,应使用EVIOCGKEY,且需要传入足够大的缓冲区(按键状态按位存储,通常定义为unsigned char key_states[KEY_MAX/8 + 1])。

另外,你的场景是要读取按键的按下/释放事件,其实不需要调用ioctl,直接读取input_event结构体即可——gpio-keys驱动会主动上报EV_KEY类型的事件。

2. 设备节点可能不匹配

/dev/input/event0不一定是gpio-keys对应的设备节点。可以通过以下命令确认正确的节点:

cat /proc/bus/input/devices

找到包含GPIO Key USER1标签的设备,查看其Handlers字段(比如eventX),替换代码中的DP_EVENT_INPUT路径。

3. 代码修正

  • 补充缺失的头文件(比如stdlib.h用于exit,errno.h用于errno)
  • 移除错误的EVIOCGKEYCODE调用
  • 完善设备打开失败的错误处理
  • 可选:添加非阻塞模式(如果不想read挂起)

修正后的示例代码:

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

// 替换为实际的event节点
#define DP_EVENT_INPUT "/dev/input/eventX"

int main(int argc, char **argv) {
    struct input_event evt;
    int fd;

    // 可选:添加O_NONBLOCK以非阻塞方式打开
    if ((fd = open(DP_EVENT_INPUT, O_RDONLY)) < 0) {
        if (errno == EACCES && getuid() != 0) {
            fprintf(stderr, "权限不足,请以root身份运行\n");
        } else {
            perror("打开输入设备失败");
        }
        exit(EXIT_FAILURE);
    }

    while(1) {
        ssize_t ret = read(fd, &evt, sizeof(struct input_event));
        if (ret == sizeof(struct input_event)) {
            if(evt.type == EV_KEY) {
                printf("EV_KEY事件:code=%d,state=%d\n", evt.code, evt.value);
                // state=0表示按键释放,state=1表示按下,state=2表示长按重复
            }
        } else if (ret == -1 && errno != EAGAIN) {
            perror("读取事件失败");
            break;
        }
    }

    close(fd);
    return 0;
}

4. 设备树优化(可选)

  • linux,input-type = <EV_KEY>可以省略,gpio-keys驱动默认使用EV_KEY类型
  • 当前的GPIO配置(上拉+GPIO_ACTIVE_LOW)是正确的,与微动开关的接线逻辑匹配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:57:07