Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ KNOB 是 4 个普通键加 1 个旋钮的 USB 版本。旋钮在 Studio 中会
- `FN1` 短按 / 长按:在 USB 与 BLE 模式之间切换,设备会保存模式并重启进入目标模式。
- `FN2` 短按:切换到下一层。
- `FN2` 长按:在 BLE 模式下清除蓝牙配对信息并重启。
- 按住任意 FN 键再按 K1~K5:直接切换到第 1~5 层。
- 按住 `BOOT` 再按 K1~K5:直接切换到第 1~5 层。

## 无线版 KNOB

Expand All @@ -165,7 +165,7 @@ KNOB 是 4 个普通键加 1 个旋钮的 USB 版本。旋钮在 Studio 中会
- `FN1` 短按 / 长按:切换 USB / BLE 模式。
- `FN2` 短按:切换到下一层。
- `FN2` 长按:在 BLE 模式下清除蓝牙配对信息。
- 按住任意 FN 键再按 K1~K4:切换到第 1~4 层;旋钮不参与层选择。
- 按住 `BOOT` 再按 K1~K4:切换到第 1~4 层;旋钮不参与层选择。
- 旋钮适合配置为音量、滚轮、媒体键、层切换或宏触发。

## 无线版蓝牙配对
Expand Down Expand Up @@ -236,7 +236,7 @@ KNOB 是 4 个普通键加 1 个旋钮的 USB 版本。旋钮在 Studio 中会

- 首刷或救砖使用 `CH592F-<MODEL>-<version>-full.hex`。
- 正常在线更新使用 Studio 下载并写入 `CH592F-<MODEL>-<version>-app.bin`。
- 设备正常运行时,单击 `BOOT` 会进入 IAP / Bootloader 路径
- 设备正常运行时,`BOOT` 只作为层切换修饰键;按住 `BOOT` 再按普通键可直达对应层

## 接下来

Expand Down
4 changes: 2 additions & 2 deletions docs/wireless/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
- `FN1` 短按或长按:切换 USB / BLE 模式,切换时设备会重启进入目标模式。
- `FN2` 短按:切换到下一层。
- `FN2` 长按:在 BLE 模式下清除配对信息并重启。
- 按住任意 FN 键再按普通键:切换到对应层。
- 单击 `BOOT`:进入 IAP / Bootloader,用于恢复或调试
- 按住 `BOOT` 再按普通键:切换到对应层。
- 单击 `BOOT` 不再进入 IAP / Bootloader。

## 准备工作

Expand Down
2 changes: 1 addition & 1 deletion firmware/CH552G/app/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#define FW_VERSION_PATCH BK_FIRMWARE_VERSION_PATCH

// CH552 的特性级别用于旧 HID 读写兼容,不参与发布版本号
#define CH552_FEATURE_LEVEL 0x0D
#define CH552_FEATURE_LEVEL 0x0E
// EEPROM 头部只保留本地 schema 常量,避免旧布局被误读
#define EEPROM_SCHEMA_ID 0x01

Expand Down
13 changes: 13 additions & 0 deletions firmware/CH552G/keyboard/KeysDataHandler.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ static void loadRgbFromEEPROM(void);

static __xdata KeyConfig keySettings[MAX_LAYERS][KEY_CONFIG_SLOTS];
static __xdata uint8_t currentLayer = 0;
__xdata uint8_t osMode = OS_MODE_WIN;

void KeysDataInit(void)
{
Expand Down Expand Up @@ -109,6 +110,8 @@ static void initDefaultConfig(void)
eeprom_write_byte(EEPROM_RGB_PRESS_EFFECT_ADDR, PRESS_EFFECT_NONE);
// USB 轮询率默认 100Hz (bInterval=10)
eeprom_write_byte(EEPROM_POLL_RATE_ADDR, 10);
// 系统模式默认 Win
eeprom_write_byte(EEPROM_OS_MODE_ADDR, OS_MODE_WIN);
// MeowFS
meowfs_format();
eeprom_write_byte(EEPROM_MEOWFS_FMT_ADDR, MEOWFS_FMT_MAGIC);
Expand Down Expand Up @@ -173,6 +176,12 @@ void setCurrentLayer(uint8_t layer)
eeprom_write_byte(EEPROM_LAYER_ADDR, layer);
}

