feat(hids): 添加HID服务模块支持键盘和多媒体功能
- 新增hids_module.c实现蓝牙HID服务,支持键盘NKRO和Consumer控制 - 添加hid_report_descriptor.h定义统一的HID描述符,包括键盘、多媒体和RAW HID - 在CMakeLists.txt中注册hids模块源文件 - 配置prj.conf启用蓝牙HID相关配置项,设置设备名称和外观 - 修改main.c移除启动LED效果,简化主函数逻辑 - 添加settings_loader_def.h确保模块依赖正确加载 - 配置pm_static.yml分配flash存储空间给mcuboot和settings - 调整电源管理超时时间从20秒增加到300秒 - 启用MCUBOOT引导加载器支持
This commit is contained in:
19
src/main.c
19
src/main.c
@@ -3,33 +3,14 @@
|
||||
#define MODULE main
|
||||
#include <caf/events/module_state_event.h>
|
||||
|
||||
#include <caf/events/led_event.h>
|
||||
#include <caf/led_effect.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(MODULE);
|
||||
|
||||
/*
|
||||
* 通过 CAF leds 模块控制第 0 号 LED 常亮。
|
||||
* 颜色值在单色 LED 上会被折算为亮度,因此这里使用白色全亮。
|
||||
*/
|
||||
static const struct led_effect startup_led_effect =
|
||||
LED_EFFECT_LED_ON(LED_COLOR(255, 255, 255));
|
||||
|
||||
int main(void)
|
||||
{
|
||||
if (app_event_manager_init()) {
|
||||
LOG_ERR("Application Event Manager not initialized");
|
||||
} else {
|
||||
/*
|
||||
* 先提交 led_event,再上报 main ready。
|
||||
* CAF leds 模块会缓存 effect,并在收到 main ready 完成初始化后开始输出。
|
||||
*/
|
||||
struct led_event *event = new_led_event();
|
||||
|
||||
event->led_id = 0;
|
||||
event->led_effect = &startup_led_effect;
|
||||
APP_EVENT_SUBMIT(event);
|
||||
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
}
|
||||
|
||||
|
||||
193
src/modules/hids_module.c
Normal file
193
src/modules/hids_module.c
Normal file
@@ -0,0 +1,193 @@
|
||||
#include <bluetooth/services/hids.h>
|
||||
|
||||
#include <app_event_manager.h>
|
||||
|
||||
#define MODULE hids
|
||||
#include <caf/events/module_state_event.h>
|
||||
#include <caf/events/ble_common_event.h>
|
||||
|
||||
#include "hid_types.h"
|
||||
#include "hid_report_descriptor.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
|
||||
#define INPUT_REPORT_COUNT 2
|
||||
#define OUTPUT_REPORT_COUNT 1
|
||||
#define KEYBOARD_REPORT_LEN 30
|
||||
#define CONSUMER_REPORT_LEN 2
|
||||
#define KEYBOARD_LED_REPORT_LEN 1
|
||||
|
||||
/* 注册 HIDS 实例。此版本聚焦最小可用链路(Boot + Report)。 */
|
||||
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 void pm_evt_handler(enum bt_hids_pm_evt evt, struct bt_conn *conn)
|
||||
{
|
||||
ARG_UNUSED(conn);
|
||||
|
||||
switch (evt) {
|
||||
case BT_HIDS_PM_EVT_BOOT_MODE_ENTERED:
|
||||
current_pm = BT_HIDS_PM_BOOT;
|
||||
LOG_INF("HIDS protocol: boot");
|
||||
break;
|
||||
case BT_HIDS_PM_EVT_REPORT_MODE_ENTERED:
|
||||
current_pm = BT_HIDS_PM_REPORT;
|
||||
LOG_INF("HIDS protocol: report");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void report_notify_handler(enum bt_hids_notify_evt evt)
|
||||
{
|
||||
ARG_UNUSED(evt);
|
||||
}
|
||||
|
||||
static void boot_keyboard_notif_handler(enum bt_hids_notify_evt evt)
|
||||
{
|
||||
ARG_UNUSED(evt);
|
||||
}
|
||||
|
||||
static void boot_keyboard_output_report_handler(struct bt_hids_rep *rep,
|
||||
struct bt_conn *conn,
|
||||
bool write)
|
||||
{
|
||||
ARG_UNUSED(conn);
|
||||
|
||||
/* Basic boot protocol support: accept host LED writes and keep state locally. */
|
||||
if (!write || !rep || (rep->size == 0) || !rep->data) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("Boot KB out report 0x%02x", rep->data[0]);
|
||||
}
|
||||
|
||||
static void keyboard_output_report_handler(struct bt_hids_rep *rep,
|
||||
struct bt_conn *conn,
|
||||
bool write)
|
||||
{
|
||||
ARG_UNUSED(conn);
|
||||
|
||||
/*
|
||||
* 该回调用于 Report 协议的键盘 LED 输出(NumLock 等)。
|
||||
* 这里仅做最小解析并暴露注册回调,具体业务(例如驱动指示灯)留给上层实现。
|
||||
*/
|
||||
if (!write || !rep || !rep->data || (rep->size < KEYBOARD_LED_REPORT_LEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t leds = rep->data[0];
|
||||
LOG_DBG("Report KB out report 0x%02x", leds);
|
||||
|
||||
/*
|
||||
* 预留:后续在这里把 LED 输出转换为 CAF 事件(例如 NumLock 状态事件),
|
||||
* 由上层模块消费并驱动板级指示灯。
|
||||
*/
|
||||
ARG_UNUSED(leds);
|
||||
}
|
||||
|
||||
static int hids_service_init(void)
|
||||
{
|
||||
static const uint8_t report_map[] = HID_DESC_KEYBOARD_NKRO_CONSUMER();
|
||||
struct bt_hids_init_param init_param = { 0 };
|
||||
struct bt_hids_inp_rep *input_report = &init_param.inp_rep_group_init.reports[0];
|
||||
struct bt_hids_outp_feat_rep *output_report = &init_param.outp_rep_group_init.reports[0];
|
||||
|
||||
init_param.info.bcd_hid = 0x0101;
|
||||
init_param.info.b_country_code = 0x00;
|
||||
init_param.info.flags = BT_HIDS_REMOTE_WAKE | BT_HIDS_NORMALLY_CONNECTABLE;
|
||||
|
||||
init_param.rep_map.data = report_map;
|
||||
init_param.rep_map.size = sizeof(report_map);
|
||||
|
||||
input_report[0].id = REPORT_ID_KEYBOARD;
|
||||
input_report[0].size = KEYBOARD_REPORT_LEN;
|
||||
input_report[0].handler = report_notify_handler;
|
||||
|
||||
input_report[1].id = REPORT_ID_CONSUMER;
|
||||
input_report[1].size = CONSUMER_REPORT_LEN;
|
||||
input_report[1].handler = report_notify_handler;
|
||||
|
||||
/*
|
||||
* Report 协议键盘输出报告:
|
||||
* 与 Report Map 中 REPORT_ID_KEYBOARD 下定义的 1 字节 LED Output 对齐。
|
||||
*/
|
||||
output_report[0].id = REPORT_ID_KEYBOARD;
|
||||
output_report[0].size = KEYBOARD_LED_REPORT_LEN;
|
||||
output_report[0].handler = keyboard_output_report_handler;
|
||||
|
||||
init_param.inp_rep_group_init.cnt = INPUT_REPORT_COUNT;
|
||||
init_param.outp_rep_group_init.cnt = OUTPUT_REPORT_COUNT;
|
||||
init_param.pm_evt_handler = pm_evt_handler;
|
||||
init_param.is_kb = true;
|
||||
init_param.boot_kb_notif_handler = boot_keyboard_notif_handler;
|
||||
init_param.boot_kb_outp_rep_handler = boot_keyboard_output_report_handler;
|
||||
|
||||
return bt_hids_init(&hids_obj, &init_param);
|
||||
}
|
||||
|
||||
static void handle_ble_peer_event(const struct ble_peer_event *event)
|
||||
{
|
||||
switch (event->state) {
|
||||
case PEER_STATE_CONNECTED:
|
||||
__ASSERT_NO_MSG(active_conn == NULL);
|
||||
active_conn = event->id;
|
||||
if (bt_hids_connected(&hids_obj, active_conn)) {
|
||||
LOG_WRN("bt_hids_connected failed");
|
||||
}
|
||||
break;
|
||||
|
||||
case PEER_STATE_DISCONNECTED:
|
||||
if (active_conn == event->id) {
|
||||
if (bt_hids_disconnected(&hids_obj, active_conn)) {
|
||||
LOG_WRN("bt_hids_disconnected failed");
|
||||
}
|
||||
active_conn = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
static bool initialized;
|
||||
|
||||
__ASSERT_NO_MSG(!initialized);
|
||||
initialized = true;
|
||||
|
||||
if (hids_service_init()) {
|
||||
LOG_ERR("Cannot initialize HIDS service");
|
||||
module_set_state(MODULE_STATE_ERROR);
|
||||
} else {
|
||||
module_set_state(MODULE_STATE_READY);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_ble_peer_event(aeh)) {
|
||||
handle_ble_peer_event(cast_ble_peer_event(aeh));
|
||||
return false;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
APP_EVENT_LISTENER(MODULE, app_event_handler);
|
||||
/* Ensure GATT HIDS is registered before BLE is enabled by ble_state. */
|
||||
APP_EVENT_SUBSCRIBE_EARLY(MODULE, module_state_event);
|
||||
APP_EVENT_SUBSCRIBE_EARLY(MODULE, ble_peer_event);
|
||||
Reference in New Issue
Block a user