From cc9a612161b3d2731c65c1e8d3dda2044d387fcb Mon Sep 17 00:00:00 2001 From: lokher Date: Tue, 20 Aug 2024 09:50:47 +0800 Subject: [PATCH] Added Lemokey P1 ANSI --- builddefs/common_features.mk | 17 +- drivers/led/snled27351-simple-spi.c | 235 ++++++++++++ drivers/led/snled27351-simple-spi.h | 346 +++++++++++++++++ drivers/led/snled27351-spi.c | 250 ++++++++++++ drivers/led/snled27351-spi.h | 349 +++++++++++++++++ keyboards/lemokey/common/common.mk | 6 + keyboards/lemokey/common/factory_test.c | 355 ++++++++++++++++++ keyboards/lemokey/common/factory_test.h | 33 ++ keyboards/lemokey/common/lemokey_common.c | 113 ++++++ keyboards/lemokey/common/lemokey_common.h | 61 +++ keyboards/lemokey/common/lemokey_common.mk | 11 + keyboards/lemokey/common/lemokey_raw_hid.c | 91 +++++ keyboards/lemokey/common/lemokey_task.c | 119 ++++++ keyboards/lemokey/common/lemokey_task.h | 24 ++ keyboards/lemokey/common/matrix.c | 218 +++++++++++ .../lemokey/p1/ansi_encoder/ansi_encoder.c | 148 ++++++++ keyboards/lemokey/p1/ansi_encoder/config.h | 48 +++ keyboards/lemokey/p1/ansi_encoder/info.json | 99 +++++ .../p1/ansi_encoder/keymaps/default/keymap.c | 77 ++++ .../p1/ansi_encoder/keymaps/via/keymap.c | 77 ++++ .../p1/ansi_encoder/keymaps/via/rules.mk | 1 + keyboards/lemokey/p1/ansi_encoder/rules.mk | 1 + keyboards/lemokey/p1/config.h | 54 +++ keyboards/lemokey/p1/halconf.h | 24 ++ keyboards/lemokey/p1/info.json | 67 ++++ keyboards/lemokey/p1/mcuconf.h | 26 ++ keyboards/lemokey/p1/p1.c | 34 ++ keyboards/lemokey/p1/readme.md | 21 ++ keyboards/lemokey/p1/rules.mk | 3 + .../p1/via_json/p1_ansi_encoder_v1.0.json | 280 ++++++++++++++ quantum/led_matrix/led_matrix.h | 3 + quantum/led_matrix/led_matrix_drivers.c | 19 + quantum/rgb_matrix/rgb_matrix.h | 2 + quantum/rgb_matrix/rgb_matrix_drivers.c | 19 + 34 files changed, 3229 insertions(+), 2 deletions(-) create mode 100644 drivers/led/snled27351-simple-spi.c create mode 100644 drivers/led/snled27351-simple-spi.h create mode 100644 drivers/led/snled27351-spi.c create mode 100644 drivers/led/snled27351-spi.h create mode 100644 keyboards/lemokey/common/common.mk create mode 100644 keyboards/lemokey/common/factory_test.c create mode 100644 keyboards/lemokey/common/factory_test.h create mode 100644 keyboards/lemokey/common/lemokey_common.c create mode 100644 keyboards/lemokey/common/lemokey_common.h create mode 100644 keyboards/lemokey/common/lemokey_common.mk create mode 100644 keyboards/lemokey/common/lemokey_raw_hid.c create mode 100644 keyboards/lemokey/common/lemokey_task.c create mode 100644 keyboards/lemokey/common/lemokey_task.h create mode 100644 keyboards/lemokey/common/matrix.c create mode 100644 keyboards/lemokey/p1/ansi_encoder/ansi_encoder.c create mode 100644 keyboards/lemokey/p1/ansi_encoder/config.h create mode 100644 keyboards/lemokey/p1/ansi_encoder/info.json create mode 100644 keyboards/lemokey/p1/ansi_encoder/keymaps/default/keymap.c create mode 100644 keyboards/lemokey/p1/ansi_encoder/keymaps/via/keymap.c create mode 100644 keyboards/lemokey/p1/ansi_encoder/keymaps/via/rules.mk create mode 100644 keyboards/lemokey/p1/ansi_encoder/rules.mk create mode 100644 keyboards/lemokey/p1/config.h create mode 100644 keyboards/lemokey/p1/halconf.h create mode 100644 keyboards/lemokey/p1/info.json create mode 100644 keyboards/lemokey/p1/mcuconf.h create mode 100644 keyboards/lemokey/p1/p1.c create mode 100644 keyboards/lemokey/p1/readme.md create mode 100644 keyboards/lemokey/p1/rules.mk create mode 100644 keyboards/lemokey/p1/via_json/p1_ansi_encoder_v1.0.json diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index 094eda6fef..e7018ce21d 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -348,7 +348,7 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes) endif LED_MATRIX_ENABLE ?= no -VALID_LED_MATRIX_TYPES := is31fl3731 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 custom +VALID_LED_MATRIX_TYPES := is31fl3731 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 snled27351_spi custom # TODO: is31fl3733 is31fl3737 is31fl3741 ifeq ($(strip $(LED_MATRIX_ENABLE)), yes) @@ -412,11 +412,17 @@ endif QUANTUM_LIB_SRC += i2c_master.c endif + ifeq ($(strip $(RGB_MATRIX_DRIVER)), snled27351_spi) + OPT_DEFS += -DSNLED27351_SPI -DSTM32_SPI -DHAL_USE_SPI=TRUE + COMMON_VPATH += $(DRIVER_PATH)/led + SRC += snled27351-simple-spi.c + QUANTUM_LIB_SRC += spi_master.c + endif endif RGB_MATRIX_ENABLE ?= no -VALID_RGB_MATRIX_TYPES := aw20216 is31fl3731 is31fl3733 is31fl3736 is31fl3737 is31fl3741 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 ws2812 custom +VALID_RGB_MATRIX_TYPES := aw20216 is31fl3731 is31fl3733 is31fl3736 is31fl3737 is31fl3741 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 snled27351_spi ws2812 custom ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes) ifeq ($(filter $(RGB_MATRIX_DRIVER),$(VALID_RGB_MATRIX_TYPES)),) $(call CATASTROPHIC_ERROR,Invalid RGB_MATRIX_DRIVER,RGB_MATRIX_DRIVER="$(RGB_MATRIX_DRIVER)" is not a valid matrix type) @@ -514,6 +520,13 @@ endif QUANTUM_LIB_SRC += i2c_master.c endif + ifeq ($(strip $(RGB_MATRIX_DRIVER)), snled27351_spi) + OPT_DEFS += -DSNLED27351_SPI -DSTM32_SPI -DHAL_USE_SPI=TRUE + COMMON_VPATH += $(DRIVER_PATH)/led + SRC += snled27351-spi.c + QUANTUM_LIB_SRC += spi_master.c + endif + ifeq ($(strip $(RGB_MATRIX_DRIVER)), ws2812) OPT_DEFS += -DWS2812 WS2812_DRIVER_REQUIRED := yes diff --git a/drivers/led/snled27351-simple-spi.c b/drivers/led/snled27351-simple-spi.c new file mode 100644 index 0000000000..37097c8539 --- /dev/null +++ b/drivers/led/snled27351-simple-spi.c @@ -0,0 +1,235 @@ +/* Copyright 2021 @ 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 . + */ + +#include "snled27351-simple-spi.h" +#include "spi_master.h" + +#define SNLED27351_PWM_REGISTER_COUNT 192 +#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24 + + +#ifndef SNLED27351_PHASE_CHANNEL +# define SNLED27351_PHASE_CHANNEL MSKPHASE_12CHANNEL +#endif + +#ifndef SNLED27351_CURRENT_TUNE +# define SNLED27351_CURRENT_TUNE \ + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } +#endif + +#define SNLED27351_WRITE (0 << 7) +#define SNLED27351_READ (1 << 7) +#define SNLED27351_PATTERN (2 << 4) + +#ifdef DRIVER_CS_PINS +pin_t cs_pins[] = DRIVER_CS_PINS; +#else +error "no DRIVER_CS_PINS defined" +#endif + +// These buffers match the snled27351 PWM registers. +// The control buffers match the PG0 LED On/Off registers. +// Storing them like this is optimal for I2C transfers to the registers. +// We could optimize this and take out the unused registers from these +// buffers and the transfers in snled27351_write_pwm_buffer() but it's +// probably not worth the extra complexity. +uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][SNLED27351_PWM_REGISTER_COUNT]; +bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false}; + +uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][SNLED27351_LED_CONTROL_REGISTER_COUNT] = {0}; +bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false}; + + + +bool snled27351_write(uint8_t index, uint8_t page, uint8_t reg, uint8_t *data, uint8_t len) { + static uint8_t spi_transfer_buffer[2] = {0}; + + if (index > ARRAY_SIZE(((pin_t[])DRIVER_CS_PINS)) - 1) return false; + + if (!spi_start(cs_pins[index], false, 0, SNLED23751_SPI_DIVISOR)) { + spi_stop(); + return false; + } + + spi_transfer_buffer[0] = SNLED27351_WRITE | SNLED27351_PATTERN | (page & 0x0F); + spi_transfer_buffer[1] = reg; + + if (spi_transmit(spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) { + spi_stop(); + return false; + } + + if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) { + spi_stop(); + return false; + } + + spi_stop(); + return true; +} + +bool snled27351_write_register(uint8_t index, uint8_t page, uint8_t reg, uint8_t data) { + return snled27351_write(index, page, reg, &data, 1); +} + +bool snled27351_write_pwm_buffer(uint8_t index, uint8_t *pwm_buffer) { + if (g_pwm_buffer_update_required[index]) { + snled27351_write(index, LED_PWM_PAGE, 0, g_pwm_buffer[index], SNLED27351_PWM_REGISTER_COUNT); + } + g_pwm_buffer_update_required[index] = false; + return true; +} + +void snled27351_init_drivers(void) { +#if defined(LED_DRIVER_SHUTDOWN_PIN) + setPinOutput(LED_DRIVER_SHUTDOWN_PIN); + writePinHigh(LED_DRIVER_SHUTDOWN_PIN); +#endif + + spi_init(); + + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_init(i); + + for (int index = 0; index < SNLED27351_LED_COUNT; index++) { + snled27351_set_led_control_register(index, true); + } + + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_update_led_control_registers(i); +} + +void snled27351_init(uint8_t index) { + setPinOutput(cs_pins[index]); + writePinHigh(cs_pins[index]); + // Setting LED driver to shutdown mode + snled27351_write_register(index, FUNCTION_PAGE, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE); + // Setting internal channel pulldown/pullup + snled27351_write_register(index, FUNCTION_PAGE, PDU_REG, MSKSET_CA_CB_CHANNEL); + // Select number of scan phase + snled27351_write_register(index, FUNCTION_PAGE, SCAN_PHASE_REG, SNLED27351_PHASE_CHANNEL); + // Setting PWM Delay Phase + snled27351_write_register(index, FUNCTION_PAGE, SLEW_RATE_CONTROL_MODE1_REG, MSKPWM_DELAY_PHASE_ENABLE); + // Setting Driving/Sinking Channel Slew Rate + snled27351_write_register(index, FUNCTION_PAGE, SLEW_RATE_CONTROL_MODE2_REG, MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE); + // Setting Iref + snled27351_write_register(index, FUNCTION_PAGE, SOFTWARE_SLEEP_REG, MSKSLEEP_DISABLE); + + // Set LED CONTROL PAGE (Page 0) + uint8_t on_off_reg[LED_CONTROL_ON_OFF_LENGTH] = {0}; + snled27351_write(index, LED_CONTROL_PAGE, 0, on_off_reg, LED_CONTROL_ON_OFF_LENGTH); + + // Set PWM PAGE (Page 1) + uint8_t pwm_reg[LED_PWM_LENGTH]; + memset(pwm_reg, 0, LED_PWM_LENGTH); + snled27351_write(index, LED_PWM_PAGE, 0, pwm_reg, LED_PWM_LENGTH); + + // Set CURRENT PAGE (Page 4) + uint8_t current_tune_reg[LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE; + snled27351_write(index, CURRENT_TUNE_PAGE, 0, current_tune_reg, LED_CURRENT_TUNE_LENGTH); + + // // Enable LEDs ON/OFF + // memset(on_off_reg, 0xFF, LED_CONTROL_ON_OFF_LENGTH); + // snled27351_write(index, LED_CONTROL_PAGE, 0, on_off_reg, LED_CONTROL_ON_OFF_LENGTH); + + // Setting LED driver to normal mode + snled27351_write_register(index, FUNCTION_PAGE, CONFIGURATION_REG, MSKSW_NORMAL_MODE); +} + +void snled27351_set_value(int index, uint8_t value) { + snled27351_led_t led; + if (index >= 0 && index < SNLED27351_LED_COUNT) { + memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); + + g_pwm_buffer[led.driver][led.v] = value; + g_pwm_buffer_update_required[led.driver] = true; + } +} + +void snled27351_set_value_all(uint8_t value) { + for (int i = 0; i < SNLED27351_LED_COUNT; i++) { + snled27351_set_value(i, value); + } +} + +void snled27351_set_led_control_register(uint8_t index, bool value) { + snled27351_led_t led; + memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); + + uint8_t control_register = led.v / 8; + uint8_t bit_value = led.v % 8; + + if (value) { + g_led_control_registers[led.driver][control_register] |= (1 << bit_value); + } else { + g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value); + } + + g_led_control_registers_update_required[led.driver] = true; +} + +void snled27351_update_pwm_buffers(uint8_t index) { + if (g_pwm_buffer_update_required[index]) { + if (!snled27351_write_pwm_buffer(index, g_pwm_buffer[index])) { + g_led_control_registers_update_required[index] = true; + } + } + g_pwm_buffer_update_required[index] = false; +} + +void snled27351_update_led_control_registers(uint8_t index) { + if (g_led_control_registers_update_required[index]) { + snled27351_write(index, LED_CONTROL_PAGE, 0, g_led_control_registers[index], 24); + } + g_led_control_registers_update_required[index] = false; +} + +void snled27351_flush(void) { + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_update_pwm_buffers(i); +} + +void snled27351_shutdown(void) { +# if defined(LED_DRIVER_SHUTDOWN_PIN) + writePinLow(LED_DRIVER_SHUTDOWN_PIN); +# else + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_sw_shutdown(i); +# endif +} + +void snled27351_exit_shutdown(void) { +# if defined(LED_DRIVER_SHUTDOWN_PIN) + writePinHigh(LED_DRIVER_SHUTDOWN_PIN); +# else + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_sw_return_normal(i); +# endif +} + +void snled27351_sw_return_normal(uint8_t index) { + // Select to function page + // Setting LED driver to normal mode + snled27351_write_register(index, FUNCTION_PAGE, CONFIGURATION_REG, MSKSW_NORMAL_MODE); +} + +void snled27351_sw_shutdown(uint8_t index) { + // Select to function page + // Setting LED driver to shutdown mode + snled27351_write_register(index, FUNCTION_PAGE, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE); + // Write SW Sleep Register + snled27351_write_register(index, FUNCTION_PAGE, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE); +} diff --git a/drivers/led/snled27351-simple-spi.h b/drivers/led/snled27351-simple-spi.h new file mode 100644 index 0000000000..465b63ca6d --- /dev/null +++ b/drivers/led/snled27351-simple-spi.h @@ -0,0 +1,346 @@ +/* Copyright 2021 @ 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 . + */ + +#pragma once + +#include +#include +#include "progmem.h" +#include "util.h" + +#if defined(LED_MATRIX_SNLED27351_SPI) +# define SNLED27351_LED_COUNT LED_MATRIX_LED_COUNT +#endif + +#define SNLED27351_DRIVER_COUNT (sizeof(cs_pins) / sizeof(pin_t)) +typedef struct snled27351_led_t { + uint8_t driver : 2; + uint8_t v; +} PACKED snled27351_led_t; + +extern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT]; + +void snled27351_init_drivers(void); +void snled27351_init(uint8_t index); +bool snled27351_write_register(uint8_t index, uint8_t page, uint8_t reg, uint8_t data); +bool snled27351_write_pwm_buffer(uint8_t index, uint8_t *pwm_buffer); + +void snled27351_set_value(int index, uint8_t value); +void snled27351_set_value_all(uint8_t value); + +void snled27351_set_led_control_register(uint8_t index, bool value); + +// This should not be called from an interrupt +// (eg. from a timer interrupt). +// Call this while idle (in between matrix scans). +// If the buffer is dirty, it will update the driver with the buffer. +void snled27351_update_pwm_buffers(uint8_t index); +void snled27351_update_led_control_registers(uint8_t index); +void snled27351_flush(void); +void snled27351_shutdown(void); +void snled27351_exit_shutdown(void); +void snled27351_sw_return_normal(uint8_t index); +void snled27351_sw_shutdown(uint8_t index); + +// Registers Page Define +#define CONFIGURE_CMD_PAGE 0xFD +#define LED_CONTROL_PAGE 0x00 +#define LED_PWM_PAGE 0x01 +#define FUNCTION_PAGE 0x03 +#define CURRENT_TUNE_PAGE 0x04 + +// Function Register: address 0x00 +#define CONFIGURATION_REG 0x00 +#define MSKSW_SHUT_DOWN_MODE (0x0 << 0) +#define MSKSW_NORMAL_MODE (0x1 << 0) + +#define DRIVER_ID_REG 0x11 +#define SNLED27351_ID 0x8A + +#define PDU_REG 0x13 +#define MSKSET_CA_CB_CHANNEL 0xAA +#define MSKCLR_CA_CB_CHANNEL 0x00 + +#define SCAN_PHASE_REG 0x14 +#define MSKPHASE_12CHANNEL 0x00 +#define MSKPHASE_11CHANNEL 0x01 +#define MSKPHASE_10CHANNEL 0x02 +#define MSKPHASE_9CHANNEL 0x03 +#define MSKPHASE_8CHANNEL 0x04 +#define MSKPHASE_7CHANNEL 0x05 +#define MSKPHASE_6CHANNEL 0x06 +#define MSKPHASE_5CHANNEL 0x07 +#define MSKPHASE_4CHANNEL 0x08 +#define MSKPHASE_3CHANNEL 0x09 +#define MSKPHASE_2CHANNEL 0x0A +#define MSKPHASE_1CHANNEL 0x0B + +#define SLEW_RATE_CONTROL_MODE1_REG 0x15 +#define MSKPWM_DELAY_PHASE_ENABLE 0x04 +#define MSKPWM_DELAY_PHASE_DISABLE 0x00 + +#define SLEW_RATE_CONTROL_MODE2_REG 0x16 +#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE 0xC0 +#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_DISABLE 0x00 + +#define OPEN_SHORT_ENABLE_REG 0x17 +#define MSKOPEN_DETECTION_ENABLE (0x01 << 7) +#define MSKOPEN_DETECTION_DISABLE (0x00) + +#define MSKSHORT_DETECTION_ENABLE (0x01 << 6) +#define MSKSHORT_DETECTION_DISABLE (0x00) + +#define OPEN_SHORT_DUTY_REG 0x18 +#define OPEN_SHORT_FLAG_REG 0x19 + +#define MSKOPEN_DETECTION_INTERRUPT_ENABLE (0x01 << 7) +#define MSKOPEN_DETECTION_INTERRUPT_DISABLE (0x00) + +#define MSKSHORT_DETECTION_INTERRUPT_ENABLE (0x01 << 6) +#define MSKSHORT_DETECTION_INTERRUPT_DISABLE (0x00) + +#define SOFTWARE_SLEEP_REG 0x1A +#define MSKSLEEP_ENABLE 0x02 +#define MSKSLEEP_DISABLE 0x00 + +// LED Control Registers +#define LED_CONTROL_ON_OFF_FIRST_ADDR 0x0 +#define LED_CONTROL_ON_OFF_LAST_ADDR 0x17 +#define LED_CONTROL_ON_OFF_LENGTH ((LED_CONTROL_ON_OFF_LAST_ADDR - LED_CONTROL_ON_OFF_FIRST_ADDR) + 1) + +#define LED_CONTROL_OPEN_FIRST_ADDR 0x18 +#define LED_CONTROL_OPEN_LAST_ADDR 0x2F +#define LED_CONTROL_OPEN_LENGTH ((LED_CONTROL_OPEN_LAST_ADDR - LED_CONTROL_OPEN_FIRST_ADDR) + 1) + +#define LED_CONTROL_SHORT_FIRST_ADDR 0x30 +#define LED_CONTROL_SHORT_LAST_ADDR 0x47 +#define LED_CONTROL_SHORT_LENGTH ((LED_CONTROL_SHORT_LAST_ADDR - LED_CONTROL_SHORT_FIRST_ADDR) + 1) + +#define LED_CONTROL_PAGE_LENGTH 0x48 + +// LED Control Registers +#define LED_PWM_FIRST_ADDR 0x00 +#define LED_PWM_LAST_ADDR 0xBF +#define LED_PWM_LENGTH 0xC0 + +// Current Tune Registers +#define LED_CURRENT_TUNE_FIRST_ADDR 0x00 +#define LED_CURRENT_TUNE_LAST_ADDR 0x0B +#define LED_CURRENT_TUNE_LENGTH 0x0C + +#define A_1 0x00 +#define A_2 0x01 +#define A_3 0x02 +#define A_4 0x03 +#define A_5 0x04 +#define A_6 0x05 +#define A_7 0x06 +#define A_8 0x07 +#define A_9 0x08 +#define A_10 0x09 +#define A_11 0x0A +#define A_12 0x0B +#define A_13 0x0C +#define A_14 0x0D +#define A_15 0x0E +#define A_16 0x0F + +#define B_1 0x10 +#define B_2 0x11 +#define B_3 0x12 +#define B_4 0x13 +#define B_5 0x14 +#define B_6 0x15 +#define B_7 0x16 +#define B_8 0x17 +#define B_9 0x18 +#define B_10 0x19 +#define B_11 0x1A +#define B_12 0x1B +#define B_13 0x1C +#define B_14 0x1D +#define B_15 0x1E +#define B_16 0x1F + +#define C_1 0x20 +#define C_2 0x21 +#define C_3 0x22 +#define C_4 0x23 +#define C_5 0x24 +#define C_6 0x25 +#define C_7 0x26 +#define C_8 0x27 +#define C_9 0x28 +#define C_10 0x29 +#define C_11 0x2A +#define C_12 0x2B +#define C_13 0x2C +#define C_14 0x2D +#define C_15 0x2E +#define C_16 0x2F + +#define D_1 0x30 +#define D_2 0x31 +#define D_3 0x32 +#define D_4 0x33 +#define D_5 0x34 +#define D_6 0x35 +#define D_7 0x36 +#define D_8 0x37 +#define D_9 0x38 +#define D_10 0x39 +#define D_11 0x3A +#define D_12 0x3B +#define D_13 0x3C +#define D_14 0x3D +#define D_15 0x3E +#define D_16 0x3F + +#define E_1 0x40 +#define E_2 0x41 +#define E_3 0x42 +#define E_4 0x43 +#define E_5 0x44 +#define E_6 0x45 +#define E_7 0x46 +#define E_8 0x47 +#define E_9 0x48 +#define E_10 0x49 +#define E_11 0x4A +#define E_12 0x4B +#define E_13 0x4C +#define E_14 0x4D +#define E_15 0x4E +#define E_16 0x4F + +#define F_1 0x50 +#define F_2 0x51 +#define F_3 0x52 +#define F_4 0x53 +#define F_5 0x54 +#define F_6 0x55 +#define F_7 0x56 +#define F_8 0x57 +#define F_9 0x58 +#define F_10 0x59 +#define F_11 0x5A +#define F_12 0x5B +#define F_13 0x5C +#define F_14 0x5D +#define F_15 0x5E +#define F_16 0x5F + +#define G_1 0x60 +#define G_2 0x61 +#define G_3 0x62 +#define G_4 0x63 +#define G_5 0x64 +#define G_6 0x65 +#define G_7 0x66 +#define G_8 0x67 +#define G_9 0x68 +#define G_10 0x69 +#define G_11 0x6A +#define G_12 0x6B +#define G_13 0x6C +#define G_14 0x6D +#define G_15 0x6E +#define G_16 0x6F + +#define H_1 0x70 +#define H_2 0x71 +#define H_3 0x72 +#define H_4 0x73 +#define H_5 0x74 +#define H_6 0x75 +#define H_7 0x76 +#define H_8 0x77 +#define H_9 0x78 +#define H_10 0x79 +#define H_11 0x7A +#define H_12 0x7B +#define H_13 0x7C +#define H_14 0x7D +#define H_15 0x7E +#define H_16 0x7F + +#define I_1 0x80 +#define I_2 0x81 +#define I_3 0x82 +#define I_4 0x83 +#define I_5 0x84 +#define I_6 0x85 +#define I_7 0x86 +#define I_8 0x87 +#define I_9 0x88 +#define I_10 0x89 +#define I_11 0x8A +#define I_12 0x8B +#define I_13 0x8C +#define I_14 0x8D +#define I_15 0x8E +#define I_16 0x8F + +#define J_1 0x90 +#define J_2 0x91 +#define J_3 0x92 +#define J_4 0x93 +#define J_5 0x94 +#define J_6 0x95 +#define J_7 0x96 +#define J_8 0x97 +#define J_9 0x98 +#define J_10 0x99 +#define J_11 0x9A +#define J_12 0x9B +#define J_13 0x9C +#define J_14 0x9D +#define J_15 0x9E +#define J_16 0x9F + +#define K_1 0xA0 +#define K_2 0xA1 +#define K_3 0xA2 +#define K_4 0xA3 +#define K_5 0xA4 +#define K_6 0xA5 +#define K_7 0xA6 +#define K_8 0xA7 +#define K_9 0xA8 +#define K_10 0xA9 +#define K_11 0xAA +#define K_12 0xAB +#define K_13 0xAC +#define K_14 0xAD +#define K_15 0xAE +#define K_16 0xAF + +#define L_1 0xB0 +#define L_2 0xB1 +#define L_3 0xB2 +#define L_4 0xB3 +#define L_5 0xB4 +#define L_6 0xB5 +#define L_7 0xB6 +#define L_8 0xB7 +#define L_9 0xB8 +#define L_10 0xB9 +#define L_11 0xBA +#define L_12 0xBB +#define L_13 0xBC +#define L_14 0xBD +#define L_15 0xBE +#define L_16 0xBF diff --git a/drivers/led/snled27351-spi.c b/drivers/led/snled27351-spi.c new file mode 100644 index 0000000000..a6d810a6c2 --- /dev/null +++ b/drivers/led/snled27351-spi.c @@ -0,0 +1,250 @@ +/* Copyright 2021 @ 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 . + */ + +#include "snled27351-spi.h" +#include "spi_master.h" + +#define SNLED27351_PWM_REGISTER_COUNT 192 +#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24 + +#ifndef SNLED27351_PHASE_CHANNEL +# define SNLED27351_PHASE_CHANNEL MSKPHASE_12CHANNEL +#endif + +#ifndef SNLED27351_CURRENT_TUNE +# define SNLED27351_CURRENT_TUNE \ + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } +#endif + +#define SNLED27351_WRITE (0 << 7) +#define SNLED27351_READ (1 << 7) +#define SNLED27351_PATTERN (2 << 4) + +#ifdef DRIVER_CS_PINS +pin_t cs_pins[] = DRIVER_CS_PINS; +#else +error "no DRIVER_CS_PINS defined" +#endif + +// These buffers match the snled27351 PWM registers. +// The control buffers match the PG0 LED On/Off registers. +// Storing them like this is optimal for I2C transfers to the registers. +// We could optimize this and take out the unused registers from these +// buffers and the transfers in snled27351_write_pwm_buffer() but it's +// probably not worth the extra complexity. +uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][SNLED27351_PWM_REGISTER_COUNT]; +bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false}; + +uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][SNLED27351_LED_CONTROL_REGISTER_COUNT] = {0}; +bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false}; + + + +bool snled27351_write(uint8_t index, uint8_t page, uint8_t reg, uint8_t *data, uint8_t len) { + static uint8_t spi_transfer_buffer[2] = {0}; + + if (index > ARRAY_SIZE(((pin_t[])DRIVER_CS_PINS)) - 1) return false; + + if (!spi_start(cs_pins[index], false, 0, SNLED27351_SPI_DIVISOR)) { + spi_stop(); + return false; + } + + spi_transfer_buffer[0] = SNLED27351_WRITE | SNLED27351_PATTERN | (page & 0x0F); + spi_transfer_buffer[1] = reg; + + if (spi_transmit(spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) { + spi_stop(); + return false; + } + + if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) { + spi_stop(); + return false; + } + + spi_stop(); + return true; +} + +bool snled27351_write_register(uint8_t index, uint8_t page, uint8_t reg, uint8_t data) { + return snled27351_write(index, page, reg, &data, 1); +} + +bool snled27351_write_pwm_buffer(uint8_t index, uint8_t *pwm_buffer) { + if (g_pwm_buffer_update_required[index]) { + snled27351_write(index, LED_PWM_PAGE, 0, g_pwm_buffer[index], SNLED27351_PWM_REGISTER_COUNT); + } + g_pwm_buffer_update_required[index] = false; + return true; +} + +void snled27351_init_drivers(void) { +#if defined(LED_DRIVER_SHUTDOWN_PIN) + setPinOutput(LED_DRIVER_SHUTDOWN_PIN); + writePinHigh(LED_DRIVER_SHUTDOWN_PIN); +#endif + + spi_init(); + + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_init(i); + + for (int index = 0; index < SNLED27351_LED_COUNT; index++) { + snled27351_set_led_control_register(index, true, true, true); + } + + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_update_led_control_registers(i); +} + +void snled27351_init(uint8_t index) { + setPinOutput(cs_pins[index]); + writePinHigh(cs_pins[index]); + // Setting LED driver to shutdown mode + snled27351_write_register(index, FUNCTION_PAGE, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE); + // Setting internal channel pulldown/pullup + snled27351_write_register(index, FUNCTION_PAGE, PDU_REG, MSKSET_CA_CB_CHANNEL); + // Select number of scan phase + snled27351_write_register(index, FUNCTION_PAGE, SCAN_PHASE_REG, SNLED27351_PHASE_CHANNEL); + // Setting PWM Delay Phase + snled27351_write_register(index, FUNCTION_PAGE, SLEW_RATE_CONTROL_MODE1_REG, MSKPWM_DELAY_PHASE_ENABLE); + // Setting Driving/Sinking Channel Slew Rate + snled27351_write_register(index, FUNCTION_PAGE, SLEW_RATE_CONTROL_MODE2_REG, MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE); + // Setting Iref + snled27351_write_register(index, FUNCTION_PAGE, SOFTWARE_SLEEP_REG, MSKSLEEP_DISABLE); + + // Set LED CONTROL PAGE (Page 0) + uint8_t on_off_reg[LED_CONTROL_ON_OFF_LENGTH] = {0}; + snled27351_write(index, LED_CONTROL_PAGE, 0, on_off_reg, LED_CONTROL_ON_OFF_LENGTH); + + // Set PWM PAGE (Page 1) + uint8_t pwm_reg[LED_PWM_LENGTH]; + memset(pwm_reg, 0, LED_PWM_LENGTH); + snled27351_write(index, LED_PWM_PAGE, 0, pwm_reg, LED_PWM_LENGTH); + + // Set CURRENT PAGE (Page 4) + uint8_t current_tune_reg[LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE; + snled27351_write(index, CURRENT_TUNE_PAGE, 0, current_tune_reg, LED_CURRENT_TUNE_LENGTH); + + // // Enable LEDs ON/OFF + // memset(on_off_reg, 0xFF, LED_CONTROL_ON_OFF_LENGTH); + // snled27351_write(index, LED_CONTROL_PAGE, 0, on_off_reg, LED_CONTROL_ON_OFF_LENGTH); + + // Setting LED driver to normal mode + snled27351_write_register(index, FUNCTION_PAGE, CONFIGURATION_REG, MSKSW_NORMAL_MODE); +} + +void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { + snled27351_led_t led; + if (index >= 0 && index < SNLED27351_LED_COUNT) { + memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); + + g_pwm_buffer[led.driver][led.r] = red; + g_pwm_buffer[led.driver][led.g] = green; + g_pwm_buffer[led.driver][led.b] = blue; + g_pwm_buffer_update_required[led.driver] = true; + } +} + +void snled27351_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { + for (int i = 0; i < SNLED27351_LED_COUNT; i++) { + snled27351_set_color(i, red, green, blue); + } +} + +void snled27351_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { + snled27351_led_t led; + memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led)); + + uint8_t control_register_r = led.r / 8; + uint8_t control_register_g = led.g / 8; + uint8_t control_register_b = led.b / 8; + uint8_t bit_r = led.r % 8; + uint8_t bit_g = led.g % 8; + uint8_t bit_b = led.b % 8; + + if (red) { + g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r); + } else { + g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r); + } + if (green) { + g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g); + } else { + g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g); + } + if (blue) { + g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b); + } else { + g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); + } + + g_led_control_registers_update_required[led.driver] = true; +} + +void snled27351_update_pwm_buffers(uint8_t index) { + if (g_pwm_buffer_update_required[index]) { + if (!snled27351_write_pwm_buffer(index, g_pwm_buffer[index])) { + g_led_control_registers_update_required[index] = true; + } + } + g_pwm_buffer_update_required[index] = false; +} + +void snled27351_update_led_control_registers(uint8_t index) { + if (g_led_control_registers_update_required[index]) { + snled27351_write(index, LED_CONTROL_PAGE, 0, g_led_control_registers[index], 24); + } + g_led_control_registers_update_required[index] = false; +} + +void snled27351_flush(void) { + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_update_pwm_buffers(i); +} + +void snled27351_shutdown(void) { +# if defined(LED_DRIVER_SHUTDOWN_PIN) + writePinLow(LED_DRIVER_SHUTDOWN_PIN); +# else + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_sw_shutdown(i); +# endif +} + +void snled27351_exit_shutdown(void) { +# if defined(LED_DRIVER_SHUTDOWN_PIN) + writePinHigh(LED_DRIVER_SHUTDOWN_PIN); +# else + for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) + snled27351_sw_return_normal(i); +# endif +} + +void snled27351_sw_return_normal(uint8_t index) { + // Select to function page + // Setting LED driver to normal mode + snled27351_write_register(index, FUNCTION_PAGE, CONFIGURATION_REG, MSKSW_NORMAL_MODE); +} + +void snled27351_sw_shutdown(uint8_t index) { + // Select to function page + // Setting LED driver to shutdown mode + snled27351_write_register(index, FUNCTION_PAGE, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE); + // Write SW Sleep Register + snled27351_write_register(index, FUNCTION_PAGE, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE); +} diff --git a/drivers/led/snled27351-spi.h b/drivers/led/snled27351-spi.h new file mode 100644 index 0000000000..92cad6e52d --- /dev/null +++ b/drivers/led/snled27351-spi.h @@ -0,0 +1,349 @@ +/* Copyright 2021 @ 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 . + */ + +#pragma once + +#include +#include +#include "progmem.h" +#include "util.h" + +#if defined(SNLED27351_SPI) +# define SNLED27351_LED_COUNT RGB_MATRIX_LED_COUNT +#endif + +# define SNLED27351_DRIVER_COUNT (sizeof(cs_pins)/sizeof(pin_t)) + +typedef struct snled27351_led_t { + uint8_t driver : 2; + uint8_t r; + uint8_t g; + uint8_t b; +} __attribute__((packed)) snled27351_led_t; + +extern const snled27351_led_t g_snled27351_leds[SNLED27351_LED_COUNT]; + +void snled27351_init_drivers(void); +void snled27351_init(uint8_t index); +bool snled27351_write_register(uint8_t index, uint8_t page, uint8_t reg, uint8_t data); +bool snled27351_write_pwm_buffer(uint8_t index, uint8_t *pwm_buffer); + +void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); +void snled27351_set_color_all(uint8_t red, uint8_t green, uint8_t blue); + +void snled27351_set_led_control_register(uint8_t index, bool red, bool green, bool blue); + +// This should not be called from an interrupt +// (eg. from a timer interrupt). +// Call this while idle (in between matrix scans). +// If the buffer is dirty, it will update the driver with the buffer. +void snled27351_update_pwm_buffers(uint8_t index); +void snled27351_update_led_control_registers(uint8_t index); +void snled27351_flush(void); +void snled27351_shutdown(void); +void snled27351_exit_shutdown(void); +void snled27351_sw_return_normal(uint8_t index); +void snled27351_sw_shutdown(uint8_t index); + +// Registers Page Define +#define CONFIGURE_CMD_PAGE 0xFD +#define LED_CONTROL_PAGE 0x00 +#define LED_PWM_PAGE 0x01 +#define FUNCTION_PAGE 0x03 +#define CURRENT_TUNE_PAGE 0x04 + +// Function Register: address 0x00 +#define CONFIGURATION_REG 0x00 +#define MSKSW_SHUT_DOWN_MODE (0x0 << 0) +#define MSKSW_NORMAL_MODE (0x1 << 0) + +#define DRIVER_ID_REG 0x11 +#define SNLED27351_ID 0x8A + +#define PDU_REG 0x13 +#define MSKSET_CA_CB_CHANNEL 0xAA +#define MSKCLR_CA_CB_CHANNEL 0x00 + +#define SCAN_PHASE_REG 0x14 +#define MSKPHASE_12CHANNEL 0x00 +#define MSKPHASE_11CHANNEL 0x01 +#define MSKPHASE_10CHANNEL 0x02 +#define MSKPHASE_9CHANNEL 0x03 +#define MSKPHASE_8CHANNEL 0x04 +#define MSKPHASE_7CHANNEL 0x05 +#define MSKPHASE_6CHANNEL 0x06 +#define MSKPHASE_5CHANNEL 0x07 +#define MSKPHASE_4CHANNEL 0x08 +#define MSKPHASE_3CHANNEL 0x09 +#define MSKPHASE_2CHANNEL 0x0A +#define MSKPHASE_1CHANNEL 0x0B + +#define SLEW_RATE_CONTROL_MODE1_REG 0x15 +#define MSKPWM_DELAY_PHASE_ENABLE 0x04 +#define MSKPWM_DELAY_PHASE_DISABLE 0x00 + +#define SLEW_RATE_CONTROL_MODE2_REG 0x16 +#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE 0xC0 +#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_DISABLE 0x00 + +#define OPEN_SHORT_ENABLE_REG 0x17 +#define MSKOPEN_DETECTION_ENABLE (0x01 << 7) +#define MSKOPEN_DETECTION_DISABLE (0x00) + +#define MSKSHORT_DETECTION_ENABLE (0x01 << 6) +#define MSKSHORT_DETECTION_DISABLE (0x00) + +#define OPEN_SHORT_DUTY_REG 0x18 +#define OPEN_SHORT_FLAG_REG 0x19 + +#define MSKOPEN_DETECTION_INTERRUPT_ENABLE (0x01 << 7) +#define MSKOPEN_DETECTION_INTERRUPT_DISABLE (0x00) + +#define MSKSHORT_DETECTION_INTERRUPT_ENABLE (0x01 << 6) +#define MSKSHORT_DETECTION_INTERRUPT_DISABLE (0x00) + +#define SOFTWARE_SLEEP_REG 0x1A +#define MSKSLEEP_ENABLE 0x02 +#define MSKSLEEP_DISABLE 0x00 + +// LED Control Registers +#define LED_CONTROL_ON_OFF_FIRST_ADDR 0x0 +#define LED_CONTROL_ON_OFF_LAST_ADDR 0x17 +#define LED_CONTROL_ON_OFF_LENGTH ((LED_CONTROL_ON_OFF_LAST_ADDR - LED_CONTROL_ON_OFF_FIRST_ADDR) + 1) + +#define LED_CONTROL_OPEN_FIRST_ADDR 0x18 +#define LED_CONTROL_OPEN_LAST_ADDR 0x2F +#define LED_CONTROL_OPEN_LENGTH ((LED_CONTROL_OPEN_LAST_ADDR - LED_CONTROL_OPEN_FIRST_ADDR) + 1) + +#define LED_CONTROL_SHORT_FIRST_ADDR 0x30 +#define LED_CONTROL_SHORT_LAST_ADDR 0x47 +#define LED_CONTROL_SHORT_LENGTH ((LED_CONTROL_SHORT_LAST_ADDR - LED_CONTROL_SHORT_FIRST_ADDR) + 1) + +#define LED_CONTROL_PAGE_LENGTH 0x48 + +// LED Control Registers +#define LED_PWM_FIRST_ADDR 0x00 +#define LED_PWM_LAST_ADDR 0xBF +#define LED_PWM_LENGTH 0xC0 + +// Current Tune Registers +#define LED_CURRENT_TUNE_FIRST_ADDR 0x00 +#define LED_CURRENT_TUNE_LAST_ADDR 0x0B +#define LED_CURRENT_TUNE_LENGTH 0x0C + +#define A_1 0x00 +#define A_2 0x01 +#define A_3 0x02 +#define A_4 0x03 +#define A_5 0x04 +#define A_6 0x05 +#define A_7 0x06 +#define A_8 0x07 +#define A_9 0x08 +#define A_10 0x09 +#define A_11 0x0A +#define A_12 0x0B +#define A_13 0x0C +#define A_14 0x0D +#define A_15 0x0E +#define A_16 0x0F + +#define B_1 0x10 +#define B_2 0x11 +#define B_3 0x12 +#define B_4 0x13 +#define B_5 0x14 +#define B_6 0x15 +#define B_7 0x16 +#define B_8 0x17 +#define B_9 0x18 +#define B_10 0x19 +#define B_11 0x1A +#define B_12 0x1B +#define B_13 0x1C +#define B_14 0x1D +#define B_15 0x1E +#define B_16 0x1F + +#define C_1 0x20 +#define C_2 0x21 +#define C_3 0x22 +#define C_4 0x23 +#define C_5 0x24 +#define C_6 0x25 +#define C_7 0x26 +#define C_8 0x27 +#define C_9 0x28 +#define C_10 0x29 +#define C_11 0x2A +#define C_12 0x2B +#define C_13 0x2C +#define C_14 0x2D +#define C_15 0x2E +#define C_16 0x2F + +#define D_1 0x30 +#define D_2 0x31 +#define D_3 0x32 +#define D_4 0x33 +#define D_5 0x34 +#define D_6 0x35 +#define D_7 0x36 +#define D_8 0x37 +#define D_9 0x38 +#define D_10 0x39 +#define D_11 0x3A +#define D_12 0x3B +#define D_13 0x3C +#define D_14 0x3D +#define D_15 0x3E +#define D_16 0x3F + +#define E_1 0x40 +#define E_2 0x41 +#define E_3 0x42 +#define E_4 0x43 +#define E_5 0x44 +#define E_6 0x45 +#define E_7 0x46 +#define E_8 0x47 +#define E_9 0x48 +#define E_10 0x49 +#define E_11 0x4A +#define E_12 0x4B +#define E_13 0x4C +#define E_14 0x4D +#define E_15 0x4E +#define E_16 0x4F + +#define F_1 0x50 +#define F_2 0x51 +#define F_3 0x52 +#define F_4 0x53 +#define F_5 0x54 +#define F_6 0x55 +#define F_7 0x56 +#define F_8 0x57 +#define F_9 0x58 +#define F_10 0x59 +#define F_11 0x5A +#define F_12 0x5B +#define F_13 0x5C +#define F_14 0x5D +#define F_15 0x5E +#define F_16 0x5F + +#define G_1 0x60 +#define G_2 0x61 +#define G_3 0x62 +#define G_4 0x63 +#define G_5 0x64 +#define G_6 0x65 +#define G_7 0x66 +#define G_8 0x67 +#define G_9 0x68 +#define G_10 0x69 +#define G_11 0x6A +#define G_12 0x6B +#define G_13 0x6C +#define G_14 0x6D +#define G_15 0x6E +#define G_16 0x6F + +#define H_1 0x70 +#define H_2 0x71 +#define H_3 0x72 +#define H_4 0x73 +#define H_5 0x74 +#define H_6 0x75 +#define H_7 0x76 +#define H_8 0x77 +#define H_9 0x78 +#define H_10 0x79 +#define H_11 0x7A +#define H_12 0x7B +#define H_13 0x7C +#define H_14 0x7D +#define H_15 0x7E +#define H_16 0x7F + +#define I_1 0x80 +#define I_2 0x81 +#define I_3 0x82 +#define I_4 0x83 +#define I_5 0x84 +#define I_6 0x85 +#define I_7 0x86 +#define I_8 0x87 +#define I_9 0x88 +#define I_10 0x89 +#define I_11 0x8A +#define I_12 0x8B +#define I_13 0x8C +#define I_14 0x8D +#define I_15 0x8E +#define I_16 0x8F + +#define J_1 0x90 +#define J_2 0x91 +#define J_3 0x92 +#define J_4 0x93 +#define J_5 0x94 +#define J_6 0x95 +#define J_7 0x96 +#define J_8 0x97 +#define J_9 0x98 +#define J_10 0x99 +#define J_11 0x9A +#define J_12 0x9B +#define J_13 0x9C +#define J_14 0x9D +#define J_15 0x9E +#define J_16 0x9F + +#define K_1 0xA0 +#define K_2 0xA1 +#define K_3 0xA2 +#define K_4 0xA3 +#define K_5 0xA4 +#define K_6 0xA5 +#define K_7 0xA6 +#define K_8 0xA7 +#define K_9 0xA8 +#define K_10 0xA9 +#define K_11 0xAA +#define K_12 0xAB +#define K_13 0xAC +#define K_14 0xAD +#define K_15 0xAE +#define K_16 0xAF + +#define L_1 0xB0 +#define L_2 0xB1 +#define L_3 0xB2 +#define L_4 0xB3 +#define L_5 0xB4 +#define L_6 0xB5 +#define L_7 0xB6 +#define L_8 0xB7 +#define L_9 0xB8 +#define L_10 0xB9 +#define L_11 0xBA +#define L_12 0xBB +#define L_13 0xBC +#define L_14 0xBD +#define L_15 0xBE +#define L_16 0xBF diff --git a/keyboards/lemokey/common/common.mk b/keyboards/lemokey/common/common.mk new file mode 100644 index 0000000000..28aed642b9 --- /dev/null +++ b/keyboards/lemokey/common/common.mk @@ -0,0 +1,6 @@ +COMMON_DIR = common +SRC += \ + $(COMMON_DIR)/lemokey_common.c \ + $(COMMON_DIR)/factory_test.c + +VPATH += $(TOP_DIR)/keyboards/lemokey/$(COMMON_DIR) diff --git a/keyboards/lemokey/common/factory_test.c b/keyboards/lemokey/common/factory_test.c new file mode 100644 index 0000000000..293308efd6 --- /dev/null +++ b/keyboards/lemokey/common/factory_test.c @@ -0,0 +1,355 @@ +/* Copyright 2024 @ 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 . + */ + +#include "quantum.h" +#include "raw_hid.h" +#include "via.h" +#include "lemokey_task.h" +#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 + +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, +}; + +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 + } 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 repleased 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; + + uint8_t payload[32]; + uint8_t len = 0; + + 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]; + break; + + case FACTORY_TEST_CMD_JUMP_TO_BL: + break; + + 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; + + case FACTORY_TEST_CMD_GET_DEVICE_ID: + payload[len++] = FACTORY_TEST_CMD_GET_DEVICE_ID; + payload[len++] = 12; + 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; + } + } +} diff --git a/keyboards/lemokey/common/factory_test.h b/keyboards/lemokey/common/factory_test.h new file mode 100644 index 0000000000..953ee10e34 --- /dev/null +++ b/keyboards/lemokey/common/factory_test.h @@ -0,0 +1,33 @@ +/* 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 . + */ + +#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) || defined(Lightless_Projects) +bool factory_test_indicator(void); +#endif + +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); + diff --git a/keyboards/lemokey/common/lemokey_common.c b/keyboards/lemokey/common/lemokey_common.c new file mode 100644 index 0000000000..370571dd02 --- /dev/null +++ b/keyboards/lemokey/common/lemokey_common.c @@ -0,0 +1,113 @@ +/* Copyright 2024 @ 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 . + */ + +#include QMK_KEYBOARD_H +#include "lemokey_common.h" +#include "raw_hid.h" +#include "version.h" // for QMK_BUILDDATE used in EEPROM magic + +#ifdef FACTORY_TEST_ENABLE +# include "factory_test.h" +# include "lemokey_common.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 LOCK_SCREEN_KEY_ENABLE + {2, {KC_LWIN, KC_L}}, + {3, {KC_LCTL, KC_LCMD, KC_Q}}, +#endif +}; +// clang-format on + +bool process_record_lemokey_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 LOCK_SCREEN_KEY_ENABLE + case KC_WLCK: + 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 lemokey_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; + } +} diff --git a/keyboards/lemokey/common/lemokey_common.h b/keyboards/lemokey/common/lemokey_common.h new file mode 100644 index 0000000000..de0ba4284a --- /dev/null +++ b/keyboards/lemokey/common/lemokey_common.h @@ -0,0 +1,61 @@ +/* Copyright 2024 @ 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 . + */ + +#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_SIRI, + KC_TASK_VIEW, + KC_FILE_EXPLORER, + KC_SCREEN_SHOT, + KC_CORTANA, +#ifdef LOCK_SCREEN_KEY_ENABLE + KC_WIN_LOCK_SCREEN, + KC_MAC_LOCK_SCREEN, +#endif + NEW_SAFE_RANGE, +}; + +#define KC_TASK KC_TASK_VIEW +#define KC_FILE KC_FILE_EXPLORER +#define KC_SNAP KC_SCREEN_SHOT +#define KC_CTANA KC_CORTANA + +#ifdef LOCK_SCREEN_KEY_ENABLE +#define KC_WLCK KC_WIN_LOCK_SCREEN +#define KC_MLCK KC_MAC_LOCK_SCREEN +#endif + +typedef struct PACKED { + uint8_t len; + uint8_t keycode[3]; +} key_combination_t; + +bool process_record_lemokey_common(uint16_t keycode, keyrecord_t *record); +void lemokey_common_task(void); + +#ifdef ENCODER_ENABLE +void encoder_cb_init(void); +#endif diff --git a/keyboards/lemokey/common/lemokey_common.mk b/keyboards/lemokey/common/lemokey_common.mk new file mode 100644 index 0000000000..ee9200494c --- /dev/null +++ b/keyboards/lemokey/common/lemokey_common.mk @@ -0,0 +1,11 @@ +OPT_DEFS += -DFACTORY_TEST_ENABLE + +LEMOKEY_COMMON_DIR = common +SRC += \ + $(LEMOKEY_COMMON_DIR)/lemokey_task.c \ + $(LEMOKEY_COMMON_DIR)/lemokey_common.c \ + $(LEMOKEY_COMMON_DIR)/lemokey_raw_hid.c \ + $(LEMOKEY_COMMON_DIR)/factory_test.c + +VPATH += $(TOP_DIR)/keyboards/lemokey/$(LEMOKEY_COMMON_DIR) + diff --git a/keyboards/lemokey/common/lemokey_raw_hid.c b/keyboards/lemokey/common/lemokey_raw_hid.c new file mode 100644 index 0000000000..62492df5fb --- /dev/null +++ b/keyboards/lemokey/common/lemokey_raw_hid.c @@ -0,0 +1,91 @@ +/* Copyright 2024 @ 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 . + */ + +#include QMK_KEYBOARD_H +#include "lemokey_common.h" +#include "raw_hid.h" +#include "version.h" +#ifdef FACTORY_TEST_ENABLE +# include "factory_test.h" +#endif + +#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, +}; + +void get_support_feature(uint8_t *data) { + data[1] = FEATURE_DEFAULT_LAYER; +} + +bool lemokey_raw_hid_rx(uint8_t *data, uint8_t length) { + switch (data[0]) { + case KC_GET_PROTOCOL_VERSION: + data[1] = PROTOCOL_VERSION; + 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); + } break; + + case KC_GET_SUPPORT_FEATURE: + get_support_feature(&data[1]); + break; + + case KC_GET_DEFAULT_LAYER: + data[1] = get_highest_layer(default_layer_state); + break; + +#ifdef FACTORY_TEST_ENABLE + case 0xAB: + factory_test_rx(data, length); + return true; +#endif + default: + return false; + } + + raw_hid_send(data, length); + return true; +} + +#if defined(VIA_ENABLE) +bool via_command_kb(uint8_t *data, uint8_t length) { + return lemokey_raw_hid_rx(data, length); +} +#else +void raw_hid_receive(uint8_t *data, uint8_t length) { + lemokey_raw_hid_rx(data, length); +} +#endif diff --git a/keyboards/lemokey/common/lemokey_task.c b/keyboards/lemokey/common/lemokey_task.c new file mode 100644 index 0000000000..4793bba8b9 --- /dev/null +++ b/keyboards/lemokey/common/lemokey_task.c @@ -0,0 +1,119 @@ +/* Copyright 2024 @ 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 . + */ + +#include +#include "lemokey_task.h" +#include "quantum.h" +#include "lemokey_common.h" +#ifdef FACTORY_TEST_ENABLE +#include "factory_test.h" +#endif + +__attribute__((weak)) bool process_record_lemokey_kb(uint16_t keycode, keyrecord_t *record) { return true; } + +bool process_record_lemokey(uint16_t keycode, keyrecord_t *record) { + +#ifdef FACTORY_TEST_ENABLE + if (!process_record_factory_test(keycode, record)) + return false; +#endif + + if (!process_record_lemokey_kb(keycode, record)) + return false; + + return true; +} + +#if defined(LED_MATRIX_ENABLE) +bool led_matrix_indicators_lemokey(void) { +#ifdef FACTORY_TEST_ENABLE + factory_test_indicator(); +#endif + return true; +} +#endif + +#if defined(RGB_MATRIX_ENABLE) +bool rgb_matrix_indicators_lemokey(void) { +# if defined(CAPS_LOCK_INDEX) + if (host_keyboard_led_state().caps_lock) { +# if defined(DIM_CAPS_LOCK) + rgb_matrix_set_color(CAPS_LOCK_INDEX, 0, 0, 0); +# else + rgb_matrix_set_color(CAPS_LOCK_INDEX, 255, 255, 255);, +# endif + } +# endif +# if defined(NUM_LOCK_INDEX) + if (host_keyboard_led_state().num_lock) { + rgb_matrix_set_color(NUM_LOCK_INDEX, 255, 255, 255); + } +# endif + +#ifdef FACTORY_TEST_ENABLE + factory_test_indicator(); +#endif + return true; +} +#endif + +__attribute__((weak)) bool lemokey_task_kb(void){ return true; } + +void lemokey_task(void) { +#ifdef FACTORY_TEST_ENABLE + factory_test_task(); +#endif + lemokey_common_task(); + + lemokey_task_kb(); +} + +bool process_record_kb(uint16_t keycode, keyrecord_t *record) { + if (!process_record_user(keycode, record)) + return false; + + if (!process_record_lemokey(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_lemokey(); + + return true; +} +#endif + +#ifdef LED_MATRIX_ENABLE +bool led_matrix_indicators_kb(void) { + if (!led_matrix_indicators_user()) + return false; + + led_matrix_indicators_lemokey(); + + return true; +} +#endif + +void housekeeping_task_kb(void) { + lemokey_task(); +} + diff --git a/keyboards/lemokey/common/lemokey_task.h b/keyboards/lemokey/common/lemokey_task.h new file mode 100644 index 0000000000..06e49843dc --- /dev/null +++ b/keyboards/lemokey/common/lemokey_task.h @@ -0,0 +1,24 @@ +/* Copyright 2024 @ Keychron (https://www.lemokey.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 . + */ + +#pragma once + +#include "stdint.h" +#include "action.h" + +bool lemokey_task_kb(void); +void lemokey_task(void); + diff --git a/keyboards/lemokey/common/matrix.c b/keyboards/lemokey/common/matrix.c new file mode 100644 index 0000000000..f58b0c8665 --- /dev/null +++ b/keyboards/lemokey/common/matrix.c @@ -0,0 +1,218 @@ +/* Copyright 2023 ~ 2024 @ 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 . + */ + +#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 readPin(pin); + } else { + return 1; + } +} + +static inline void setPinOutput_writeLow(pin_t pin) { + setPinOutput(pin); + writePinLow(pin); +} + +static inline void setPinOutput_writeHigh(pin_t pin) { + setPinOutput(pin); + writePinHigh(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) { + writePinHigh(HC595_DS); + } else { + writePinLow(HC595_DS); + } + writePinHigh(HC595_SHCP); + HC595_delay(n); + writePinLow(HC595_SHCP); + HC595_delay(n); + if (bit_flag) { + break; + } else { + data = data >> 1; + } + } + writePinHigh(HC595_STCP); + HC595_delay(n); + writePinLow(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 + setPinInputHigh(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 + setPinInputHigh(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) { + setPinOutput(HC595_DS); + setPinOutput(HC595_STCP); + setPinOutput(HC595_SHCP); + + for (uint8_t x = 0; x < MATRIX_ROWS; x++) { + if (row_pins[x] != NO_PIN) { + setPinInputHigh(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(); +} diff --git a/keyboards/lemokey/p1/ansi_encoder/ansi_encoder.c b/keyboards/lemokey/p1/ansi_encoder/ansi_encoder.c new file mode 100644 index 0000000000..9e89e38ff5 --- /dev/null +++ b/keyboards/lemokey/p1/ansi_encoder/ansi_encoder.c @@ -0,0 +1,148 @@ +/* Copyright 2024 @ 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, A_15, C_15, B_15}, + {0, A_14, C_14, B_14}, + {0, A_13, C_13, B_13}, + {0, A_12, C_12, B_12}, + {0, A_11, C_11, B_11}, + {0, A_10, C_10, B_10}, + {0, A_9, C_9, B_9}, + {0, A_8, C_8, B_8}, + {0, A_7, C_7, B_7}, + {0, A_6, C_6, B_6}, + {0, A_5, C_5, B_5}, + {0, A_4, C_4, B_4}, + {0, A_3, C_3, B_3}, + {0, A_2, C_2, B_2}, + + {0, G_15, I_15, H_15}, + {0, G_14, I_14, H_14}, + {0, G_13, I_13, H_13}, + {0, G_12, I_12, H_12}, + {0, G_11, I_11, H_11}, + {0, G_10, I_10, H_10}, + {0, G_9, I_9, H_9}, + {0, G_8, I_8, H_8}, + {0, G_7, I_7, H_7}, + {0, G_6, I_6, H_6}, + {0, G_5, I_5, H_5}, + {0, G_4, I_4, H_4}, + {0, G_3, I_3, H_3}, + {0, G_2, I_2, H_2}, + {0, G_1, I_1, H_1}, + + {0, D_15, F_15, E_15}, + {0, D_14, F_14, E_14}, + {0, D_13, F_13, E_13}, + {0, D_12, F_12, E_12}, + {0, D_11, F_11, E_11}, + {0, D_10, F_10, E_10}, + {0, D_9, F_9, E_9}, + {0, D_8, F_8, E_8}, + {0, D_7, F_7, E_7}, + {0, D_6, F_6, E_6}, + {0, D_5, F_5, E_5}, + {0, D_4, F_4, E_4}, + {0, D_3, F_3, E_3}, + {0, D_2, F_2, E_2}, + {0, D_1, F_1, E_1}, + + {1, A_15, C_15, B_15}, + {1, A_14, C_14, B_14}, + {1, A_13, C_13, B_13}, + {1, A_12, C_12, B_12}, + {1, A_11, C_11, B_11}, + {1, A_10, C_10, B_10}, + {1, A_9, C_9, B_9}, + {1, A_8, C_8, B_8}, + {1, A_7, C_7, B_7}, + {1, A_6, C_6, B_6}, + {1, A_5, C_5, B_5}, + {1, A_4, C_4, B_4}, + {1, A_2, C_2, B_2}, + {1, A_1, C_1, B_1}, + + {1, G_15, I_15, H_15}, + {1, G_13, I_13, H_13}, + {1, G_12, I_12, H_12}, + {1, G_11, I_11, H_11}, + {1, G_10, I_10, H_10}, + {1, G_9, I_9, H_9}, + {1, G_8, I_8, H_8}, + {1, G_7, I_7, H_7}, + {1, G_6, I_6, H_6}, + {1, G_5, I_5, H_5}, + {1, G_4, I_4, H_4}, + {1, G_3, I_3, H_3}, + {1, G_2, I_2, H_2}, + + {1, D_15, F_15, E_15}, + {1, D_14, F_14, E_14}, + {1, D_13, F_13, E_13}, + {1, D_9, F_9, E_9}, + {1, D_6, F_6, E_6}, + {1, D_5, F_5, E_5}, + {1, D_4, F_4, E_4}, + {1, D_3, F_3, E_3}, + {1, D_2, F_2, E_2}, + {1, D_1, F_1, E_1}, +}; + +#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 }, + }, + { + // LED Index to Physical Position + {0, 0}, {18, 0}, {33, 0}, {48, 0}, {62, 0}, {81, 0}, {95, 0}, {110, 0}, {125, 0}, {143, 0}, {158, 0}, {178, 0}, {187, 0}, {203, 0}, + {0,15}, {15,15}, {29,15}, {44,15}, {59,15}, {73,15}, {88,15}, {103,15}, {117,15}, {132,15}, {146,15}, {161,15}, {176,15}, {195,15}, {224,15}, + {4,26}, {22,26}, {37,26}, {51,26}, {66,26}, {81,26}, {95,26}, {110,26}, {125,26}, {139,26}, {154,26}, {173,26}, {187,26}, {201,26}, {224,26}, + {6,38}, {26,38}, {40,38}, {55,38}, {70,38}, {84,38}, {99,38}, {114,38}, {128,38}, {143,38}, {158,38}, {172,38}, {195,38}, {224,38}, + {8,49}, {33,49}, {48,49}, {62,49}, {77,49}, {92,49}, {106,49}, {121,49}, {136,49}, {150,49}, {165,49}, {186,49}, {203,52}, + {2,61}, {20,61}, {38,61}, {94,61}, {147,61}, {161,61}, {178,61}, {192,64}, {203,64}, {224,64}, + }, + { + // 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, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + } +}; +#endif diff --git a/keyboards/lemokey/p1/ansi_encoder/config.h b/keyboards/lemokey/p1/ansi_encoder/config.h new file mode 100644 index 0000000000..f9492a72d9 --- /dev/null +++ b/keyboards/lemokey/p1/ansi_encoder/config.h @@ -0,0 +1,48 @@ +/* Copyright 2024 @ 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 . + */ + +#pragma once + +#ifdef RGB_MATRIX_ENABLE +/* RGB Matrix driver configuration */ +# define DRIVER_COUNT 2 +# define RGB_MATRIX_LED_COUNT 81 + +# define SPI_SCK_PIN A5 +# define SPI_MISO_PIN A6 +# define SPI_MOSI_PIN A7 + +# define DRIVER_CS_PINS \ + { B15, C6 } +# define SNLED27351_SPI_DIVISOR 16 +# define SPI_DRIVER SPIDQ + +/* Scan phase of led driver set as MSKPHASE_9CHANNEL(defined as 0x03 in SNLED27351.h) */ +# define PHASE_CHANNEL MSKPHASE_9CHANNEL + +/* Set LED driver current */ +# define SNLED27351_CURRENT_TUNE \ + { 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C } + +/* Caps lock indicating led */ +# define CAPS_LOCK_INDEX 44 +# define DIM_CAPS_LOCK + +/* win lock indicating led */ +# define WIN_LOCK_LED_PIN C0 +# define WIN_LOCK_INDEX 72 +# define DIM_WIN_LOCK +#endif diff --git a/keyboards/lemokey/p1/ansi_encoder/info.json b/keyboards/lemokey/p1/ansi_encoder/info.json new file mode 100644 index 0000000000..ec5e3df8f2 --- /dev/null +++ b/keyboards/lemokey/p1/ansi_encoder/info.json @@ -0,0 +1,99 @@ +{ + "usb": { + "pid": "0x0310", + "device_version": "1.0.0" + }, + "layouts": { + "LAYOUT_ansi_82": { + "layout": [ + {"matrix": [0, 0], "x": 0, "y": 0}, + {"matrix": [0, 1], "x": 1.25, "y": 0}, + {"matrix": [0, 2], "x": 2.25, "y": 0}, + {"matrix": [0, 3], "x": 3.25, "y": 0}, + {"matrix": [0, 4], "x": 4.25, "y": 0}, + {"matrix": [0, 5], "x": 5.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.75, "y": 0}, + {"matrix": [0, 10], "x": 10.75, "y": 0}, + {"matrix": [0, 11], "x": 11.75, "y": 0}, + {"matrix": [0, 12], "x": 12.75, "y": 0}, + {"matrix": [0, 13], "x": 14, "y": 0}, + {"matrix": [0, 14], "x": 15.25, "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": [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": [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, 13], "x": 12.75, "y": 3.25, "w": 2.25}, + {"matrix": [3, 14], "x": 15.25, "y": 3.25}, + + {"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, 12], "x": 12.25, "y": 4.25, "w": 1.75}, + {"matrix": [4, 13], "x": 14.25, "y": 4.5}, + + {"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, 9], "x": 10, "y": 5.25}, + {"matrix": [5, 10], "x": 11, "y": 5.25}, + {"matrix": [5, 11], "x": 12, "y": 5.25}, + {"matrix": [5, 12], "x": 13.25, "y": 5.5}, + {"matrix": [5, 13], "x": 14.25, "y": 5.5}, + {"matrix": [5, 14], "x": 15.25, "y": 5.5} + ] + } + } +} diff --git a/keyboards/lemokey/p1/ansi_encoder/keymaps/default/keymap.c b/keyboards/lemokey/p1/ansi_encoder/keymaps/default/keymap.c new file mode 100644 index 0000000000..5c4cb93228 --- /dev/null +++ b/keyboards/lemokey/p1/ansi_encoder/keymaps/default/keymap.c @@ -0,0 +1,77 @@ +/* Copyright 3 @ 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 . + */ + +#include QMK_KEYBOARD_H +#include "lemokey_common.h" + +// clang-format off +enum layers{ + MAC_BASE, + MAC_FN, + WIN_BASE, + WIN_FN +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + [MAC_BASE] = LAYOUT_ansi_82( + KC_ESC, KC_BRID, KC_BRIU, KC_MCTL, KC_LPAD, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_DEL, KC_MUTE, + 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_PGUP, + 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_PGDN, + 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_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, + KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, KC_LCMMD,MO(MAC_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT), + + [MAC_FN] = LAYOUT_ansi_82( + KC_TRNS, 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_TRNS, RGB_TOG, + KC_TRNS, BT_HST1, BT_HST2, BT_HST3, P2P4G, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_END, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, BAT_LVL, NK_TOGG, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS), + + [WIN_BASE] = LAYOUT_ansi_82( + 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_DEL, KC_MUTE, + 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_PGUP, + 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_PGDN, + 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_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, + KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(WIN_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT), + + [WIN_FN] = LAYOUT_ansi_82( + KC_TRNS, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_TRNS, RGB_TOG, + KC_TRNS, BT_HST1, BT_HST2, BT_HST3, P2P4G, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_END, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, BAT_LVL, NK_TOGG, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS) + +}; + +#if defined(ENCODER_MAP_ENABLE) +const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = { + [MAC_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)}, + [MAC_FN] = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI)}, + [WIN_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)}, + [WIN_FN] = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI)} +}; +#endif + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + if(!process_record_lemokey_common(keycode, record)) { + return false; + } + return true; +} diff --git a/keyboards/lemokey/p1/ansi_encoder/keymaps/via/keymap.c b/keyboards/lemokey/p1/ansi_encoder/keymaps/via/keymap.c new file mode 100644 index 0000000000..fe7b76588e --- /dev/null +++ b/keyboards/lemokey/p1/ansi_encoder/keymaps/via/keymap.c @@ -0,0 +1,77 @@ +/* Copyright 2024 @ 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 . + */ + +#include QMK_KEYBOARD_H +#include "lemokey_common.h" + +// clang-format off +enum layers { + WIN_BASE, + WIN_FN, + WIN_L2, + WIN_L3, +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + [WIN_BASE] = LAYOUT_ansi_82( + 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_DEL, KC_MUTE, + 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_HOME, + 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_PGUP, + 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_PGDN, + 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_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(WIN_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT), + + [WIN_FN] = LAYOUT_ansi_82( + _______, 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, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_END, + RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, NK_TOGG, _______, _______, _______, _______, _______, _______, + _______, GU_TOGG, _______, _______, _______, _______, _______, _______, _______, _______), + + [WIN_L2] = LAYOUT_ansi_82( + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______), + + [WIN_L3] = LAYOUT_ansi_82( + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______) + +}; + +#if defined(ENCODER_MAP_ENABLE) +const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = { + [WIN_BASE] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)}, + [WIN_FN] = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI)}, + [WIN_L2] = {ENCODER_CCW_CW(KC_VOLD, KC_VOLU)}, + [WIN_L3] = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI)} +}; +#endif + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + if(!process_record_lemokey_common(keycode, record)) { + return false; + } + return true; +} diff --git a/keyboards/lemokey/p1/ansi_encoder/keymaps/via/rules.mk b/keyboards/lemokey/p1/ansi_encoder/keymaps/via/rules.mk new file mode 100644 index 0000000000..1e5b99807c --- /dev/null +++ b/keyboards/lemokey/p1/ansi_encoder/keymaps/via/rules.mk @@ -0,0 +1 @@ +VIA_ENABLE = yes diff --git a/keyboards/lemokey/p1/ansi_encoder/rules.mk b/keyboards/lemokey/p1/ansi_encoder/rules.mk new file mode 100644 index 0000000000..6e7633bfe0 --- /dev/null +++ b/keyboards/lemokey/p1/ansi_encoder/rules.mk @@ -0,0 +1 @@ +# This file intentionally left blank diff --git a/keyboards/lemokey/p1/config.h b/keyboards/lemokey/p1/config.h new file mode 100644 index 0000000000..9c8fb27c92 --- /dev/null +++ b/keyboards/lemokey/p1/config.h @@ -0,0 +1,54 @@ +/* Copyright 2024 @ 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 . + */ + +#pragma once + +#define UID_BASE 0x1FFFF204U + +/* I2C Driver Configuration */ +#define I2C1_SCL_PIN B8 +#define I2C1_SDA_PIN B9 +#define I2C1_CLOCK_SPEED 400000 +#define I2C1_DUTY_CYCLE FAST_DUTY_CYCLE_2 + +/* EEPROM Driver Configuration */ +#define EXTERNAL_EEPROM_BYTE_COUNT 2048 +#define EXTERNAL_EEPROM_PAGE_SIZE 32 +#define EXTERNAL_EEPROM_WRITE_TIME 3 + +/* User used eeprom */ +//#define EECONFIG_USER_DATA_SIZE 1 + +#define I2C1_OPMODE OPMODE_I2C +#define EXTERNAL_EEPROM_I2C_BASE_ADDRESS 0b10100010 + +/* Encoder Configuration */ +#define ENCODER_DEFAULT_POS 0x3 +#define ENCODER_MAP_KEY_DELAY 2 + +# define LED_DRIVER_SHUTDOWN_PIN B14 + +/* Raw hid command for factory test */ +# define RAW_HID_CMD 0xAB + +/* Factory test keys */ +#define FN_KEY_1 MO(1) +#define FN_KEY_2 MO(3) +#define FN_BL_TRIG_KEY KC_END + +#define MATRIX_IO_DELAY 10 + + diff --git a/keyboards/lemokey/p1/halconf.h b/keyboards/lemokey/p1/halconf.h new file mode 100644 index 0000000000..b26e07d7bc --- /dev/null +++ b/keyboards/lemokey/p1/halconf.h @@ -0,0 +1,24 @@ +/* Copyright 2024 @ 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 . + */ + +#pragma once + +#define _CHIBIOS_HAL_CONF_VER_8_0_ + +#define HAL_USE_I2C TRUE +#define HAL_USE_SPI TRUE + +#include_next diff --git a/keyboards/lemokey/p1/info.json b/keyboards/lemokey/p1/info.json new file mode 100644 index 0000000000..7dbf58f8fe --- /dev/null +++ b/keyboards/lemokey/p1/info.json @@ -0,0 +1,67 @@ +{ + "keyboard_name": "Lemokey P1", + "manufacturer": "Keychron", + "url": "https://github.com/Keychron", + "maintainer": "Keychron", + "processor": "WB32F3G71", + "bootloader": "wb32-dfu", + "usb": { + "vid": "0x362D" + }, + "features": { + "bootmagic": true, + "extrakey": true, + "mousekey": true, + "nkro": true, + "rgb_matrix": true, + "raw" : true, + "send_string" : true + }, + "matrix_pins": { + "cols": ["C14", "C15", "C2", "C3", "A0", "A1", "A2", "A3", "B10", "B12", "B13", "C7", "C8", "C9", "A10"], + "rows": ["C12", "D2", "B3", "B4", "B5", "B6"] + }, + "diode_direction": "ROW2COL", + "eeprom": { + "driver": "i2c" + }, + "encoder": { + "rotary": [ + {"pin_a": "A8", "pin_b": "A9"} + ] + }, + "indicators": { + "caps_lock": "C1" + }, + "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 + } + }, + "build": { + "debounce_type": "sym_eager_pk" + }, + "debounce": 20 +} diff --git a/keyboards/lemokey/p1/mcuconf.h b/keyboards/lemokey/p1/mcuconf.h new file mode 100644 index 0000000000..48e4fa4e67 --- /dev/null +++ b/keyboards/lemokey/p1/mcuconf.h @@ -0,0 +1,26 @@ +/* Copyright 2024 @ 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 + +#undef WB32_I2C_USE_I2C1 +#define WB32_I2C_USE_I2C1 TRUE + +#undef WB32_SPI_USE_QSPI +#define WB32_SPI_USE_QSPI TRUE + + diff --git a/keyboards/lemokey/p1/p1.c b/keyboards/lemokey/p1/p1.c new file mode 100644 index 0000000000..2634b2459c --- /dev/null +++ b/keyboards/lemokey/p1/p1.c @@ -0,0 +1,34 @@ +/* Copyright 2024 @ 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 . + */ + +#include "quantum.h" +#include "eeprom.h" + +void eeconfig_init_kb(void) { +#if (EECONFIG_KB_DATA_SIZE) == 0 + // Reset Keyboard EEPROM value to blank, rather than to a set value + eeconfig_update_kb(0); +#endif + + keymap_config.raw = eeconfig_read_keymap(); + keymap_config.nkro = 0; + eeconfig_update_keymap(keymap_config.raw); + + eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 1U << 0); + default_layer_set(1U << 0); + + eeconfig_init_user(); +} diff --git a/keyboards/lemokey/p1/readme.md b/keyboards/lemokey/p1/readme.md new file mode 100644 index 0000000000..b666fcdf2e --- /dev/null +++ b/keyboards/lemokey/p1/readme.md @@ -0,0 +1,21 @@ +# Lemokey P1 + +![Lemokey P1] + +A customizable 75% keyboard. + +* Keyboard Maintainer: [Keychron](https://github.com/keychron) +* Hardware Supported: Lemokey P1 +* Hardware Availability: [Lemokey P1 QMK/VIA Wireless Custom Mechanical Keyboard] + +Make example for this keyboard (after setting up your build environment): + + make keychron/p1/ansi_encoder:default + +Flashing example for this keyboard: + + make keychron/p1/ansi_encoder:default:flash + +**Reset Key**: Disconnect the USB cable, toggle mode switch to "Cable", hold down the *Esc* key or reset button underneath space bar, then connect 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). diff --git a/keyboards/lemokey/p1/rules.mk b/keyboards/lemokey/p1/rules.mk new file mode 100644 index 0000000000..95cd29f122 --- /dev/null +++ b/keyboards/lemokey/p1/rules.mk @@ -0,0 +1,3 @@ +include keyboards/lemokey/common/lemokey_common.mk + +VPATH += $(TOP_DIR)/keyboards/lemokey diff --git a/keyboards/lemokey/p1/via_json/p1_ansi_encoder_v1.0.json b/keyboards/lemokey/p1/via_json/p1_ansi_encoder_v1.0.json new file mode 100644 index 0000000000..3274b2fdae --- /dev/null +++ b/keyboards/lemokey/p1/via_json/p1_ansi_encoder_v1.0.json @@ -0,0 +1,280 @@ +{ + "name": "Lemokey P1 ANSI Knob", + "vendorId": "0x362D", + "productId": "0x0310", + "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": "Siri", "title": "Siri in macOS", "shortName": "Siri"}, + {"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"} + ], + "matrix": {"rows": 6, "cols" : 15}, + "layouts": { + "keymap": [ + [ + { + "c": "#777777" + }, + "0, 0", + { + "x": 0.25, + "c": "#cccccc" + }, + "0, 1", + "0, 2", + "0, 3", + "0, 4", + { + "x": 0.25, + "c": "#aaaaaa" + }, + "0, 5", + "0, 6", + "0, 7", + "0, 8", + { + "x": 0.25, + "c": "#cccccc" + }, + "0, 9", + "0, 10", + "0, 11", + "0, 12", + { + "x": 0.25, + "c": "#aaaaaa" + }, + "0, 13", + { + "x": 0.25 + }, + "0, 14\n\n\n\n\n\n\n\n\ne0" + ], + [ + { + "y": 0.25, + "c": "#aaaaaa" + }, + "1, 0", + { + "c": "#cccccc" + }, + "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": "#aaaaaa" + }, + "1, 13", + { + "x": 0.25 + }, + "1, 14" + ], + [ + { + "w": 1.5, + "c": "#aaaaaa" + }, + "2, 0", + { + "c": "#cccccc" + }, + "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": "#aaaaaa" + }, + "2, 13", + { + "x": 0.25 + }, + "2, 14" + ], + [ + { + "w": 1.75, + "c": "#aaaaaa" + }, + "3, 0", + { + "c": "#cccccc" + }, + "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": "#aaaaaa" + }, + "3, 13", + { + "x": 0.25 + }, + "3, 14" + ], + [ + { + "w": 2.25, + "c": "#aaaaaa" + }, + "4, 0", + { + "c": "#cccccc" + }, + "4, 2", + "4, 3", + "4, 4", + "4, 5", + "4, 6", + "4, 7", + "4, 8", + "4, 9", + "4, 10", + "4, 11", + { + "w": 1.75, + "c": "#aaaaaa" + }, + "4, 12", + { + "x": 0.25, + "y": 0.25 + }, + "4, 13" + ], + [ + { + "y": -0.25, + "w": 1.25, + "c": "#aaaaaa" + }, + "5, 0", + { + "w": 1.25 + }, + "5, 1", + { + "w": 1.25 + }, + "5, 2", + { + "w": 6.25, + "c": "#cccccc" + }, + "5, 6", + { + "c": "#aaaaaa" + }, + "5, 9", + "5, 10", + "5, 11", + { + "x": 0.25, + "y": 0.25 + }, + "5, 12", + "5, 13", + "5, 14" + ] + ] + } +} diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h index c2533ca49c..5b6b2655e5 100644 --- a/quantum/led_matrix/led_matrix.h +++ b/quantum/led_matrix/led_matrix.h @@ -36,6 +36,9 @@ #ifdef CKLED2001 # include "ckled2001-simple.h" #endif +#ifdef SNLED27351_SPI +# include "snled27351-simple-spi.h" +#endif #ifndef LED_MATRIX_LED_FLUSH_LIMIT # define LED_MATRIX_LED_FLUSH_LIMIT 16 diff --git a/quantum/led_matrix/led_matrix_drivers.c b/quantum/led_matrix/led_matrix_drivers.c index 13c8935d11..cd4e374ad3 100644 --- a/quantum/led_matrix/led_matrix_drivers.c +++ b/quantum/led_matrix/led_matrix_drivers.c @@ -244,4 +244,23 @@ const led_matrix_driver_t led_matrix_driver = { .set_value_all = ckled2001_set_value_all, }; # endif +#elif defined(SNLED27351_SPI) +# include "spi_master.h" + +static void init(void) { + spi_init(); + + snled27351_init_drivers(); +} + +static void flush(void) { + snled27351_flush(); +} + +const rgb_matrix_driver_t rgb_matrix_driver = { + .init = init, + .flush = flush, + .set_color = snled27351_set_value, + .set_color_all = snled27351_set_value_all, +}; #endif diff --git a/quantum/rgb_matrix/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h index 38040fb0cc..c1ce69fcea 100644 --- a/quantum/rgb_matrix/rgb_matrix.h +++ b/quantum/rgb_matrix/rgb_matrix.h @@ -38,6 +38,8 @@ # include "is31flcommon.h" #elif defined(CKLED2001) # include "ckled2001.h" +#elif defined(SNLED27351_SPI) +# include "snled27351-spi.h" #elif defined(AW20216) # include "aw20216.h" #elif defined(WS2812) diff --git a/quantum/rgb_matrix/rgb_matrix_drivers.c b/quantum/rgb_matrix/rgb_matrix_drivers.c index 695ecc78a4..81fa44898a 100644 --- a/quantum/rgb_matrix/rgb_matrix_drivers.c +++ b/quantum/rgb_matrix/rgb_matrix_drivers.c @@ -392,6 +392,25 @@ const rgb_matrix_driver_t rgb_matrix_driver = { .set_color_all = ckled2001_set_color_all, }; # endif +#elif defined(SNLED27351_SPI) +# include "spi_master.h" + +static void init(void) { + spi_init(); + + snled27351_init_drivers(); +} + +static void flush(void) { + snled27351_flush(); +} + +const rgb_matrix_driver_t rgb_matrix_driver = { + .init = init, + .flush = flush, + .set_color = snled27351_set_color, + .set_color_all = snled27351_set_color_all, +}; #elif defined(AW20216) # include "spi_master.h"