From 579dc35a36bd017a677be2539e2b6ff063908d6e Mon Sep 17 00:00:00 2001 From: skiinder Date: Fri, 20 Mar 2026 11:04:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0HID=E4=BC=A0=E8=BE=93?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=92=8C=E6=97=8B=E8=BD=AC=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E5=99=A8=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了hid_tx_event和hid_tx_done_event事件类型,用于统一管理HID 数据传输,并在ble_hid_module和usb_hid_module中实现相应的处理逻辑。 新增qdec_module模块来处理旋转编码器输入,将旋转事件转换为步进事件, 并在keyboard_module中集成音量控制功能。 更新CMakeLists.txt以包含新的事件和模块文件,在app.overlay中启 用qdec设备,并在prj.conf中添加SENSOR配置。 BREAKING CHANGE: 将原有的hid_boot_event和hid_report_event替换 为统一的hid_tx_event事件系统。 --- CMakeLists.txt | 5 + app.overlay | 4 + prj.conf | 1 + src/events/hid_tx_done_event.c | 29 ++++ src/events/hid_tx_done_event.h | 28 ++++ src/events/hid_tx_event.c | 43 ++++++ src/events/hid_tx_event.h | 48 +++++++ src/events/qdec_step_event.c | 26 ++++ src/events/qdec_step_event.h | 36 +++++ src/modules/ble_hid_module.c | 63 ++++----- src/modules/hid_tx_manager_module.c | 204 ++++++++++++++++++++++++++++ src/modules/keyboard_module.c | 38 ++++++ src/modules/qdec_module.c | 155 +++++++++++++++++++++ src/modules/usb_hid_module.c | 94 +++++++------ 14 files changed, 698 insertions(+), 76 deletions(-) create mode 100644 src/events/hid_tx_done_event.c create mode 100644 src/events/hid_tx_done_event.h create mode 100644 src/events/hid_tx_event.c create mode 100644 src/events/hid_tx_event.h create mode 100644 src/events/qdec_step_event.c create mode 100644 src/events/qdec_step_event.h create mode 100644 src/modules/hid_tx_manager_module.c create mode 100644 src/modules/qdec_module.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9abceb5..24f9170 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,16 +22,21 @@ target_sources(app PRIVATE src/events/hid_boot_event.c src/events/hid_protocol_event.c src/events/hid_report_event.c + src/events/hid_tx_done_event.c + src/events/hid_tx_event.c src/events/keyboard_led_event.c src/events/mode_event.c + src/events/qdec_step_event.c src/modules/battery_module.c src/modules/ble_adv_ctrl_module.c src/modules/ble_battery_module.c src/modules/ble_bond_module.c src/modules/ble_slot_ctrl_module.c + src/modules/hid_tx_manager_module.c src/modules/keyboard_module.c src/modules/led_state_module.c src/modules/mode_switch_module.c + src/modules/qdec_module.c src/modules/usb_hid_module.c src/modules/ble_hid_module.c ) diff --git a/app.overlay b/app.overlay index 212a8e4..7b57426 100644 --- a/app.overlay +++ b/app.overlay @@ -74,3 +74,7 @@ &usbd { status = "okay"; }; + +qdec: &qdec { + status = "okay"; +}; diff --git a/prj.conf b/prj.conf index 1bac018..a89b07b 100644 --- a/prj.conf +++ b/prj.conf @@ -77,5 +77,6 @@ CONFIG_CAF_BUTTONS_DEBOUNCE_INTERVAL=10 CONFIG_ADC=y CONFIG_I2C=y CONFIG_IP5305=y +CONFIG_SENSOR=y CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096 diff --git a/src/events/hid_tx_done_event.c b/src/events/hid_tx_done_event.c new file mode 100644 index 0000000..1554302 --- /dev/null +++ b/src/events/hid_tx_done_event.c @@ -0,0 +1,29 @@ +#include "hid_tx_done_event.h" + +static void log_hid_tx_done_event(const struct app_event_header *aeh) +{ + const struct hid_tx_done_event *event = cast_hid_tx_done_event(aeh); + + APP_EVENT_MANAGER_LOG(aeh, "kind=%u success=%u", + event->kind, event->success); +} + +static void profile_hid_tx_done_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct hid_tx_done_event *event = cast_hid_tx_done_event(aeh); + + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->kind); + nrf_profiler_log_encode_uint8(buf, event->success ? 1U : 0U); +} + +APP_EVENT_INFO_DEFINE(hid_tx_done_event, + ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8), + ENCODE("kind", "success"), + profile_hid_tx_done_event); + +APP_EVENT_TYPE_DEFINE(hid_tx_done_event, + log_hid_tx_done_event, + &hid_tx_done_event_info, + APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/hid_tx_done_event.h b/src/events/hid_tx_done_event.h new file mode 100644 index 0000000..e2f069f --- /dev/null +++ b/src/events/hid_tx_done_event.h @@ -0,0 +1,28 @@ +#ifndef HID_TX_DONE_EVENT_H__ +#define HID_TX_DONE_EVENT_H__ + +#include +#include + +#include +#include +#include "hid_tx_event.h" + +struct hid_tx_done_event { + struct app_event_header header; + enum hid_tx_kind kind; + bool success; +}; + +APP_EVENT_TYPE_DECLARE(hid_tx_done_event); + +static inline void hid_tx_done_event_submit(enum hid_tx_kind kind, bool success) +{ + struct hid_tx_done_event *event = new_hid_tx_done_event(); + + event->kind = kind; + event->success = success; + APP_EVENT_SUBMIT(event); +} + +#endif /* HID_TX_DONE_EVENT_H__ */ diff --git a/src/events/hid_tx_event.c b/src/events/hid_tx_event.c new file mode 100644 index 0000000..36f81e7 --- /dev/null +++ b/src/events/hid_tx_event.c @@ -0,0 +1,43 @@ +#include "hid_tx_event.h" + +static void log_hid_tx_event(const struct app_event_header *aeh) +{ + const struct hid_tx_event *event = cast_hid_tx_event(aeh); + uint8_t report_id = 0x00; + uint16_t payload_len = event->dyndata.size; + + if ((event->kind == HID_TX_KIND_REPORT) && (event->dyndata.size >= 1U)) { + report_id = event->dyndata.data[0]; + payload_len = event->dyndata.size - 1U; + } + + APP_EVENT_MANAGER_LOG(aeh, "kind=%u report_id=0x%02x payload_len=%u", + event->kind, report_id, payload_len); +} + +static void profile_hid_tx_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct hid_tx_event *event = cast_hid_tx_event(aeh); + uint8_t report_id = 0x00; + + if ((event->kind == HID_TX_KIND_REPORT) && (event->dyndata.size >= 1U)) { + report_id = event->dyndata.data[0]; + } + + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->kind); + nrf_profiler_log_encode_uint8(buf, report_id); + nrf_profiler_log_encode_uint16(buf, event->dyndata.size); +} + +APP_EVENT_INFO_DEFINE(hid_tx_event, + ENCODE(NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U8, + NRF_PROFILER_ARG_U16), + ENCODE("kind", "report_id", "len"), + profile_hid_tx_event); + +APP_EVENT_TYPE_DEFINE(hid_tx_event, + log_hid_tx_event, + &hid_tx_event_info, + APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/hid_tx_event.h b/src/events/hid_tx_event.h new file mode 100644 index 0000000..51e18ce --- /dev/null +++ b/src/events/hid_tx_event.h @@ -0,0 +1,48 @@ +#ifndef HID_TX_EVENT_H__ +#define HID_TX_EVENT_H__ + +#include +#include +#include + +#include +#include + +enum hid_tx_kind { + HID_TX_KIND_BOOT = 0, + HID_TX_KIND_REPORT, +}; + +struct hid_tx_event { + struct app_event_header header; + enum hid_tx_kind kind; + struct event_dyndata dyndata; +}; + +APP_EVENT_TYPE_DYNDATA_DECLARE(hid_tx_event); + +static inline void hid_tx_event_submit(enum hid_tx_kind kind, + const uint8_t *data, + size_t size) +{ + struct hid_tx_event *event = new_hid_tx_event(size); + + event->kind = kind; + if ((size > 0U) && (data != NULL)) { + memcpy(event->dyndata.data, data, size); + } + + APP_EVENT_SUBMIT(event); +} + +static inline const uint8_t *hid_tx_event_get_data(const struct hid_tx_event *event) +{ + return event->dyndata.data; +} + +static inline size_t hid_tx_event_get_size(const struct hid_tx_event *event) +{ + return event->dyndata.size; +} + +#endif /* HID_TX_EVENT_H__ */ diff --git a/src/events/qdec_step_event.c b/src/events/qdec_step_event.c new file mode 100644 index 0000000..9a342d7 --- /dev/null +++ b/src/events/qdec_step_event.c @@ -0,0 +1,26 @@ +#include "qdec_step_event.h" + +static void log_qdec_step_event(const struct app_event_header *aeh) +{ + const struct qdec_step_event *event = cast_qdec_step_event(aeh); + + APP_EVENT_MANAGER_LOG(aeh, "step=%d", event->step); +} + +static void profile_qdec_step_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct qdec_step_event *event = cast_qdec_step_event(aeh); + + nrf_profiler_log_encode_int8(buf, event->step); +} + +APP_EVENT_INFO_DEFINE(qdec_step_event, + ENCODE(NRF_PROFILER_ARG_S8), + ENCODE("step"), + profile_qdec_step_event); + +APP_EVENT_TYPE_DEFINE(qdec_step_event, + log_qdec_step_event, + &qdec_step_event_info, + APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/qdec_step_event.h b/src/events/qdec_step_event.h new file mode 100644 index 0000000..6c6a3c6 --- /dev/null +++ b/src/events/qdec_step_event.h @@ -0,0 +1,36 @@ +#ifndef QDEC_STEP_EVENT_H__ +#define QDEC_STEP_EVENT_H__ + +#include + +#include +#include + +struct qdec_step_event { + struct app_event_header header; + int8_t step; +}; + +APP_EVENT_TYPE_DECLARE(qdec_step_event); + +static inline void qdec_step_event_submit(int8_t step) +{ + struct qdec_step_event *event; + + __ASSERT((step == 1) || (step == -1), "qdec step event must be +/-1"); + + if (step == 0) { + return; + } + + event = new_qdec_step_event(); + event->step = step; + APP_EVENT_SUBMIT(event); +} + +static inline int8_t qdec_step_event_get_step(const struct qdec_step_event *event) +{ + return event->step; +} + +#endif /* QDEC_STEP_EVENT_H__ */ diff --git a/src/modules/ble_hid_module.c b/src/modules/ble_hid_module.c index 5a45ccd..70b1325 100644 --- a/src/modules/ble_hid_module.c +++ b/src/modules/ble_hid_module.c @@ -8,9 +8,9 @@ #include #include "hid_protocol_event.h" -#include "hid_boot_event.h" -#include "hid_report_event.h" #include "hid_report_descriptor.h" +#include "hid_tx_done_event.h" +#include "hid_tx_event.h" #include "keyboard_led_event.h" #include "mode_event.h" @@ -211,37 +211,35 @@ static void handle_ble_peer_event(const struct ble_peer_event *event) } } -static bool handle_hid_boot_event(const struct hid_boot_event *event) +static bool handle_hid_tx_event(const struct hid_tx_event *event) { if (!ble_hid_is_active()) { return false; } - if (!ble_hid_is_boot_mode()) { - return false; - } + if (event->kind == HID_TX_KIND_BOOT) { + const uint8_t *payload = hid_tx_event_get_data(event); + size_t payload_len = hid_tx_event_get_size(event); + int err; - const uint8_t *payload = hid_boot_event_get_data(event); - size_t payload_len = hid_boot_event_get_size(event); + if (!ble_hid_is_boot_mode()) { + return false; + } - if (payload_len != BOOT_KEYBOARD_REPORT_LEN) { - LOG_WRN("Invalid boot keyboard payload len=%u", payload_len); - return false; - } + if (payload_len != BOOT_KEYBOARD_REPORT_LEN) { + LOG_WRN("Invalid boot keyboard payload len=%u", payload_len); + hid_tx_done_event_submit(HID_TX_KIND_BOOT, false); + return false; + } - int err = bt_hids_boot_kb_inp_rep_send(&hids_obj, ble_hid.link.conn, - payload, payload_len, NULL); + err = bt_hids_boot_kb_inp_rep_send(&hids_obj, ble_hid.link.conn, + payload, payload_len, NULL); - if (err) { - LOG_WRN("BLE HID boot send failed err=%d", err); - } + if (err) { + LOG_WRN("BLE HID boot send failed err=%d", err); + } - return false; -} - -static bool handle_hid_report_event(const struct hid_report_event *event) -{ - if (!ble_hid_is_active()) { + hid_tx_done_event_submit(HID_TX_KIND_BOOT, (err == 0)); return false; } @@ -249,13 +247,14 @@ static bool handle_hid_report_event(const struct hid_report_event *event) return false; } - const uint8_t *data = hid_report_event_get_data(event); - size_t data_len = hid_report_event_get_size(event); + const uint8_t *data = hid_tx_event_get_data(event); + size_t data_len = hid_tx_event_get_size(event); uint8_t report_id; const uint8_t *payload; size_t payload_len; if (data_len < 1U) { + hid_tx_done_event_submit(HID_TX_KIND_REPORT, false); return false; } @@ -270,11 +269,13 @@ static bool handle_hid_report_event(const struct hid_report_event *event) } else if (report_id == REPORT_ID_CONSUMER) { rep_index = 1U; } else { + hid_tx_done_event_submit(HID_TX_KIND_REPORT, false); return false; } if (payload_len > UINT8_MAX) { LOG_WRN("Payload too large=%u", payload_len); + hid_tx_done_event_submit(HID_TX_KIND_REPORT, false); return false; } @@ -285,6 +286,7 @@ static bool handle_hid_report_event(const struct hid_report_event *event) LOG_WRN("BLE HID send failed report=0x%02x err=%d", report_id, err); } + hid_tx_done_event_submit(HID_TX_KIND_REPORT, (err == 0)); return false; } @@ -321,12 +323,8 @@ static bool app_event_handler(const struct app_event_header *aeh) return false; } - if (is_hid_report_event(aeh)) { - return handle_hid_report_event(cast_hid_report_event(aeh)); - } - - if (is_hid_boot_event(aeh)) { - return handle_hid_boot_event(cast_hid_boot_event(aeh)); + if (is_hid_tx_event(aeh)) { + return handle_hid_tx_event(cast_hid_tx_event(aeh)); } __ASSERT_NO_MSG(false); @@ -337,5 +335,4 @@ APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE_EARLY(MODULE, module_state_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, ble_peer_event); APP_EVENT_SUBSCRIBE(MODULE, mode_event); -APP_EVENT_SUBSCRIBE(MODULE, hid_boot_event); -APP_EVENT_SUBSCRIBE(MODULE, hid_report_event); +APP_EVENT_SUBSCRIBE(MODULE, hid_tx_event); diff --git a/src/modules/hid_tx_manager_module.c b/src/modules/hid_tx_manager_module.c new file mode 100644 index 0000000..7dbb2cb --- /dev/null +++ b/src/modules/hid_tx_manager_module.c @@ -0,0 +1,204 @@ +#include + +#include + +#define MODULE hid_tx_manager +#include + +#include "hid_boot_event.h" +#include "hid_report_event.h" +#include "hid_tx_done_event.h" +#include "hid_tx_event.h" +#include "mode_event.h" + +#include +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define HID_TX_QUEUE_SIZE 16 +#define HID_TX_MAX_DATA 32 + +struct hid_tx_item { + enum hid_tx_kind kind; + size_t len; + uint8_t data[HID_TX_MAX_DATA]; +}; + +struct hid_tx_ctx { + struct hid_tx_item queue[HID_TX_QUEUE_SIZE]; + uint8_t head; + uint8_t tail; + uint8_t count; + bool initialized; + bool in_flight; + mode_type_t active_mode; + enum hid_tx_kind inflight_kind; +}; + +static struct hid_tx_ctx tx = { + .active_mode = MODE_TYPE_COUNT, +}; + +static bool hid_tx_queue_push(enum hid_tx_kind kind, const uint8_t *data, size_t len) +{ + struct hid_tx_item *item; + + if (len > HID_TX_MAX_DATA) { + LOG_WRN("Drop HID tx kind=%u len=%u: too large", kind, len); + return false; + } + + if (tx.count >= HID_TX_QUEUE_SIZE) { + LOG_WRN("Drop HID tx kind=%u len=%u: queue full", kind, len); + return false; + } + + item = &tx.queue[tx.tail]; + item->kind = kind; + item->len = len; + if ((len > 0U) && (data != NULL)) { + memcpy(item->data, data, len); + } + + tx.tail = (tx.tail + 1U) % HID_TX_QUEUE_SIZE; + tx.count++; + return true; +} + +static struct hid_tx_item *hid_tx_queue_front(void) +{ + if (tx.count == 0U) { + return NULL; + } + + return &tx.queue[tx.head]; +} + +static void hid_tx_queue_pop(void) +{ + __ASSERT_NO_MSG(tx.count > 0U); + tx.head = (tx.head + 1U) % HID_TX_QUEUE_SIZE; + tx.count--; +} + +static void dispatch_next_if_possible(void) +{ + struct hid_tx_item *item; + + if (!tx.initialized || tx.in_flight) { + return; + } + + item = hid_tx_queue_front(); + if (item == NULL) { + return; + } + + if ((tx.active_mode != MODE_TYPE_USB) && (tx.active_mode != MODE_TYPE_BLE)) { + LOG_WRN("Drop HID tx kind=%u: unsupported mode=%u", + item->kind, tx.active_mode); + hid_tx_queue_pop(); + dispatch_next_if_possible(); + return; + } + + tx.in_flight = true; + tx.inflight_kind = item->kind; + hid_tx_event_submit(item->kind, item->data, item->len); +} + +static bool handle_module_state_event(const struct module_state_event *event) +{ + if (!check_state(event, MODULE_ID(main), MODULE_STATE_READY)) { + return false; + } + + __ASSERT_NO_MSG(!tx.initialized); + tx.initialized = true; + module_set_state(MODULE_STATE_READY); + dispatch_next_if_possible(); + + return false; +} + +static bool handle_mode_event(const struct mode_event *event) +{ + tx.active_mode = event->mode_type; + dispatch_next_if_possible(); + return false; +} + +static bool handle_hid_boot_request_event(const struct hid_boot_event *event) +{ + (void)hid_tx_queue_push(HID_TX_KIND_BOOT, + hid_boot_event_get_data(event), + hid_boot_event_get_size(event)); + dispatch_next_if_possible(); + return true; +} + +static bool handle_hid_report_request_event(const struct hid_report_event *event) +{ + (void)hid_tx_queue_push(HID_TX_KIND_REPORT, + hid_report_event_get_data(event), + hid_report_event_get_size(event)); + dispatch_next_if_possible(); + return true; +} + +static bool handle_hid_tx_done_event(const struct hid_tx_done_event *event) +{ + struct hid_tx_item *item; + + if (!tx.in_flight) { + return false; + } + + if (event->kind != tx.inflight_kind) { + return false; + } + + item = hid_tx_queue_front(); + if (item == NULL) { + tx.in_flight = false; + return false; + } + + hid_tx_queue_pop(); + tx.in_flight = false; + dispatch_next_if_possible(); + + return false; +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_module_state_event(aeh)) { + return handle_module_state_event(cast_module_state_event(aeh)); + } + + if (is_mode_event(aeh)) { + return handle_mode_event(cast_mode_event(aeh)); + } + + if (is_hid_boot_event(aeh)) { + return handle_hid_boot_request_event(cast_hid_boot_event(aeh)); + } + + if (is_hid_report_event(aeh)) { + return handle_hid_report_request_event(cast_hid_report_event(aeh)); + } + + if (is_hid_tx_done_event(aeh)) { + return handle_hid_tx_done_event(cast_hid_tx_done_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_EARLY(MODULE, hid_boot_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_report_event); +APP_EVENT_SUBSCRIBE(MODULE, hid_tx_done_event); diff --git a/src/modules/keyboard_module.c b/src/modules/keyboard_module.c index e2dd419..8a8f46b 100644 --- a/src/modules/keyboard_module.c +++ b/src/modules/keyboard_module.c @@ -13,6 +13,7 @@ #include "hid_boot_event.h" #include "hid_protocol_event.h" #include "hid_report_event.h" +#include "qdec_step_event.h" #include #include @@ -241,6 +242,23 @@ static void submit_consumer_report_payload(void) submit_hid_report(HID_PROTO_REPORT, REPORT_ID_CONSUMER, payload, sizeof(payload)); } +static void submit_consumer_click_usage(uint16_t usage_id) +{ + uint8_t payload[CONSUMER_PAYLOAD]; + + if (active_protocol_get() == HID_PROTO_BOOT) { + return; + } + + payload[0] = usage_id & 0xFF; + payload[1] = (usage_id >> 8) & 0xFF; + submit_hid_report(HID_PROTO_REPORT, REPORT_ID_CONSUMER, payload, sizeof(payload)); + + payload[0] = 0U; + payload[1] = 0U; + submit_hid_report(HID_PROTO_REPORT, REPORT_ID_CONSUMER, payload, sizeof(payload)); +} + /* * 处理键盘类 usage: * - 仅在按键状态实际变化时提交报告,避免无效重复上报。 @@ -311,6 +329,21 @@ static bool handle_hid_protocol_event(const struct hid_protocol_event *event) return false; } +static bool handle_qdec_step_event(const struct qdec_step_event *event) +{ + int8_t step = qdec_step_event_get_step(event); + uint16_t usage_id; + + if (step == 0) { + return false; + } + + usage_id = (step > 0) ? 0x00E9U : 0x00EAU; + submit_consumer_click_usage(usage_id); + + return false; +} + /* 模块总事件分发入口。 */ static bool app_event_handler(const struct app_event_header *aeh) { @@ -322,6 +355,10 @@ static bool app_event_handler(const struct app_event_header *aeh) return handle_hid_protocol_event(cast_hid_protocol_event(aeh)); } + if (is_qdec_step_event(aeh)) { + return handle_qdec_step_event(cast_qdec_step_event(aeh)); + } + if (is_module_state_event(aeh)) { const struct module_state_event *event = cast_module_state_event(aeh); @@ -341,4 +378,5 @@ static bool app_event_handler(const struct app_event_header *aeh) APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, button_event); APP_EVENT_SUBSCRIBE(MODULE, hid_protocol_event); +APP_EVENT_SUBSCRIBE(MODULE, qdec_step_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); diff --git a/src/modules/qdec_module.c b/src/modules/qdec_module.c new file mode 100644 index 0000000..1d5ae1d --- /dev/null +++ b/src/modules/qdec_module.c @@ -0,0 +1,155 @@ +#include + +#include +#include +#include + +#include + +#define MODULE qdec +#include + +#include "qdec_step_event.h" + +#include +LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF); + +#define QDEC_DEG_PER_STEP_EVENT 36 +#define QDEC_EVENT_INTERVAL_MS 20 + +BUILD_ASSERT(QDEC_DEG_PER_STEP_EVENT > 0, "QDEC_DEG_PER_STEP_EVENT must be positive"); +BUILD_ASSERT(QDEC_EVENT_INTERVAL_MS > 0, "QDEC_EVENT_INTERVAL_MS must be positive"); + +struct qdec_ctx { + const struct device *dev; + struct sensor_trigger trigger; + struct k_work_delayable emit_work; + int32_t acc_deg; + bool emit_scheduled; +}; + +static struct qdec_ctx qdec = { + .dev = DEVICE_DT_GET(DT_NODELABEL(qdec)), + .trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ROTATION, + }, +}; + +static void schedule_emit_work(void) +{ + if (qdec.emit_scheduled) { + return; + } + + qdec.emit_scheduled = true; + (void)k_work_reschedule(&qdec.emit_work, K_MSEC(QDEC_EVENT_INTERVAL_MS)); +} + +static void qdec_emit_work_handler(struct k_work *work) +{ + int8_t step_delta; + + ARG_UNUSED(work); + + qdec.emit_scheduled = false; + + if ((qdec.acc_deg < QDEC_DEG_PER_STEP_EVENT) && + (qdec.acc_deg > -QDEC_DEG_PER_STEP_EVENT)) { + return; + } + + if (qdec.acc_deg > 0) { + step_delta = 1; + qdec.acc_deg -= QDEC_DEG_PER_STEP_EVENT; + } else { + step_delta = -1; + qdec.acc_deg += QDEC_DEG_PER_STEP_EVENT; + } + + qdec_step_event_submit(step_delta); + + if ((qdec.acc_deg >= QDEC_DEG_PER_STEP_EVENT) || + (qdec.acc_deg <= -QDEC_DEG_PER_STEP_EVENT)) { + schedule_emit_work(); + } +} + +static void qdec_data_handler(const struct device *dev, + const struct sensor_trigger *trigger) +{ + struct sensor_value rotation = {0}; + int err; + + ARG_UNUSED(trigger); + + err = sensor_sample_fetch_chan(dev, SENSOR_CHAN_ROTATION); + if (err) { + LOG_ERR("QDEC sample fetch failed: %d", err); + return; + } + + err = sensor_channel_get(dev, SENSOR_CHAN_ROTATION, &rotation); + if (err) { + LOG_ERR("QDEC channel get failed: %d", err); + return; + } + + qdec.acc_deg += rotation.val1; + LOG_DBG("QDEC rotation=%d acc_deg=%d", rotation.val1, qdec.acc_deg); + + if ((qdec.acc_deg >= QDEC_DEG_PER_STEP_EVENT) || + (qdec.acc_deg <= -QDEC_DEG_PER_STEP_EVENT)) { + schedule_emit_work(); + } +} + +static int qdec_init(void) +{ + int err; + + if (!device_is_ready(qdec.dev)) { + LOG_ERR("QDEC device not ready"); + return -ENODEV; + } + + qdec.acc_deg = 0; + qdec.emit_scheduled = false; + k_work_init_delayable(&qdec.emit_work, qdec_emit_work_handler); + + err = sensor_trigger_set(qdec.dev, &qdec.trigger, qdec_data_handler); + if (err) { + LOG_ERR("QDEC trigger set failed: %d", err); + return err; + } + + LOG_INF("QDEC initialized: %d deg/step, <=1 step per %d ms", + QDEC_DEG_PER_STEP_EVENT, QDEC_EVENT_INTERVAL_MS); + + return 0; +} + +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)) { + int err = qdec_init(); + + if (err) { + module_set_state(MODULE_STATE_ERROR); + } else { + 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, module_state_event); diff --git a/src/modules/usb_hid_module.c b/src/modules/usb_hid_module.c index 37e1c6c..5e128e2 100644 --- a/src/modules/usb_hid_module.c +++ b/src/modules/usb_hid_module.c @@ -15,7 +15,8 @@ #include "hid_report_descriptor.h" #include "hid_boot_event.h" #include "hid_protocol_event.h" -#include "hid_report_event.h" +#include "hid_tx_done_event.h" +#include "hid_tx_event.h" #include "keyboard_led_event.h" #include "mode_event.h" @@ -67,6 +68,11 @@ static struct usb_hid_ctx g_usb_hid = { .current_protocol = HID_PROTO_REPORT, }; +static void submit_usb_tx_done(enum hid_tx_kind kind, bool success) +{ + hid_tx_done_event_submit(kind, success); +} + USBD_DEVICE_DEFINE(new_kbd_usbd, DEVICE_DT_GET(DT_NODELABEL(usbd)), APP_USB_VID, APP_USB_PID); @@ -242,6 +248,9 @@ static void hid_stub_input_done(const struct device *dev, const uint8_t *report) if (iface) { iface->in_flight = false; + submit_usb_tx_done((dev == g_usb_hid.boot.dev) ? + HID_TX_KIND_BOOT : HID_TX_KIND_REPORT, + true); return; } @@ -596,73 +605,76 @@ static bool handle_wake_up_event(void) return false; } -static bool handle_hid_boot_event(const struct hid_boot_event *event) +static bool handle_hid_tx_event(const struct hid_tx_event *event) { if (!g_usb_hid.policy.usb_mode_selected || !usb_hid_stack_is_active()) { return false; } - if (g_usb_hid.current_protocol != HID_PROTO_BOOT) { + if (event->kind == HID_TX_KIND_BOOT) { + const uint8_t *payload = hid_tx_event_get_data(event); + size_t payload_len = hid_tx_event_get_size(event); + int err; + + if (g_usb_hid.current_protocol != HID_PROTO_BOOT) { + return false; + } + + if (!g_usb_hid.boot.iface_ready || !g_usb_hid.boot.dev) { + submit_usb_tx_done(HID_TX_KIND_BOOT, false); + return false; + } + if (g_usb_hid.boot.in_flight) { + LOG_WRN("Drop boot tx: previous report not sent"); + submit_usb_tx_done(HID_TX_KIND_BOOT, false); + return false; + } + + err = hid_device_submit_report(g_usb_hid.boot.dev, + payload_len, + payload); + if (err) { + LOG_WRN("USB boot report send failed err=%d", err); + submit_usb_tx_done(HID_TX_KIND_BOOT, false); + } else { + g_usb_hid.boot.in_flight = true; + } + return false; } - const uint8_t *payload = hid_boot_event_get_data(event); - size_t payload_len = hid_boot_event_get_size(event); - - if (!g_usb_hid.boot.iface_ready || !g_usb_hid.boot.dev) { - return false; - } - if (g_usb_hid.boot.in_flight) { - LOG_WRN("Drop boot report: previous report not sent"); - return false; - } - - int err = hid_device_submit_report(g_usb_hid.boot.dev, - payload_len, - payload); - if (err) { - LOG_WRN("USB boot report send failed err=%d", err); - } else { - g_usb_hid.boot.in_flight = true; - } - - return false; -} - -static bool handle_hid_report_event(const struct hid_report_event *event) -{ /* * USB 侧仅在 active 条件满足时发送: * - 当前 mode 为 USB; * - USB HID 栈已启用且对应接口 ready。 */ - if (!g_usb_hid.policy.usb_mode_selected || !usb_hid_stack_is_active()) { - return false; - } - if (g_usb_hid.current_protocol != HID_PROTO_REPORT) { return false; } - const uint8_t *data = hid_report_event_get_data(event); - size_t data_len = hid_report_event_get_size(event); + const uint8_t *data = hid_tx_event_get_data(event); + size_t data_len = hid_tx_event_get_size(event); uint8_t report_id; if (data_len < 1U) { + submit_usb_tx_done(HID_TX_KIND_REPORT, false); return false; } report_id = data[0]; if (!g_usb_hid.nkro.iface_ready || !g_usb_hid.nkro.dev) { + submit_usb_tx_done(HID_TX_KIND_REPORT, false); return false; } if ((report_id != REPORT_ID_KEYBOARD) && (report_id != REPORT_ID_CONSUMER)) { + submit_usb_tx_done(HID_TX_KIND_REPORT, false); return false; } if (g_usb_hid.nkro.in_flight) { - LOG_WRN("Drop report id=0x%02x: previous report not sent", report_id); + LOG_WRN("Drop tx report id=0x%02x: previous report not sent", report_id); + submit_usb_tx_done(HID_TX_KIND_REPORT, false); return false; } @@ -670,6 +682,7 @@ static bool handle_hid_report_event(const struct hid_report_event *event) int err = hid_device_submit_report(g_usb_hid.nkro.dev, data_len, data); if (err) { LOG_WRN("USB report send failed id=0x%02x err=%d", report_id, err); + submit_usb_tx_done(HID_TX_KIND_REPORT, false); } else { g_usb_hid.nkro.in_flight = true; } @@ -695,12 +708,8 @@ static bool app_event_handler(const struct app_event_header *aeh) return handle_wake_up_event(); } - if (is_hid_report_event(aeh)) { - return handle_hid_report_event(cast_hid_report_event(aeh)); - } - - if (is_hid_boot_event(aeh)) { - return handle_hid_boot_event(cast_hid_boot_event(aeh)); + if (is_hid_tx_event(aeh)) { + return handle_hid_tx_event(cast_hid_tx_event(aeh)); } __ASSERT_NO_MSG(false); @@ -712,5 +721,4 @@ APP_EVENT_SUBSCRIBE(MODULE, module_state_event); APP_EVENT_SUBSCRIBE(MODULE, mode_event); APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); APP_EVENT_SUBSCRIBE(MODULE, wake_up_event); -APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_boot_event); -APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_report_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, hid_tx_event);