diff --git a/CMakeLists.txt b/CMakeLists.txt index 59b556d..bec54ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,6 @@ project(new_kbd) zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc) zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/events) -zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/configuration/atguigu_mini_keyboard_nrf52840) target_compile_definitions(app PRIVATE APP_HID_KEYMAP_DEF_PATH=\"hid_keymap_def.h\" @@ -22,12 +21,14 @@ target_sources(app PRIVATE src/events/config_event.c src/events/hid_protocol_event.c src/events/hid_report_event.c + src/events/keyboard_led_state_event.c src/events/mode_event.c src/events/usb_hid_event.c src/modules/battery_module.c src/modules/ble_adv_ctrl_module.c src/modules/ble_bond_module.c src/modules/keyboard_module.c + src/modules/led_state_module.c src/modules/mode_switch_module.c src/modules/usb_hid_module.c src/modules/ble_hid_module.c diff --git a/app.overlay b/app.overlay index a552a65..212a8e4 100644 --- a/app.overlay +++ b/app.overlay @@ -53,6 +53,10 @@ status = "okay"; }; +&led_1 { + status = "okay"; +}; + /* 使能 SAADC,mode_switch_module 使用 channel 7 采样模式拨码电压。 */ &adc { status = "okay"; @@ -63,7 +67,6 @@ ip5305: pmic@75 { status = "okay"; - /* 试验项:调整 IP5305 KEY 保活周期,观察 I2C 失败窗口是否随周期移动。 */ keepalive-interval-ms = <10000>; }; }; diff --git a/configuration/atguigu_mini_keyboard_nrf52840/hid_keymap_def.h b/inc/hid_keymap_def.h similarity index 100% rename from configuration/atguigu_mini_keyboard_nrf52840/hid_keymap_def.h rename to inc/hid_keymap_def.h diff --git a/inc/led_state.h b/inc/led_state.h new file mode 100644 index 0000000..0ada0b8 --- /dev/null +++ b/inc/led_state.h @@ -0,0 +1,29 @@ +#ifndef NEW_KBD_LED_STATE_H__ +#define NEW_KBD_LED_STATE_H__ + +#include + +/* 模块内系统状态:只用于本项目的 LED 映射,不对外暴露协议语义。 */ +enum led_ble_state { + LED_BLE_STATE_OFF = 0, + LED_BLE_STATE_WAIT_RECONNECT, + LED_BLE_STATE_PAIRING, + LED_BLE_STATE_CONNECTED, + LED_BLE_STATE_COUNT, +}; + +enum led_num_lock_state { + LED_NUM_LOCK_STATE_OFF = 0, + LED_NUM_LOCK_STATE_ON, + LED_NUM_LOCK_STATE_COUNT, +}; + +enum led_id_new_kbd { + LED_ID_NUM_LOCK = 0, + LED_ID_BLE_STATE, + LED_ID_COUNT, +}; + +#define LED_UNAVAILABLE 0xFF + +#endif /* NEW_KBD_LED_STATE_H__ */ diff --git a/inc/led_state_def.h b/inc/led_state_def.h new file mode 100644 index 0000000..42732da --- /dev/null +++ b/inc/led_state_def.h @@ -0,0 +1,39 @@ +#include "led_state.h" +#include + +/* + * 该文件仅被 led_state_module.c 包含一次,用于定义: + * 1) 逻辑 LED 到 CAF LED 实例编号映射; + * 2) 每个逻辑状态对应的 LED 效果。 + */ +const struct {} led_state_def_include_once; + +/* + * CAF LED 实例编号来源于 DTS 中 status=okay 的 gpio-leds 顺序: + * - led_0 -> 0(Num Lock) + * - led_1 -> 1(BLE 状态) + */ +static const uint8_t led_map[LED_ID_COUNT] = { + [LED_ID_NUM_LOCK] = 0, + [LED_ID_BLE_STATE] = 1, +}; + +/* Num Lock 指示:灭=关闭,常亮=开启。 */ +static const struct led_effect led_num_lock_state_effect[LED_NUM_LOCK_STATE_COUNT] = { + [LED_NUM_LOCK_STATE_OFF] = LED_EFFECT_LED_OFF(), + [LED_NUM_LOCK_STATE_ON] = LED_EFFECT_LED_ON(LED_COLOR(255, 255, 255)), +}; + +/* + * BLE 指示灯策略: + * - OFF: 熄灭(USB 连接或 BLE 非活动模式) + * - WAIT_RECONNECT: 慢闪(1s toggle) + * - PAIRING: 快闪(0.5s toggle) + * - CONNECTED: 常亮 + */ +static const struct led_effect led_ble_state_effect[LED_BLE_STATE_COUNT] = { + [LED_BLE_STATE_OFF] = LED_EFFECT_LED_OFF(), + [LED_BLE_STATE_WAIT_RECONNECT] = LED_EFFECT_LED_BLINK(1000, LED_COLOR(255, 255, 255)), + [LED_BLE_STATE_PAIRING] = LED_EFFECT_LED_BLINK(500, LED_COLOR(255, 255, 255)), + [LED_BLE_STATE_CONNECTED] = LED_EFFECT_LED_ON(LED_COLOR(255, 255, 255)), +}; diff --git a/src/events/keyboard_led_state_event.c b/src/events/keyboard_led_state_event.c new file mode 100644 index 0000000..fcfac10 --- /dev/null +++ b/src/events/keyboard_led_state_event.c @@ -0,0 +1,31 @@ +#include "keyboard_led_state_event.h" + +static void log_keyboard_led_state_event(const struct app_event_header *aeh) +{ + const struct keyboard_led_state_event *event = + cast_keyboard_led_state_event(aeh); + + APP_EVENT_MANAGER_LOG(aeh, "mask=0x%02x num_lock=%u", + event->led_mask, event->num_lock); +} + +static void profile_keyboard_led_state_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct keyboard_led_state_event *event = + cast_keyboard_led_state_event(aeh); + + nrf_profiler_log_encode_uint8(buf, event->led_mask); + nrf_profiler_log_encode_uint8(buf, event->num_lock); +} + +APP_EVENT_INFO_DEFINE(keyboard_led_state_event, + ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8), + ENCODE("led_mask", "num_lock"), + profile_keyboard_led_state_event); + +APP_EVENT_TYPE_DEFINE(keyboard_led_state_event, + log_keyboard_led_state_event, + &keyboard_led_state_event_info, + APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/keyboard_led_state_event.h b/src/events/keyboard_led_state_event.h new file mode 100644 index 0000000..5fc5fa2 --- /dev/null +++ b/src/events/keyboard_led_state_event.h @@ -0,0 +1,24 @@ +#ifndef KEYBOARD_LED_STATE_EVENT_H__ +#define KEYBOARD_LED_STATE_EVENT_H__ + +#include +#include + +#include +#include + +/* + * 键盘 LED 状态事件: + * - 由 USB/BLE HID 接收主机输出报告后上报; + * - 当前仅消费 Num Lock 位,保留 led_mask 便于后续扩展 Caps/Scroll。 + */ +struct keyboard_led_state_event { + struct app_event_header header; + + uint8_t led_mask; + bool num_lock; +}; + +APP_EVENT_TYPE_DECLARE(keyboard_led_state_event); + +#endif /* KEYBOARD_LED_STATE_EVENT_H__ */ diff --git a/src/modules/ble_hid_module.c b/src/modules/ble_hid_module.c index 86aa6e3..db248b3 100644 --- a/src/modules/ble_hid_module.c +++ b/src/modules/ble_hid_module.c @@ -10,6 +10,7 @@ #include "hid_protocol_event.h" #include "hid_report_event.h" #include "hid_report_descriptor.h" +#include "keyboard_led_state_event.h" #include "mode_event.h" #include @@ -27,6 +28,8 @@ BT_HIDS_DEF(hids_obj, INPUT_REPORT_COUNT, OUTPUT_REPORT_COUNT, 0); static struct bt_conn *active_conn; static enum bt_hids_pm current_pm = BT_HIDS_PM_REPORT; static bool ble_mode_selected; +static bool num_lock_known; +static bool num_lock_on; static enum hid_protocol_type pm_to_protocol(enum bt_hids_pm pm) { @@ -42,6 +45,25 @@ static void publish_hid_protocol_event(enum hid_protocol_type protocol) APP_EVENT_SUBMIT(event); } +/* BLE 输出报告的 bit0 对应 Num Lock。仅在状态变化时上报,避免重复通知。 */ +static void publish_num_lock_state_from_led_mask(uint8_t led_mask) +{ + bool new_num_lock = (led_mask & BIT(0)) != 0U; + + if (num_lock_known && (num_lock_on == new_num_lock)) { + return; + } + + num_lock_known = true; + num_lock_on = new_num_lock; + + struct keyboard_led_state_event *event = new_keyboard_led_state_event(); + + event->led_mask = led_mask; + event->num_lock = new_num_lock; + APP_EVENT_SUBMIT(event); +} + static void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn) { ARG_UNUSED(conn); @@ -86,6 +108,7 @@ static void boot_keyboard_output_report_handler(struct bt_hids_rep *rep, return; } + publish_num_lock_state_from_led_mask(rep->data[0]); LOG_DBG("Boot KB out report 0x%02x", rep->data[0]); } @@ -99,6 +122,7 @@ static void keyboard_output_report_handler(struct bt_hids_rep *rep, return; } + publish_num_lock_state_from_led_mask(rep->data[0]); LOG_DBG("Report KB out report 0x%02x", rep->data[0]); } diff --git a/src/modules/led_state_module.c b/src/modules/led_state_module.c new file mode 100644 index 0000000..58bf563 --- /dev/null +++ b/src/modules/led_state_module.c @@ -0,0 +1,180 @@ +#include + +#include + +#define MODULE led_state +#include +#include +#include + +#include "keyboard_led_state_event.h" +#include "led_state.h" +#include "mode_event.h" +#include "led_state_def.h" + +#include +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +static uint8_t connected_peer_count; +static bool ble_mode_selected; +static bool peer_search_active; +static enum peer_operation peer_op = PEER_OPERATION_CANCEL; +static bool num_lock_on; + +/* 根据当前聚合上下文决定 BLE 状态灯的逻辑状态。 */ +static enum led_ble_state resolve_ble_led_state(void) +{ + if (!ble_mode_selected) + return LED_BLE_STATE_OFF; + + switch (peer_op) { + case PEER_OPERATION_SELECT: + case PEER_OPERATION_ERASE: + case PEER_OPERATION_ERASE_ADV: + return LED_BLE_STATE_PAIRING; + + case PEER_OPERATION_SELECTED: + case PEER_OPERATION_ERASE_ADV_CANCEL: + case PEER_OPERATION_ERASED: + case PEER_OPERATION_CANCEL: + case PEER_OPERATION_SCAN_REQUEST: + if (peer_search_active) + return LED_BLE_STATE_PAIRING; + + if (connected_peer_count > 0U) + return LED_BLE_STATE_CONNECTED; + + return LED_BLE_STATE_WAIT_RECONNECT; + + default: + __ASSERT_NO_MSG(false); + return LED_BLE_STATE_OFF; + } +} + +/* 发布 Num Lock 灯效。 */ +static void submit_num_lock_led(void) +{ + if (led_map[LED_ID_NUM_LOCK] == LED_UNAVAILABLE) + return; + + enum led_num_lock_state state = + num_lock_on ? LED_NUM_LOCK_STATE_ON : LED_NUM_LOCK_STATE_OFF; + struct led_event *event = new_led_event(); + + event->led_id = led_map[LED_ID_NUM_LOCK]; + event->led_effect = &led_num_lock_state_effect[state]; + APP_EVENT_SUBMIT(event); +} + +/* 发布 BLE 状态灯效。 */ +static void submit_ble_led(void) +{ + if (led_map[LED_ID_BLE_STATE] == LED_UNAVAILABLE) + return; + + enum led_ble_state state = resolve_ble_led_state(); + struct led_event *event = new_led_event(); + + event->led_id = led_map[LED_ID_BLE_STATE]; + event->led_effect = &led_ble_state_effect[state]; + APP_EVENT_SUBMIT(event); +} + +static bool handle_mode_event(const struct mode_event *event) +{ + ble_mode_selected = (event->mode_type == MODE_TYPE_BLE); + submit_ble_led(); + return false; +} + +static bool handle_ble_peer_event(const struct ble_peer_event *event) +{ + switch (event->state) { + case PEER_STATE_CONNECTED: + __ASSERT_NO_MSG(connected_peer_count < UINT8_MAX); + connected_peer_count++; + break; + + case PEER_STATE_DISCONNECTED: + __ASSERT_NO_MSG(connected_peer_count > 0U); + connected_peer_count--; + break; + + case PEER_STATE_SECURED: + case PEER_STATE_CONN_FAILED: + case PEER_STATE_DISCONNECTING: + break; + + default: + __ASSERT_NO_MSG(false); + break; + } + + submit_ble_led(); + return false; +} + +static bool handle_ble_peer_search_event(const struct ble_peer_search_event *event) +{ + peer_search_active = event->active; + submit_ble_led(); + return false; +} + +static bool handle_ble_peer_operation_event(const struct ble_peer_operation_event *event) +{ + peer_op = event->op; + submit_ble_led(); + return false; +} + +static bool handle_keyboard_led_state_event(const struct keyboard_led_state_event *event) +{ + num_lock_on = event->num_lock; + submit_num_lock_led(); + return false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + 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)) { + submit_num_lock_led(); + submit_ble_led(); + module_set_state(MODULE_STATE_READY); + } + + return false; + } + + if (is_mode_event(aeh)) + return handle_mode_event(cast_mode_event(aeh)); + + if (is_keyboard_led_state_event(aeh)) + return handle_keyboard_led_state_event(cast_keyboard_led_state_event(aeh)); + + if (IS_ENABLED(CONFIG_CAF_BLE_COMMON_EVENTS) && is_ble_peer_event(aeh)) + return handle_ble_peer_event(cast_ble_peer_event(aeh)); + + if (IS_ENABLED(CONFIG_CAF_BLE_COMMON_EVENTS) && is_ble_peer_search_event(aeh)) + return handle_ble_peer_search_event(cast_ble_peer_search_event(aeh)); + + if (IS_ENABLED(CONFIG_CAF_BLE_COMMON_EVENTS) && is_ble_peer_operation_event(aeh)) + return handle_ble_peer_operation_event(cast_ble_peer_operation_event(aeh)); + + __ASSERT_NO_MSG(false); + return false; +} + +APP_EVENT_LISTENER(MODULE, app_event_handler); +APP_EVENT_SUBSCRIBE(MODULE, module_state_event); +APP_EVENT_SUBSCRIBE(MODULE, mode_event); +APP_EVENT_SUBSCRIBE(MODULE, keyboard_led_state_event); +#ifdef CONFIG_CAF_BLE_COMMON_EVENTS +APP_EVENT_SUBSCRIBE(MODULE, ble_peer_event); +APP_EVENT_SUBSCRIBE(MODULE, ble_peer_search_event); +APP_EVENT_SUBSCRIBE(MODULE, ble_peer_operation_event); +#endif diff --git a/src/modules/usb_hid_module.c b/src/modules/usb_hid_module.c index f1767c7..0aa122c 100644 --- a/src/modules/usb_hid_module.c +++ b/src/modules/usb_hid_module.c @@ -15,6 +15,7 @@ #include "hid_report_descriptor.h" #include "hid_protocol_event.h" #include "hid_report_event.h" +#include "keyboard_led_state_event.h" #include "mode_event.h" #include "usb_hid_event.h" @@ -55,6 +56,8 @@ struct usb_hid_ctx { enum usb_hid_usbd_state usbd_state; enum hid_protocol_type current_protocol; + bool num_lock_known; + bool num_lock_on; }; static struct usb_hid_ctx g_usb_hid = { @@ -85,6 +88,25 @@ static void publish_usb_hid_state(void) APP_EVENT_SUBMIT(event); } +/* 从主机输出报告同步 Num Lock 位,并在状态变化时发布事件。 */ +static void publish_num_lock_state_from_led_mask(uint8_t led_mask) +{ + bool new_num_lock = (led_mask & BIT(0)) != 0U; + + if (g_usb_hid.num_lock_known && (g_usb_hid.num_lock_on == new_num_lock)) { + return; + } + + g_usb_hid.num_lock_known = true; + g_usb_hid.num_lock_on = new_num_lock; + + struct keyboard_led_state_event *event = new_keyboard_led_state_event(); + + event->led_mask = led_mask; + event->num_lock = new_num_lock; + APP_EVENT_SUBMIT(event); +} + static void recompute_hid_state(void) { /* 兼容现有调用点:对外仅发布 enable + usbd 状态。 */ @@ -118,8 +140,11 @@ static int hid_stub_set_report(const struct device *dev, ARG_UNUSED(dev); ARG_UNUSED(type); ARG_UNUSED(id); - ARG_UNUSED(len); - ARG_UNUSED(buf); + + if ((len > 0U) && (buf != NULL)) { + publish_num_lock_state_from_led_mask(buf[0]); + } + return 0; } @@ -188,8 +213,10 @@ static void hid_stub_input_done(const struct device *dev, const uint8_t *report) static void hid_stub_output_report(const struct device *dev, uint16_t len, const uint8_t *buf) { ARG_UNUSED(dev); - ARG_UNUSED(len); - ARG_UNUSED(buf); + + if ((len > 0U) && (buf != NULL)) { + publish_num_lock_state_from_led_mask(buf[0]); + } } static void hid_iface_ready_cb(const struct device *dev, bool ready)