Initial import of firmware and host projects

This commit is contained in:
2026-04-10 16:53:41 +08:00
commit 42a36164be
124 changed files with 13943 additions and 0 deletions

34
inc/buttons_def.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* CAF buttons 矩阵引脚定义
*
* 设计说明:
* - 本文件被 CAF buttons 模块通过 CONFIG_CAF_BUTTONS_DEF_PATH 直接包含;
* - 行列引脚顺序必须与板级 DTS 中 my_keyboard 的 row-gpios/col-gpios 保持一致;
* - key_id 的行列编号完全基于这里的数组下标,不依赖 input-keymap 节点。
*/
#include <caf/gpio_pins.h>
/*
* 该符号用于保证配置文件只被链接一次:
* 若被重复包含到多个编译单元,会在链接阶段报重复定义,避免静默错配。
*/
const struct {} buttons_def_include_once;
/* 列引脚:对应 atguigu_mini_keyboard.dts 中 my_keyboard/col-gpios 顺序。 */
static const struct gpio_pin col[] = {
{ .port = 0, .pin = 5 },
{ .port = 0, .pin = 6 },
{ .port = 0, .pin = 26 },
{ .port = 0, .pin = 30 },
};
/* 行引脚:对应 atguigu_mini_keyboard.dts 中 my_keyboard/row-gpios 顺序。 */
static const struct gpio_pin row[] = {
{ .port = 0, .pin = 15 },
{ .port = 0, .pin = 7 },
{ .port = 0, .pin = 12 },
{ .port = 0, .pin = 4 },
{ .port = 1, .pin = 9 },
{ .port = 0, .pin = 8 },
};

View File

@@ -0,0 +1,16 @@
#ifndef HID_HOST_COMMAND_PROTOCOL_H__
#define HID_HOST_COMMAND_PROTOCOL_H__
#include <stdint.h>
#define HID_HOST_CMD_DATA_SIZE 8U
#define HID_HOST_CMD_OUTPUT_PAYLOAD_SIZE (1U + HID_HOST_CMD_DATA_SIZE)
#define HID_HOST_CMD_ACK_PAYLOAD_SIZE 1U
#define HID_HOST_CMD_ID_THEME_COLOR 0x01U
#define HID_HOST_CMD_ID_TIME_SYNC 0x02U
#define HID_HOST_CMD_THEME_PARAM_SIZE 3U
#define HID_HOST_CMD_TIME_SYNC_PARAM_SIZE 8U
#endif /* HID_HOST_COMMAND_PROTOCOL_H__ */

9
inc/hid_host_transport.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef HID_HOST_TRANSPORT_H__
#define HID_HOST_TRANSPORT_H__
enum hid_host_transport {
HID_HOST_TRANSPORT_USB = 0,
HID_HOST_TRANSPORT_BLE,
};
#endif /* HID_HOST_TRANSPORT_H__ */

43
inc/hid_keymap_def.h Normal file
View File

@@ -0,0 +1,43 @@
/*
* HID keymap for current new_kbd numeric keypad layout.
* 说明:
* - 本文件仿照 nrf_desktop 的 hid_keymap_def.h 组织方式;
* - 仅由 keyboard_module.c 包含一次(通过 APP_HID_KEYMAP_DEF_PATH
* - 条目必须按 key_id 升序排列(按 KEY_ID(col, row) 计算后的数值顺序)。
*/
#include <caf/key_id.h>
/*
* 防止该定义文件被多处 include 导致重复符号。
* 约定仅由 keyboard_module.c 包含一次。
*/
const struct {} hid_keymap_def_include_once;
static const struct hid_keymap hid_keymap[] = {
/* col 0 */
{ KEY_ID(0, 1), 0x0053, REPORT_ID_KEYBOARD }, /* Num Lock */
{ KEY_ID(0, 2), 0x005F, REPORT_ID_KEYBOARD }, /* Keypad 7 */
{ KEY_ID(0, 3), 0x005C, REPORT_ID_KEYBOARD }, /* Keypad 4 */
{ KEY_ID(0, 4), 0x0059, REPORT_ID_KEYBOARD }, /* Keypad 1 */
{ KEY_ID(0, 5), 0x0062, REPORT_ID_KEYBOARD }, /* Keypad 0 */
/* col 1 */
{ KEY_ID(1, 1), 0x0054, REPORT_ID_KEYBOARD }, /* Keypad / */
{ KEY_ID(1, 2), 0x0060, REPORT_ID_KEYBOARD }, /* Keypad 8 */
{ KEY_ID(1, 3), 0x005D, REPORT_ID_KEYBOARD }, /* Keypad 5 */
{ KEY_ID(1, 4), 0x005A, REPORT_ID_KEYBOARD }, /* Keypad 2 */
{ KEY_ID(1, 5), 0x0063, REPORT_ID_KEYBOARD }, /* Keypad . */
/* col 2 */
{ KEY_ID(2, 1), 0x0055, REPORT_ID_KEYBOARD }, /* Keypad * */
{ KEY_ID(2, 2), 0x0061, REPORT_ID_KEYBOARD }, /* Keypad 9 */
{ KEY_ID(2, 3), 0x005E, REPORT_ID_KEYBOARD }, /* Keypad 6 */
{ KEY_ID(2, 4), 0x005B, REPORT_ID_KEYBOARD }, /* Keypad 3 */
/* col 3 */
{ KEY_ID(3, 0), 0x00E2, REPORT_ID_CONSUMER }, /* Mute */
{ KEY_ID(3, 1), 0x0056, REPORT_ID_KEYBOARD }, /* Keypad - */
{ KEY_ID(3, 3), 0x0057, REPORT_ID_KEYBOARD }, /* Keypad + */
{ KEY_ID(3, 5), 0x0058, REPORT_ID_KEYBOARD }, /* Keypad Enter */
};