void setOsMode(uint8_t mode)
{
osMode = (mode == OS_MODE_MAC) ? OS_MODE_MAC : OS_MODE_WIN;
eeprom_write_byte(EEPROM_OS_MODE_ADDR, osMode);
}

// ── EEPROM 读写 ──

void saveLayerToEEPROM(uint8_t layer)
Expand Down Expand Up @@ -200,6 +209,10 @@ void loadAllFromEEPROM(void)
if (currentLayer >= MAX_LAYERS)
currentLayer = 0;

osMode = eeprom_read_byte(EEPROM_OS_MODE_ADDR);
if (osMode > OS_MODE_MAC)
osMode = OS_MODE_WIN;

for (uint8_t layer = 0; layer < MAX_LAYERS; layer++)
{
uint16_t base = EEPROM_KEYDATA_START + (uint16_t)layer * LAYER_DATA_SIZE;
Expand Down
9 changes: 9 additions & 0 deletions firmware/CH552G/keyboard/KeysDataHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ extern "C"
#define HOST_CMD_READ_RGB 0x06
#define HOST_CMD_WRITE_RGB 0x07
#define HOST_CMD_FACTORY_RESET 0x08
#define HOST_CMD_READ_OS_MODE 0x09
#define HOST_CMD_WRITE_OS_MODE 0x0A

#define HOST_CMD_MACRO_INFO 0x40

#define OS_MODE_WIN 0
#define OS_MODE_MAC 1

// RGB EEPROM 地址 (紧接层数据后面, 动态计算)
#define EEPROM_RGB_BASE_ADDR (EEPROM_KEYDATA_START + MAX_LAYERS * LAYER_DATA_SIZE)
#define EEPROM_RGB_ENABLED_ADDR (EEPROM_RGB_BASE_ADDR + 0)
Expand All @@ -58,6 +63,7 @@ extern "C"

// USB HID 轮询率 (bInterval: 1=1000Hz, 2=500Hz, 10=100Hz)
#define EEPROM_POLL_RATE_ADDR (EEPROM_RGB_BASE_ADDR + 11)
#define EEPROM_OS_MODE_ADDR (EEPROM_RGB_BASE_ADDR + 12)

// 函数声明
void KeysDataInit(void);
Expand All @@ -68,8 +74,11 @@ extern "C"
uint16_t getKeyValueAt(uint8_t layer, uint8_t index);

// 层管理
extern __xdata uint8_t osMode;

uint8_t getCurrentLayer(void);
void setCurrentLayer(uint8_t layer);
void setOsMode(uint8_t mode);

// EEPROM 接口
void saveLayerToEEPROM(uint8_t layer);
Expand Down
40 changes: 39 additions & 1 deletion firmware/CH552G/usb/CustomUSBHID.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,29 @@ __xdata uint8_t CustomBuf[31] = {0x01};

typedef void (*pTaskFn)(void);

#define MOD_LCTRL 0x01
#define MOD_LGUI 0x08
#define MOD_RCTRL 0x10
#define MOD_RGUI 0x80

static uint8_t applyOsModeModifier(uint8_t modifier)
{
uint8_t mapped = modifier;
if (osMode != OS_MODE_MAC)
return mapped;

mapped &= (uint8_t)~(MOD_LCTRL | MOD_LGUI | MOD_RCTRL | MOD_RGUI);
if (modifier & MOD_LCTRL)
mapped |= MOD_LGUI;
if (modifier & MOD_LGUI)
mapped |= MOD_LCTRL;
if (modifier & MOD_RCTRL)
mapped |= MOD_RGUI;
if (modifier & MOD_RGUI)
mapped |= MOD_RCTRL;
return mapped;
}

void USBInit()
{
USBDeviceCfg(); // Device mode configuration
Expand Down Expand Up @@ -209,6 +232,21 @@ void USB_EP1_OUT()
CustomBuf[2] = flash_read_byte(MEOWFS_BASE + 1);
USB_EP1_send(5);
}
else if (cmd == HOST_CMD_READ_OS_MODE)
{
memset(CustomBuf, 0, sizeof(CustomBuf));
CustomBuf[0] = HOST_CMD_READ_OS_MODE;
CustomBuf[1] = osMode;
USB_EP1_send(5);
}
else if (cmd == HOST_CMD_WRITE_OS_MODE)
{
setOsMode(Ep1Buffer[2]);
memset(CustomBuf, 0, sizeof(CustomBuf));
CustomBuf[0] = HOST_CMD_WRITE_OS_MODE;
CustomBuf[1] = 0;
USB_EP1_send(5);
}
else if (cmd >= HOST_CMD_MACRO_INFO)
{
handle_macro_cmd();
Expand Down Expand Up @@ -262,7 +300,7 @@ uint8_t USB_EP1_send(__data uint8_t reportID)
Ep1Buffer[64] = reportID;
for (__data uint8_t i = 0; i < len; i++)
{
Ep1Buffer[64 + 1 + i] = src[i];
Ep1Buffer[64 + 1 + i] = (reportID == 1 && i == 0) ? applyOsModeModifier(src[i]) : src[i];
}
UEP1_T_LEN = 1 + len;

Expand Down
1 change: 1 addition & 0 deletions firmware/CH592F/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ else()
UART_LOG_ENABLE=0
KBD_DEBUG_BUILD=0
KBD_USB_LOG_ENABLE=0
KBD_LOG_DEFAULT_ENABLED=0
)
endif()

Expand Down
34 changes: 27 additions & 7 deletions firmware/CH592F/ble/hid/src/kbd_mode.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,12 @@ int KBD_Mode_Init(kbd_work_mode_t initial_mode, kbd_mode_callbacks_t *pCBs)
LOG_I(TAG, "init mode=%d", initial_mode);

/*
* WCH Application 示例思路:每种模式只初始化对应协议栈
* - USB 模式:仅 USB,不初始化 BLE HID
* - BLE 模式:仅 BLE HID,不初始化 USB
* 模式切换通过 SYS_ResetExecute() 全系统复位实现
* 工作模式只决定按键报告发送到哪里:
* - USB 模式:按键报告走 USB
* - BLE 模式:按键报告走 BLE
*
* BLE 模式也初始化 USB Device。这样无线使用时插上 USB,Studio 仍可通过
* USB HID 配置接口读写当前键盘配置,不需要先切换到 USB 工作模式。
*/
if (initial_mode == KBD_WORK_MODE_BLE)
{
Expand All @@ -127,6 +129,7 @@ int KBD_Mode_Init(kbd_work_mode_t initial_mode, kbd_mode_callbacks_t *pCBs)
LOG_E(TAG, "BLE init failed %d", ret);
return ret;
}
USB_Device_Init();
}
else
{
Expand Down Expand Up @@ -386,6 +389,22 @@ int KBD_Mode_USB_Wakeup(void)
/* HID 报告发送实现 */
/*============================================================================*/

static uint8_t KBD_Mode_ApplyOsModeModifier(uint8_t modifier)
{
if (KBD_GetOsMode() != KBD_OS_MODE_MAC)
{
return modifier;
}

uint8_t mapped = modifier;
mapped &= (uint8_t)~(KBD_MOD_LCTRL | KBD_MOD_LGUI | KBD_MOD_RCTRL | KBD_MOD_RGUI);
if (modifier & KBD_MOD_LCTRL) mapped |= KBD_MOD_LGUI;
if (modifier & KBD_MOD_LGUI) mapped |= KBD_MOD_LCTRL;
if (modifier & KBD_MOD_RCTRL) mapped |= KBD_MOD_RGUI;
if (modifier & KBD_MOD_RGUI) mapped |= KBD_MOD_RCTRL;
return mapped;
}

int KBD_Mode_SendKeyboardReport(uint8_t modifier, uint8_t *keys, uint8_t key_count)
{
if (!KBD_Mode_IsConnected())
Expand All @@ -394,10 +413,11 @@ int KBD_Mode_SendKeyboardReport(uint8_t modifier, uint8_t *keys, uint8_t key_cou
}

KBD_Mode_RecordActivityInternal();
uint8_t report_modifier = KBD_Mode_ApplyOsModeModifier(modifier);

/* 构建报告 */
memset(g_kbd_report, 0, sizeof(g_kbd_report));
g_kbd_report[0] = modifier;
g_kbd_report[0] = report_modifier;
g_kbd_report[1] = 0; /* Reserved */

uint8_t count = (key_count > 6) ? 6 : key_count;
Expand All @@ -408,12 +428,12 @@ int KBD_Mode_SendKeyboardReport(uint8_t modifier, uint8_t *keys, uint8_t key_cou

if (g_current_mode == KBD_WORK_MODE_USB)
{
USB_Keyboard_Press(modifier, keys, key_count);
USB_Keyboard_Press(report_modifier, keys, key_count);
return 0;
}
else
{
return BLE_HID_SendKeyboardReport(modifier, keys, key_count);
return BLE_HID_SendKeyboardReport(report_modifier, keys, key_count);
}
}

Expand Down
7 changes: 1 addition & 6 deletions firmware/CH592F/hal/include/kbd_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,10 @@ typedef struct

#define KBD_FN_NUM_KEYS 2u

/* BOOT 按键: PB22 */
/* BOOT 按键: PB22 (层切换修饰键: BOOT + K1..Kn -> Layer 1..n) */
#define KBD_FN_BOOT_PORT GPIO_PORT_B
#define KBD_FN_BOOT_PIN GPIO_Pin_22

/* 调试入口:单击 BOOT 键后直接进入 IAP bootloader */
#ifndef KBD_BOOT_KEY_ENTER_BOOTLOADER
#define KBD_BOOT_KEY_ENTER_BOOTLOADER 1
#endif

/* FN1: PA4 */
#define KBD_FN1_PORT GPIO_PORT_A
#define KBD_FN1_PIN GPIO_Pin_4
Expand Down
10 changes: 2 additions & 8 deletions firmware/CH592F/hal/include/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,15 @@ extern "C" {
* @brief 普通按键事件队列容量(必须为 2 的幂)。
*/
#ifndef KEY_QUEUE_SIZE
#define KEY_QUEUE_SIZE 32
#define KEY_QUEUE_SIZE 16
#endif

/**
* @def FNKEY_QUEUE_SIZE
* @brief FN 按键事件队列容量(必须为 2 的幂)。
*/
#ifndef FNKEY_QUEUE_SIZE
#define FNKEY_QUEUE_SIZE 16
#define FNKEY_QUEUE_SIZE 8
#endif

/**
Expand Down Expand Up @@ -227,12 +227,6 @@ uint8_t FnKey_GetEvent(fnkey_event_t *evt);
*/
int8_t FnKey_IsDown(uint8_t id);

/**
* @brief 标记 FN 键已被 FN+Key 组合使用,松开时跳过 CLICK/LONG 事件。
* @param id FN 键索引(0..KBD_FN_NUM_KEYS-1)
*/
void FnKey_MarkComboUsed(uint8_t id);

/**
* @brief 读取 BOOT 键是否按下(原始电平读取,不使用中断)。
* @return 1=按下,0=松开
Expand Down
2 changes: 1 addition & 1 deletion firmware/CH592F/hal/include/ws2812.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#define WS2812_T0H (uint32_t)(0.35 * WS2812_CLK_FREQ)
#define WS2812_T1H (uint32_t)(0.85 * WS2812_CLK_FREQ)
#define WS2812_BIT_CYCLE (uint32_t)(1.25 * WS2812_CLK_FREQ)
#define WS2812_RESET_CYCLES 300 /**< 复位信号周期数 (≈300μs @ 1.25μs/cycle) */
#define WS2812_RESET_CYCLES 80 /**< 复位信号周期数 (≈100μs @ 1.25μs/cycle) */
#define WS2812_POWER_ON_US 300 /**< RGB 上电后的稳定时间 */

/** 缓冲区长度:LED数据(每个LED 24bit) + 复位信号 */
Expand Down
2 changes: 1 addition & 1 deletion firmware/CH592F/hal/src/kbd_battery.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ void KBD_Battery_RequestRefresh(void)

uint16_t KBD_Battery_GetVoltage_mV(void)
{
if (!s_cache_ready || !s_sample_pending)
if (!s_cache_ready && !s_sample_pending)
{
KBD_Battery_RequestRefresh();
}
Expand Down
32 changes: 7 additions & 25 deletions firmware/CH592F/hal/src/key.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ typedef struct
volatile uint16_t lock_ms; /**< 锁定倒计时(ms)。 */
volatile uint8_t is_down; /**< 当前是否按下(1=按下,0=松开)。 */
volatile uint8_t expect; /**< 期待边沿:0=按下(下降沿),1=松开(上升沿)。 */
volatile uint8_t combo_used; /**< FN+Key 组合已触发,松开时跳过事件。 */
volatile uint32_t press_tick; /**< 按下边沿发生时的 tick(ms)。 */
} fn_ctx_t;

Expand Down Expand Up @@ -533,7 +532,6 @@ static inline void HandleFnKeyEdge(uint8_t id)
{
/* Press edge. */
s_fn_ctx[id].is_down = 1;
s_fn_ctx[id].combo_used = 0;
s_fn_ctx[id].press_tick = now;
s_fn_ctx[id].expect = 1;
ConfigPinRiseEdge(pin);
Expand All @@ -545,20 +543,15 @@ static inline void HandleFnKeyEdge(uint8_t id)
s_fn_ctx[id].expect = 0;
ConfigPinFallEdge(pin);

/* FN+Key 组合已使用过,跳过 click/long 事件 */
if (!s_fn_ctx[id].combo_used)
uint32_t dur = now - s_fn_ctx[id].press_tick;
if (dur >= (uint32_t)FN_LONG_PRESS_MS)
{
uint32_t dur = now - s_fn_ctx[id].press_tick;
if (dur >= (uint32_t)FN_LONG_PRESS_MS)
{
PushFnEvent(id, FNKEY_EVT_LONG, now);
}
else
{
PushFnEvent(id, FNKEY_EVT_CLICK, now);
}
PushFnEvent(id, FNKEY_EVT_LONG, now);
}
else
{
PushFnEvent(id, FNKEY_EVT_CLICK, now);
}
s_fn_ctx[id].combo_used = 0;
}

s_fn_ctx[id].lock_ms = FN_LOCKOUT_MS;
Expand Down Expand Up @@ -755,17 +748,6 @@ int8_t FnKey_IsDown(uint8_t id)
return s_fn_ctx[id].is_down ? 1 : 0;
}

/**
* @brief 标记 FN 键已被 FN+Key 组合使用。
* @param id FN 键索引
* @details 调用后,该 FN 键松开时不会产生 CLICK/LONG 事件。
*/
void FnKey_MarkComboUsed(uint8_t id)
{
if (id < KBD_FN_NUM_KEYS)
s_fn_ctx[id].combo_used = 1;
}

/**
* @brief BOOT 键是否按下(原始读取,Active-Low)。
* @return 1=按下;0=松开
Expand Down
Loading
Loading