Added Q/K/V Max series keyboards

This commit is contained in:
lokher
2025-03-21 16:14:28 +08:00
parent 0bc3ffb43f
commit 363b5bc6dc
891 changed files with 86181 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
COMMON_DIR = common
SRC += $(COMMON_DIR)/matrix.c
VPATH += $(TOP_DIR)/keyboards/keychron/$(COMMON_DIR)

View File

@@ -0,0 +1,450 @@
/* Copyright 2021 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "raw_hid.h"
#include "via.h"
#include "keychron_task.h"
#ifdef LK_WIRELESS_ENABLE
# include "transport.h"
# include "battery.h"
# include "lpm.h"
# include "lkbt51.h"
# include "indicator.h"
#endif
#include "config.h"
#include "version.h"
#ifndef RAW_EPSIZE
# define RAW_EPSIZE 32
#endif
#ifndef BL_CYCLE_KEY
# define BL_CYCLE_KEY KC_RIGHT
#endif
#ifndef BL_TRIG_KEY
# define BL_TRIG_KEY KC_HOME
#endif
#ifndef P2P4G_CELAR_MASK
# define P2P4G_CELAR_MASK P2P4G_CLEAR_PAIRING_TYPE_C
#endif
enum {
BACKLIGHT_TEST_OFF = 0,
BACKLIGHT_TEST_WHITE,
BACKLIGHT_TEST_RED,
BACKLIGHT_TEST_GREEN,
BACKLIGHT_TEST_BLUE,
BACKLIGHT_TEST_MAX,
};
enum {
KEY_PRESS_FN = 0x01 << 0,
KEY_PRESS_J = 0x01 << 1,
KEY_PRESS_Z = 0x01 << 2,
KEY_PRESS_BL_KEY1 = 0x01 << 3,
KEY_PRESS_BL_KEY2 = 0x01 << 4,
KEY_PRESS_FACTORY_RESET = KEY_PRESS_FN | KEY_PRESS_J | KEY_PRESS_Z,
KEY_PRESS_BACKLIGTH_TEST = KEY_PRESS_FN | KEY_PRESS_BL_KEY1 | KEY_PRESS_BL_KEY2,
};
enum {
FACTORY_TEST_CMD_BACKLIGHT = 0x01,
FACTORY_TEST_CMD_OS_SWITCH,
FACTORY_TEST_CMD_JUMP_TO_BL,
FACTORY_TEST_CMD_INT_PIN,
FACTORY_TEST_CMD_GET_TRANSPORT,
FACTORY_TEST_CMD_CHARGING_ADC,
FACTORY_TEST_CMD_RADIO_CARRIER,
FACTORY_TEST_CMD_GET_BUILD_TIME,
FACTORY_TEST_CMD_GET_DEVICE_ID
};
enum {
P2P4G_CLEAR_PAIRING_TYPE_A = 0x01 << 0,
P2P4G_CLEAR_PAIRING_TYPE_C = 0x01 << 1,
};
enum {
OS_SWITCH = 0x01,
};
static uint32_t factory_reset_timer = 0;
static uint8_t factory_reset_state = 0;
static uint8_t backlight_test_mode = BACKLIGHT_TEST_OFF;
static uint32_t factory_reset_ind_timer = 0;
static uint8_t factory_reset_ind_state = 0;
static bool report_os_sw_state = false;
static bool keys_released = true;
void factory_timer_start(void) {
factory_reset_timer = timer_read32();
}
static inline void factory_timer_check(void) {
if (timer_elapsed32(factory_reset_timer) > 3000) {
factory_reset_timer = 0;
if (factory_reset_state == KEY_PRESS_FACTORY_RESET) {
factory_reset_ind_timer = timer_read32();
factory_reset_ind_state++;
keys_released = false;
clear_keyboard(); // Avoid key being pressed after NKRO state changed
layer_state_t default_layer_tmp = default_layer_state;
eeconfig_init();
keymap_config.raw = eeconfig_read_keymap();
default_layer_set(default_layer_tmp);
#ifdef LED_MATRIX_ENABLE
if (!led_matrix_is_enabled()) led_matrix_enable();
led_matrix_init();
#endif
#ifdef RGB_MATRIX_ENABLE
if (!rgb_matrix_is_enabled()) rgb_matrix_enable();
rgb_matrix_init();
#endif
#ifdef LK_WIRELESS_ENABLE
lkbt51_factory_reset(P2P4G_CELAR_MASK);
#endif
} else if (factory_reset_state == KEY_PRESS_BACKLIGTH_TEST) {
#ifdef LED_MATRIX_ENABLE
if (!led_matrix_is_enabled()) led_matrix_enable();
#endif
#ifdef RGB_MATRIX_ENABLE
if (!rgb_matrix_is_enabled()) rgb_matrix_enable();
#endif
backlight_test_mode = BACKLIGHT_TEST_WHITE;
}
factory_reset_state = 0;
}
}
static inline void factory_reset_ind_timer_check(void) {
if (factory_reset_ind_timer && timer_elapsed32(factory_reset_ind_timer) > 250) {
if (factory_reset_ind_state++ > 6) {
factory_reset_ind_timer = factory_reset_ind_state = 0;
} else {
factory_reset_ind_timer = timer_read32();
}
}
}
bool process_record_factory_test(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
#if defined(FN_KEY_1) || defined(FN_KEY_2)
# if defined(FN_KEY_1)
case FN_KEY_1: /* fall through */
# endif
# if defined(FN_KEY_2)
case FN_KEY_2:
# endif
# if defined(FN_KEY_3)
case FN_KEY_3:
# endif
if (record->event.pressed) {
factory_reset_state |= KEY_PRESS_FN;
} else {
factory_reset_state &= ~KEY_PRESS_FN;
factory_reset_timer = 0;
}
break;
#endif
case KC_J:
if (record->event.pressed) {
factory_reset_state |= KEY_PRESS_J;
if (factory_reset_state == 0x07) factory_timer_start();
if (factory_reset_state & KEY_PRESS_FN) return false;
} else {
factory_reset_state &= ~KEY_PRESS_J;
factory_reset_timer = 0;
}
break;
case KC_Z:
#if defined(FN_Z_KEY)
case FN_Z_KEY:
#endif
if (record->event.pressed) {
factory_reset_state |= KEY_PRESS_Z;
if (factory_reset_state == 0x07) factory_timer_start();
if ((factory_reset_state & KEY_PRESS_FN) && keycode == KC_Z) return false;
} else {
factory_reset_state &= ~KEY_PRESS_Z;
factory_reset_timer = 0;
/* Avoid changing backlight effect on key released if FN_Z_KEY is mode*/
if (!keys_released && keycode >= QK_BACKLIGHT_ON && keycode <= RGB_MODE_TWINKLE) {
keys_released = true;
return false;
}
}
break;
#if defined(BL_CYCLE_KEY) || defined(BL_CYCLE_KEY_2)
# if defined(BL_CYCLE_KEY)
case BL_CYCLE_KEY:
# endif
# if defined(FN_BL_CYCLE_KEY)
case FN_BL_CYCLE_KEY:
# endif
if (record->event.pressed) {
if (backlight_test_mode) {
if (++backlight_test_mode >= BACKLIGHT_TEST_MAX) {
backlight_test_mode = BACKLIGHT_TEST_WHITE;
}
} else {
factory_reset_state |= KEY_PRESS_BL_KEY1;
if (factory_reset_state == 0x19) {
factory_timer_start();
}
}
} else {
factory_reset_state &= ~KEY_PRESS_BL_KEY1;
factory_reset_timer = 0;
}
break;
#endif
#if defined(BL_TRIG_KEY) || defined(BL_TRIG_KEY_2)
# if defined(BL_TRIG_KEY)
case BL_TRIG_KEY:
# endif
# if defined(FN_BL_TRIG_KEY)
case FN_BL_TRIG_KEY:
# endif
if (record->event.pressed) {
if (backlight_test_mode) {
backlight_test_mode = BACKLIGHT_TEST_OFF;
} else {
factory_reset_state |= KEY_PRESS_BL_KEY2;
if (factory_reset_state == 0x19) {
factory_timer_start();
}
}
} else {
factory_reset_state &= ~KEY_PRESS_BL_KEY2;
factory_reset_timer = 0;
}
break;
#endif
}
return true;
}
#ifdef LED_MATRIX_ENABLE
bool factory_test_indicator(void) {
if (factory_reset_ind_state) {
led_matrix_set_value_all(factory_reset_ind_state % 2 ? 0 : 255);
return false;
}
return true;
}
#endif
#ifdef RGB_MATRIX_ENABLE
bool factory_test_indicator(void) {
if (factory_reset_ind_state) {
backlight_test_mode = BACKLIGHT_TEST_OFF;
rgb_matrix_set_color_all(factory_reset_ind_state % 2 ? 0 : 255, 0, 0);
return false;
} else if (backlight_test_mode) {
switch (backlight_test_mode) {
case BACKLIGHT_TEST_WHITE:
rgb_matrix_set_color_all(255, 255, 255);
break;
case BACKLIGHT_TEST_RED:
rgb_matrix_set_color_all(255, 0, 0);
break;
case BACKLIGHT_TEST_GREEN:
rgb_matrix_set_color_all(0, 255, 0);
break;
case BACKLIGHT_TEST_BLUE:
rgb_matrix_set_color_all(0, 0, 255);
break;
}
return false;
}
return true;
}
#endif
bool factory_reset_indicating(void) {
return factory_reset_ind_timer;
}
bool factory_test_task(void) {
if (factory_reset_timer) factory_timer_check();
if (factory_reset_ind_timer) factory_reset_ind_timer_check();
return true;
}
void factory_test_send(uint8_t *payload, uint8_t length) {
#ifdef RAW_ENABLE
uint16_t checksum = 0;
uint8_t data[RAW_EPSIZE] = {0};
uint8_t i = 0;
data[i++] = 0xAB;
memcpy(&data[i], payload, length);
i += length;
for (uint8_t i = 1; i < RAW_EPSIZE - 3; i++)
checksum += data[i];
data[RAW_EPSIZE - 2] = checksum & 0xFF;
data[RAW_EPSIZE - 1] = (checksum >> 8) & 0xFF;
raw_hid_send(data, RAW_EPSIZE);
#endif
}
void factory_test_rx(uint8_t *data, uint8_t length) {
if (data[0] == 0xAB) {
uint16_t checksum = 0;
for (uint8_t i = 1; i < RAW_EPSIZE - 3; i++) {
checksum += data[i];
}
/* Verify checksum */
if ((checksum & 0xFF) != data[RAW_EPSIZE - 2] || checksum >> 8 != data[RAW_EPSIZE - 1]) return;
#ifdef LK_WIRELESS_ENABLE
uint8_t payload[32];
uint8_t len = 0;
#endif
switch (data[1]) {
case FACTORY_TEST_CMD_BACKLIGHT:
backlight_test_mode = data[2];
factory_reset_timer = 0;
break;
case FACTORY_TEST_CMD_OS_SWITCH:
report_os_sw_state = data[2];
if (report_os_sw_state) {
// dip_switch_read(true);
}
break;
case FACTORY_TEST_CMD_JUMP_TO_BL:
// if (memcmp(&data[2], "JumpToBootloader", strlen("JumpToBootloader")) == 0) bootloader_jump();
break;
#ifdef LK_WIRELESS_ENABLE
case FACTORY_TEST_CMD_INT_PIN:
switch (data[2]) {
/* Enalbe/disable test */
case 0xA1:
lkbt51_int_pin_test(data[3]);
break;
/* Set INT state */
case 0xA2:
kc_printf("pin %d\n\r", data[3]);
gpio_write_pin(LKBT51_INT_OUTPUT_PIN, data[3]);
break;
/* Report INT state */
// case 0xA3:
// payload[len++] = FACTORY_TEST_CMD_INT_PIN;
// payload[len++] = 0xA3;
// payload[len++] = gpio_read_pin(LKBT51_INT_INPUT_PIN);
// factory_test_send(payload, len);
// break;
}
break;
case FACTORY_TEST_CMD_GET_TRANSPORT:
payload[len++] = FACTORY_TEST_CMD_GET_TRANSPORT;
payload[len++] = get_transport();
payload[len++] = gpio_read_pin(USB_POWER_SENSE_PIN);
factory_test_send(payload, len);
break;
#endif
#ifdef BATTERY_CHARGE_DONE_DETECT_ADC
case FACTORY_TEST_CMD_CHARGING_ADC:
case 0xA1:
battery_charging_monitor(data[3]);
break;
case 0xA2:
payload[len++] = FACTORY_TEST_CMD_CHARGING_ADC;
payload[len++] = battery_adc_read_charging_pin();
factory_test_send(payload, len);
break;
#endif
#ifdef LK_WIRELESS_ENABLE
case FACTORY_TEST_CMD_RADIO_CARRIER:
if (data[2] < 79) lkbt51_radio_test(data[2]);
break;
# ifdef WERELESS_PRESSURE_TEST
case 0x70:
switch (data[2]) {
/* Enalbe/disable test */
case 0xB1:
SEND_STRING("abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890\n");
break;
case 0xB2:
payload[len++] = 0x70;
payload[len++] = 0xB2;
payload[len++] = wireless_get_state();
factory_test_send(payload, len);
break;
}
break;
# endif
#endif
case FACTORY_TEST_CMD_GET_BUILD_TIME: {
payload[len++] = FACTORY_TEST_CMD_GET_BUILD_TIME;
payload[len++] = 'v';
if ((DEVICE_VER & 0xF000) != 0) itoa((DEVICE_VER >> 12), (char *)&payload[len++], 16);
itoa((DEVICE_VER >> 8) & 0xF, (char *)&payload[len++], 16);
payload[len++] = '.';
itoa((DEVICE_VER >> 4) & 0xF, (char *)&payload[len++], 16);
payload[len++] = '.';
itoa((DEVICE_VER >> 4) & 0xF, (char *)&payload[len++], 16);
payload[len++] = ' ';
memcpy(&payload[len], QMK_BUILDDATE, sizeof(QMK_BUILDDATE));
len += sizeof(QMK_BUILDDATE);
factory_test_send(payload, len);
} break;
#if 0
case FACTORY_TEST_CMD_GET_DEVICE_ID:
payload[len++] = FACTORY_TEST_CMD_GET_DEVICE_ID;
payload[len++] = 12; // UUID length
memcpy(&payload[len], (uint32_t *)UID_BASE, 4);
memcpy(&payload[len+4], (uint32_t *)UID_BASE+4, 4);
memcpy(&payload[len+8], (uint32_t *)UID_BASE+8, 4);
len += 12;
factory_test_send(payload, len);
break;
#endif
}
}
}
bool dip_switch_update_user(uint8_t index, bool active) {
if (report_os_sw_state) {
#ifdef INVERT_OS_SWITCH_STATE
active = !active;
#endif
uint8_t payload[3] = {FACTORY_TEST_CMD_OS_SWITCH, OS_SWITCH, active};
factory_test_send(payload, 3);
}
return true;
}

View File

@@ -0,0 +1,34 @@
/* Copyright 2022 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define FACTORY_RESET_CHECK process_record_factory_test
#define FACTORY_RESET_TASK factory_test_task
void factory_test_init(void);
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
bool factory_test_indicator(void);
#endif
//void process_record_factory_test(uint16_t keycode, keyrecord_t *record);
bool factory_reset_indicating(void);
void factory_test_task(void);
void factory_test_rx(uint8_t *data, uint8_t length);
bool process_record_factory_test(uint16_t keycode, keyrecord_t *record);

View File

@@ -0,0 +1,239 @@
/* Copyright 2023 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
#include "raw_hid.h"
#include "version.h"
#ifdef FACTORY_TEST_ENABLE
# include "factory_test.h"
# include "keychron_common.h"
#endif
#ifdef LK_WIRELESS_ENABLE
# include "lkbt51.h"
#endif
bool is_siri_active = false;
uint32_t siri_timer = 0;
static uint8_t mac_keycode[4] = {
KC_LOPT,
KC_ROPT,
KC_LCMD,
KC_RCMD,
};
// clang-format off
static key_combination_t key_comb_list[] = {
{2, {KC_LWIN, KC_TAB}},
{2, {KC_LWIN, KC_E}},
{3, {KC_LSFT, KC_LCMD, KC_4}},
{2, {KC_LWIN, KC_C}},
#ifdef WIN_LOCK_SCREEN_ENABLE
{2, {KC_LWIN, KC_L}},
#endif
#ifdef MAC_LOCK_SCREEN_ENABLE
{3, {KC_LCTL, KC_LCMD, KC_Q}},
#endif
};
// clang-format on
bool process_record_keychron_common(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case KC_MCTRL:
if (record->event.pressed) {
register_code(KC_MISSION_CONTROL);
} else {
unregister_code(KC_MISSION_CONTROL);
}
return false; // Skip all further processing of this key
case KC_LNPAD:
if (record->event.pressed) {
register_code(KC_LAUNCHPAD);
} else {
unregister_code(KC_LAUNCHPAD);
}
return false; // Skip all further processing of this key
case KC_LOPTN:
case KC_ROPTN:
case KC_LCMMD:
case KC_RCMMD:
if (record->event.pressed) {
register_code(mac_keycode[keycode - KC_LOPTN]);
} else {
unregister_code(mac_keycode[keycode - KC_LOPTN]);
}
return false; // Skip all further processing of this key
case KC_SIRI:
if (record->event.pressed) {
if (!is_siri_active) {
is_siri_active = true;
register_code(KC_LCMD);
register_code(KC_SPACE);
}
siri_timer = timer_read32();
} else {
// Do something else when release
}
return false; // Skip all further processing of this key
case KC_TASK:
case KC_FILE:
case KC_SNAP:
case KC_CTANA:
#ifdef WIN_LOCK_SCREEN_ENABLE
case KC_WLCK:
#endif
#ifdef MAC_LOCK_SCREEN_ENABLE
case KC_MLCK:
#endif
if (record->event.pressed) {
for (uint8_t i = 0; i < key_comb_list[keycode - KC_TASK].len; i++) {
register_code(key_comb_list[keycode - KC_TASK].keycode[i]);
}
} else {
for (uint8_t i = 0; i < key_comb_list[keycode - KC_TASK].len; i++) {
unregister_code(key_comb_list[keycode - KC_TASK].keycode[i]);
}
}
return false; // Skip all further processing of this key
default:
return true; // Process all other keycodes normally
}
}
void keychron_common_task(void) {
if (is_siri_active && timer_elapsed32(siri_timer) > 500) {
unregister_code(KC_LCMD);
unregister_code(KC_SPACE);
is_siri_active = false;
siri_timer = 0;
}
}
#ifdef ENCODER_ENABLE
static void encoders_pins_cb(void *param) {
uint8_t index = (uint32_t)param;
extern void encoder_quadrature_handle_inerrupt_read(uint8_t index);
encoder_quadrature_handle_inerrupt_read(index);
}
void encoder_cb_init(void) {
pin_t encoders_pin_a[] = ENCODER_A_PINS;
pin_t encoders_pin_b[] = ENCODER_B_PINS;
for (uint32_t i=0; i<NUM_ENCODERS; i++)
{
palEnableLineEvent(encoders_pin_a[i], PAL_EVENT_MODE_BOTH_EDGES);
palEnableLineEvent(encoders_pin_b[i], PAL_EVENT_MODE_BOTH_EDGES);
palSetLineCallback(encoders_pin_a[i], encoders_pins_cb, (void*)i);
palSetLineCallback(encoders_pin_b[i], encoders_pins_cb, (void*)i);
}
}
#endif
//__attribute__((weak)) bool raw_hid_receive_keychron(uint8_t *data, uint8_t length) { return true; }
#define PROTOCOL_VERSION 0x02
enum { kc_get_protocol_version = 0xA0, kc_get_firmware_version = 0xA1, kc_get_support_feature = 0xA2, kc_get_default_layer = 0xA3 };
enum {
FEATURE_DEFAULT_LAYER = 0x01 << 0,
FEATURE_BLUETOOTH = 0x01 << 1,
FEATURE_P2P4G = 0x01 << 2,
FEATURE_ANALOG_MATRIX = 0x01 << 3,
};
void get_support_feature(uint8_t *data) {
data[1] = FEATURE_DEFAULT_LAYER
#ifdef KC_BLUETOOTH_ENABLE
| FEATURE_BLUETOOTH
#endif
#ifdef LK_WIRELESS_ENABLE
| FEATURE_BLUETOOTH | FEATURE_P2P4G
#endif
#ifdef ANANLOG_MATRIX
| FEATURE_ANALOG_MATRIX
#endif
;
}
bool kc_raw_hid_rx(uint8_t *data, uint8_t length) {
// if (!raw_hid_receive_keychron(data, length))
// return false;
switch (data[0]) {
case kc_get_protocol_version:
data[1] = PROTOCOL_VERSION;
raw_hid_send(data, length);
break;
case kc_get_firmware_version: {
uint8_t i = 1;
data[i++] = 'v';
if ((DEVICE_VER & 0xF000) != 0) itoa((DEVICE_VER >> 12), (char *)&data[i++], 16);
itoa((DEVICE_VER >> 8) & 0xF, (char *)&data[i++], 16);
data[i++] = '.';
itoa((DEVICE_VER >> 4) & 0xF, (char *)&data[i++], 16);
data[i++] = '.';
itoa(DEVICE_VER & 0xF, (char *)&data[i++], 16);
data[i++] = ' ';
memcpy(&data[i], QMK_BUILDDATE, sizeof(QMK_BUILDDATE));
i += sizeof(QMK_BUILDDATE);
raw_hid_send(data, length);
} break;
case kc_get_support_feature:
get_support_feature(&data[1]);
raw_hid_send(data, length);
break;
case kc_get_default_layer:
data[1] = get_highest_layer(default_layer_state);
raw_hid_send(data, length);
break;
#ifdef ANANLOG_MATRIX
case 0xA9:
analog_matrix_rx(data, length);
break;
#endif
#ifdef LK_WIRELESS_ENABLE
case 0xAA:
lkbt51_dfu_rx(data, length);
break;
#endif
#ifdef FACTORY_TEST_ENABLE
case 0xAB:
factory_test_rx(data, length);
break;
#endif
default:
return false;
}
return true;
}
#if defined(VIA_ENABLE)
bool via_command_kb(uint8_t *data, uint8_t length) {
return kc_raw_hid_rx(data, length);
}
#else
void raw_hid_receive(uint8_t *data, uint8_t length) {
kc_raw_hid_rx(data, length);
}
#endif

View File

@@ -0,0 +1,107 @@
/* Copyright 2023 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "stdint.h"
// clang-format off
enum {
KC_LOPTN = QK_KB_0,
KC_ROPTN,
KC_LCMMD,
KC_RCMMD,
KC_MCTRL,
KC_LNPAD,
KC_TASK_VIEW,
KC_FILE_EXPLORER,
KC_SCREEN_SHOT,
KC_CORTANA,
#ifdef WIN_LOCK_SCREEN_ENABLE
KC_WIN_LOCK_SCREEN,
__KC_WIN_LOCK_SCREEN_NEXT,
#else
__KC_WIN_LOCK_SCREEN_NEXT = KC_CORTANA + 1,
#endif
#ifdef MAC_LOCK_SCREEN_ENABLE
KC_MAC_LOCK_SCREEN = __KC_WIN_LOCK_SCREEN_NEXT,
__KC_MAC_LOCK_SCREEN_NEXT,
#else
__KC_MAC_LOCK_SCREEN_NEXT = __KC_WIN_LOCK_SCREEN_NEXT,
#endif
KC_SIRI = __KC_MAC_LOCK_SCREEN_NEXT,
#ifdef LK_WIRELESS_ENABLE
BT_HST1,
BT_HST2,
BT_HST3,
#if defined(P2P4_MODE_ENABLE) || defined(P24G_MODE_SELECT_PIN)
P2P4G,
__P2P4G_NEXT,
#else
P2P4G = _______,
__P2P4G_NEXT = BT_HST3 + 1,
#endif
#if defined(TRANSPORT_SOFT_SWITCH_ENABLE)
TP_USB = __P2P4G_NEXT,
__TP_USB_NEXT,
#else
__TP_USB_NEXT = __P2P4G_NEXT,
#endif
BAT_LVL = __TP_USB_NEXT,
__BAT_LVL_NEXT = BAT_LVL + 1,
#else
BT_HST1 = _______,
BT_HST2 = _______,
BT_HST3 = _______,
#if defined(TRANSPORT_SOFT_SWITCH_ENABLE)
MD_USB = _______,
#endif
BAT_LVL = _______,
__BAT_LVL_NEXT = KC_SIRI + 1,
#endif
#ifdef ANANLOG_MATRIX
PROF1 = __BAT_LVL_NEXT,
PROF2,
PROF3,
__PROF3_NEXT = PROF3 + 1,
#else
PROF1 = _______,
PROF2 = _______,
PROF3 = _______,
__PROF3_NEXT = __BAT_LVL_NEXT,
#endif
NEW_SAFE_RANGE = __PROF3_NEXT
};
#define KC_TASK KC_TASK_VIEW
#define KC_FILE KC_FILE_EXPLORER
#define KC_SNAP KC_SCREEN_SHOT
#define KC_CTANA KC_CORTANA
#define KC_WLCK KC_WIN_LOCK_SCREEN
#define KC_MLCK KC_MAC_LOCK_SCREEN
typedef struct PACKED {
uint8_t len;
uint8_t keycode[3];
} key_combination_t;
bool process_record_keychron_common(uint16_t keycode, keyrecord_t *record);
void keychron_common_task(void);
#ifdef ENCODER_ENABLE
void encoder_cb_init(void);
#endif

View File

@@ -0,0 +1,10 @@
OPT_DEFS += -DFACTORY_TEST_ENABLE -DAPDAPTIVE_NKRO_ENABLE
KEYCHRON_COMMON_DIR = common
SRC += \
$(KEYCHRON_COMMON_DIR)/keychron_task.c \
$(KEYCHRON_COMMON_DIR)/keychron_common.c \
$(KEYCHRON_COMMON_DIR)/factory_test.c
VPATH += $(TOP_DIR)/keyboards/keychron/$(KEYCHRON_COMMON_DIR)

View File

@@ -0,0 +1,117 @@
/* Copyright 2023 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "keychron_task.h"
#include "quantum.h"
#include "keychron_common.h"
#ifdef FACTORY_TEST_ENABLE
# include "factory_test.h"
#endif
__attribute__((weak)) bool process_record_keychron_kb(uint16_t keycode, keyrecord_t *record) {
return true;
}
bool process_record_keychron(uint16_t keycode, keyrecord_t *record) {
#ifdef LK_WIRELESS_ENABLE
extern bool process_record_wireless(uint16_t keycode, keyrecord_t * record);
if (!process_record_wireless(keycode, record)) return false;
#endif
#ifdef FACTORY_TEST_ENABLE
if (!process_record_factory_test(keycode, record)) return false;
#endif
// extern bool process_record_keychron_kb(uint16_t keycode, keyrecord_t *record);
if (!process_record_keychron_kb(keycode, record)) return false;
return true;
}
#if defined(LED_MATRIX_ENABLE)
bool led_matrix_indicators_keychron(void) {
# ifdef LK_WIRELESS_ENABLE
extern bool led_matrix_indicators_bt(void);
led_matrix_indicators_bt();
# endif
# ifdef FACTORY_TEST_ENABLE
factory_test_indicator();
# endif
return true;
}
#endif
#if defined(RGB_MATRIX_ENABLE)
bool rgb_matrix_indicators_keychron(void) {
# ifdef LK_WIRELESS_ENABLE
extern bool rgb_matrix_indicators_bt(void);
rgb_matrix_indicators_bt();
# endif
# ifdef FACTORY_TEST_ENABLE
factory_test_indicator();
# endif
return true;
}
#endif
__attribute__((weak)) bool keychron_task_kb(void) {
return true;
}
void keychron_task(void) {
#ifdef LK_WIRELESS_ENABLE
extern void wireless_tasks(void);
wireless_tasks();
#endif
#ifdef FACTORY_TEST_ENABLE
factory_test_task();
#endif
keychron_common_task();
keychron_task_kb();
}
bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
if (!process_record_user(keycode, record)) return false;
if (!process_record_keychron(keycode, record)) return false;
return true;
}
#ifdef RGB_MATRIX_ENABLE
bool rgb_matrix_indicators_kb(void) {
if (!rgb_matrix_indicators_user()) return false;
rgb_matrix_indicators_keychron();
return true;
}
#endif
#ifdef LED_MATRIX_ENABLE
bool led_matrix_indicators_kb(void) {
if (!led_matrix_indicators_user()) return false;
led_matrix_indicators_keychron();
return true;
}
#endif
void housekeeping_task_kb(void) {
keychron_task();
}

View File

@@ -0,0 +1,25 @@
/* Copyright 2022 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "stdint.h"
#include "action.h"
bool keychron_task_kb(void);
bool process_record_keychron_kb(uint16_t keycode, keyrecord_t *record);
void keychron_task(void);

View File

@@ -0,0 +1,218 @@
/* Copyright 2023 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#ifndef HC595_STCP
# define HC595_STCP B0
#endif
#ifndef HC595_SHCP
# define HC595_SHCP A1
#endif
#ifndef HC595_DS
# define HC595_DS A7
#endif
#ifndef HC595_START_INDEX
# define HC595_START_INDEX 0
#endif
#ifndef HC595_END_INDEX
# define HC595_END_INDEX 15
#endif
#ifndef HC595_OFFSET_INDEX
# define HC595_OFFSET_INDEX 0
#endif
#if defined(HC595_START_INDEX) && defined(HC595_END_INDEX)
# if ((HC595_END_INDEX - HC595_START_INDEX + 1) > 16)
# define SIZE_T uint32_t
# define UNSELECT_ALL_COL 0xFFFFFFFF
# define SELECT_ALL_COL 0x00000000
# elif ((HC595_END_INDEX - HC595_START_INDEX + 1) > 8)
# define SIZE_T uint16_t
# define UNSELECT_ALL_COL 0xFFFF
# define SELECT_ALL_COL 0x0000
# else
# define SIZE_T uint8_t
# define UNSELECT_ALL_COL 0xFF
# define SELECT_ALL_COL 0x00
# endif
#endif
pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
static inline uint8_t readMatrixPin(pin_t pin) {
if (pin != NO_PIN) {
return gpio_read_pin(pin);
} else {
return 1;
}
}
static inline void setPinOutput_writeLow(pin_t pin) {
gpio_set_pin_output_push_pull(pin);
gpio_write_pin_low(pin);
}
static inline void setPinOutput_writeHigh(pin_t pin) {
gpio_set_pin_output_push_pull(pin);
gpio_write_pin_high(pin);
}
static inline void HC595_delay(uint16_t n) {
while (n-- > 0) {
asm volatile("nop" ::: "memory");
}
}
static void HC595_output(SIZE_T data, bool bit_flag) {
uint8_t n = 1;
ATOMIC_BLOCK_FORCEON {
for (uint8_t i = 0; i < (HC595_END_INDEX - HC595_START_INDEX + 1); i++) {
if (data & 0x1) {
gpio_write_pin_high(HC595_DS);
} else {
gpio_write_pin_low(HC595_DS);
}
gpio_write_pin_high(HC595_SHCP);
HC595_delay(n);
gpio_write_pin_low(HC595_SHCP);
HC595_delay(n);
if (bit_flag) {
break;
} else {
data = data >> 1;
}
}
gpio_write_pin_high(HC595_STCP);
HC595_delay(n);
gpio_write_pin_low(HC595_STCP);
HC595_delay(n);
}
}
static void select_col(uint8_t col) {
if (col < HC595_START_INDEX || col > HC595_END_INDEX) {
setPinOutput_writeLow(col_pins[col]);
} else {
if (col == HC595_START_INDEX) {
HC595_output(0x00, true);
if (col < HC595_OFFSET_INDEX) {
HC595_output(0x01, true);
}
}
}
}
static void unselect_col(uint8_t col) {
if (col < HC595_START_INDEX || col > HC595_END_INDEX) {
#ifdef MATRIX_UNSELECT_DRIVE_HIGH
setPinOutput_writeHigh(col_pins[col]);
#else
gpio_set_pin_input_high(col_pins[col]);
#endif
} else {
HC595_output(0x01, true);
}
}
static void unselect_cols(void) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
if (col < HC595_START_INDEX || col > HC595_END_INDEX) {
#ifdef MATRIX_UNSELECT_DRIVE_HIGH
setPinOutput_writeHigh(col_pins[col]);
#else
gpio_set_pin_input_high(col_pins[col]);
#endif
} else {
if (col == HC595_START_INDEX) {
HC595_output(UNSELECT_ALL_COL, false);
}
break;
}
}
}
void select_all_cols(void) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
if (col < HC595_START_INDEX || col > HC595_END_INDEX) {
setPinOutput_writeLow(col_pins[col]);
} else {
if (col == HC595_START_INDEX) {
HC595_output(SELECT_ALL_COL, false);
}
break;
}
}
}
static void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter) {
// Select col
select_col(current_col); // select col
HC595_delay(200);
// For each row...
for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) {
// Check row pin state
if (readMatrixPin(row_pins[row_index]) == 0) {
// Pin LO, set col bit
current_matrix[row_index] |= row_shifter;
} else {
// Pin HI, clear col bit
current_matrix[row_index] &= ~row_shifter;
}
}
// Unselect col
unselect_col(current_col);
HC595_delay(200); // wait for all Row signals to go HIGH
}
void matrix_init_custom(void) {
gpio_set_pin_output_push_pull(HC595_DS);
gpio_set_pin_output_push_pull(HC595_STCP);
gpio_set_pin_output_push_pull(HC595_SHCP);
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
if (row_pins[x] != NO_PIN) {
gpio_set_pin_input_high(row_pins[x]);
}
}
unselect_cols();
}
bool matrix_scan_custom(matrix_row_t current_matrix[]) {
matrix_row_t curr_matrix[MATRIX_ROWS] = {0};
// Set col, read rows
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) {
matrix_read_rows_on_col(curr_matrix, current_col, row_shifter);
}
bool changed = memcmp(current_matrix, curr_matrix, sizeof(curr_matrix)) != 0;
if (changed) memcpy(current_matrix, curr_matrix, sizeof(curr_matrix));
return changed;
}
void suspend_wakeup_init_kb(void) {
// code will run on keyboard wakeup
clear_keyboard();
}

View File

@@ -0,0 +1,147 @@
#include "quantum.h"
#include "wireless.h"
#include "indicator.h"
#include "lpm.h"
#if defined(PROTOCOL_CHIBIOS)
# include <usb_main.h>
#elif if defined(PROTOCOL_LUFA)
# include "lufa.h"
#endif
#include "eeprom.h"
#if (defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)) && defined(BAT_LEVEL_LED_LIST)
#ifndef BAT_LEVEL_GROWING_INTERVAL
# define BAT_LEVEL_GROWING_INTERVAL 150
#endif
#ifndef BAT_LEVEL_ON_INTERVAL
# define BAT_LEVEL_ON_INTERVAL 3000
#endif
#ifdef LED_MATRIX_ENABLE
# define LED_DRIVER_IS_ENABLED led_matrix_is_enabled
#endif
#ifdef RGB_MATRIX_ENABLE
# define LED_DRIVER_IS_ENABLED rgb_matrix_is_enabled
#endif
enum {
BAT_LVL_ANI_NONE,
BAT_LVL_ANI_GROWING,
BAT_LVL_ANI_BLINK_OFF,
BAT_LVL_ANI_BLINK_ON,
};
static uint8_t animation_state = 0;
static uint32_t bat_lvl_ani_timer_buffer = 0;
static uint8_t bat_percentage;
static uint8_t cur_percentage;
static uint32_t time_interval;
#ifdef RGB_MATRIX_ENABLE
static uint8_t r, g, b;
#endif
extern indicator_config_t indicator_config;
extern backlight_state_t original_backlight_state;
void bat_level_animiation_start(uint8_t percentage) {
/* Turn on backlight mode for indicator */
indicator_enable();
animation_state = BAT_LVL_ANI_GROWING;
bat_percentage = percentage;
bat_lvl_ani_timer_buffer = timer_read32();
cur_percentage = 0;
time_interval = BAT_LEVEL_GROWING_INTERVAL;
#ifdef RGB_MATRIX_ENABLE
r = g = b = 255;
#endif
}
void bat_level_animiation_stop(void) {
animation_state = BAT_LVL_ANI_NONE;
}
bool bat_level_animiation_actived(void) {
return animation_state;
}
void bat_level_animiation_indicate(void) {
#ifdef LED_MATRIX_ENABLE
uint8_t bat_lvl_led_list[10] = BAT_LEVEL_LED_LIST;
for (uint8_t i = 0; i <= LED_MATRIX_LED_COUNT; i++) {
led_matrix_set_value(i, 0);
}
if (animation_state == BAT_LVL_ANI_GROWING || animation_state == BAT_LVL_ANI_BLINK_ON)
for (uint8_t i = 0; i < cur_percentage / 10; i++)
led_matrix_set_value(bat_lvl_led_list[i], 255);
#endif
#ifdef RGB_MATRIX_ENABLE
uint8_t bat_lvl_led_list[10] = BAT_LEVEL_LED_LIST;
for (uint8_t i = 0; i <= RGB_MATRIX_LED_COUNT; i++) {
rgb_matrix_set_color(i, 0, 0, 0);
}
if (animation_state == BAT_LVL_ANI_GROWING || animation_state == BAT_LVL_ANI_BLINK_ON) {
for (uint8_t i = 0; i < cur_percentage / 10; i++) {
rgb_matrix_set_color(bat_lvl_led_list[i], r, g, b);
}
}
#endif
}
void bat_level_animiation_update(void) {
switch (animation_state) {
case BAT_LVL_ANI_GROWING:
if (cur_percentage < bat_percentage)
cur_percentage += 10;
else {
if (cur_percentage == 0) cur_percentage = 10;
animation_state = BAT_LVL_ANI_BLINK_OFF;
}
break;
case BAT_LVL_ANI_BLINK_OFF:
#ifdef RGB_MATRIX_ENABLE
if (bat_percentage < 30) {
r = 255;
b = g = 0;
} else {
r = b = 0;
g = 255;
}
#endif
time_interval = BAT_LEVEL_ON_INTERVAL;
animation_state = BAT_LVL_ANI_BLINK_ON;
break;
case BAT_LVL_ANI_BLINK_ON:
animation_state = BAT_LVL_ANI_NONE;
indicator_eeconfig_reload();
if (indicator_config.value == 0 && !LED_DRIVER_IS_ENABLED()) {
indicator_disable();
}
lpm_timer_reset();
break;
default:
break;
}
bat_lvl_ani_timer_buffer = timer_read32();
}
void bat_level_animiation_task(void) {
if (animation_state && sync_timer_elapsed32(bat_lvl_ani_timer_buffer) > time_interval) {
bat_level_animiation_update();
}
}
#endif

View File

@@ -0,0 +1,23 @@
/* Copyright 2022 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
void bat_level_animiation_start(uint8_t percentage);
void bat_level_animiation_stop(void);
bool bat_level_animiation_actived(void);
void bat_level_animiation_indicate(void);
void bat_level_animiation_task(void);

View File

@@ -0,0 +1,216 @@
/*/* Copyright 2022 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "wireless.h"
#include "battery.h"
#include "transport.h"
#include "lkbt51.h"
#include "lpm.h"
#include "indicator.h"
#include "rtc_timer.h"
#include "analog.h"
#define BATTERY_EMPTY_COUNT 10
#define CRITICAL_LOW_COUNT 20
/* Battery voltage resistive voltage divider setting of MCU */
#ifndef RVD_R1
# define RVD_R1 10 // Upper side resitor value (uint: KΩ)
#endif
#ifndef RVD_R2
# define RVD_R2 10 // Lower side resitor value (uint: KΩ)
#endif
/* Battery voltage resistive voltage divider setting of Bluetooth */
#ifndef LKBT51_RVD_R1
# define LKBT51_RVD_R1 560
#endif
#ifndef LKBT51_RVD_R2
# define LKBT51_RVD_R2 499
#endif
#ifndef VOLTAGE_TRIM_LED_MATRIX
# define VOLTAGE_TRIM_LED_MATRIX 30
#endif
#ifndef VOLTAGE_TRIM_RGB_MATRIX
# define VOLTAGE_TRIM_RGB_MATRIX 60
#endif
static uint32_t bat_monitor_timer_buffer = 0;
static uint16_t voltage = FULL_VOLTAGE_VALUE;
static uint8_t bat_empty = 0;
static uint8_t critical_low = 0;
static uint8_t bat_state;
static uint8_t power_on_sample = 0;
void battery_init(void) {
bat_state = BAT_NOT_CHARGING;
#if defined(BAT_CHARGING_PIN)
# if (BAT_CHARGING_LEVEL == 0)
palSetLineMode(BAT_CHARGING_PIN, PAL_MODE_INPUT_PULLUP);
# else
palSetLineMode(BAT_CHARGING_PIN, PAL_MODE_INPUT_PULLDOWN);
# endif
#endif
#ifdef BAT_ADC_ENABLE_PIN
palSetLineMode(BAT_ADC_ENABLE_PIN, PAL_MODE_OUTPUT_PUSHPULL);
gpio_write_pin(BAT_ADC_ENABLE_PIN, 1);
#endif
#ifdef BAT_ADC_PIN
palSetLineMode(BAT_ADC_PIN, PAL_MODE_INPUT_ANALOG);
#endif
}
void battery_stop(void) {
#if (HAL_USE_ADC)
# ifdef BAT_ADC_ENABLE_PIN
gpio_write_pin(BAT_ADC_ENABLE_PIN, 0);
# endif
# ifdef BAT_ADC_PIN
palSetLineMode(BAT_ADC_PIN, PAL_MODE_INPUT_ANALOG);
analog_stop(BAT_ADC_PIN);
# endif
#endif
}
__attribute__((weak)) void battery_measure(void) {
lkbt51_read_state_reg(0x05, 0x02);
}
/* Calculate the voltage */
__attribute__((weak)) void battery_calculate_voltage(bool vol_src_bt, uint16_t value) {
uint16_t voltage;
if (vol_src_bt)
voltage = ((uint32_t)value) * (LKBT51_RVD_R1 + LKBT51_RVD_R2) / LKBT51_RVD_R2;
else
voltage = (uint32_t)value * 3300 / 1024 * (RVD_R1 + RVD_R2) / RVD_R2;
#ifdef LED_MATRIX_ENABLE
if (led_matrix_is_enabled()) {
/* We assumpt it is linear relationship*/
uint32_t compensation = (VOLTAGE_TRIM_LED_MATRIX * led_matrix_driver.get_load_ratio());
voltage += compensation;
}
#endif
#ifdef RGB_MATRIX_ENABLE
if (rgb_matrix_is_enabled()) {
/* We assumpt it is linear relationship*/
uint32_t compensation = (VOLTAGE_TRIM_LED_MATRIX * rgb_matrix_driver.get_load_ratio());
voltage += compensation;
}
#endif
battery_set_voltage(voltage);
}
void battery_set_voltage(uint16_t value) {
voltage = value;
}
uint16_t battery_get_voltage(void) {
return voltage;
}
uint8_t battery_get_percentage(void) {
if (voltage > FULL_VOLTAGE_VALUE) return 100;
if (voltage > EMPTY_VOLTAGE_VALUE) {
return ((uint32_t)voltage - EMPTY_VOLTAGE_VALUE) * 80 / (FULL_VOLTAGE_VALUE - EMPTY_VOLTAGE_VALUE) + 20;
}
if (voltage > SHUTDOWN_VOLTAGE_VALUE) {
return ((uint32_t)voltage - SHUTDOWN_VOLTAGE_VALUE) * 20 / (EMPTY_VOLTAGE_VALUE - SHUTDOWN_VOLTAGE_VALUE);
} else
return 0;
}
bool battery_is_empty(void) {
return bat_empty > BATTERY_EMPTY_COUNT;
}
bool battery_is_critical_low(void) {
return critical_low > CRITICAL_LOW_COUNT;
}
void battery_check_empty(void) {
if (voltage < EMPTY_VOLTAGE_VALUE) {
if (bat_empty <= BATTERY_EMPTY_COUNT) {
if (++bat_empty > BATTERY_EMPTY_COUNT) {
indicator_battery_low_enable(true);
power_on_sample = VOLTAGE_POWER_ON_MEASURE_COUNT;
}
}
}
}
void battery_check_critical_low(void) {
if (voltage < SHUTDOWN_VOLTAGE_VALUE) {
if (critical_low <= CRITICAL_LOW_COUNT) {
if (++critical_low > CRITICAL_LOW_COUNT) wireless_low_battery_shutdown();
}
} else if (critical_low <= CRITICAL_LOW_COUNT) {
critical_low = 0;
}
}
bool battery_power_on_sample(void) {
return power_on_sample < VOLTAGE_POWER_ON_MEASURE_COUNT;
}
void battery_task(void) {
uint32_t t = rtc_timer_elapsed_ms(bat_monitor_timer_buffer);
if ((get_transport() & TRANSPORT_WIRELESS) && (wireless_get_state() == WT_CONNECTED || battery_power_on_sample())) {
#if defined(BAT_CHARGING_PIN)
if (usb_power_connected() && t > VOLTAGE_MEASURE_INTERVAL) {
if (gpio_read_pin(BAT_CHARGING_PIN) == BAT_CHARGING_LEVEL)
lkbt51_update_bat_state(BAT_CHARGING);
else
lkbt51_update_bat_state(BAT_FULL_CHARGED);
}
#endif
if ((battery_power_on_sample()
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
&& !indicator_is_enabled()
#endif
&& t > BACKLIGHT_OFF_VOLTAGE_MEASURE_INTERVAL) ||
t > VOLTAGE_MEASURE_INTERVAL) {
battery_check_empty();
battery_check_critical_low();
bat_monitor_timer_buffer = rtc_timer_read_ms();
if (bat_monitor_timer_buffer > RTC_MAX_TIME) {
bat_monitor_timer_buffer = 0;
rtc_timer_clear();
}
battery_measure();
if (power_on_sample < VOLTAGE_POWER_ON_MEASURE_COUNT) power_on_sample++;
}
}
if ((bat_empty || critical_low) && usb_power_connected()) {
bat_empty = false;
critical_low = false;
indicator_battery_low_enable(false);
}
}

View File

@@ -0,0 +1,61 @@
/*/* Copyright 2022 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
enum {
BAT_NOT_CHARGING = 0,
BAT_CHARGING,
BAT_FULL_CHARGED,
};
#ifndef FULL_VOLTAGE_VALUE
# define FULL_VOLTAGE_VALUE 4100
#endif
#ifndef EMPTY_VOLTAGE_VALUE
# define EMPTY_VOLTAGE_VALUE 3500
#endif
#ifndef SHUTDOWN_VOLTAGE_VALUE
# define SHUTDOWN_VOLTAGE_VALUE 3300
#endif
#ifndef VOLTAGE_MEASURE_INTERVAL
# define VOLTAGE_MEASURE_INTERVAL 3000
#endif
#ifndef VOLTAGE_POWER_ON_MEASURE_COUNT
# define VOLTAGE_POWER_ON_MEASURE_COUNT 15
#endif
#ifndef BACKLIGHT_OFF_VOLTAGE_MEASURE_INTERVAL
# define BACKLIGHT_OFF_VOLTAGE_MEASURE_INTERVAL 200
#endif
void battery_init(void);
void battery_stop(void);
void battery_measure(void);
void battery_calculate_voltage(bool vol_src_bt, uint16_t value);
void battery_set_voltage(uint16_t value);
uint16_t battery_get_voltage(void);
uint8_t battery_get_percentage(void);
bool battery_is_empty(void);
bool battery_is_critical_low(void);
bool battery_power_on_sample(void);
void battery_task(void);

View File

@@ -0,0 +1,770 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "indicator.h"
#include "transport.h"
#include "battery.h"
#include "eeconfig.h"
#include "wireless_config.h"
#include "config.h"
#include "rtc_timer.h"
#include "keychron_common.h"
#include "usb_main.h"
#ifdef FACTORY_TEST_ENABLE
# include "factory_test.h"
#endif
#include "lpm.h"
#include "keychron_task.h"
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
# ifdef LED_MATRIX_ENABLE
# include "led_matrix.h"
# endif
# ifdef RGB_MATRIX_ENABLE
# include "rgb_matrix.h"
# endif
# include "bat_level_animation.h"
# include "eeprom.h"
#endif
#define HOST_INDEX_MASK 0x0F
#define HOST_P2P4G 0x10
#define LED_ON 0x80
// #define RGB_MATRIX_TIMEOUT_INFINITE 0xFFFFFFFF
#ifdef LED_MATRIX_ENABLE
# define DECIDE_TIME(t, duration) (duration == 0 ? LED_MATRIX_TIMEOUT_INFINITE : ((t > duration) ? t : duration))
#endif
#ifdef RGB_MATRIX_ENABLE
# define DECIDE_TIME(t, duration) (duration == 0 ? RGB_MATRIX_TIMEOUT_INFINITE : ((t > duration) ? t : duration))
#endif
#define INDICATOR_SET(s) memcpy(&indicator_config, &s##_config, sizeof(indicator_config_t));
enum {
BACKLIGHT_OFF = 0x00,
BACKLIGHT_ON_CONNECTED = 0x01,
BACKLIGHT_ON_UNCONNECTED = 0x02,
};
static indicator_config_t pairing_config = INDICATOR_CONFIG_PARING;
static indicator_config_t connected_config = INDICATOR_CONFIG_CONNECTD;
static indicator_config_t reconnecting_config = INDICATOR_CONFIG_RECONNECTING;
static indicator_config_t disconnected_config = INDICATOR_CONFIG_DISCONNECTED;
indicator_config_t indicator_config;
static wt_state_t indicator_state;
static uint16_t next_period;
static indicator_type_t type;
static uint32_t indicator_timer_buffer = 0;
#if defined(BAT_LOW_LED_PIN) || defined(SPACE_KEY_LOW_BAT_IND)
static uint32_t bat_low_backlit_indicator = 0;
static uint8_t bat_low_ind_state = 0;
static uint32_t rtc_time = 0;
#endif
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
backlight_state_t original_backlight_state;
# ifdef BT_INDCATION_LED_MATRIX_LIST
static uint8_t bt_ind_led_matrix_list[BT_HOST_DEVICES_COUNT] = BT_INDCATION_LED_MATRIX_LIST;
# endif
#endif
#ifdef BT_INDICATION_LED_PIN_LIST
static pin_t bt_led_pin_list[BT_HOST_DEVICES_COUNT] = BT_INDICATION_LED_PIN_LIST;
#endif
#ifdef P24G_INDICATION_LED_PIN_LIST
static pin_t p24g_led_pin_list[P24G_HOST_DEVICES_COUNT] = P24G_INDICATION_LED_PIN_LIST;
#endif
#ifdef LED_MATRIX_ENABLE
# define LED_DRIVER led_matrix_driver
# define LED_INDICATORS_KB led_matrix_indicators_bt
# define LED_INDICATORS_USER led_matrix_indicators_user
# define LED_NONE_INDICATORS_KB led_matrix_none_indicators_kb
# define SET_ALL_LED_OFF() led_matrix_set_value_all(0)
# define SET_LED_OFF(idx) led_matrix_set_value(idx, 0)
# define SET_LED_ON(idx) led_matrix_set_value(idx, 255)
# define SET_LED_BT(idx) led_matrix_set_value(idx, 255)
# define SET_LED_P24G(idx) led_matrix_set_value(idx, 255)
# define SET_LED_LOW_BAT(idx) led_matrix_set_value(idx, 255)
# define LED_DRIVER_IS_ENABLED led_matrix_is_enabled
# define LED_DRIVER_EECONFIG_RELOAD() \
eeprom_read_block(&led_matrix_eeconfig, EECONFIG_LED_MATRIX, sizeof(led_matrix_eeconfig)); \
if (!led_matrix_eeconfig.mode) { \
eeconfig_update_led_matrix_default(); \
}
# define LED_DRIVER_ALLOW_SHUTDOWN led_matrix_driver_allow_shutdown
# define LED_DRIVER_SHUTDOWN led_matrix_driver_shutdown
# define LED_DRIVER_EXIT_SHUTDOWN led_matrix_driver_exit_shutdown
# define LED_DRIVER_ENABLE_NOEEPROM led_matrix_enable_noeeprom
# define LED_DRIVER_DISABLE_NOEEPROM led_matrix_disable_noeeprom
# define LED_DRIVER_DISABLE_TIMEOUT_SET led_matrix_disable_timeout_set
# define LED_DRIVER_DISABLE_TIME_RESET led_matrix_disable_time_reset
# define LED_DRIVER_TIMEOUTED led_matrix_timeouted
#endif
#ifdef RGB_MATRIX_ENABLE
# define LED_DRIVER rgb_matrix_driver
# define LED_INDICATORS_KB rgb_matrix_indicators_bt
# define LED_INDICATORS_USER rgb_matrix_indicators_user
# define LED_NONE_INDICATORS_KB rgb_matrix_none_indicators_kb
# define SET_ALL_LED_OFF() rgb_matrix_set_color_all(0, 0, 0)
# define SET_LED_OFF(idx) rgb_matrix_set_color(idx, 0, 0, 0)
# define SET_LED_ON(idx) rgb_matrix_set_color(idx, 255, 255, 255)
# define SET_LED_BT(idx) rgb_matrix_set_color(idx, 0, 0, 255)
# define SET_LED_P24G(idx) rgb_matrix_set_color(idx, 0, 255, 0)
# define SET_LED_LOW_BAT(idx) rgb_matrix_set_color(idx, 255, 0, 0)
# define LED_DRIVER_IS_ENABLED rgb_matrix_is_enabled
# define LED_DRIVER_EECONFIG_RELOAD() \
eeprom_read_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config)); \
if (!rgb_matrix_config.mode) { \
eeconfig_update_rgb_matrix_default(); \
}
# define LED_DRIVER_ALLOW_SHUTDOWN rgb_matrix_driver_allow_shutdown
# define LED_DRIVER_SHUTDOWN rgb_matrix_driver_shutdown
# define LED_DRIVER_EXIT_SHUTDOWN rgb_matrix_driver_exit_shutdown
# define LED_DRIVER_ENABLE_NOEEPROM rgb_matrix_enable_noeeprom
# define LED_DRIVER_DISABLE_NOEEPROM rgb_matrix_disable_noeeprom
# define LED_DRIVER_DISABLE_TIMEOUT_SET rgb_matrix_disable_timeout_set
# define LED_DRIVER_DISABLE_TIME_RESET rgb_matrix_disable_time_reset
# define LED_DRIVER_TIMEOUTED rgb_matrix_timeouted
#endif
bool LED_INDICATORS_KB(void);
void indicator_init(void) {
memset(&indicator_config, 0, sizeof(indicator_config));
#if defined(BT_INDICATION_LED_PIN_LIST)
for (uint8_t i = 0; i < BT_HOST_DEVICES_COUNT; i++) {
gpio_set_pin_output_push_pull(bt_led_pin_list[i]);
gpio_write_pin(bt_led_pin_list[i], !BT_INDICATION_LED_ON_STATE);
}
#endif
#ifdef P24G_INDICATION_LED_PIN_LIST
for (uint8_t i = 0; i < P24G_HOST_DEVICES_COUNT; i++) {
gpio_set_pin_output_push_pull(p24g_led_pin_list[i]);
gpio_write_pin(p24g_led_pin_list[i], !BT_INDICATION_LED_ON_STATE);
}
#endif
#ifdef COMMON_BT_LED_PIN
gpio_set_pin_output_push_pull(COMMON_BT_LED_PIN);
gpio_write_pin(COMMON_BT_LED_PIN, !COMMON_BT_LED_PIN_ON_STATE);
#endif
#ifdef COMMON_P24G_LED_PIN
gpio_set_pin_output_push_pull(COMMON_P24G_LED_PIN);
gpio_write_pin(COMMON_P24G_LED_PIN, !COMMON_BT_LED_PIN_ON_STATE);
#endif
#ifdef BAT_LOW_LED_PIN
# ifdef POWER_ON_LED_DURATION
if (timer_read32() > POWER_ON_LED_DURATION)
# endif
{
gpio_set_pin_output_push_pull(BAT_LOW_LED_PIN);
gpio_write_pin(BAT_LOW_LED_PIN, !BAT_LOW_LED_PIN_ON_STATE);
}
#endif
}
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
void indicator_enable(void) {
if (!LED_DRIVER_IS_ENABLED()) {
LED_DRIVER_ENABLE_NOEEPROM();
}
}
inline void indicator_disable(void) {
LED_DRIVER_DISABLE_NOEEPROM();
}
void indicator_set_backlit_timeout(uint32_t time) {
LED_DRIVER_DISABLE_TIMEOUT_SET(time);
}
static inline void indicator_reset_backlit_time(void) {
LED_DRIVER_DISABLE_TIME_RESET();
}
bool indicator_is_enabled(void) {
return LED_DRIVER_IS_ENABLED();
}
void indicator_eeconfig_reload(void) {
LED_DRIVER_EECONFIG_RELOAD();
}
#endif
bool indicator_is_running(void) {
return
#if defined(BAT_LOW_LED_PIN) || defined(SPACE_KEY_LOW_BAT_IND)
bat_low_ind_state ||
#endif
!!indicator_config.value;
}
static void indicator_timer_cb(void *arg) {
if (*(indicator_type_t *)arg != INDICATOR_LAST) type = *(indicator_type_t *)arg;
bool time_up = false;
switch (type) {
case INDICATOR_NONE:
break;
case INDICATOR_OFF:
next_period = 0;
time_up = true;
break;
case INDICATOR_ON:
if (indicator_config.value) {
if (indicator_config.elapsed == 0) {
indicator_config.value |= LED_ON;
if (indicator_config.duration) {
indicator_config.elapsed += indicator_config.duration;
}
} else
time_up = true;
}
break;
case INDICATOR_ON_OFF:
if (indicator_config.value) {
if (indicator_config.elapsed == 0) {
indicator_config.value |= LED_ON;
next_period = indicator_config.on_time;
} else {
indicator_config.value = indicator_config.value & 0x1F;
next_period = indicator_config.duration - indicator_config.on_time;
}
if ((indicator_config.duration == 0 || indicator_config.elapsed <= indicator_config.duration) && next_period != 0) {
indicator_config.elapsed += next_period;
} else {
time_up = true;
}
}
break;
case INDICATOR_BLINK:
if (indicator_config.value) {
if (indicator_config.value & LED_ON) {
indicator_config.value = indicator_config.value & 0x1F;
next_period = indicator_config.off_time;
} else {
indicator_config.value |= LED_ON;
next_period = indicator_config.on_time;
}
if ((indicator_config.duration == 0 || indicator_config.elapsed <= indicator_config.duration) && next_period != 0) {
indicator_config.elapsed += next_period;
} else {
time_up = true;
}
}
break;
default:
time_up = true;
next_period = 0;
break;
}
#if defined(BT_INDICATION_LED_PIN_LIST) || defined(P24G_INDICATION_LED_PIN_LIST) || defined(COMMON_BT_LED_PIN) || defined(COMMON_P24G_LED_PIN)
if (indicator_config.value) {
uint8_t idx = (indicator_config.value & HOST_INDEX_MASK) - 1;
# if defined(BT_INDICATION_LED_PIN_LIST) || defined(P24G_INDICATION_LED_PIN_LIST)
pin_t *led_lin_list = NULL;
# endif
# if defined(COMMON_BT_LED_PIN) || defined(COMMON_P24G_LED_PIN)
pin_t led_pin = NO_PIN;
# endif
uint8_t led_count;
# if defined(P24G_INDICATION_LED_PIN_LIST) || defined(COMMON_P24G_LED_PIN)
if (indicator_config.value & HOST_P2P4G) {
if (idx < P24G_HOST_DEVICES_COUNT) {
# if defined(P24G_INDICATION_LED_PIN_LIST)
led_lin_list = p24g_led_pin_list;
# endif
# if defined(COMMON_P24G_LED_PIN)
led_pin = COMMON_P24G_LED_PIN;
# endif
}
led_count = P24G_HOST_DEVICES_COUNT;
} else
# endif
{
if (idx < BT_HOST_DEVICES_COUNT) {
# if defined(BT_INDICATION_LED_PIN_LIST)
led_lin_list = bt_led_pin_list;
# endif
# if defined(COMMON_BT_LED_PIN)
led_pin = COMMON_BT_LED_PIN;
# endif
}
led_count = BT_HOST_DEVICES_COUNT;
}
#if defined(BT_INDICATION_LED_PIN_LIST) || defined(P24G_INDICATION_LED_PIN_LIST)
for (uint8_t i = 0; i < led_count; i++) {
if (i != idx) {
if (led_lin_list) gpio_write_pin(led_lin_list[idx], !BT_INDICATION_LED_ON_STATE);
}
}
#endif
if ((indicator_config.value & LED_ON) && !time_up) {
if (led_lin_list) gpio_write_pin(led_lin_list[idx], BT_INDICATION_LED_ON_STATE);
# if defined(COMMON_BT_LED_PIN) || defined(COMMON_P24G_LED_PIN)
if (led_pin != NO_PIN) gpio_write_pin(led_pin, COMMON_BT_LED_PIN_ON_STATE);
# endif
} else {
if (led_lin_list) gpio_write_pin(led_lin_list[idx], !BT_INDICATION_LED_ON_STATE);
# if defined(COMMON_BT_LED_PIN) || defined(COMMON_P24G_LED_PIN)
if (led_pin != NO_PIN) gpio_write_pin(led_pin, !COMMON_BT_LED_PIN_ON_STATE);
# endif
}
}
#endif
if (time_up) {
/* Set indicator to off on timeup, avoid keeping light up until next update in raindrop effect */
indicator_config.value = indicator_config.value & 0x1F;
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
LED_INDICATORS_KB();
#endif
indicator_config.value = 0;
lpm_timer_reset();
}
if (indicator_config.value == 0) {
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
indicator_eeconfig_reload();
if (!LED_DRIVER_IS_ENABLED()) indicator_disable();
#endif
}
}
void indicator_set(wt_state_t state, uint8_t host_index) {
if (get_transport() == TRANSPORT_USB) return;
static uint8_t pre_state = 0;
static uint8_t current_state = 0;
static uint8_t current_host = 0;
bool host_index_changed = false;
if (host_index == 24) host_index = HOST_P2P4G | 0x01;
if (current_host != host_index && state != WT_DISCONNECTED) {
host_index_changed = true;
current_host = host_index;
}
if (current_state != state || host_index_changed || state == WT_RECONNECTING) {
// Some BT chips need to reset to enter sleep mode, ignore it.
if (current_state == WT_SUSPEND && state == WT_DISCONNECTED) return;
pre_state = current_state;
current_state = state;
(void)pre_state;
} else {
return;
}
indicator_timer_buffer = timer_read32();
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
/* Turn on backlight mode for indicator */
indicator_enable();
indicator_reset_backlit_time();
#endif
switch (state) {
case WT_DISCONNECTED:
#if defined(BT_INDICATION_LED_PIN_LIST)
if ((host_index & HOST_P2P4G) != HOST_P2P4G) gpio_write_pin(bt_led_pin_list[(host_index & HOST_INDEX_MASK) - 1], !BT_INDICATION_LED_ON_STATE);
#endif
#if defined(P24G_INDICATION_LED_PIN_LIST)
if (host_index & HOST_P2P4G) gpio_write_pin(p24g_led_pin_list[(host_index & HOST_INDEX_MASK) - 1], !BT_INDICATION_LED_ON_STATE);
#endif
#ifdef COMMON_BT_LED_PIN
gpio_write_pin(COMMON_BT_LED_PIN, !COMMON_BT_LED_PIN_ON_STATE);
#endif
#ifdef COMMON_P24G_LED_PIN
gpio_write_pin(COMMON_P24G_LED_PIN, !COMMON_BT_LED_PIN_ON_STATE);
#endif
INDICATOR_SET(disconnected);
indicator_config.value = (indicator_config.type == INDICATOR_NONE) ? 0 : host_index;
indicator_timer_cb((void *)&indicator_config.type);
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
if (battery_is_critical_low()) {
indicator_set_backlit_timeout(1000);
} else {
if (pre_state == WT_CONNECTED)
indicator_set_backlit_timeout(1000);
else
/* Set timer so that user has chance to turn on the backlight when is off */
indicator_set_backlit_timeout(DECIDE_TIME(DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT * 1000, indicator_config.duration));
}
#endif
break;
case WT_CONNECTED:
if (indicator_state != WT_CONNECTED) {
INDICATOR_SET(connected);
indicator_config.value = (indicator_config.type == INDICATOR_NONE) ? 0 : host_index;
indicator_timer_cb((void *)&indicator_config.type);
}
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
indicator_set_backlit_timeout(DECIDE_TIME(CONNECTED_BACKLIGHT_DISABLE_TIMEOUT * 1000, indicator_config.duration));
#endif
break;
case WT_PARING:
INDICATOR_SET(pairing);
indicator_config.value = (indicator_config.type == INDICATOR_NONE) ? 0 : LED_ON | host_index;
indicator_timer_cb((void *)&indicator_config.type);
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
indicator_set_backlit_timeout(DECIDE_TIME(DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT * 1000, indicator_config.duration));
#endif
break;
case WT_RECONNECTING:
INDICATOR_SET(reconnecting);
indicator_config.value = (indicator_config.type == INDICATOR_NONE) ? 0 : LED_ON | host_index;
indicator_timer_cb((void *)&indicator_config.type);
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
indicator_set_backlit_timeout(DECIDE_TIME(DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT * 1000, indicator_config.duration));
#endif
break;
case WT_SUSPEND:
INDICATOR_SET(disconnected);
indicator_config.value = (indicator_config.type == INDICATOR_NONE) ? 0 : host_index;
indicator_timer_cb((void *)&indicator_config.type);
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
# ifdef FACTORY_TEST_ENABLE
if (factory_reset_indicating())
indicator_set_backlit_timeout(3000);
else
# endif
{
indicator_set_backlit_timeout(1000);
}
#endif
#if defined(BT_INDICATION_LED_PIN_LIST)
for (uint8_t i = 0; i < BT_HOST_DEVICES_COUNT; i++) gpio_write_pin(bt_led_pin_list[i], !BT_INDICATION_LED_ON_STATE);
#endif
#if defined(P24G_INDICATION_LED_PIN_LIST)
for (uint8_t i = 0; i < P24G_HOST_DEVICES_COUNT; i++) gpio_write_pin(p24g_led_pin_list[i], !BT_INDICATION_LED_ON_STATE);
#endif
#ifdef COMMON_BT_LED_PIN
gpio_write_pin(COMMON_BT_LED_PIN, !COMMON_BT_LED_PIN_ON_STATE);
#endif
#ifdef COMMON_P24G_LED_PIN
gpio_write_pin(COMMON_P24G_LED_PIN, !COMMON_BT_LED_PIN_ON_STATE);
#endif
break;
default:
break;
}
indicator_state = state;
}
void indicator_stop(void) {
indicator_config.value = 0;
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
indicator_eeconfig_reload();
if (indicator_is_enabled()) {
indicator_enable();
} else {
indicator_disable();
}
#endif
}
void indicator_battery_low_enable(bool enable) {
#if defined(BAT_LOW_LED_PIN) || defined(SPACE_KEY_LOW_BAT_IND)
if (enable) {
uint32_t t = rtc_timer_read_ms();
/* Check overflow */
if (rtc_time > t) {
if (bat_low_ind_state == 0)
rtc_time = t; // Update rtc_time if indicating is not running
else {
rtc_time += t;
}
}
/* Indicating at first time or after the interval */
if ((rtc_time == 0 || t - rtc_time > LOW_BAT_LED_TRIG_INTERVAL) && bat_low_ind_state == 0) {
bat_low_backlit_indicator = enable ? timer_read32() : 0;
rtc_time = rtc_timer_read_ms();
bat_low_ind_state = 1;
# if defined(SPACE_KEY_LOW_BAT_IND)
indicator_enable();
# endif
}
} else {
rtc_time = 0;
bat_low_ind_state = 0;
# if defined(SPACE_KEY_LOW_BAT_IND)
indicator_eeconfig_reload();
if (!LED_DRIVER_IS_ENABLED()) indicator_disable();
# endif
}
#endif
}
void indicator_battery_low(void) {
#if defined(BAT_LOW_LED_PIN) || defined(SPACE_KEY_LOW_BAT_IND)
if (bat_low_ind_state) {
if ((bat_low_ind_state & 0x0F) <= (LOW_BAT_LED_BLINK_TIMES) && timer_elapsed32(bat_low_backlit_indicator) > (LOW_BAT_LED_BLINK_PERIOD)) {
if (bat_low_ind_state & 0x80) {
bat_low_ind_state &= 0x7F;
bat_low_ind_state++;
# if defined(BAT_LOW_LED_PIN)
gpio_write_pin(BAT_LOW_LED_PIN, !BAT_LOW_LED_PIN_ON_STATE);
# endif
} else {
bat_low_ind_state |= 0x80;
# if defined(BAT_LOW_LED_PIN)
gpio_write_pin(BAT_LOW_LED_PIN, BAT_LOW_LED_PIN_ON_STATE);
# endif
}
bat_low_backlit_indicator = timer_read32();
/* Restore backligth state */
if ((bat_low_ind_state & 0x0F) > (LOW_BAT_LED_BLINK_TIMES)) {
# if defined(BAT_LOW_LED_PIN)
gpio_write_pin(BAT_LOW_LED_PIN, !BAT_LOW_LED_PIN_ON_STATE);
# endif
# if defined(SPACE_KEY_LOW_BAT_IND)
# if defined(NUM_LOCK_INDEX) || defined(CAPS_LOCK_INDEX) || defined(SCROLL_LOCK_INDEX) || defined(COMPOSE_LOCK_INDEX) || defined(KANA_LOCK_INDEX)
if (LED_DRIVER_ALLOW_SHUTDOWN())
# endif
indicator_disable();
# endif
}
} else if ((bat_low_ind_state & 0x0F) > (LOW_BAT_LED_BLINK_TIMES)) {
# if defined(BAT_LOW_LED_PIN)
gpio_write_pin(BAT_LOW_LED_PIN, !BAT_LOW_LED_PIN_ON_STATE);
# endif
bat_low_ind_state = 0;
lpm_timer_reset();
}
}
#endif
}
void indicator_task(void) {
#if defined(BAT_LEVEL_LED_LIST)
bat_level_animiation_task();
#endif
if (indicator_config.value && timer_elapsed32(indicator_timer_buffer) >= next_period) {
indicator_timer_cb((void *)&type);
indicator_timer_buffer = timer_read32();
}
indicator_battery_low();
}
#if defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)
__attribute__((weak)) void os_state_indicate(void) {
# if defined(RGB_MATRIX_SLEEP) || defined(LED_MATRIX_SLEEP)
if (get_transport() == TRANSPORT_USB && USB_DRIVER.state == USB_SUSPENDED) return;
# endif
# if defined(NUM_LOCK_INDEX)
if (host_keyboard_led_state().num_lock) {
# if defined(DIM_NUM_LOCK)
SET_LED_OFF(NUM_LOCK_INDEX);
# else
SET_LED_ON(NUM_LOCK_INDEX);
# endif
}
# endif
# if defined(CAPS_LOCK_INDEX)
if (host_keyboard_led_state().caps_lock) {
# if defined(DIM_CAPS_LOCK)
SET_LED_OFF(CAPS_LOCK_INDEX);
# else
SET_LED_ON(CAPS_LOCK_INDEX);
# endif
}
# endif
# if defined(SCROLL_LOCK_INDEX)
if (host_keyboard_led_state().scroll_lock) {
SET_LED_ON(SCROLL_LOCK_INDEX);
}
# endif
# if defined(COMPOSE_LOCK_INDEX)
if (host_keyboard_led_state().compose) {
SET_LED_ON(COMPOSE_LOCK_INDEX);
}
# endif
# if defined(KANA_LOCK_INDEX)
if (host_keyboard_led_state().kana) {
SET_LED_ON(KANA_LOCK_INDEX);
}
# endif
}
bool LED_INDICATORS_KB(void) {
if (get_transport() & TRANSPORT_WIRELESS) {
/* Prevent backlight flash caused by key activities */
if (battery_is_critical_low()) {
SET_ALL_LED_OFF();
return true;
}
if (battery_is_empty()) SET_ALL_LED_OFF();
# if defined(LOW_BAT_IND_INDEX)
if (bat_low_ind_state && (bat_low_ind_state & 0x0F) <= LOW_BAT_LED_BLINK_TIMES) {
uint8_t idx_list[] = LOW_BAT_IND_INDEX;
for (uint8_t i = 0; i < sizeof(idx_list); i++) {
if (bat_low_ind_state & LED_ON) {
SET_LED_LOW_BAT(idx_list[i]);
} else {
SET_LED_OFF(idx_list[i]);
}
}
}
# endif
# if (defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)) && defined(BAT_LEVEL_LED_LIST)
if (bat_level_animiation_actived()) {
bat_level_animiation_indicate();
}
# endif
static uint8_t last_host_index = 0xFF;
if (indicator_config.value) {
uint8_t host_index = indicator_config.value & HOST_INDEX_MASK;
if (indicator_config.highlight) {
SET_ALL_LED_OFF();
} else if (last_host_index != host_index) {
# ifdef P24G_INDICATION_LED_MATRIX_INDEX
if (indicator_config.value & HOST_P2P4G)
SET_LED_OFF(P24G_INDICATION_LED_MATRIX_INDEX);
else
# endif
# ifdef BT_INDCATION_LED_MATRIX_LIST
SET_LED_OFF(bt_ind_led_matrix_list[host_index - 1]);
# endif
last_host_index = host_index;
}
if (indicator_config.value & LED_ON) {
# ifdef P24G_INDICATION_LED_MATRIX_INDEX
if (indicator_config.value & HOST_P2P4G)
SET_LED_P24G(P24G_INDICATION_LED_MATRIX_INDEX);
else
# endif
# ifdef BT_INDCATION_LED_MATRIX_LIST
SET_LED_BT(bt_ind_led_matrix_list[host_index - 1]);
# endif
} else {
# ifdef P24G_INDICATION_LED_MATRIX_INDEX
if (indicator_config.value & HOST_P2P4G)
SET_LED_OFF(P24G_INDICATION_LED_MATRIX_INDEX);
else
# endif
# ifdef BT_INDCATION_LED_MATRIX_LIST
SET_LED_OFF(bt_ind_led_matrix_list[host_index - 1]);
# endif
}
} else
os_state_indicate();
} else
os_state_indicate();
if (!LED_INDICATORS_USER()) return true;
return true;
}
bool led_update_kb(led_t led_state) {
bool res = led_update_user(led_state);
if (res) {
led_update_ports(led_state);
if (!LED_DRIVER_IS_ENABLED() || (LED_DRIVER_IS_ENABLED() && LED_DRIVER_TIMEOUTED())) {
# if defined(LED_MATRIX_DRIVER_SHUTDOWN_ENABLE) || defined(RGB_MATRIX_DRIVER_SHUTDOWN_ENABLE)
LED_DRIVER_EXIT_SHUTDOWN();
# endif
SET_ALL_LED_OFF();
os_state_indicate();
LED_DRIVER.flush();
# if defined(LED_MATRIX_DRIVER_SHUTDOWN_ENABLE) || defined(RGB_MATRIX_DRIVER_SHUTDOWN_ENABLE)
if (LED_DRIVER_ALLOW_SHUTDOWN()) LED_DRIVER_SHUTDOWN();
# endif
}
}
return res;
}
void LED_NONE_INDICATORS_KB(void) {
# if defined(RGB_MATRIX_SLEEP) || defined(LED_MATRIX_SLEEP)
if (get_transport() == TRANSPORT_USB && USB_DRIVER.state == USB_SUSPENDED) return;
# endif
os_state_indicate();
}
# if defined(LED_MATRIX_DRIVER_SHUTDOWN_ENABLE) || defined(RGB_MATRIX_DRIVER_SHUTDOWN_ENABLE)
bool LED_DRIVER_ALLOW_SHUTDOWN(void) {
# if defined(NUM_LOCK_INDEX)
if (host_keyboard_led_state().num_lock) return false;
# endif
# if defined(CAPS_LOCK_INDEX) && !defined(DIM_CAPS_LOCK)
if (host_keyboard_led_state().caps_lock) return false;
# endif
# if defined(SCROLL_LOCK_INDEX)
if (host_keyboard_led_state().scroll_lock) return false;
# endif
# if defined(COMPOSE_LOCK_INDEX)
if (host_keyboard_led_state().compose) return false;
# endif
# if defined(KANA_LOCK_INDEX)
if (host_keyboard_led_state().kana) return false;
# endif
return true;
}
# endif
#endif

View File

@@ -0,0 +1,114 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "config.h"
#include "wireless.h"
/* Indication of pairing */
#ifndef INDICATOR_CONFIG_PARING
# define INDICATOR_CONFIG_PARING {INDICATOR_BLINK, 1000, 1000, 0, true, 0};
#endif
/* Indication on Connected */
#ifndef INDICATOR_CONFIG_CONNECTD
# define INDICATOR_CONFIG_CONNECTD {INDICATOR_ON_OFF, 2000, 250, 2000, true, 0};
#endif
/* Reconnecting indication */
#ifndef INDICATOR_CONFIG_RECONNECTING
# define INDICATOR_CONFIG_RECONNECTING {INDICATOR_BLINK, 100, 100, 600, true, 0};
#endif
/* Disconnected indication */
#ifndef INDICATOR_CONFIG_DISCONNECTED
# define INDICATOR_CONFIG_DISCONNECTED {INDICATOR_NONE, 100, 100, 600, false, 0};
#endif
/* Uint: Second */
#ifndef DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT
# define DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT 40
#endif
/* Uint: Second, the timer restarts on key activities. */
#ifndef CONNECTED_BACKLIGHT_DISABLE_TIMEOUT
# define CONNECTED_BACKLIGHT_DISABLE_TIMEOUT 600
#endif
/* Uint: ms */
#ifndef LOW_BAT_LED_BLINK_PERIOD
# define LOW_BAT_LED_BLINK_PERIOD 1000
#endif
#ifndef LOW_BAT_LED_BLINK_TIMES
# define LOW_BAT_LED_BLINK_TIMES 5
#endif
#ifndef LOW_BAT_LED_TRIG_INTERVAL
# define LOW_BAT_LED_TRIG_INTERVAL 30000
#endif
#if ((defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)) && defined(LOW_BAT_IND_INDEX))
# define SPACE_KEY_LOW_BAT_IND
#endif
#if BT_HOST_MAX_COUNT > 6
# pragma error("HOST_COUNT max value is 6")
#endif
#ifndef P24G_HOST_DEVICES_COUNT
# define P24G_HOST_DEVICES_COUNT 1
#endif
typedef enum {
INDICATOR_NONE,
INDICATOR_OFF,
INDICATOR_ON,
INDICATOR_ON_OFF,
INDICATOR_BLINK,
INDICATOR_LAST,
} indicator_type_t;
typedef struct {
indicator_type_t type;
uint32_t on_time;
uint32_t off_time;
uint32_t duration;
bool highlight;
uint8_t value;
uint32_t elapsed;
} indicator_config_t;
typedef struct {
uint8_t value;
bool saved;
} backlight_state_t;
void indicator_init(void);
void indicator_set(wt_state_t state, uint8_t host_index);
void indicator_set_backlit_timeout(uint32_t time);
void indicator_backlight_timer_reset(bool enable);
bool indicator_hook_key(uint16_t keycode);
void indicator_enable(void);
void indicator_disable(void);
void indicator_stop(void);
void indicator_eeconfig_reload(void);
bool indicator_is_enabled(void);
bool indicator_is_running(void);
void indicator_battery_low_enable(bool enable);
void indicator_task(void);

View File

@@ -0,0 +1,171 @@
/* Copyright 2022 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#ifdef LK_WIRELESS_ENABLE
# include "lkbt51.h"
# include "wireless.h"
# include "indicator.h"
# include "transport.h"
# include "battery.h"
# include "bat_level_animation.h"
# include "lpm.h"
# include "keychron_wireless_common.h"
# include "keychron_task.h"
#endif
#include "keychron_common.h"
bool firstDisconnect = true;
static uint32_t pairing_key_timer;
static uint8_t host_idx = 0;
bool process_record_keychron_wireless(uint16_t keycode, keyrecord_t *record) {
static uint8_t host_idx;
switch (keycode) {
case BT_HST1 ... BT_HST3:
if (get_transport() == TRANSPORT_BLUETOOTH) {
if (record->event.pressed) {
host_idx = keycode - BT_HST1 + 1;
pairing_key_timer = timer_read32();
wireless_connect_ex(host_idx, 0);
} else {
host_idx = 0;
pairing_key_timer = 0;
}
}
break;
case P2P4G:
if (get_transport() == TRANSPORT_P2P4) {
if (record->event.pressed) {
host_idx = P24G_INDEX;
pairing_key_timer = timer_read32();
} else {
host_idx = 0;
pairing_key_timer = 0;
}
}
break;
#if (defined(LED_MATRIX_ENABLE) || defined(RGB_MATRIX_ENABLE)) && defined(BAT_LEVEL_LED_LIST)
case BAT_LVL:
if ((get_transport() & TRANSPORT_WIRELESS) && !usb_power_connected()) {
bat_level_animiation_start(battery_get_percentage());
}
break;
#endif
default:
break;
}
return true;
}
void lkbt51_param_init(void) {
/* Set bluetooth device name */
lkbt51_set_local_name(PRODUCT);
wait_ms(3);
// clang-format off
/* Set bluetooth parameters */
module_param_t param = {.event_mode = 0x02,
.connected_idle_timeout = 7200,
.pairing_timeout = 180,
.pairing_mode = 0,
.reconnect_timeout = 5,
.report_rate = 90,
.vendor_id_source = 1,
.verndor_id = 0x3434, // Must be 0x3434
.product_id = PRODUCT_ID};
// clang-format on
lkbt51_set_param(&param);
}
void wireless_enter_reset_kb(uint8_t reason) {
lkbt51_param_init();
}
void wireless_enter_disconnected_kb(uint8_t host_idx, uint8_t reason) {
/* CKBT51 bluetooth module boot time is slower, it enters disconnected after boot,
so we place initialization here. */
if (firstDisconnect && timer_read32() < 1000) {
lkbt51_param_init();
if (get_transport() == TRANSPORT_BLUETOOTH) wireless_connect();
firstDisconnect = false;
}
}
void keychron_wireless_common_task(void) {
if (pairing_key_timer) {
if (timer_elapsed32(pairing_key_timer) > 2000) {
pairing_key_timer = 0;
wireless_pairing_ex(host_idx, NULL);
}
}
}
void wireless_pre_task(void) {
static uint8_t dip_switch_state = 0;
static uint32_t time = 0;
if (time == 0) {
uint8_t pins_state = (gpio_read_pin(BT_MODE_SELECT_PIN) << 1)
# ifdef P24G_MODE_SELECT_PIN
| gpio_read_pin(P24G_MODE_SELECT_PIN)
# endif
;
if (pins_state != dip_switch_state) {
dip_switch_state = pins_state;
time = timer_read32();
}
}
if ((time && timer_elapsed32(time) > 100) || get_transport() == TRANSPORT_NONE) {
uint8_t pins_state = (gpio_read_pin(BT_MODE_SELECT_PIN) << 1)
# ifdef P24G_MODE_SELECT_PIN
| gpio_read_pin(P24G_MODE_SELECT_PIN)
# endif
;
if (pins_state == dip_switch_state) {
time = 0;
switch (dip_switch_state) {
# ifndef P24G_MODE_SELECT_PIN
case 0x00:
set_transport(TRANSPORT_BLUETOOTH);
break;
# else
case 0x01:
set_transport(TRANSPORT_BLUETOOTH);
break;
case 0x02:
set_transport(TRANSPORT_P2P4);
break;
# endif
default:
set_transport(TRANSPORT_USB);
break;
}
} else {
dip_switch_state = pins_state;
time = timer_read32();
}
}
}

View File

@@ -0,0 +1,26 @@
/* Copyright 2023 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdint.h"
#ifdef VIA_ENABLE
# include "via.h"
#endif
#include "quantum_keycodes.h"
void lkbt51_param_init(void);
bool process_record_keychron_wireless(uint16_t keycode, keyrecord_t *record);
void keychron_wireless_common_task(void);

View File

@@ -0,0 +1,885 @@
/* Copyright 2023 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "lkbt51.h"
#include "wireless.h"
#include "wireless_event_type.h"
#include "battery.h"
#include "raw_hid.h"
#include "report_buffer.h"
#include "factory_test.h"
extern void factory_test_send(uint8_t* payload, uint8_t length);
#ifndef RAW_EPSIZE
# define RAW_EPSIZE 32
#endif
#ifndef SPI_SCK_PIN
# define SPI_SCK_PIN A5
#endif
#ifndef SPI_MISO_PIN
# define SPI_MISO_PIN A6
#endif
#ifndef SPI_MOSI_PIN
# define SPI_MOSI_PIN A7
#endif
#ifndef SPI_CLK_PAL_MODE
# define SPI_CLK_PAL_MODE 5
#endif
#ifndef SPI_MISO_PAL_MODE
# define SPI_MISO_PAL_MODE 5
#endif
#ifndef SPI_MOSI_PAL_MODE
# define SPI_MOSI_PAL_MODE 5
#endif
#ifndef LKBT51_INT_INPUT_PIN
# error "LKBT51_INT_INPUT_PIN is not defined"
#endif
#ifndef LKBT51_TX_RETRY_COUNT
# define LKBT51_TX_RETRY_COUNT 3
#endif
// clang-format off
enum {
/* HID Report */
LKBT51_CMD_SEND_KB = 0x11,
LKBT51_CMD_SEND_KB_NKRO = 0x12,
LKBT51_CMD_SEND_CONSUMER = 0x13,
LKBT51_CMD_SEND_SYSTEM = 0x14,
LKBT51_CMD_SEND_FN = 0x15, // Not used currently
LKBT51_CMD_SEND_MOUSE = 0x16,
LKBT51_CMD_SEND_BOOT_KB = 0x17,
/* Bluetooth connections */
LKBT51_CMD_PAIRING = 0x21,
LKBT51_CMD_CONNECT = 0x22,
LKBT51_CMD_DISCONNECT = 0x23,
LKBT51_CMD_SWITCH_HOST = 0x24,
LKBT51_CMD_READ_STATE_REG = 0x25,
/* Battery */
LKBT51_CMD_BATTERY_MANAGE = 0x31,
LKBT51_CMD_UPDATE_BAT_LVL = 0x32,
LKBT51_CMD_UPDATE_BAT_STATE = 0x33,
/* Set/get parameters */
LKBT51_CMD_GET_MODULE_INFO = 0x40,
LKBT51_CMD_SET_CONFIG = 0x41,
LKBT51_CMD_GET_CONFIG = 0x42,
LKBT51_CMD_SET_BDA = 0x43,
LKBT51_CMD_GET_BDA = 0x44,
LKBT51_CMD_SET_NAME = 0x45,
LKBT51_CMD_GET_NAME = 0x46,
LKBT51_CMD_WRTE_CSTM_DATA = 0x49,
/* DFU */
LKBT51_CMD_GET_DFU_VER = 0x60,
LKBT51_CMD_HAND_SHAKE_TOKEN = 0x61,
LKBT51_CMD_START_DFU = 0x62,
LKBT51_CMD_SEND_FW_DATA = 0x63,
LKBT51_CMD_VERIFY_CRC32 = 0x64,
LKBT51_CMD_SWITCH_FW = 0x65,
/* Factory test */
LKBT51_CMD_FACTORY_RESET = 0x71,
LKBT51_CMD_IO_TEST = 0x72,
LKBT51_CMD_RADIO_TEST = 0x73,
/* Event */
LKBT51_EVT_LKBT51_CMD_RECEIVED = 0xA1,
LKBT51_EVT_OTA_RSP = 0xA3,
LKBT51_CONNECTION_EVT_ACK = 0xA4,
};
enum {
LKBT51_EVT_ACK = 0xA1,
LKBT51_EVT_QUERY_RSP = 0xA2,
LKBT51_EVT_RESET = 0xB0,
LKBT51_EVT_LE_CONNECTION = 0xB1,
LKBT51_EVT_HOST_TYPE = 0xB2,
LKBT51_EVT_CONNECTION = 0xB3,
LKBT51_EVT_HID_EVENT = 0xB4,
LKBT51_EVT_BATTERY = 0xB5,
};
enum {
LKBT51_CONNECTED = 0x20,
LKBT51_DISCOVERABLE = 0x21,
LKBT51_RECONNECTING = 0x22,
LKBT51_DISCONNECTED = 0x23,
LKBT51_PINCODE_ENTRY = 0x24,
LKBT51_EXIT_PINCODE_ENTRY = 0x25,
LKBT51_SLEEP = 0x26
};
enum {
ACK_SUCCESS = 0x00,
ACK_CHECKSUM_ERROR,
ACK_FIFO_HALF_WARNING,
ACK_FIFO_FULL_ERROR,
};
enum{
LK_EVT_MSK_CONNECTION = 0x01 << 0,
LK_EVT_MSK_LED = 0x01 << 1,
LK_EVT_MSK_BATT = 0x01 << 2,
LK_EVT_MSK_RESET = 0x01 << 3,
LK_EVT_MSK_RPT_INTERVAL = 0x01 << 4,
LK_EVT_MSK_MD = 0x01 << 7,
};
// clang-format on
static uint8_t payload[PACKET_MAX_LEN];
static uint8_t reg_offset = 0xFF;
static uint8_t expect_len = 22;
static uint16_t connection_interval = 1;
static uint32_t wake_time;
static uint32_t factory_reset = 0;
// clang-format off
wt_func_t wireless_transport = {
lkbt51_init,
lkbt51_connect,
lkbt51_become_discoverable,
lkbt51_disconnect,
lkbt51_send_keyboard,
lkbt51_send_nkro,
lkbt51_send_consumer,
lkbt51_send_system,
lkbt51_send_mouse,
lkbt51_update_bat_lvl,
lkbt51_task
};
// clang-format on
#if defined(MCU_STM32)
/* Init SPI */
const SPIConfig spicfg = {
.circular = false,
.slave = false,
.data_cb = NULL,
.error_cb = NULL,
.ssport = PAL_PORT(LKBT51_INT_OUTPUT_PIN),
.sspad = PAL_PAD(LKBT51_INT_OUTPUT_PIN),
.cr1 = SPI_CR1_MSTR | SPI_CR1_BR_1 | SPI_CR1_BR_0,
.cr2 = 0U,
};
#endif
#if defined(WB32F3G71xx)
/* Init SPI */
const SPIConfig spicfg = {
.ssport = PAL_PORT(LKBT51_INT_OUTPUT_PIN),
.sspad = PAL_PAD(LKBT51_INT_OUTPUT_PIN),
.SPI_CPOL = 0U,
.SPI_CPHA = 0U,
.SPI_BaudRatePrescaler = 32U,
};
#endif
void lkbt51_init(bool wakeup_from_low_power_mode) {
#ifdef LKBT51_RESET_PIN
if (!wakeup_from_low_power_mode) {
gpio_set_pin_output_push_pull(LKBT51_RESET_PIN);
gpio_write_pin_low(LKBT51_RESET_PIN);
wait_ms(1);
gpio_write_pin_high(LKBT51_RESET_PIN);
}
#endif
#if (HAL_USE_SPI == TRUE)
palSetLineMode(SPI_SCK_PIN, PAL_MODE_ALTERNATE(SPI_CLK_PAL_MODE));
palSetLineMode(SPI_MISO_PIN, PAL_MODE_ALTERNATE(SPI_MISO_PAL_MODE));
palSetLineMode(SPI_MOSI_PIN, PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE));
if (WT_DRIVER.state == SPI_UNINIT) {
if (wakeup_from_low_power_mode) {
spiInit();
return;
}
spiInit();
}
#endif
gpio_set_pin_output_push_pull(LKBT51_INT_OUTPUT_PIN);
gpio_write_pin_high(LKBT51_INT_OUTPUT_PIN);
gpio_set_pin_input_high(LKBT51_INT_INPUT_PIN);
}
static inline void lkbt51_wake(void) {
if (timer_elapsed32(wake_time) > 3000) {
wake_time = timer_read32();
palWriteLine(LKBT51_INT_OUTPUT_PIN, 0);
wait_ms(10);
palWriteLine(LKBT51_INT_OUTPUT_PIN, 1);
wait_ms(300);
}
}
void lkbt51_send_protocol_ver(uint16_t ver) {
uint8_t pkt[PACKET_MAX_LEN] = {0};
memset(pkt, 0, PACKET_MAX_LEN);
uint8_t i = 0;
pkt[i++] = 0x84;
pkt[i++] = 0x7e;
pkt[i++] = 0x00;
pkt[i++] = 0x00;
pkt[i++] = 0xAA;
pkt[i++] = 0x54;
pkt[i++] = ver & 0xFF;
pkt[i++] = (ver >> 8) & 0xFF;
pkt[i++] = (uint8_t)(~0x54);
pkt[i++] = (uint8_t)(~0xAA);
#if HAL_USE_SPI
expect_len = 10;
spiStart(&WT_DRIVER, &spicfg);
spiSelect(&WT_DRIVER);
spiSend(&WT_DRIVER, i, pkt);
spiUnselectI(&WT_DRIVER);
spiStop(&WT_DRIVER);
#endif
}
void lkbt51_send_cmd(uint8_t* payload, uint8_t len, bool ack_enable, bool retry) {
static uint8_t sn = 0;
uint8_t i;
uint8_t pkt[PACKET_MAX_LEN] = {0};
memset(pkt, 0, PACKET_MAX_LEN);
if (!retry) ++sn;
if (sn == 0) ++sn;
uint16_t checksum = 0;
for (i = 0; i < len; i++)
checksum += payload[i];
i = 0;
pkt[i++] = 0x84;
pkt[i++] = 0x7e;
pkt[i++] = 0x00;
pkt[i++] = 0x00;
pkt[i++] = 0xAA;
pkt[i++] = ack_enable ? 0x56 : 0x55;
pkt[i++] = len + 2;
pkt[i++] = ~(len + 2) & 0xFF;
pkt[i++] = sn;
memcpy(pkt + i, payload, len);
i += len;
pkt[i++] = checksum & 0xFF;
pkt[i++] = (checksum >> 8) & 0xFF;
#if HAL_USE_SPI
if ((payload[0] & 0xF0) == 0x60)
expect_len = 64;
else
expect_len = 64;
spiStart(&WT_DRIVER, &spicfg);
spiSelect(&WT_DRIVER);
spiSend(&WT_DRIVER, i, pkt);
spiUnselectI(&WT_DRIVER);
spiStop(&WT_DRIVER);
#endif
}
void lkbt51_read(uint8_t* payload, uint8_t len) {
uint8_t i;
uint8_t pkt[PACKET_MAX_LEN] = {0};
memset(pkt, 0, PACKET_MAX_LEN);
i = 0;
pkt[i++] = 0x84;
pkt[i++] = 0x7f;
pkt[i++] = 0x00;
pkt[i++] = 0x80;
i += len;
#if HAL_USE_SPI
spiStart(&WT_DRIVER, &spicfg);
spiSelect(&WT_DRIVER);
spiExchange(&WT_DRIVER, i, pkt, payload);
spiUnselect(&WT_DRIVER);
spiStop(&WT_DRIVER);
#endif
}
void lkbt51_send_keyboard(uint8_t* report) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_SEND_KB;
memcpy(payload + i, report, 8);
i += 8;
lkbt51_send_cmd(payload, i, true, false);
}
void lkbt51_send_nkro(uint8_t* report) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_SEND_KB_NKRO;
memcpy(payload + i, report, 20); // NKRO report lenght is limited to 20 bytes
i += 20;
lkbt51_send_cmd(payload, i, true, false);
}
void lkbt51_send_consumer(uint16_t report) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_SEND_CONSUMER;
payload[i++] = report & 0xFF;
payload[i++] = ((report) >> 8) & 0xFF;
i += 4; // QMK doesn't send multiple consumer reports, just skip 2nd and 3rd consumer reports
lkbt51_send_cmd(payload, i, true, false);
}
void lkbt51_send_system(uint16_t report) {
uint8_t hid_usage = report & 0xFF;
if (hid_usage < 0x81 || hid_usage > 0x83) return;
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_SEND_SYSTEM;
payload[i++] = 0x01 << (hid_usage - 0x81);
lkbt51_send_cmd(payload, i, true, false);
}
void lkbt51_send_mouse(uint8_t* report) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_SEND_MOUSE; // Cmd type
payload[i++] = report[1]; // Button
payload[i++] = report[2]; // X
payload[i++] = (report[2] & 0x80) ? 0xff : 0x00; // ckbt51 use 16bit report, set high byte
payload[i++] = report[3]; // Y
payload[i++] = (report[3] & 0x80) ? 0xff : 0x00; // ckbt51 use 16bit report, set high byte
payload[i++] = report[4]; // V wheel
payload[i++] = report[5]; // H wheel
lkbt51_send_cmd(payload, i, false, false);
}
/* Send ack to connection event, wireless module will retry 2 times if no ack received */
void lkbt51_send_conn_evt_ack(void) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CONNECTION_EVT_ACK;
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_become_discoverable(uint8_t host_idx, void* param) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
pairing_param_t default_pairing_param = {0, 0, PAIRING_MODE_LESC_OR_SSP, BT_MODE_CLASSIC, 0, NULL};
if (param == NULL) {
param = &default_pairing_param;
}
pairing_param_t* p = (pairing_param_t*)param;
payload[i++] = LKBT51_CMD_PAIRING; // Cmd type
payload[i++] = host_idx; // Host Index
payload[i++] = p->timeout & 0xFF; // Timeout
payload[i++] = (p->timeout >> 8) & 0xFF;
payload[i++] = p->pairingMode;
payload[i++] = p->BRorLE; // BR/LE
payload[i++] = p->txPower; // LE TX POWER
if (p->leName) {
memcpy(&payload[i], p->leName, strlen(p->leName));
i += strlen(p->leName);
}
lkbt51_wake();
lkbt51_send_cmd(payload, i, true, false);
}
/* Timeout : 2 ~ 255 seconds */
void lkbt51_connect(uint8_t hostIndex, uint16_t timeout) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_CONNECT;
payload[i++] = hostIndex; // Host index
payload[i++] = timeout & 0xFF; // Timeout
payload[i++] = (timeout >> 8) & 0xFF;
lkbt51_wake();
lkbt51_send_cmd(payload, i, true, false);
}
void lkbt51_disconnect(void) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_DISCONNECT;
payload[i++] = 0; // Sleep mode
if (WT_DRIVER.state != SPI_READY)
spiStart(&WT_DRIVER, &spicfg);
spiSelect(&WT_DRIVER);
wait_ms(30);
// spiUnselect(&SPID1);
wait_ms(70);
lkbt51_send_cmd(payload, i, true, false);
}
void lkbt51_switch_host(uint8_t hostIndex) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_SWITCH_HOST;
payload[i++] = hostIndex;
lkbt51_send_cmd(payload, i, true, false);
}
void lkbt51_read_state_reg(uint8_t reg, uint8_t len) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_READ_STATE_REG;
payload[i++] = reg_offset = reg;
payload[i++] = len;
// TODO
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_update_bat_lvl(uint8_t bat_lvl) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_UPDATE_BAT_LVL;
payload[i++] = bat_lvl;
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_update_bat_state(uint8_t bat_state) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_UPDATE_BAT_STATE;
payload[i++] = bat_state;
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_get_info(module_info_t* info) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_GET_MODULE_INFO;
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_set_param(module_param_t* param) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_SET_CONFIG;
memcpy(payload + i, param, sizeof(module_param_t));
i += sizeof(module_param_t);
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_get_param(module_param_t* param) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_GET_CONFIG;
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_set_local_name(const char* name) {
uint8_t i = 0;
uint8_t len = strlen(name);
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_SET_NAME;
memcpy(payload + i, name, len);
i += len;
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_get_local_name(void) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_GET_NAME;
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_factory_reset(uint8_t p2p4g_clr_msk) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_FACTORY_RESET;
payload[i++] = p2p4g_clr_msk;
lkbt51_wake();
lkbt51_send_cmd(payload, i, false, false);
factory_reset = timer_read32();
}
void lkbt51_int_pin_test(bool enable) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_IO_TEST;
payload[i++] = enable;
lkbt51_send_cmd(payload, i, false, false);
}
void lkbt51_radio_test(uint8_t channel) {
uint8_t i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_RADIO_TEST;
payload[i++] = channel;
payload[i++] = 0;
lkbt51_send_cmd(payload, i, false, false);
}
bool lkbt51_read_customize_data(uint8_t* data, uint8_t len) {
uint8_t i;
uint8_t buf[20] = {0};
i = 0;
buf[i++] = 0x84;
buf[i++] = 0x7a;
buf[i++] = 0x00;
buf[i++] = 0x80;
#if HAL_USE_SPI
spiStart(&WT_DRIVER, &spicfg);
spiSelect(&WT_DRIVER);
spiExchange(&WT_DRIVER, 20, buf, payload);
uint16_t state = buf[5] | (buf[6] << 8);
if (state == 0x9527) spiExchange(&WT_DRIVER, len, data, payload);
spiUnselect(&WT_DRIVER);
spiStop(&WT_DRIVER);
#endif
return true;
}
void lkbt51_write_customize_data(uint8_t* data, uint8_t len) {
uint8_t i;
uint8_t pkt[PACKET_MAX_LEN] = {0};
i = 0;
pkt[i++] = 0x84;
pkt[i++] = 0x7a;
pkt[i++] = 0x00;
pkt[i++] = 0x00;
#if HAL_USE_SPI
spiStart(&WT_DRIVER, &spicfg);
spiSelect(&WT_DRIVER);
spiSend(&WT_DRIVER, i, pkt);
spiSend(&WT_DRIVER, len, data);
spiUnselectI(&WT_DRIVER);
spiStop(&WT_DRIVER);
#endif
i = 0;
memset(payload, 0, PACKET_MAX_LEN);
payload[i++] = LKBT51_CMD_WRTE_CSTM_DATA;
lkbt51_send_cmd(payload, i, false, false);
}
#ifdef RAW_ENABLE
void lkbt51_dfu_tx(uint8_t rsp, uint8_t* data, uint8_t len, uint8_t sn) {
uint16_t checksum = 0;
uint8_t buf[RAW_EPSIZE] = {0};
uint8_t i = 0;
buf[i++] = 0x03;
buf[i++] = 0xAA;
buf[i++] = 0x57;
buf[i++] = len;
buf[i++] = ~len;
buf[i++] = sn;
buf[i++] = rsp;
memcpy(&buf[i], data, len);
i += len;
for (uint8_t k = 0; k < i; k++)
checksum += buf[i];
raw_hid_send(buf, RAW_EPSIZE);
if (len > 25) {
i = 0;
memset(buf, 0, RAW_EPSIZE);
buf[i++] = 0x03;
memcpy(&buf[i], data + 25, len - 25);
i = i + len - 25;
raw_hid_send(buf, RAW_EPSIZE);
}
}
#endif
void lkbt51_dfu_rx(uint8_t* data, uint8_t length) {
if (data[0] == 0xAA && (data[1] == 0x55 || data[1] == 0x56) && data[2] == (~data[3] & 0xFF)) {
uint16_t checksum = 0;
uint8_t payload_len = data[2];
/* Check payload_len validity */
if (payload_len > RAW_EPSIZE - PACKECT_HEADER_LEN) return;
uint8_t* payload = &data[PACKECT_HEADER_LEN];
for (uint8_t i = 0; i < payload_len - 2; i++) {
checksum += payload[i];
}
/* Verify checksum */
if ((checksum & 0xFF) != payload[payload_len - 2] || checksum >> 8 != payload[payload_len - 1]) return;
static uint8_t sn = 0;
bool retry = true;
if (sn != data[4]) {
sn = data[4];
retry = false;
}
if ((payload[0] & 0xF0) == 0x60) {
lkbt51_wake();
lkbt51_send_cmd(payload, payload_len - 2, data[1] == 0x56, retry);
}
}
}
static void ack_handler(uint8_t* data, uint8_t len) {
switch (data[1]) {
case LKBT51_CMD_SEND_KB:
case LKBT51_CMD_SEND_KB_NKRO:
case LKBT51_CMD_SEND_CONSUMER:
case LKBT51_CMD_SEND_SYSTEM:
case LKBT51_CMD_SEND_MOUSE:
switch (data[2]) {
case ACK_SUCCESS:
report_buffer_set_retry(0);
report_buffer_set_inverval(connection_interval);
break;
case ACK_FIFO_HALF_WARNING:
report_buffer_set_retry(0);
report_buffer_set_inverval(connection_interval + 5);
break;
case ACK_FIFO_FULL_ERROR:
report_buffer_set_inverval(connection_interval + 10);
break;
}
break;
default:
break;
}
}
static void query_rsp_handler(uint8_t* data, uint8_t len) {
if (data[2]) return;
switch (data[1]) {
case LKBT51_CMD_IO_TEST:
factory_test_send(data, len);
break;
default:
break;
}
}
static void lkbt51_event_handler(uint8_t evt_type, uint8_t* data, uint8_t len, uint8_t sn) {
wireless_event_t event = {0};
switch (evt_type) {
case LKBT51_EVT_ACK:
ack_handler(data, len);
break;
case LKBT51_EVT_RESET:
kc_printf("LKBT51_EVT_RESET\n");
event.evt_type = EVT_RESET;
event.params.reason = data[0];
break;
case LKBT51_EVT_LE_CONNECTION:
kc_printf("LKBT51_EVT_LE_CONNECTION\n");
break;
case LKBT51_EVT_HOST_TYPE:
kc_printf("LKBT51_EVT_HOST_TYPE\n");
break;
case LKBT51_EVT_HID_EVENT:
kc_printf("LKBT51_EVT_HID_EVENT\n");
event.evt_type = EVT_HID_INDICATOR;
event.params.led = data[0];
break;
case LKBT51_EVT_QUERY_RSP:
kc_printf("LKBT51_EVT_QUERY_RSP\n\r");
query_rsp_handler(data, len);
break;
case LKBT51_EVT_OTA_RSP:
#ifdef RAW_ENABLE
kc_printf("LKBT51_EVT_OTA_RSP\n");
lkbt51_dfu_tx(LKBT51_EVT_OTA_RSP, data, len, sn);
#endif
break;
default:
kc_printf("Unknown event!!!\n");
break;
}
if (event.evt_type) wireless_event_enqueue(event);
}
void lkbt51_task(void) {
#define VALID_DATA_START_INDEX 4
#define BUFFER_SIZE 64
static bool wait_for_new_pkt = true;
static uint8_t len = 0xff;
static uint8_t sn = 0;
if (gpio_read_pin(LKBT51_INT_INPUT_PIN) == 0) {
uint8_t buf[BUFFER_SIZE] = {0};
lkbt51_read(buf, expect_len);
uint8_t* pbuf = buf + VALID_DATA_START_INDEX;
if (pbuf[0] == 0xAA && pbuf[1] == 0x54 && pbuf[4] == (uint8_t)(~0x54) && pbuf[5] == (uint8_t)(~0xAA)) {
uint16_t protol_ver = pbuf[3] << 8 | pbuf[2];
kc_printf("protol_ver: %x\n\r", protol_ver);
(void)protol_ver;
} else if (pbuf[0] == 0xAA) {
wireless_event_t event = {0};
uint8_t evt_mask = pbuf[1];
if (evt_mask & LK_EVT_MSK_RESET) {
event.evt_type = EVT_RESET;
event.params.reason = pbuf[2];
wireless_event_enqueue(event);
}
if (evt_mask & LK_EVT_MSK_CONNECTION) {
lkbt51_send_conn_evt_ack();
switch (pbuf[2]) {
case LKBT51_CONNECTED:
event.evt_type = EVT_CONNECTED;
break;
case LKBT51_DISCOVERABLE:
event.evt_type = EVT_DISCOVERABLE;
break;
case LKBT51_RECONNECTING:
event.evt_type = EVT_RECONNECTING;
break;
case LKBT51_DISCONNECTED:
event.evt_type = EVT_DISCONNECTED;
if (factory_reset && timer_elapsed32(factory_reset) < 3000) {
factory_reset = 0;
event.data = 1;
}
break;
case LKBT51_PINCODE_ENTRY:
event.evt_type = EVT_BT_PINCODE_ENTRY;
break;
case LKBT51_EXIT_PINCODE_ENTRY:
event.evt_type = EVT_EXIT_BT_PINCODE_ENTRY;
break;
case LKBT51_SLEEP:
event.evt_type = EVT_SLEEP;
break;
}
event.params.hostIndex = pbuf[3];
wireless_event_enqueue(event);
}
if (evt_mask & LK_EVT_MSK_LED) {
memset(&event, 0, sizeof(event));
event.evt_type = EVT_HID_INDICATOR;
event.params.led = pbuf[4];
wireless_event_enqueue(event);
}
if (evt_mask & LK_EVT_MSK_RPT_INTERVAL) {
uint32_t interval;
if (pbuf[8] & 0x80) {
interval = (pbuf[8] & 0x7F) * 1250;
} else {
interval = (pbuf[8] & 0x7F) * 125;
}
connection_interval = interval / 1000;
if (connection_interval > 7) connection_interval /= 3;
memset(&event, 0, sizeof(event));
event.evt_type = EVT_CONECTION_INTERVAL;
event.params.interval = connection_interval;
wireless_event_enqueue(event);
}
if (evt_mask & LK_EVT_MSK_BATT) {
battery_calculate_voltage(true, pbuf[6] << 8 | pbuf[5]);
}
}
pbuf = buf;
if (wait_for_new_pkt) {
for (uint8_t i = 10; i < BUFFER_SIZE - 5; i++) {
if (buf[i] == 0xAA && buf[i + 1] == 0x57 // Packet Head
&& (~buf[i + 2] & 0xFF) == buf[i + 3]) { // Check wheather len is valid
len = buf[i + 2];
sn = buf[i + 4];
pbuf = &buf[i + 5];
wait_for_new_pkt = false;
}
}
}
if (!wait_for_new_pkt && BUFFER_SIZE - 5 >= len) {
wait_for_new_pkt = true;
uint16_t checksum = 0;
for (int i = 0; i < len - 2; i++) {
checksum += pbuf[i];
}
if ((checksum & 0xff) == pbuf[len - 2] && ((checksum >> 8) & 0xff) == pbuf[len - 1]) {
lkbt51_event_handler(pbuf[0], pbuf + 1, len - 3, sn);
} else {
// TODO: Error handle
}
}
}
}

View File

@@ -0,0 +1,136 @@
/* Copyright 2023 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "stdint.h"
#include "hal.h"
#include "config.h"
// Error checking
#if HAL_USE_SPI == FALSE
# error "Please enable SPI to use LKBT51"
#endif
#if defined(WB32F3G71xx)
# ifndef WT_DRIVER
# define WT_DRIVER SPIDQ
# endif
#endif
#if defined(MCU_STM32)
# ifndef WT_DRIVER
# define WT_DRIVER SPID1
# endif
#endif
#define PACKECT_HEADER_LEN 5
#define BDA_LEN 6
#define PACKET_MAX_LEN 64
#define P24G_INDEX 24
enum {
PAIRING_MODE_DEFAULT = 0x00,
PAIRING_MODE_JUST_WORK,
PAIRING_MODE_PASSKEY_ENTRY,
PAIRING_MODE_LESC_OR_SSP,
PAIRING_MODE_INVALID,
};
enum {
BT_MODE_DEFAUL,
BT_MODE_CLASSIC,
BT_MODE_LE,
BT_MODE_INVALID,
};
typedef struct {
uint8_t hostIndex;
uint16_t timeout; /* Pairing timeout, valid value range from 30 to 3600 seconds, 0 for default */
uint8_t pairingMode; /* 0: default, 1: Just Works, 2: Passkey Entry */
uint8_t BRorLE; /* Only available for dual mode module. Keep 0 for single mode module */
uint8_t txPower; /* Only available for BLE module */
const char* leName; /* Only available for BLE module */
} pairing_param_t;
typedef struct {
uint8_t type;
uint16_t full_votage;
uint16_t empty_voltage;
uint16_t shutdown_voltage;
} battery_param_t;
typedef struct {
uint8_t model_name[11];
uint8_t mode;
uint8_t bluetooth_version;
uint8_t firmware_version[11];
uint8_t hardware_version[11];
uint16_t cmd_set_verson;
} __attribute__((packed)) module_info_t;
typedef struct {
uint8_t event_mode; /* Must be 0x02 */
uint16_t connected_idle_timeout;
uint16_t pairing_timeout; /* Range: 30 ~ 3600 second, 0 for default */
uint8_t pairing_mode; /* 0: default, 1: Just Works, 2: Passkey Entry */
uint16_t reconnect_timeout; /* 0: default, 0xFF: Unlimited time, 2 ~ 254 seconds */
uint8_t report_rate; /* 90 or 133 */
uint8_t rsvd1;
uint8_t rsvd2;
uint8_t vendor_id_source; /* 0: From Bluetooth SIG, 1: From USB-IF */
uint16_t verndor_id; /* No effect, the vendor ID is 0x3434 */
uint16_t product_id;
/* Below parametes is only available for BLE module */
uint16_t le_connection_interval_min;
uint16_t le_connection_interval_max;
uint16_t le_connection_interval_timeout;
} __attribute__((packed)) module_param_t;
void lkbt51_init(bool wakeup_from_low_power_mode);
void lkbt51_send_protocol_ver(uint16_t ver);
void lkbt51_send_cmd(uint8_t* payload, uint8_t len, bool ack_enable, bool retry);
void lkbt51_send_keyboard(uint8_t* report);
void lkbt51_send_nkro(uint8_t* report);
void lkbt51_send_consumer(uint16_t report);
void lkbt51_send_system(uint16_t report);
void lkbt51_send_mouse(uint8_t* report);
void lkbt51_become_discoverable(uint8_t host_idx, void* param);
void lkbt51_connect(uint8_t hostIndex, uint16_t timeout);
void lkbt51_disconnect(void);
void lkbt51_switch_host(uint8_t hostIndex);
void lkbt51_read_state_reg(uint8_t reg, uint8_t len);
void lkbt51_update_bat_lvl(uint8_t bat_lvl);
void lkbt51_update_bat_state(uint8_t bat_state);
void lkbt51_get_info(module_info_t* info);
void lkbt51_set_param(module_param_t* param);
void lkbt51_get_param(module_param_t* param);
void lkbt51_set_local_name(const char* name);
void lkbt51_get_local_name(void);
void lkbt51_factory_reset(uint8_t p2p4g_clr_msk);
void lkbt51_int_pin_test(bool enable);
void lkbt51_dfu_rx(uint8_t* data, uint8_t length);
void lkbt51_radio_test(uint8_t channel);
void lkbt51_write_customize_data(uint8_t* data, uint8_t len);
bool lkbt51_read_customize_data(uint8_t* data, uint8_t len);
void lkbt51_task(void);

View File

@@ -0,0 +1,315 @@
/*/* Copyright 2022 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/******************************************************************************
*
* Filename: lpm.c
*
* Description: Contains low power mode implementation
*
******************************************************************************/
#include "quantum.h"
#if defined(PROTOCOL_CHIBIOS)
# include <usb_main.h>
#endif
#include "debounce.h"
#include "wireless.h"
#include "indicator.h"
#include "lpm.h"
#include "transport.h"
#include "battery.h"
#include "report_buffer.h"
#include "keychron_common.h"
extern matrix_row_t matrix[MATRIX_ROWS];
extern wt_func_t wireless_transport;
static uint32_t lpm_timer_buffer;
static bool lpm_time_up = false;
#ifndef OPTICAL_SWITCH
static matrix_row_t empty_matrix[MATRIX_ROWS] = {0};
#endif
pin_t pins_row[MATRIX_ROWS] = MATRIX_ROW_PINS;
pin_t pins_col[MATRIX_COLS] = MATRIX_COL_PINS;
;
__attribute__((weak)) void select_all_cols(void) {
for (uint8_t i = 0; i < MATRIX_COLS; i++) {
if (pins_col[i] == NO_PIN) continue;
gpio_set_pin_output_push_pull(pins_col[i]);
gpio_write_pin_low(pins_col[i]);
}
}
void lpm_init(void) {
#ifdef USB_POWER_SENSE_PIN
# if (USB_POWER_CONNECTED_LEVEL == 0)
gpio_set_pin_input_high(USB_POWER_SENSE_PIN);
# else
setPinInputLow(USB_POWER_SENSE_PIN);
# endif
#endif
lpm_timer_reset();
}
inline void lpm_timer_reset(void) {
lpm_time_up = false;
lpm_timer_buffer = timer_read32();
}
void lpm_timer_stop(void) {
lpm_time_up = false;
lpm_timer_buffer = 0;
}
static inline bool lpm_any_matrix_action(void) {
return memcmp(matrix, empty_matrix, sizeof(empty_matrix));
}
__attribute__((weak)) void matrix_enter_low_power(void) {
/* Enable key matrix wake up */
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
if (pins_row[x] != NO_PIN) {
palEnableLineEvent(pins_row[x], PAL_EVENT_MODE_BOTH_EDGES);
}
}
select_all_cols();
}
__attribute__((weak)) void matrix_exit_low_power(void) {
/* Disable all wake up pins */
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
if (pins_row[x] != NO_PIN) {
palDisableLineEvent(pins_row[x]);
}
}
}
static void lpm_turn_off_backlight_and_led(void) {
#ifdef RGB_MATRIX_ENABLE
rgb_matrix_set_color_all(0, 0, 0);
rgb_matrix_driver.flush();
rgb_matrix_driver_shutdown();
#endif
#ifdef LED_MATRIX_ENABLE
led_matrix_set_value_all(0);
led_matrix_driver.flush();
led_matrix_driver_shutdown();
#endif
#ifdef LED_NUM_LOCK_PIN
gpio_write_pin(LED_NUM_LOCK_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_CAPS_LOCK_PIN
gpio_write_pin(LED_CAPS_LOCK_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef BAT_LOW_LED_PIN
gpio_write_pin(BAT_LOW_LED_PIN, !BAT_LOW_LED_PIN_ON_STATE);
#endif
#ifdef BT_INDICATION_LED_PIN_LIST
pin_t bt_led_pins[] = BT_INDICATION_LED_PIN_LIST;
for (uint8_t i = 0; i < sizeof(bt_led_pins) / sizeof(pin_t); i++)
gpio_write_pin(bt_led_pins[i], !BT_INDICATION_LED_ON_STATE);
#endif
}
/* Implement of entering low power mode and wakeup varies per mcu or platform */
__attribute__((weak)) void lpm_pre_enter_low_power(void) {}
__attribute__((weak)) void lpm_enter_low_power_kb(void) {}
__attribute__((weak)) void lpm_enter_low_power(void) {
if (get_transport() == TRANSPORT_USB && !usb_power_connected()) {
lpm_turn_off_backlight_and_led();
}
/* Usb unit is actived and running, stop and disconnect first */
usbStop(&USBD1);
usbDisconnectBus(&USBD1);
/* Isolate USB to save power.*/
// PWR->CR2 &= ~PWR_CR2_USV; /*PWR_CR2_USV is available on STM32L4x2xx and STM32L4x3xx devices only. */
#if (HAL_USE_SPI == TRUE)
spiStop(&SPI_DRIVER);
palSetLineMode(SPI_SCK_PIN, PAL_MODE_INPUT_PULLDOWN);
#ifdef SPI_MOSI_PIN
palSetLineMode(SPI_MOSI_PIN, PAL_MODE_INPUT_PULLDOWN);
#endif
#ifdef SPI_MISO_PIN
palSetLineMode(SPI_MISO_PIN, PAL_MODE_INPUT_PULLDOWN);
#endif
#endif
palEnableLineEvent(LKBT51_INT_INPUT_PIN, PAL_EVENT_MODE_FALLING_EDGE);
#ifdef USB_POWER_SENSE_PIN
palEnableLineEvent(USB_POWER_SENSE_PIN, PAL_EVENT_MODE_BOTH_EDGES);
#endif
#ifdef P24G_MODE_SELECT_PIN
palEnableLineEvent(P24G_MODE_SELECT_PIN, PAL_EVENT_MODE_BOTH_EDGES);
#endif
#ifdef BT_MODE_SELECT_PIN
palEnableLineEvent(BT_MODE_SELECT_PIN, PAL_EVENT_MODE_BOTH_EDGES);
#endif
matrix_enter_low_power();
#if defined(DIP_SWITCH_PINS)
# define NUMBER_OF_DIP_SWITCHES (sizeof(dip_switch_pad) / sizeof(pin_t))
static pin_t dip_switch_pad[] = DIP_SWITCH_PINS;
for (uint8_t i = 0; i < NUMBER_OF_DIP_SWITCHES; i++) {
setPinInputLow(dip_switch_pad[i]);
}
#endif
battery_stop();
lpm_enter_low_power_kb();
}
__attribute__((weak)) void lpm_post_enter_low_power(void) {}
__attribute__((weak)) void lpm_standby(pm_t mode) {}
__attribute__((weak)) void lpm_early_wakeup(void) {
gpio_write_pin_low(LKBT51_INT_OUTPUT_PIN);
}
__attribute__((weak)) void lpm_wakeup_init(void) {}
__attribute__((weak)) void lpm_pre_wakeup(void) {
gpio_write_pin_high(LKBT51_INT_OUTPUT_PIN);
}
__attribute__((weak)) void lpm_wakeup(void) {
matrix_exit_low_power();
halInit();
#ifdef ENCODER_ENABLE
encoder_cb_init();
#endif
if (wireless_transport.init) wireless_transport.init(true);
battery_init();
palDisableLineEvent(LKBT51_INT_INPUT_PIN);
#ifdef P24G_MODE_SELECT_PIN
palDisableLineEvent(P24G_MODE_SELECT_PIN);
#endif
#ifdef BT_MODE_SELECT_PIN
palDisableLineEvent(BT_MODE_SELECT_PIN);
#endif
#ifdef USB_POWER_SENSE_PIN
palDisableLineEvent(USB_POWER_SENSE_PIN);
# if defined(KEEP_USB_CONNECTION_IN_WIRELESS_MODE)
if (usb_power_connected()
# ifndef BT_MODE_SELECT_PIN
&& (get_transport() == TRANSPORT_USB)
# endif
) {
usb_event_queue_init();
init_usb_driver(&USB_DRIVER);
}
# endif
#endif
#if defined(DIP_SWITCH_PINS)
dip_switch_init();
dip_switch_read(true);
#endif
/* Call debounce_free() to avoiding memory leak of debounce_counters as debounce_init()
invoked in matrix_init() alloc new memory to debounce_counters */
debounce_free();
matrix_init();
}
__attribute__((weak)) void lpm_post_wakeup(void) {}
__attribute__((weak)) bool usb_power_connected(void) {
#ifdef USB_POWER_SENSE_PIN
return gpio_read_pin(USB_POWER_SENSE_PIN) == USB_POWER_CONNECTED_LEVEL;
#else
return true;
#endif
}
__attribute__((weak)) bool lpm_is_kb_idle(void) {
return true;
}
__attribute__((weak)) bool lpm_set(pm_t mode) {
return false;
}
bool allow_low_power_mode(pm_t mode) {
/* Don't enter low power mode if attached to the host */
if (mode > PM_SLEEP && usb_power_connected()) return false;
if (!lpm_set(mode)) return false;
return true;
}
void lpm_task(void) {
if (!lpm_time_up && sync_timer_elapsed32(lpm_timer_buffer) > RUN_MODE_PROCESS_TIME) {
lpm_time_up = true;
lpm_timer_buffer = 0;
}
if (usb_power_connected() && USBD1.state == USB_STOP) {
usb_event_queue_init();
init_usb_driver(&USB_DRIVER);
}
if ((get_transport() & TRANSPORT_WIRELESS) && lpm_time_up && !indicator_is_running() && lpm_is_kb_idle()) {
if (
#ifdef LED_MATRIX_ENABLE
(!led_matrix_is_enabled() ||
(led_matrix_is_enabled() && led_matrix_is_driver_shutdown()))
#elif defined(RGB_MATRIX_ENABLE)
(!rgb_matrix_is_enabled() ||
(rgb_matrix_is_enabled() && rgb_matrix_is_driver_shutdown()))
#else
!bat_level_animiation_actived()
#endif
&& !lpm_any_matrix_action()) {
if (allow_low_power_mode(LOW_POWER_MODE)) {
lpm_pre_enter_low_power();
lpm_enter_low_power();
lpm_post_enter_low_power();
lpm_standby(LOW_POWER_MODE);
lpm_early_wakeup();
lpm_wakeup_init();
lpm_pre_wakeup();
lpm_wakeup();
lpm_post_wakeup();
lpm_timer_reset();
report_buffer_init();
lpm_set(PM_RUN);
}
}
}
}

View File

@@ -0,0 +1,49 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef RUN_MODE_PROCESS_TIME
# define RUN_MODE_PROCESS_TIME 1000
#endif
typedef enum {
PM_RUN,
PM_SLEEP,
PM_STOP,
PM_STANDBY,
} pm_t;
void lpm_init(void);
void lpm_timer_reset(void);
void lpm_timer_stop(void);
void select_all_cols(void);
void matrix_enter_low_power(void);
void matrix_exit_low_power(void);
void lpm_pre_enter_low_power(void);
void lpm_enter_low_power(void);
void lpm_enter_low_power_kb(void);
void lpm_post_enter_low_power(void) ;
void lpm_standby(pm_t mode);
void lpm_early_wakeup(void);
void lpm_wakeup_init(void);
void lpm_pre_wakeup(void);
void lpm_wakeup(void);
void lpm_post_wakeup(void);
bool usb_power_connected(void);
bool lpm_is_kb_idle(void);
bool lpm_set(pm_t mode);
void lpm_task(void);

View File

@@ -0,0 +1,142 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/******************************************************************************
*
* Filename: lpm_stm32f401.c
*
* Description: Contains low power mode implementation
*
******************************************************************************/
#include "quantum.h"
#include <usb_main.h>
#include "wireless.h"
#include "lpm.h"
#include "lpm_stm32f401.h"
#include "config.h"
#include "hal.h"
static pm_t power_mode = PM_RUN;
void lpm_post_enter_low_power(void) {
/* USB D+/D- */
palSetLineMode(A12, PAL_MODE_INPUT_PULLDOWN);
palSetLineMode(A11, PAL_MODE_INPUT_PULLDOWN);
}
void lpm_pre_wakeup(void) {
/* USB D+/D- */
#if (HAL_USE_USB == TRUE)
palSetLineMode(A11, PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUPDR_FLOATING | PAL_MODE_ALTERNATE(10U));
palSetLineMode(A12, PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUPDR_FLOATING | PAL_MODE_ALTERNATE(10U));
#endif
/* SPI */
#if (HAL_USE_SPI == TRUE)
palSetLineMode(SPI_SCK_PIN, PAL_MODE_ALTERNATE(5));
if (SPI_MOSI_PIN != NO_PIN) {
palSetLineMode(SPI_MOSI_PIN, PAL_MODE_ALTERNATE(5));
}
if (SPI_MISO_PIN != NO_PIN) {
palSetLineMode(SPI_MISO_PIN, PAL_MODE_ALTERNATE(5));
}
#endif
}
bool lpm_set(pm_t mode) {
bool ret = true;
switch (mode) {
case PM_SLEEP:
/* Wake source: Any interrupt or event */
if (power_mode != PM_RUN)
ret = false;
else
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
break;
case PM_STOP:
/* Wake source: Reset pin, all I/Os, BOR, PVD, PVM, RTC, LCD, IWDG,
COMPx, USARTx, LPUART1, I2Cx, LPTIMx, USB, SWPMI */
if (power_mode != PM_RUN)
ret = false;
else {
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
PWR->CR |=
#if STOP_MODE_MAIN_REGULATOR_LOW_VOLTAGE
PWR_CR_MRLVDS |
#endif
#if STOP_MODE_LOW_POWER_REGULATOR_LOW_VOLTAG
PWR_CR_LPLVDS |
#endif
#if STOP_MODE_FLASH_POWER_DOWN
PWR_CR_FPDS |
#endif
#if STOP_MODE_LOW_POWER_DEEPSLEEP
PWR_CR_LPDS |
#endif
0;
}
break;
case PM_STANDBY:
if (power_mode != PM_RUN)
ret = false;
else {
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
}
break;
default:
break;
}
power_mode = mode;
return ret;
}
void lpm_standby(pm_t mode) {
#if STM32_HSE_ENABLED
/* Switch to HSI */
RCC->CFGR = (RCC->CFGR & (~STM32_SW_MASK)) | STM32_SW_HSI;
while ((RCC->CFGR & RCC_CFGR_SWS) != (STM32_SW_HSI << 2))
;
/* Set HSE off */
RCC->CR &= ~RCC_CR_HSEON;
while ((RCC->CR & RCC_CR_HSERDY))
;
/* To avoid power consumption of floating GPIO */
palSetLineMode(H0, PAL_MODE_INPUT_PULLDOWN);
palSetLineMode(H1, PAL_MODE_INPUT_PULLDOWN);
#endif
__WFI();
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
}
void lpm_wakeup_init(void) {
gpio_write_pin_low(LKBT51_INT_OUTPUT_PIN);
stm32_clock_init();
gpio_write_pin_high(LKBT51_INT_OUTPUT_PIN);
}
void usb_power_connect(void) {}
void usb_power_disconnect(void) {}

View File

@@ -0,0 +1,33 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef STOP_MODE_MAIN_REGULATOR_LOW_VOLTAGE
# define STOP_MODE_MAIN_REGULATOR_LOW_VOLTAGE TRUE
#endif
#ifndef STOP_MODE_LOW_POWER_REGULATOR_LOW_VOLTAG
# define STOP_MODE_LOW_POWER_REGULATOR_LOW_VOLTAG TRUE
#endif
#ifndef STOP_MODE_FLASH_POWER_DOWN
# define STOP_MODE_FLASH_POWER_DOWN TRUE
#endif
#ifndef STOP_MODE_LOW_POWER_DEEPSLEEP
# define STOP_MODE_LOW_POWER_DEEPSLEEP TRUE
#endif

View File

@@ -0,0 +1,146 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "lpm.h"
bool wakeup_from_lpm;
// clang-format off
static const uint32_t pre_lp_code[] = {
553863175u, 554459777u, 1208378049u, 4026624001u, 688390415u,
554227969u, 3204472833u, 1198571264u, 1073807360u, 1073808388u};
#define PRE_LP() ((void (*)(void))((unsigned int)(pre_lp_code) | 0x01))()
static const uint32_t post_lp_code[] = {
553863177u, 554459777u, 1208509121u, 51443856u, 4026550535u,
1745485839u, 3489677954u, 536895496u, 673389632u, 1198578684u,
1073807360u, 536866816u, 1073808388u};
#define POST_LP() ((void (*)(void))((unsigned int)(post_lp_code) | 0x01))()
// clang-format on
extern void __early_init(void);
extern void matrix_init_pins(void);
void stop_mode_entry(void);
void lpm_post_enter_low_power(void) {
#if (HAL_USE_USB == TRUE)
/* USB D+/D- */
palSetLineMode(A12, PAL_MODE_INPUT_PULLUP); // why PAL_MODE_INPUT_PULLUP
palSetLineMode(A11, PAL_MODE_INPUT_PULLDOWN);
#endif
palSetLineMode(DP_PULLUP_CONTROL_PIN, PAL_MODE_INPUT_PULLDOWN);
}
void lpm_pre_wakeup(void) {
#if (HAL_USE_USB == TRUE)
/* USB D+/D- */
palSetLineMode(A11, PAL_WB32_OTYPE_PUSHPULL | PAL_WB32_OSPEED_HIGH | PAL_WB32_PUPDR_FLOATING | PAL_MODE_ALTERNATE(10U));
palSetLineMode(A12, PAL_WB32_OTYPE_PUSHPULL | PAL_WB32_OSPEED_HIGH | PAL_WB32_PUPDR_FLOATING | PAL_MODE_ALTERNATE(10U));
#endif
/* SPI */
#if (HAL_USE_SPI == TRUE)
palSetLineMode(SPI_SCK_PIN, PAL_MODE_ALTERNATE(5));
if (SPI_MOSI_PIN != NO_PIN) {
palSetLineMode(SPI_MOSI_PIN, PAL_MODE_ALTERNATE(5));
}
if (SPI_MISO_PIN != NO_PIN) {
palSetLineMode(SPI_MISO_PIN, PAL_MODE_ALTERNATE(5));
}
#endif
}
bool lpm_set(pm_t mode) {
bool ret = true;
static pm_t power_mode = PM_RUN;
switch (mode) {
case PM_SLEEP:
/* Wake source: Any interrupt or event */
if (power_mode != PM_RUN)
ret = false;
else
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
break;
case PM_STOP:
if (power_mode != PM_RUN) ret = false;
break;
case PM_STANDBY:
if (power_mode != PM_RUN)
ret = false;
else {
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
}
break;
default:
break;
}
power_mode = mode;
return ret;
}
void lpm_standby(pm_t mode) {
chSysDisable();
wb32_set_main_clock_to_mhsi();
rtclp_lld_init();
stop_mode_entry();
chSysEnable();
}
void lpm_wakeup_init(void) {
wakeup_from_lpm = true;
__early_init();
wakeup_from_lpm = false;
}
void stop_mode_entry(void) {
EXTI->PR = 0x7FFFF;
for (uint8_t i = 0; i < 8; i++) {
for (uint8_t j = 0; j < 32; j++) {
if (NVIC->ISPR[i] & (0x01UL < j)) {
NVIC->ICPR[i] = (0x01UL < j);
}
}
}
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk; // Clear Systick IRQ Pending
/* Clear all bits except DBP and FCLKSD bit */
PWR->CR0 &= 0x09U;
/* STOP LP4 MODE S32KON */
PWR->CR0 |= 0x3B004U;
PWR->CFGR = 0x3B3;
PRE_LP();
/* Set SLEEPDEEP bit of Cortex System Control Register */
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
/* Request Wait For Interrupt */
__WFI();
POST_LP();
/* Clear SLEEPDEEP bit of Cortex System Control Register */
SCB->SCR &= (~SCB_SCR_SLEEPDEEP_Msk);
}

View File

@@ -0,0 +1,144 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "report_buffer.h"
#include "wireless.h"
#include "lpm.h"
/* The report buffer is mainly used to fix key press lost issue of macro
* when wireless module fifo isn't large enough. The maximun macro
* string length is determined by this queue size, and should be
* REPORT_BUFFER_QUEUE_SIZE devided by 2 since each character is implemented
* by sending a key pressing then a key releasing report.
* Please note that it cosume sizeof(report_buffer_t) * REPORT_BUFFER_QUEUE_SIZE
* bytes RAM, with default setting, used RAM size is
* sizeof(report_buffer_t) * 256 = 34* 256 = 8704 bytes
*/
#ifndef REPORT_BUFFER_QUEUE_SIZE
# define REPORT_BUFFER_QUEUE_SIZE 256
#endif
extern wt_func_t wireless_transport;
/* report_interval value should be less than bluetooth connection interval because
* it takes some time for communicating between mcu and bluetooth module. Carefully
* set this value to feed the bt module so that we don't lost the key report nor lost
* the anchor point of bluetooth interval. The bluetooth connection interval varies
* if BLE is used, invoke report_buffer_set_inverval() to update the value
*/
uint8_t report_interval = DEFAULT_2P4G_REPORT_INVERVAL_MS;
static uint32_t report_timer_buffer = 0;
uint32_t retry_time_buffer = 0;
report_buffer_t report_buffer_queue[REPORT_BUFFER_QUEUE_SIZE];
uint16_t report_buffer_queue_head;
uint16_t report_buffer_queue_tail;
report_buffer_t kb_rpt;
uint8_t retry = 0;
void report_buffer_task(void);
void report_buffer_init(void) {
// Initialise the report queue
memset(&report_buffer_queue, 0, sizeof(report_buffer_queue));
report_buffer_queue_head = 0;
report_buffer_queue_tail = 0;
retry = 0;
report_timer_buffer = timer_read32();
}
bool report_buffer_enqueue(report_buffer_t *report) {
uint16_t next = (report_buffer_queue_head + 1) % REPORT_BUFFER_QUEUE_SIZE;
if (next == report_buffer_queue_tail) {
return false;
}
report_buffer_queue[report_buffer_queue_head] = *report;
report_buffer_queue_head = next;
return true;
}
inline bool report_buffer_dequeue(report_buffer_t *report) {
if (report_buffer_queue_head == report_buffer_queue_tail) {
return false;
}
*report = report_buffer_queue[report_buffer_queue_tail];
report_buffer_queue_tail = (report_buffer_queue_tail + 1) % REPORT_BUFFER_QUEUE_SIZE;
return true;
}
bool report_buffer_is_empty() {
return report_buffer_queue_head == report_buffer_queue_tail;
}
void report_buffer_update_timer(void) {
report_timer_buffer = timer_read32();
}
bool report_buffer_next_inverval(void) {
return timer_elapsed32(report_timer_buffer) > report_interval;
}
void report_buffer_set_inverval(uint8_t interval) {
// OG_TRACE("report_buffer_set_inverval: %d\n\r", interval);
report_interval = interval;
}
uint8_t report_buffer_get_retry(void) {
return retry;
}
void report_buffer_set_retry(uint8_t times) {
retry = times;
}
void report_buffer_task(void) {
if (wireless_get_state() == WT_CONNECTED && (!report_buffer_is_empty() || retry) && report_buffer_next_inverval()) {
bool pending_data = false;
if (!retry) {
if (report_buffer_dequeue(&kb_rpt) && kb_rpt.type != REPORT_TYPE_NONE) {
if (timer_read32() > 2) {
pending_data = true;
retry = RETPORT_RETRY_COUNT;
retry_time_buffer = timer_read32();
}
}
} else {
if (timer_elapsed32(retry_time_buffer) > 2) {
pending_data = true;
--retry;
retry_time_buffer = timer_read32();
}
}
if (pending_data) {
#if defined(NKRO_ENABLE) && defined(WIRELESS_NKRO_ENABLE)
if (kb_rpt.type == REPORT_TYPE_NKRO && wireless_transport.send_nkro) {
wireless_transport.send_nkro(&kb_rpt.nkro.mods);
} else if (kb_rpt.type == REPORT_TYPE_KB && wireless_transport.send_keyboard)
wireless_transport.send_keyboard(&kb_rpt.keyboard.mods);
#else
if (kb_rpt.type == REPORT_TYPE_KB && wireless_transport.send_keyboard) wireless_transport.send_keyboard(&kb_rpt.keyboard.mods);
#endif
if (kb_rpt.type == REPORT_TYPE_CONSUMER && wireless_transport.send_consumer) wireless_transport.send_consumer(kb_rpt.consumer);
report_timer_buffer = timer_read32();
lpm_timer_reset();
}
}
}

View File

@@ -0,0 +1,61 @@
/*/* Copyright 2022 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "report.h"
/* Default report interval value */
#ifndef DEFAULT_BLE_REPORT_INVERVAL_MS
# define DEFAULT_BLE_REPORT_INVERVAL_MS 3
#endif
/* Default report interval value */
#ifndef DEFAULT_2P4G_REPORT_INVERVAL_MS
# define DEFAULT_2P4G_REPORT_INVERVAL_MS 1
#endif
/* Default report interval value */
#ifndef RETPORT_RETRY_COUNT
# define RETPORT_RETRY_COUNT 30
#endif
enum {
REPORT_TYPE_NONE,
REPORT_TYPE_KB,
REPORT_TYPE_NKRO,
REPORT_TYPE_CONSUMER,
};
typedef struct {
uint8_t type;
union {
report_keyboard_t keyboard;
report_nkro_t nkro;
uint16_t consumer;
};
} report_buffer_t;
void report_buffer_init(void);
bool report_buffer_enqueue(report_buffer_t *report);
bool report_buffer_dequeue(report_buffer_t *report);
bool report_buffer_is_empty(void);
void report_buffer_update_timer(void);
bool report_buffer_next_inverval(void);
void report_buffer_set_inverval(uint8_t interval);
uint8_t report_buffer_get_retry(void);
void report_buffer_set_retry(uint8_t times);
void report_buffer_task(void);

View File

@@ -0,0 +1,43 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hal.h"
#if (HAL_USE_RTC)
# include "rtc_timer.h"
void rtc_timer_init(void) {
rtc_timer_clear();
}
void rtc_timer_clear(void) {
RTCDateTime tm = {0, 0, 0, 0, 0, 0};
rtcSetTime(&RTCD1, &tm);
}
uint32_t rtc_timer_read_ms(void) {
RTCDateTime tm;
rtcGetTime(&RTCD1, &tm);
return tm.millisecond;
}
uint32_t rtc_timer_elapsed_ms(uint32_t last) {
return TIMER_DIFF_32(rtc_timer_read_ms(), last);
}
#endif

View File

@@ -0,0 +1,35 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "timer.h"
#include <stdint.h>
#define RTC_MAX_TIME (24 * 3600 * 1000) // Set to 1 day
#ifdef __cplusplus
extern "C" {
#endif
void rtc_timer_init(void);
void rtc_timer_clear(void);
uint32_t rtc_timer_read_ms(void);
uint32_t rtc_timer_elapsed_ms(uint32_t last);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,259 @@
/*/* Copyright 2022 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "wireless.h"
#include "indicator.h"
#include "lpm.h"
#include "mousekey.h"
#if defined(PROTOCOL_CHIBIOS)
# include <usb_main.h>
#endif
#include "transport.h"
#include "lkbt51.h"
#ifndef REINIT_LED_DRIVER
# define REINIT_LED_DRIVER 0
#endif
#if defined(PROTOCOL_CHIBIOS)
extern host_driver_t chibios_driver;
#endif
extern host_driver_t wireless_driver;
extern keymap_config_t keymap_config;
extern wt_func_t wireless_transport;
static transport_t transport = TRANSPORT_NONE;
#ifdef NKRO_ENABLE
nkro_t nkro = {false, false};
#endif
static void transport_changed(transport_t new_transport);
__attribute__((weak)) void bt_transport_enable(bool enable) {
if (enable) {
// if (host_get_driver() != &wireless_driver) {
host_set_driver(&wireless_driver);
/* Disconnect and reconnect to sync the wireless state
* TODO: query wireless state to sync
*/
wireless_disconnect();
uint32_t t = timer_read32();
while (timer_elapsed32(t) < 100) {
wireless_transport.task();
}
// wireless_connect();
wireless_connect_ex(30, 0);
// TODO: Clear USB report
//}
} else {
indicator_stop();
if (wireless_get_state() == WT_CONNECTED && transport == TRANSPORT_BLUETOOTH) {
report_keyboard_t empty_report = {0};
wireless_driver.send_keyboard(&empty_report);
}
}
}
__attribute__((weak)) void p24g_transport_enable(bool enable) {
if (enable) {
// if (host_get_driver() != &wireless_driver) {
host_set_driver(&wireless_driver);
/* Disconnect and reconnect to sync the wireless state
* TODO: query bluetooth state to sync
*/
wireless_disconnect();
uint32_t t = timer_read32();
while (timer_elapsed32(t) < 100) {
wireless_transport.task();
}
wireless_connect_ex(P24G_INDEX, 0);
// wireless_connect();
// TODO: Clear USB report
//}
} else {
indicator_stop();
if (wireless_get_state() == WT_CONNECTED && transport == TRANSPORT_P2P4) {
report_keyboard_t empty_report = {0};
wireless_driver.send_keyboard(&empty_report);
}
}
}
__attribute__((weak)) void usb_power_connect(void) {}
__attribute__((weak)) void usb_power_disconnect(void) {}
__attribute__((weak)) void usb_transport_enable(bool enable) {
if (enable) {
if (host_get_driver() != &chibios_driver) {
// #if !defined(KEEP_USB_CONNECTION_IN_WIRELESS_MODE)
// usb_power_connect();
// usb_start(&USBD1);
// #endif
host_set_driver(&chibios_driver);
}
} else {
if (USB_DRIVER.state == USB_ACTIVE) {
report_keyboard_t empty_report = {0};
chibios_driver.send_keyboard(&empty_report);
}
#if !defined(KEEP_USB_CONNECTION_IN_WIRELESS_MODE)
usbStop(&USBD1);
usbDisconnectBus(&USBD1);
usb_power_disconnect();
#endif
}
}
void set_transport(transport_t new_transport) {
if (transport != new_transport) {
if (transport == TRANSPORT_USB || ((transport != TRANSPORT_USB) && wireless_get_state() == WT_CONNECTED)) clear_keyboard();
transport = new_transport;
switch (transport) {
case TRANSPORT_USB:
usb_transport_enable(true);
bt_transport_enable(false);
wait_ms(5);
p24g_transport_enable(false);
wireless_disconnect();
lpm_timer_stop();
break;
case TRANSPORT_BLUETOOTH:
p24g_transport_enable(false);
wait_ms(1);
bt_transport_enable(true);
usb_transport_enable(false);
lpm_timer_reset();
break;
case TRANSPORT_P2P4:
bt_transport_enable(false);
wait_ms(1);
p24g_transport_enable(true);
usb_transport_enable(false);
lpm_timer_reset();
break;
default:
break;
}
transport_changed(transport);
}
}
transport_t get_transport(void) {
return transport;
}
#if (REINIT_LED_DRIVER)
/* Changing transport may cause bronw-out reset of led driver
* withoug MCU reset, which lead backlight to not work,
* reinit the led driver workgound this issue */
static void reinit_led_drvier(void) {
/* Wait circuit to discharge for a while */
systime_t start = chVTGetSystemTime();
while (chTimeI2MS(chVTTimeElapsedSinceX(start)) < 100) {
};
# ifdef LED_MATRIX_ENABLE
led_matrix_init();
# endif
# ifdef RGB_MATRIX_ENABLE
rgb_matrix_init();
# endif
}
#endif
void transport_changed(transport_t new_transport) {
kc_printf("transport_changed %d\n\r", new_transport);
indicator_init();
#if (REINIT_LED_DRIVER)
reinit_led_drvier();
#endif
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_TIMEOUT)
# if (RGB_MATRIX_TIMEOUT > 0)
rgb_matrix_disable_timeout_set(RGB_MATRIX_TIMEOUT_INFINITE);
rgb_matrix_disable_time_reset();
# endif
#endif
#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_TIMEOUT)
# if (LED_MATRIX_TIMEOUT > 0)
led_matrix_disable_timeout_set(LED_MATRIX_TIMEOUT_INFINITE);
led_matrix_disable_time_reset();
# endif
#endif
}
void usb_remote_wakeup(void) {
if (USB_DRIVER.state == USB_SUSPENDED) {
while (USB_DRIVER.state == USB_SUSPENDED) {
wireless_pre_task();
if (get_transport() != TRANSPORT_USB) {
suspend_wakeup_init_quantum();
return;
}
/* Do this in the suspended state */
suspend_power_down(); // on AVR this deep sleeps for 15ms
/* Remote wakeup */
if (suspend_wakeup_condition()
#ifdef ENCODER_ENABLE
//|| encoder_read()
#endif
) {
usbWakeupHost(&USB_DRIVER);
wait_ms(300);
#ifdef MOUSEKEY_ENABLE
// Wiggle to wakeup
mousekey_on(KC_MS_LEFT);
mousekey_send();
wait_ms(10);
mousekey_on(KC_MS_RIGHT);
mousekey_send();
wait_ms(10);
mousekey_off((KC_MS_RIGHT));
mousekey_send();
#else
set_mods(0x02);
send_keyboard_report();
wait_ms(10);
del_mods(0x02);
send_keyboard_report();
#endif
}
}
/* Woken up */
// variables has been already cleared by the wakeup hook
send_keyboard_report();
#ifdef MOUSEKEY_ENABLE
mousekey_send();
#endif /* MOUSEKEY_ENABLE */
usb_event_queue_task();
}
}

View File

@@ -0,0 +1,42 @@
/*/* Copyright 2022 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
typedef enum {
TRANSPORT_NONE,
TRANSPORT_USB = 0x01 << 0,
TRANSPORT_BLUETOOTH = 0x01 << 1,
TRANSPORT_P2P4 = 0x01 << 2,
TRANSPORT_MAX,
} transport_t;
#ifdef NKRO_ENABLE
typedef struct {
bool usb : 1;
bool bluetooth : 1;
} nkro_t;
#endif
#define TRANSPORT_WIRELESS (TRANSPORT_BLUETOOTH | TRANSPORT_P2P4)
void set_transport(transport_t new_transport);
transport_t get_transport(void);
void usb_power_connect(void);
void usb_power_disconnect(void);
void usb_transport_enable(bool enable);
void usb_remote_wakeup(void);

View File

@@ -0,0 +1,541 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "wireless.h"
#include "report_buffer.h"
#include "lpm.h"
#include "battery.h"
#include "indicator.h"
#include "transport.h"
#include "rtc_timer.h"
#include "keychron_wireless_common.h"
#include "keychron_task.h"
extern uint8_t pairing_indication;
extern host_driver_t chibios_driver;
extern report_buffer_t kb_rpt;
extern uint32_t retry_time_buffer;
extern uint8_t retry;
static uint8_t host_index = 0;
static uint8_t led_state = 0;
extern wt_func_t wireless_transport;
static wt_state_t wireless_state = WT_RESET;
static bool pincodeEntry = false;
uint8_t wireless_report_protocol = true;
/* declarations */
uint8_t wreless_keyboard_leds(void);
void wireless_send_keyboard(report_keyboard_t *report);
void wireless_send_nkro(report_nkro_t *report);
void wireless_send_mouse(report_mouse_t *report);
void wireless_send_extra(report_extra_t *report);
bool process_record_wireless(uint16_t keycode, keyrecord_t *record);
/* host struct */
host_driver_t wireless_driver = {wreless_keyboard_leds, wireless_send_keyboard, wireless_send_nkro, wireless_send_mouse, wireless_send_extra};
#define WT_EVENT_QUEUE_SIZE 16
wireless_event_t wireless_event_queue[WT_EVENT_QUEUE_SIZE];
uint8_t wireless_event_queue_head;
uint8_t wireless_event_queue_tail;
void wireless_event_queue_init(void) {
// Initialise the event queue
memset(&wireless_event_queue, 0, sizeof(wireless_event_queue));
wireless_event_queue_head = 0;
wireless_event_queue_tail = 0;
}
bool wireless_event_enqueue(wireless_event_t event) {
uint8_t next = (wireless_event_queue_head + 1) % WT_EVENT_QUEUE_SIZE;
if (next == wireless_event_queue_tail) {
/* Override the first report */
wireless_event_queue_tail = (wireless_event_queue_tail + 1) % WT_EVENT_QUEUE_SIZE;
}
wireless_event_queue[wireless_event_queue_head] = event;
wireless_event_queue_head = next;
return true;
}
static inline bool wireless_event_dequeue(wireless_event_t *event) {
if (wireless_event_queue_head == wireless_event_queue_tail) {
return false;
}
*event = wireless_event_queue[wireless_event_queue_tail];
wireless_event_queue_tail = (wireless_event_queue_tail + 1) % WT_EVENT_QUEUE_SIZE;
return true;
}
/*
* Bluetooth init.
*/
void wireless_init(void) {
wireless_state = WT_INITIALIZED;
wireless_event_queue_init();
#ifndef DISABLE_REPORT_BUFFER
report_buffer_init();
#endif
indicator_init();
#ifdef BLUETOOTH_INT_INPUT_PIN
gpio_set_pin_input_high(BLUETOOTH_INT_INPUT_PIN);
#endif
battery_init();
lpm_init();
#if HAL_USE_RTC
rtc_timer_init();
#endif
}
/*
* Bluetooth trasponrt init. Bluetooth module driver shall use this function to register a callback
* to its implementation.
*/
void wireless_set_transport(wt_func_t *transport) {
if (transport) memcpy(&wireless_transport, transport, sizeof(wt_func_t));
}
/*
* Enter pairing with current host index
*/
void wireless_pairing(void) {
if (battery_is_critical_low()) return;
wireless_pairing_ex(0, NULL);
wireless_state = WT_PARING;
}
/*
* Enter pairing with specified host index and param
*/
void wireless_pairing_ex(uint8_t host_idx, void *param) {
kc_printf("wireless_pairing_ex %d\n\r", host_idx);
if (battery_is_critical_low()) return;
if (wireless_transport.pairing_ex) wireless_transport.pairing_ex(host_idx, param);
wireless_state = WT_PARING;
host_index = host_idx;
}
/*
* Initiate connection request to paired host
*/
void wireless_connect(void) {
/* Work around empty report after wakeup, which leads to reconneect/disconnected loop */
if (battery_is_critical_low() || timer_read32() == 0) return;
if (wireless_state == WT_RECONNECTING && !indicator_is_running()) {
indicator_set(wireless_state, host_index);
}
wireless_transport.connect_ex(0, 0);
wireless_state = WT_RECONNECTING;
}
/*
* Initiate connection request to paired host with argument
*/
void wireless_connect_ex(uint8_t host_idx, uint16_t timeout) {
kc_printf("wireless_connect_ex %d\n\r", host_idx);
if (battery_is_critical_low()) return;
if (host_idx != 0) {
/* Do nothing when trying to connect to current connected host*/
if (host_index == host_idx && wireless_state == WT_CONNECTED) return;
host_index = host_idx;
led_state = 0;
}
wireless_transport.connect_ex(host_idx, timeout);
wireless_state = WT_RECONNECTING;
}
/* Initiate a disconnection */
void wireless_disconnect(void) {
kc_printf("wireless_disconnect\n\r");
if (wireless_transport.disconnect) wireless_transport.disconnect();
}
/* Called when the BT device is reset. */
static void wireless_enter_reset(uint8_t reason) {
kc_printf("wireless_enter_reset\n\r");
wireless_state = WT_RESET;
wireless_enter_reset_kb(reason);
}
/* Enters discoverable state. Upon entering this state we perform the following actions:
* - change state to WT_PARING
* - set pairing indication
*/
static void wireless_enter_discoverable(uint8_t host_idx) {
kc_printf("wireless_enter_discoverable: %d\n\r", host_idx);
host_index = host_idx;
wireless_state = WT_PARING;
indicator_set(wireless_state, host_idx);
wireless_enter_discoverable_kb(host_idx);
}
/*
* Enters reconnecting state. Upon entering this state we perform the following actions:
* - change state to RECONNECTING
* - set reconnect indication
*/
static void wireless_enter_reconnecting(uint8_t host_idx) {
host_index = host_idx;
kc_printf("wireless_reconnecting %d\n\r", host_idx);
wireless_state = WT_RECONNECTING;
indicator_set(wireless_state, host_idx);
wireless_enter_reconnecting_kb(host_idx);
}
/* Enters connected state. Upon entering this state we perform the following actions:
* - change state to CONNECTED
* - set connected indication
* - enable NKRO if it is support
*/
static void wireless_enter_connected(uint8_t host_idx) {
kc_printf("wireless_connected %d\n\r", host_idx);
wireless_state = WT_CONNECTED;
indicator_set(wireless_state, host_idx);
host_index = host_idx;
clear_keyboard();
/* Enable NKRO since it may be disabled in pin code entry */
#if defined(NKRO_ENABLE) && !defined(WIRELESS_NKRO_ENABLE)
keymap_config.nkro = false;
#endif
wireless_enter_connected_kb(host_idx);
if (battery_is_empty()) {
indicator_battery_low_enable(true);
}
if (wireless_transport.update_bat_level) wireless_transport.update_bat_level(battery_get_percentage());
lpm_timer_reset();
}
/* Enters disconnected state. Upon entering this state we perform the following actions:
* - change state to DISCONNECTED
* - set disconnected indication
*/
static void wireless_enter_disconnected(uint8_t host_idx, uint8_t reason) {
kc_printf("wireless_disconnected %d, %d\n\r", host_idx, reason);
uint8_t previous_state = wireless_state;
led_state = 0;
if (get_transport() & TRANSPORT_WIRELESS)
led_update_kb((led_t)led_state);
wireless_state = WT_DISCONNECTED;
if (previous_state == WT_CONNECTED) {
lpm_timer_reset();
indicator_set(WT_SUSPEND, host_idx);
} else {
indicator_set(wireless_state, host_idx);
#if defined(RGB_MATRIX) || defined(LED_MATRIX)
if (reason && (get_transport() & TRANSPORT_WIRELESS))
indicator_set_backlit_timeout(DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT*1000);
#endif
}
#ifndef DISABLE_REPORT_BUFFER
report_buffer_init();
#endif
retry = 0;
wireless_enter_disconnected_kb(host_idx, reason);
indicator_battery_low_enable(false);
}
/* Enter pin code entry state. */
static void wireless_enter_bluetooth_pin_code_entry(void) {
#if defined(NKRO_ENABLE)
keymap_config.nkro = FALSE;
#endif
pincodeEntry = true;
wireless_enter_bluetooth_pin_code_entry_kb();
}
/* Exit pin code entry state. */
static void wireless_exit_bluetooth_pin_code_entry(void) {
#if defined(NKRO_ENABLE) || defined(WIRELESS_NKRO_ENABLE)
keymap_config.raw = eeconfig_read_keymap();
#endif
pincodeEntry = false;
wireless_exit_bluetooth_pin_code_entry_kb();
}
/* Enters disconnected state. Upon entering this state we perform the following actions:
* - change state to DISCONNECTED
* - set disconnected indication
*/
static void wireless_enter_sleep(void) {
kc_printf("wireless_enter_sleep %d\n\r", wireless_state);
led_state = 0;
if (wireless_state == WT_CONNECTED || wireless_state == WT_PARING) {
wireless_state = WT_SUSPEND;
kc_printf("WT_SUSPEND\n\r");
lpm_timer_reset();
wireless_enter_sleep_kb();
indicator_set(wireless_state, 0);
indicator_battery_low_enable(false);
}
}
__attribute__((weak)) void wireless_enter_reset_kb(uint8_t reason) {}
__attribute__((weak)) void wireless_enter_discoverable_kb(uint8_t host_idx) {}
__attribute__((weak)) void wireless_enter_reconnecting_kb(uint8_t host_idx) {}
__attribute__((weak)) void wireless_enter_connected_kb(uint8_t host_idx) {}
__attribute__((weak)) void wireless_enter_disconnected_kb(uint8_t host_idx, uint8_t reason) {}
__attribute__((weak)) void wireless_enter_bluetooth_pin_code_entry_kb(void) {}
__attribute__((weak)) void wireless_exit_bluetooth_pin_code_entry_kb(void) {}
__attribute__((weak)) void wireless_enter_sleep_kb(void) {}
/* */
static void wireless_hid_set_protocol(bool report_protocol) {
wireless_report_protocol = false;
}
uint8_t wreless_keyboard_leds(void) {
if (wireless_state == WT_CONNECTED) {
return led_state;
}
return 0;
}
extern keymap_config_t keymap_config;
void wireless_send_keyboard(report_keyboard_t *report) {
if (battery_is_critical_low()) return;
if (wireless_state == WT_PARING && !pincodeEntry) return;
if (wireless_state == WT_CONNECTED || (wireless_state == WT_PARING && pincodeEntry)) {
if (wireless_transport.send_keyboard) {
#ifndef DISABLE_REPORT_BUFFER
bool empty = report_buffer_is_empty();
report_buffer_t report_buffer;
report_buffer.type = REPORT_TYPE_KB;
memcpy(&report_buffer.keyboard, report, sizeof(report_keyboard_t));
report_buffer_enqueue(&report_buffer);
if (empty)
report_buffer_task();
#else
wireless_transport.send_keyboard(&report->mods);
#endif
}
} else if (wireless_state != WT_RESET) {
wireless_connect();
}
}
void wireless_send_nkro(report_nkro_t *report) {
if (battery_is_critical_low()) return;
if (wireless_state == WT_PARING && !pincodeEntry) return;
if (wireless_state == WT_CONNECTED || (wireless_state == WT_PARING && pincodeEntry)) {
if (wireless_transport.send_nkro) {
#ifndef DISABLE_REPORT_BUFFER
bool empty = report_buffer_is_empty();
report_buffer_t report_buffer;
report_buffer.type = REPORT_TYPE_NKRO;
memcpy(&report_buffer.nkro, report, sizeof(report_nkro_t));
report_buffer_enqueue(&report_buffer);
if (empty)
report_buffer_task();
#else
wireless_transport.send_nkro(&report->mods);
#endif
}
} else if (wireless_state != WT_RESET) {
wireless_connect();
}
}
void wireless_send_mouse(report_mouse_t *report) {
if (battery_is_critical_low()) return;
if (wireless_state == WT_CONNECTED) {
if (wireless_transport.send_mouse) wireless_transport.send_mouse((uint8_t *)report);
} else if (wireless_state != WT_RESET) {
wireless_connect();
}
}
void wireless_send_system(uint16_t data) {
if (wireless_state == WT_CONNECTED) {
if (wireless_transport.send_system) wireless_transport.send_system(data);
} else if (wireless_state != WT_RESET) {
wireless_connect();
}
}
void wireless_send_consumer(uint16_t data) {
if (wireless_state == WT_CONNECTED) {
#ifndef DISABLE_REPORT_BUFFER
if (report_buffer_is_empty() && report_buffer_next_inverval()) {
if (wireless_transport.send_consumer) wireless_transport.send_consumer(data);
report_buffer_update_timer();
} else {
report_buffer_t report_buffer;
report_buffer.type = REPORT_TYPE_CONSUMER;
report_buffer.consumer = data;
report_buffer_enqueue(&report_buffer);
}
#else
if (wireless_transport.send_consumer) wireless_transport.send_consumer(data);
#endif
} else if (wireless_state != WT_RESET) {
wireless_connect();
}
}
void wireless_send_extra(report_extra_t *report) {
if (battery_is_critical_low()) return;
if (report->report_id == REPORT_ID_SYSTEM) {
wireless_send_system(report->usage);
} else if (report->report_id == REPORT_ID_CONSUMER) {
wireless_send_consumer(report->usage);
}
}
void wireless_low_battery_shutdown(void) {
indicator_battery_low_enable(false);
report_buffer_init();
clear_keyboard(); //
wait_ms(50); // wait a while for bt module to free buffer by sending report
// Release all keys by sending empty reports
if (keymap_config.nkro) {
report_nkro_t empty_nkro_report;
memset(&empty_nkro_report, 0, sizeof(empty_nkro_report));
wireless_transport.send_nkro(&empty_nkro_report.mods);
} else {
report_keyboard_t empty_report;
memset(&empty_report, 0, sizeof(empty_report));
wireless_transport.send_keyboard(&empty_report.mods);
}
wait_ms(10);
wireless_transport.send_consumer(0);
wait_ms(10);
report_mouse_t empty_mouse_report;
memset(&empty_mouse_report, 0, sizeof(empty_mouse_report));
wireless_transport.send_mouse((uint8_t *)&empty_mouse_report);
wait_ms(300); // Wait for bt module to send all buffered report
wireless_disconnect();
}
void wireless_event_task(void) {
wireless_event_t event;
while (wireless_event_dequeue(&event)) {
switch (event.evt_type) {
case EVT_RESET:
wireless_enter_reset(event.params.reason);
break;
case EVT_CONNECTED:
wireless_enter_connected(event.params.hostIndex);
break;
case EVT_DISCOVERABLE:
wireless_enter_discoverable(event.params.hostIndex);
break;
case EVT_RECONNECTING:
wireless_enter_reconnecting(event.params.hostIndex);
break;
case EVT_DISCONNECTED:
wireless_enter_disconnected(event.params.hostIndex, event.data);
break;
case EVT_BT_PINCODE_ENTRY:
wireless_enter_bluetooth_pin_code_entry();
break;
case EVT_EXIT_BT_PINCODE_ENTRY:
wireless_exit_bluetooth_pin_code_entry();
break;
case EVT_SLEEP:
wireless_enter_sleep();
break;
case EVT_HID_INDICATOR:
led_state = event.params.led;
break;
case EVT_HID_SET_PROTOCOL:
wireless_hid_set_protocol(event.params.protocol);
break;
case EVT_CONECTION_INTERVAL:
report_buffer_set_inverval(event.params.interval);
break;
default:
break;
}
}
}
void wireless_task(void) {
wireless_transport.task();
wireless_event_task();
#ifndef DISABLE_REPORT_BUFFER
report_buffer_task();
#endif
indicator_task();
keychron_wireless_common_task();
battery_task();
lpm_task();
}
void send_string_task(void) {
if ((get_transport() & TRANSPORT_WIRELESS) && wireless_get_state() == WT_CONNECTED) {
wireless_transport.task();
#ifndef DISABLE_REPORT_BUFFER
report_buffer_task();
#endif
}
}
wt_state_t wireless_get_state(void) {
return wireless_state;
};
bool process_record_wireless(uint16_t keycode, keyrecord_t *record) {
if (get_transport() & TRANSPORT_WIRELESS) {
lpm_timer_reset();
if (battery_is_empty() && wireless_get_state() == WT_CONNECTED && record->event.pressed) {
indicator_battery_low_enable(true);
}
}
if (!process_record_keychron_wireless(keycode, record)) return false;
return true;
}

View File

@@ -0,0 +1,101 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "wireless_event_type.h"
#include "action.h"
#ifdef KC_DEBUG
# define kc_printf dprintf
#else
# define kc_printf(format, ...)
#endif
/* Low power mode */
#ifndef LOW_POWER_MODE
# define LOW_POWER_MODE PM_STOP
#endif
/* Wake pin used for blueooth module/controller to wake up MCU in low power mode*/
#ifndef BLUETOOTH_INT_INPUT_PIN
# define WAKE_PIN A5
#endif
// clang-format off
/* Type of an enumeration of the possible wireless transport state.*/
typedef enum {
WT_RESET,
WT_INITIALIZED, // 1
WT_DISCONNECTED, // 2
WT_CONNECTED, // 3
WT_PARING, // 4
WT_RECONNECTING, // 5
WT_SUSPEND
} wt_state_t;
//extern event_listener_t wireless_driver;
typedef struct {
void (*init)(bool);
void (*connect_ex)(uint8_t, uint16_t);
void (*pairing_ex)(uint8_t, void *);
void (*disconnect)(void);
void (*send_keyboard)(uint8_t *);
void (*send_nkro)(uint8_t *);
void (*send_consumer)(uint16_t);
void (*send_system)(uint16_t);
void (*send_mouse)(uint8_t *);
void (*update_bat_level)(uint8_t);
void (*task)(void);
} wt_func_t;
// clang-format on
extern void register_wt_tasks(void);
void wireless_init(void);
void wireless_set_transport(wt_func_t *transport);
void wireless(void);
bool wireless_event_enqueue(wireless_event_t event);
void wireless_connect(void);
void wireless_connect_ex(uint8_t host_idx, uint16_t timeout);
void wireless_disconnect(void);
void wireless_pairing(void);
void wireless_pairing_ex(uint8_t host_idx, void *param);
// bool bluetooth_is_activated(void);
void wireless_enter_reset_kb(uint8_t reason);
void wireless_enter_discoverable_kb(uint8_t host_idx);
void wireless_enter_reconnecting_kb(uint8_t host_idx);
void wireless_enter_connected_kb(uint8_t host_idx);
void wireless_enter_disconnected_kb(uint8_t host_idx, uint8_t reason);
void wireless_enter_bluetooth_pin_code_entry_kb(void);
void wireless_exit_bluetooth_pin_code_entry_kb(void);
void wireless_enter_sleep_kb(void);
void wireless_task(void);
void wireless_pre_task(void);
void wireless_post_task(void);
void send_string_task(void);
wt_state_t wireless_get_state(void);
void wireless_low_battery_shutdown(void);
bool process_record_wireless(uint16_t keycode, keyrecord_t *record);

View File

@@ -0,0 +1,32 @@
OPT_DEFS += -DLK_WIRELESS_ENABLE
OPT_DEFS += -DNO_USB_STARTUP_CHECK
OPT_DEFS += -DCORTEX_ENABLE_WFI_IDLE=TRUE
OPT_DEFS += -DWIRELESS_NKRO_ENABLE
INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --keyboard $(KEYBOARD) --output $(INTERMEDIATE_OUTPUT)/src/info_rules.mk)
include $(INFO_RULES_MK)
WIRELESS_DIR = common/wireless
SRC += \
$(WIRELESS_DIR)/wireless.c \
$(WIRELESS_DIR)/report_buffer.c \
$(WIRELESS_DIR)/lkbt51.c \
$(WIRELESS_DIR)/indicator.c \
$(WIRELESS_DIR)/wireless_main.c \
$(WIRELESS_DIR)/transport.c \
$(WIRELESS_DIR)/lpm.c \
$(WIRELESS_DIR)/battery.c \
$(WIRELESS_DIR)/bat_level_animation.c \
$(WIRELESS_DIR)/rtc_timer.c \
$(WIRELESS_DIR)/keychron_wireless_common.c
ifeq ($(strip $(MCU)), STM32F401)
SRC += $(WIRELESS_DIR)/lpm_stm32f401.c
endif
ifeq ($(strip $(MCU)), WB32F3G71)
SRC += $(WIRELESS_DIR)/lpm_wb32f3g71.c
endif
VPATH += $(TOP_DIR)/keyboards/keychron/$(WIRELESS_DIR)

View File

@@ -0,0 +1,36 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "config.h"
//
#ifndef BT_HOST_DEVICES_COUNT
# define BT_HOST_DEVICES_COUNT 3
#endif
#define P2P4G_HOST_DEVICES_COUNT 1
// Uint: Second
#ifndef DISCONNECTED_BACKLIGHT_OFF_DELAY_TIME
# define DISCONNECTED_BACKLIGHT_OFF_DELAY_TIME 40
#endif
// Uint: Second, the timer restarts on key activities.
#ifndef CONNECTED_BACKLIGHT_OFF_DELAY_TIME
# define CONNECTED_BACKLIGHT_OFF_DELAY_TIME 600
#endif

View File

@@ -0,0 +1,45 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
/* Type of an enumeration of the possible wireless events.*/
typedef enum {
EVT_NONE = 0,
EVT_RESET,
EVT_DISCOVERABLE,
EVT_RECONNECTING,
EVT_CONNECTED,
EVT_DISCONNECTED,
EVT_BT_PINCODE_ENTRY,
EVT_EXIT_BT_PINCODE_ENTRY,
EVT_SLEEP,
EVT_HID_SET_PROTOCOL,
EVT_HID_INDICATOR,
EVT_CONECTION_INTERVAL,
} event_type_t;
typedef struct {
event_type_t evt_type; /*The type of the event. */
union {
uint8_t reason; /* Parameters to WT_RESET event */
uint8_t hostIndex; /* Parameters to connection event from EVT_DISCOVERABLE to EVT_DISCONECTED */
uint8_t led; /* Parameters to EVT_HID_INDICATOR event */
uint8_t protocol; /* Parameters to EVT_HID_SET_PROTOCOL event */
uint8_t interval; /* Parameters to EVT_CONECTION_INTERVAL event */
} params;
uint8_t data;
} wireless_event_t;

View File

@@ -0,0 +1,36 @@
/* Copyright 2023 ~ 2025 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "wireless.h"
#include "transport.h"
#include "factory_test.h"
#include "keychron_task.h"
__attribute__((weak)) void wireless_pre_task(void) {}
__attribute__((weak)) void wireless_post_task(void) {}
bool wireless_tasks(void) {
wireless_pre_task();
wireless_task();
wireless_post_task();
/* usb_remote_wakeup() should be invoked last so that we have chance
* to switch to wireless after start-up when usb is not connected
*/
if (get_transport() == TRANSPORT_USB) usb_remote_wakeup();
return true;
}

View File

@@ -0,0 +1,43 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef RGB_MATRIX_ENABLE
/* RGB Matrix driver configuration */
# define RGB_MATRIX_LED_COUNT 108
# define DRIVER_CS_PINS \
{ B8, B9 }
/* Set LED driver current */
# define SNLED27351_CURRENT_TUNE \
{ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 }
/* Set to infinit, which is use in USB mode by default */
# define RGB_MATRIX_TIMEOUT RGB_MATRIX_TIMEOUT_INFINITE
/* Allow shutdown of led driver to save power */
# define RGB_MATRIX_DRIVER_SHUTDOWN_ENABLE
# define RGB_MATRIX_DRIVER_LOAD_ENABLE
/* Turn off backlight on low brightness to save power */
# define RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL 48
/* Indications */
# define NUM_LOCK_INDEX 36
# define RGB_MATRIX_KEYPRESSES
# define RGB_MATRIX_FRAMEBUFFER_EFFECTS
#endif

View File

@@ -0,0 +1,40 @@
{
"usb": {
"pid": "0x0AA0",
"device_version": "1.0.0"
},
"features": {
"rgb_matrix": true
},
"rgb_matrix": {
"driver": "snled27351_spi",
"sleep": true,
"animations": {
"band_spiral_val": true,
"breathing": true,
"cycle_all": true,
"cycle_left_right": true,
"cycle_out_in": true,
"cycle_out_in_dual": true,
"cycle_pinwheel": true,
"cycle_spiral": true,
"cycle_up_down": true,
"digital_rain": true,
"dual_beacon": true,
"jellybean_raindrops": true,
"pixel_rain": true,
"rainbow_beacon": true,
"rainbow_moving_chevron": true,
"solid_reactive_multinexus": true,
"solid_reactive_multiwide": true,
"solid_reactive_simple": true,
"solid_splash": true,
"splash": true,
"typing_heatmap": true
}
},
"indicators": {
"caps_lock": "C9",
"on_state": 1
}
}

View File

@@ -0,0 +1,67 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
enum layers {
MAC_BASE,
MAC_FN,
WIN_BASE,
WIN_FN,
};
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_ansi_108(
KC_ESC, KC_BRID, KC_BRIU, KC_MCTRL, KC_LNPAD, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_SNAP, KC_SIRI, RGB_MOD, KC_F13, KC_F14, KC_F15, KC_F16,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL, KC_END, KC_PGDN, KC_P7, KC_P8, KC_P9,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, KC_RCMMD, KC_ROPTN, MO(MAC_FN), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
[MAC_FN] = LAYOUT_ansi_108(
_______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______, RGB_TOG, _______, _______, _______, _______,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, BAT_LVL, NK_TOGG, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______),
[WIN_BASE] = LAYOUT_ansi_108(
KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_PSCR, KC_CTANA, RGB_MOD, _______, _______, _______, _______,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL, KC_END, KC_PGDN, KC_P7, KC_P8, KC_P9,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
KC_LCTL, KC_LWIN, KC_LALT, KC_SPC, KC_RALT, KC_RWIN, MO(WIN_FN), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
[WIN_FN] = LAYOUT_ansi_108(
_______, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, RGB_TOG, _______, _______, _______, _______,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, BAT_LVL, NK_TOGG, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______)
};
// clang-format on
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_record_keychron_common(keycode, record)) {
return false;
}
return true;
}

View File

@@ -0,0 +1,175 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software : you can redistribute it and /or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see < http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
// clang-format off
#ifdef RGB_MATRIX_ENABLE
const snled27351_led_t g_snled27351_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to SNLED27351 manual for these locations
* driver
* | R location
* | | G location
* | | | B location
* | | | | */
{0, CB7_CA16, CB9_CA16, CB8_CA16},
{0, CB7_CA14, CB9_CA14, CB8_CA14},
{0, CB7_CA13, CB9_CA13, CB8_CA13},
{0, CB7_CA12, CB9_CA12, CB8_CA12},
{0, CB7_CA11, CB9_CA11, CB8_CA11},
{0, CB7_CA10, CB9_CA10, CB8_CA10},
{0, CB7_CA9, CB9_CA9, CB8_CA9},
{0, CB7_CA8, CB9_CA8, CB8_CA8},
{0, CB7_CA7, CB9_CA7, CB8_CA7},
{0, CB7_CA6, CB9_CA6, CB8_CA6},
{0, CB7_CA5, CB9_CA5, CB8_CA5},
{0, CB7_CA4, CB9_CA4, CB8_CA4},
{0, CB7_CA3, CB9_CA3, CB8_CA3},
{0, CB7_CA2, CB9_CA2, CB8_CA2},
{0, CB7_CA1, CB9_CA1, CB8_CA1},
{1, CB1_CA3, CB3_CA3, CB2_CA3},
{0, CB4_CA4, CB6_CA4, CB5_CA4},
{0, CB4_CA3, CB6_CA3, CB5_CA3},
{0, CB4_CA2, CB6_CA2, CB5_CA2},
{0, CB1_CA16, CB3_CA16, CB2_CA16},
{0, CB1_CA15, CB3_CA15, CB2_CA15},
{0, CB1_CA14, CB3_CA14, CB2_CA14},
{0, CB1_CA13, CB3_CA13, CB2_CA13},
{0, CB1_CA12, CB3_CA12, CB2_CA12},
{0, CB1_CA11, CB3_CA11, CB2_CA11},
{0, CB1_CA10, CB3_CA10, CB2_CA10},
{0, CB1_CA9, CB3_CA9, CB2_CA9},
{0, CB1_CA8, CB3_CA8, CB2_CA8},
{0, CB1_CA7, CB3_CA7, CB2_CA7},
{0, CB1_CA6, CB3_CA6, CB2_CA6},
{0, CB1_CA5, CB3_CA5, CB2_CA5},
{0, CB1_CA4, CB3_CA4, CB2_CA4},
{0, CB1_CA3, CB3_CA3, CB2_CA3},
{0, CB1_CA2, CB3_CA2, CB2_CA2},
{0, CB1_CA1, CB3_CA1, CB2_CA1},
{1, CB1_CA2, CB3_CA2, CB2_CA2},
{0, CB4_CA8, CB6_CA8, CB5_CA8},
{0, CB4_CA7, CB6_CA7, CB5_CA7},
{0, CB4_CA6, CB6_CA6, CB5_CA6},
{0, CB10_CA16, CB12_CA16, CB11_CA16},
{0, CB10_CA15, CB12_CA15, CB11_CA15},
{0, CB10_CA14, CB12_CA14, CB11_CA14},
{0, CB10_CA13, CB12_CA13, CB11_CA13},
{0, CB10_CA12, CB12_CA12, CB11_CA12},
{0, CB10_CA11, CB12_CA11, CB11_CA11},
{0, CB10_CA10, CB12_CA10, CB11_CA10},
{0, CB10_CA9, CB12_CA9, CB11_CA9},
{0, CB10_CA8, CB12_CA8, CB11_CA8},
{0, CB10_CA7, CB12_CA7, CB11_CA7},
{0, CB10_CA6, CB12_CA6, CB11_CA6},
{0, CB10_CA5, CB12_CA5, CB11_CA5},
{0, CB10_CA4, CB12_CA4, CB11_CA4},
{0, CB10_CA3, CB12_CA3, CB11_CA3},
{0, CB10_CA2, CB12_CA2, CB11_CA2},
{0, CB10_CA1, CB12_CA1, CB11_CA1},
{1, CB1_CA1, CB3_CA1, CB2_CA1},
{0, CB4_CA12, CB6_CA12, CB5_CA12},
{0, CB4_CA11, CB6_CA11, CB5_CA11},
{0, CB4_CA10, CB6_CA10, CB5_CA10},
{1, CB1_CA16, CB3_CA16, CB2_CA16},
{1, CB1_CA15, CB3_CA15, CB2_CA15},
{1, CB1_CA14, CB3_CA14, CB2_CA14},
{1, CB1_CA13, CB3_CA13, CB2_CA13},
{1, CB1_CA12, CB3_CA12, CB2_CA12},
{1, CB1_CA11, CB3_CA11, CB2_CA11},
{1, CB1_CA10, CB3_CA10, CB2_CA10},
{1, CB1_CA9, CB3_CA9, CB2_CA9},
{1, CB1_CA8, CB3_CA8, CB2_CA8},
{1, CB1_CA7, CB3_CA7, CB2_CA7},
{1, CB1_CA6, CB3_CA6, CB2_CA6},
{1, CB1_CA5, CB3_CA5, CB2_CA5},
{1, CB1_CA4, CB3_CA4, CB2_CA4},
{0, CB4_CA1, CB6_CA1, CB5_CA1},
{0, CB4_CA5, CB6_CA5, CB5_CA5},
{0, CB4_CA9, CB6_CA9, CB5_CA9},
{1, CB10_CA3, CB12_CA3, CB11_CA3},
{1, CB10_CA9, CB12_CA9, CB11_CA9},
{1, CB10_CA8, CB12_CA8, CB11_CA8},
{1, CB10_CA7, CB12_CA7, CB11_CA7},
{1, CB7_CA16, CB9_CA16, CB8_CA16},
{1, CB7_CA14, CB9_CA14, CB8_CA14},
{1, CB7_CA13, CB9_CA13, CB8_CA13},
{1, CB7_CA12, CB9_CA12, CB8_CA12},
{1, CB7_CA11, CB9_CA11, CB8_CA11},
{1, CB7_CA10, CB9_CA10, CB8_CA10},
{1, CB7_CA9, CB9_CA9, CB8_CA9},
{1, CB7_CA8, CB9_CA8, CB8_CA8},
{1, CB7_CA7, CB9_CA7, CB8_CA7},
{1, CB7_CA6, CB9_CA6, CB8_CA6},
{1, CB7_CA5, CB9_CA5, CB8_CA5},
{1, CB7_CA3, CB9_CA3, CB8_CA3},
{1, CB7_CA1, CB9_CA1, CB8_CA1},
{1, CB10_CA6, CB12_CA6, CB11_CA6},
{1, CB10_CA5, CB12_CA5, CB11_CA5},
{1, CB10_CA4, CB12_CA4, CB11_CA4},
{1, CB4_CA16, CB6_CA16, CB5_CA16},
{1, CB4_CA15, CB6_CA15, CB5_CA15},
{1, CB4_CA14, CB6_CA14, CB5_CA14},
{1, CB4_CA10, CB6_CA10, CB5_CA10},
{1, CB4_CA6, CB6_CA6, CB5_CA6},
{1, CB4_CA5, CB6_CA5, CB5_CA5},
{1, CB4_CA4, CB6_CA4, CB5_CA4},
{1, CB4_CA3, CB6_CA3, CB5_CA3},
{1, CB4_CA2, CB6_CA2, CB5_CA2},
{1, CB4_CA1, CB6_CA1, CB5_CA1},
{1, CB7_CA2, CB9_CA2, CB8_CA2},
{1, CB10_CA2, CB12_CA2, CB11_CA2},
{1, CB10_CA1, CB12_CA1, CB11_CA1},
};
#define __ NO_LED
led_config_t g_led_config = {
{
// Key Matrix to RGB Index
{ 0, __, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38 },
{ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 },
{ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78 },
{ 79, __, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, __, 90, __, 91, __, 92, 93, 94 },
{ 95, 96, 97, __, __, __, 98, __, __, __, 99, 100, 101, 102, 103, 104, 105, __, 106, 107 },
},
{
// RGB Index to Physical Position
{0, 0}, {21, 0}, {31, 0}, {42, 0}, {52, 0}, {68, 0}, {78, 0}, {89, 0}, {99, 0}, {115, 0}, {125, 0}, {136, 0}, {146, 0}, {159, 0}, {169, 0}, {180, 0}, {193, 0}, {203, 0}, {214, 0},
{0,15}, {10,15}, {21,15}, {31,15}, {42,15}, {52,15}, {63,15}, {73,15}, {83,15}, {94,15}, {104,15}, {115,15}, {125,15}, {141,15}, {159,15}, {169,15}, {180,15}, {193,15}, {203,15}, {214,15},
{3,27}, {16,27}, {26,27}, {36,27}, {47,27}, {57,27}, {68,27}, {78,27}, {89,27}, {99,27}, {109,27}, {120,27}, {130,27}, {143,27}, {159,27}, {169,27}, {180,27}, {193,27}, {203,27}, {214,27},
{4,40}, {18,40}, {29,40}, {39,40}, {50,40}, {60,40}, {70,40}, {81,40}, {91,40}, {102,40},{112,40}, {123,40}, {139,40}, {224, 0}, {224,15}, {224,34}, {224,58}, {193,40}, {203,40}, {214,40},
{7,52}, {23,52}, {34,52}, {44,52}, {55,52}, {65,52}, {76,52}, {86,52}, {96,52}, {107,52}, {117,52}, {137,52}, {169,52}, {193,52}, {203,52}, {214,52},
{1,64}, {14,64}, {27,64}, {66,64}, {105,64}, {118,64}, {131,64}, {145,64}, {159,64}, {169,64}, {180,64}, {198,64}, {214,64},
},
{
// RGB Index to Flag
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
};
#endif

View File

@@ -0,0 +1 @@
# This file intentionally left blank

View File

@@ -0,0 +1,45 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef LED_MATRIX_ENABLE
/* LED matrix driver configuration */
# define LED_MATRIX_LED_COUNT 108
# define LED_MATRIX_VAL_STEP 16
# define DRIVER_CS_PINS \
{ B9 }
/* Use first 9 channels of LED driver */
# define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_9_CHANNEL
/* Set LED driver current */
# define SNLED27351_CURRENT_TUNE \
{ 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56 }
/* Set to infinit, which is use in USB mode by default */
# define LED_MATRIX_TIMEOUT LED_MATRIX_TIMEOUT_INFINITE
/* Allow shutdown of led driver to save power */
# define LED_MATRIX_DRIVER_SHUTDOWN_ENABLE
# define LED_MATRIX_DRIVER_LOAD_ENABLE
/* Turn off backlight on low brightness to save power */
# define LED_MATRIX_BRIGHTNESS_TURN_OFF_VAL 48
/* Indications */
# define DIM_NUM_LOCK
# define NUM_LOCK_INDEX 36
# define LED_MATRIX_KEYPRESSES
#endif

View File

@@ -0,0 +1,34 @@
{
"usb": {
"pid": "0x0AA3",
"device_version": "1.0.0"
},
"features": {
"led_matrix": true,
"encoder": false
},
"indicators": {
"num_lock": "B2"
},
"led_matrix": {
"driver": "snled27351_spi",
"sleep": true,
"animations": {
"none": true,
"solid": true,
"breathing": true,
"band_pinwheel": true,
"band_spiral": true,
"cycle_left_right": true,
"cycle_up_down": true,
"cycle_out_in": true,
"dual_beacon": true,
"solid_reactive_simple": true,
"solid_reactive_multiwide": true,
"solid_reactive_multinexus": true,
"solid_splash": true,
"wave_left_right": true,
"wave_up_down": true
}
}
}

View File

@@ -0,0 +1,67 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
enum layers {
MAC_BASE,
MAC_FN,
WIN_BASE,
WIN_FN,
};
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_ansi_108(
KC_ESC, KC_BRID, KC_BRIU, KC_MCTRL, KC_LNPAD, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_SNAP, KC_SIRI, BL_STEP, KC_F13, KC_F14, KC_F15, KC_F16,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL, KC_END, KC_PGDN, KC_P7, KC_P8, KC_P9,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, KC_RCMMD, KC_ROPTN, MO(MAC_FN), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
[MAC_FN] = LAYOUT_ansi_108(
_______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______, BL_TOGG, _______, _______, _______, _______,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
BL_TOGG, BL_STEP, BL_UP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, BL_DOWN, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, BAT_LVL, NK_TOGG, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______),
[WIN_BASE] = LAYOUT_ansi_108(
KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_PSCR, KC_CTANA, BL_STEP, _______, _______, _______, _______,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL, KC_END, KC_PGDN, KC_P7, KC_P8, KC_P9,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
KC_LCTL, KC_LWIN, KC_LALT, KC_SPC, KC_RALT, KC_RWIN, MO(WIN_FN), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
[WIN_FN] = LAYOUT_ansi_108(
_______, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, BL_TOGG, _______, _______, _______, _______,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
BL_TOGG, BL_STEP, BL_UP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, BL_DOWN, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, BAT_LVL, NK_TOGG, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______)
};
// clang-format on
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_record_keychron_common(keycode, record)) {
return false;
}
return true;
}

View File

@@ -0,0 +1 @@
# This file intentionally left blank

View File

@@ -0,0 +1,172 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
// clang-format off
#ifdef LED_MATRIX_ENABLE
const snled27351_led_t g_snled27351_leds[LED_MATRIX_LED_COUNT] = {
/* Refer to SNLED27351 manual for these locations
* driver
* | LED address
* | | */
{0, CB1_CA16},
{0, CB1_CA14},
{0, CB1_CA13},
{0, CB1_CA12},
{0, CB1_CA11},
{0, CB1_CA10},
{0, CB1_CA9},
{0, CB1_CA8},
{0, CB1_CA7},
{0, CB1_CA6},
{0, CB1_CA5},
{0, CB1_CA4},
{0, CB1_CA3},
{0, CB1_CA2},
{0, CB1_CA1},
{0, CB7_CA1},
{0, CB7_CA2},
{0, CB7_CA3},
{0, CB7_CA4},
{0, CB2_CA16},
{0, CB2_CA15},
{0, CB2_CA14},
{0, CB2_CA13},
{0, CB2_CA12},
{0, CB2_CA11},
{0, CB2_CA10},
{0, CB2_CA9},
{0, CB2_CA8},
{0, CB2_CA7},
{0, CB2_CA6},
{0, CB2_CA5},
{0, CB2_CA4},
{0, CB2_CA3},
{0, CB2_CA2},
{0, CB2_CA1},
{0, CB8_CA1},
{0, CB8_CA2},
{0, CB8_CA3},
{0, CB8_CA4},
{0, CB3_CA16},
{0, CB3_CA15},
{0, CB3_CA14},
{0, CB3_CA13},
{0, CB3_CA12},
{0, CB3_CA11},
{0, CB3_CA10},
{0, CB3_CA9},
{0, CB3_CA8},
{0, CB3_CA7},
{0, CB3_CA6},
{0, CB3_CA5},
{0, CB3_CA4},
{0, CB3_CA3},
{0, CB3_CA2},
{0, CB3_CA1},
{0, CB7_CA6},
{0, CB7_CA7},
{0, CB7_CA8},
{0, CB7_CA9},
{0, CB4_CA16},
{0, CB4_CA15},
{0, CB4_CA14},
{0, CB4_CA13},
{0, CB4_CA12},
{0, CB4_CA11},
{0, CB4_CA10},
{0, CB4_CA9},
{0, CB4_CA8},
{0, CB4_CA7},
{0, CB4_CA6},
{0, CB4_CA5},
{0, CB4_CA4},
{0, CB7_CA5},
{0, CB8_CA5},
{0, CB7_CA10},
{0, CB8_CA10},
{0, CB8_CA7},
{0, CB8_CA8},
{0, CB8_CA9},
{0, CB5_CA16},
{0, CB5_CA14},
{0, CB5_CA13},
{0, CB5_CA12},
{0, CB5_CA11},
{0, CB5_CA10},
{0, CB5_CA9},
{0, CB5_CA8},
{0, CB5_CA7},
{0, CB5_CA6},
{0, CB5_CA5},
{0, CB5_CA3},
{0, CB5_CA1},
{0, CB8_CA6},
{0, CB8_CA11},
{0, CB8_CA12},
{0, CB6_CA16},
{0, CB6_CA15},
{0, CB6_CA14},
{0, CB6_CA10},
{0, CB6_CA6},
{0, CB6_CA5},
{0, CB6_CA4},
{0, CB6_CA3},
{0, CB6_CA2},
{0, CB6_CA1},
{0, CB7_CA13},
{0, CB7_CA11},
{0, CB7_CA12},
};
#define __ NO_LED
led_config_t g_led_config = {
{
// Key Matrix to LED Index
{ 0, __, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38 },
{ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 },
{ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78 },
{ 79, __, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, __, 90, __, 91, __, 92, 93, 94 },
{ 95, 96, 97, __, __, __, 98, __, __, __, 99, 100, 101, 102, 103, 104, 105, __, 106, 107 },
},
{
// LED Index to Physical Position
{0, 0}, {21, 0}, {31, 0}, {42, 0}, {52, 0}, {68, 0}, {78, 0}, {89, 0}, {99, 0}, {115, 0}, {125, 0}, {136, 0}, {146, 0}, {159, 0}, {169, 0}, {180, 0}, {193, 0}, {203, 0}, {214, 0},
{0,15}, {10,15}, {21,15}, {31,15}, {42,15}, {52,15}, {63,15}, {73,15}, {83,15}, {94,15}, {104,15}, {115,15}, {125,15}, {141,15}, {159,15}, {169,15}, {180,15}, {193,15}, {203,15}, {214,15},
{3,27}, {16,27}, {26,27}, {36,27}, {47,27}, {57,27}, {68,27}, {78,27}, {89,27}, {99,27}, {109,27}, {120,27}, {130,27}, {143,27}, {159,27}, {169,27}, {180,27}, {193,27}, {203,27}, {214,27},
{4,40}, {18,40}, {29,40}, {39,40}, {50,40}, {60,40}, {70,40}, {81,40}, {91,40}, {102,40},{112,40}, {123,40}, {139,40}, {224, 0}, {224,15}, {224,34}, {224,58}, {193,40}, {203,40}, {214,40},
{7,52}, {23,52}, {34,52}, {44,52}, {55,52}, {65,52}, {76,52}, {86,52}, {96,52}, {107,52}, {117,52}, {137,52}, {169,52}, {193,52}, {203,52}, {214,52},
{1,64}, {14,64}, {27,64}, {66,64}, {105,64}, {118,64}, {131,64}, {145,64}, {159,64}, {169,64}, {180,64}, {198,64}, {214,64},
},
{
// LED Index to Flag
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
};
#endif

View File

@@ -0,0 +1,225 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include_next <board.h>
// clang-format off
/* Set GPIOA_SWDIO to INPUT and NOT FLOATING */
#undef VAL_GPIOA_MODER
#define VAL_GPIOA_MODER (PIN_MODE_INPUT(GPIOA_BUTTON) | \
PIN_MODE_INPUT(GPIOA_PIN1) | \
PIN_MODE_INPUT(GPIOA_PIN2) | \
PIN_MODE_INPUT(GPIOA_PIN3) | \
PIN_MODE_ALTERNATE(GPIOA_CS43L22_LRCK) |\
PIN_MODE_ALTERNATE(GPIOA_L3GD20_SCL) | \
PIN_MODE_ALTERNATE(GPIOA_L3GD20_SD0) | \
PIN_MODE_ALTERNATE(GPIOA_L3GD20_SDI) | \
PIN_MODE_INPUT(GPIOA_PIN8) | \
PIN_MODE_INPUT(GPIOA_VBUS_FS) | \
PIN_MODE_ALTERNATE(GPIOA_OTG_FS_ID) | \
PIN_MODE_ALTERNATE(GPIOA_OTG_FS_DM) | \
PIN_MODE_ALTERNATE(GPIOA_OTG_FS_DP) | \
PIN_MODE_INPUT(GPIOA_SWDIO) | \
PIN_MODE_INPUT(GPIOA_SWCLK) | \
PIN_MODE_INPUT(GPIOA_PIN15))
#undef VAL_GPIOA_PUPDR
#define VAL_GPIOA_PUPDR (PIN_PUPDR_FLOATING(GPIOA_BUTTON) | \
PIN_PUPDR_PULLUP(GPIOA_PIN1) | \
PIN_PUPDR_PULLUP(GPIOA_PIN2) | \
PIN_PUPDR_PULLUP(GPIOA_PIN3) | \
PIN_PUPDR_FLOATING(GPIOA_CS43L22_LRCK) |\
PIN_PUPDR_FLOATING(GPIOA_L3GD20_SCL) | \
PIN_PUPDR_PULLUP(GPIOA_L3GD20_SD0) | \
PIN_PUPDR_PULLUP(GPIOA_L3GD20_SDI) | \
PIN_PUPDR_PULLDOWN(GPIOA_PIN8) | \
PIN_PUPDR_FLOATING(GPIOA_VBUS_FS) | \
PIN_PUPDR_FLOATING(GPIOA_OTG_FS_ID) | \
PIN_PUPDR_FLOATING(GPIOA_OTG_FS_DM) | \
PIN_PUPDR_FLOATING(GPIOA_OTG_FS_DP) | \
PIN_PUPDR_PULLDOWN(GPIOA_SWDIO) | \
PIN_PUPDR_PULLUP(GPIOA_SWCLK) | \
PIN_PUPDR_PULLUP(GPIOA_PIN15))
#undef VAL_GPIOB_MODER
#define VAL_GPIOB_MODER (PIN_MODE_INPUT(GPIOB_PIN0) | \
PIN_MODE_INPUT(GPIOB_PIN1) | \
PIN_MODE_INPUT(GPIOB_PIN2) | \
PIN_MODE_INPUT(GPIOB_SWO) | \
PIN_MODE_INPUT(GPIOB_PIN4) | \
PIN_MODE_INPUT(GPIOB_PIN5) | \
PIN_MODE_INPUT(GPIOB_LSM303DLHC_SCL) | \
PIN_MODE_INPUT(GPIOB_PIN7) | \
PIN_MODE_INPUT(GPIOB_PIN8) | \
PIN_MODE_INPUT(GPIOB_LSM303DLHC_SDA) | \
PIN_MODE_INPUT(GPIOB_MP45DT02_CLK_IN) |\
PIN_MODE_INPUT(GPIOB_PIN11) | \
PIN_MODE_INPUT(GPIOB_PIN12) | \
PIN_MODE_INPUT(GPIOB_PIN13) | \
PIN_MODE_INPUT(GPIOB_PIN14) | \
PIN_MODE_INPUT(GPIOB_PIN15))
#undef VAL_GPIOB_PUPDR
#define VAL_GPIOB_PUPDR (PIN_PUPDR_PULLDOWN(GPIOB_PIN0) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN1) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN2) | \
PIN_PUPDR_PULLDOWN(GPIOB_SWO) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN4) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN5) | \
PIN_PUPDR_PULLDOWN(GPIOB_LSM303DLHC_SCL) |\
PIN_PUPDR_PULLDOWN(GPIOB_PIN7) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN8) | \
PIN_PUPDR_PULLDOWN(GPIOB_LSM303DLHC_SDA) |\
PIN_PUPDR_PULLDOWN(GPIOB_MP45DT02_CLK_IN) |\
PIN_PUPDR_PULLDOWN(GPIOB_PIN11) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN12) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN13) | \
PIN_PUPDR_PULLUP(GPIOB_PIN14) | \
PIN_PUPDR_PULLUP(GPIOB_PIN15))
#undef VAL_GPIOB_AFRL
#define VAL_GPIOB_AFRL (PIN_AFIO_AF(GPIOB_PIN0, 0U) | \
PIN_AFIO_AF(GPIOB_PIN1, 0U) | \
PIN_AFIO_AF(GPIOB_PIN2, 0U) | \
PIN_AFIO_AF(GPIOB_SWO, 0U) | \
PIN_AFIO_AF(GPIOB_PIN4, 0U) | \
PIN_AFIO_AF(GPIOB_PIN5, 0U) | \
PIN_AFIO_AF(GPIOB_LSM303DLHC_SCL, 0) | \
PIN_AFIO_AF(GPIOB_PIN7, 0U))
#undef VAL_GPIOB_AFRH
#define VAL_GPIOB_AFRH (PIN_AFIO_AF(GPIOB_PIN8, 0U) | \
PIN_AFIO_AF(GPIOB_LSM303DLHC_SDA, 0) | \
PIN_AFIO_AF(GPIOB_MP45DT02_CLK_IN, 0U) |\
PIN_AFIO_AF(GPIOB_PIN11, 0U) | \
PIN_AFIO_AF(GPIOB_PIN12, 0U) | \
PIN_AFIO_AF(GPIOB_PIN13, 0U) | \
PIN_AFIO_AF(GPIOB_PIN14, 0U) | \
PIN_AFIO_AF(GPIOB_PIN15, 0U))
/* C5 Need to be pulldown */
#undef VAL_GPIOC_MODER
#define VAL_GPIOC_MODER (PIN_MODE_INPUT(GPIOC_OTG_FS_POWER_ON) |\
PIN_MODE_INPUT(GPIOC_PIN1) | \
PIN_MODE_INPUT(GPIOC_PIN2) | \
PIN_MODE_INPUT(GPIOC_CS43L22_AIN4x) | \
PIN_MODE_INPUT(GPIOC_PIN4) | \
PIN_MODE_INPUT(GPIOC_PIN5) | \
PIN_MODE_INPUT(GPIOC_PIN6) | \
PIN_MODE_INPUT(GPIOC_CS43L22_MCLK) | \
PIN_MODE_INPUT(GPIOC_PIN8) | \
PIN_MODE_INPUT(GPIOC_PIN9) | \
PIN_MODE_INPUT(GPIOC_CS43L22_SCLK) | \
PIN_MODE_INPUT(GPIOC_PIN11) | \
PIN_MODE_INPUT(GPIOC_CS43L22_SDIN) | \
PIN_MODE_INPUT(GPIOC_PIN13) | \
PIN_MODE_INPUT(GPIOC_OSC32_IN) | \
PIN_MODE_INPUT(GPIOC_OSC32_OUT))
#undef VAL_GPIOC_PUPDR
#define VAL_GPIOC_PUPDR (PIN_PUPDR_PULLUP(GPIOC_OTG_FS_POWER_ON) |\
PIN_PUPDR_PULLUP(GPIOC_PIN1) | \
PIN_PUPDR_PULLUP(GPIOC_PIN2) | \
PIN_PUPDR_PULLUP(GPIOC_CS43L22_AIN4x) |\
PIN_PUPDR_PULLUP(GPIOC_PIN4) | \
PIN_PUPDR_PULLDOWN(GPIOC_PIN5) | \
PIN_PUPDR_PULLDOWN(GPIOC_PIN6) | \
PIN_PUPDR_PULLUP(GPIOC_CS43L22_MCLK) | \
PIN_PUPDR_PULLDOWN(GPIOC_PIN8) | \
PIN_PUPDR_PULLDOWN(GPIOC_PIN9) | \
PIN_PUPDR_PULLUP(GPIOC_CS43L22_SCLK) | \
PIN_PUPDR_PULLUP(GPIOC_PIN11) | \
PIN_PUPDR_PULLUP(GPIOC_CS43L22_SDIN) | \
PIN_PUPDR_PULLUP(GPIOC_PIN13) | \
PIN_PUPDR_PULLUP(GPIOC_OSC32_IN) | \
PIN_PUPDR_PULLUP(GPIOC_OSC32_OUT))
/* Set all GPIOD pins to INPUT & PULLUP to avoid FLOATING */
#undef VAL_GPIOD_MODER
#define VAL_GPIOD_MODER (PIN_MODE_INPUT(GPIOD_PIN0) | \
PIN_MODE_INPUT(GPIOD_PIN1) | \
PIN_MODE_INPUT(GPIOD_PIN2) | \
PIN_MODE_INPUT(GPIOD_PIN3) | \
PIN_MODE_INPUT(GPIOD_CS43L22_RESET) | \
PIN_MODE_INPUT(GPIOD_OverCurrent) | \
PIN_MODE_INPUT(GPIOD_PIN6) | \
PIN_MODE_INPUT(GPIOD_PIN7) | \
PIN_MODE_INPUT(GPIOD_PIN8) | \
PIN_MODE_INPUT(GPIOD_PIN9) | \
PIN_MODE_INPUT(GPIOD_PIN10) | \
PIN_MODE_INPUT(GPIOD_PIN11) | \
PIN_MODE_INPUT(GPIOD_LED4) | \
PIN_MODE_INPUT(GPIOD_LED3) | \
PIN_MODE_INPUT(GPIOD_LED5) | \
PIN_MODE_INPUT(GPIOD_LED6))
#undef VAL_GPIOD_PUPDR
#define VAL_GPIOD_PUPDR (PIN_PUPDR_PULLUP(GPIOD_PIN0) | \
PIN_PUPDR_PULLUP(GPIOD_PIN1) | \
PIN_PUPDR_PULLUP(GPIOD_PIN2) | \
PIN_PUPDR_PULLUP(GPIOD_PIN3) | \
PIN_PUPDR_PULLUP(GPIOD_CS43L22_RESET) |\
PIN_PUPDR_PULLUP(GPIOD_OverCurrent) | \
PIN_PUPDR_PULLUP(GPIOD_PIN6) | \
PIN_PUPDR_PULLUP(GPIOD_PIN7) | \
PIN_PUPDR_PULLUP(GPIOD_PIN8) | \
PIN_PUPDR_PULLUP(GPIOD_PIN9) | \
PIN_PUPDR_PULLUP(GPIOD_PIN10) | \
PIN_PUPDR_PULLUP(GPIOD_PIN11) | \
PIN_PUPDR_PULLUP(GPIOD_LED4) | \
PIN_PUPDR_PULLUP(GPIOD_LED3) | \
PIN_PUPDR_PULLUP(GPIOD_LED5) | \
PIN_PUPDR_PULLUP(GPIOD_LED6))
/* Set all GPIOE pins to INPUT & PULLUP to avoid FLOATING */
#undef VAL_GPIOE_MODER
#define VAL_GPIOE_MODER (PIN_MODE_INPUT(GPIOE_L3GD20_INT1) | \
PIN_MODE_INPUT(GPIOE_L3GD20_INT2) | \
PIN_MODE_INPUT(GPIOE_LSM303DLHC_DRDY) |\
PIN_MODE_INPUT(GPIOE_L3GD20_CS) | \
PIN_MODE_INPUT(GPIOE_LSM303DLHC_INT1) |\
PIN_MODE_INPUT(GPIOE_LSM303DLHC_INT2) |\
PIN_MODE_INPUT(GPIOE_PIN6) | \
PIN_MODE_INPUT(GPIOE_PIN7) | \
PIN_MODE_INPUT(GPIOE_PIN8) | \
PIN_MODE_INPUT(GPIOE_PIN9) | \
PIN_MODE_INPUT(GPIOE_PIN10) | \
PIN_MODE_INPUT(GPIOE_PIN11) | \
PIN_MODE_INPUT(GPIOE_PIN12) | \
PIN_MODE_INPUT(GPIOE_PIN13) | \
PIN_MODE_INPUT(GPIOE_PIN14) | \
PIN_MODE_INPUT(GPIOE_PIN15))
#undef VAL_GPIOE_PUPDR
#define VAL_GPIOE_PUPDR (PIN_PUPDR_PULLUP(GPIOE_L3GD20_INT1) | \
PIN_PUPDR_PULLUP(GPIOE_L3GD20_INT2) | \
PIN_PUPDR_PULLUP(GPIOE_LSM303DLHC_DRDY) |\
PIN_PUPDR_PULLUP(GPIOE_L3GD20_CS) | \
PIN_PUPDR_PULLUP(GPIOE_LSM303DLHC_INT1) |\
PIN_PUPDR_PULLUP(GPIOE_LSM303DLHC_INT2) |\
PIN_PUPDR_PULLUP(GPIOE_PIN6) | \
PIN_PUPDR_PULLUP(GPIOE_PIN7) | \
PIN_PUPDR_PULLUP(GPIOE_PIN8) | \
PIN_PUPDR_PULLUP(GPIOE_PIN9) | \
PIN_PUPDR_PULLUP(GPIOE_PIN10) | \
PIN_PUPDR_PULLUP(GPIOE_PIN11) | \
PIN_PUPDR_PULLUP(GPIOE_PIN12) | \
PIN_PUPDR_PULLUP(GPIOE_PIN13) | \
PIN_PUPDR_PULLUP(GPIOE_PIN14) | \
PIN_PUPDR_PULLUP(GPIOE_PIN15))

View File

@@ -0,0 +1,83 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE) || defined(LK_WIRELESS_ENABLE)
/* SPI configuration */
# define SPI_DRIVER SPID1
# define SPI_SCK_PIN A5
# define SPI_MISO_PIN A6
# define SPI_MOSI_PIN A7
#endif
#if defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE)
# define SNLED27351_SDB_PIN B7
# define SNLED23751_SPI_DIVISOR 16
#endif
#ifdef LK_WIRELESS_ENABLE
/* Hardware configuration */
# define P24G_MODE_SELECT_PIN A9
# define BT_MODE_SELECT_PIN A10
# define LKBT51_RESET_PIN C4
# define LKBT51_INT_INPUT_PIN B1
# define LKBT51_INT_OUTPUT_PIN A4
# define USB_POWER_SENSE_PIN B0
# define USB_POWER_CONNECTED_LEVEL 0
# define BAT_CHARGING_PIN B13
# define BAT_CHARGING_LEVEL 0
# define BAT_LOW_LED_PIN C5
# define BAT_LOW_LED_PIN_ON_STATE 1
# define BT_HOST_DEVICES_COUNT 3
# define BT_INDICATION_LED_PIN_LIST \
{ B15, B15, B15 }
# define BT_INDICATION_LED_ON_STATE 0
# define P24G_HOST_DEVICES_COUNT 1
# define P24G_INDICATION_LED_PIN_LIST \
{ B14 }
# if defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE)
# define P24G_INDICATION_LED_MATRIX_INDEX 23
# define BT_INDCATION_LED_MATRIX_LIST \
{ 20, 21, 22 }
# define BAT_LEVEL_LED_LIST \
{ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 }
/* Backlit disable timeout when keyboard is disconnected(unit: second) */
# define DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT 40
/* Backlit disable timeout when keyboard is connected(unit: second) */
# define CONNECTED_BACKLIGHT_DISABLE_TIMEOUT 600
/* Reinit LED driver on tranport changed */
# define REINIT_LED_DRIVER 1
# endif
/* Keep USB connection in wireless mode */
# define KEEP_USB_CONNECTION_IN_WIRELESS_MODE
#endif
/* Factory test keys */
#define FN_KEY_1 MO(1)
#define FN_KEY_2 MO(3)
#define MATRIX_IO_DELAY 10

View File

@@ -0,0 +1,28 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software : you can redistribute it and /or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see < http://www.gnu.org/licenses/>.
*/
#pragma once
#define _CHIBIOS_HAL_CONF_VER_8_4_
#define HAL_USE_SPI TRUE
#ifdef LK_WIRELESS_ENABLE
# define HAL_USE_RTC TRUE
# define PAL_USE_CALLBACKS TRUE
#endif
#include_next <halconf.h>

View File

@@ -0,0 +1,281 @@
{
"keyboard_name": "Keychron K10 Max",
"manufacturer": "Keychron",
"url": "https://github.com/Keychron",
"maintainer": "Keychron",
"processor": "STM32F401",
"bootloader": "stm32-dfu",
"usb": {
"vid": "0x3434"
},
"features": {
"bootmagic": true,
"extrakey": true,
"mousekey": true,
"dip_switch": true,
"encoder": false,
"encoder_map": false,
"nkro": true,
"raw": true,
"sendstring": true
},
"diode_direction": "ROW2COL",
"dip_switch" :{
"pins": ["B12"]
},
"dynamic_keymap": {
"layer_count": 4
},
"eeprom": {
"wear_leveling": {
"driver": "embedded_flash",
"logical_size": 2048,
"backing_size": 4096
}
},
"indicators": {
"caps_lock": "C9",
"on_state": 1
},
"matrix_pins": {
"cols": ["C6", "C7", "C8", "A13", "A14", "A15", "C10", "C11", "C13", "C14", "C15", "C0", "C1", "C2", "C3", "A0", "A1", "A2", "A3", "B10"],
"rows": ["C12", "D2", "B3", "B4", "B5", "B6"]
},
"layouts": {
"LAYOUT_ansi_108": {
"layout": [
{"matrix": [0, 0], "x": 0, "y": 0},
{"matrix": [0, 2], "x": 2, "y": 0},
{"matrix": [0, 3], "x": 3, "y": 0},
{"matrix": [0, 4], "x": 4, "y": 0},
{"matrix": [0, 5], "x": 5, "y": 0},
{"matrix": [0, 6], "x": 6.5, "y": 0},
{"matrix": [0, 7], "x": 7.5, "y": 0},
{"matrix": [0, 8], "x": 8.5, "y": 0},
{"matrix": [0, 9], "x": 9.5, "y": 0},
{"matrix": [0, 10], "x": 11, "y": 0},
{"matrix": [0, 11], "x": 12, "y": 0},
{"matrix": [0, 12], "x": 13, "y": 0},
{"matrix": [0, 13], "x": 14, "y": 0},
{"matrix": [0, 14], "x": 15.25, "y": 0},
{"matrix": [0, 15], "x": 16.25, "y": 0},
{"matrix": [0, 16], "x": 17.25, "y": 0},
{"matrix": [0, 17], "x": 18.5, "y": 0},
{"matrix": [0, 18], "x": 19.5, "y": 0},
{"matrix": [0, 19], "x": 20.5, "y": 0},
{"matrix": [3, 13], "x": 21.5, "y": 0},
{"matrix": [1, 0], "x": 0, "y": 1.25},
{"matrix": [1, 1], "x": 1, "y": 1.25},
{"matrix": [1, 2], "x": 2, "y": 1.25},
{"matrix": [1, 3], "x": 3, "y": 1.25},
{"matrix": [1, 4], "x": 4, "y": 1.25},
{"matrix": [1, 5], "x": 5, "y": 1.25},
{"matrix": [1, 6], "x": 6, "y": 1.25},
{"matrix": [1, 7], "x": 7, "y": 1.25},
{"matrix": [1, 8], "x": 8, "y": 1.25},
{"matrix": [1, 9], "x": 9, "y": 1.25},
{"matrix": [1, 10], "x": 10, "y": 1.25},
{"matrix": [1, 11], "x": 11, "y": 1.25},
{"matrix": [1, 12], "x": 12, "y": 1.25},
{"matrix": [1, 13], "x": 13, "y": 1.25, "w": 2},
{"matrix": [1, 14], "x": 15.25, "y": 1.25},
{"matrix": [1, 15], "x": 16.25, "y": 1.25},
{"matrix": [1, 16], "x": 17.25, "y": 1.25},
{"matrix": [1, 17], "x": 18.5, "y": 1.25},
{"matrix": [1, 18], "x": 19.5, "y": 1.25},
{"matrix": [1, 19], "x": 20.5, "y": 1.25},
{"matrix": [3, 14], "x": 21.5, "y": 1.25},
{"matrix": [2, 0], "x": 0, "y": 2.25, "w": 1.5},
{"matrix": [2, 1], "x": 1.5, "y": 2.25},
{"matrix": [2, 2], "x": 2.5, "y": 2.25},
{"matrix": [2, 3], "x": 3.5, "y": 2.25},
{"matrix": [2, 4], "x": 4.5, "y": 2.25},
{"matrix": [2, 5], "x": 5.5, "y": 2.25},
{"matrix": [2, 6], "x": 6.5, "y": 2.25},
{"matrix": [2, 7], "x": 7.5, "y": 2.25},
{"matrix": [2, 8], "x": 8.5, "y": 2.25},
{"matrix": [2, 9], "x": 9.5, "y": 2.25},
{"matrix": [2, 10], "x": 10.5, "y": 2.25},
{"matrix": [2, 11], "x": 11.5, "y": 2.25},
{"matrix": [2, 12], "x": 12.5, "y": 2.25},
{"matrix": [2, 13], "x": 13.5, "y": 2.25, "w": 1.5},
{"matrix": [2, 14], "x": 15.25, "y": 2.25},
{"matrix": [2, 15], "x": 16.25, "y": 2.25},
{"matrix": [2, 16], "x": 17.25, "y": 2.25},
{"matrix": [2, 17], "x": 18.5, "y": 2.25},
{"matrix": [2, 18], "x": 19.5, "y": 2.25},
{"matrix": [2, 19], "x": 20.5, "y": 2.25},
{"matrix": [3, 0], "x": 0, "y": 3.25, "w": 1.75},
{"matrix": [3, 1], "x": 1.75, "y": 3.25},
{"matrix": [3, 2], "x": 2.75, "y": 3.25},
{"matrix": [3, 3], "x": 3.75, "y": 3.25},
{"matrix": [3, 4], "x": 4.75, "y": 3.25},
{"matrix": [3, 5], "x": 5.75, "y": 3.25},
{"matrix": [3, 6], "x": 6.75, "y": 3.25},
{"matrix": [3, 7], "x": 7.75, "y": 3.25},
{"matrix": [3, 8], "x": 8.75, "y": 3.25},
{"matrix": [3, 9], "x": 9.75, "y": 3.25},
{"matrix": [3, 10], "x": 10.75, "y": 3.25},
{"matrix": [3, 11], "x": 11.75, "y": 3.25},
{"matrix": [3, 12], "x": 12.75, "y": 3.25, "w": 2.25},
{"matrix": [3, 17], "x": 18.5, "y": 3.25},
{"matrix": [3, 18], "x": 19.5, "y": 3.25},
{"matrix": [3, 19], "x": 20.5, "y": 3.25},
{"matrix": [3, 15], "x": 21.5, "y": 2.25, "h": 2},
{"matrix": [4, 0], "x": 0, "y": 4.25, "w": 2.25},
{"matrix": [4, 2], "x": 2.25, "y": 4.25},
{"matrix": [4, 3], "x": 3.25, "y": 4.25},
{"matrix": [4, 4], "x": 4.25, "y": 4.25},
{"matrix": [4, 5], "x": 5.25, "y": 4.25},
{"matrix": [4, 6], "x": 6.25, "y": 4.25},
{"matrix": [4, 7], "x": 7.25, "y": 4.25},
{"matrix": [4, 8], "x": 8.25, "y": 4.25},
{"matrix": [4, 9], "x": 9.25, "y": 4.25},
{"matrix": [4, 10], "x": 10.25, "y": 4.25},
{"matrix": [4, 11], "x": 11.25, "y": 4.25},
{"matrix": [4, 13], "x": 12.25, "y": 4.25, "w": 2.75},
{"matrix": [4, 15], "x": 16.25, "y": 4.25},
{"matrix": [4, 17], "x": 18.5, "y": 4.25},
{"matrix": [4, 18], "x": 19.5, "y": 4.25},
{"matrix": [4, 19], "x": 20.5, "y": 4.25},
{"matrix": [5, 0], "x": 0, "y": 5.25, "w": 1.25},
{"matrix": [5, 1], "x": 1.25, "y": 5.25, "w": 1.25},
{"matrix": [5, 2], "x": 2.5, "y": 5.25, "w": 1.25},
{"matrix": [5, 6], "x": 3.75, "y": 5.25, "w": 6.25},
{"matrix": [5, 10], "x": 10, "y": 5.25, "w": 1.25},
{"matrix": [5, 11], "x": 11.25, "y": 5.25, "w": 1.25},
{"matrix": [5, 12], "x": 12.5, "y": 5.25, "w": 1.25},
{"matrix": [5, 13], "x": 13.75, "y": 5.25, "w": 1.25},
{"matrix": [5, 14], "x": 15.25, "y": 5.25},
{"matrix": [5, 15], "x": 16.25, "y": 5.25},
{"matrix": [5, 16], "x": 17.25, "y": 5.25},
{"matrix": [5, 18], "x": 18.5, "y": 5.25, "w": 2},
{"matrix": [5, 19], "x": 20.5, "y": 5.25},
{"matrix": [3, 16], "x": 21.5, "y": 4.25, "h": 2}
]
},
"LAYOUT_iso_109": {
"layout": [
{"matrix": [0, 0], "x": 0, "y": 0},
{"matrix": [0, 2], "x": 2, "y": 0},
{"matrix": [0, 3], "x": 3, "y": 0},
{"matrix": [0, 4], "x": 4, "y": 0},
{"matrix": [0, 5], "x": 5, "y": 0},
{"matrix": [0, 6], "x": 6.5, "y": 0},
{"matrix": [0, 7], "x": 7.5, "y": 0},
{"matrix": [0, 8], "x": 8.5, "y": 0},
{"matrix": [0, 9], "x": 9.5, "y": 0},
{"matrix": [0, 10], "x": 11, "y": 0},
{"matrix": [0, 11], "x": 12, "y": 0},
{"matrix": [0, 12], "x": 13, "y": 0},
{"matrix": [0, 13], "x": 14, "y": 0},
{"matrix": [0, 14], "x": 15.25, "y": 0},
{"matrix": [0, 15], "x": 16.25, "y": 0},
{"matrix": [0, 16], "x": 17.25, "y": 0},
{"matrix": [0, 17], "x": 18.5, "y": 0},
{"matrix": [0, 18], "x": 19.5, "y": 0},
{"matrix": [0, 19], "x": 20.5, "y": 0},
{"matrix": [3, 13], "x": 21.5, "y": 0},
{"matrix": [1, 0], "x": 0, "y": 1.25},
{"matrix": [1, 1], "x": 1, "y": 1.25},
{"matrix": [1, 2], "x": 2, "y": 1.25},
{"matrix": [1, 3], "x": 3, "y": 1.25},
{"matrix": [1, 4], "x": 4, "y": 1.25},
{"matrix": [1, 5], "x": 5, "y": 1.25},
{"matrix": [1, 6], "x": 6, "y": 1.25},
{"matrix": [1, 7], "x": 7, "y": 1.25},
{"matrix": [1, 8], "x": 8, "y": 1.25},
{"matrix": [1, 9], "x": 9, "y": 1.25},
{"matrix": [1, 10], "x": 10, "y": 1.25},
{"matrix": [1, 11], "x": 11, "y": 1.25},
{"matrix": [1, 12], "x": 12, "y": 1.25},
{"matrix": [1, 13], "x": 13, "y": 1.25, "w": 2},
{"matrix": [1, 14], "x": 15.25, "y": 1.25},
{"matrix": [1, 15], "x": 16.25, "y": 1.25},
{"matrix": [1, 16], "x": 17.25, "y": 1.25},
{"matrix": [1, 17], "x": 18.5, "y": 1.25},
{"matrix": [1, 18], "x": 19.5, "y": 1.25},
{"matrix": [1, 19], "x": 20.5, "y": 1.25},
{"matrix": [3, 14], "x": 21.5, "y": 1.25},
{"matrix": [2, 0], "x": 0, "y": 2.25, "w": 1.5},
{"matrix": [2, 1], "x": 1.5, "y": 2.25},
{"matrix": [2, 2], "x": 2.5, "y": 2.25},
{"matrix": [2, 3], "x": 3.5, "y": 2.25},
{"matrix": [2, 4], "x": 4.5, "y": 2.25},
{"matrix": [2, 5], "x": 5.5, "y": 2.25},
{"matrix": [2, 6], "x": 6.5, "y": 2.25},
{"matrix": [2, 7], "x": 7.5, "y": 2.25},
{"matrix": [2, 8], "x": 8.5, "y": 2.25},
{"matrix": [2, 9], "x": 9.5, "y": 2.25},
{"matrix": [2, 10], "x": 10.5, "y": 2.25},
{"matrix": [2, 11], "x": 11.5, "y": 2.25},
{"matrix": [2, 12], "x": 12.5, "y": 2.25},
{"matrix": [2, 14], "x": 15.25, "y": 2.25},
{"matrix": [2, 15], "x": 16.25, "y": 2.25},
{"matrix": [2, 16], "x": 17.25, "y": 2.25},
{"matrix": [2, 17], "x": 18.5, "y": 2.25},
{"matrix": [2, 18], "x": 19.5, "y": 2.25},
{"matrix": [2, 19], "x": 20.5, "y": 2.25},
{"matrix": [3, 0], "x": 0, "y": 3.25, "w": 1.75},
{"matrix": [3, 1], "x": 1.75, "y": 3.25},
{"matrix": [3, 2], "x": 2.75, "y": 3.25},
{"matrix": [3, 3], "x": 3.75, "y": 3.25},
{"matrix": [3, 4], "x": 4.75, "y": 3.25},
{"matrix": [3, 5], "x": 5.75, "y": 3.25},
{"matrix": [3, 6], "x": 6.75, "y": 3.25},
{"matrix": [3, 7], "x": 7.75, "y": 3.25},
{"matrix": [3, 8], "x": 8.75, "y": 3.25},
{"matrix": [3, 9], "x": 9.75, "y": 3.25},
{"matrix": [3, 10], "x": 10.75, "y": 3.25},
{"matrix": [3, 11], "x": 11.75, "y": 3.25},
{"matrix": [3, 12], "x": 12.75, "y": 3.25},
{"matrix": [2, 13], "x": 13.75, "y": 2.25, "w": 1.25, "h": 2},
{"matrix": [3, 17], "x": 18.5, "y": 3.25},
{"matrix": [3, 18], "x": 19.5, "y": 3.25},
{"matrix": [3, 19], "x": 20.5, "y": 3.25},
{"matrix": [3, 15], "x": 21.5, "y": 2.25, "h": 2},
{"matrix": [4, 0], "x": 0, "y": 4.25, "w": 1.25},
{"matrix": [4, 1], "x": 1.25, "y": 4.25},
{"matrix": [4, 2], "x": 2.25, "y": 4.25},
{"matrix": [4, 3], "x": 3.25, "y": 4.25},
{"matrix": [4, 4], "x": 4.25, "y": 4.25},
{"matrix": [4, 5], "x": 5.25, "y": 4.25},
{"matrix": [4, 6], "x": 6.25, "y": 4.25},
{"matrix": [4, 7], "x": 7.25, "y": 4.25},
{"matrix": [4, 8], "x": 8.25, "y": 4.25},
{"matrix": [4, 9], "x": 9.25, "y": 4.25},
{"matrix": [4, 10], "x": 10.25, "y": 4.25},
{"matrix": [4, 11], "x": 11.25, "y": 4.25},
{"matrix": [4, 13], "x": 12.25, "y": 4.25, "w": 2.75},
{"matrix": [4, 15], "x": 16.25, "y": 4.25},
{"matrix": [4, 17], "x": 18.5, "y": 4.25},
{"matrix": [4, 18], "x": 19.5, "y": 4.25},
{"matrix": [4, 19], "x": 20.5, "y": 4.25},
{"matrix": [5, 0], "x": 0, "y": 5.25, "w": 1.25},
{"matrix": [5, 1], "x": 1.25, "y": 5.25, "w": 1.25},
{"matrix": [5, 2], "x": 2.5, "y": 5.25, "w": 1.25},
{"matrix": [5, 6], "x": 3.75, "y": 5.25, "w": 6.25},
{"matrix": [5, 10], "x": 10, "y": 5.25, "w": 1.25},
{"matrix": [5, 11], "x": 11.25, "y": 5.25, "w": 1.25},
{"matrix": [5, 12], "x": 12.5, "y": 5.25, "w": 1.25},
{"matrix": [5, 13], "x": 13.75, "y": 5.25, "w": 1.25},
{"matrix": [5, 14], "x": 15.25, "y": 5.25},
{"matrix": [5, 15], "x": 16.25, "y": 5.25},
{"matrix": [5, 16], "x": 17.25, "y": 5.25},
{"matrix": [5, 18], "x": 18.5, "y": 5.25, "w": 2},
{"matrix": [5, 19], "x": 20.5, "y": 5.25},
{"matrix": [3, 16], "x": 21.5, "y": 4.25, "h": 2}
]
}
}
}

View File

@@ -0,0 +1,43 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef RGB_MATRIX_ENABLE
/* RGB Matrix driver configuration */
# define RGB_MATRIX_LED_COUNT 109
# define DRIVER_CS_PINS \
{ B8, B9 }
/* Set LED driver current */
# define SNLED27351_CURRENT_TUNE \
{ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C }
/* Set to infinit, which is use in USB mode by default */
# define RGB_MATRIX_TIMEOUT RGB_MATRIX_TIMEOUT_INFINITE
/* Allow shutdown of led driver to save power */
# define RGB_MATRIX_DRIVER_SHUTDOWN_ENABLE
# define RGB_MATRIX_DRIVER_LOAD_ENABLE
/* Turn off backlight on low brightness to save power */
# define RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL 48
/* Indications */
# define NUM_LOCK_INDEX 36
# define RGB_MATRIX_KEYPRESSES
# define RGB_MATRIX_FRAMEBUFFER_EFFECTS
#endif

View File

@@ -0,0 +1,36 @@
{
"usb": {
"pid": "0x0AA1",
"device_version": "1.0.0"
},
"features": {
"rgb_matrix": true
},
"rgb_matrix": {
"driver": "snled27351_spi",
"sleep": true,
"animations": {
"band_spiral_val": true,
"breathing": true,
"cycle_all": true,
"cycle_left_right": true,
"cycle_out_in": true,
"cycle_out_in_dual": true,
"cycle_pinwheel": true,
"cycle_spiral": true,
"cycle_up_down": true,
"digital_rain": true,
"dual_beacon": true,
"jellybean_raindrops": true,
"pixel_rain": true,
"rainbow_beacon": true,
"rainbow_moving_chevron": true,
"solid_reactive_multinexus": true,
"solid_reactive_multiwide": true,
"solid_reactive_simple": true,
"solid_splash": true,
"splash": true,
"typing_heatmap": true
}
}
}

View File

@@ -0,0 +1,67 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
enum layers {
MAC_BASE,
MAC_FN,
WIN_BASE,
WIN_FN,
};
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_iso_109(
KC_ESC, KC_BRID, KC_BRIU, KC_MCTRL, KC_LNPAD, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_SNAP, KC_SIRI, RGB_MOD, KC_F13, KC_F14, KC_F15, KC_F16,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_DEL, KC_END, KC_PGDN, KC_P7, KC_P8, KC_P9,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, KC_RCMMD, KC_ROPTN, MO(MAC_FN), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
[MAC_FN] = LAYOUT_iso_109(
_______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______, RGB_TOG, _______, _______, _______, _______,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, BAT_LVL, NK_TOGG, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______),
[WIN_BASE] = LAYOUT_iso_109(
KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_PSCR, KC_CTANA, RGB_MOD, _______, _______, _______, _______,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_DEL, KC_END, KC_PGDN, KC_P7, KC_P8, KC_P9,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
KC_LCTL, KC_LWIN, KC_LALT, KC_SPC, KC_RALT, KC_RWIN, MO(WIN_FN), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
[WIN_FN] = LAYOUT_iso_109(
_______, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, RGB_TOG, _______, _______, _______, _______,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, BAT_LVL, NK_TOGG, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______)
};
// clang-format on
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_record_keychron_common(keycode, record)) {
return false;
}
return true;
}

View File

@@ -0,0 +1,176 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software : you can redistribute it and /or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see < http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
// clang-format off
#ifdef RGB_MATRIX_ENABLE
const snled27351_led_t g_snled27351_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to SNLED27351 manual for these locations
* driver
* | R location
* | | G location
* | | | B location
* | | | | */
{0, CB7_CA16, CB9_CA16, CB8_CA16},
{0, CB7_CA14, CB9_CA14, CB8_CA14},
{0, CB7_CA13, CB9_CA13, CB8_CA13},
{0, CB7_CA12, CB9_CA12, CB8_CA12},
{0, CB7_CA11, CB9_CA11, CB8_CA11},
{0, CB7_CA10, CB9_CA10, CB8_CA10},
{0, CB7_CA9, CB9_CA9, CB8_CA9},
{0, CB7_CA8, CB9_CA8, CB8_CA8},
{0, CB7_CA7, CB9_CA7, CB8_CA7},
{0, CB7_CA6, CB9_CA6, CB8_CA6},
{0, CB7_CA5, CB9_CA5, CB8_CA5},
{0, CB7_CA4, CB9_CA4, CB8_CA4},
{0, CB7_CA3, CB9_CA3, CB8_CA3},
{0, CB7_CA2, CB9_CA2, CB8_CA2},
{0, CB7_CA1, CB9_CA1, CB8_CA1},
{1, CB1_CA3, CB3_CA3, CB2_CA3},
{0, CB4_CA4, CB6_CA4, CB5_CA4},
{0, CB4_CA3, CB6_CA3, CB5_CA3},
{0, CB4_CA2, CB6_CA2, CB5_CA2},
{0, CB1_CA16, CB3_CA16, CB2_CA16},
{0, CB1_CA15, CB3_CA15, CB2_CA15},
{0, CB1_CA14, CB3_CA14, CB2_CA14},
{0, CB1_CA13, CB3_CA13, CB2_CA13},
{0, CB1_CA12, CB3_CA12, CB2_CA12},
{0, CB1_CA11, CB3_CA11, CB2_CA11},
{0, CB1_CA10, CB3_CA10, CB2_CA10},
{0, CB1_CA9, CB3_CA9, CB2_CA9},
{0, CB1_CA8, CB3_CA8, CB2_CA8},
{0, CB1_CA7, CB3_CA7, CB2_CA7},
{0, CB1_CA6, CB3_CA6, CB2_CA6},
{0, CB1_CA5, CB3_CA5, CB2_CA5},
{0, CB1_CA4, CB3_CA4, CB2_CA4},
{0, CB1_CA3, CB3_CA3, CB2_CA3},
{0, CB1_CA2, CB3_CA2, CB2_CA2},
{0, CB1_CA1, CB3_CA1, CB2_CA1},
{1, CB1_CA2, CB3_CA2, CB2_CA2},
{0, CB4_CA8, CB6_CA8, CB5_CA8},
{0, CB4_CA7, CB6_CA7, CB5_CA7},
{0, CB4_CA6, CB6_CA6, CB5_CA6},
{0, CB10_CA16, CB12_CA16, CB11_CA16},
{0, CB10_CA15, CB12_CA15, CB11_CA15},
{0, CB10_CA14, CB12_CA14, CB11_CA14},
{0, CB10_CA13, CB12_CA13, CB11_CA13},
{0, CB10_CA12, CB12_CA12, CB11_CA12},
{0, CB10_CA11, CB12_CA11, CB11_CA11},
{0, CB10_CA10, CB12_CA10, CB11_CA10},
{0, CB10_CA9, CB12_CA9, CB11_CA9},
{0, CB10_CA8, CB12_CA8, CB11_CA8},
{0, CB10_CA7, CB12_CA7, CB11_CA7},
{0, CB10_CA6, CB12_CA6, CB11_CA6},
{0, CB10_CA5, CB12_CA5, CB11_CA5},
{0, CB10_CA4, CB12_CA4, CB11_CA4},
{0, CB10_CA3, CB12_CA3, CB11_CA3},
{0, CB10_CA2, CB12_CA2, CB11_CA2},
{0, CB10_CA1, CB12_CA1, CB11_CA1},
{1, CB1_CA1, CB3_CA1, CB2_CA1},
{0, CB4_CA12, CB6_CA12, CB5_CA12},
{0, CB4_CA11, CB6_CA11, CB5_CA11},
{0, CB4_CA10, CB6_CA10, CB5_CA10},
{1, CB1_CA16, CB3_CA16, CB2_CA16},
{1, CB1_CA15, CB3_CA15, CB2_CA15},
{1, CB1_CA14, CB3_CA14, CB2_CA14},
{1, CB1_CA13, CB3_CA13, CB2_CA13},
{1, CB1_CA12, CB3_CA12, CB2_CA12},
{1, CB1_CA11, CB3_CA11, CB2_CA11},
{1, CB1_CA10, CB3_CA10, CB2_CA10},
{1, CB1_CA9, CB3_CA9, CB2_CA9},
{1, CB1_CA8, CB3_CA8, CB2_CA8},
{1, CB1_CA7, CB3_CA7, CB2_CA7},
{1, CB1_CA6, CB3_CA6, CB2_CA6},
{1, CB1_CA5, CB3_CA5, CB2_CA5},
{1, CB1_CA4, CB3_CA4, CB2_CA4},
{0, CB4_CA1, CB6_CA1, CB5_CA1},
{0, CB4_CA5, CB6_CA5, CB5_CA5},
{0, CB4_CA9, CB6_CA9, CB5_CA9},
{1, CB10_CA3, CB12_CA3, CB11_CA3},
{1, CB10_CA9, CB12_CA9, CB11_CA9},
{1, CB10_CA8, CB12_CA8, CB11_CA8},
{1, CB10_CA7, CB12_CA7, CB11_CA7},
{1, CB7_CA16, CB9_CA16, CB8_CA16},
{1, CB7_CA15, CB9_CA15, CB8_CA15},
{1, CB7_CA14, CB9_CA14, CB8_CA14},
{1, CB7_CA13, CB9_CA13, CB8_CA13},
{1, CB7_CA12, CB9_CA12, CB8_CA12},
{1, CB7_CA11, CB9_CA11, CB8_CA11},
{1, CB7_CA10, CB9_CA10, CB8_CA10},
{1, CB7_CA9, CB9_CA9, CB8_CA9},
{1, CB7_CA8, CB9_CA8, CB8_CA8},
{1, CB7_CA7, CB9_CA7, CB8_CA7},
{1, CB7_CA6, CB9_CA6, CB8_CA6},
{1, CB7_CA5, CB9_CA5, CB8_CA5},
{1, CB7_CA3, CB9_CA3, CB8_CA3},
{1, CB7_CA1, CB9_CA1, CB8_CA1},
{1, CB10_CA6, CB12_CA6, CB11_CA6},
{1, CB10_CA5, CB12_CA5, CB11_CA5},
{1, CB10_CA4, CB12_CA4, CB11_CA4},
{1, CB4_CA16, CB6_CA16, CB5_CA16},
{1, CB4_CA15, CB6_CA15, CB5_CA15},
{1, CB4_CA14, CB6_CA14, CB5_CA14},
{1, CB4_CA10, CB6_CA10, CB5_CA10},
{1, CB4_CA6, CB6_CA6, CB5_CA6},
{1, CB4_CA5, CB6_CA5, CB5_CA5},
{1, CB4_CA4, CB6_CA4, CB5_CA4},
{1, CB4_CA3, CB6_CA3, CB5_CA3},
{1, CB4_CA2, CB6_CA2, CB5_CA2},
{1, CB4_CA1, CB6_CA1, CB5_CA1},
{1, CB7_CA2, CB9_CA2, CB8_CA2},
{1, CB10_CA2, CB12_CA2, CB11_CA2},
{1, CB10_CA1, CB12_CA1, CB11_CA1},
};
#define __ NO_LED
led_config_t g_led_config = {
{
// Key Matrix to RGB Index
{ 0, __, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38 },
{ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 },
{ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78 },
{ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, __, 91, __, 92, __, 93, 94, 95 },
{ 96, 97, 98, __, __, __, 99, __, __, __, 100, 101, 102, 103, 104, 105, 106, __, 107, 108 },
},
{
// RGB Index to Physical Position
{0, 0}, {21, 0}, {31, 0}, {42, 0}, {52, 0}, {68, 0}, {78, 0}, {89, 0}, {99, 0}, {115, 0}, {125, 0}, {136, 0}, {146, 0}, {159, 0}, {169, 0}, {180, 0}, {193, 0}, {203, 0}, {214, 0},
{0,15}, {10,15}, {21,15}, {31,15}, {42,15}, {52,15}, {63,15}, {73,15}, {83,15}, {94,15}, {104,15}, {115,15}, {125,15}, {141,15}, {159,15}, {169,15}, {180,15}, {193,15}, {203,15}, {214,15},
{3,27}, {16,27}, {26,27}, {36,27}, {47,27}, {57,27}, {68,27}, {78,27}, {89,27}, {99,27}, {109,27}, {120,27}, {130,27}, {143,33}, {159,27}, {169,27}, {180,27}, {193,27}, {203,27}, {214,27},
{4,40}, {18,40}, {29,40}, {39,40}, {50,40}, {60,40}, {70,40}, {81,40}, {91,40}, {102,40},{112,40}, {123,40}, {134,40}, {224, 0}, {224,15}, {224,34}, {224,58}, {193,40}, {203,40}, {214,40},
{1,52}, {13,52}, {23,52}, {34,52}, {44,52}, {55,52}, {65,52}, {76,52}, {86,52}, {96,52}, {107,52}, {117,52}, {137,52}, {169,52}, {193,52}, {203,52}, {214,52},
{1,64}, {14,64}, {27,64}, {66,64}, {105,64}, {118,64}, {131,64}, {145,64}, {159,64}, {169,64}, {180,64}, {198,64}, {214,64},
},
{
// RGB Index to Flag
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
};
#endif

View File

@@ -0,0 +1 @@
# This file intentionally left blank

View File

@@ -0,0 +1,45 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef LED_MATRIX_ENABLE
/* LED matrix driver configuration */
# define LED_MATRIX_LED_COUNT 109
# define LED_MATRIX_VAL_STEP 16
# define DRIVER_CS_PINS \
{ B9 }
/* Use first 9 channels of LED driver */
# define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_9_CHANNEL
/* Set LED driver current */
# define SNLED27351_CURRENT_TUNE \
{ 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56 }
/* Set to infinit, which is use in USB mode by default */
# define LED_MATRIX_TIMEOUT LED_MATRIX_TIMEOUT_INFINITE
/* Allow shutdown of led driver to save power */
# define LED_MATRIX_DRIVER_SHUTDOWN_ENABLE
# define LED_MATRIX_DRIVER_LOAD_ENABLE
/* Turn off backlight on low brightness to save power */
# define LED_MATRIX_BRIGHTNESS_TURN_OFF_VAL 48
/* Low battery indicating led */
# define DIM_NUM_LOCK
# define NUM_LOCK_INDEX 36
# define LED_MATRIX_KEYPRESSES
#endif

View File

@@ -0,0 +1,33 @@
{
"usb": {
"pid": "0x0AA4",
"device_version": "1.0.0"
},
"features": {
"led_matrix": true
},
"indicators": {
"num_lock": "B2"
},
"led_matrix": {
"driver": "snled27351_spi",
"sleep": true,
"animations": {
"none": true,
"solid": true,
"breathing": true,
"band_pinwheel": true,
"band_spiral": true,
"cycle_left_right": true,
"cycle_up_down": true,
"cycle_out_in": true,
"dual_beacon": true,
"solid_reactive_simple": true,
"solid_reactive_multiwide": true,
"solid_reactive_multinexus": true,
"solid_splash": true,
"wave_left_right": true,
"wave_up_down": true
}
}
}

View File

@@ -0,0 +1,68 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
enum layers {
MAC_BASE,
MAC_FN,
WIN_BASE,
WIN_FN,
};
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_iso_109(
KC_ESC, KC_BRID, KC_BRIU, KC_MCTRL, KC_LNPAD, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_SNAP, KC_SIRI, BL_STEP, KC_F13, KC_F14, KC_F15, KC_F16,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_DEL, KC_END, KC_PGDN, KC_P7, KC_P8, KC_P9,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, KC_RCMMD, KC_ROPTN, MO(MAC_FN), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
[MAC_FN] = LAYOUT_iso_109(
_______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______, BL_TOGG, _______, _______, _______, _______,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
BL_TOGG, BL_STEP, BL_UP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, BL_DOWN, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, BAT_LVL, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______),
[WIN_BASE] = LAYOUT_iso_109(
KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_PSCR, KC_CTANA, BL_STEP, _______, _______, _______, _______,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_DEL, KC_END, KC_PGDN, KC_P7, KC_P8, KC_P9,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
KC_LCTL, KC_LWIN, KC_LALT, KC_SPC, KC_RALT, KC_RWIN, MO(WIN_FN), KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
[WIN_FN] = LAYOUT_iso_109(
_______, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, BL_TOGG, _______, _______, _______, _______,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
BL_TOGG, BL_STEP, BL_UP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, BL_DOWN, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, BAT_LVL, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______)
};
// clang-format on
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_record_keychron_common(keycode, record)) {
return false;
}
return true;
}

View File

@@ -0,0 +1 @@
# This file intentionally left blank

View File

@@ -0,0 +1,173 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
// clang-format off
#ifdef LED_MATRIX_ENABLE
const snled27351_led_t g_snled27351_leds[LED_MATRIX_LED_COUNT] = {
/* Refer to SNLED27351 manual for these locations
* driver
* | LED address
* | | */
{0, CB1_CA16},
{0, CB1_CA14},
{0, CB1_CA13},
{0, CB1_CA12},
{0, CB1_CA11},
{0, CB1_CA10},
{0, CB1_CA9},
{0, CB1_CA8},
{0, CB1_CA7},
{0, CB1_CA6},
{0, CB1_CA5},
{0, CB1_CA4},
{0, CB1_CA3},
{0, CB1_CA2},
{0, CB1_CA1},
{0, CB7_CA1},
{0, CB7_CA2},
{0, CB7_CA3},
{0, CB7_CA4},
{0, CB2_CA16},
{0, CB2_CA15},
{0, CB2_CA14},
{0, CB2_CA13},
{0, CB2_CA12},
{0, CB2_CA11},
{0, CB2_CA10},
{0, CB2_CA9},
{0, CB2_CA8},
{0, CB2_CA7},
{0, CB2_CA6},
{0, CB2_CA5},
{0, CB2_CA4},
{0, CB2_CA3},
{0, CB2_CA2},
{0, CB2_CA1},
{0, CB8_CA1},
{0, CB8_CA2},
{0, CB8_CA3},
{0, CB8_CA4},
{0, CB3_CA16},
{0, CB3_CA15},
{0, CB3_CA14},
{0, CB3_CA13},
{0, CB3_CA12},
{0, CB3_CA11},
{0, CB3_CA10},
{0, CB3_CA9},
{0, CB3_CA8},
{0, CB3_CA7},
{0, CB3_CA6},
{0, CB3_CA5},
{0, CB3_CA4},
{0, CB3_CA3},
{0, CB3_CA2},
{0, CB3_CA1},
{0, CB7_CA6},
{0, CB7_CA7},
{0, CB7_CA8},
{0, CB7_CA9},
{0, CB4_CA16},
{0, CB4_CA15},
{0, CB4_CA14},
{0, CB4_CA13},
{0, CB4_CA12},
{0, CB4_CA11},
{0, CB4_CA10},
{0, CB4_CA9},
{0, CB4_CA8},
{0, CB4_CA7},
{0, CB4_CA6},
{0, CB4_CA5},
{0, CB4_CA4},
{0, CB7_CA5},
{0, CB8_CA5},
{0, CB7_CA10},
{0, CB8_CA10},
{0, CB8_CA7},
{0, CB8_CA8},
{0, CB8_CA9},
{0, CB5_CA16},
{0, CB5_CA15},
{0, CB5_CA14},
{0, CB5_CA13},
{0, CB5_CA12},
{0, CB5_CA11},
{0, CB5_CA10},
{0, CB5_CA9},
{0, CB5_CA8},
{0, CB5_CA7},
{0, CB5_CA6},
{0, CB5_CA5},
{0, CB5_CA3},
{0, CB5_CA1},
{0, CB8_CA6},
{0, CB8_CA11},
{0, CB8_CA12},
{0, CB6_CA16},
{0, CB6_CA15},
{0, CB6_CA14},
{0, CB6_CA10},
{0, CB6_CA6},
{0, CB6_CA5},
{0, CB6_CA4},
{0, CB6_CA3},
{0, CB6_CA2},
{0, CB6_CA1},
{0, CB7_CA13},
{0, CB7_CA11},
{0, CB7_CA12},
};
#define __ NO_LED
led_config_t g_led_config = {
{
// Key Matrix to LED Index
{ 0, __, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 },
{ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38 },
{ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 },
{ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78 },
{ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, __, 91, __, 92, __, 93, 94, 95 },
{ 96, 97, 98, __, __, __, 99, __, __, __, 100, 101, 102, 103, 104, 105, 106, __, 107, 108 },
},
{
// LED Index to Physical Position
{0, 0}, {21, 0}, {31, 0}, {42, 0}, {52, 0}, {68, 0}, {78, 0}, {89, 0}, {99, 0}, {115, 0}, {125, 0}, {136, 0}, {146, 0}, {159, 0}, {169, 0}, {180, 0}, {193, 0}, {203, 0}, {214, 0},
{0,15}, {10,15}, {21,15}, {31,15}, {42,15}, {52,15}, {63,15}, {73,15}, {83,15}, {94,15}, {104,15}, {115,15}, {125,15}, {141,15}, {159,15}, {169,15}, {180,15}, {193,15}, {203,15}, {214,15},
{3,27}, {16,27}, {26,27}, {36,27}, {47,27}, {57,27}, {68,27}, {78,27}, {89,27}, {99,27}, {109,27}, {120,27}, {130,27}, {143,33}, {159,27}, {169,27}, {180,27}, {193,27}, {203,27}, {214,27},
{4,40}, {18,40}, {29,40}, {39,40}, {50,40}, {60,40}, {70,40}, {81,40}, {91,40}, {102,40},{112,40}, {123,40}, {134,40}, {224, 0}, {224,15}, {224,34}, {224,58}, {193,40}, {203,40}, {214,40},
{1,52}, {13,52}, {23,52}, {34,52}, {44,52}, {55,52}, {65,52}, {76,52}, {86,52}, {96,52}, {107,52}, {117,52}, {137,52}, {169,52}, {193,52}, {203,52}, {214,52},
{1,64}, {14,64}, {27,64}, {66,64}, {105,64}, {118,64}, {131,64}, {145,64}, {159,64}, {169,64}, {180,64}, {198,64}, {214,64},
},
{
// LED Index to Flag
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
};
#endif

View File

@@ -0,0 +1,96 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "keychron_task.h"
#ifdef FACTORY_TEST_ENABLE
# include "factory_test.h"
# include "keychron_common.h"
#endif
#ifdef LK_WIRELESS_ENABLE
# include "lkbt51.h"
# include "wireless.h"
# include "transport.h"
# include "keychron_wireless_common.h"
# include "battery.h"
#endif
#define POWER_ON_LED_DURATION 3000
static uint32_t power_on_indicator_timer;
#ifdef LK_WIRELESS_ENABLE
pin_t bt_led_pins[] = BT_INDICATION_LED_PIN_LIST;
pin_t p24g_led_pins[] = P24G_INDICATION_LED_PIN_LIST;
#endif
bool dip_switch_update_kb(uint8_t index, bool active) {
if (index == 0) {
default_layer_set(1UL << (active ? 0 : 2));
}
dip_switch_update_user(index, active);
return true;
}
void keyboard_post_init_kb(void) {
#ifdef LK_WIRELESS_ENABLE
palSetLineMode(P24G_MODE_SELECT_PIN, PAL_MODE_INPUT);
palSetLineMode(BT_MODE_SELECT_PIN, PAL_MODE_INPUT);
lkbt51_init(false);
wireless_init();
#endif
power_on_indicator_timer = timer_read32();
keyboard_post_init_user();
}
bool keychron_task_kb(void) {
if (power_on_indicator_timer) {
if (timer_elapsed32(power_on_indicator_timer) > POWER_ON_LED_DURATION) {
power_on_indicator_timer = 0;
if (!host_keyboard_led_state().caps_lock) gpio_write_pin(LED_CAPS_LOCK_PIN, !LED_PIN_ON_STATE);
#ifdef LK_WIRELESS_ENABLE
gpio_write_pin(BAT_LOW_LED_PIN, !BAT_LOW_LED_PIN_ON_STATE);
for (uint8_t i = 0; i < sizeof(bt_led_pins) / sizeof(pin_t); i++)
gpio_write_pin(bt_led_pins[i], 1);
for (uint8_t i = 0; i < sizeof(p24g_led_pins) / sizeof(pin_t); i++)
gpio_write_pin(p24g_led_pins[i], 1);
#endif
} else {
gpio_write_pin(LED_CAPS_LOCK_PIN, LED_PIN_ON_STATE);
#ifdef LK_WIRELESS_ENABLE
gpio_write_pin(BAT_LOW_LED_PIN, BAT_LOW_LED_PIN_ON_STATE);
if (get_transport() != TRANSPORT_P2P4)
for (uint8_t i = 0; i < sizeof(bt_led_pins) / sizeof(pin_t); i++)
gpio_write_pin(bt_led_pins[i], 0);
if (get_transport() != TRANSPORT_BLUETOOTH)
for (uint8_t i = 0; i < sizeof(p24g_led_pins) / sizeof(pin_t); i++)
gpio_write_pin(p24g_led_pins[i], 0);
#endif
}
}
return true;
}
#ifdef LK_WIRELESS_ENABLE
bool lpm_is_kb_idle(void) {
return power_on_indicator_timer == 0 && !factory_reset_indicating();
}
#endif

View File

@@ -0,0 +1,37 @@
/* Copyright 2024 @ lokher (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include_next <mcuconf.h>
#undef STM32_HSECLK
#define STM32_HSECLK 16000000
#undef STM32_PLLM_VALUE
#define STM32_PLLM_VALUE 8
#undef STM32_PLLN_VALUE
#define STM32_PLLN_VALUE 96
#undef STM32_PLLP_VALUE
#define STM32_PLLP_VALUE 4
#undef STM32_PLLQ_VALUE
#define STM32_PLLQ_VALUE 4
#undef STM32_SPI_USE_SPI1
#define STM32_SPI_USE_SPI1 TRUE

View File

@@ -0,0 +1,29 @@
# Keychron K10 Max
![Keychron K10 Max](https://cdn.shopify.com/s/files/1/0059/0630/1017/files/K10-Max-1.jpg?v=1722492751)
A customizable 108 keys TKL keyboard.
* Keyboard Maintainer: [Keychron](https://github.com/keychron)
* Hardware Supported: Keychron K10 Max
* Hardware Availability: [Keychron K10 Max QMK/VIA Wireless Custom Mechanical Keyboard](https://www.keychron.com/products/keychron-k10-max-qmk-wireless-mechanical-keyboard)
Make example for this keyboard (after setting up your build environment):
make keychron/k10_max/ansi/rgb:default
make keychron/k10_max/ansi/white:default
make keychron/k10_max/iso/rgb:default
make keychron/k10_max/iso/white:default
Flashing example for this keyboard:
make keychron/k10_max/ansi/rgb:default:flash
make keychron/k10_max/ansi/white:default:flash
make keychron/k10_max/iso/rgb:default:flash
make keychron/k10_max/iso/white:default:flash
**Reset Key**: Toggle mode switch to "Cable", hold down the *Esc* key or reset button underneath space bar while connecting the USB cable,
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).

View File

@@ -0,0 +1,4 @@
include keyboards/keychron/common/wireless/wireless.mk
include keyboards/keychron/common/keychron_common.mk
VPATH += $(TOP_DIR)/keyboards/keychron

View File

@@ -0,0 +1,345 @@
{
"name": "Keychron K10 Max ANSI RGB",
"vendorId": "0x3434",
"productId": "0x0AA0",
"keycodes": ["qmk_lighting"],
"menus": [
{
"label": "Lighting",
"content": [
{
"label": "Backlight",
"content": [
{
"label": "Brightness",
"type": "range",
"options": [0, 255],
"content": ["id_qmk_rgb_matrix_brightness", 3, 1]
},
{
"label": "Effect",
"type": "dropdown",
"content": ["id_qmk_rgb_matrix_effect", 3, 2],
"options": [
["None", 0],
["Solid Color", 1],
["Breathing", 2],
["Band Spiral Val", 3],
["Cycle All", 4],
["Cycle Left Right", 5],
["Cycle Up Down", 6],
["Rainbow Moving Chevron", 7],
["Cycle Out In", 8],
["Cycle Out In Dual", 9],
["Cycle Pinwheel", 10],
["Cycle Spiral", 11],
["Dual Beacon", 12],
["Rainbow Beacon", 13],
["Jellybean Raindrops", 14],
["Pixel Rain", 15],
["Typing Heatmap", 16],
["Digital Rain", 17],
["Reactive Simple", 18],
["Reactive Multiwide", 19],
["Reactive Multinexus", 20],
["Splash", 21],
["Solid Splash", 22]
]
},
{
"showIf": "{id_qmk_rgb_matrix_effect} > 1",
"label": "Effect Speed",
"type": "range",
"options": [0, 255],
"content": ["id_qmk_rgb_matrix_effect_speed", 3, 3]
},
{
"showIf": "{id_qmk_rgb_matrix_effect} != 0 && ( {id_qmk_rgb_matrix_effect} < 4 || {id_qmk_rgb_matrix_effect} == 18 || ({id_qmk_rgb_matrix_effect} > 17 && {id_qmk_rgb_matrix_effect} != 21) ) ",
"label": "Color",
"type": "color",
"content": ["id_qmk_rgb_matrix_color", 3, 4]
}
]
}
]
}
],
"customKeycodes": [
{"name": "Left Option", "title": "Left Option", "shortName": "LOpt"},
{"name": "Right Option", "title": "Right Option", "shortName": "ROpt"},
{"name": "Left Cmd", "title": "Left Command", "shortName": "LCmd"},
{"name": "Right Cmd", "title": "Right Command", "shortName": "RCmd"},
{"name": "Misson Control", "title": "Misson Control in Mac", "shortName": "MCtl"},
{"name": "Lanuch Pad", "title": "Lanuch Pad in Windows", "shortName": "LPad"},
{"name": "Task View", "title": "Task View in Windows", "shortName": "Task"},
{"name": "File Explorer", "title": "File Explorer in Windows", "shortName": "File"},
{"name": "Screen shot", "title": "Screenshot in macOS", "shortName": "SShot"},
{"name": "Cortana", "title": "Cortana in Windows", "shortName": "Cortana"},
{"name": "Siri", "title": "Siri in macOS", "shortName": "Siri"},
{"name": "Bluetooth Host 1", "title": "Bluetooth Host 1", "shortName": "BTH1"},
{"name": "Bluetooth Host 2", "title": "Bluetooth Host 2", "shortName": "BTH2"},
{"name": "Bluetooth Host 3", "title": "Bluetooth Host 3", "shortName": "BTH3"},
{"name": "2.4G", "title": "2.4G", "shortName": "2.4G"},
{"name": "Battery Level", "title": "Show battery level", "shortName": "Batt"}
],
"matrix": {"rows": 6, "cols" : 20},
"layouts": {
"keymap": [
[
{
"c": "#777777"
},
"0, 0",
{
"x": 1,
"c": "#aaaaaa"
},
"0, 2",
"0, 3",
"0, 4",
"0, 5",
{
"x": 0.5,
"c": "#cccccc"
},
"0, 6",
"0, 7",
"0, 8",
"0, 9",
{
"x": 0.5,
"c": "#aaaaaa"
},
"0, 10",
"0, 11",
"0, 12",
"0, 13",
{
"x": 0.25,
"c": "#cccccc"
},
"0, 14",
"0, 15",
"0, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"0, 17",
"0, 18",
"0, 19",
"3, 13"
],
[
{
"y": 0.25,
"c": "#cccccc"
},
"1, 0",
{
"c": "#aaaaaa"
},
"1, 1",
"1, 2",
"1, 3",
"1, 4",
"1, 5",
"1, 6",
"1, 7",
"1, 8",
"1, 9",
"1, 10",
"1, 11",
"1, 12",
{
"w": 2,
"c": "#cccccc"
},
"1, 13",
{
"x": 0.25
},
"1, 14",
"1, 15",
"1, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"1, 17",
"1, 18",
"1, 19",
"3, 14"
],
[
{
"w": 1.5,
"c": "#cccccc"
},
"2, 0",
{
"c": "#aaaaaa"
},
"2, 1",
"2, 2",
"2, 3",
"2, 4",
"2, 5",
"2, 6",
"2, 7",
"2, 8",
"2, 9",
"2, 10",
"2, 11",
"2, 12",
{
"w": 1.5,
"c": "#cccccc"
},
"2, 13",
{
"x": 0.25
},
"2, 14",
"2, 15",
"2, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"2, 17",
"2, 18",
"2, 19",
{
"h": 2,
"c": "#aaaaaa"
},
"3, 15"
],
[
{
"w": 1.75,
"c": "#cccccc"
},
"3, 0",
{
"c": "#aaaaaa"
},
"3, 1",
"3, 2",
"3, 3",
"3, 4",
"3, 5",
"3, 6",
"3, 7",
"3, 8",
"3, 9",
"3, 10",
"3, 11",
{
"w": 2.25,
"c": "#777777"
},
"3, 12",
{
"x": 3.5,
"c": "#aaaaaa"
},
"3, 17",
"3, 18",
"3, 19"
],
[
{
"w": 2.25,
"c": "#cccccc"
},
"4, 0",
{
"c": "#aaaaaa"
},
"4, 2",
"4, 3",
"4, 4",
"4, 5",
"4, 6",
"4, 7",
"4, 8",
"4, 9",
"4, 10",
"4, 11",
{
"w": 2.75,
"c": "#cccccc"
},
"4, 13",
{
"x": 1.25,
"c": "#aaaaaa"
},
"4, 15",
{
"x": 1.25
},
"4, 17",
"4, 18",
"4, 19",
{
"h": 2
},
"3, 16"
],
[
{
"w": 1.25,
"c": "#cccccc"
},
"5, 0",
{
"w": 1.25
},
"5, 1",
{
"w": 1.25
},
"5, 2",
{
"w": 6.25,
"c": "#aaaaaa"
},
"5, 6",
{
"w": 1.25,
"c": "#cccccc"
},
"5, 10",
{
"w": 1.25
},
"5, 11",
{
"w": 1.25,
"c": "#cccccc"
},
"5, 12",
{
"w": 1.25
},
"5, 13",
{
"x": 0.25,
"c": "#aaaaaa"
},
"5, 14",
"5, 15",
"5, 16",
{
"x": 0.25,
"w": 2
},
"5, 18",
"5, 19"
]
]
}
}

View File

@@ -0,0 +1,284 @@
{
"name": "Keychron K10 Max ANSI White",
"vendorId": "0x3434",
"productId": "0x0AA3",
"keycodes": ["qmk_lighting"],
"customKeycodes": [
{"name": "Left Option", "title": "Left Option", "shortName": "LOpt"},
{"name": "Right Option", "title": "Right Option", "shortName": "ROpt"},
{"name": "Left Cmd", "title": "Left Command", "shortName": "LCmd"},
{"name": "Right Cmd", "title": "Right Command", "shortName": "RCmd"},
{"name": "Misson Control", "title": "Misson Control in Mac", "shortName": "MCtl"},
{"name": "Lanuch Pad", "title": "Lanuch Pad in Windows", "shortName": "LPad"},
{"name": "Task View", "title": "Task View in Windows", "shortName": "Task"},
{"name": "File Explorer", "title": "File Explorer in Windows", "shortName": "File"},
{"name": "Screen shot", "title": "Screenshot in macOS", "shortName": "SShot"},
{"name": "Cortana", "title": "Cortana in Windows", "shortName": "Cortana"},
{"name": "Siri", "title": "Siri in macOS", "shortName": "Siri"},
{"name": "Bluetooth Host 1", "title": "Bluetooth Host 1", "shortName": "BTH1"},
{"name": "Bluetooth Host 2", "title": "Bluetooth Host 2", "shortName": "BTH2"},
{"name": "Bluetooth Host 3", "title": "Bluetooth Host 3", "shortName": "BTH3"},
{"name": "2.4G", "title": "2.4G", "shortName": "2.4G"},
{"name": "Battery Level", "title": "Show battery level", "shortName": "Batt"}
],
"matrix": {"rows": 6, "cols" : 20},
"layouts": {
"keymap": [
[
{
"c": "#777777"
},
"0, 0",
{
"x": 1,
"c": "#aaaaaa"
},
"0, 2",
"0, 3",
"0, 4",
"0, 5",
{
"x": 0.5,
"c": "#cccccc"
},
"0, 6",
"0, 7",
"0, 8",
"0, 9",
{
"x": 0.5,
"c": "#aaaaaa"
},
"0, 10",
"0, 11",
"0, 12",
"0, 13",
{
"x": 0.25,
"c": "#cccccc"
},
"0, 14",
"0, 15",
"0, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"0, 17",
"0, 18",
"0, 19",
"3, 13"
],
[
{
"y": 0.25,
"c": "#cccccc"
},
"1, 0",
{
"c": "#aaaaaa"
},
"1, 1",
"1, 2",
"1, 3",
"1, 4",
"1, 5",
"1, 6",
"1, 7",
"1, 8",
"1, 9",
"1, 10",
"1, 11",
"1, 12",
{
"w": 2,
"c": "#cccccc"
},
"1, 13",
{
"x": 0.25
},
"1, 14",
"1, 15",
"1, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"1, 17",
"1, 18",
"1, 19",
"3, 14"
],
[
{
"w": 1.5,
"c": "#cccccc"
},
"2, 0",
{
"c": "#aaaaaa"
},
"2, 1",
"2, 2",
"2, 3",
"2, 4",
"2, 5",
"2, 6",
"2, 7",
"2, 8",
"2, 9",
"2, 10",
"2, 11",
"2, 12",
{
"w": 1.5,
"c": "#cccccc"
},
"2, 13",
{
"x": 0.25
},
"2, 14",
"2, 15",
"2, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"2, 17",
"2, 18",
"2, 19",
{
"h": 2,
"c": "#aaaaaa"
},
"3, 15"
],
[
{
"w": 1.75,
"c": "#cccccc"
},
"3, 0",
{
"c": "#aaaaaa"
},
"3, 1",
"3, 2",
"3, 3",
"3, 4",
"3, 5",
"3, 6",
"3, 7",
"3, 8",
"3, 9",
"3, 10",
"3, 11",
{
"w": 2.25,
"c": "#777777"
},
"3, 12",
{
"x": 3.5,
"c": "#aaaaaa"
},
"3, 17",
"3, 18",
"3, 19"
],
[
{
"w": 2.25,
"c": "#cccccc"
},
"4, 0",
{
"c": "#aaaaaa"
},
"4, 2",
"4, 3",
"4, 4",
"4, 5",
"4, 6",
"4, 7",
"4, 8",
"4, 9",
"4, 10",
"4, 11",
{
"w": 2.75,
"c": "#cccccc"
},
"4, 13",
{
"x": 1.25,
"c": "#aaaaaa"
},
"4, 15",
{
"x": 1.25
},
"4, 17",
"4, 18",
"4, 19",
{
"h": 2
},
"3, 16"
],
[
{
"w": 1.25,
"c": "#cccccc"
},
"5, 0",
{
"w": 1.25
},
"5, 1",
{
"w": 1.25
},
"5, 2",
{
"w": 6.25,
"c": "#aaaaaa"
},
"5, 6",
{
"w": 1.25,
"c": "#cccccc"
},
"5, 10",
{
"w": 1.25
},
"5, 11",
{
"w": 1.25,
"c": "#cccccc"
},
"5, 12",
{
"w": 1.25
},
"5, 13",
{
"x": 0.25,
"c": "#aaaaaa"
},
"5, 14",
"5, 15",
"5, 16",
{
"x": 0.25,
"w": 2
},
"5, 18",
"5, 19"
]
]
}
}

View File

@@ -0,0 +1,347 @@
{
"name": "Keychron K10 Max ISO RGB",
"vendorId": "0x3434",
"productId": "0x0AA1",
"keycodes": ["qmk_lighting"],
"menus": [
{
"label": "Lighting",
"content": [
{
"label": "Backlight",
"content": [
{
"label": "Brightness",
"type": "range",
"options": [0, 255],
"content": ["id_qmk_rgb_matrix_brightness", 3, 1]
},
{
"label": "Effect",
"type": "dropdown",
"content": ["id_qmk_rgb_matrix_effect", 3, 2],
"options": [
["None", 0],
["Solid Color", 1],
["Breathing", 2],
["Band Spiral Val", 3],
["Cycle All", 4],
["Cycle Left Right", 5],
["Cycle Up Down", 6],
["Rainbow Moving Chevron", 7],
["Cycle Out In", 8],
["Cycle Out In Dual", 9],
["Cycle Pinwheel", 10],
["Cycle Spiral", 11],
["Dual Beacon", 12],
["Rainbow Beacon", 13],
["Jellybean Raindrops", 14],
["Pixel Rain", 15],
["Typing Heatmap", 16],
["Digital Rain", 17],
["Reactive Simple", 18],
["Reactive Multiwide", 19],
["Reactive Multinexus", 20],
["Splash", 21],
["Solid Splash", 22]
]
},
{
"showIf": "{id_qmk_rgb_matrix_effect} > 1",
"label": "Effect Speed",
"type": "range",
"options": [0, 255],
"content": ["id_qmk_rgb_matrix_effect_speed", 3, 3]
},
{
"showIf": "{id_qmk_rgb_matrix_effect} != 0 && ( {id_qmk_rgb_matrix_effect} < 4 || {id_qmk_rgb_matrix_effect} == 18 || ({id_qmk_rgb_matrix_effect} > 17 && {id_qmk_rgb_matrix_effect} != 21) ) ",
"label": "Color",
"type": "color",
"content": ["id_qmk_rgb_matrix_color", 3, 4]
}
]
}
]
}
],
"customKeycodes": [
{"name": "Left Option", "title": "Left Option", "shortName": "LOpt"},
{"name": "Right Option", "title": "Right Option", "shortName": "ROpt"},
{"name": "Left Cmd", "title": "Left Command", "shortName": "LCmd"},
{"name": "Right Cmd", "title": "Right Command", "shortName": "RCmd"},
{"name": "Misson Control", "title": "Misson Control in Mac", "shortName": "MCtl"},
{"name": "Lanuch Pad", "title": "Lanuch Pad in Windows", "shortName": "LPad"},
{"name": "Task View", "title": "Task View in Windows", "shortName": "Task"},
{"name": "File Explorer", "title": "File Explorer in Windows", "shortName": "File"},
{"name": "Screen shot", "title": "Screenshot in macOS", "shortName": "SShot"},
{"name": "Cortana", "title": "Cortana in Windows", "shortName": "Cortana"},
{"name": "Siri", "title": "Siri in macOS", "shortName": "Siri"},
{"name": "Bluetooth Host 1", "title": "Bluetooth Host 1", "shortName": "BTH1"},
{"name": "Bluetooth Host 2", "title": "Bluetooth Host 2", "shortName": "BTH2"},
{"name": "Bluetooth Host 3", "title": "Bluetooth Host 3", "shortName": "BTH3"},
{"name": "2.4G", "title": "2.4G", "shortName": "2.4G"},
{"name": "Battery Level", "title": "Show battery level", "shortName": "Batt"}
],
"matrix": {"rows": 6, "cols" : 20},
"layouts": {
"keymap": [
[
{
"c": "#777777"
},
"0, 0",
{
"x": 1,
"c": "#aaaaaa"
},
"0, 2",
"0, 3",
"0, 4",
"0, 5",
{
"x": 0.5,
"c": "#cccccc"
},
"0, 6",
"0, 7",
"0, 8",
"0, 9",
{
"x": 0.5,
"c": "#aaaaaa"
},
"0, 10",
"0, 11",
"0, 12",
"0, 13",
{
"x": 0.25,
"c": "#cccccc"
},
"0, 14",
"0, 15",
"0, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"0, 17",
"0, 18",
"0, 19",
"3, 13"
],
[
{
"y": 0.25,
"c": "#cccccc"
},
"1, 0",
{
"c": "#aaaaaa"
},
"1, 1",
"1, 2",
"1, 3",
"1, 4",
"1, 5",
"1, 6",
"1, 7",
"1, 8",
"1, 9",
"1, 10",
"1, 11",
"1, 12",
{
"w": 2,
"c": "#cccccc"
},
"1, 13",
{
"x": 0.25
},
"1, 14",
"1, 15",
"1, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"1, 17",
"1, 18",
"1, 19",
"3, 14"
],
[
{
"w": 1.5,
"c": "#cccccc"
},
"2, 0",
{
"c": "#aaaaaa"
},
"2, 1",
"2, 2",
"2, 3",
"2, 4",
"2, 5",
"2, 6",
"2, 7",
"2, 8",
"2, 9",
"2, 10",
"2, 11",
"2, 12",
{
"x": 0.25,
"c": "#cccccc",
"w": 1.25,
"h": 2,
"w2": 1.5,
"h2": 1,
"x2": -0.25
},
"2, 13",
{
"x": 0.25
},
"2, 14",
"2, 15",
"2, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"2, 17",
"2, 18",
"2, 19",
{
"h": 2,
"c": "#aaaaaa"
},
"3, 15"
],
[
{
"w": 1.75,
"c": "#cccccc"
},
"3, 0",
{
"c": "#aaaaaa"
},
"3, 1",
"3, 2",
"3, 3",
"3, 4",
"3, 5",
"3, 6",
"3, 7",
"3, 8",
"3, 9",
"3, 10",
"3, 11",
"3, 12",
{
"x": 4.75,
"c": "#aaaaaa"
},
"3, 17",
"3, 18",
"3, 19"
],
[
{
"w": 1.25,
"c": "#cccccc"
},
"4, 0",
{
"c": "#aaaaaa"
},
"4, 1",
"4, 2",
"4, 3",
"4, 4",
"4, 5",
"4, 6",
"4, 7",
"4, 8",
"4, 9",
"4, 10",
"4, 11",
{
"w": 2.75,
"c": "#cccccc"
},
"4, 13",
{
"x": 1.25,
"c": "#aaaaaa"
},
"4, 15",
{
"x": 1.25
},
"4, 17",
"4, 18",
"4, 19",
{
"h": 2
},
"3, 16"
],
[
{
"w": 1.25,
"c": "#cccccc"
},
"5, 0",
{
"w": 1.25
},
"5, 1",
{
"w": 1.25
},
"5, 2",
{
"w": 6.25,
"c": "#aaaaaa"
},
"5, 6",
{
"w": 1.25,
"c": "#cccccc"
},
"5, 10",
{
"w": 1.25
},
"5, 11",
{
"w": 1.25,
"c": "#cccccc"
},
"5, 12",
{
"w": 1.25
},
"5, 13",
{
"x": 0.25,
"c": "#aaaaaa"
},
"5, 14",
"5, 15",
"5, 16",
{
"x": 0.25,
"w": 2
},
"5, 18",
"5, 19"
]
]
}
}

View File

@@ -0,0 +1,286 @@
{
"name": "Keychron K10 Max ISO White",
"vendorId": "0x3434",
"productId": "0x0AA4",
"keycodes": ["qmk_lighting"],
"customKeycodes": [
{"name": "Left Option", "title": "Left Option", "shortName": "LOpt"},
{"name": "Right Option", "title": "Right Option", "shortName": "ROpt"},
{"name": "Left Cmd", "title": "Left Command", "shortName": "LCmd"},
{"name": "Right Cmd", "title": "Right Command", "shortName": "RCmd"},
{"name": "Misson Control", "title": "Misson Control in Mac", "shortName": "MCtl"},
{"name": "Lanuch Pad", "title": "Lanuch Pad in Windows", "shortName": "LPad"},
{"name": "Task View", "title": "Task View in Windows", "shortName": "Task"},
{"name": "File Explorer", "title": "File Explorer in Windows", "shortName": "File"},
{"name": "Screen shot", "title": "Screenshot in macOS", "shortName": "SShot"},
{"name": "Cortana", "title": "Cortana in Windows", "shortName": "Cortana"},
{"name": "Siri", "title": "Siri in macOS", "shortName": "Siri"},
{"name": "Bluetooth Host 1", "title": "Bluetooth Host 1", "shortName": "BTH1"},
{"name": "Bluetooth Host 2", "title": "Bluetooth Host 2", "shortName": "BTH2"},
{"name": "Bluetooth Host 3", "title": "Bluetooth Host 3", "shortName": "BTH3"},
{"name": "2.4G", "title": "2.4G", "shortName": "2.4G"},
{"name": "Battery Level", "title": "Show battery level", "shortName": "Batt"}
],
"matrix": {"rows": 6, "cols" : 20},
"layouts": {
"keymap": [
[
{
"c": "#777777"
},
"0, 0",
{
"x": 1,
"c": "#aaaaaa"
},
"0, 2",
"0, 3",
"0, 4",
"0, 5",
{
"x": 0.5,
"c": "#cccccc"
},
"0, 6",
"0, 7",
"0, 8",
"0, 9",
{
"x": 0.5,
"c": "#aaaaaa"
},
"0, 10",
"0, 11",
"0, 12",
"0, 13",
{
"x": 0.25,
"c": "#cccccc"
},
"0, 14",
"0, 15",
"0, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"0, 17",
"0, 18",
"0, 19",
"3, 13"
],
[
{
"y": 0.25,
"c": "#cccccc"
},
"1, 0",
{
"c": "#aaaaaa"
},
"1, 1",
"1, 2",
"1, 3",
"1, 4",
"1, 5",
"1, 6",
"1, 7",
"1, 8",
"1, 9",
"1, 10",
"1, 11",
"1, 12",
{
"w": 2,
"c": "#cccccc"
},
"1, 13",
{
"x": 0.25
},
"1, 14",
"1, 15",
"1, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"1, 17",
"1, 18",
"1, 19",
"3, 14"
],
[
{
"w": 1.5,
"c": "#cccccc"
},
"2, 0",
{
"c": "#aaaaaa"
},
"2, 1",
"2, 2",
"2, 3",
"2, 4",
"2, 5",
"2, 6",
"2, 7",
"2, 8",
"2, 9",
"2, 10",
"2, 11",
"2, 12",
{
"x": 0.25,
"c": "#cccccc",
"w": 1.25,
"h": 2,
"w2": 1.5,
"h2": 1,
"x2": -0.25
},
"2, 13",
{
"x": 0.25
},
"2, 14",
"2, 15",
"2, 16",
{
"x": 0.25,
"c": "#aaaaaa"
},
"2, 17",
"2, 18",
"2, 19",
{
"h": 2,
"c": "#aaaaaa"
},
"3, 15"
],
[
{
"w": 1.75,
"c": "#cccccc"
},
"3, 0",
{
"c": "#aaaaaa"
},
"3, 1",
"3, 2",
"3, 3",
"3, 4",
"3, 5",
"3, 6",
"3, 7",
"3, 8",
"3, 9",
"3, 10",
"3, 11",
"3, 12",
{
"x": 4.75,
"c": "#aaaaaa"
},
"3, 17",
"3, 18",
"3, 19"
],
[
{
"w": 1.25,
"c": "#cccccc"
},
"4, 0",
{
"c": "#aaaaaa"
},
"4, 1",
"4, 2",
"4, 3",
"4, 4",
"4, 5",
"4, 6",
"4, 7",
"4, 8",
"4, 9",
"4, 10",
"4, 11",
{
"w": 2.75,
"c": "#cccccc"
},
"4, 13",
{
"x": 1.25,
"c": "#aaaaaa"
},
"4, 15",
{
"x": 1.25
},
"4, 17",
"4, 18",
"4, 19",
{
"h": 2
},
"3, 16"
],
[
{
"w": 1.25,
"c": "#cccccc"
},
"5, 0",
{
"w": 1.25
},
"5, 1",
{
"w": 1.25
},
"5, 2",
{
"w": 6.25,
"c": "#aaaaaa"
},
"5, 6",
{
"w": 1.25,
"c": "#cccccc"
},
"5, 10",
{
"w": 1.25
},
"5, 11",
{
"w": 1.25,
"c": "#cccccc"
},
"5, 12",
{
"w": 1.25
},
"5, 13",
{
"x": 0.25,
"c": "#aaaaaa"
},
"5, 14",
"5, 15",
"5, 16",
{
"x": 0.25,
"w": 2
},
"5, 18",
"5, 19"
]
]
}
}

View File

@@ -0,0 +1,46 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef RGB_MATRIX_ENABLE
/* RGB Matrix driver configuration */
# define RGB_MATRIX_LED_COUNT 68
# define DRIVER_CS_PINS \
{ B8, B9 }
/* Set LED driver current */
# define SNLED27351_CURRENT_TUNE \
{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }
/* Set to infinit, which is use in USB mode by default */
# define RGB_MATRIX_TIMEOUT RGB_MATRIX_TIMEOUT_INFINITE
/* Allow shutdown of led driver to save power */
# define RGB_MATRIX_DRIVER_SHUTDOWN_ENABLE
# define RGB_MATRIX_DRIVER_LOAD_ENABLE
/* Turn off backlight on low brightness to save power */
# define RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL 48
/* Indications */
# define DIM_CAPS_LOCK
# define CAPS_LOCK_INDEX 29
# define LOW_BAT_IND_INDEX \
{ 60, 63 }
# define RGB_MATRIX_KEYPRESSES
# define RGB_MATRIX_FRAMEBUFFER_EFFECTS
#endif

View File

@@ -0,0 +1,45 @@
{
"usb": {
"pid": "0x0AB3",
"device_version": "1.0.0"
},
"features": {
"rgb_matrix": true,
"encoder": true
},
"encoder": {
"rotary": [
{
"pin_a": "A8",
"pin_b": "C9"
}
]
},
"rgb_matrix": {
"driver": "snled27351_spi",
"sleep": true,
"animations": {
"band_spiral_val": true,
"breathing": true,
"cycle_all": true,
"cycle_left_right": true,
"cycle_out_in": true,
"cycle_out_in_dual": true,
"cycle_pinwheel": true,
"cycle_spiral": true,
"cycle_up_down": true,
"digital_rain": true,
"dual_beacon": true,
"jellybean_raindrops": true,
"pixel_rain": true,
"rainbow_beacon": true,
"rainbow_moving_chevron": true,
"solid_reactive_multinexus": true,
"solid_reactive_multiwide": true,
"solid_reactive_simple": true,
"solid_splash": true,
"splash": true,
"typing_heatmap": true
}
}
}

View File

@@ -0,0 +1,82 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
enum layers{
MAC_BASE,
WIN_BASE,
MAC_FN1,
WIN_FN1,
FN2,
};
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_69_ansi(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_MUTE,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_HOME,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, MO(MAC_FN1), MO(FN2), KC_SPC, KC_RCMMD, KC_LEFT, KC_DOWN, KC_RGHT),
[WIN_BASE] = LAYOUT_69_ansi(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_MUTE,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_HOME,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LWIN, KC_LALT, KC_SPC, MO(WIN_FN1), MO(FN2), KC_SPC, KC_RALT, KC_LEFT, KC_DOWN, KC_RGHT),
[MAC_FN1] = LAYOUT_69_ansi(
KC_GRV, KC_BRID, KC_BRIU, KC_MCTRL, KC_LNPAD,RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, RGB_TOG,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_INS,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, KC_END,
_______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, NK_TOGG, _______, _______, _______, _______, _______, KC_PGUP,
_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______),
[WIN_FN1] = LAYOUT_69_ansi(
KC_GRV, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, RGB_TOG,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_INS,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, KC_END,
_______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, NK_TOGG, _______, _______, _______, _______, _______, KC_PGUP,
_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______),
[FN2] = LAYOUT_69_ansi(
KC_TILD, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, BAT_LVL, BAT_LVL, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______)
};
#if defined(ENCODER_MAP_ENABLE)
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
[MAC_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
[WIN_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
[MAC_FN1] = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI)},
[WIN_FN1] = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI)},
[FN2] = {ENCODER_CCW_CW(_______, _______)},
};
#endif // ENCODER_MAP_ENABLE
// clang-format on
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_record_keychron_common(keycode, record)) {
return false;
}
return true;
}

View File

@@ -0,0 +1,131 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
// clang-format off
#ifdef RGB_MATRIX_ENABLE
const snled27351_led_t PROGMEM g_snled27351_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to SNLED27351 manual for these locations
* driver
* | R location
* | | G location
* | | | B location
* | | | | */
{0, CB1_CA1, CB3_CA1, CB2_CA1},
{0, CB1_CA2, CB3_CA2, CB2_CA2},
{0, CB1_CA3, CB3_CA3, CB2_CA3},
{0, CB1_CA4, CB3_CA4, CB2_CA4},
{0, CB1_CA5, CB3_CA5, CB2_CA5},
{0, CB1_CA6, CB3_CA6, CB2_CA6},
{0, CB1_CA7, CB3_CA7, CB2_CA7},
{0, CB1_CA8, CB3_CA8, CB2_CA8},
{0, CB1_CA9, CB3_CA9, CB2_CA9},
{0, CB1_CA10, CB3_CA10, CB2_CA10},
{0, CB1_CA11, CB3_CA11, CB2_CA11},
{0, CB1_CA12, CB3_CA12, CB2_CA12},
{0, CB1_CA13, CB3_CA13, CB2_CA13},
{0, CB1_CA14, CB3_CA14, CB2_CA14},
{0, CB6_CA1, CB4_CA1, CB5_CA1},
{0, CB6_CA2, CB4_CA2, CB5_CA2},
{0, CB6_CA3, CB4_CA3, CB5_CA3},
{0, CB6_CA4, CB4_CA4, CB5_CA4},
{0, CB6_CA5, CB4_CA5, CB5_CA5},
{0, CB6_CA6, CB4_CA6, CB5_CA6},
{0, CB6_CA7, CB4_CA7, CB5_CA7},
{0, CB6_CA8, CB4_CA8, CB5_CA8},
{0, CB6_CA9, CB4_CA9, CB5_CA9},
{0, CB6_CA10, CB4_CA10, CB5_CA10},
{0, CB6_CA11, CB4_CA11, CB5_CA11},
{0, CB6_CA12, CB4_CA12, CB5_CA12},
{0, CB6_CA13, CB4_CA13, CB5_CA13},
{0, CB6_CA14, CB4_CA14, CB5_CA14},
{0, CB6_CA16, CB4_CA16, CB5_CA16},
{1, CB9_CA1, CB7_CA1, CB8_CA1},
{1, CB9_CA2, CB7_CA2, CB8_CA2},
{1, CB9_CA3, CB7_CA3, CB8_CA3},
{1, CB9_CA4, CB7_CA4, CB8_CA4},
{1, CB9_CA5, CB7_CA5, CB8_CA5},
{1, CB9_CA6, CB7_CA6, CB8_CA6},
{1, CB9_CA7, CB7_CA7, CB8_CA7},
{1, CB9_CA8, CB7_CA8, CB8_CA8},
{1, CB9_CA9, CB7_CA9, CB8_CA9},
{1, CB9_CA10, CB7_CA10, CB8_CA10},
{1, CB9_CA11, CB7_CA11, CB8_CA11},
{1, CB9_CA12, CB7_CA12, CB8_CA12},
{1, CB9_CA14, CB7_CA14, CB8_CA14},
{1, CB9_CA16, CB7_CA16, CB8_CA16},
{1, CB3_CA1, CB1_CA1, CB2_CA1},
{1, CB3_CA3, CB1_CA3, CB2_CA3},
{1, CB3_CA4, CB1_CA4, CB2_CA4},
{1, CB3_CA5, CB1_CA5, CB2_CA5},
{1, CB3_CA6, CB1_CA6, CB2_CA6},
{1, CB3_CA7, CB1_CA7, CB2_CA7},
{1, CB3_CA8, CB1_CA8, CB2_CA8},
{1, CB3_CA9, CB1_CA9, CB2_CA9},
{1, CB3_CA10, CB1_CA10, CB2_CA10},
{1, CB3_CA11, CB1_CA11, CB2_CA11},
{1, CB3_CA12, CB1_CA12, CB2_CA12},
{1, CB3_CA14, CB1_CA14, CB2_CA14},
{1, CB3_CA15, CB1_CA15, CB2_CA15},
{1, CB3_CA16, CB1_CA16, CB2_CA16},
{1, CB6_CA1, CB4_CA1, CB5_CA1},
{1, CB6_CA2, CB4_CA2, CB5_CA2},
{1, CB6_CA3, CB4_CA3, CB5_CA3},
{1, CB6_CA6, CB4_CA6, CB5_CA6},
{1, CB6_CA7, CB4_CA7, CB5_CA7},
{1, CB6_CA8, CB4_CA8, CB5_CA8},
{1, CB6_CA10, CB4_CA10, CB5_CA10},
{1, CB6_CA13, CB4_CA13, CB5_CA13},
{1, CB6_CA14, CB4_CA14, CB5_CA14},
{1, CB6_CA15, CB4_CA15, CB5_CA15},
{1, CB6_CA16, CB4_CA16, CB5_CA16}
};
#define __ NO_LED
led_config_t g_led_config = {
{
// Key Matrix to LED Index
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, __, __ },
{ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, __, 28 },
{ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, __, 41, __, 42 },
{ 43, __, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, __, 54, 55, 56 },
{ 57, 58, 59, __, __, 60, 61, 62, __, 63, __, __, 64, 65, 66, 67 },
},
{
// LED Index to Physical Position
{8, 1}, {20, 1}, {33, 0}, {48, 3}, {61, 6}, {74, 8}, {87,11}, {106,11}, {119, 8}, {132, 6}, {145, 3}, {160, 0}, {173, 1}, {193, 1},
{8,14}, {24,14}, {39,14}, {52,17}, {65,20}, {78,22}, {103,25},{116,22}, {129,20}, {142,17}, {155,14}, {170,14}, {183,14}, {200,14}, {222,14},
{8,27}, {24,27}, {39,28}, {52,30}, {65,33}, {78,36}, {109,37},{122,34}, {135,32}, {148,29}, {162,27}, {176,27}, {197,27}, {224,27},
{8,40}, {28,40}, {43,42}, {56,44}, {69,47}, {82,50}, {102,52}, {115,49}, {128,46}, {141,44}, {154,44}, {169,40}, {189,40}, {209,43},
{0,53}, {16,53}, {42,55}, {65,60}, {86,64}, {107,64}, {131,59}, {156,54}, {196,56}, {209,56}, {222,56},
},
{
// RGB LED Index to Flag
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
};
#endif

View File

@@ -0,0 +1 @@
# This file intentionally left blank

View File

@@ -0,0 +1,46 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef LED_MATRIX_ENABLE
/* LED matrix driver configuration */
# define LED_MATRIX_LED_COUNT 68
# define DRIVER_CS_PINS \
{ B9 }
/* Use first 6 channels of LED driver */
# define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_6_CHANNEL
/* Set LED driver current */
# define SNLED27351_CURRENT_TUNE \
{ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50 }
/* Set to infinit, which is use in USB mode by default */
# define LED_MATRIX_TIMEOUT LED_MATRIX_TIMEOUT_INFINITE
/* Allow shutdown of led driver to save power */
# define LED_MATRIX_DRIVER_SHUTDOWN_ENABLE
# define LED_MATRIX_DRIVER_LOAD_ENABLE
/* Turn off backlight on low brightness to save power */
# define LED_MATRIX_BRIGHTNESS_TURN_OFF_VAL 48
/* Indications */
# define DIM_CAPS_LOCK
# define CAPS_LOCK_INDEX 29
# define LOW_BAT_IND_INDEX \
{ 60, 63 }
# define LED_MATRIX_KEYPRESSES
#endif

View File

@@ -0,0 +1,39 @@
{
"usb": {
"pid": "0x0AB9",
"device_version": "1.0.0"
},
"features": {
"led_matrix": true,
"encoder": true
},
"encoder": {
"rotary": [
{
"pin_a": "A8",
"pin_b": "C9"
}
]
},
"led_matrix": {
"driver": "snled27351_spi",
"sleep": true,
"animations": {
"none": true,
"solid": true,
"breathing": true,
"band_pinwheel": true,
"band_spiral": true,
"cycle_left_right": true,
"cycle_up_down": true,
"cycle_out_in": true,
"dual_beacon": true,
"solid_reactive_simple": true,
"solid_reactive_multiwide": true,
"solid_reactive_multinexus": true,
"solid_splash": true,
"wave_left_right": true,
"wave_up_down": true
}
}
}

View File

@@ -0,0 +1,83 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
enum layers{
MAC_BASE,
WIN_BASE,
MAC_FN1,
WIN_FN1,
FN2,
};
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_69_ansi(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_MUTE,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_HOME,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, MO(MAC_FN1), MO(FN2), KC_SPC, KC_RCMMD, KC_LEFT, KC_DOWN, KC_RGHT),
[WIN_BASE] = LAYOUT_69_ansi(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_MUTE,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_HOME,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LWIN, KC_LALT, KC_SPC, MO(WIN_FN1), MO(FN2), KC_SPC, KC_RALT, KC_LEFT, KC_DOWN, KC_RGHT),
[MAC_FN1] = LAYOUT_69_ansi(
KC_GRV, KC_BRID, KC_BRIU, KC_MCTRL,KC_LNPAD, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, BL_TOGG,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_INS,
BL_TOGG, BL_STEP, BL_UP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_END,
_______, _______, BL_DOWN, _______, _______, _______, _______, NK_TOGG, _______, _______, _______, _______, _______, KC_PGUP,
_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______),
[WIN_FN1] = LAYOUT_69_ansi(
KC_GRV, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, BL_TOGG,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_INS,
BL_TOGG, BL_STEP, BL_UP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_END,
_______, _______, BL_DOWN, _______, _______, _______, _______, NK_TOGG, _______, _______, _______, _______, _______, KC_PGUP,
_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______),
[FN2] = LAYOUT_69_ansi(
KC_TILD, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, BAT_LVL, BAT_LVL, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______)
};
#if defined(ENCODER_MAP_ENABLE)
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
[MAC_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
[WIN_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
[MAC_FN1] = {ENCODER_CCW_CW(BL_DOWN, BL_UP)},
[WIN_FN1] = {ENCODER_CCW_CW(BL_DOWN, BL_UP)},
[FN2] = {ENCODER_CCW_CW(_______, _______)},
};
#endif // ENCODER_MAP_ENABLE
// clang-format on
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_record_keychron_common(keycode, record)) {
return false;
}
return true;
}

View File

@@ -0,0 +1 @@
# This file intentionally left blank

View File

@@ -0,0 +1,129 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
// clang-format off
#ifdef LED_MATRIX_ENABLE
const snled27351_led_t g_snled27351_leds[LED_MATRIX_LED_COUNT] = {
/* Refer to SNLED27351 manual for these locations
* driver
* | LED address
* | | */
{0, CB5_CA1},
{0, CB5_CA2},
{0, CB5_CA3},
{0, CB5_CA4},
{0, CB5_CA5},
{0, CB5_CA6},
{0, CB5_CA7},
{0, CB5_CA8},
{0, CB5_CA9},
{0, CB5_CA10},
{0, CB5_CA11},
{0, CB5_CA12},
{0, CB5_CA13},
{0, CB5_CA14},
{0, CB4_CA1},
{0, CB4_CA2},
{0, CB4_CA3},
{0, CB4_CA4},
{0, CB4_CA5},
{0, CB4_CA6},
{0, CB4_CA7},
{0, CB4_CA8},
{0, CB4_CA9},
{0, CB4_CA10},
{0, CB4_CA11},
{0, CB4_CA12},
{0, CB4_CA13},
{0, CB4_CA14},
{0, CB4_CA16},
{0, CB3_CA1},
{0, CB3_CA2},
{0, CB3_CA3},
{0, CB3_CA4},
{0, CB3_CA5},
{0, CB3_CA6},
{0, CB3_CA7},
{0, CB3_CA8},
{0, CB3_CA9},
{0, CB3_CA10},
{0, CB3_CA11},
{0, CB3_CA12},
{0, CB3_CA14},
{0, CB3_CA16},
{0, CB2_CA1},
{0, CB2_CA3},
{0, CB2_CA4},
{0, CB2_CA5},
{0, CB2_CA6},
{0, CB2_CA7},
{0, CB2_CA8},
{0, CB2_CA9},
{0, CB2_CA10},
{0, CB2_CA11},
{0, CB2_CA12},
{0, CB2_CA14},
{0, CB2_CA15},
{0, CB2_CA16},
{0, CB1_CA1},
{0, CB1_CA2},
{0, CB1_CA3},
{0, CB1_CA6},
{0, CB1_CA7},
{0, CB1_CA8},
{0, CB1_CA10},
{0, CB1_CA13},
{0, CB1_CA14},
{0, CB1_CA15},
{0, CB1_CA16},
};
#define __ NO_LED
led_config_t g_led_config = {
{
// Key Matrix to LED Index
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, __, __ },
{ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, __, 28 },
{ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, __, 41, __, 42 },
{ 43, __, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, __, 54, 55, 56 },
{ 57, 58, 59, __, __, 60, 61, 62, __, 63, __, __, 64, 65, 66, 67 },
},
{
// LED Index to Physical Position
{8, 1}, {20, 1}, {33, 0}, {48, 3}, {61, 6}, {74, 8}, {87,11}, {106,11}, {119, 8}, {132, 6}, {145, 3}, {160, 0}, {173, 1}, {193, 1},
{8,14}, {24,14}, {39,14}, {52,17}, {65,20}, {78,22}, {103,25},{116,22}, {129,20}, {142,17}, {155,14}, {170,14}, {183,14}, {200,14}, {222,14},
{8,27}, {24,27}, {39,28}, {52,30}, {65,33}, {78,36}, {109,37},{122,34}, {135,32}, {148,29}, {162,27}, {176,27}, {197,27}, {224,27},
{8,40}, {28,40}, {43,42}, {56,44}, {69,47}, {82,50}, {102,52}, {115,49}, {128,46}, {141,44}, {154,44}, {169,40}, {189,40}, {209,43},
{0,53}, {16,53}, {42,55}, {65,60}, {86,64}, {107,64}, {131,59}, {156,54}, {196,56}, {209,56}, {222,56},
},
{
//LED Index to Flag
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
};
#endif

View File

@@ -0,0 +1,226 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include_next <board.h>
// clang-format off
/* Set GPIOA_SWDIO to INPUT and NOT FLOATING */
#undef VAL_GPIOA_MODER
#define VAL_GPIOA_MODER (PIN_MODE_INPUT(GPIOA_BUTTON) | \
PIN_MODE_INPUT(GPIOA_PIN1) | \
PIN_MODE_INPUT(GPIOA_PIN2) | \
PIN_MODE_INPUT(GPIOA_PIN3) | \
PIN_MODE_ALTERNATE(GPIOA_CS43L22_LRCK) |\
PIN_MODE_ALTERNATE(GPIOA_L3GD20_SCL) | \
PIN_MODE_ALTERNATE(GPIOA_L3GD20_SD0) | \
PIN_MODE_ALTERNATE(GPIOA_L3GD20_SDI) | \
PIN_MODE_INPUT(GPIOA_PIN8) | \
PIN_MODE_INPUT(GPIOA_VBUS_FS) | \
PIN_MODE_ALTERNATE(GPIOA_OTG_FS_ID) | \
PIN_MODE_ALTERNATE(GPIOA_OTG_FS_DM) | \
PIN_MODE_ALTERNATE(GPIOA_OTG_FS_DP) | \
PIN_MODE_INPUT(GPIOA_SWDIO) | \
PIN_MODE_INPUT(GPIOA_SWCLK) | \
PIN_MODE_INPUT(GPIOA_PIN15))
#undef VAL_GPIOA_PUPDR
#define VAL_GPIOA_PUPDR (PIN_PUPDR_FLOATING(GPIOA_BUTTON) | \
PIN_PUPDR_PULLUP(GPIOA_PIN1) | \
PIN_PUPDR_PULLUP(GPIOA_PIN2) | \
PIN_PUPDR_PULLUP(GPIOA_PIN3) | \
PIN_PUPDR_FLOATING(GPIOA_CS43L22_LRCK) |\
PIN_PUPDR_FLOATING(GPIOA_L3GD20_SCL) | \
PIN_PUPDR_PULLUP(GPIOA_L3GD20_SD0) | \
PIN_PUPDR_PULLUP(GPIOA_L3GD20_SDI) | \
PIN_PUPDR_PULLUP(GPIOA_PIN8) | \
PIN_PUPDR_FLOATING(GPIOA_VBUS_FS) | \
PIN_PUPDR_FLOATING(GPIOA_OTG_FS_ID) | \
PIN_PUPDR_FLOATING(GPIOA_OTG_FS_DM) | \
PIN_PUPDR_FLOATING(GPIOA_OTG_FS_DP) | \
PIN_PUPDR_PULLDOWN(GPIOA_SWDIO) | \
PIN_PUPDR_PULLUP(GPIOA_SWCLK) | \
PIN_PUPDR_PULLUP(GPIOA_PIN15))
#undef VAL_GPIOB_MODER
#define VAL_GPIOB_MODER (PIN_MODE_INPUT(GPIOB_PIN0) | \
PIN_MODE_INPUT(GPIOB_PIN1) | \
PIN_MODE_INPUT(GPIOB_PIN2) | \
PIN_MODE_INPUT(GPIOB_SWO) | \
PIN_MODE_INPUT(GPIOB_PIN4) | \
PIN_MODE_INPUT(GPIOB_PIN5) | \
PIN_MODE_INPUT(GPIOB_LSM303DLHC_SCL) | \
PIN_MODE_INPUT(GPIOB_PIN7) | \
PIN_MODE_INPUT(GPIOB_PIN8) | \
PIN_MODE_INPUT(GPIOB_LSM303DLHC_SDA) | \
PIN_MODE_INPUT(GPIOB_MP45DT02_CLK_IN) |\
PIN_MODE_INPUT(GPIOB_PIN11) | \
PIN_MODE_INPUT(GPIOB_PIN12) | \
PIN_MODE_INPUT(GPIOB_PIN13) | \
PIN_MODE_INPUT(GPIOB_PIN14) | \
PIN_MODE_INPUT(GPIOB_PIN15))
#undef VAL_GPIOB_PUPDR
#define VAL_GPIOB_PUPDR (PIN_PUPDR_PULLDOWN(GPIOB_PIN0) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN1) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN2) | \
PIN_PUPDR_PULLDOWN(GPIOB_SWO) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN4) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN5) | \
PIN_PUPDR_PULLDOWN(GPIOB_LSM303DLHC_SCL) |\
PIN_PUPDR_PULLDOWN(GPIOB_PIN7) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN8) | \
PIN_PUPDR_PULLDOWN(GPIOB_LSM303DLHC_SDA) |\
PIN_PUPDR_PULLDOWN(GPIOB_MP45DT02_CLK_IN) |\
PIN_PUPDR_PULLDOWN(GPIOB_PIN11) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN12) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN13) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN14) | \
PIN_PUPDR_PULLDOWN(GPIOB_PIN15))
#undef VAL_GPIOB_AFRL
#define VAL_GPIOB_AFRL (PIN_AFIO_AF(GPIOB_PIN0, 0U) | \
PIN_AFIO_AF(GPIOB_PIN1, 0U) | \
PIN_AFIO_AF(GPIOB_PIN2, 0U) | \
PIN_AFIO_AF(GPIOB_SWO, 0U) | \
PIN_AFIO_AF(GPIOB_PIN4, 0U) | \
PIN_AFIO_AF(GPIOB_PIN5, 0U) | \
PIN_AFIO_AF(GPIOB_LSM303DLHC_SCL, 0) | \
PIN_AFIO_AF(GPIOB_PIN7, 0U))
#undef VAL_GPIOB_AFRH
#define VAL_GPIOB_AFRH (PIN_AFIO_AF(GPIOB_PIN8, 0U) | \
PIN_AFIO_AF(GPIOB_LSM303DLHC_SDA, 0) | \
PIN_AFIO_AF(GPIOB_MP45DT02_CLK_IN, 0U) |\
PIN_AFIO_AF(GPIOB_PIN11, 0U) | \
PIN_AFIO_AF(GPIOB_PIN12, 0U) | \
PIN_AFIO_AF(GPIOB_PIN13, 0U) | \
PIN_AFIO_AF(GPIOB_PIN14, 0U) | \
PIN_AFIO_AF(GPIOB_PIN15, 0U))
/* C5 Need to be pulldown */
#undef VAL_GPIOC_MODER
#define VAL_GPIOC_MODER (PIN_MODE_INPUT(GPIOC_OTG_FS_POWER_ON) |\
PIN_MODE_INPUT(GPIOC_PIN1) | \
PIN_MODE_INPUT(GPIOC_PIN2) | \
PIN_MODE_INPUT(GPIOC_CS43L22_AIN4x) | \
PIN_MODE_INPUT(GPIOC_PIN4) | \
PIN_MODE_INPUT(GPIOC_PIN5) | \
PIN_MODE_INPUT(GPIOC_PIN6) | \
PIN_MODE_INPUT(GPIOC_CS43L22_MCLK) | \
PIN_MODE_INPUT(GPIOC_PIN8) | \
PIN_MODE_INPUT(GPIOC_PIN9) | \
PIN_MODE_INPUT(GPIOC_CS43L22_SCLK) | \
PIN_MODE_INPUT(GPIOC_PIN11) | \
PIN_MODE_INPUT(GPIOC_CS43L22_SDIN) | \
PIN_MODE_INPUT(GPIOC_PIN13) | \
PIN_MODE_INPUT(GPIOC_OSC32_IN) | \
PIN_MODE_INPUT(GPIOC_OSC32_OUT))
#undef VAL_GPIOC_PUPDR
#define VAL_GPIOC_PUPDR (PIN_PUPDR_PULLUP(GPIOC_OTG_FS_POWER_ON) |\
PIN_PUPDR_PULLUP(GPIOC_PIN1) | \
PIN_PUPDR_PULLUP(GPIOC_PIN2) | \
PIN_PUPDR_PULLUP(GPIOC_CS43L22_AIN4x) |\
PIN_PUPDR_PULLUP(GPIOC_PIN4) | \
PIN_PUPDR_PULLDOWN(GPIOC_PIN5) | \
PIN_PUPDR_PULLUP(GPIOC_PIN6) | \
PIN_PUPDR_PULLUP(GPIOC_CS43L22_MCLK) | \
PIN_PUPDR_PULLUP(GPIOC_PIN8) | \
PIN_PUPDR_PULLUP(GPIOC_PIN9) | \
PIN_PUPDR_PULLUP(GPIOC_CS43L22_SCLK) | \
PIN_PUPDR_PULLUP(GPIOC_PIN11) | \
PIN_PUPDR_PULLUP(GPIOC_CS43L22_SDIN) | \
PIN_PUPDR_PULLUP(GPIOC_PIN13) | \
PIN_PUPDR_PULLUP(GPIOC_OSC32_IN) | \
PIN_PUPDR_PULLUP(GPIOC_OSC32_OUT))
/* Set all GPIOD pins to INPUT & PULLUP to avoid FLOATING */
#undef VAL_GPIOD_MODER
#define VAL_GPIOD_MODER (PIN_MODE_INPUT(GPIOD_PIN0) | \
PIN_MODE_INPUT(GPIOD_PIN1) | \
PIN_MODE_INPUT(GPIOD_PIN2) | \
PIN_MODE_INPUT(GPIOD_PIN3) | \
PIN_MODE_INPUT(GPIOD_CS43L22_RESET) | \
PIN_MODE_INPUT(GPIOD_OverCurrent) | \
PIN_MODE_INPUT(GPIOD_PIN6) | \
PIN_MODE_INPUT(GPIOD_PIN7) | \
PIN_MODE_INPUT(GPIOD_PIN8) | \
PIN_MODE_INPUT(GPIOD_PIN9) | \
PIN_MODE_INPUT(GPIOD_PIN10) | \
PIN_MODE_INPUT(GPIOD_PIN11) | \
PIN_MODE_INPUT(GPIOD_LED4) | \
PIN_MODE_INPUT(GPIOD_LED3) | \
PIN_MODE_INPUT(GPIOD_LED5) | \
PIN_MODE_INPUT(GPIOD_LED6))
#undef VAL_GPIOD_PUPDR
#define VAL_GPIOD_PUPDR (PIN_PUPDR_PULLUP(GPIOD_PIN0) | \
PIN_PUPDR_PULLUP(GPIOD_PIN1) | \
PIN_PUPDR_PULLUP(GPIOD_PIN2) | \
PIN_PUPDR_PULLUP(GPIOD_PIN3) | \
PIN_PUPDR_PULLUP(GPIOD_CS43L22_RESET) |\
PIN_PUPDR_PULLUP(GPIOD_OverCurrent) | \
PIN_PUPDR_PULLUP(GPIOD_PIN6) | \
PIN_PUPDR_PULLUP(GPIOD_PIN7) | \
PIN_PUPDR_PULLUP(GPIOD_PIN8) | \
PIN_PUPDR_PULLUP(GPIOD_PIN9) | \
PIN_PUPDR_PULLUP(GPIOD_PIN10) | \
PIN_PUPDR_PULLUP(GPIOD_PIN11) | \
PIN_PUPDR_PULLUP(GPIOD_LED4) | \
PIN_PUPDR_PULLUP(GPIOD_LED3) | \
PIN_PUPDR_PULLUP(GPIOD_LED5) | \
PIN_PUPDR_PULLUP(GPIOD_LED6))
/* Set all GPIOE pins to INPUT & PULLUP to avoid FLOATING */
#undef VAL_GPIOE_MODER
#define VAL_GPIOE_MODER (PIN_MODE_INPUT(GPIOE_L3GD20_INT1) | \
PIN_MODE_INPUT(GPIOE_L3GD20_INT2) | \
PIN_MODE_INPUT(GPIOE_LSM303DLHC_DRDY) |\
PIN_MODE_INPUT(GPIOE_L3GD20_CS) | \
PIN_MODE_INPUT(GPIOE_LSM303DLHC_INT1) |\
PIN_MODE_INPUT(GPIOE_LSM303DLHC_INT2) |\
PIN_MODE_INPUT(GPIOE_PIN6) | \
PIN_MODE_INPUT(GPIOE_PIN7) | \
PIN_MODE_INPUT(GPIOE_PIN8) | \
PIN_MODE_INPUT(GPIOE_PIN9) | \
PIN_MODE_INPUT(GPIOE_PIN10) | \
PIN_MODE_INPUT(GPIOE_PIN11) | \
PIN_MODE_INPUT(GPIOE_PIN12) | \
PIN_MODE_INPUT(GPIOE_PIN13) | \
PIN_MODE_INPUT(GPIOE_PIN14) | \
PIN_MODE_INPUT(GPIOE_PIN15))
#undef VAL_GPIOE_PUPDR
#define VAL_GPIOE_PUPDR (PIN_PUPDR_PULLUP(GPIOE_L3GD20_INT1) | \
PIN_PUPDR_PULLUP(GPIOE_L3GD20_INT2) | \
PIN_PUPDR_PULLUP(GPIOE_LSM303DLHC_DRDY) |\
PIN_PUPDR_PULLUP(GPIOE_L3GD20_CS) | \
PIN_PUPDR_PULLUP(GPIOE_LSM303DLHC_INT1) |\
PIN_PUPDR_PULLUP(GPIOE_LSM303DLHC_INT2) |\
PIN_PUPDR_PULLUP(GPIOE_PIN6) | \
PIN_PUPDR_PULLUP(GPIOE_PIN7) | \
PIN_PUPDR_PULLUP(GPIOE_PIN8) | \
PIN_PUPDR_PULLUP(GPIOE_PIN9) | \
PIN_PUPDR_PULLUP(GPIOE_PIN10) | \
PIN_PUPDR_PULLUP(GPIOE_PIN11) | \
PIN_PUPDR_PULLUP(GPIOE_PIN12) | \
PIN_PUPDR_PULLUP(GPIOE_PIN13) | \
PIN_PUPDR_PULLUP(GPIOE_PIN14) | \
PIN_PUPDR_PULLUP(GPIOE_PIN15))

View File

@@ -0,0 +1,80 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
/* Encoder Configuration */
#define ENCODER_DEFAULT_POS 0x3
#define ENCODER_MAP_KEY_DELAY 2
#if defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE) || defined(LK_WIRELESS_ENABLE)
/* SPI configuration */
# define SPI_DRIVER SPID1
# define SPI_SCK_PIN A5
# define SPI_MISO_PIN A6
# define SPI_MOSI_PIN A7
#endif
#if defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE)
# define SNLED27351_SDB_PIN B7
# define SNLED23751_SPI_DIVISOR 16
#endif
#ifdef LK_WIRELESS_ENABLE
/* Hardware configuration */
# define P24G_MODE_SELECT_PIN A10
# define BT_MODE_SELECT_PIN A9
# define LKBT51_RESET_PIN C4
# define LKBT51_INT_INPUT_PIN B1
# define LKBT51_INT_OUTPUT_PIN A4
# define USB_POWER_SENSE_PIN B0
# define USB_POWER_CONNECTED_LEVEL 0
# define BAT_CHARGING_PIN B13
# define BAT_CHARGING_LEVEL 0
# define BAT_LOW_LED_PIN B12
# define BAT_LOW_LED_PIN_ON_STATE 1
# define BT_HOST_DEVICES_COUNT 3
# define P24G_HOST_DEVICES_COUNT 1
# if defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE)
# define P24G_INDICATION_LED_MATRIX_INDEX 18
# define BT_INDCATION_LED_MATRIX_LIST \
{ 15, 16, 17 }
# define BAT_LEVEL_LED_LIST \
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
/* Backlit disable timeout when keyboard is disconnected(unit: second) */
# define DISCONNECTED_BACKLIGHT_DISABLE_TIMEOUT 40
/* Backlit disable timeout when keyboard is connected(unit: second) */
# define CONNECTED_BACKLIGHT_DISABLE_TIMEOUT 600
/* Reinit LED driver on tranport changed */
# define REINIT_LED_DRIVER 1
# endif
/* Keep USB connection in wireless mode */
# define KEEP_USB_CONNECTION_IN_WIRELESS_MODE
#endif
/* Factory test keys */
#define FN_KEY_1 MO(4)
#define MATRIX_IO_DELAY 10

View File

@@ -0,0 +1,31 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software : you can redistribute it and /or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see < http://www.gnu.org/licenses/>.
*/
#pragma once
#define _CHIBIOS_HAL_CONF_VER_8_4_
#define HAL_USE_SPI TRUE
#ifdef LK_WIRELESS_ENABLE
# define HAL_USE_RTC TRUE
#endif
#if defined(LK_WIRELESS_ENABLE) || defined(ENCODER_ENABLE)
# define PAL_USE_CALLBACKS TRUE
#endif
#include_next <halconf.h>

View File

@@ -0,0 +1,202 @@
{
"keyboard_name": "Keychron K11 Max",
"manufacturer": "Keychron",
"url": "https://github.com/Keychron",
"maintainer": "lokher",
"processor": "STM32F401",
"bootloader": "stm32-dfu",
"usb": {
"vid": "0x3434"
},
"features": {
"bootmagic": true,
"extrakey": true,
"mousekey": true,
"dip_switch": true,
"encoder": true,
"encoder_map": true,
"nkro": true,
"raw": true,
"sendstring": true
},
"matrix_pins": {
"cols": ["C6", "C7", "C8", "A14", "A15", "C10", "C11", "C13", "C14", "C15", "C0", "C1", "C2", "C3", "A0", "A1"],
"rows": ["D2", "B3", "B4", "B5", "B6"]
},
"diode_direction": "ROW2COL",
"dip_switch": {
"pins": ["B14"]
},
"indicators": {
"caps_lock": "A13",
"on_state": 1
},
"dynamic_keymap": {
"layer_count": 5
},
"eeprom": {
"wear_leveling": {
"driver": "embedded_flash",
"logical_size": 2048,
"backing_size": 4096
}
},
"layouts": {
"LAYOUT_69_ansi": {
"layout": [
{"matrix":[0, 0], "x":0.75, "y":0.25},
{"matrix":[0, 1], "x":1.75, "y":0.25},
{"matrix":[0, 2], "x":2.75, "y":0},
{"matrix":[0, 3], "x":3.75, "y":0.25},
{"matrix":[0, 4], "x":4.75, "y":0.25},
{"matrix":[0, 5], "x":5.75, "y":0.25},
{"matrix":[0, 6], "x":6.75, "y":0.25},
{"matrix":[0, 7], "x":9.5, "y":0.25},
{"matrix":[0, 8], "x":10.5, "y":0.25},
{"matrix":[0, 9], "x":11.5, "y":0.25},
{"matrix":[0,10], "x":12.5, "y":0.25},
{"matrix":[0,11], "x":13.5, "y":0},
{"matrix":[0,12], "x":14.5, "y":0.25},
{"matrix":[0,13], "x":15.5, "y":0.25, "w":2},
{"matrix":[0,15], "x":18, "y":0},
{"matrix":[1, 0], "x":0.5, "y":1.25, "w":1.5},
{"matrix":[1, 1], "x":2, "y":1.25},
{"matrix":[1, 2], "x":3.25, "y":1.25},
{"matrix":[1, 3], "x":4.25, "y":1.25},
{"matrix":[1, 4], "x":5.25, "y":1.25},
{"matrix":[1, 5], "x":6.25, "y":1.25},
{"matrix":[1, 6], "x":9, "y":1.25},
{"matrix":[1, 7], "x":10, "y":1.25},
{"matrix":[1, 8], "x":11, "y":1.25},
{"matrix":[1, 9], "x":12, "y":1.25},
{"matrix":[1,10], "x":13.25, "y":1.25},
{"matrix":[1,11], "x":14.25, "y":1.25},
{"matrix":[1,12], "x":15.25, "y":1.25},
{"matrix":[1,13], "x":16.25, "y":1.25, "w":1.5},
{"matrix":[1,15], "x":18.25, "y":1.5},
{"matrix":[2, 0], "x":0.25, "y":2.25, "w":1.75},
{"matrix":[2, 1], "x":2, "y":2.25},
{"matrix":[2, 2], "x":3.5, "y":2.25},
{"matrix":[2, 3], "x":4.5, "y":2.25},
{"matrix":[2, 4], "x":5.5, "y":2.25},
{"matrix":[2, 5], "x":6.5, "y":2.25},
{"matrix":[2, 6], "x":9.5, "y":2.25},
{"matrix":[2, 7], "x":10.25, "y":2.25},
{"matrix":[2, 8], "x":11.25, "y":2.25},
{"matrix":[2, 9], "x":12.25, "y":2.25},
{"matrix":[2,10], "x":13.25, "y":2.25},
{"matrix":[2,11], "x":14.75, "y":2.25},
{"matrix":[2,13], "x":15.75, "y":2.25, "w":2.25},
{"matrix":[2,15], "x":18.5, "y":2.5},
{"matrix":[3, 0], "x":0, "y":3.25, "w":2.25},
{"matrix":[3, 2], "x":2.25, "y":3.25},
{"matrix":[3, 3], "x":3.75, "y":3.25},
{"matrix":[3, 4], "x":4.75, "y":3.25},
{"matrix":[3, 5], "x":5.75, "y":3.25},
{"matrix":[3, 6], "x":6.75, "y":3.25},
{"matrix":[3, 7], "x":8.5, "y":3.25},
{"matrix":[3, 8], "x":9.5, "y":3.25},
{"matrix":[3, 9], "x":10.5, "y":3.25},
{"matrix":[3,10], "x":11.5, "y":3.25},
{"matrix":[3,11], "x":12.5, "y":3.25},
{"matrix":[3,13], "x":14.25, "y":3.25},
{"matrix":[3,14], "x":15.25, "y":3.25, "w":1.75},
{"matrix":[3,15], "x":17.25, "y":3.5},
{"matrix":[4, 0], "x":0, "y":4.25, "w":1.25},
{"matrix":[4, 1], "x":1.25, "y":4.25, "w":1.25},
{"matrix":[4, 2], "x":3.75, "y":4.25, "w":1.25},
{"matrix":[4, 5], "x":5, "y":4.25, "w":2.25},
{"matrix":[4, 6], "x":7.25, "y":4.25},
{"matrix":[4, 7], "x":8.75, "y":4.25},
{"matrix":[4, 9], "x":9.75, "y":4.25, "w":2.75},
{"matrix":[4,12], "x":12.5, "y":4.25},
{"matrix":[4,13], "x":16.25, "y":4.5},
{"matrix":[4,14], "x":17.25, "y":4.5},
{"matrix":[4,15], "x":18.25, "y":4.5}
]
},
"LAYOUT_70_iso": {
"layout": [
{"matrix":[0, 0], "x":0.75, "y":0.25},
{"matrix":[0, 1], "x":1.75, "y":0.25},
{"matrix":[0, 2], "x":2.75, "y":0},
{"matrix":[0, 3], "x":3.75, "y":0.25},
{"matrix":[0, 4], "x":4.75, "y":0.25},
{"matrix":[0, 5], "x":5.75, "y":0.25},
{"matrix":[0, 6], "x":6.75, "y":0.25},
{"matrix":[0, 7], "x":9.5, "y":0.25},
{"matrix":[0, 8], "x":10.5, "y":0.25},
{"matrix":[0, 9], "x":11.5, "y":0.25},
{"matrix":[0,10], "x":12.5, "y":0.25},
{"matrix":[0,11], "x":13.5, "y":0},
{"matrix":[0,12], "x":14.5, "y":0.25},
{"matrix":[0,13], "x":15.5, "y":0.25, "w":2},
{"matrix":[0,15], "x":18, "y":0},
{"matrix":[1, 0], "x":0.5, "y":1.25, "w":1.5},
{"matrix":[1, 1], "x":2, "y":1.25},
{"matrix":[1, 2], "x":3.25, "y":1.25},
{"matrix":[1, 3], "x":4.25, "y":1.25},
{"matrix":[1, 4], "x":5.25, "y":1.25},
{"matrix":[1, 5], "x":6.25, "y":1.25},
{"matrix":[1, 6], "x":9, "y":1.25},
{"matrix":[1, 7], "x":10, "y":1.25},
{"matrix":[1, 8], "x":11, "y":1.25},
{"matrix":[1, 9], "x":12, "y":1.25},
{"matrix":[1,10], "x":13.25, "y":1.25},
{"matrix":[1,11], "x":14.25, "y":1.25},
{"matrix":[1,12], "x":15.25, "y":1.25},
{"matrix":[1,15], "x":18.25, "y":1.5},
{"matrix":[2, 0], "x":0.25, "y":2.25, "w":1.75},
{"matrix":[2, 1], "x":2, "y":2.25},
{"matrix":[2, 2], "x":3.5, "y":2.25},
{"matrix":[2, 3], "x":4.5, "y":2.25},
{"matrix":[2, 4], "x":5.5, "y":2.25},
{"matrix":[2, 5], "x":6.5, "y":2.25},
{"matrix":[2, 6], "x":9.5, "y":2.25},
{"matrix":[2, 7], "x":10.25, "y":2.25},
{"matrix":[2, 8], "x":11.25, "y":2.25},
{"matrix":[2, 9], "x":12.25, "y":2.25},
{"matrix":[2,10], "x":13.25, "y":2.25},
{"matrix":[2,11], "x":14.75, "y":2.25},
{"matrix":[2,13], "x":15.75, "y":2.25},
{"matrix":[1,13], "x":16.75, "y":1.25, "w":1.25, "h":2},
{"matrix":[2,15], "x":18.5, "y":2.5},
{"matrix":[3, 0], "x":0, "y":3.25, "w":1.25},
{"matrix":[3, 1], "x":2.25, "y":3.25},
{"matrix":[3, 2], "x":2.25, "y":3.25},
{"matrix":[3, 3], "x":3.75, "y":3.25},
{"matrix":[3, 4], "x":4.75, "y":3.25},
{"matrix":[3, 5], "x":5.75, "y":3.25},
{"matrix":[3, 6], "x":6.75, "y":3.25},
{"matrix":[3, 7], "x":8.5, "y":3.25},
{"matrix":[3, 8], "x":9.5, "y":3.25},
{"matrix":[3, 9], "x":10.5, "y":3.25},
{"matrix":[3,10], "x":11.5, "y":3.25},
{"matrix":[3,11], "x":12.5, "y":3.25},
{"matrix":[3,13], "x":14.25, "y":3.25},
{"matrix":[3,14], "x":15.25, "y":3.25, "w":1.75},
{"matrix":[3,15], "x":17.25, "y":3.5},
{"matrix":[4, 0], "x":0, "y":4.25, "w":1.25},
{"matrix":[4, 1], "x":1.25, "y":4.25, "w":1.25},
{"matrix":[4, 2], "x":3.75, "y":4.25, "w":1.25},
{"matrix":[4, 5], "x":5, "y":4.25, "w":2.25},
{"matrix":[4, 6], "x":7.25, "y":4.25},
{"matrix":[4, 7], "x":8.75, "y":4.25},
{"matrix":[4, 9], "x":9.75, "y":4.25, "w":2.75},
{"matrix":[4,12], "x":12.5, "y":4.25},
{"matrix":[4,13], "x":16.25, "y":4.5},
{"matrix":[4,14], "x":17.25, "y":4.5},
{"matrix":[4,15], "x":18.25, "y":4.5}
]
}
}
}

View File

@@ -0,0 +1,46 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef RGB_MATRIX_ENABLE
/* RGB Matrix driver configuration */
# define RGB_MATRIX_LED_COUNT 69
# define DRIVER_CS_PINS \
{ B8, B9 }
/* Set LED driver current */
# define SNLED27351_CURRENT_TUNE \
{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }
/* Set to infinit, which is use in USB mode by default */
# define RGB_MATRIX_TIMEOUT RGB_MATRIX_TIMEOUT_INFINITE
/* Allow shutdown of led driver to save power */
# define RGB_MATRIX_DRIVER_SHUTDOWN_ENABLE
# define RGB_MATRIX_DRIVER_LOAD_ENABLE
/* Turn off backlight on low brightness to save power */
# define RGB_MATRIX_BRIGHTNESS_TURN_OFF_VAL 48
/* Indications */
# define DIM_CAPS_LOCK
# define CAPS_LOCK_INDEX 29
# define LOW_BAT_IND_INDEX \
{ 61, 64 }
# define RGB_MATRIX_KEYPRESSES
# define RGB_MATRIX_FRAMEBUFFER_EFFECTS
#endif

View File

@@ -0,0 +1,45 @@
{
"usb": {
"pid": "0x0AB4",
"device_version": "1.0.0"
},
"features": {
"rgb_matrix": true,
"encoder": true
},
"encoder": {
"rotary": [
{
"pin_a": "A8",
"pin_b": "C9"
}
]
},
"rgb_matrix": {
"driver": "snled27351_spi",
"sleep": true,
"animations": {
"band_spiral_val": true,
"breathing": true,
"cycle_all": true,
"cycle_left_right": true,
"cycle_out_in": true,
"cycle_out_in_dual": true,
"cycle_pinwheel": true,
"cycle_spiral": true,
"cycle_up_down": true,
"digital_rain": true,
"dual_beacon": true,
"jellybean_raindrops": true,
"pixel_rain": true,
"rainbow_beacon": true,
"rainbow_moving_chevron": true,
"solid_reactive_multinexus": true,
"solid_reactive_multiwide": true,
"solid_reactive_simple": true,
"solid_splash": true,
"splash": true,
"typing_heatmap": true
}
}
}

View File

@@ -0,0 +1,82 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
enum layers{
MAC_BASE,
WIN_BASE,
MAC_FN1,
WIN_FN1,
FN2,
};
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_70_iso(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_MUTE,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_DEL,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, KC_HOME,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, MO(MAC_FN1), MO(FN2), KC_SPC, KC_RCMMD, KC_LEFT, KC_DOWN, KC_RGHT),
[WIN_BASE] = LAYOUT_70_iso(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_MUTE,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_DEL,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, KC_HOME,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LWIN, KC_LALT, KC_SPC, MO(WIN_FN1), MO(FN2), KC_SPC, KC_RALT, KC_LEFT, KC_DOWN, KC_RGHT),
[MAC_FN1] = LAYOUT_70_iso(
KC_GRV, KC_BRID, KC_BRIU, KC_MCTRL, KC_LNPAD,RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, RGB_TOG,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, KC_INS,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, _______, KC_END,
_______, _______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, NK_TOGG, _______, _______, _______, _______, _______, KC_PGUP,
_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______),
[WIN_FN1] = LAYOUT_70_iso(
KC_GRV, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, RGB_TOG,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, KC_INS,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, _______, KC_END,
_______, _______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, NK_TOGG, _______, _______, _______, _______, _______, KC_PGUP,
_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______),
[FN2] = LAYOUT_70_iso(
KC_TILD, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, BAT_LVL, BAT_LVL, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______)
};
#if defined(ENCODER_MAP_ENABLE)
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
[MAC_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
[WIN_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
[MAC_FN1] = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI)},
[WIN_FN1] = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI)},
[FN2] = {ENCODER_CCW_CW(_______, _______)},
};
#endif // ENCODER_MAP_ENABLE
// clang-format on
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_record_keychron_common(keycode, record)) {
return false;
}
return true;
}

View File

@@ -0,0 +1,132 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
// clang-format off
#ifdef RGB_MATRIX_ENABLE
const snled27351_led_t PROGMEM g_snled27351_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to SNLED27351 manual for these locations
* driver
* | R location
* | | G location
* | | | B location
* | | | | */
{0, CB1_CA1, CB3_CA1, CB2_CA1},
{0, CB1_CA2, CB3_CA2, CB2_CA2},
{0, CB1_CA3, CB3_CA3, CB2_CA3},
{0, CB1_CA4, CB3_CA4, CB2_CA4},
{0, CB1_CA5, CB3_CA5, CB2_CA5},
{0, CB1_CA6, CB3_CA6, CB2_CA6},
{0, CB1_CA7, CB3_CA7, CB2_CA7},
{0, CB1_CA8, CB3_CA8, CB2_CA8},
{0, CB1_CA9, CB3_CA9, CB2_CA9},
{0, CB1_CA10, CB3_CA10, CB2_CA10},
{0, CB1_CA11, CB3_CA11, CB2_CA11},
{0, CB1_CA12, CB3_CA12, CB2_CA12},
{0, CB1_CA13, CB3_CA13, CB2_CA13},
{0, CB1_CA14, CB3_CA14, CB2_CA14},
{0, CB6_CA1, CB4_CA1, CB5_CA1},
{0, CB6_CA2, CB4_CA2, CB5_CA2},
{0, CB6_CA3, CB4_CA3, CB5_CA3},
{0, CB6_CA4, CB4_CA4, CB5_CA4},
{0, CB6_CA5, CB4_CA5, CB5_CA5},
{0, CB6_CA6, CB4_CA6, CB5_CA6},
{0, CB6_CA7, CB4_CA7, CB5_CA7},
{0, CB6_CA8, CB4_CA8, CB5_CA8},
{0, CB6_CA9, CB4_CA9, CB5_CA9},
{0, CB6_CA10, CB4_CA10, CB5_CA10},
{0, CB6_CA11, CB4_CA11, CB5_CA11},
{0, CB6_CA12, CB4_CA12, CB5_CA12},
{0, CB6_CA13, CB4_CA13, CB5_CA13},
{0, CB6_CA14, CB4_CA14, CB5_CA14},
{0, CB6_CA16, CB4_CA16, CB5_CA16},
{1, CB9_CA1, CB7_CA1, CB8_CA1},
{1, CB9_CA2, CB7_CA2, CB8_CA2},
{1, CB9_CA3, CB7_CA3, CB8_CA3},
{1, CB9_CA4, CB7_CA4, CB8_CA4},
{1, CB9_CA5, CB7_CA5, CB8_CA5},
{1, CB9_CA6, CB7_CA6, CB8_CA6},
{1, CB9_CA7, CB7_CA7, CB8_CA7},
{1, CB9_CA8, CB7_CA8, CB8_CA8},
{1, CB9_CA9, CB7_CA9, CB8_CA9},
{1, CB9_CA10, CB7_CA10, CB8_CA10},
{1, CB9_CA11, CB7_CA11, CB8_CA11},
{1, CB9_CA12, CB7_CA12, CB8_CA12},
{1, CB9_CA14, CB7_CA14, CB8_CA14},
{1, CB9_CA16, CB7_CA16, CB8_CA16},
{1, CB3_CA1, CB1_CA1, CB2_CA1},
{1, CB3_CA2, CB1_CA2, CB2_CA2},
{1, CB3_CA3, CB1_CA3, CB2_CA3},
{1, CB3_CA4, CB1_CA4, CB2_CA4},
{1, CB3_CA5, CB1_CA5, CB2_CA5},
{1, CB3_CA6, CB1_CA6, CB2_CA6},
{1, CB3_CA7, CB1_CA7, CB2_CA7},
{1, CB3_CA8, CB1_CA8, CB2_CA8},
{1, CB3_CA9, CB1_CA9, CB2_CA9},
{1, CB3_CA10, CB1_CA10, CB2_CA10},
{1, CB3_CA11, CB1_CA11, CB2_CA11},
{1, CB3_CA12, CB1_CA12, CB2_CA12},
{1, CB3_CA14, CB1_CA14, CB2_CA14},
{1, CB3_CA15, CB1_CA15, CB2_CA15},
{1, CB3_CA16, CB1_CA16, CB2_CA16},
{1, CB6_CA1, CB4_CA1, CB5_CA1},
{1, CB6_CA2, CB4_CA2, CB5_CA2},
{1, CB6_CA3, CB4_CA3, CB5_CA3},
{1, CB6_CA6, CB4_CA6, CB5_CA6},
{1, CB6_CA7, CB4_CA7, CB5_CA7},
{1, CB6_CA8, CB4_CA8, CB5_CA8},
{1, CB6_CA10, CB4_CA10, CB5_CA10},
{1, CB6_CA13, CB4_CA13, CB5_CA13},
{1, CB6_CA14, CB4_CA14, CB5_CA14},
{1, CB6_CA15, CB4_CA15, CB5_CA15},
{1, CB6_CA16, CB4_CA16, CB5_CA16}
};
#define __ NO_LED
led_config_t g_led_config = {
{
// Key Matrix to LED Index
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, __, __ },
{ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, __, 28 },
{ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, __, 41, __, 42 },
{ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, __, 55, 56, 57 },
{ 58, 59, 60, __, __, 61, 62, 63, __, 64, __, __, 65, 66, 67, 68 },
},
{
// LED Index to Physical Position
{8, 1}, {20, 1}, {33, 0}, {48, 3}, {61, 6}, {74, 8}, {87,11}, {106,11}, {119, 8}, {132, 6}, {145, 3}, {160, 0}, {173, 1}, {193, 1},
{8,14}, {24,14}, {39,14}, {52,17}, {65,20}, {78,22}, {103,25},{116,22}, {129,20}, {142,17}, {155,14}, {170,14}, {183,14}, {200,20}, {222,14},
{8,27}, {24,27}, {39,28}, {52,30}, {65,33}, {78,36}, {109,37},{122,34}, {135,32}, {148,29}, {162,27}, {176,27}, {190,27}, {224,27},
{8,40}, {13,40}, {28,40}, {43,42}, {56,44}, {69,47}, {82,50}, {102,52}, {115,49}, {128,46}, {141,44}, {154,44}, {169,40}, {189,40}, {209,43},
{0,53}, {16,53}, {42,55}, {65,60}, {86,64}, {107,64}, {131,59}, {156,54}, {196,56}, {209,56}, {222,56},
},
{
// RGB LED Index to Flag
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
};
#endif

View File

@@ -0,0 +1 @@
# This file intentionally left blank

View File

@@ -0,0 +1,46 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef LED_MATRIX_ENABLE
/* LED matrix driver configuration */
# define LED_MATRIX_LED_COUNT 69
# define DRIVER_CS_PINS \
{ B9 }
/* Use first 6 channels of LED driver */
# define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_6_CHANNEL
/* Set LED driver current */
# define SNLED27351_CURRENT_TUNE \
{ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50 }
/* Set to infinit, which is use in USB mode by default */
# define LED_MATRIX_TIMEOUT LED_MATRIX_TIMEOUT_INFINITE
/* Allow shutdown of led driver to save power */
# define LED_MATRIX_DRIVER_SHUTDOWN_ENABLE
# define LED_MATRIX_DRIVER_LOAD_ENABLE
/* Turn off backlight on low brightness to save power */
# define LED_MATRIX_BRIGHTNESS_TURN_OFF_VAL 48
/* Indications */
# define DIM_CAPS_LOCK
# define CAPS_LOCK_INDEX 29
# define LOW_BAT_IND_INDEX \
{ 61, 64 }
# define LED_MATRIX_KEYPRESSES
#endif

View File

@@ -0,0 +1,39 @@
{
"usb": {
"pid": "0x0ABA",
"device_version": "1.0.0"
},
"features": {
"led_matrix": true,
"encoder": true
},
"encoder": {
"rotary": [
{
"pin_a": "A8",
"pin_b": "C9"
}
]
},
"led_matrix": {
"driver": "snled27351_spi",
"sleep": true,
"animations": {
"none": true,
"solid": true,
"breathing": true,
"band_pinwheel": true,
"band_spiral": true,
"cycle_left_right": true,
"cycle_up_down": true,
"cycle_out_in": true,
"dual_beacon": true,
"solid_reactive_simple": true,
"solid_reactive_multiwide": true,
"solid_reactive_multinexus": true,
"solid_splash": true,
"wave_left_right": true,
"wave_up_down": true
}
}
}

View File

@@ -0,0 +1,83 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include "keychron_common.h"
enum layers{
MAC_BASE,
WIN_BASE,
MAC_FN1,
WIN_FN1,
FN2,
};
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_70_iso(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_MUTE,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_DEL,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, KC_HOME,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, MO(MAC_FN1), MO(FN2), KC_SPC, KC_RCMMD, KC_LEFT, KC_DOWN, KC_RGHT),
[WIN_BASE] = LAYOUT_70_iso(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_MUTE,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_DEL,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NUHS, KC_ENT, KC_HOME,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LWIN, KC_LALT, KC_SPC, MO(WIN_FN1), MO(FN2), KC_SPC, KC_RALT, KC_LEFT, KC_DOWN, KC_RGHT),
[MAC_FN1] = LAYOUT_70_iso(
KC_GRV, KC_BRID, KC_BRIU, KC_MCTRL,KC_LNPAD, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, BL_TOGG,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, KC_INS,
BL_TOGG, BL_STEP, BL_UP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_END,
_______, _______, _______, BL_DOWN, _______, _______, _______, _______, NK_TOGG, _______, _______, _______, _______, _______, KC_PGUP,
_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______),
[WIN_FN1] = LAYOUT_70_iso(
KC_GRV, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, BL_TOGG,
_______, BT_HST1, BT_HST2, BT_HST3, P2P4G, _______, _______, _______, _______, _______, _______, _______, _______, KC_INS,
BL_TOGG, BL_STEP, BL_UP, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_END,
_______, _______, _______, BL_DOWN, _______, _______, _______, _______, NK_TOGG, _______, _______, _______, _______, _______, KC_PGUP,
_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______),
[FN2] = LAYOUT_70_iso(
KC_TILD, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, BAT_LVL, BAT_LVL, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______)
};
#if defined(ENCODER_MAP_ENABLE)
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
[MAC_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
[WIN_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
[MAC_FN1] = {ENCODER_CCW_CW(BL_DOWN, BL_UP)},
[WIN_FN1] = {ENCODER_CCW_CW(BL_DOWN, BL_UP)},
[FN2] = {ENCODER_CCW_CW(_______, _______)},
};
#endif // ENCODER_MAP_ENABLE
// clang-format on
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_record_keychron_common(keycode, record)) {
return false;
}
return true;
}

View File

@@ -0,0 +1 @@
# This file intentionally left blank

View File

@@ -0,0 +1,130 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
// clang-format off
#ifdef LED_MATRIX_ENABLE
const snled27351_led_t g_snled27351_leds[LED_MATRIX_LED_COUNT] = {
/* Refer to SNLED27351 manual for these locations
* driver
* | LED address
* | | */
{0, CB5_CA1},
{0, CB5_CA2},
{0, CB5_CA3},
{0, CB5_CA4},
{0, CB5_CA5},
{0, CB5_CA6},
{0, CB5_CA7},
{0, CB5_CA8},
{0, CB5_CA9},
{0, CB5_CA10},
{0, CB5_CA11},
{0, CB5_CA12},
{0, CB5_CA13},
{0, CB5_CA14},
{0, CB4_CA1},
{0, CB4_CA2},
{0, CB4_CA3},
{0, CB4_CA4},
{0, CB4_CA5},
{0, CB4_CA6},
{0, CB4_CA7},
{0, CB4_CA8},
{0, CB4_CA9},
{0, CB4_CA10},
{0, CB4_CA11},
{0, CB4_CA12},
{0, CB4_CA13},
{0, CB4_CA14},
{0, CB4_CA16},
{0, CB3_CA1},
{0, CB3_CA2},
{0, CB3_CA3},
{0, CB3_CA4},
{0, CB3_CA5},
{0, CB3_CA6},
{0, CB3_CA7},
{0, CB3_CA8},
{0, CB3_CA9},
{0, CB3_CA10},
{0, CB3_CA11},
{0, CB3_CA12},
{0, CB3_CA14},
{0, CB3_CA16},
{0, CB2_CA1},
{0, CB2_CA2},
{0, CB2_CA3},
{0, CB2_CA4},
{0, CB2_CA5},
{0, CB2_CA6},
{0, CB2_CA7},
{0, CB2_CA8},
{0, CB2_CA9},
{0, CB2_CA10},
{0, CB2_CA11},
{0, CB2_CA12},
{0, CB2_CA14},
{0, CB2_CA15},
{0, CB2_CA16},
{0, CB1_CA1},
{0, CB1_CA2},
{0, CB1_CA3},
{0, CB1_CA6},
{0, CB1_CA7},
{0, CB1_CA8},
{0, CB1_CA10},
{0, CB1_CA13},
{0, CB1_CA14},
{0, CB1_CA15},
{0, CB1_CA16},
};
#define __ NO_LED
led_config_t g_led_config = {
{
// Key Matrix to LED Index
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, __, __ },
{ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, __, 28 },
{ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, __, 41, __, 42 },
{ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, __, 55, 56, 57 },
{ 58, 59, 60, __, __, 61, 62, 63, __, 64, __, __, 65, 66, 67, 68 },
},
{
// LED Index to Physical Position
{8, 1}, {20, 1}, {33, 0}, {48, 3}, {61, 6}, {74, 8}, {87,11}, {106,11}, {119, 8}, {132, 6}, {145, 3}, {160, 0}, {173, 1}, {193, 1},
{8,14}, {24,14}, {39,14}, {52,17}, {65,20}, {78,22}, {103,25},{116,22}, {129,20}, {142,17}, {155,14}, {170,14}, {183,14}, {200,20}, {222,14},
{8,27}, {24,27}, {39,28}, {52,30}, {65,33}, {78,36}, {109,37},{122,34}, {135,32}, {148,29}, {162,27}, {176,27}, {190,27}, {224,27},
{8,40}, {13,40}, {28,40}, {43,42}, {56,44}, {69,47}, {82,50}, {102,52}, {115,49}, {128,46}, {141,44}, {154,44}, {169,40}, {189,40}, {209,43},
{0,53}, {16,53}, {42,55}, {65,60}, {86,64}, {107,64}, {131,59}, {156,54}, {196,56}, {209,56}, {222,56},
},
{
//LED Index to Flag
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
};
#endif

View File

@@ -0,0 +1,82 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "keychron_task.h"
#ifdef FACTORY_TEST_ENABLE
# include "factory_test.h"
# include "keychron_common.h"
#endif
#ifdef LK_WIRELESS_ENABLE
# include "lkbt51.h"
# include "wireless.h"
# include "transport.h"
# include "keychron_wireless_common.h"
# include "battery.h"
#endif
#define POWER_ON_LED_DURATION 3000
static uint32_t power_on_indicator_timer;
bool dip_switch_update_kb(uint8_t index, bool active) {
if (index == 0) {
default_layer_set(1UL << (active ? 0 : 1));
}
dip_switch_update_user(index, active);
return true;
}
void keyboard_post_init_kb(void) {
#ifdef LK_WIRELESS_ENABLE
palSetLineMode(P24G_MODE_SELECT_PIN, PAL_MODE_INPUT);
palSetLineMode(BT_MODE_SELECT_PIN, PAL_MODE_INPUT);
gpio_write_pin(BAT_LOW_LED_PIN, BAT_LOW_LED_PIN_ON_STATE);
lkbt51_init(false);
wireless_init();
#endif
power_on_indicator_timer = timer_read32();
#ifdef ENCODER_ENABLE
encoder_cb_init();
#endif
keyboard_post_init_user();
}
bool keychron_task_kb(void) {
if (power_on_indicator_timer) {
if (timer_elapsed32(power_on_indicator_timer) > POWER_ON_LED_DURATION) {
power_on_indicator_timer = 0;
#ifdef LK_WIRELESS_ENABLE
gpio_write_pin(BAT_LOW_LED_PIN, !BAT_LOW_LED_PIN_ON_STATE);
#endif
} else {
#ifdef LK_WIRELESS_ENABLE
gpio_write_pin(BAT_LOW_LED_PIN, BAT_LOW_LED_PIN_ON_STATE);
#endif
}
}
return true;
}
#ifdef LK_WIRELESS_ENABLE
bool lpm_is_kb_idle(void) {
return power_on_indicator_timer == 0 && !factory_reset_indicating();
}
#endif

View File

@@ -0,0 +1,37 @@
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include_next <mcuconf.h>
#undef STM32_HSECLK
#define STM32_HSECLK 16000000
#undef STM32_PLLM_VALUE
#define STM32_PLLM_VALUE 8
#undef STM32_PLLN_VALUE
#define STM32_PLLN_VALUE 96
#undef STM32_PLLP_VALUE
#define STM32_PLLP_VALUE 4
#undef STM32_PLLQ_VALUE
#define STM32_PLLQ_VALUE 4
#undef STM32_SPI_USE_SPI1
#define STM32_SPI_USE_SPI1 TRUE

View File

@@ -0,0 +1,23 @@
# Keychron K11 Max
![Keychron K11 Max](https://cdn.shopify.com/s/files/1/0059/0630/1017/files/K11-Max-page-5.jpg?v=1713336023)
A customizable 75% ergonomic keyboard.
* Keyboard Maintainer: [Keychron](https://github.com/keychron)
* Hardware Supported: Keychron K11 Max
* Hardware Availability: [Keychron K11 Max QMK/VIA Wireless Custom Mechanical Keyboard](https://www.keychron.com/products/keychron-k11-max-qmk-via-wireless-custom-mechanical-keyboard)
Make example for this keyboard (after setting up your build environment):
make keychron/k11_max/ansi_encoder/rgb:default
make keychron/k11_max/ansi_encoder/white:default
Flashing example for this keyboard:
make keychron/k11_max/ansi_encoder/rgb:default:flash
make keychron/k11_max/ansi_encoder/white:default:flash
**Reset Key**: Toggle mode switch to "Cable", hold down the *Esc* key or reset button underneath space bar while connecting the USB cable,
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).

View File

@@ -0,0 +1,4 @@
include keyboards/keychron/common/wireless/wireless.mk
include keyboards/keychron/common/keychron_common.mk
VPATH += $(TOP_DIR)/keyboards/keychron

View File

@@ -0,0 +1,350 @@
{
"name": "Keychron K11 Max ANSI RGB Knob",
"vendorId": "0x3434",
"productId": "0x0AB3",
"keycodes": ["qmk_lighting"],
"menus": [
{
"label": "Lighting",
"content": [
{
"label": "Backlight",
"content": [
{
"label": "Brightness",
"type": "range",
"options": [0, 255],
"content": ["id_qmk_rgb_matrix_brightness", 3, 1]
},
{
"label": "Effect",
"type": "dropdown",
"content": ["id_qmk_rgb_matrix_effect", 3, 2],
"options": [
["None", 0],
["Solid Color", 1],
["Breathing", 2],
["Band Spiral Val", 3],
["Cycle All", 4],
["Cycle Left Right", 5],
["Cycle Up Down", 6],
["Rainbow Moving Chevron", 7],
["Cycle Out In", 8],
["Cycle Out In Dual", 9],
["Cycle Pinwheel", 10],
["Cycle Spiral", 11],
["Dual Beacon", 12],
["Rainbow Beacon", 13],
["Jellybean Raindrops", 14],
["Pixel Rain", 15],
["Typing Heatmap", 16],
["Digital Rain", 17],
["Reactive Simple", 18],
["Reactive Multiwide", 19],
["Reactive Multinexus", 20],
["Splash", 21],
["Solid Splash", 22]
]
},
{
"showIf": "{id_qmk_rgb_matrix_effect} > 1",
"label": "Effect Speed",
"type": "range",
"options": [0, 255],
"content": ["id_qmk_rgb_matrix_effect_speed", 3, 3]
},
{
"showIf": "{id_qmk_rgb_matrix_effect} != 0 && ( {id_qmk_rgb_matrix_effect} < 4 || {id_qmk_rgb_matrix_effect} == 18 || ({id_qmk_rgb_matrix_effect} > 17 && {id_qmk_rgb_matrix_effect} != 21) ) ",
"label": "Color",
"type": "color",
"content": ["id_qmk_rgb_matrix_color", 3, 4]
}
]
}
]
}
],
"customKeycodes": [
{"name": "Left Option", "title": "Left Option", "shortName": "LOpt"},
{"name": "Right Option", "title": "Right Option", "shortName": "ROpt"},
{"name": "Left Cmd", "title": "Left Command", "shortName": "LCmd"},
{"name": "Right Cmd", "title": "Right Command", "shortName": "RCmd"},
{"name": "Misson Control", "title": "Misson Control in Mac", "shortName": "MCtl"},
{"name": "Lanuch Pad", "title": "Lanuch Pad in Windows", "shortName": "LPad"},
{"name": "Task View", "title": "Task View in Windows", "shortName": "Task"},
{"name": "File Explorer", "title": "File Explorer in Windows", "shortName": "File"},
{"name": "Screen shot", "title": "Screenshot in macOS", "shortName": "SShot"},
{"name": "Cortana", "title": "Cortana in Windows", "shortName": "Cortana"},
{"name": "Siri", "title": "Siri in macOS", "shortName": "Siri"},
{"name": "Bluetooth Host 1", "title": "Bluetooth Host 1", "shortName": "BTH1"},
{"name": "Bluetooth Host 2", "title": "Bluetooth Host 2", "shortName": "BTH2"},
{"name": "Bluetooth Host 3", "title": "Bluetooth Host 3", "shortName": "BTH3"},
{"name": "2.4G", "title": "2.4G", "shortName": "2.4G"},
{"name": "Battery Level", "title": "Show battery level", "shortName": "Batt"}
],
"matrix": {"rows": 5, "cols": 16},
"layouts": {
"keymap": [
[
{
"x": 2.75
},
"0,2",
{
"x": 8.85
},
"0,11"
],
[
{
"y": -0.85,
"x": 0.75,
"c": "#777777"
},
"0,0\nESC",
{
"c": "#cccccc"
},
"0,1"
],
[
{
"y": -0.85,
"x": 14.6,
"c": "#aaaaaa",
"w": 2
},
"0,13",
{
"x": 0.5
},
"0,15\n\n\n\n\n\n\n\n\ne0"
],
[
{
"y": -1,
"x": 13.6,
"c": "#cccccc"
},
"0,12"
],
[
{
"y": -0.15,
"x": 0.5,
"c": "#aaaaaa",
"w": 1.5
},
"1,0",
{
"c": "#cccccc"
},
"1,1"
],
[
{
"y": -0.85,
"x": 13.3
},
"1,11",
"1,12",
{
"c": "#aaaaaa",
"w": 1.5
},
"1,13",
{
"x": 0.5
},
"1,15"
],
[
{
"y": -0.15,
"x": 0.25,
"w": 1.75
},
"2,0",
{
"c": "#cccccc"
},
"2,1"
],
[
{
"y": -0.85,
"x": 12.75
},
"2,10",
"2,11",
{
"c": "#aaaaaa",
"w": 2.25
},
"2,13",
{
"x": 0.5
},
"2,15"
],
[
{
"y": -0.15,
"w": 2.25
},
"3,0",
{
"c": "#cccccc"
},
"3,2"
],
[
{
"y": -0.85,
"x": 13.4
},
"3,13",
{
"c": "#aaaaaa",
"w": 1.75
},
"3,14"
],
[
{
"y": -0.75,
"x": 16.4,
"c": "#cccccc"
},
"3,15"
],
[
{
"y": -0.4,
"c": "#aaaaaa",
"w": 1.25
},
"4,0",
{
"w": 1.25
},
"4,1"
],
[
{
"y": -0.6,
"x": 15.4,
"c": "#cccccc"
},
"4,13",
"4,14",
"4,15"
],
[
{
"r": 6,
"y": -5.7,
"x": 3.85,
"c": "#cccccc"
},
"0,3",
"0,4",
"0,5",
"0,6"
],
[
{
"x": 3.35
},
"1,2",
"1,3",
"1,4",
"1,5"
],
[
{
"x": 3.55
},
"2,2",
"2,3",
"2,4",
"2,5"
],
[
{
"x": 3.9
},
"3,3",
"3,4",
"3,5",
"3,6"
],
[
{
"x": 3.9,
"c": "#aaaaaa",
"w": 1.25
},
"4,2",
{
"w": 2.25
},
"4,5",
"4,6"
],
[
{
"r": -6,
"y": -3.3,
"x": 8.35,
"c": "#cccccc"
},
"0,7",
"0,8",
"0,9",
"0,10"
],
[
{
"x": 7.9
},
"1,6",
"1,7",
"1,8",
"1,9",
"1,10"
],
[
{
"x": 8.25
},
"2,6",
"2,7",
"2,8",
"2,9"
],
[
{
"x": 7.8
},
"3,7",
"3,8",
"3,9",
"3,10",
"3,11"
],
[
{
"x": 7.8,
"c": "#aaaaaa"
},
"4,7",
{
"w": 2.75
},
"4,9",
"4,12"
]
]
}
}

View File

@@ -0,0 +1,289 @@
{
"name": "Keychron K11 Max ANSI White Knob",
"vendorId": "0x3434",
"productId": "0x0AB9",
"keycodes": ["qmk_lighting"],
"customKeycodes": [
{"name": "Left Option", "title": "Left Option", "shortName": "LOpt"},
{"name": "Right Option", "title": "Right Option", "shortName": "ROpt"},
{"name": "Left Cmd", "title": "Left Command", "shortName": "LCmd"},
{"name": "Right Cmd", "title": "Right Command", "shortName": "RCmd"},
{"name": "Misson Control", "title": "Misson Control in Mac", "shortName": "MCtl"},
{"name": "Lanuch Pad", "title": "Lanuch Pad in Windows", "shortName": "LPad"},
{"name": "Task View", "title": "Task View in Windows", "shortName": "Task"},
{"name": "File Explorer", "title": "File Explorer in Windows", "shortName": "File"},
{"name": "Screen shot", "title": "Screenshot in macOS", "shortName": "SShot"},
{"name": "Cortana", "title": "Cortana in Windows", "shortName": "Cortana"},
{"name": "Siri", "title": "Siri in macOS", "shortName": "Siri"},
{"name": "Bluetooth Host 1", "title": "Bluetooth Host 1", "shortName": "BTH1"},
{"name": "Bluetooth Host 2", "title": "Bluetooth Host 2", "shortName": "BTH2"},
{"name": "Bluetooth Host 3", "title": "Bluetooth Host 3", "shortName": "BTH3"},
{"name": "2.4G", "title": "2.4G", "shortName": "2.4G"},
{"name": "Battery Level", "title": "Show battery level", "shortName": "Batt"}
],
"matrix": {"rows": 5, "cols": 16},
"layouts": {
"keymap": [
[
{
"x": 2.75
},
"0,2",
{
"x": 8.85
},
"0,11"
],
[
{
"y": -0.85,
"x": 0.75,
"c": "#777777"
},
"0,0\nESC",
{
"c": "#cccccc"
},
"0,1"
],
[
{
"y": -0.85,
"x": 14.6,
"c": "#aaaaaa",
"w": 2
},
"0,13",
{
"x": 0.5
},
"0,15\n\n\n\n\n\n\n\n\ne0"
],
[
{
"y": -1,
"x": 13.6,
"c": "#cccccc"
},
"0,12"
],
[
{
"y": -0.15,
"x": 0.5,
"c": "#aaaaaa",
"w": 1.5
},
"1,0",
{
"c": "#cccccc"
},
"1,1"
],
[
{
"y": -0.85,
"x": 13.3
},
"1,11",
"1,12",
{
"c": "#aaaaaa",
"w": 1.5
},
"1,13",
{
"x": 0.5
},
"1,15"
],
[
{
"y": -0.15,
"x": 0.25,
"w": 1.75
},
"2,0",
{
"c": "#cccccc"
},
"2,1"
],
[
{
"y": -0.85,
"x": 12.75
},
"2,10",
"2,11",
{
"c": "#aaaaaa",
"w": 2.25
},
"2,13",
{
"x": 0.5
},
"2,15"
],
[
{
"y": -0.15,
"w": 2.25
},
"3,0",
{
"c": "#cccccc"
},
"3,2"
],
[
{
"y": -0.85,
"x": 13.4
},
"3,13",
{
"c": "#aaaaaa",
"w": 1.75
},
"3,14"
],
[
{
"c": "#cccccc",
"y": -0.75,
"x": 16.4
},
"3,15"
],
[
{
"y": -0.4,
"c": "#aaaaaa",
"w": 1.25
},
"4,0",
{
"w": 1.25
},
"4,1"
],
[
{
"y": -0.6,
"x": 15.4,
"c": "#cccccc"
},
"4,13",
"4,14",
"4,15"
],
[
{
"r": 6,
"y": -5.7,
"x": 3.85,
"c": "#cccccc"
},
"0,3",
"0,4",
"0,5",
"0,6"
],
[
{
"x": 3.35
},
"1,2",
"1,3",
"1,4",
"1,5"
],
[
{
"x": 3.55
},
"2,2",
"2,3",
"2,4",
"2,5"
],
[
{
"x": 3.9
},
"3,3",
"3,4",
"3,5",
"3,6"
],
[
{
"x": 3.9,
"c": "#aaaaaa",
"w": 1.25
},
"4,2",
{
"w": 2.25
},
"4,5",
"4,6"
],
[
{
"r": -6,
"y": -3.3,
"x": 8.35,
"c": "#cccccc"
},
"0,7",
"0,8",
"0,9",
"0,10"
],
[
{
"x": 7.9
},
"1,6",
"1,7",
"1,8",
"1,9",
"1,10"
],
[
{
"x": 8.25
},
"2,6",
"2,7",
"2,8",
"2,9"
],
[
{
"x": 7.8
},
"3,7",
"3,8",
"3,9",
"3,10",
"3,11"
],
[
{
"x": 7.8,
"c": "#aaaaaa"
},
"4,7",
{
"w": 2.75
},
"4,9",
"4,12"
]
]
}
}

View File

@@ -0,0 +1,356 @@
{
"name": "Keychron K11 Max ISO RGB Knob",
"vendorId": "0x3434",
"productId": "0x0AB4",
"keycodes": ["qmk_lighting"],
"menus": [
{
"label": "Lighting",
"content": [
{
"label": "Backlight",
"content": [
{
"label": "Brightness",
"type": "range",
"options": [0, 255],
"content": ["id_qmk_rgb_matrix_brightness", 3, 1]
},
{
"label": "Effect",
"type": "dropdown",
"content": ["id_qmk_rgb_matrix_effect", 3, 2],
"options": [
["None", 0],
["Solid Color", 1],
["Breathing", 2],
["Band Spiral Val", 3],
["Cycle All", 4],
["Cycle Left Right", 5],
["Cycle Up Down", 6],
["Rainbow Moving Chevron", 7],
["Cycle Out In", 8],
["Cycle Out In Dual", 9],
["Cycle Pinwheel", 10],
["Cycle Spiral", 11],
["Dual Beacon", 12],
["Rainbow Beacon", 13],
["Jellybean Raindrops", 14],
["Pixel Rain", 15],
["Typing Heatmap", 16],
["Digital Rain", 17],
["Reactive Simple", 18],
["Reactive Multiwide", 19],
["Reactive Multinexus", 20],
["Splash", 21],
["Solid Splash", 22]
]
},
{
"showIf": "{id_qmk_rgb_matrix_effect} > 1",
"label": "Effect Speed",
"type": "range",
"options": [0, 255],
"content": ["id_qmk_rgb_matrix_effect_speed", 3, 3]
},
{
"showIf": "{id_qmk_rgb_matrix_effect} != 0 && ( {id_qmk_rgb_matrix_effect} < 4 || {id_qmk_rgb_matrix_effect} == 18 || ({id_qmk_rgb_matrix_effect} > 17 && {id_qmk_rgb_matrix_effect} != 21) ) ",
"label": "Color",
"type": "color",
"content": ["id_qmk_rgb_matrix_color", 3, 4]
}
]
}
]
}
],
"customKeycodes": [
{"name": "Left Option", "title": "Left Option", "shortName": "LOpt"},
{"name": "Right Option", "title": "Right Option", "shortName": "ROpt"},
{"name": "Left Cmd", "title": "Left Command", "shortName": "LCmd"},
{"name": "Right Cmd", "title": "Right Command", "shortName": "RCmd"},
{"name": "Misson Control", "title": "Misson Control in Mac", "shortName": "MCtl"},
{"name": "Lanuch Pad", "title": "Lanuch Pad in Windows", "shortName": "LPad"},
{"name": "Task View", "title": "Task View in Windows", "shortName": "Task"},
{"name": "File Explorer", "title": "File Explorer in Windows", "shortName": "File"},
{"name": "Screen shot", "title": "Screenshot in macOS", "shortName": "SShot"},
{"name": "Cortana", "title": "Cortana in Windows", "shortName": "Cortana"},
{"name": "Siri", "title": "Siri in macOS", "shortName": "Siri"},
{"name": "Bluetooth Host 1", "title": "Bluetooth Host 1", "shortName": "BTH1"},
{"name": "Bluetooth Host 2", "title": "Bluetooth Host 2", "shortName": "BTH2"},
{"name": "Bluetooth Host 3", "title": "Bluetooth Host 3", "shortName": "BTH3"},
{"name": "2.4G", "title": "2.4G", "shortName": "2.4G"},
{"name": "Battery Level", "title": "Show battery level", "shortName": "Batt"}
],
"matrix": {"rows": 5, "cols": 16},
"layouts": {
"keymap": [
[
{
"x": 2.75
},
"0,2",
{
"x": 8.85
},
"0,11"
],
[
{
"y": -0.85,
"x": 0.75,
"c": "#777777"
},
"0,0\nESC",
{
"c": "#cccccc"
},
"0,1"
],
[
{
"y": -0.85,
"x": 14.6,
"c": "#aaaaaa",
"w": 2
},
"0,13",
{
"x": 0.5
},
"0,15\n\n\n\n\n\n\n\n\ne0"
],
[
{
"y": -1,
"x": 13.6,
"c": "#cccccc"
},
"0,12"
],
[
{
"y": -0.15,
"x": 0.5,
"c": "#aaaaaa",
"w": 1.5
},
"1,0",
{
"c": "#cccccc"
},
"1,1"
],
[
{
"y": -0.85,
"x": 13.5
},
"1,11",
"1,12",
{
"x": 0.25,
"c": "#777777",
"w": 1.5,
"h": 2,
"w2": 1.75,
"h2": 1,
"x2": -0.25
},
"1,13",
{
"x": 0.25,
"c": "#aaaaaa"
},
"1,15"
],
[
{
"y": -0.15,
"x": 0.25,
"w": 1.75
},
"2,0",
{
"c": "#cccccc"
},
"2,1"
],
[
{
"y": -0.85,
"x": 12.75
},
"2,10",
"2,11",
{
"c": "#aaaaaa"
},
"2,13",
{
"x": 1.75
},
"2,15"
],
[
{
"y": -0.15,
"w": 1.25
},
"3,0",
{
"c": "#cccccc"
},
"3,1",
"3,2"
],
[
{
"y": -0.85,
"x": 13.4
},
"3,13",
{
"c": "#aaaaaa",
"w": 1.75
},
"3,14"
],
[
{
"y": -0.75,
"x": 16.4,
"c": "#cccccc"
},
"3,15"
],
[
{
"y": -0.4,
"c": "#aaaaaa",
"w": 1.25
},
"4,0",
{
"w": 1.25
},
"4,1"
],
[
{
"y": -0.6,
"x": 15.4,
"c": "#cccccc"
},
"4,13",
"4,14",
"4,15"
],
[
{
"r": 6,
"y": -5.7,
"x": 3.85,
"c": "#cccccc"
},
"0,3",
"0,4",
"0,5",
"0,6"
],
[
{
"x": 3.35
},
"1,2",
"1,3",
"1,4",
"1,5"
],
[
{
"x": 3.55
},
"2,2",
"2,3",
"2,4",
"2,5"
],
[
{
"x": 3.9
},
"3,3",
"3,4",
"3,5",
"3,6"
],
[
{
"x": 3.9,
"c": "#aaaaaa",
"w": 1.25
},
"4,2",
{
"w": 2.25
},
"4,5",
"4,6"
],
[
{
"r": -6,
"y": -3.3,
"x": 8.35,
"c": "#cccccc"
},
"0,7",
"0,8",
"0,9",
"0,10"
],
[
{
"x": 7.9
},
"1,6",
"1,7",
"1,8",
"1,9",
"1,10"
],
[
{
"x": 8.25
},
"2,6",
"2,7",
"2,8",
"2,9"
],
[
{
"x": 7.8
},
"3,7",
"3,8",
"3,9",
"3,10",
"3,11"
],
[
{
"x": 7.8,
"c": "#aaaaaa"
},
"4,7",
{
"w": 2.75
},
"4,9",
"4,12"
]
]
}
}

View File

@@ -0,0 +1,295 @@
{
"name": "Keychron K11 Max ISO White Knob",
"vendorId": "0x3434",
"productId": "0x0ABA",
"keycodes": ["qmk_lighting"],
"customKeycodes": [
{"name": "Left Option", "title": "Left Option", "shortName": "LOpt"},
{"name": "Right Option", "title": "Right Option", "shortName": "ROpt"},
{"name": "Left Cmd", "title": "Left Command", "shortName": "LCmd"},
{"name": "Right Cmd", "title": "Right Command", "shortName": "RCmd"},
{"name": "Misson Control", "title": "Misson Control in Mac", "shortName": "MCtl"},
{"name": "Lanuch Pad", "title": "Lanuch Pad in Windows", "shortName": "LPad"},
{"name": "Task View", "title": "Task View in Windows", "shortName": "Task"},
{"name": "File Explorer", "title": "File Explorer in Windows", "shortName": "File"},
{"name": "Screen shot", "title": "Screenshot in macOS", "shortName": "SShot"},
{"name": "Cortana", "title": "Cortana in Windows", "shortName": "Cortana"},
{"name": "Siri", "title": "Siri in macOS", "shortName": "Siri"},
{"name": "Bluetooth Host 1", "title": "Bluetooth Host 1", "shortName": "BTH1"},
{"name": "Bluetooth Host 2", "title": "Bluetooth Host 2", "shortName": "BTH2"},
{"name": "Bluetooth Host 3", "title": "Bluetooth Host 3", "shortName": "BTH3"},
{"name": "2.4G", "title": "2.4G", "shortName": "2.4G"},
{"name": "Battery Level", "title": "Show battery level", "shortName": "Batt"}
],
"matrix": {"rows": 5, "cols": 16},
"layouts": {
"keymap": [
[
{
"x": 2.75
},
"0,2",
{
"x": 8.85
},
"0,11"
],
[
{
"y": -0.85,
"x": 0.75,
"c": "#777777"
},
"0,0\nESC",
{
"c": "#cccccc"
},
"0,1"
],
[
{
"y": -0.85,
"x": 14.6,
"c": "#aaaaaa",
"w": 2
},
"0,13",
{
"x": 0.5
},
"0,15\n\n\n\n\n\n\n\n\ne0"
],
[
{
"y": -1,
"x": 13.6,
"c": "#cccccc"
},
"0,12"
],
[
{
"y": -0.15,
"x": 0.5,
"c": "#aaaaaa",
"w": 1.5
},
"1,0",
{
"c": "#cccccc"
},
"1,1"
],
[
{
"y": -0.85,
"x": 13.5
},
"1,11",
"1,12",
{
"x": 0.25,
"c": "#777777",
"w": 1.5,
"h": 2,
"w2": 1.75,
"h2": 1,
"x2": -0.25
},
"1,13",
{
"x": 0.25,
"c": "#aaaaaa"
},
"1,15"
],
[
{
"y": -0.15,
"x": 0.25,
"w": 1.75
},
"2,0",
{
"c": "#cccccc"
},
"2,1"
],
[
{
"y": -0.85,
"x": 12.75
},
"2,10",
"2,11",
{
"c": "#aaaaaa"
},
"2,13",
{
"x": 1.75
},
"2,15"
],
[
{
"y": -0.15,
"w": 1.25
},
"3,0",
{
"c": "#cccccc"
},
"3,1",
"3,2"
],
[
{
"y": -0.85,
"x": 13.4
},
"3,13",
{
"c": "#aaaaaa",
"w": 1.75
},
"3,14"
],
[
{
"y": -0.75,
"x": 16.4,
"c": "#cccccc"
},
"3,15"
],
[
{
"y": -0.4,
"c": "#aaaaaa",
"w": 1.25
},
"4,0",
{
"w": 1.25
},
"4,1"
],
[
{
"y": -0.6,
"x": 15.4,
"c": "#cccccc"
},
"4,13",
"4,14",
"4,15"
],
[
{
"r": 6,
"y": -5.7,
"x": 3.85,
"c": "#cccccc"
},
"0,3",
"0,4",
"0,5",
"0,6"
],
[
{
"x": 3.35
},
"1,2",
"1,3",
"1,4",
"1,5"
],
[
{
"x": 3.55
},
"2,2",
"2,3",
"2,4",
"2,5"
],
[
{
"x": 3.9
},
"3,3",
"3,4",
"3,5",
"3,6"
],
[
{
"x": 3.9,
"c": "#aaaaaa",
"w": 1.25
},
"4,2",
{
"w": 2.25
},
"4,5",
"4,6"
],
[
{
"r": -6,
"y": -3.3,
"x": 8.35,
"c": "#cccccc"
},
"0,7",
"0,8",
"0,9",
"0,10"
],
[
{
"x": 7.9
},
"1,6",
"1,7",
"1,8",
"1,9",
"1,10"
],
[
{
"x": 8.25
},
"2,6",
"2,7",
"2,8",
"2,9"
],
[
{
"x": 7.8
},
"3,7",
"3,8",
"3,9",
"3,10",
"3,11"
],
[
{
"x": 7.8,
"c": "#aaaaaa"
},
"4,7",
{
"w": 2.75
},
"4,9",
"4,12"
]
]
}
}

Some files were not shown because too many files have changed in this diff Show More