161
inc/hid_report_descriptor.h Normal file
View File

@@ -0,0 +1,161 @@
#ifndef HID_REPORT_DESCRIPTOR_H_
#define HID_REPORT_DESCRIPTOR_H_
#include <zephyr/usb/class/usbd_hid.h>
#include "hid_host_command_protocol.h"
/* 与 HID Report Map 对齐的 Report ID。 */
enum {
REPORT_ID_KEYBOARD = 1,
REPORT_ID_CONSUMER = 3,
REPORT_ID_VENDOR = 4,
REPORT_ID_VENDOR_CMD = 5,
};
#define HID_KBD_USAGE_MAX 0x00E7U
#define HID_KBD_MOD_COUNT 8U
#define HID_KBD_BITMAP_BITS (HID_KBD_USAGE_MAX + 1U)
#define HID_KBD_BITMAP_SIZE ((HID_KBD_BITMAP_BITS + 7U) / 8U)
#define HID_KBD_PAYLOAD_SIZE (1U + HID_KBD_BITMAP_SIZE)
#define HID_BOOT_KBD_PAYLOAD_SIZE 8U
#define HID_CONSUMER_PAYLOAD_SIZE 2U
#define HID_VENDOR_PAYLOAD_SIZE HID_KBD_PAYLOAD_SIZE
#define HID_VENDOR_ACK_PAYLOAD_SIZE HID_HOST_CMD_ACK_PAYLOAD_SIZE
#define HID_KBD_LED_PAYLOAD_SIZE 1U
#define HID_FULL_REPORT_SIZE(payload) (1U + (payload))
/*
* HID_USAGE_PAGE() 只支持 1 字节 Usage Page。
* Vendor Defined Page(0xFF00) 需要 2 字节编码,因此在本地补一个 16 位版本,
* 避免在描述符里混用裸字节,后续维护时可以一眼看出字段语义。
*/
#define HID_USAGE_PAGE16(page_lsb, page_msb) \
HID_ITEM(HID_ITEM_TAG_USAGE_PAGE, HID_ITEM_TYPE_GLOBAL, 2), page_lsb, page_msb
/*
* 键盘(NKRO) + Consumer + Vendor 的复合 Report 描述符:
* - USB Report 接口和 BLE HIDS Report Map 统一使用这份定义,
* 避免两边手写常量后长期演进出现不一致。
* - Report ID 0x04 继续复用 NKRO payload承载私有状态/遮罩语义。
* - Report ID 0x05 预留给“主机命令 + 设备 ACK”通道
* - Output payload 固定 9 字节:[cmd(1) | data(8)]
* - Input payload 固定 1 字节:[cmd]
*/
#define HID_DESC_KEYBOARD_NKRO_CONSUMER() \
{ \
/* Generic Desktop 页:声明这是一个 Keyboard Application 集合。 */ \
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP), \
HID_USAGE(HID_USAGE_GEN_DESKTOP_KEYBOARD), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_ID(REPORT_ID_KEYBOARD), \
\
/* Keyboard/Keypad 页:先定义 8bit Modifier再定义 232bit NKRO 位图。 */ \
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP_KEYPAD), \
HID_USAGE_MIN8(0xE0), \
HID_USAGE_MAX8(0xE7), \
HID_LOGICAL_MIN8(0), \
HID_LOGICAL_MAX8(1), \
HID_REPORT_SIZE(1), \
HID_REPORT_COUNT(8), \
HID_INPUT(0x02), \
HID_USAGE_MIN8(0x00), \
HID_USAGE_MAX8(0xE7), \
HID_LOGICAL_MIN8(0), \
HID_LOGICAL_MAX8(1), \
HID_REPORT_SIZE(1), \
HID_REPORT_COUNT(HID_KBD_BITMAP_BITS), \
HID_INPUT(0x02), \
\
/* Report 协议下键盘 LED 输出NumLock/CapsLock/ScrollLock/Compose/Kana。 */ \
HID_USAGE_PAGE(0x08U), \
HID_USAGE_MIN8(0x01), \
HID_USAGE_MAX8(0x05), \
HID_LOGICAL_MIN8(0), \
HID_LOGICAL_MAX8(1), \
HID_REPORT_SIZE(1), \
HID_REPORT_COUNT(5), \
HID_OUTPUT(0x02), \
/* 补齐到 1 字节3bit padding标记为常量。 */ \
HID_REPORT_SIZE(3), \
HID_REPORT_COUNT(1), \
HID_OUTPUT(0x01), \
HID_END_COLLECTION, \
\
/* Consumer 页:使用 16bit Usage 承载多媒体按键(音量/播放/亮度等)。 */ \
HID_USAGE_PAGE(0x0CU), \
HID_USAGE(0x01U), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_ID(REPORT_ID_CONSUMER), \
HID_LOGICAL_MIN8(0), \
HID_LOGICAL_MAX16(0xEA, 0x00), \
HID_USAGE_MIN16(0x00, 0x00), \
HID_USAGE_MAX16(0xEA, 0x00), \
HID_REPORT_SIZE(16), \
HID_REPORT_COUNT(1), \
HID_INPUT(0x00), \
HID_END_COLLECTION, \
\
/* Vendor 页:双向传输完整键盘状态/屏蔽遮罩payload 结构与 NKRO 一致。 */ \
HID_USAGE_PAGE16(0x00, 0xFF), \
HID_USAGE(0x02U), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_ID(REPORT_ID_VENDOR), \
HID_LOGICAL_MIN8(0), \
HID_LOGICAL_MAX16(0xFF, 0x00), \
HID_REPORT_SIZE(8), \
HID_REPORT_COUNT(HID_VENDOR_PAYLOAD_SIZE), \
HID_USAGE(0x02U), \
HID_INPUT(0x02), \
HID_REPORT_COUNT(HID_VENDOR_PAYLOAD_SIZE), \
HID_USAGE(0x02U), \
HID_OUTPUT(0x02), \
HID_END_COLLECTION, \
\
/* Vendor 页(0xFF01):主机命令写入 + 设备 ACK 返回。 */ \
HID_USAGE_PAGE16(0x01, 0xFF), \
HID_USAGE(0x05U), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_REPORT_ID(REPORT_ID_VENDOR_CMD), \
HID_LOGICAL_MIN8(0), \
HID_LOGICAL_MAX16(0xFF, 0x00), \
HID_REPORT_SIZE(8), \
HID_REPORT_COUNT(HID_VENDOR_ACK_PAYLOAD_SIZE), \
HID_USAGE(0x05U), \
HID_INPUT(0x02), \
HID_REPORT_COUNT(HID_HOST_CMD_OUTPUT_PAYLOAD_SIZE), \
HID_USAGE(0x05U), \
HID_OUTPUT(0x02), \
HID_END_COLLECTION, \
}
/*
* RAW HID 的固定 64 字节输入/输出描述符。
* 设计意图:
* - 采用 Vendor Defined(0xFF00) 页,避免与标准键盘/多媒体语义冲突;
* - IN/OUT 都固定 64 字节,便于固件与上位机用定长帧做双向透传;
* - 不使用 Report ID接口只承载一个 RAW Report减少主机端解析分支。
*/
#define HID_DESC_RAW_64() \
{ \
/* Vendor Defined 页(0xFF00):供厂商私有协议传输,不绑定标准 HID 语义。 */ \
HID_USAGE_PAGE16(0x00, 0xFF), \
HID_USAGE(0x01), \
HID_COLLECTION(HID_COLLECTION_APPLICATION), \
HID_LOGICAL_MIN8(0), \
HID_LOGICAL_MAX16(0xFF, 0x00), \
HID_REPORT_SIZE(8), \
\
/* 输入页:定义 64 字节 Input 报文Data|Var|Abs(0x02) 与原描述符一致。 */ \
HID_REPORT_COUNT(0x40), \
HID_USAGE(0x01), \
HID_INPUT(0x02), \
\
/* 输出页:定义 64 字节 Output 报文,与输入长度对齐,简化双向协议。 */ \
HID_REPORT_COUNT(0x40), \
HID_USAGE(0x01), \
HID_OUTPUT(0x02), \
HID_END_COLLECTION, \
}
#endif

