feat: 添加HID传输管理和旋转编码器支持

添加了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事件系统。
This commit is contained in:
2026-03-20 11:04:48 +08:00
parent 2a389ef19b
commit 579dc35a36
14 changed files with 698 additions and 76 deletions

View File

@@ -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
)

View File

@@ -74,3 +74,7 @@
&usbd {
status = "okay";
};
qdec: &qdec {
status = "okay";
};

View File

@@ -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

View File

@@ -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));

View File

@@ -0,0 +1,28 @@
#ifndef HID_TX_DONE_EVENT_H__
#define HID_TX_DONE_EVENT_H__
#include <stdbool.h>
#include <stdint.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
#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__ */

43
src/events/hid_tx_event.c Normal file
View File

@@ -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));

48
src/events/hid_tx_event.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef HID_TX_EVENT_H__
#define HID_TX_EVENT_H__
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
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__ */

View File

@@ -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));

View File

@@ -0,0 +1,36 @@
#ifndef QDEC_STEP_EVENT_H__
#define QDEC_STEP_EVENT_H__
#include <stdint.h>
#include <app_event_manager.h>
#include <app_event_manager_profiler_tracer.h>
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__ */

View File

@@ -8,9 +8,9 @@
#include <caf/events/ble_common_event.h>
#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);

View File

@@ -0,0 +1,204 @@
#include <string.h>
#include <app_event_manager.h>
#define MODULE hid_tx_manager
#include <caf/events/module_state_event.h>
#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 <zephyr/logging/log.h>
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);

View File

@@ -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 <zephyr/sys/util.h>
#include <zephyr/logging/log.h>
@@ -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);

155
src/modules/qdec_module.c Normal file
View File

@@ -0,0 +1,155 @@
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/kernel.h>
#include <app_event_manager.h>
#define MODULE qdec
#include <caf/events/module_state_event.h>
#include "qdec_step_event.h"
#include <zephyr/logging/log.h>
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);

View File

@@ -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);