Keyboard_Class & KeysState
头文件: utility/Keyboard/Keyboard.h
访问: M5Cardputer.Keyboard
处理所有键盘输入:物理按键扫描、字符映射、修饰键跟踪、Caps Lock 以及 USB HID 键码生成。
KeysState
当前按下按键的快照,由 updateKeysState() 填充。
字段
| 字段 | 类型 | 说明 |
|---|---|---|
tab |
bool |
Tab 键被按下 |
fn |
bool |
Fn 修饰键激活 |
shift |
bool |
Shift 修饰键激活 |
ctrl |
bool |
Ctrl 修饰键激活 |
opt |
bool |
Opt 修饰键激活 |
alt |
bool |
Alt 修饰键激活 |
del |
bool |
退格键被按下 |
enter |
bool |
回车键被按下 |
space |
bool |
空格键被按下 |
modifiers |
uint8_t |
USB HID 修饰键位掩码 |
word |
std::vector<char> |
可打印字符(遵循 Shift/Ctrl/Caps 状态) |
hid_keys |
std::vector<uint8_t> |
所有非修饰键的 USB HID 键码 |
modifier_keys |
std::vector<uint8_t> |
修饰键的 USB HID 键码 |
reset()
清空所有字段:布尔值 → false,向量清空。
Keyboard_Class 方法
begin()
自动检测板型并创建对应的 KeyboardReader:
- board_M5Cardputer → IOMatrixKeyboardReader(GPIO 矩阵)
- board_M5CardputerADV → TCA8418KeyboardReader(I2C/TCA8418)
由 M5Cardputer.begin() 自动调用 — 通常无需手动调用。
begin(reader)
注入自定义的 KeyboardReader 实现。用于测试或自定义键盘硬件。
auto customReader = std::make_unique<IOMatrixKeyboardReader>();
M5Cardputer.Keyboard.begin(std::move(customReader));
updateKeyList()
委托给活动的 KeyboardReader 扫描物理矩阵并刷新按键列表。由 M5Cardputer.update() 自动调用。
keyList()
返回所有当前按下的按键坐标,格式为 Point2D_t {x, y}。
| 返回值 | 说明 |
|---|---|
const std::vector<Point2D_t>& |
按下的按键位置列表 |
void loop() {
M5Cardputer.update();
auto& keys = M5Cardputer.Keyboard.keyList();
M5Cardputer.Display.setCursor(0, 0);
M5Cardputer.Display.printf("按下按键: %d\n", keys.size());
}
isPressed()
返回当前按住的按键数量。无按键时返回 0。
| 返回值 | 说明 |
|---|---|
uint8_t |
按下的按键数量 |
void loop() {
M5Cardputer.update();
if (M5Cardputer.Keyboard.isPressed()) {
// 至少有一个键被按住
M5Cardputer.Display.drawString("输入中...", 10, 10);
}
}
isChange()
自上次调用以来按键数量发生变化时返回 true。用于检测新输入而无需每帧处理。
| 返回值 | 说明 |
|---|---|
true |
按键数量变化(按下或释放) |
false |
自上次检查以来无变化 |
void loop() {
M5Cardputer.update();
if (M5Cardputer.Keyboard.isChange()) {
if (M5Cardputer.Keyboard.isPressed()) {
M5Cardputer.Keyboard.updateKeysState();
auto& state = M5Cardputer.Keyboard.keysState();
// 处理新输入
for (char c : state.word) {
M5Cardputer.Display.print(c);
}
if (state.enter) M5Cardputer.Display.println();
} else {
// 所有按键已释放
M5Cardputer.Display.println("按键已释放");
}
}
}
isKeyPressed(c)
ASCII 字符 c 对应的按键当前被按下时返回 true。
| 参数 | 类型 | 说明 |
|---|---|---|
c |
char |
要检查的 ASCII 字符 |
void loop() {
M5Cardputer.update();
// WASD 移动
int x = 100, y = 100;
if (M5Cardputer.Keyboard.isKeyPressed('w')) y--;
if (M5Cardputer.Keyboard.isKeyPressed('s')) y++;
if (M5Cardputer.Keyboard.isKeyPressed('a')) x--;
if (M5Cardputer.Keyboard.isKeyPressed('d')) x++;
M5Cardputer.Display.fillRect(x, y, 8, 8, TFT_GREEN);
// 退出快捷键
if (M5Cardputer.Keyboard.isKeyPressed('q')) {
M5Cardputer.Display.println("正在退出...");
}
}
getKey(keyCoor)
返回指定物理按键位置的 ASCII 字符,自动应用当前 Shift/Ctrl/Caps Lock 状态。
| 参数 | 类型 | 说明 |
|---|---|---|
keyCoor |
Point2D_t |
按键坐标 {x, y} |
| 返回值 | 说明 |
|---|---|
uint8_t |
ASCII 字符,无效或缺失时返回 0 |
void loop() {
M5Cardputer.update();
// 读取位置 (5, 1) 是什么字符
Point2D_t keyPos = {5, 1}; // 't' 键
uint8_t ch = M5Cardputer.Keyboard.getKey(keyPos);
M5Cardputer.Display.printf("按键(5,1) = '%c'\n", ch);
}
getKeyValue(keyCoor)
返回指定坐标的原始 KeyValue_t {value_first, value_second},不应用修饰键。
Point2D_t keyPos = {1, 0}; // '1' 键
KeyValue_t kv = M5Cardputer.Keyboard.getKeyValue(keyPos);
// kv.value_first = '1'
// kv.value_second = '!'
M5Cardputer.Display.printf("'%c' / '%c'\n", kv.value_first, kv.value_second);
updateKeysState()
扫描所有按下的按键并填充 KeysState 缓冲区。采用两遍算法:
- 第一遍: 识别修饰键(Fn、Opt、Ctrl、Shift、Alt)
- 第二遍: 以正确的修饰键状态处理所有其他按键(可打印字符、Tab、Enter、Backspace、Space)
填充 word、hid_keys、modifier_keys 以及所有布尔标志。在读取 keysState() 之前调用。
keysState()
返回当前 KeysState 缓冲区的引用。缓冲区由 updateKeysState() 填充。
void loop() {
M5Cardputer.update();
if (M5Cardputer.Keyboard.isChange() && M5Cardputer.Keyboard.isPressed()) {
M5Cardputer.Keyboard.updateKeysState();
auto& state = M5Cardputer.Keyboard.keysState();
// 显示按下的字符
M5Cardputer.Display.setCursor(0, 0);
M5Cardputer.Display.print("输入: ");
for (char c : state.word) {
M5Cardputer.Display.print(c);
}
// 显示当前修饰键
M5Cardputer.Display.setCursor(0, 20);
if (state.shift) M5Cardputer.Display.print("SHIFT ");
if (state.ctrl) M5Cardputer.Display.print("CTRL ");
if (state.alt) M5Cardputer.Display.print("ALT ");
if (state.fn) M5Cardputer.Display.print("FN ");
}
}
capslocked() / setCapsLocked()
软件管理的大写锁定状态。启用后 getKey() 返回大写字符。不是 硬件 LED — 应用程序自行管理视觉指示。
// 按 Tab 切换大写锁定
void loop() {
M5Cardputer.update();
if (M5Cardputer.Keyboard.isChange() && M5Cardputer.Keyboard.isPressed()) {
M5Cardputer.Keyboard.updateKeysState();
auto& state = M5Cardputer.Keyboard.keysState();
if (state.tab) {
bool caps = M5Cardputer.Keyboard.capslocked();
M5Cardputer.Keyboard.setCapsLocked(!caps);
M5Cardputer.Display.print(caps ? "大写: 关\n" : "大写: 开\n");
}
}
}
按键布局
物理键盘为 4 行 × 14 列 矩阵,存储在 _key_value_map[4][14] 中。每个位置有 value_first(正常)和 value_second(Shift)。
第0行: ` 1 2 3 4 5 6 7 8 9 0 - = BKSP
第1行: TAB q w e r t y u i o p [ ] \
第2行: FN SHIFT a s d f g h j k l ; ' ENTER
第3行: CTRL OPT ALT z x c v b n m , . / SPACE
坐标: Point2D_t {列, 行}。例如 a 键位于 {2, 2},ENTER 键位于 {13, 2}。
键码常量
定义于 utility/Keyboard/Keyboard_def.h。用于 USB HID 键盘模拟。
修饰键
| 常量 | 值 | USB HID | 说明 |
|---|---|---|---|
KEY_LEFT_CTRL |
0x80 |
0xE0 |
左 Ctrl |
KEY_LEFT_SHIFT |
0x81 |
0xE1 |
左 Shift |
KEY_LEFT_ALT |
0x82 |
0xE2 |
左 Alt |
KEY_FN |
0xFF |
— | Fn(M5Cardputer 特有) |
KEY_OPT |
0x00 |
— | Opt(M5Cardputer 特有) |
特殊按键
| 常量 | 值 | USB HID | 说明 |
|---|---|---|---|
KEY_BACKSPACE |
0x2A |
0x2A |
退格 |
KEY_TAB |
0x2B |
0x2B |
Tab |
KEY_ENTER |
0x28 |
0x28 |
回车 |
SHIFT 标志
当 _kb_asciimap[] 中某键码设置了此位,表示该键需要 Shift。
_kb_asciimap[128]
将 ASCII 字符映射到 USB HID 键码。内部用于填充 KeysState.hid_keys。完整表格见 src/utility/Keyboard/Keyboard_def.h。
完整使用模式
#include <M5Cardputer.h>
void setup() {
auto cfg = M5.config();
M5Cardputer.begin(cfg, true);
M5Cardputer.Display.setRotation(1);
}
void loop() {
M5Cardputer.update();
// 消抖:仅在状态变化时处理
if (M5Cardputer.Keyboard.isChange()) {
if (M5Cardputer.Keyboard.isPressed()) {
M5Cardputer.Keyboard.updateKeysState();
auto& state = M5Cardputer.Keyboard.keysState();
// 打印字符
for (char c : state.word) {
M5Cardputer.Display.print(c);
}
if (state.enter) M5Cardputer.Display.println();
if (state.del) {
// 在此实现退格逻辑
}
}
}
// 轮询:按住期间持续触发
if (M5Cardputer.Keyboard.isKeyPressed('r')) {
M5Cardputer.Display.fillScreen(TFT_RED);
}
}