将 PE8 / PE9 IO 转换为按键输入功能 (gpio-keys)

1. 背景

在 Allwinner T113 (ZQBoard) 等嵌入式 Linux 开发中,部分 IO 口(如 PE8 和 PE9)默认作为 LED 控制输出使用。如果需要将这些 IO 改为按键输入并通过 /dev/input/eventX 设备读取按键事件,可以使用 Linux 的 gpio-keys 驱动实现。

本文将为大家介绍把 PE8 和 PE9 从 LED 输出改为按键输入的具体方法。


2. 修改步骤

2.1 启用 gpio-keys 驱动

执行:

make kernel_menuconfig

进入内核菜单配置界面,找到:

Device Drivers --->
    Input device support --->
        [*] Keyboards --->
            [*] GPIO Buttons

确保 GPIO Buttons 驱动被启用并保存。


2.2 修改设备树

设备树路径: device/config/chips/t113/configs/zqboard/linux-5.4/board.dts

2.2.1 注释掉 PE8 / PE9 LED 配置

找到 &soc 节点下的 leds 子节点,将 PE8 / PE9 对应部分注释或删除:

&soc {
    leds {
        compatible = "gpio-leds";
        gpio_pe0 {
            label = "GPIO_PE0";
            gpios = <&pio PE 0 GPIO_ACTIVE_HIGH>; /* PE0 */
            default-state = "off";
        };

        gpio_pe1 {
            label = "GPIO_PE1";
            gpios = <&pio PE 1 GPIO_ACTIVE_HIGH>; /* PE1 */
            default-state = "off";
        };

    /*    gpio_pe8 {
            label = "GPIO_PE8";
            gpios = <&pio PE 8 GPIO_ACTIVE_HIGH>;
            default-state = "off";
        };

        gpio_pe9 {
            label = "GPIO_PE9";
            gpios = <&pio PE 9 GPIO_ACTIVE_HIGH>;
            default-state = "off";
        };*/
    };

2.2.2 增加 gpio-keys 节点

&soc 节点中添加:

    gpio-keys {
        compatible = "gpio-keys";

        key_pe8 {
            label = "PE8_INPUT";
            gpios = <&pio PE 8 GPIO_ACTIVE_HIGH>;
            linux,code = <KEY_1>;
        };

        key_pe9 {
            label = "PE9_INPUT";
            gpios = <&pio PE 9 GPIO_ACTIVE_HIGH>;
            linux,code = <KEY_2>;
        };
    };
};

2.2.3 添加输入按键定义头文件

#include "sun8iw20p1.dtsi" 上面插入:

#include <dt-bindings/input/input.h>

修改后文件开头示例:

/*
 * Allwinner Technology CO., Ltd.
 */

/dts-v1/;

/memreserve/ 0x41B00000 0x00100000;
/* DSP used 1MB */
/*/memreserve/ 0x42000000 0x00100000;*/
#include <dt-bindings/input/input.h>
#include "sun8iw20p1.dtsi"

input.h 中定义了 KEY_1, KEY_2 等常量,即 linux,code 的对应值。


3. 编译 & 烧录

保存设备树文件后,重新编译内核:

make
pack

将新的固件烧录到开发板。


4. 测试验证

4.1 确认按键设备节点

进入开发板命令行:

ls /dev/input/event*

示例:

/dev/input/event0  /dev/input/event1  /dev/input/event2

找到新出现的按键设备(如 event2)。

查看设备名称:

cat /sys/class/input/event2/device/name

输出类似:

soc@3000000:gpio-keys

4.2 Python 测试脚本

保存为 key_test.py

#!/usr/bin/env python3
import struct

EV_KEY = 0x01
KEY_NAMES = { 2: "KEY_1", 3: "KEY_2" }
DEVICE = "/dev/input/event2"  # 替换为实际设备路径

def main():
    with open(DEVICE, "rb") as f:
        while True:
            data = f.read(16)
            if not data:
                break
            sec, usec, ev_type, code, value = struct.unpack("llHHI", data)
            if ev_type == EV_KEY:
                action = {0:"RELEASE",1:"PRESS",2:"HOLD"}.get(value,"UNKNOWN")
                key_name = KEY_NAMES.get(code, f"KEY_{code}")
                print("{:.6f}: {} {}".format(sec + usec/1e6, key_name, action))

if __name__ == "__main__":
    main()

运行:

python3 key_test.py

用杜邦线将 PE8 或 PE9 接到 GND 或 VCC,即可看到按键事件信息。 GND 或 VCC 在引出的gpios的排针另一侧就有


4.3 C 语言测试程序

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

const char* key_name(int code) {
    switch (code) {
        case KEY_1: return "KEY_1";
        case KEY_2: return "KEY_2";
        default: {
            static char buf[32];
            snprintf(buf, sizeof(buf), "KEY_%d", code);
            return buf;
        }
    }
}

const char* action_name(int value) {
    switch (value) {
        case 0: return "RELEASE";
        case 1: return "PRESS";
        case 2: return "HOLD";
        default: return "UNKNOWN";
    }
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s /dev/input/eventX\n", argv[0]);
        return 1;
    }
    const char *device = argv[1];
    int fd = open(device, O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "Error opening %s: %s\n", device, strerror(errno));
        return 1;
    }
    struct input_event ev;
    while (1) {
        ssize_t n = read(fd, &ev, sizeof(ev));
        if (n == -1) {
            fprintf(stderr, "Error reading: %s\n", strerror(errno));
            break;
        }
        if (ev.type == EV_KEY) {
            printf("%ld.%06ld: %s %s\n",
                   (long)ev.time.tv_sec,
                   (long)ev.time.tv_usec,
                   key_name(ev.code),
                   action_name(ev.value));
            fflush(stdout);
        }
    }
    close(fd);
    return 0;
}

5. 实际测试效果

按下 PE9 输入:

1758621930.736401: KEY_2 PRESS
1758621933.656351: KEY_2 RELEASE

按下 PE8 输入:

1758621930.776376: KEY_1 PRESS
1758621934.356355: KEY_1 RELEASE