29
inc/led_state.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef NEW_KBD_LED_STATE_H__
#define NEW_KBD_LED_STATE_H__
#include <zephyr/types.h>
/* 模块内系统状态:只用于本项目的 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__ */

39
inc/led_state_def.h Normal file
View File

@@ -0,0 +1,39 @@
#include "led_state.h"
#include <caf/led_effect.h>
/*
* 该文件仅被 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 -> 0Num Lock
* - led_1 -> 1BLE 状态)
*/
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)),
};

17
inc/settings_loader_def.h Normal file
View File

@@ -0,0 +1,17 @@
/*
* Defines modules that must reach READY before CAF settings_loader
* calls settings_load().
*/
/* Enforce single inclusion in the final link unit. */
const struct {} settings_loader_def_include_once;
#include <caf/events/module_state_event.h>
static inline void get_req_modules(struct module_flags *mf)
{
module_flags_set_bit(mf, MODULE_IDX(main));
#ifdef CONFIG_CAF_BLE_STATE
module_flags_set_bit(mf, MODULE_IDX(ble_state));
#endif
}

69
inc/time_manager.h Normal file
View File

@@ -0,0 +1,69 @@
#ifndef TIME_MANAGER_H__
#define TIME_MANAGER_H__
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* 时间同步来源保持传输无关:
* - BLE/USB/手动设置都复用同一套枚举;
* - 后续如果新增其他同步链路,只需要补枚举值,不需要改事件语义。
*/
enum time_sync_source {
TIME_SYNC_SOURCE_NONE = 0,
TIME_SYNC_SOURCE_BLE,
TIME_SYNC_SOURCE_USB,
TIME_SYNC_SOURCE_MANUAL,
TIME_SYNC_SOURCE_HID,
};
/*
* 时间同步更新载荷:
* - utc_ms 统一使用 UTC 毫秒时间戳,避免内部状态受本地时区影响;
* - timezone_min 记录“显示层”所需的时区偏移,当前不拆 DST
* - accuracy_ms 允许上位机表达这次校时的可信度,未知时传 0 即可。
*/
struct time_sync_update {
uint64_t utc_ms;
int16_t timezone_min;
uint32_t accuracy_ms;
enum time_sync_source source;
};
/*
* 时间快照用于提供给显示、日志或后续 USB/调试接口:
* - synchronized=true 表示当前开机周期内已经收到有效校时;
* - has_persisted_time=true 仅表示 flash 里存过一次历史校时,不代表当前时间仍然可信;
* - ready=false 表示 time_manager 还没等到 settings_loader 完成初始化。
*/
struct time_manager_snapshot {
uint64_t utc_ms;
int16_t timezone_min;
uint32_t accuracy_ms;
enum time_sync_source source;
bool ready;
bool synchronized;
bool has_persisted_time;
};
/* 返回当前模块是否已经完成初始化,供同步入口快速拒绝“过早写入”。 */
bool time_manager_is_ready(void);
/*
* 获取当前时间快照:
* - 返回 0snapshot 已填充;
* - 返回 -EINVAL参数为空
* - 返回 -EAGAIN模块未 ready
* - 返回 -ENODATA当前开机周期尚未完成有效校时。
*/
int time_manager_get_snapshot(struct time_manager_snapshot *snapshot);
#ifdef __cplusplus
}
#endif
#endif /* TIME_MANAGER_H__ */

