将 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