feat(kbd): 添加CAF按钮模块支持

- 在CMakeLists.txt中添加button_map_module.c源文件和头文件目录
- 创建inc/buttons_def.h定义按钮矩阵引脚配置
- 配置prj.conf启用CAF按钮相关功能和参数
- 实现src/modules/button_map_module.c按钮映射逻辑
- 支持6x4键盘矩阵的按键事件处理和Linux输入键码转换
This commit is contained in:
2026-03-10 16:09:33 +08:00
parent cd3400a9ba
commit 3d9ce9168f
4 changed files with 114 additions and 1 deletions

View File

@@ -0,0 +1,69 @@
#include <app_event_manager.h>
#define MODULE button_map
#include <caf/events/module_state_event.h>
#include <caf/events/button_event.h>
#include <caf/key_id.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(MODULE);
/*
* keymap[row][col] 直接对齐板级 DTS 里的 MATRIX_KEY(row, col, key) 定义。
* 值为 Linux input key code-1 表示该矩阵位置未映射功能键。
*/
static const int16_t keymap[6][4] = {
/* row 0 */ { -1, -1, -1, INPUT_KEY_MUTE },
/* row 1 */ { INPUT_KEY_NUMLOCK, INPUT_KEY_KPSLASH, INPUT_KEY_KPASTERISK, INPUT_KEY_KPMINUS },
/* row 2 */ { INPUT_KEY_KP7, INPUT_KEY_KP8, INPUT_KEY_KP9, -1 },
/* row 3 */ { INPUT_KEY_KP4, INPUT_KEY_KP5, INPUT_KEY_KP6, INPUT_KEY_KPPLUS },
/* row 4 */ { INPUT_KEY_KP1, INPUT_KEY_KP2, INPUT_KEY_KP3, -1 },
/* row 5 */ { INPUT_KEY_KP0, INPUT_KEY_KPDOT, -1, INPUT_KEY_KPENTER },
};
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_button_event(aeh)) {
const struct button_event *event = cast_button_event(aeh);
uint8_t row = KEY_ROW(event->key_id);
uint8_t col = KEY_COL(event->key_id);
/*
* 防御性检查:若行列越界,说明 buttons_def 与实际扫描结果不一致,
* 记录错误以便尽快发现硬件矩阵定义或固件配置问题。
*/
if ((row >= ARRAY_SIZE(keymap)) || (col >= ARRAY_SIZE(keymap[0]))) {
LOG_ERR("Unknown key_id=0x%04x (row=%u, col=%u)", event->key_id, row, col);
return false;
}
int16_t code = keymap[row][col];
if (code < 0) {
LOG_INF("Button %s row=%u col=%u unmapped", event->pressed ? "down" : "up", row, col);
} else {
LOG_INF("Button %s row=%u col=%u keycode=%d", event->pressed ? "down" : "up", row, col, code);
}
return false;
}
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
module_set_state(MODULE_STATE_READY);
}
return false;
}
/* 未处理但已订阅的事件类型不应进入这里。 */
__ASSERT_NO_MSG(false);
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, button_event);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);