33
inc/time_sync_protocol.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef TIME_SYNC_PROTOCOL_H__
#define TIME_SYNC_PROTOCOL_H__
#include <stdint.h>
#include <zephyr/sys/util.h>
/*
* 统一定义时间同步协议帧格式,方便 BLE/USB 两条链路共享:
*
* byte 0 : version
* byte 1 : flags
* byte 2-3 : timezone_min (little-endian, int16)
* byte 4-11: utc_ms (little-endian, uint64)
* byte 12-15: accuracy_ms (little-endian, uint32)
*/
#define TIME_SYNC_PROTOCOL_VERSION 1U
#define TIME_SYNC_PROTOCOL_PAYLOAD_SIZE 16U
#define TIME_SYNC_PROTOCOL_OFFSET_VERSION 0U
#define TIME_SYNC_PROTOCOL_OFFSET_FLAGS 1U
#define TIME_SYNC_PROTOCOL_OFFSET_TIMEZONE 2U
#define TIME_SYNC_PROTOCOL_OFFSET_UTC_MS 4U
#define TIME_SYNC_PROTOCOL_OFFSET_ACCURACY_MS 12U
/*
* 预留 flags 字段:
* - 当前版本只要求时区字段有效;
* - 后续如果要加 DST、闰秒或来源质量扩展可以继续复用这个字节。
*/
#define TIME_SYNC_PROTOCOL_FLAG_TIMEZONE_VALID BIT(0)
#endif /* TIME_SYNC_PROTOCOL_H__ */