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:
@@ -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
|
||||
)
|
||||
|
||||
@@ -74,3 +74,7 @@
|
||||
&usbd {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
qdec: &qdec {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
1
prj.conf
1
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
|
||||
|
||||
29
src/events/hid_tx_done_event.c
Normal file
29
src/events/hid_tx_done_event.c
Normal 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));
|
||||
28
src/events/hid_tx_done_event.h
Normal file
28
src/events/hid_tx_done_event.h
Normal 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
43
src/events/hid_tx_event.c
Normal 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
48
src/events/hid_tx_event.h
Normal 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__ */
|
||||
26
src/events/qdec_step_event.c
Normal file
26
src/events/qdec_step_event.c
Normal 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));
|
||||
36
src/events/qdec_step_event.h
Normal file
36
src/events/qdec_step_event.h
Normal 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__ */
|
||||
@@ -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);
|
||||
|
||||
204
src/modules/hid_tx_manager_module.c
Normal file
204
src/modules/hid_tx_manager_module.c
Normal 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);
|
||||
@@ -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
155
src/modules/qdec_module.c
Normal 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);
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user