This commit is contained in:
esplo 2025-07-23 13:39:25 -07:00 committed by GitHub
commit 33418e7723
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 3220 additions and 0 deletions

View File

@ -0,0 +1,32 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#define FW_VERSION "20250419-1.2.0"
// see `wear_leveling_rp2040_flash_config.h`
// currently, using 16MB SPI flash
#define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 8192
// using W25Q128JVS as an EEPROM
#define EXTERNAL_EEPROM_PAGE_SIZE 256
#define EXTERNAL_EEPROM_BYTE_COUNT (65536 * EXTERNAL_EEPROM_PAGE_SIZE)
#define EXTERNAL_EEPROM_ADDRESS_SIZE 4
#define EECONFIG_KB_DATA_SIZE 1024 // 1KB for kb_config_t
#define EECONFIG_KB_DATA_VERSION (0x01)
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET // Activates the double-tap behavior
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 1000U // Timeout window in ms in which the double tap can occur.
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED GP25 // Specify a optional status led by GPIO number which blinks when entering the bootloader
#define I2C_DRIVER I2CD1
#define I2C1_SDA_PIN GP2
#define I2C1_SCL_PIN GP3
#define ENCODER_A_PINS {NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN}
#define ENCODER_B_PINS {NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN, NO_PIN}
#define POINTING_DEVICE_AUTO_MOUSE_ENABLE
#define AUTO_MOUSE_DEFAULT_LAYER 3

View File

@ -0,0 +1,418 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#include QMK_KEYBOARD_H
#include <stdint.h>
#include <stdbool.h>
#include "matrix.h"
#include "atomic_util.h"
#include "i2clib.h"
#include "i2c_master.h"
#include "kb_config.h"
#include "./drivers/encoder_dynamic_res.h"
extern DeviceList deviceList[MAX_MCP_NUM];
extern uint16_t nDevices;
#ifdef OLED_ENABLE
DisplayMode display_mode = DisplayMode_Layer;
#endif
static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
kb_config_t kb_config = {0};
uint16_t calc_auto_mouse_timeout_by_kbconfig(uint8_t value) {
return 100 * value;
}
#ifdef POINTING_DEVICE_ENABLE
bool set_scrolling = false;
// Variables to store accumulated scroll values
float scroll_accumulated_h = 0;
float scroll_accumulated_v = 0;
#endif
void keyboard_post_init_kb(void) {
debug_enable = true;
// debug_matrix = true;
// debug_keyboard=true;
// debug_mouse=true;
// initialize with the keyboard config data
eeconfig_read_kb_datablock(&kb_config.raw, 0, sizeof(kb_config));
#ifdef POINTING_DEVICE_ENABLE
modular_adns5050_set_angle(0, kb_config.angle_L);
modular_adns5050_set_angle(1, kb_config.angle_R);
set_auto_mouse_timeout(calc_auto_mouse_timeout_by_kbconfig(kb_config.mouse_layer_off_delay_ms));
modular_adns5050_set_led_off_length(kb_config.trackball_led_off_timeout * 5 * 60 * 1000);
#endif
#ifdef ENCODER_ENABLE
dynamic_res_encoder_update_res(0, kb_config.re_resolution_1);
dynamic_res_encoder_update_res(1, kb_config.re_resolution_2);
dynamic_res_encoder_update_res(2, kb_config.re_resolution_3);
dynamic_res_encoder_update_res(3, kb_config.re_resolution_4);
dynamic_res_encoder_update_res(4, kb_config.re_resolution_5);
dynamic_res_encoder_update_res(5, kb_config.re_resolution_6);
dynamic_res_encoder_update_res(6, kb_config.re_resolution_7);
dynamic_res_encoder_update_res(7, kb_config.re_resolution_8);
dynamic_res_encoder_update_res(8, kb_config.re_resolution_9);
dynamic_res_encoder_update_res(9, kb_config.re_resolution_10);
dynamic_res_encoder_update_res(10, kb_config.re_resolution_11);
dynamic_res_encoder_update_res(11, kb_config.re_resolution_12);
dynamic_res_encoder_update_res(12, kb_config.re_resolution_13);
dynamic_res_encoder_update_res(13, kb_config.re_resolution_14);
dynamic_res_encoder_update_res(14, kb_config.re_resolution_15);
dynamic_res_encoder_update_res(15, kb_config.re_resolution_16);
#endif
i2c_init();
do_scan();
keyboard_post_init_user();
}
void eeconfig_init_kb(void) {
memset(&kb_config.raw, 0, sizeof(kb_config));
// initialization of kb_config
my_kb_config_init(&kb_config);
eeconfig_update_kb_datablock(&kb_config.raw, 0, sizeof(kb_config));
eeconfig_init_user();
}
void matrix_init_custom(void) {
// initialize system pins
ATOMIC_BLOCK_FORCEON {
for (uint16_t i = 0; i < SYSTEM_KEY_NUM; i++) {
pin_t dpin = direct_pins[0][i];
if (dpin != NO_PIN) gpio_set_pin_input_high(dpin);
}
}
}
void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
matrix_row_t row_data = 0;
// system pins
if (current_row == 0) {
for (uint16_t i = 0; i < SYSTEM_KEY_NUM; i++) {
pin_t dpin = direct_pins[current_row][i];
if (dpin != NO_PIN) {
uint8_t pin_state = gpio_read_pin(dpin);
row_data |= (matrix_row_t)(0x1 ^ pin_state) << i;
}
}
}
for (uint16_t i = 0; i < nDevices; i++) {
// skip if the row is not the target
if (!(MATRIX_COLS * current_row <= deviceList[i].keymapShift && deviceList[i].keymapShift < MATRIX_COLS * (current_row + 1))) continue;
uint16_t shift_value = deviceList[i].keymapShift % MATRIX_COLS;
bool success = change_channel(deviceList[i].ch);
if (!success) {
continue;
}
Keys_Data data = {};
if (deviceList[i].type == Type_PCA9557_Keys4 || deviceList[i].type == Type_PCA9557_Keys5) {
data = read_PCA9557_register(deviceList[i].address);
} else if (deviceList[i].type == Type_XL9555_Keys4 || deviceList[i].type == Type_XL9555_Keys5) {
data = read_XL9555_register(deviceList[i].address);
} else if (deviceList[i].type == Type_PCA9534A_RE_CLICKABLE) {
data = read_PCA9534A_register(deviceList[i].address);
} else {
dprintf("failed to read data. unknown type: %d, ch: %X, addr: %X\n", deviceList[i].type, deviceList[i].ch, deviceList[i].address);
continue;
}
if (data.type == Type_PCA9557_Keys4 || data.type == Type_PCA9557_Keys5 || data.type == Type_XL9555_Keys4 || data.type == Type_XL9555_Keys5) {
row_data |= (matrix_row_t)data.data[0] << shift_value;
} else if (deviceList[i].type == Type_PCA9534A_RE_CLICKABLE) {
// pick lower 3 bits
row_data |= (matrix_row_t)(((data.data[0] & 0x04) >> 2)) << shift_value;
} else {
dprintf("failed to read data. unknown type: %d, ch: %X, addr: %X, data: %X\n", deviceList[i].type, deviceList[i].ch, deviceList[i].address, data.data[0]);
continue;
}
}
current_matrix[current_row] = row_data;
}
bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
#ifdef POINTING_DEVICE_ENABLE
modular_adns5050_wake_up_all(true);
#endif
// If console is enabled, it will print the matrix position and status of each key pressed
#ifdef CONSOLE_ENABLE
uprintf("KL: kc: 0x%04X, col: %2u, row: %2u, pressed: %u, time: %5u, int: %u, count: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed, record->event.time, record->tap.interrupted, record->tap.count);
uprintf("#device %u\n", sizeof(deviceList));
#endif
// system keys, but not combo keys
if (record->event.pressed && record->event.key.row == 0 && record->event.key.col == 0 && record->event.type != COMBO_EVENT) {
dprintf("GP9 pressed\n");
display_mode = (display_mode + 1) % DisplayMode_MAX;
#ifdef OLED_ENABLE
oled_clear();
#endif
return false;
}
// process next oled page
if (record->event.pressed) {
switch (keycode) {
case NEXT_OLED_PAGE:
display_mode = (display_mode + 1) % DisplayMode_MAX;
#ifdef OLED_ENABLE
oled_clear();
#endif
return false;
}
}
// drag scroll
#ifdef POINTING_DEVICE_ENABLE
switch (keycode) {
case DRAG_SCROLL:
// Toggle set_scrolling
set_scrolling = record->event.pressed;
return false;
}
#endif
if (!process_kb_config_modification(&kb_config, keycode, record)) {
eeconfig_update_kb_datablock(&kb_config.raw, 0, sizeof(kb_config));
switch (keycode) {
case AUTO_MOUSE_LAYER_P1:
set_auto_mouse_enable(kb_config.mouse_layer_on);
break;
case AUTO_MOUSE_LAYER_OFF_DELAY_P1:
case AUTO_MOUSE_LAYER_OFF_DELAY_M1:
set_auto_mouse_timeout(calc_auto_mouse_timeout_by_kbconfig(kb_config.mouse_layer_off_delay_ms));
break;
case ANGLE_L_ADJUSTMENT_P1:
case ANGLE_L_ADJUSTMENT_M1:
case ANGLE_L_ADJUSTMENT_P30:
case ANGLE_L_ADJUSTMENT_M30:
modular_adns5050_set_angle(0, kb_config.angle_L);
break;
case ANGLE_R_ADJUSTMENT_P1:
case ANGLE_R_ADJUSTMENT_M1:
case ANGLE_R_ADJUSTMENT_P30:
case ANGLE_R_ADJUSTMENT_M30:
modular_adns5050_set_angle(1, kb_config.angle_R);
break;
}
return false;
}
return process_record_user(keycode, record);
}
#ifdef OLED_ENABLE
bool oled_task_kb(void) {
if (!oled_task_user()) {
return false;
}
{
uint8_t current_layer = get_highest_layer(layer_state);
static char type_count_str[7];
oled_write_P(PSTR("layer: "), false);
itoa(current_layer, type_count_str, 10);
oled_write_ln(type_count_str, false);
}
if (display_mode == DisplayMode_Layer) {
// first line
{
static char type_count_str[7];
oled_write_P(PSTR("# Device: "), false);
itoa(nDevices, type_count_str, 10);
oled_write_ln(type_count_str, false);
}
// second line
{
{
static char type_count_str[7];
oled_write_P(PSTR(" KEY4: "), false);
uint16_t keynum = 0;
for (uint16_t i = 0; i < nDevices; i++) {
keynum += (int)(deviceList[i].type == Type_PCA9557_Keys4 || deviceList[i].type == Type_XL9555_Keys4);
}
itoa(keynum, type_count_str, 10);
oled_write_P(type_count_str, false);
oled_write_P(PSTR(", "), false);
}
{
static char type_count_str[7];
oled_write_P(PSTR(" KEY5: "), false);
uint16_t keynum = 0;
for (uint16_t i = 0; i < nDevices; i++) {
keynum += (int)(deviceList[i].type == Type_PCA9557_Keys5 || deviceList[i].type == Type_XL9555_Keys5);
}
itoa(keynum, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_ln(PSTR(""), false);
}
// third line
{
{
static char type_count_str[7];
oled_write_P(PSTR(" RE: "), false);
uint16_t keynum = 0;
for (uint16_t i = 0; i < nDevices; i++) {
keynum += (int)(deviceList[i].type == Type_PCA9534A_RE_CLICKABLE);
}
itoa(keynum, type_count_str, 10);
oled_write_P(type_count_str, false);
oled_write_P(PSTR(", "), false);
}
{
static char type_count_str[7];
oled_write_P(PSTR(" TB: "), false);
uint16_t num = modular_adns5050_get_connected_count();
itoa(num, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_ln(PSTR(""), false);
}
} else if (display_mode == DisplayMode_CONF_0) {
// debug output of the all kb_config data
debug_output_kb_config(&kb_config);
oled_kb_config_output(&kb_config, 0);
} else if (display_mode == DisplayMode_CONF_1) {
oled_kb_config_output(&kb_config, 1);
} else if (display_mode == DisplayMode_CONF_2) {
oled_kb_config_output(&kb_config, 2);
} else if (display_mode == DisplayMode_CONF_3) {
oled_kb_config_output(&kb_config, 3);
} else if (display_mode == DisplayMode_CONF_4) {
oled_kb_config_output(&kb_config, 4);
} else if (display_mode == DisplayMode_VERSION) {
oled_write_ln_P(PSTR("-- VERSION --"), false);
oled_write_ln_P(PSTR(FW_VERSION), false);
} else {
oled_write_ln_P(PSTR("-- INVALID MODE --"), false);
}
return true;
}
#endif
#ifdef ENCODER_ENABLE
uint8_t encoder_quadrature_read_pin(uint8_t index, bool pad_b) {
uint8_t target = index;
for (uint16_t i = 0; i < nDevices; i++) {
if (deviceList[i].type == Type_PCA9534A_RE_CLICKABLE) {
// skip if this RE is not the indexed one
if (target != 0) {
target--;
continue;
}
bool success = change_channel(deviceList[i].ch);
if (!success) {
continue;
}
Keys_Data data = read_PCA9534A_register(deviceList[i].address);
// data -> XXXXX (click) (B) (A)
if (pad_b) {
return (data.data[0] & (0x01 << 1)) >> 1;
} else {
return (data.data[0] & (0x01 << 0)) >> 0;
}
}
}
// if reaches
return 0;
}
bool encoder_update_kb(uint8_t index, bool clockwise) {
# ifdef POINTING_DEVICE_ENABLE
modular_adns5050_wake_up_all(true);
# endif
return encoder_update_user(index, clockwise);
}
// override existing weak function
void encoder_driver_task(void) {
for (uint8_t i = 0; i < NUM_ENCODERS; i++) {
dynamic_res_encoder_quadrature_handle_read(i, encoder_quadrature_read_pin(i, false), encoder_quadrature_read_pin(i, true));
}
}
#endif
#ifdef POINTING_DEVICE_ENABLE
void pointing_device_driver_init(void) {
modular_adns5050_pointing_device_driver.init();
set_auto_mouse_layer(AUTO_MOUSE_DEFAULT_LAYER); // default
set_auto_mouse_enable(true);
}
report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) {
float mag = 0.25 * (kb_config.pointer_speed_magnification + 1);
float scr_mag = 0.25 * (kb_config.drag_scroll_speed_magnification + 1);
report_mouse_t t = modular_adns5050_pointing_device_driver.get_report(mouse_report);
// Check if drag scrolling is active
if (set_scrolling) {
// Calculate and accumulate scroll values based on mouse movement and divisors
scroll_accumulated_h += (float)t.x / SCROLL_DIVISOR_H;
scroll_accumulated_v += (float)t.y / SCROLL_DIVISOR_V;
// Assign integer parts of accumulated scroll values to the mouse report
t.h = (int8_t)(scroll_accumulated_h * scr_mag);
t.v = (int8_t)(scroll_accumulated_v * scr_mag);
if (kb_config.invert_drag_scroll_y) {
t.h *= -1;
}
if (kb_config.invert_drag_scroll_x) {
t.v *= -1;
}
// Update accumulated scroll values by subtracting the integer parts
scroll_accumulated_h -= (int8_t)scroll_accumulated_h;
scroll_accumulated_v -= (int8_t)scroll_accumulated_v;
// Clear the X and Y values of the mouse report
t.x = 0;
t.y = 0;
} else {
t.x *= mag;
t.y *= mag;
}
return t;
}
uint16_t pointing_device_driver_get_cpi(void) {
return modular_adns5050_pointing_device_driver.get_cpi();
}
void pointing_device_driver_set_cpi(uint16_t cpi) {
modular_adns5050_pointing_device_driver.set_cpi(cpi);
}
bool is_mouse_record_kb(uint16_t keycode, keyrecord_t *record) {
if (layer_state_is(get_auto_mouse_layer()) && kb_config.all_keys_are_mouse_keys) {
return true;
}
return is_mouse_record_user(keycode, record);
}
#endif

View File

@ -0,0 +1,20 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "quantum.h"
#include "kb_config.h"
#ifdef OLED_ENABLE
typedef enum _DisplayMode { DisplayMode_Layer, DisplayMode_CONF_0, DisplayMode_CONF_1, DisplayMode_CONF_2, DisplayMode_CONF_3, DisplayMode_CONF_4, DisplayMode_VERSION, DisplayMode_MAX } DisplayMode;
#endif
#ifdef POINTING_DEVICE_ENABLE
# include "drivers/modular_adns5050.h"
# define NUM_MODULAR_ADNS5050 ARRAY_SIZE(((pin_t[])MODULAR_ADNS5050_SCLK_PINS))
# define SCROLL_DIVISOR_H 32.0
# define SCROLL_DIVISOR_V 32.0
#endif

View File

@ -0,0 +1,86 @@
// Copyright 2018 Jack Humbert <jack.humb@gmail.com>
// Copyright 2018-2023 Nick Brassel (@tzarc)
// Copyright 2025 @esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#include <stdint.h>
#include "encoder.h"
#include "quantum.h"
#include "./drivers/encoder_dynamic_res.h"
#ifdef ENCODER_ENABLE
static uint8_t encoder_resolutions[NUM_ENCODERS] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4};
static uint8_t encoder_state[NUM_ENCODERS] = {0};
static int8_t encoder_pulses[NUM_ENCODERS] = {0};
static int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
static void dynamic_res_encoder_handle_state_change(uint8_t index, uint8_t state) {
uint8_t i = index;
const uint8_t resolution = encoder_resolutions[i];
static uint32_t timer = 0;
if (timer_elapsed32(timer) > 1000) {
for (uint8_t j = 0; j < NUM_ENCODERS; ++j) {
dprintf("enc[%u] res: %d pul: %d\n", j, encoder_resolutions[j], encoder_pulses[j]);
}
timer = timer_read32();
}
encoder_pulses[i] += encoder_LUT[state & 0xF];
# ifdef ENCODER_DEFAULT_POS
if ((encoder_pulses[i] >= resolution) || (encoder_pulses[i] <= -resolution) || ((state & 0x3) == ENCODER_DEFAULT_POS)) {
if (encoder_pulses[i] >= 1) {
# else
if (encoder_pulses[i] >= resolution) {
# endif
encoder_queue_event(i, ENCODER_COUNTER_CLOCKWISE);
}
# ifdef ENCODER_DEFAULT_POS
if (encoder_pulses[i] <= -1) {
# else
if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise
# endif
encoder_queue_event(i, ENCODER_CLOCKWISE);
}
encoder_pulses[i] %= resolution;
# ifdef ENCODER_DEFAULT_POS
encoder_pulses[i] = 0;
}
# endif
}
void dynamic_res_encoder_quadrature_handle_read(uint8_t index, uint8_t pin_a_state, uint8_t pin_b_state) {
uint8_t state = pin_a_state | (pin_b_state << 1);
if ((encoder_state[index] & 0x3) != state) {
encoder_state[index] <<= 2;
encoder_state[index] |= state;
dynamic_res_encoder_handle_state_change(index, encoder_state[index]);
}
}
void dynamic_res_encoder_update_res(uint8_t index, uint8_t res) {
switch (res) {
case 0:
encoder_resolutions[index] = 1;
break;
case 1:
encoder_resolutions[index] = 2;
break;
case 2:
encoder_resolutions[index] = 4;
break;
case 3:
encoder_resolutions[index] = 8;
break;
default:
encoder_resolutions[index] = 4;
}
encoder_state[index] = 0;
encoder_pulses[index] = 0;
}
#endif

View File

@ -0,0 +1,16 @@
// Copyright 2018 Jack Humbert <jack.humb@gmail.com>
// Copyright 2018-2023 Nick Brassel (@tzarc)
// Copyright 2025 @esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define ENCODER_CLOCKWISE true
#define ENCODER_COUNTER_CLOCKWISE false
extern uint8_t encoder_quadrature_read_pin(uint8_t index, bool pad_b);
void dynamic_res_encoder_quadrature_handle_read(uint8_t index, uint8_t pin_a_state, uint8_t pin_b_state);
void dynamic_res_encoder_update_res(uint8_t index, uint8_t res);

View File

@ -0,0 +1,383 @@
/*
* Copyright 2025 esplo
* Copyright 2021 Colin Lam (Ploopy Corporation)
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2019 Sunjun Kim
* Copyright 2019 Hiroyuki Okada
*
* 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 <math.h>
#include "wait.h"
#include "debug.h"
#include "gpio.h"
#include "timer.h"
#include "pointing_device_internal.h"
#include "modular_adns5050.h"
// Registers
// clang-format off
#define REG_PRODUCT_ID 0x00
#define REG_REVISION_ID 0x01
#define REG_MOTION 0x02
#define REG_DELTA_X 0x03
#define REG_DELTA_Y 0x04
#define REG_SQUAL 0x05
#define REG_SHUTTER_UPPER 0x06
#define REG_SHUTTER_LOWER 0x07
#define REG_MAXIMUM_PIXEL 0x08
#define REG_PIXEL_SUM 0x09
#define REG_MINIMUM_PIXEL 0x0a
#define REG_PIXEL_GRAB 0x0b
#define REG_MOUSE_CONTROL 0x0d
#define REG_MOUSE_CONTROL2 0x19
#define REG_LED_DC_MODE 0x22
#define REG_CHIP_RESET 0x3a
#define REG_PRODUCT_ID2 0x3e
#define REG_INV_REV_ID 0x3f
#define REG_MOTION_BURST 0x63
// clang-format on
const pointing_device_driver_t modular_adns5050_pointing_device_driver = {
.init = modular_adns5050_init,
.get_report = modular_adns5050_get_all_report,
.set_cpi = modular_adns5050_set_all_cpi,
.get_cpi = modular_adns5050_get_all_cpi,
};
// set zero for all elements
static bool connected[NUM_MODULAR_ADNS5050] = {false};
static bool powered_down[NUM_MODULAR_ADNS5050] = {false};
const pin_t SCLK_PINS[] = MODULAR_ADNS5050_SCLK_PINS;
const pin_t SDIO_PINS[] = MODULAR_ADNS5050_SDIO_PINS;
const pin_t CS_PINS[] = MODULAR_ADNS5050_CS_PINS;
const pin_t RESET_PINS[] = MODULAR_ADNS5050_RESET_PINS;
// angle for each sensor.
uint16_t angle[NUM_MODULAR_ADNS5050] = {0};
uint32_t trackball_timeout_length = 5 * 60 * 1000; // default: 5 min
uint32_t trackball_timeout = 0;
void modular_adns5050_init(void) {
for (uint8_t i = 0; i < NUM_MODULAR_ADNS5050; i++) {
// Initialize the ADNS serial pins.
gpio_set_pin_output(SCLK_PINS[i]);
gpio_set_pin_output(SDIO_PINS[i]);
gpio_set_pin_output(CS_PINS[i]);
// RESET pin must be high
gpio_set_pin_output(RESET_PINS[i]);
gpio_write_pin_high(RESET_PINS[i]);
powered_down[i] = true;
connected[i] = false;
}
modular_adns5050_wake_up_all(false);
}
report_mouse_t modular_adns5050_get_all_report(report_mouse_t mouse_report) {
for (uint8_t i = 0; i < NUM_MODULAR_ADNS5050; i++) {
report_mouse_t m = modular_adns5050_get_report(i, mouse_report);
mouse_report.x += m.x;
mouse_report.y += m.y;
}
modular_adns5050_check_timeout();
return mouse_report;
}
uint16_t modular_adns5050_get_all_cpi(void) {
// get the maximum CPI since the CPI should be the same amoung all sensors
uint16_t maxCPI = 0;
for (uint8_t i = 0; i < NUM_MODULAR_ADNS5050; i++) {
uint16_t cpi = modular_adns5050_get_cpi(i);
maxCPI = (maxCPI > cpi) ? maxCPI : cpi;
}
return maxCPI;
}
void modular_adns5050_set_all_cpi(uint16_t cpi) {
for (uint8_t i = 0; i < NUM_MODULAR_ADNS5050; i++) {
modular_adns5050_set_cpi(i, cpi);
}
}
void modular_adns5050_power_down_all(void) {
for (uint8_t i = 0; i < NUM_MODULAR_ADNS5050; i++) {
if (powered_down[i]) {
continue;
}
modular_adns5050_power_down(i);
}
}
void modular_adns5050_wake_up_all(bool connected_only) {
for (uint8_t i = 0; i < NUM_MODULAR_ADNS5050; i++) {
modular_adns5050_wake_up(i, connected_only);
}
}
void modular_adns5050_check_timeout(void) {
if (trackball_timeout_length == 0) {
return;
}
if (timer_expired32(timer_read32(), trackball_timeout)) {
modular_adns5050_power_down_all();
}
}
uint8_t modular_adns5050_get_connected_count(void) {
uint8_t count = 0;
for (uint8_t i = 0; i < NUM_MODULAR_ADNS5050; i++) {
if (connected[i]) {
count++;
}
}
return count;
}
void modular_adns5050_set_led_off_length(uint32_t length_ms) {
trackball_timeout_length = length_ms;
}
void modular_adns5050_wake_up(uint8_t index, bool connected_only) {
trackball_timeout = timer_read32() + trackball_timeout_length;
// skip if the sensor is already active
if (!powered_down[index]) {
return;
}
if (connected_only && !connected[index]) {
return;
}
// reboot the adns.
// if the adns hasn't initialized yet, this is harmless.
modular_adns5050_write_reg(index, REG_CHIP_RESET, 0x5a);
// wait maximum time before adns is ready.
// this ensures that the adns is actuall ready after reset.
wait_ms(55);
// check if ADNS-5050 is connected
// Product_ID must be 0x12
// if you read this value before initialized, this value is 0x09 AFAIK
uint8_t product_id = modular_adns5050_read_reg(index, REG_PRODUCT_ID);
if (product_id != 0x12) {
powered_down[index] = true;
return;
}
powered_down[index] = false;
connected[index] = true;
// read a burst from the adns and then discard it.
// gets the adns ready for write commands
// (for example, setting the dpi).
modular_adns5050_read_burst(index);
}
// Perform a synchronization with the ADNS.
// Just as with the serial protocol, this is used by the slave to send a
// synchronization signal to the master.
void modular_adns5050_sync(uint8_t index) {
gpio_write_pin_low(CS_PINS[index]);
wait_us(1);
gpio_write_pin_high(CS_PINS[index]);
}
void modular_adns5050_cs_select(uint8_t index) {
gpio_write_pin_low(CS_PINS[index]);
}
void modular_adns5050_cs_deselect(uint8_t index) {
gpio_write_pin_high(CS_PINS[index]);
}
uint8_t modular_adns5050_serial_read(uint8_t index) {
gpio_set_pin_input(SDIO_PINS[index]);
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; ++i) {
gpio_write_pin_low(SCLK_PINS[index]);
wait_us(1);
byte = (byte << 1) | gpio_read_pin(SDIO_PINS[index]);
gpio_write_pin_high(SCLK_PINS[index]);
wait_us(1);
}
return byte;
}
void modular_adns5050_serial_write(uint8_t index, uint8_t data) {
gpio_set_pin_output(SDIO_PINS[index]);
for (int8_t b = 7; b >= 0; b--) {
gpio_write_pin_low(SCLK_PINS[index]);
if (data & (1 << b))
gpio_write_pin_high(SDIO_PINS[index]);
else
gpio_write_pin_low(SDIO_PINS[index]);
wait_us(2);
gpio_write_pin_high(SCLK_PINS[index]);
}
// tSWR. See page 15 of the ADNS spec sheet.
// Technically, this is only necessary if the next operation is an SDIO
// read. This is not guaranteed to be the case, but we're being lazy.
wait_us(4);
// Note that tSWW is never necessary. All write operations require at
// least 32us, which exceeds tSWW, so there's never a need to wait for it.
}
// Read a byte of data from a register on the ADNS.
// Don't forget to use the register map (as defined in the header file).
uint8_t modular_adns5050_read_reg(uint8_t index, uint8_t reg_addr) {
modular_adns5050_cs_select(index);
modular_adns5050_serial_write(index, reg_addr);
// We don't need a minimum tSRAD here. That's because a 4ms wait time is
// already included in adns5050_serial_write(), so we're good.
// See page 10 and 15 of the ADNS spec sheet.
// wait_us(4);
uint8_t byte = modular_adns5050_serial_read(index);
// tSRW & tSRR. See page 15 of the ADNS spec sheet.
// Technically, this is only necessary if the next operation is an SDIO
// read or write. This is not guaranteed to be the case.
// Honestly, this wait could probably be removed.
wait_us(1);
modular_adns5050_cs_deselect(index);
return byte;
}
void modular_adns5050_write_reg(uint8_t index, uint8_t reg_addr, uint8_t data) {
modular_adns5050_cs_select(index);
modular_adns5050_serial_write(index, 0b10000000 | reg_addr);
modular_adns5050_serial_write(index, data);
modular_adns5050_cs_deselect(index);
}
// Convert a two's complement byte from an unsigned data type into a signed
// data type.
int8_t modular_convert_twoscomp(uint8_t data) {
if ((data & 0x80) == 0x80) {
return -128 + (data & 0x7F);
} else {
return data;
}
}
report_modular_adns5050_t modular_adns5050_read_burst(uint8_t index) {
modular_adns5050_cs_select(index);
report_modular_adns5050_t data;
data.dx = 0;
data.dy = 0;
if (powered_down[index]) {
return data;
}
modular_adns5050_serial_write(index, REG_MOTION_BURST);
// We don't need a minimum tSRAD here. That's because a 4ms wait time is
// already included in adns5050_serial_write(), so we're good.
// See page 10 and 15 of the ADNS spec sheet.
// wait_us(4);
uint8_t x = modular_adns5050_serial_read(index);
uint8_t y = modular_adns5050_serial_read(index);
// Burst mode returns a bunch of other shit that we don't really need.
// Setting CS to high ends burst mode early.
modular_adns5050_cs_deselect(index);
data.dx = modular_convert_twoscomp(x);
data.dy = modular_convert_twoscomp(y);
return data;
}
// Don't forget to use the definitions for CPI in the header file.
void modular_adns5050_set_cpi(uint8_t index, uint16_t cpi) {
uint8_t cpival = constrain((cpi / 125), 0x1, 0xD); // limits to 0--119
modular_adns5050_write_reg(index, REG_MOUSE_CONTROL2, 0b10000 | cpival);
}
uint16_t modular_adns5050_get_cpi(uint8_t index) {
uint8_t cpival = modular_adns5050_read_reg(index, REG_MOUSE_CONTROL2);
return (uint16_t)((cpival & 0b10000) * 125);
}
bool modular_adns5050_check_signature(uint8_t index) {
uint8_t pid = modular_adns5050_read_reg(index, REG_PRODUCT_ID);
uint8_t rid = modular_adns5050_read_reg(index, REG_REVISION_ID);
uint8_t pid2 = modular_adns5050_read_reg(index, REG_PRODUCT_ID2);
return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
}
void modular_adns5050_power_down(uint8_t index) {
if (!powered_down[index]) {
powered_down[index] = true;
modular_adns5050_write_reg(index, REG_MOUSE_CONTROL, 0b10);
dprintf("mouse %d powered down\n", index);
}
}
report_mouse_t modular_adns5050_get_report(uint8_t index, report_mouse_t mouse_report) {
report_modular_adns5050_t data = modular_adns5050_read_burst(index);
if (data.dx != 0 || data.dy != 0) {
// use true just to update the timeout
modular_adns5050_wake_up(index, true);
pd_dprintf("[%X] Raw ] X: %d, Y: %d\n", i, data.dx, data.dy);
double rad = angle[index] * (M_PI / 180);
mouse_xy_report_t x_rev = cos(rad) * data.dx + -sin(rad) * data.dy;
mouse_xy_report_t y_rev = sin(rad) * data.dx + cos(rad) * data.dy;
mouse_report.x += x_rev;
mouse_report.y += y_rev;
}
return mouse_report;
}
void modular_adns5050_set_angle(uint8_t index, uint16_t value) {
angle[index] = value;
}
void modular_adns5050_add_angle(uint8_t index, int16_t value) {
angle[index] = (360 + angle[index] + value) % 360;
}
uint16_t modular_adns5050_get_angle(uint8_t index) {
return angle[index];
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 2025 esplo
* Copyright 2021 Colin Lam (Ploopy Corporation)
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2019 Sunjun Kim
* Copyright 2019 Hiroyuki Okada
*
* 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
// not re-use existing adns5050.h since the `ADNS5050_SCLK_PIN` should not be defined to avoid selecting invalid pins in functions
#include <stdbool.h>
#include <stdint.h>
#include "pointing_device.h"
#define MODULAR_ADNS5050_SDIO_PINS {GP4, GP28}
#define MODULAR_ADNS5050_SCLK_PINS {GP6, GP26}
#define MODULAR_ADNS5050_CS_PINS {GP5, GP29}
#define MODULAR_ADNS5050_RESET_PINS {GP7, GP27}
// Definitions for the ADNS serial line.
#ifndef MODULAR_ADNS5050_SCLK_PINS
# error "No clock pin defined -- missing MODULAR_ADNS5050_SCLK_PINS"
#endif
#ifndef MODULAR_ADNS5050_SDIO_PINS
# error "No data pin defined -- missing MODULAR_ADNS5050_SDIO_PINS"
#endif
#ifndef MODULAR_ADNS5050_CS_PINS
# error "No data pin defined -- missing MODULAR_ADNS5050_CS_PINS"
#endif
#ifndef MODULAR_ADNS5050_RESET_PINS
# error "No data pin defined -- missing MODULAR_ADNS5050_RESET_PINS"
#endif
#define NUM_MODULAR_ADNS5050 ARRAY_SIZE(((pin_t[])MODULAR_ADNS5050_SCLK_PINS))
// CPI values
// clang-format off
#define CPI125 0x11
#define CPI250 0x12
#define CPI375 0x13
#define CPI500 0x14
#define CPI625 0x15
#define CPI750 0x16
#define CPI875 0x17
#define CPI1000 0x18
#define CPI1125 0x19
#define CPI1250 0x1a
#define CPI1375 0x1b
// clang-format on
#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
typedef struct {
int8_t dx;
int8_t dy;
} report_modular_adns5050_t;
const pointing_device_driver_t modular_adns5050_pointing_device_driver;
// A bunch of functions to implement the ADNS5050-specific serial protocol.
// Note that the "serial.h" driver is insufficient, because it does not
// manually manipulate a serial clock signal.
void modular_adns5050_init(void);
report_mouse_t modular_adns5050_get_all_report(report_mouse_t mouse_report);
uint16_t modular_adns5050_get_all_cpi(void);
void modular_adns5050_set_all_cpi(uint16_t cpi);
void modular_adns5050_power_down_all(void);
void modular_adns5050_wake_up_all(bool connected_only);
void modular_adns5050_check_timeout(void);
uint8_t modular_adns5050_get_connected_count(void);
void modular_adns5050_set_led_off_length(uint32_t length_ms);
// functions for each sensors
void modular_adns5050_wake_up(uint8_t index, bool connected_only);
void modular_adns5050_sync(uint8_t index);
uint8_t modular_adns5050_serial_read(uint8_t index);
void modular_adns5050_serial_write(uint8_t index, uint8_t data);
uint8_t modular_adns5050_read_reg(uint8_t index, uint8_t reg_addr);
void modular_adns5050_write_reg(uint8_t index, uint8_t reg_addr, uint8_t data);
report_modular_adns5050_t modular_adns5050_read_burst(uint8_t index);
void modular_adns5050_set_cpi(uint8_t index, uint16_t cpi);
uint16_t modular_adns5050_get_cpi(uint8_t index);
bool modular_adns5050_check_signature(uint8_t index);
void modular_adns5050_power_down(uint8_t index);
report_mouse_t modular_adns5050_get_report(uint8_t index, report_mouse_t mouse_report);
void modular_adns5050_set_angle(uint8_t index, uint16_t angle);
void modular_adns5050_add_angle(uint8_t index, int16_t angle);
uint16_t modular_adns5050_get_angle(uint8_t index);

View File

@ -0,0 +1,9 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#undef HAL_USE_I2C
#define HAL_USE_I2C TRUE
#include_next <halconf.h>

398
keyboards/cue2keys/i2clib.c Normal file
View File

@ -0,0 +1,398 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#include "i2clib.h"
#include <stdint.h>
#include "i2c_master.h"
#include "debug.h"
DeviceList deviceList[MAX_MCP_NUM];
uint16_t nDevices = 0;
bool change_channel(uint8_t channel) {
// channel must be 1-4
if (channel < 1 || 4 < channel) {
dprintf("Invalid channel %u!\n", channel);
return false;
}
const uint8_t data = 0x01 << (channel - 1);
i2c_status_t expander_status = i2c_write_register(I2C_MULTIPX_ADDR << 1, 0x0, &data, 1, MY_I2C_TIMEOUT);
if (expander_status == I2C_STATUS_SUCCESS) {
return true;
} else {
dprintf("failed to change channel: %u!\n", expander_status);
return false;
}
}
bool write_PCA9557_register_1byte(uint8_t address, uint8_t ctrl_addr, uint8_t data) {
i2c_status_t expander_status = i2c_write_register(address << 1, ctrl_addr, &data, 1, MY_I2C_TIMEOUT);
if (expander_status == I2C_STATUS_SUCCESS) {
dprintf("[PCA9557 set register] %X, reg:%X, data:%X\n", address, ctrl_addr, data);
return true;
} else {
dprintf("[PCA9557 set register] failed to set input %u! %X, reg:%X, data:%X\n", expander_status, address, ctrl_addr, data);
return false;
}
}
Keys_Data read_PCA9557_register(uint8_t address) {
Keys_Data error_result = {.type = Type_Unknown};
uint8_t inputData = 0;
i2c_status_t expander_status = i2c_read_register(address << 1, 0x0, &inputData, 1, MY_I2C_TIMEOUT);
if (expander_status != I2C_STATUS_SUCCESS) {
dprintf("failed to read data from %X! %X\n", address, expander_status);
return error_result;
}
// upper 3 bits are type indication
uint8_t type_data = (inputData & (0x07 << 5)) >> 5;
Type type = Type_Unknown;
if (type_data == 2) {
type = Type_PCA9557_Keys5;
} else if (type_data == 1) {
type = Type_PCA9557_Keys4;
} else {
// unknown type
dprintf("failed to read type from %X! Type Unknown: %X, type_data: %X\n", address, type, type_data);
return error_result;
}
// lower 5 bits are key input data. They are pulled-up, so they need to be inverted
uint8_t data = (~inputData & 0x1F);
if (type == Type_PCA9557_Keys4) {
// remove 1 bit if the device is `4 keys`
data &= 0x0F;
} else if (type == Type_PCA9557_Keys5) {
// 5 keys
// do nothing
} else {
// unknown
return error_result;
}
Keys_Data d = {.type = type};
memcpy(d.data, &data, 1);
return d;
}
bool write_XL9555_register_1byte(uint8_t address, uint8_t ctrl_addr, uint8_t data) {
i2c_status_t expander_status = i2c_write_register(address << 1, ctrl_addr, &data, 1, MY_I2C_TIMEOUT);
if (expander_status == I2C_STATUS_SUCCESS) {
dprintf("[XL9555 set register] %X, reg:%X, data:%X\n", address, ctrl_addr, data);
return true;
} else {
dprintf("[XL9555 set register] failed to set input %u! %X, reg:%X, data:%X\n", expander_status, address, ctrl_addr, data);
return false;
}
}
Keys_Data read_XL9555_register(uint8_t address) {
Keys_Data error_result = {.type = Type_Unknown};
uint8_t inputData[2] = {0x11, 0x22};
i2c_status_t expander_status = i2c_read_register(address << 1, 0x0, inputData, 2, MY_I2C_TIMEOUT);
if (expander_status != I2C_STATUS_SUCCESS) {
dprintf("failed to read data from %X! %X\n", address, expander_status);
return error_result;
}
// upper 4 bits are type indication
// Note: temporary, this device
uint8_t type_data = (inputData[0] & 0xF0) >> 4;
Type type = Type_Unknown;
if (type_data == 0x2) {
type = Type_XL9555_Keys5;
} else if (type_data == 0x0 || type_data == 0xF) { // temporary
type = Type_XL9555_Keys4;
} else {
// unknown type
dprintf("failed to read data from %X! Type Unknown: %X, type_data: %X\n", address, type, type_data);
return error_result;
}
// key input data are on 2 bytes
// inputData[0]: lower 4 bits
// inputData[1]: lower 1 bits
// They are pulled-up, so they need to be inverted
uint8_t data = (uint8_t)((inputData[1] << 4) | (0x0F & inputData[0]));
if (type == Type_XL9555_Keys4) {
// remove 1 bit if the device is `4 keys`
data &= 0x0F;
data ^= 0x0F;
} else if (type == Type_XL9555_Keys5) {
data ^= 0x1F;
}
Keys_Data d = {.type = type};
memcpy(d.data, &data, 1);
return d;
}
bool write_PCA9534A_register_1byte(uint8_t address, uint8_t ctrl_addr, uint8_t data) {
i2c_status_t expander_status = i2c_write_register(address << 1, ctrl_addr, &data, 1, MY_I2C_TIMEOUT);
if (expander_status == I2C_STATUS_SUCCESS) {
dprintf("[PCA9534A set register] %X, reg:%X, data:%X\n", address, ctrl_addr, data);
return true;
} else {
dprintf("[PCA9534A set register] failed to set input %u! %X, reg:%X, data:%X\n", expander_status, address, ctrl_addr, data);
return false;
}
}
Keys_Data read_PCA9534A_register(uint8_t address) {
Keys_Data error_result = {.type = Type_Unknown};
uint8_t inputData = 0;
i2c_status_t expander_status = i2c_read_register(address << 1, 0x0, &inputData, 1, MY_I2C_TIMEOUT);
if (expander_status != I2C_STATUS_SUCCESS) {
dprintf("failed to read data from %X! %X\n", address, expander_status);
return error_result;
}
// upper 3 bits are type indication
uint8_t type_data = (inputData & (0xFF << 5)) >> 5;
Type type = Type_Unknown;
if (type_data == 2) {
type = Type_PCA9534A_RE_CLICKABLE;
} else {
// unknown type
dprintf("failed to read type from %X! Type Unknown: %X, type_data: %X, inputData: %X \n", address, type, type_data, inputData);
return error_result;
}
// lower 5 bits are input data
uint8_t data = (inputData & 0x1F);
if (type == Type_PCA9534A_RE_CLICKABLE) {
// use only lower 3 bits. RO_A, RO_B, L_E
data &= 0x07;
} else {
// unknown
return error_result;
}
Keys_Data d = {.type = type};
memcpy(d.data, &data, 1);
return d;
}
Keys_Data init_PCA9557(uint8_t address) {
bool success = true;
// pins: input
success &= write_PCA9557_register_1byte(address, 0x3, 0xFF);
// polarity: no change
success &= write_PCA9557_register_1byte(address, 0x2, 0x00);
if (!success) {
dprintf("Failed to init PCA9557: %X\n", address);
Keys_Data data = {Type_Unknown};
return data;
}
// load inputs to detect the type
Keys_Data data = read_PCA9557_register(address);
return data;
}
Keys_Data init_XL9555(uint8_t address) {
bool success = true;
// pins: input
success &= write_XL9555_register_1byte(address, 0x6, 0xFF);
success &= write_XL9555_register_1byte(address, 0x7, 0xFF);
// polarity: no change
success &= write_XL9555_register_1byte(address, 0x4, 0x00);
success &= write_XL9555_register_1byte(address, 0x5, 0x00);
if (!success) {
dprintf("Failed to init XL9555: %X\n", address);
Keys_Data data = {Type_Unknown};
return data;
}
// load inputs to detect the type
Keys_Data data = read_XL9555_register(address);
return data;
}
Keys_Data init_PCA9534A(uint8_t address) {
bool success = true;
// pins: input
success &= write_PCA9534A_register_1byte(address, 0x3, 0xFF);
// polarity: no change
success &= write_PCA9534A_register_1byte(address, 0x2, 0x00);
if (!success) {
dprintf("Failed to init PCA9534A: %X\n", address);
Keys_Data data = {Type_Unknown};
return data;
}
// load inputs to detect the type
Keys_Data data = read_PCA9534A_register(address);
return data;
}
typedef enum {
ShiftType_Key_Start = 0,
ShiftType_RE_Start = 1,
} ShiftType;
uint16_t get_keymap_shift(uint8_t ch, ShiftType type) {
uint16_t keyShift = MAX_KEY_PER_CH_NUM * 5;
uint16_t reShift = MAX_RE_PER_CH_NUM * 1;
uint16_t chShift = keyShift + reShift + CH_BUFFER_NUM;
if (type == ShiftType_Key_Start) {
return SYSTEM_KEY_NUM + chShift * (ch - 1);
} else if (type == ShiftType_RE_Start) {
return SYSTEM_KEY_NUM + chShift * (ch - 1) + keyShift;
} else {
dprintf("Invalid Shift Type: %d", type);
return 0;
}
}
uint16_t get_updated_keymap_shift(uint16_t keymapShift, uint16_t shiftValue) {
int prevAlign = keymapShift / MATRIX_COLS;
int nextAlign = (keymapShift + shiftValue - 1) / MATRIX_COLS;
if (prevAlign != nextAlign) {
// shiftValue = nextAlign * MATRIX_COLS - keymapShift;
keymapShift = nextAlign * MATRIX_COLS;
}
return keymapShift;
}
void do_scan(void) {
nDevices = 0;
dprintf("Scanning...\n");
// firstly, access to the I2C multiplexer
{
i2c_status_t error = i2c_ping_address(I2C_MULTIPX_ADDR << 1, MY_I2C_TIMEOUT);
if (error == I2C_STATUS_SUCCESS) {
dprintf(" MUX found at address 0x%02X\n", I2C_MULTIPX_ADDR);
} else {
dprintf(" MUX Unknown error (%u) at address 0x%02X\n", error, I2C_MULTIPX_ADDR);
}
}
for (uint8_t ch = 1; ch <= MAX_MEX_CH; ch++) {
change_channel(ch);
uint16_t keymapShift = get_keymap_shift(ch, ShiftType_Key_Start);
// detect PCA9557
for (uint8_t address = PCA9557_FROM_ADDR; address < PCA9557_END_ADDR; address++) {
i2c_status_t error = i2c_ping_address(address << 1, MY_I2C_TIMEOUT);
if (error == I2C_STATUS_SUCCESS) {
dprintf("[PCA9557_init] %d, 0x%X \n", ch, address);
Keys_Data data = init_PCA9557(address);
if (data.type == Type_Unknown) {
dprintf("[PCA9557_init] Failed: %d, 0x%X, type: %d \n", ch, address, data.type);
continue;
}
int shiftValue = 0;
if (data.type == Type_PCA9557_Keys4) {
shiftValue = 5; // Ideally, this value should be 4. However, for easy re-layout, use the same value as key5
} else if (data.type == Type_PCA9557_Keys5) {
shiftValue = 5;
} else {
dprintf("[PCA9557_init] type is not covered: %d \n", data.type);
}
keymapShift = get_updated_keymap_shift(keymapShift, shiftValue);
deviceList[nDevices] = (DeviceList){.ch = ch, .address = address, .type = data.type, .keymapShift = keymapShift};
keymapShift += shiftValue;
nDevices++;
} else {
dprintf(" Missing: (%u) at address 0x%02X\n", error, address);
}
}
// XL9555 devices can overwrite PCA9557 devices' results
keymapShift = get_keymap_shift(ch, ShiftType_Key_Start);
// detect XL9555
for (uint8_t address = XL9555_FROM_ADDR; address < XL9555_END_ADDR; address++) {
i2c_status_t error = i2c_ping_address(address << 1, MY_I2C_TIMEOUT);
if (error == I2C_STATUS_SUCCESS) {
dprintf("[XL9555_init] %d, 0x%X \n", ch, address);
Keys_Data data = init_XL9555(address);
if (data.type == Type_Unknown) {
dprintf("[XL9555_init] Failed: %d, 0x%X, type: %d \n", ch, address, data.type);
continue;
}
int shiftValue = 0;
if (data.type == Type_XL9555_Keys4) {
shiftValue = 5; // Ideally, this value should be 4, but for easy re-layout, use the same value as key5
} else if (data.type == Type_XL9555_Keys5) {
shiftValue = 5;
} else {
dprintf("[XL9555_init] type is not covered: %d \n", data.type);
}
keymapShift = get_updated_keymap_shift(keymapShift, shiftValue);
deviceList[nDevices] = (DeviceList){.ch = ch, .address = address, .type = data.type, .keymapShift = keymapShift};
keymapShift += shiftValue;
nDevices++;
} else {
dprintf(" Missing: (%u) at address 0x%02X\n", error, address);
}
}
// proceed to the begining of the position of rotary encoders
keymapShift = get_keymap_shift(ch, ShiftType_RE_Start);
// detect PCA9534A, rotary encoders
for (uint8_t address = PCA9534A_FROM_ADDR; address < PCA9534A_END_ADDR; address++) {
i2c_status_t error = i2c_ping_address(address << 1, MY_I2C_TIMEOUT);
if (error == I2C_STATUS_SUCCESS) {
dprintf("[PCA9534A_init] %d, 0x%X \n", ch, address);
Keys_Data data = init_PCA9534A(address);
if (data.type == Type_Unknown) {
dprintf("[PCA9534A_init] Failed: %d, 0x%X, type: %d \n", ch, address, data.type);
continue;
}
int shiftValue = 0;
if (data.type == Type_PCA9534A_RE_CLICKABLE) {
shiftValue = 1; // for click button
} else {
dprintf("[PCA9534A_init] type is not covered: %d \n", data.type);
}
keymapShift = get_updated_keymap_shift(keymapShift, shiftValue);
deviceList[nDevices] = (DeviceList){.ch = ch, .address = address, .type = data.type, .keymapShift = keymapShift};
keymapShift += shiftValue;
nDevices++;
} else {
dprintf(" Missing: (%u) at address 0x%02X\n", error, address);
}
}
}
if (nDevices == 0) {
dprintf("No I2C devices found\n");
} else {
dprintf("#devices: %d\n", nDevices);
for (uint16_t i = 0; i < nDevices; i++) {
dprintf("[I2C] %d, 0x%X \n", deviceList[i].ch, deviceList[i].address);
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <stdint.h>
#include <stdio.h>
#include "keycodes.h"
#include "debug.h"
// # of System Key
#define SYSTEM_KEY_NUM 10
// maximum number of modules per channel
#define MAX_KEY_PER_CH_NUM 8
#define MAX_RE_PER_CH_NUM 4
#define CH_BUFFER_NUM 16 // for future modules
// 0x18 is the beginning of PCA9557
#define PCA9557_FROM_ADDR 0x18
#define PCA9557_END_ADDR (0x18 + MAX_KEY_PER_CH_NUM)
// 0x20 is the beginning of XL9555/TCA9535
#define XL9555_FROM_ADDR 0x20
#define XL9555_END_ADDR (0x20 + MAX_KEY_PER_CH_NUM)
// 0x38 is the beginning of PCA9534A
// NOTICE: OLED resides on 0x3C, thus MAX_RE_PER_CH_NUM is 4 to skip this address.
// Additionally, on the hardware side, the A2 pin needs to be low.
#define PCA9534A_FROM_ADDR 0x38
#define PCA9534A_END_ADDR (PCA9534A_FROM_ADDR + MAX_RE_PER_CH_NUM)
#define I2C_MULTIPX_ADDR 0x70
#define MY_I2C_TIMEOUT 1
#define MAX_MEX_CH 4
#define MAX_MCP_NUM 60
typedef enum _Type {
Type_Unknown = 0,
Type_PCA9557_Keys4 = 1,
Type_PCA9557_Keys5 = 2,
Type_XL9555_Keys4 = 3,
Type_XL9555_Keys5 = 4,
Type_PCA9534A_RE_CLICKABLE = 5,
} Type;
typedef struct {
Type type;
uint8_t data[2]; // key input, joysticks, etc
} Keys_Data;
typedef struct {
Type type;
uint8_t ch;
uint8_t address;
uint16_t keymapShift;
} DeviceList;
bool change_channel(uint8_t channel);
Keys_Data read_PCA9557_register(uint8_t address);
Keys_Data read_XL9555_register(uint8_t address);
Keys_Data read_PCA9534A_register(uint8_t address);
void do_scan(void);

View File

@ -0,0 +1,392 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#include "kb_config.h"
void my_kb_config_init(kb_config_t* kb_config) {
kb_config->mouse_layer_on = 1;
kb_config->mouse_layer_off_delay_ms = 13;
kb_config->angle_L = 0;
kb_config->angle_R = 0;
kb_config->pointer_speed_magnification = 4;
kb_config->invert_drag_scroll_x = 0;
kb_config->invert_drag_scroll_y = 0;
kb_config->drag_scroll_speed_magnification = 4;
kb_config->trackball_led_off_timeout = 1;
kb_config->all_keys_are_mouse_keys = 0;
kb_config->_dummy_tb = 0;
kb_config->re_resolution_1 = 2;
kb_config->re_resolution_2 = 2;
kb_config->re_resolution_3 = 2;
kb_config->re_resolution_4 = 2;
kb_config->re_resolution_5 = 2;
kb_config->re_resolution_6 = 2;
kb_config->re_resolution_7 = 2;
kb_config->re_resolution_8 = 2;
kb_config->re_resolution_9 = 2;
kb_config->re_resolution_10 = 2;
kb_config->re_resolution_11 = 2;
kb_config->re_resolution_12 = 2;
kb_config->re_resolution_13 = 2;
kb_config->re_resolution_14 = 2;
kb_config->re_resolution_15 = 2;
kb_config->re_resolution_16 = 2;
}
void debug_output_kb_config(kb_config_t* kb_config) {
#ifdef CONSOLE_ENABLE
{
static uint32_t timer = 0;
if (timer_elapsed32(timer) > 1000) {
dprintf("=== kb_config ===\n");
dprintf("%s = %" PRIu32 "\n", "mouse_layer_on", (uint32_t)kb_config->mouse_layer_on);
dprintf("%s = %" PRIu32 "\n", "mouse_layer_off_delay_ms", (uint32_t)kb_config->mouse_layer_off_delay_ms);
dprintf("%s = %" PRIu32 "\n", "angle_L", (uint32_t)kb_config->angle_L);
dprintf("%s = %" PRIu32 "\n", "angle_R", (uint32_t)kb_config->angle_R);
dprintf("%s = %" PRIu32 "\n", "pointer_speed_magnification", (uint32_t)kb_config->pointer_speed_magnification);
dprintf("%s = %" PRIu32 "\n", "invert_drag_scroll_x", (uint32_t)kb_config->invert_drag_scroll_x);
dprintf("%s = %" PRIu32 "\n", "invert_drag_scroll_y", (uint32_t)kb_config->invert_drag_scroll_y);
dprintf("%s = %" PRIu32 "\n", "drag_scroll_speed_magnification", (uint32_t)kb_config->drag_scroll_speed_magnification);
dprintf("%s = %" PRIu32 "\n", "trackball_led_off_timeout", (uint32_t)kb_config->trackball_led_off_timeout);
dprintf("%s = %" PRIu32 "\n", "all_keys_are_mouse_keys", (uint32_t)kb_config->all_keys_are_mouse_keys);
dprintf("%s = %" PRIu32 "\n", "_dummy_tb", (uint32_t)kb_config->_dummy_tb);
dprintf("%s = %" PRIu32 "\n", "re_resolution_1", (uint32_t)kb_config->re_resolution_1);
dprintf("%s = %" PRIu32 "\n", "re_resolution_2", (uint32_t)kb_config->re_resolution_2);
dprintf("%s = %" PRIu32 "\n", "re_resolution_3", (uint32_t)kb_config->re_resolution_3);
dprintf("%s = %" PRIu32 "\n", "re_resolution_4", (uint32_t)kb_config->re_resolution_4);
dprintf("%s = %" PRIu32 "\n", "re_resolution_5", (uint32_t)kb_config->re_resolution_5);
dprintf("%s = %" PRIu32 "\n", "re_resolution_6", (uint32_t)kb_config->re_resolution_6);
dprintf("%s = %" PRIu32 "\n", "re_resolution_7", (uint32_t)kb_config->re_resolution_7);
dprintf("%s = %" PRIu32 "\n", "re_resolution_8", (uint32_t)kb_config->re_resolution_8);
dprintf("%s = %" PRIu32 "\n", "re_resolution_9", (uint32_t)kb_config->re_resolution_9);
dprintf("%s = %" PRIu32 "\n", "re_resolution_10", (uint32_t)kb_config->re_resolution_10);
dprintf("%s = %" PRIu32 "\n", "re_resolution_11", (uint32_t)kb_config->re_resolution_11);
dprintf("%s = %" PRIu32 "\n", "re_resolution_12", (uint32_t)kb_config->re_resolution_12);
dprintf("%s = %" PRIu32 "\n", "re_resolution_13", (uint32_t)kb_config->re_resolution_13);
dprintf("%s = %" PRIu32 "\n", "re_resolution_14", (uint32_t)kb_config->re_resolution_14);
dprintf("%s = %" PRIu32 "\n", "re_resolution_15", (uint32_t)kb_config->re_resolution_15);
dprintf("%s = %" PRIu32 "\n", "re_resolution_16", (uint32_t)kb_config->re_resolution_16);
dprintf("=== the end of kb_config ===\n");
timer = timer_read32();
}
}
#endif
}
bool process_kb_config_modification(kb_config_t* kb_config, uint16_t keycode, keyrecord_t* record) {
if (record->event.pressed) {
switch (keycode) {
case AUTO_MOUSE_LAYER_P1:
kb_config->mouse_layer_on = ((uint64_t)kb_config->mouse_layer_on + 2 + 1) % 2;
return false;
case AUTO_MOUSE_LAYER_OFF_DELAY_P1:
kb_config->mouse_layer_off_delay_ms = ((uint64_t)kb_config->mouse_layer_off_delay_ms + 64 + 1) % 64;
return false;
case AUTO_MOUSE_LAYER_OFF_DELAY_M1:
kb_config->mouse_layer_off_delay_ms = ((uint64_t)kb_config->mouse_layer_off_delay_ms + 64 - 1) % 64;
return false;
case ANGLE_L_ADJUSTMENT_P1:
kb_config->angle_L = ((uint64_t)kb_config->angle_L + 360 + 1) % 360;
return false;
case ANGLE_L_ADJUSTMENT_M1:
kb_config->angle_L = ((uint64_t)kb_config->angle_L + 360 - 1) % 360;
return false;
case ANGLE_L_ADJUSTMENT_P30:
kb_config->angle_L = ((uint64_t)kb_config->angle_L + 360 + 30) % 360;
return false;
case ANGLE_L_ADJUSTMENT_M30:
kb_config->angle_L = ((uint64_t)kb_config->angle_L + 360 - 30) % 360;
return false;
case ANGLE_R_ADJUSTMENT_P1:
kb_config->angle_R = ((uint64_t)kb_config->angle_R + 360 + 1) % 360;
return false;
case ANGLE_R_ADJUSTMENT_M1:
kb_config->angle_R = ((uint64_t)kb_config->angle_R + 360 - 1) % 360;
return false;
case ANGLE_R_ADJUSTMENT_P30:
kb_config->angle_R = ((uint64_t)kb_config->angle_R + 360 + 30) % 360;
return false;
case ANGLE_R_ADJUSTMENT_M30:
kb_config->angle_R = ((uint64_t)kb_config->angle_R + 360 - 30) % 360;
return false;
case POINTER_SPEED_MAGNIFICATION_P1:
kb_config->pointer_speed_magnification = ((uint64_t)kb_config->pointer_speed_magnification + 16 + 1) % 16;
return false;
case POINTER_SPEED_MAGNIFICATION_M1:
kb_config->pointer_speed_magnification = ((uint64_t)kb_config->pointer_speed_magnification + 16 - 1) % 16;
return false;
case INVERT_DRAG_SCROLL_X_P1:
kb_config->invert_drag_scroll_x = ((uint64_t)kb_config->invert_drag_scroll_x + 2 + 1) % 2;
return false;
case INVERT_DRAG_SCROLL_Y_P1:
kb_config->invert_drag_scroll_y = ((uint64_t)kb_config->invert_drag_scroll_y + 2 + 1) % 2;
return false;
case DRAG_SCROLL_SPEED_MAGNIFICATION_P1:
kb_config->drag_scroll_speed_magnification = ((uint64_t)kb_config->drag_scroll_speed_magnification + 16 + 1) % 16;
return false;
case DRAG_SCROLL_SPEED_MAGNIFICATION_M1:
kb_config->drag_scroll_speed_magnification = ((uint64_t)kb_config->drag_scroll_speed_magnification + 16 - 1) % 16;
return false;
case TRACKBALL_LED_OFF_TIMEOUT_P1:
kb_config->trackball_led_off_timeout = ((uint64_t)kb_config->trackball_led_off_timeout + 4 + 1) % 4;
return false;
case TRACKBALL_LED_OFF_TIMEOUT_M1:
kb_config->trackball_led_off_timeout = ((uint64_t)kb_config->trackball_led_off_timeout + 4 - 1) % 4;
return false;
case ALL_KEYS_ARE_MOUSE_KEYS_P1:
kb_config->all_keys_are_mouse_keys = ((uint64_t)kb_config->all_keys_are_mouse_keys + 2 + 1) % 2;
return false;
case ROTARY_ENCODER_1_RESOLUTION_P1:
kb_config->re_resolution_1 = ((uint64_t)kb_config->re_resolution_1 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_2_RESOLUTION_P1:
kb_config->re_resolution_2 = ((uint64_t)kb_config->re_resolution_2 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_3_RESOLUTION_P1:
kb_config->re_resolution_3 = ((uint64_t)kb_config->re_resolution_3 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_4_RESOLUTION_P1:
kb_config->re_resolution_4 = ((uint64_t)kb_config->re_resolution_4 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_5_RESOLUTION_P1:
kb_config->re_resolution_5 = ((uint64_t)kb_config->re_resolution_5 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_6_RESOLUTION_P1:
kb_config->re_resolution_6 = ((uint64_t)kb_config->re_resolution_6 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_7_RESOLUTION_P1:
kb_config->re_resolution_7 = ((uint64_t)kb_config->re_resolution_7 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_8_RESOLUTION_P1:
kb_config->re_resolution_8 = ((uint64_t)kb_config->re_resolution_8 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_9_RESOLUTION_P1:
kb_config->re_resolution_9 = ((uint64_t)kb_config->re_resolution_9 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_10_RESOLUTION_P1:
kb_config->re_resolution_10 = ((uint64_t)kb_config->re_resolution_10 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_11_RESOLUTION_P1:
kb_config->re_resolution_11 = ((uint64_t)kb_config->re_resolution_11 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_12_RESOLUTION_P1:
kb_config->re_resolution_12 = ((uint64_t)kb_config->re_resolution_12 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_13_RESOLUTION_P1:
kb_config->re_resolution_13 = ((uint64_t)kb_config->re_resolution_13 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_14_RESOLUTION_P1:
kb_config->re_resolution_14 = ((uint64_t)kb_config->re_resolution_14 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_15_RESOLUTION_P1:
kb_config->re_resolution_15 = ((uint64_t)kb_config->re_resolution_15 + 4 + 1) % 4;
return false;
case ROTARY_ENCODER_16_RESOLUTION_P1:
kb_config->re_resolution_16 = ((uint64_t)kb_config->re_resolution_16 + 4 + 1) % 4;
return false;
}
}
return true;
}
void oled_kb_config_output(kb_config_t* kb_config, uint8_t page) {
oled_write_P(PSTR("Config("), false);
static char page_str[4];
itoa(page + 1, page_str, 10);
oled_write_P(page_str, false);
oled_write_P(PSTR(") -- "), false);
switch (page) {
case 0: {
static char type_count_str[8];
oled_write_P(PSTR("mlo:"), false);
itoa(kb_config->mouse_layer_on, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("mod:"), false);
itoa(kb_config->mouse_layer_off_delay_ms, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("agl:"), false);
itoa(kb_config->angle_L, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("agr:"), false);
itoa(kb_config->angle_R, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("psm:"), false);
itoa(kb_config->pointer_speed_magnification, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("idsX:"), false);
itoa(kb_config->invert_drag_scroll_x, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
break;
case 1: {
static char type_count_str[8];
oled_write_P(PSTR("idsY:"), false);
itoa(kb_config->invert_drag_scroll_y, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("dsm:"), false);
itoa(kb_config->drag_scroll_speed_magnification, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("tlt:"), false);
itoa(kb_config->trackball_led_off_timeout, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("amk:"), false);
itoa(kb_config->all_keys_are_mouse_keys, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr1:"), false);
itoa(kb_config->re_resolution_1, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
break;
case 2: {
static char type_count_str[8];
oled_write_P(PSTR("rr2:"), false);
itoa(kb_config->re_resolution_2, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr3:"), false);
itoa(kb_config->re_resolution_3, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr4:"), false);
itoa(kb_config->re_resolution_4, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr5:"), false);
itoa(kb_config->re_resolution_5, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr6:"), false);
itoa(kb_config->re_resolution_6, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr7:"), false);
itoa(kb_config->re_resolution_7, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
break;
case 3: {
static char type_count_str[8];
oled_write_P(PSTR("rr8:"), false);
itoa(kb_config->re_resolution_8, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr9:"), false);
itoa(kb_config->re_resolution_9, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr10:"), false);
itoa(kb_config->re_resolution_10, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr11:"), false);
itoa(kb_config->re_resolution_11, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr12:"), false);
itoa(kb_config->re_resolution_12, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr13:"), false);
itoa(kb_config->re_resolution_13, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
break;
case 4: {
static char type_count_str[8];
oled_write_P(PSTR("rr14:"), false);
itoa(kb_config->re_resolution_14, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr15:"), false);
itoa(kb_config->re_resolution_15, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
{
static char type_count_str[8];
oled_write_P(PSTR("rr16:"), false);
itoa(kb_config->re_resolution_16, type_count_str, 10);
oled_write_P(type_count_str, false);
}
oled_write_P(PSTR(", "), false);
break;
default:
oled_write_ln_P(PSTR("no config"), false);
}
oled_write_ln_P("", false);
}

View File

@ -0,0 +1,146 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "quantum.h"
enum my_keycodes {
// Next OLED Page
NEXT_OLED_PAGE = QK_KB_0,
// drag scroll feature for trackball
DRAG_SCROLL,
// kb_config modification
AUTO_MOUSE_LAYER_P1,
AUTO_MOUSE_LAYER_OFF_DELAY_P1,
AUTO_MOUSE_LAYER_OFF_DELAY_M1,
ANGLE_L_ADJUSTMENT_P1,
ANGLE_L_ADJUSTMENT_M1,
ANGLE_L_ADJUSTMENT_P30,
ANGLE_L_ADJUSTMENT_M30,
ANGLE_R_ADJUSTMENT_P1,
ANGLE_R_ADJUSTMENT_M1,
ANGLE_R_ADJUSTMENT_P30,
ANGLE_R_ADJUSTMENT_M30,
POINTER_SPEED_MAGNIFICATION_P1,
POINTER_SPEED_MAGNIFICATION_M1,
INVERT_DRAG_SCROLL_X_P1,
INVERT_DRAG_SCROLL_Y_P1,
DRAG_SCROLL_SPEED_MAGNIFICATION_P1,
DRAG_SCROLL_SPEED_MAGNIFICATION_M1,
TRACKBALL_LED_OFF_TIMEOUT_P1,
TRACKBALL_LED_OFF_TIMEOUT_M1,
ALL_KEYS_ARE_MOUSE_KEYS_P1,
ROTARY_ENCODER_1_RESOLUTION_P1,
ROTARY_ENCODER_2_RESOLUTION_P1,
ROTARY_ENCODER_3_RESOLUTION_P1,
ROTARY_ENCODER_4_RESOLUTION_P1,
ROTARY_ENCODER_5_RESOLUTION_P1,
ROTARY_ENCODER_6_RESOLUTION_P1,
ROTARY_ENCODER_7_RESOLUTION_P1,
ROTARY_ENCODER_8_RESOLUTION_P1,
ROTARY_ENCODER_9_RESOLUTION_P1,
ROTARY_ENCODER_10_RESOLUTION_P1,
ROTARY_ENCODER_11_RESOLUTION_P1,
ROTARY_ENCODER_12_RESOLUTION_P1,
ROTARY_ENCODER_13_RESOLUTION_P1,
ROTARY_ENCODER_14_RESOLUTION_P1,
ROTARY_ENCODER_15_RESOLUTION_P1,
ROTARY_ENCODER_16_RESOLUTION_P1,
};
typedef union {
uint8_t raw[EECONFIG_KB_DATA_SIZE]; // 1KB
struct {
// Auto mouse layer (true/false)
// 2^1=2, default: 1, max: 1
uint32_t mouse_layer_on : 1;
// Auto mouse layer off delay time in ms (value * 100ms)
// 2^6=64, default: 13, max: 63
uint32_t mouse_layer_off_delay_ms : 6;
// Angle adjustment for trackballs (left)
// 2^9=512, default: 0, max: 359
uint32_t angle_L : 9;
// Angle adjustment for trackballs (right)
// 2^9=512, default: 0, max: 359
uint32_t angle_R : 9;
// Pointer speed magnification (value * 0.25)
// 2^4=16, default: 4, max: 15
uint32_t pointer_speed_magnification : 4;
// Invert drag scroll X-axis direction (true/false)
// 2^1=2, default: 0, max: 1
uint32_t invert_drag_scroll_x : 1;
// Invert drag scroll Y-axis direction (true/false)
// 2^1=2, default: 0, max: 1
uint32_t invert_drag_scroll_y : 1;
// Drag scroll speed magnification (value * 0.25)
// 2^4=16, default: 4, max: 15
uint32_t drag_scroll_speed_magnification : 4;
// Trackball LED off timeout (never, 5min, 10min, 15 min)
// 2^2=4, default: 1, max: 3
uint32_t trackball_led_off_timeout : 2;
// All keys are treated as mouse keys (true/false)
// 2^1=2, default: 0, max: 1
uint32_t all_keys_are_mouse_keys : 1;
// Dummy value
// 2^32=4294967296, default: 0, max: 0
uint32_t _dummy_tb : 32;
// Rotary encoder 1 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_1 : 2;
// Rotary encoder 2 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_2 : 2;
// Rotary encoder 3 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_3 : 2;
// Rotary encoder 4 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_4 : 2;
// Rotary encoder 5 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_5 : 2;
// Rotary encoder 6 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_6 : 2;
// Rotary encoder 7 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_7 : 2;
// Rotary encoder 8 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_8 : 2;
// Rotary encoder 9 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_9 : 2;
// Rotary encoder 10 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_10 : 2;
// Rotary encoder 11 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_11 : 2;
// Rotary encoder 12 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_12 : 2;
// Rotary encoder 13 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_13 : 2;
// Rotary encoder 14 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_14 : 2;
// Rotary encoder 15 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_15 : 2;
// Rotary encoder 16 resolution (restart needed to reflect)
// 2^2=4, default: 2, max: 3
uint32_t re_resolution_16 : 2;
};
} kb_config_t;
extern kb_config_t kb_config;
void my_kb_config_init(kb_config_t* kb_config);
void debug_output_kb_config(kb_config_t* kb_config);
bool process_kb_config_modification(kb_config_t* kb_config, uint16_t keycode, keyrecord_t* record);
void oled_kb_config_output(kb_config_t* kb_config, uint8_t page);

View File

@ -0,0 +1,533 @@
{
"manufacturer": "cue2keys",
"keyboard_name": "cue2keys",
"maintainer": "esplo",
"board": "GENERIC_RP_RP2040",
"bootloader": "rp2040",
"customKeycodes": [
{
"name": "NEXT_OLED_PAGE",
"shortName": "NEXT_OLED_PAGE",
"title": "Open next page on OLED display"
},
{
"name": "DRAG_SCROLL",
"shortName": "DRAG_SCROLL",
"title": "Change the pointer device movement into the mouse scroll"
},
{
"name": "AUTO_MOUSE_LAYER_P1",
"shortName": "mlo_P1",
"title": "Auto mouse layer (true/false)"
},
{
"name": "AUTO_MOUSE_LAYER_OFF_DELAY_P1",
"shortName": "mod_P1",
"title": "Auto mouse layer off delay time in ms (value * 100ms)"
},
{
"name": "AUTO_MOUSE_LAYER_OFF_DELAY_M1",
"shortName": "mod_M1",
"title": "Auto mouse layer off delay time in ms (value * 100ms)"
},
{
"name": "ANGLE_L_ADJUSTMENT_P1",
"shortName": "agl_P1",
"title": "Angle adjustment for trackballs (left)"
},
{
"name": "ANGLE_L_ADJUSTMENT_M1",
"shortName": "agl_M1",
"title": "Angle adjustment for trackballs (left)"
},
{
"name": "ANGLE_L_ADJUSTMENT_P30",
"shortName": "agl_P30",
"title": "Angle adjustment for trackballs (left)"
},
{
"name": "ANGLE_L_ADJUSTMENT_M30",
"shortName": "agl_M30",
"title": "Angle adjustment for trackballs (left)"
},
{
"name": "ANGLE_R_ADJUSTMENT_P1",
"shortName": "agr_P1",
"title": "Angle adjustment for trackballs (right)"
},
{
"name": "ANGLE_R_ADJUSTMENT_M1",
"shortName": "agr_M1",
"title": "Angle adjustment for trackballs (right)"
},
{
"name": "ANGLE_R_ADJUSTMENT_P30",
"shortName": "agr_P30",
"title": "Angle adjustment for trackballs (right)"
},
{
"name": "ANGLE_R_ADJUSTMENT_M30",
"shortName": "agr_M30",
"title": "Angle adjustment for trackballs (right)"
},
{
"name": "POINTER_SPEED_MAGNIFICATION_P1",
"shortName": "psm_P1",
"title": "Pointer speed magnification (value * 0.25)"
},
{
"name": "POINTER_SPEED_MAGNIFICATION_M1",
"shortName": "psm_M1",
"title": "Pointer speed magnification (value * 0.25)"
},
{
"name": "INVERT_DRAG_SCROLL_X_P1",
"shortName": "idsX_P1",
"title": "Invert drag scroll X-axis direction (true/false)"
},
{
"name": "INVERT_DRAG_SCROLL_Y_P1",
"shortName": "idsY_P1",
"title": "Invert drag scroll Y-axis direction (true/false)"
},
{
"name": "DRAG_SCROLL_SPEED_MAGNIFICATION_P1",
"shortName": "dsm_P1",
"title": "Drag scroll speed magnification (value * 0.25)"
},
{
"name": "DRAG_SCROLL_SPEED_MAGNIFICATION_M1",
"shortName": "dsm_M1",
"title": "Drag scroll speed magnification (value * 0.25)"
},
{
"name": "TRACKBALL_LED_OFF_TIMEOUT_P1",
"shortName": "tlt_P1",
"title": "Trackball LED off timeout (never, 5min, 10min, 15 min)"
},
{
"name": "TRACKBALL_LED_OFF_TIMEOUT_M1",
"shortName": "tlt_M1",
"title": "Trackball LED off timeout (never, 5min, 10min, 15 min)"
},
{
"name": "ALL_KEYS_ARE_MOUSE_KEYS_P1",
"shortName": "amk_P1",
"title": "All keys are treated as mouse keys (true/false)"
},
{
"name": "ROTARY_ENCODER_1_RESOLUTION_P1",
"shortName": "rr1_P1",
"title": "Rotary encoder 1 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_2_RESOLUTION_P1",
"shortName": "rr2_P1",
"title": "Rotary encoder 2 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_3_RESOLUTION_P1",
"shortName": "rr3_P1",
"title": "Rotary encoder 3 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_4_RESOLUTION_P1",
"shortName": "rr4_P1",
"title": "Rotary encoder 4 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_5_RESOLUTION_P1",
"shortName": "rr5_P1",
"title": "Rotary encoder 5 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_6_RESOLUTION_P1",
"shortName": "rr6_P1",
"title": "Rotary encoder 6 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_7_RESOLUTION_P1",
"shortName": "rr7_P1",
"title": "Rotary encoder 7 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_8_RESOLUTION_P1",
"shortName": "rr8_P1",
"title": "Rotary encoder 8 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_9_RESOLUTION_P1",
"shortName": "rr9_P1",
"title": "Rotary encoder 9 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_10_RESOLUTION_P1",
"shortName": "rr10_P1",
"title": "Rotary encoder 10 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_11_RESOLUTION_P1",
"shortName": "rr11_P1",
"title": "Rotary encoder 11 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_12_RESOLUTION_P1",
"shortName": "rr12_P1",
"title": "Rotary encoder 12 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_13_RESOLUTION_P1",
"shortName": "rr13_P1",
"title": "Rotary encoder 13 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_14_RESOLUTION_P1",
"shortName": "rr14_P1",
"title": "Rotary encoder 14 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_15_RESOLUTION_P1",
"shortName": "rr15_P1",
"title": "Rotary encoder 15 resolution (restart needed to reflect)"
},
{
"name": "ROTARY_ENCODER_16_RESOLUTION_P1",
"shortName": "rr16_P1",
"title": "Rotary encoder 16 resolution (restart needed to reflect)"
}
],
"dynamic_keymap": {
"layer_count": 8
},
"eeprom": {
"wear_leveling": {
"backing_size": 32768
}
},
"features": {
"bootmagic": true,
"command": false,
"console": true,
"encoder": true,
"extrakey": true,
"mousekey": true,
"nkro": true,
"oled": true
},
"matrix_pins": {
"direct": [
["GP9", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"],
["NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN", "NO_PIN"]
]
},
"processor": "RP2040",
"serial": {
"driver": "vendor"
},
"url": "https://cue2keys.esplo.net/",
"usb": {
"device_version": "1.0.0",
"pid": "0xC2C2",
"vid": "0xFEFD"
},
"layouts": {
"LAYOUT_5x4": {
"layout": [
{"label": "ch1_key(0,1)", "matrix": [1, 0], "x": 0, "y": 0},
{"label": "ch1_key(1,1)", "matrix": [1, 1], "x": 0, "y": 1},
{"label": "ch1_key(2,1)", "matrix": [1, 2], "x": 0, "y": 2},
{"label": "ch1_key(3,1)", "matrix": [1, 3], "x": 0, "y": 3},
{"label": "ch1_key(4,1)", "matrix": [1, 4], "x": 0, "y": 4},
{"label": "ch1_key(5,1)", "matrix": [1, 5], "x": 1, "y": 0},
{"label": "ch1_key(6,1)", "matrix": [1, 6], "x": 1, "y": 1},
{"label": "ch1_key(7,1)", "matrix": [1, 7], "x": 1, "y": 2},
{"label": "ch1_key(8,1)", "matrix": [1, 8], "x": 1, "y": 3},
{"label": "ch1_key(9,1)", "matrix": [1, 9], "x": 1, "y": 4},
{"label": "ch1_key(0,2)", "matrix": [2, 0], "x": 2, "y": 0},
{"label": "ch1_key(1,2)", "matrix": [2, 1], "x": 2, "y": 1},
{"label": "ch1_key(2,2)", "matrix": [2, 2], "x": 2, "y": 2},
{"label": "ch1_key(3,2)", "matrix": [2, 3], "x": 2, "y": 3},
{"label": "ch1_key(4,2)", "matrix": [2, 4], "x": 2, "y": 4},
{"label": "ch1_key(5,2)", "matrix": [2, 5], "x": 3, "y": 0},
{"label": "ch1_key(6,2)", "matrix": [2, 6], "x": 3, "y": 1},
{"label": "ch1_key(7,2)", "matrix": [2, 7], "x": 3, "y": 2},
{"label": "ch1_key(8,2)", "matrix": [2, 8], "x": 3, "y": 3},
{"label": "ch1_key(9,2)", "matrix": [2, 9], "x": 3, "y": 4}
]
},
"LAYOUT_all": {
"layout": [
{"label": "ch1_key(0,1)", "matrix": [1, 0], "x": 0, "y": 0},
{"label": "ch1_key(1,1)", "matrix": [1, 1], "x": 0, "y": 1},
{"label": "ch1_key(2,1)", "matrix": [1, 2], "x": 0, "y": 2},
{"label": "ch1_key(3,1)", "matrix": [1, 3], "x": 0, "y": 3},
{"label": "ch1_key(4,1)", "matrix": [1, 4], "x": 0, "y": 4},
{"label": "ch1_key(5,1)", "matrix": [1, 5], "x": 1, "y": 0},
{"label": "ch1_key(6,1)", "matrix": [1, 6], "x": 1, "y": 1},
{"label": "ch1_key(7,1)", "matrix": [1, 7], "x": 1, "y": 2},
{"label": "ch1_key(8,1)", "matrix": [1, 8], "x": 1, "y": 3},
{"label": "ch1_key(9,1)", "matrix": [1, 9], "x": 1, "y": 4},
{"label": "ch1_key(0,2)", "matrix": [2, 0], "x": 2, "y": 0},
{"label": "ch1_key(1,2)", "matrix": [2, 1], "x": 2, "y": 1},
{"label": "ch1_key(2,2)", "matrix": [2, 2], "x": 2, "y": 2},
{"label": "ch1_key(3,2)", "matrix": [2, 3], "x": 2, "y": 3},
{"label": "ch1_key(4,2)", "matrix": [2, 4], "x": 2, "y": 4},
{"label": "ch1_key(5,2)", "matrix": [2, 5], "x": 3, "y": 0},
{"label": "ch1_key(6,2)", "matrix": [2, 6], "x": 3, "y": 1},
{"label": "ch1_key(7,2)", "matrix": [2, 7], "x": 3, "y": 2},
{"label": "ch1_key(8,2)", "matrix": [2, 8], "x": 3, "y": 3},
{"label": "ch1_key(9,2)", "matrix": [2, 9], "x": 3, "y": 4},
{"label": "ch1_key(0,3)", "matrix": [3, 0], "x": 4, "y": 0},
{"label": "ch1_key(1,3)", "matrix": [3, 1], "x": 4, "y": 1},
{"label": "ch1_key(2,3)", "matrix": [3, 2], "x": 4, "y": 2},
{"label": "ch1_key(3,3)", "matrix": [3, 3], "x": 4, "y": 3},
{"label": "ch1_key(4,3)", "matrix": [3, 4], "x": 4, "y": 4},
{"label": "ch1_key(5,3)", "matrix": [3, 5], "x": 5, "y": 0},
{"label": "ch1_key(6,3)", "matrix": [3, 6], "x": 5, "y": 1},
{"label": "ch1_key(7,3)", "matrix": [3, 7], "x": 5, "y": 2},
{"label": "ch1_key(8,3)", "matrix": [3, 8], "x": 5, "y": 3},
{"label": "ch1_key(9,3)", "matrix": [3, 9], "x": 5, "y": 4},
{"label": "ch1_key(0,4)", "matrix": [4, 0], "x": 6, "y": 0},
{"label": "ch1_key(1,4)", "matrix": [4, 1], "x": 6, "y": 1},
{"label": "ch1_key(2,4)", "matrix": [4, 2], "x": 6, "y": 2},
{"label": "ch1_key(3,4)", "matrix": [4, 3], "x": 6, "y": 3},
{"label": "ch1_key(4,4)", "matrix": [4, 4], "x": 6, "y": 4},
{"label": "ch1_key(5,4)", "matrix": [4, 5], "x": 7, "y": 0},
{"label": "ch1_key(6,4)", "matrix": [4, 6], "x": 7, "y": 1},
{"label": "ch1_key(7,4)", "matrix": [4, 7], "x": 7, "y": 2},
{"label": "ch1_key(8,4)", "matrix": [4, 8], "x": 7, "y": 3},
{"label": "ch1_key(9,4)", "matrix": [4, 9], "x": 7, "y": 4},
{"label": "e1", "matrix": [5, 0], "x": 8, "y": 0, "encoder": 0},
{"label": "e2", "matrix": [5, 1], "x": 8, "y": 1, "encoder": 1},
{"label": "e3", "matrix": [5, 2], "x": 8, "y": 2, "encoder": 2},
{"label": "e4", "matrix": [5, 3], "x": 8, "y": 3, "encoder": 3},
{"label": "ch2_key(0,7)", "matrix": [7, 0], "x": 12, "y": 0},
{"label": "ch2_key(1,7)", "matrix": [7, 1], "x": 12, "y": 1},
{"label": "ch2_key(2,7)", "matrix": [7, 2], "x": 12, "y": 2},
{"label": "ch2_key(3,7)", "matrix": [7, 3], "x": 12, "y": 3},
{"label": "ch2_key(4,7)", "matrix": [7, 4], "x": 12, "y": 4},
{"label": "ch2_key(5,7)", "matrix": [7, 5], "x": 13, "y": 0},
{"label": "ch2_key(6,7)", "matrix": [7, 6], "x": 13, "y": 1},
{"label": "ch2_key(7,7)", "matrix": [7, 7], "x": 13, "y": 2},
{"label": "ch2_key(8,7)", "matrix": [7, 8], "x": 13, "y": 3},
{"label": "ch2_key(9,7)", "matrix": [7, 9], "x": 13, "y": 4},
{"label": "ch2_key(0,8)", "matrix": [8, 0], "x": 14, "y": 0},
{"label": "ch2_key(1,8)", "matrix": [8, 1], "x": 14, "y": 1},
{"label": "ch2_key(2,8)", "matrix": [8, 2], "x": 14, "y": 2},
{"label": "ch2_key(3,8)", "matrix": [8, 3], "x": 14, "y": 3},
{"label": "ch2_key(4,8)", "matrix": [8, 4], "x": 14, "y": 4},
{"label": "ch2_key(5,8)", "matrix": [8, 5], "x": 15, "y": 0},
{"label": "ch2_key(6,8)", "matrix": [8, 6], "x": 15, "y": 1},
{"label": "ch2_key(7,8)", "matrix": [8, 7], "x": 15, "y": 2},
{"label": "ch2_key(8,8)", "matrix": [8, 8], "x": 15, "y": 3},
{"label": "ch2_key(9,8)", "matrix": [8, 9], "x": 15, "y": 4},
{"label": "ch2_key(0,9)", "matrix": [9, 0], "x": 16, "y": 0},
{"label": "ch2_key(1,9)", "matrix": [9, 1], "x": 16, "y": 1},
{"label": "ch2_key(2,9)", "matrix": [9, 2], "x": 16, "y": 2},
{"label": "ch2_key(3,9)", "matrix": [9, 3], "x": 16, "y": 3},
{"label": "ch2_key(4,9)", "matrix": [9, 4], "x": 16, "y": 4},
{"label": "ch2_key(5,9)", "matrix": [9, 5], "x": 17, "y": 0},
{"label": "ch2_key(6,9)", "matrix": [9, 6], "x": 17, "y": 1},
{"label": "ch2_key(7,9)", "matrix": [9, 7], "x": 17, "y": 2},
{"label": "ch2_key(8,9)", "matrix": [9, 8], "x": 17, "y": 3},
{"label": "ch2_key(9,9)", "matrix": [9, 9], "x": 17, "y": 4},
{"label": "ch2_key(0,10)", "matrix": [10, 0], "x": 18, "y": 0},
{"label": "ch2_key(1,10)", "matrix": [10, 1], "x": 18, "y": 1},
{"label": "ch2_key(2,10)", "matrix": [10, 2], "x": 18, "y": 2},
{"label": "ch2_key(3,10)", "matrix": [10, 3], "x": 18, "y": 3},
{"label": "ch2_key(4,10)", "matrix": [10, 4], "x": 18, "y": 4},
{"label": "ch2_key(5,10)", "matrix": [10, 5], "x": 19, "y": 0},
{"label": "ch2_key(6,10)", "matrix": [10, 6], "x": 19, "y": 1},
{"label": "ch2_key(7,10)", "matrix": [10, 7], "x": 19, "y": 2},
{"label": "ch2_key(8,10)", "matrix": [10, 8], "x": 19, "y": 3},
{"label": "ch2_key(9,10)", "matrix": [10, 9], "x": 19, "y": 4},
{"label": "e5", "matrix": [11, 0], "x": 20, "y": 0, "encoder": 4},
{"label": "e6", "matrix": [11, 1], "x": 20, "y": 1, "encoder": 5},
{"label": "e7", "matrix": [11, 2], "x": 20, "y": 2, "encoder": 6},
{"label": "e8", "matrix": [11, 3], "x": 20, "y": 3, "encoder": 7},
{"label": "ch3_key(0,13)", "matrix": [13, 0], "x": 24, "y": 0},
{"label": "ch3_key(1,13)", "matrix": [13, 1], "x": 24, "y": 1},
{"label": "ch3_key(2,13)", "matrix": [13, 2], "x": 24, "y": 2},
{"label": "ch3_key(3,13)", "matrix": [13, 3], "x": 24, "y": 3},
{"label": "ch3_key(4,13)", "matrix": [13, 4], "x": 24, "y": 4},
{"label": "ch3_key(5,13)", "matrix": [13, 5], "x": 25, "y": 0},
{"label": "ch3_key(6,13)", "matrix": [13, 6], "x": 25, "y": 1},
{"label": "ch3_key(7,13)", "matrix": [13, 7], "x": 25, "y": 2},
{"label": "ch3_key(8,13)", "matrix": [13, 8], "x": 25, "y": 3},
{"label": "ch3_key(9,13)", "matrix": [13, 9], "x": 25, "y": 4},
{"label": "ch3_key(0,14)", "matrix": [14, 0], "x": 26, "y": 0},
{"label": "ch3_key(1,14)", "matrix": [14, 1], "x": 26, "y": 1},
{"label": "ch3_key(2,14)", "matrix": [14, 2], "x": 26, "y": 2},
{"label": "ch3_key(3,14)", "matrix": [14, 3], "x": 26, "y": 3},
{"label": "ch3_key(4,14)", "matrix": [14, 4], "x": 26, "y": 4},
{"label": "ch3_key(5,14)", "matrix": [14, 5], "x": 27, "y": 0},
{"label": "ch3_key(6,14)", "matrix": [14, 6], "x": 27, "y": 1},
{"label": "ch3_key(7,14)", "matrix": [14, 7], "x": 27, "y": 2},
{"label": "ch3_key(8,14)", "matrix": [14, 8], "x": 27, "y": 3},
{"label": "ch3_key(9,14)", "matrix": [14, 9], "x": 27, "y": 4},
{"label": "ch3_key(0,15)", "matrix": [15, 0], "x": 28, "y": 0},
{"label": "ch3_key(1,15)", "matrix": [15, 1], "x": 28, "y": 1},
{"label": "ch3_key(2,15)", "matrix": [15, 2], "x": 28, "y": 2},
{"label": "ch3_key(3,15)", "matrix": [15, 3], "x": 28, "y": 3},
{"label": "ch3_key(4,15)", "matrix": [15, 4], "x": 28, "y": 4},
{"label": "ch3_key(5,15)", "matrix": [15, 5], "x": 29, "y": 0},
{"label": "ch3_key(6,15)", "matrix": [15, 6], "x": 29, "y": 1},
{"label": "ch3_key(7,15)", "matrix": [15, 7], "x": 29, "y": 2},
{"label": "ch3_key(8,15)", "matrix": [15, 8], "x": 29, "y": 3},
{"label": "ch3_key(9,15)", "matrix": [15, 9], "x": 29, "y": 4},
{"label": "ch3_key(0,16)", "matrix": [16, 0], "x": 30, "y": 0},
{"label": "ch3_key(1,16)", "matrix": [16, 1], "x": 30, "y": 1},
{"label": "ch3_key(2,16)", "matrix": [16, 2], "x": 30, "y": 2},
{"label": "ch3_key(3,16)", "matrix": [16, 3], "x": 30, "y": 3},
{"label": "ch3_key(4,16)", "matrix": [16, 4], "x": 30, "y": 4},
{"label": "ch3_key(5,16)", "matrix": [16, 5], "x": 31, "y": 0},
{"label": "ch3_key(6,16)", "matrix": [16, 6], "x": 31, "y": 1},
{"label": "ch3_key(7,16)", "matrix": [16, 7], "x": 31, "y": 2},
{"label": "ch3_key(8,16)", "matrix": [16, 8], "x": 31, "y": 3},
{"label": "ch3_key(9,16)", "matrix": [16, 9], "x": 31, "y": 4},
{"label": "e9", "matrix": [17, 0], "x": 32, "y": 0, "encoder": 8},
{"label": "e10", "matrix": [17, 1], "x": 32, "y": 1, "encoder": 9},
{"label": "e11", "matrix": [17, 2], "x": 32, "y": 2, "encoder": 10},
{"label": "e12", "matrix": [17, 3], "x": 32, "y": 3, "encoder": 11},
{"label": "ch4_key(0,19)", "matrix": [19, 0], "x": 36, "y": 0},
{"label": "ch4_key(1,19)", "matrix": [19, 1], "x": 36, "y": 1},
{"label": "ch4_key(2,19)", "matrix": [19, 2], "x": 36, "y": 2},
{"label": "ch4_key(3,19)", "matrix": [19, 3], "x": 36, "y": 3},
{"label": "ch4_key(4,19)", "matrix": [19, 4], "x": 36, "y": 4},
{"label": "ch4_key(5,19)", "matrix": [19, 5], "x": 37, "y": 0},
{"label": "ch4_key(6,19)", "matrix": [19, 6], "x": 37, "y": 1},
{"label": "ch4_key(7,19)", "matrix": [19, 7], "x": 37, "y": 2},
{"label": "ch4_key(8,19)", "matrix": [19, 8], "x": 37, "y": 3},
{"label": "ch4_key(9,19)", "matrix": [19, 9], "x": 37, "y": 4},
{"label": "ch4_key(0,20)", "matrix": [20, 0], "x": 38, "y": 0},
{"label": "ch4_key(1,20)", "matrix": [20, 1], "x": 38, "y": 1},
{"label": "ch4_key(2,20)", "matrix": [20, 2], "x": 38, "y": 2},
{"label": "ch4_key(3,20)", "matrix": [20, 3], "x": 38, "y": 3},
{"label": "ch4_key(4,20)", "matrix": [20, 4], "x": 38, "y": 4},
{"label": "ch4_key(5,20)", "matrix": [20, 5], "x": 39, "y": 0},
{"label": "ch4_key(6,20)", "matrix": [20, 6], "x": 39, "y": 1},
{"label": "ch4_key(7,20)", "matrix": [20, 7], "x": 39, "y": 2},
{"label": "ch4_key(8,20)", "matrix": [20, 8], "x": 39, "y": 3},
{"label": "ch4_key(9,20)", "matrix": [20, 9], "x": 39, "y": 4},
{"label": "ch4_key(0,21)", "matrix": [21, 0], "x": 40, "y": 0},
{"label": "ch4_key(1,21)", "matrix": [21, 1], "x": 40, "y": 1},
{"label": "ch4_key(2,21)", "matrix": [21, 2], "x": 40, "y": 2},
{"label": "ch4_key(3,21)", "matrix": [21, 3], "x": 40, "y": 3},
{"label": "ch4_key(4,21)", "matrix": [21, 4], "x": 40, "y": 4},
{"label": "ch4_key(5,21)", "matrix": [21, 5], "x": 41, "y": 0},
{"label": "ch4_key(6,21)", "matrix": [21, 6], "x": 41, "y": 1},
{"label": "ch4_key(7,21)", "matrix": [21, 7], "x": 41, "y": 2},
{"label": "ch4_key(8,21)", "matrix": [21, 8], "x": 41, "y": 3},
{"label": "ch4_key(9,21)", "matrix": [21, 9], "x": 41, "y": 4},
{"label": "ch4_key(0,22)", "matrix": [22, 0], "x": 42, "y": 0},
{"label": "ch4_key(1,22)", "matrix": [22, 1], "x": 42, "y": 1},
{"label": "ch4_key(2,22)", "matrix": [22, 2], "x": 42, "y": 2},
{"label": "ch4_key(3,22)", "matrix": [22, 3], "x": 42, "y": 3},
{"label": "ch4_key(4,22)", "matrix": [22, 4], "x": 42, "y": 4},
{"label": "ch4_key(5,22)", "matrix": [22, 5], "x": 43, "y": 0},
{"label": "ch4_key(6,22)", "matrix": [22, 6], "x": 43, "y": 1},
{"label": "ch4_key(7,22)", "matrix": [22, 7], "x": 43, "y": 2},
{"label": "ch4_key(8,22)", "matrix": [22, 8], "x": 43, "y": 3},
{"label": "ch4_key(9,22)", "matrix": [22, 9], "x": 43, "y": 4},
{"label": "e13", "matrix": [23, 0], "x": 44, "y": 0, "encoder": 12},
{"label": "e14", "matrix": [23, 1], "x": 44, "y": 1, "encoder": 13},
{"label": "e15", "matrix": [23, 2], "x": 44, "y": 2, "encoder": 14},
{"label": "e16", "matrix": [23, 3], "x": 44, "y": 3, "encoder": 15}
]
},
"LAYOUT_basic": {
"layout": [
{"label": "ch1_key(0,1)", "matrix": [1, 0], "x": 0, "y": 0},
{"label": "ch1_key(1,1)", "matrix": [1, 1], "x": 0, "y": 1},
{"label": "ch1_key(2,1)", "matrix": [1, 2], "x": 0, "y": 2},
{"label": "ch1_key(3,1)", "matrix": [1, 3], "x": 0, "y": 3},
{"label": "ch1_key(4,1)", "matrix": [1, 4], "x": 0, "y": 4},
{"label": "ch1_key(5,1)", "matrix": [1, 5], "x": 1, "y": 0},
{"label": "ch1_key(6,1)", "matrix": [1, 6], "x": 1, "y": 1},
{"label": "ch1_key(7,1)", "matrix": [1, 7], "x": 1, "y": 2},
{"label": "ch1_key(8,1)", "matrix": [1, 8], "x": 1, "y": 3},
{"label": "ch1_key(9,1)", "matrix": [1, 9], "x": 1, "y": 4},
{"label": "ch1_key(0,2)", "matrix": [2, 0], "x": 2, "y": 0},
{"label": "ch1_key(1,2)", "matrix": [2, 1], "x": 2, "y": 1},
{"label": "ch1_key(2,2)", "matrix": [2, 2], "x": 2, "y": 2},
{"label": "ch1_key(3,2)", "matrix": [2, 3], "x": 2, "y": 3},
{"label": "ch1_key(4,2)", "matrix": [2, 4], "x": 2, "y": 4},
{"label": "ch1_key(5,2)", "matrix": [2, 5], "x": 3, "y": 0},
{"label": "ch1_key(6,2)", "matrix": [2, 6], "x": 3, "y": 1},
{"label": "ch1_key(7,2)", "matrix": [2, 7], "x": 3, "y": 2},
{"label": "ch1_key(8,2)", "matrix": [2, 8], "x": 3, "y": 3},
{"label": "ch1_key(9,2)", "matrix": [2, 9], "x": 3, "y": 4},
{"label": "ch1_key(0,3)", "matrix": [3, 0], "x": 4, "y": 0},
{"label": "ch1_key(1,3)", "matrix": [3, 1], "x": 4, "y": 1},
{"label": "ch1_key(2,3)", "matrix": [3, 2], "x": 4, "y": 2},
{"label": "ch1_key(3,3)", "matrix": [3, 3], "x": 4, "y": 3},
{"label": "ch1_key(4,3)", "matrix": [3, 4], "x": 4, "y": 4},
{"label": "ch1_key(5,3)", "matrix": [3, 5], "x": 5, "y": 0},
{"label": "ch1_key(6,3)", "matrix": [3, 6], "x": 5, "y": 1},
{"label": "ch1_key(7,3)", "matrix": [3, 7], "x": 5, "y": 2},
{"label": "ch1_key(8,3)", "matrix": [3, 8], "x": 5, "y": 3},
{"label": "ch1_key(0,4)", "matrix": [4, 0], "x": 6, "y": 0},
{"label": "ch1_key(1,4)", "matrix": [4, 1], "x": 6, "y": 1},
{"label": "ch1_key(2,4)", "matrix": [4, 2], "x": 6, "y": 2},
{"label": "ch1_key(3,4)", "matrix": [4, 3], "x": 6, "y": 3},
{"label": "e1", "matrix": [5, 0], "x": 8, "y": 0, "encoder": 0},
{"label": "ch2_key(0,7)", "matrix": [7, 0], "x": 12, "y": 0},
{"label": "ch2_key(1,7)", "matrix": [7, 1], "x": 12, "y": 1},
{"label": "ch2_key(2,7)", "matrix": [7, 2], "x": 12, "y": 2},
{"label": "ch2_key(3,7)", "matrix": [7, 3], "x": 12, "y": 3},
{"label": "ch2_key(5,7)", "matrix": [7, 5], "x": 13, "y": 0},
{"label": "ch2_key(6,7)", "matrix": [7, 6], "x": 13, "y": 1},
{"label": "ch2_key(7,7)", "matrix": [7, 7], "x": 13, "y": 2},
{"label": "ch2_key(8,7)", "matrix": [7, 8], "x": 13, "y": 3},
{"label": "ch2_key(0,8)", "matrix": [8, 0], "x": 14, "y": 0},
{"label": "ch2_key(1,8)", "matrix": [8, 1], "x": 14, "y": 1},
{"label": "ch2_key(2,8)", "matrix": [8, 2], "x": 14, "y": 2},
{"label": "ch2_key(3,8)", "matrix": [8, 3], "x": 14, "y": 3},
{"label": "ch2_key(4,8)", "matrix": [8, 4], "x": 14, "y": 4},
{"label": "ch2_key(5,8)", "matrix": [8, 5], "x": 15, "y": 0},
{"label": "ch2_key(6,8)", "matrix": [8, 6], "x": 15, "y": 1},
{"label": "ch2_key(7,8)", "matrix": [8, 7], "x": 15, "y": 2},
{"label": "ch2_key(8,8)", "matrix": [8, 8], "x": 15, "y": 3},
{"label": "ch2_key(9,8)", "matrix": [8, 9], "x": 15, "y": 4},
{"label": "ch2_key(0,9)", "matrix": [9, 0], "x": 16, "y": 0},
{"label": "ch2_key(1,9)", "matrix": [9, 1], "x": 16, "y": 1},
{"label": "ch2_key(2,9)", "matrix": [9, 2], "x": 16, "y": 2},
{"label": "ch2_key(3,9)", "matrix": [9, 3], "x": 16, "y": 3},
{"label": "ch2_key(4,9)", "matrix": [9, 4], "x": 16, "y": 4},
{"label": "ch2_key(5,9)", "matrix": [9, 5], "x": 17, "y": 0},
{"label": "ch2_key(6,9)", "matrix": [9, 6], "x": 17, "y": 1},
{"label": "ch2_key(7,9)", "matrix": [9, 7], "x": 17, "y": 2},
{"label": "ch2_key(8,9)", "matrix": [9, 8], "x": 17, "y": 3},
{"label": "ch2_key(9,9)", "matrix": [9, 9], "x": 17, "y": 4},
{"label": "ch2_key(0,10)", "matrix": [10, 0], "x": 18, "y": 0},
{"label": "ch2_key(1,10)", "matrix": [10, 1], "x": 18, "y": 1},
{"label": "ch2_key(2,10)", "matrix": [10, 2], "x": 18, "y": 2},
{"label": "ch2_key(3,10)", "matrix": [10, 3], "x": 18, "y": 3},
{"label": "ch2_key(4,10)", "matrix": [10, 4], "x": 18, "y": 4}
]
}
}
}

View File

@ -0,0 +1,327 @@
// Copyright 2022 QMK
// SPDX-License-Identifier: GPL-3.0-or-later
#include QMK_KEYBOARD_H
#include <stdint.h>
enum layer_names { _BASE, _NUM, _FN, _MOUSE };
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_BASE] = LAYOUT_all(
// clang-format off
// channel 1
//// keys * 8
KC_ESC, KC_TAB, KC_LEFT_CTRL, KC_LEFT_SHIFT, LT(_NUM, KC_GRAVE),
KC_1, KC_Q, KC_A, KC_Z, KC_LEFT_PAREN,
KC_2,KC_W, KC_S, KC_X, KC_RIGHT_PAREN,
KC_3, KC_E, KC_D, KC_C, KC_LEFT_ALT,
KC_4, KC_R, KC_F, KC_V, LGUI_T(KC_LANGUAGE_2),
KC_5, KC_T, KC_G, KC_B, KC_NO,
KC_SPACE, KC_BACKSPACE, _______, _______, KC_NO,
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
//// rotary encoders * 4
KC_TAB, KC_TAB, KC_TAB, KC_TAB,
// channel 2
//// keys * 8
KC_ENTER, KC_TAB, _______,LCTL(KC_LEFT_BRACKET), KC_NO,
KC_6, KC_Y, KC_H, KC_N, KC_NO,
KC_7,KC_U, KC_J, KC_M, KC_LANGUAGE_1,
KC_8, KC_I, KC_K, KC_COMMA, _______,
KC_9, KC_O, KC_L, KC_DOT, KC_LEFT_BRACKET,
KC_0, KC_P, KC_SEMICOLON, KC_SLASH, KC_RIGHT_BRACKET,
KC_MINUS, KC_BACKSLASH, KC_QUOTE, _______, KC_EQUAL,
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
//// rotary encoders * 4
KC_TAB, KC_TAB, KC_TAB, KC_TAB,
// channel 3
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, KC_7, KC_4, KC_1, _______,
_______, KC_8, KC_5, KC_2, KC_0,
_______, KC_9, KC_6, KC_3, _______,
_______, KC_MINUS, KC_PPLS, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 4
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, QK_MOUSE_WHEEL_UP, _______, _______,
_______, _______, QK_MOUSE_WHEEL_DOWN, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______
// clang-format on
),
[_NUM] = LAYOUT_all(
// clang-format off
// channel 1
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, KC_EXCLAIM, KC_CIRCUMFLEX, MO(_FN),
_______, _______, KC_AT, KC_AMPERSAND, _______,
_______, _______, KC_HASH, KC_ASTERISK, _______,
_______, _______, KC_DOLLAR, _______, _______,
_______, _______, KC_PERCENT, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 2
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, KC_7, KC_4, KC_1, _______,
_______, KC_8, KC_5, KC_2, KC_0,
_______, KC_9, KC_6, KC_3, KC_LEFT,
_______, KC_MINUS, KC_PPLS, KC_UP, KC_DOWN,
_______, _______, _______, _______, KC_RIGHT,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 3
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 4
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______
// clang-format on
),
[_FN] = LAYOUT_all(
// clang-format off
// channel 1
//// keys * 8
QK_BOOT, QK_CLEAR_EEPROM, QK_REBOOT, _______, _______,
QK_KB_15, QK_KB_10, QK_KB_5, QK_KB_0, _______,
QK_KB_16, QK_KB_11, QK_KB_6, QK_KB_1, _______,
QK_KB_17, QK_KB_12, QK_KB_7, QK_KB_2, _______,
QK_KB_18, QK_KB_13, QK_KB_8, QK_KB_3, _______,
QK_KB_19, QK_KB_14, QK_KB_9, QK_KB_4, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 2
//// keys * 8
_______, _______, _______, _______, _______,
QK_KB_20, _______, _______, _______, _______,
QK_KB_21, KC_F7, KC_F4, KC_F1, _______,
QK_KB_22, KC_F8, KC_F5, KC_F2, KC_F10,
QK_KB_23, KC_F9, KC_F6, KC_F3, _______,
QK_KB_24, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 3
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 4
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______
// clang-format on
),
[_MOUSE] = LAYOUT_all(
// clang-format off
// channel 1
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, DRAG_SCROLL, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, QK_MOUSE_WHEEL_UP, _______, _______,
_______, _______, QK_MOUSE_WHEEL_DOWN, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 2
//// keys * 8
QK_MOUSE_BUTTON_4, QK_MOUSE_BUTTON_3, QK_MOUSE_BUTTON_2, QK_MOUSE_BUTTON_1, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 3
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______,
// channel 4
//// keys * 8
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
//// rotary encoders * 4
_______, _______, _______, _______
// clang-format on
),
};
#ifdef ENCODER_MAP_ENABLE
const uint16_t PROGMEM
encoder_map[][NUM_ENCODERS]
[NUM_DIRECTIONS] = {
[_BASE] =
{
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
},
[_NUM] =
{
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
},
[_FN] =
{
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
},
[_MOUSE] =
{
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
},
};
#endif
#ifdef POINTING_DEVICE_ENABLE
void pointing_device_init_user(void) {
set_auto_mouse_layer(_MOUSE);
}
#endif

View File

@ -0,0 +1 @@
ENCODER_MAP_ENABLE = yes

View File

@ -0,0 +1,20 @@
// Copyright 2022 QMK
// SPDX-License-Identifier: GPL-3.0-or-later
#include QMK_KEYBOARD_H
#include <stdint.h>
enum layer_names { _BASE };
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {[_BASE] = LAYOUT_5x4(
// clang-format off
KC_MINUS, KC_PLUS, KC_PLUS, KC_ENTER, KC_ENTER,
KC_ASTERISK, KC_9, KC_6, KC_3, KC_0,
KC_SLASH, KC_8, KC_5, KC_2, KC_0,
KC_NUM_LOCK, KC_7, KC_4, KC_1, KC_DEL
// clang-format on
)};
#ifdef ENCODER_MAP_ENABLE
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {[_BASE] = {}};
#endif

View File

@ -0,0 +1 @@
ENCODER_MAP_ENABLE = yes

View File

@ -0,0 +1,211 @@
// Copyright 2022 QMK
// SPDX-License-Identifier: GPL-3.0-or-later
#include QMK_KEYBOARD_H
#include <stdint.h>
enum layer_names { _BASE, _NUM, _FN, _MOUSE };
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_BASE] = LAYOUT_basic(
// clang-format off
// channel 1
//// key5 * 5, key4 * 2
KC_ESC, KC_TAB, KC_LEFT_CTRL, KC_LEFT_SHIFT, LT(_NUM, KC_GRAVE),
KC_1, KC_Q, KC_A, KC_Z, KC_LEFT_PAREN,
KC_2,KC_W, KC_S, KC_X, KC_RIGHT_PAREN,
KC_3, KC_E, KC_D, KC_C, KC_LEFT_ALT,
KC_4, KC_R, KC_F, KC_V, LGUI_T(KC_LANGUAGE_2),
KC_5, KC_T, KC_G, KC_B,
KC_SPACE, KC_BACKSPACE, _______, _______,
//// rotary encoders * 1
KC_TAB,
// channel 2
//// key4 * 2, key5 * 5
KC_ENTER, KC_TAB, _______, LCTL(KC_LEFT_BRACKET),
KC_6, KC_Y, KC_H, KC_N,
KC_7, KC_U, KC_J, KC_M, KC_LANGUAGE_1,
KC_8, KC_I, KC_K, KC_COMMA, _______,
KC_9, KC_O, KC_L, KC_DOT, KC_LEFT_BRACKET,
KC_0, KC_P, KC_SEMICOLON, KC_SLASH, KC_RIGHT_BRACKET,
KC_MINUS, KC_BACKSLASH, KC_QUOTE, _______, KC_EQUAL
// clang-format on
),
[_NUM] = LAYOUT_basic(
// clang-format off
// channel 1
//// key5 * 5, key4 * 2
_______, _______, _______, _______, _______,
_______, _______, KC_EXCLAIM, KC_CIRCUMFLEX, MO(_FN),
_______, _______, KC_AT, KC_AMPERSAND, _______,
_______, _______, KC_HASH, KC_ASTERISK, _______,
_______, _______, KC_DOLLAR, _______, _______,
_______, _______, KC_PERCENT, _______,
_______, _______, _______, _______,
//// rotary encoders * 1
_______,
// channel 2
//// key4 * 2, key5 * 5
_______, _______, _______, _______,
_______, _______, _______, _______,
_______, KC_7, KC_4, KC_1, _______,
_______, KC_8, KC_5, KC_2, KC_0,
_______, KC_9, KC_6, KC_3, KC_LEFT,
_______, KC_MINUS, KC_PPLS, KC_UP, KC_DOWN,
_______, _______, _______, _______, KC_RIGHT
// clang-format on
),
[_FN] = LAYOUT_basic(
// clang-format off
// channel 1
//// key5 * 5, key4 * 2
QK_BOOT, QK_CLEAR_EEPROM, QK_REBOOT, _______, _______,
QK_KB_15, QK_KB_10, QK_KB_5, QK_KB_0, _______,
QK_KB_16, QK_KB_11, QK_KB_6, QK_KB_1, _______,
QK_KB_17, QK_KB_12, QK_KB_7, QK_KB_2, _______,
QK_KB_18, QK_KB_13, QK_KB_8, QK_KB_3, _______,
QK_KB_19, QK_KB_14, QK_KB_9, QK_KB_4,
_______, _______, _______, _______,
//// rotary encoders * 1
_______,
// channel 2
//// key4 * 2, key5 * 5
_______, _______, _______, _______,
QK_KB_20, _______, _______, _______,
QK_KB_21, KC_F7, KC_F4, KC_F1, _______,
QK_KB_22, KC_F8, KC_F5, KC_F2, KC_F10,
QK_KB_23, KC_F9, KC_F6, KC_F3, _______,
QK_KB_24, _______, _______, _______, _______,
_______, _______, _______, _______, _______
// clang-format on
),
[_MOUSE] = LAYOUT_basic(
// clang-format off
// channel 1
//// key5 * 5, key4 * 2
_______, _______, _______, _______, _______,
_______, _______, DRAG_SCROLL, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, QK_MOUSE_WHEEL_UP, _______, _______,
_______, _______, QK_MOUSE_WHEEL_DOWN, _______, _______,
_______, _______, _______, _______,
_______, _______, _______, _______,
//// rotary encoders * 1
_______,
// channel 2
//// key4 * 2, key5 * 5
QK_MOUSE_BUTTON_4, QK_MOUSE_BUTTON_3, QK_MOUSE_BUTTON_2, QK_MOUSE_BUTTON_1,
_______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______,
_______, _______, _______, _______, _______
// clang-format on
),
};
#ifdef ENCODER_MAP_ENABLE
const uint16_t PROGMEM
encoder_map[][NUM_ENCODERS]
[NUM_DIRECTIONS] = {
[_BASE] =
{
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
ENCODER_CCW_CW(KC_PGDN, KC_PGUP),
},
[_NUM] =
{
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
},
[_FN] =
{
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
},
[_MOUSE] =
{
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
ENCODER_CCW_CW(_______, _______),
},
};
#endif
#ifdef POINTING_DEVICE_ENABLE
void pointing_device_init_user(void) {
set_auto_mouse_layer(_MOUSE);
}
#endif

View File

@ -0,0 +1,16 @@
// Copyright 2025 esplo
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include_next <mcuconf.h>
#define RP2040_MCUCONF
#undef RP_I2C_USE_I2C0
#define RP_I2C_USE_I2C0 FALSE
#undef RP_I2C_USE_I2C1
#define RP_I2C_USE_I2C1 TRUE
#define RP_I2C_BUSY_TIMEOUT 50
#define RP_I2C_ADDRESS_MODE_10BIT FALSE

View File

@ -0,0 +1,27 @@
# cue2keys
![cue2keys](https://i.imgur.com/hUCVD23.jpeg)
A keyboard that achieves flexible placement and configuration by combining modules as building blocks.
- Keyboard Maintainer: [esplo](https://github.com/esplo)
- Hardware Supported: Cue2Keys PCB
- Hardware Availability: [official website](https://cue2keys.esplo.net/), [online store](https://c2k.booth.pm/items/6659174)
Make example for this keyboard (after setting up your build environment):
make cue2keys:default
Flashing example for this keyboard:
make cue2keys:default:flash
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).
## Bootloader
Enter the bootloader in 3 ways:
- **Physical reset button (connected)**: Briefly double press the RST button on the PCB
- **Physical reset button (disconnected)**: press and hold the BS button on the PCB, then connect this to the PC
- **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available

View File

@ -0,0 +1,10 @@
I2C_DRIVER_REQUIRED = yes
LIB_SRC += i2clib.c
LIB_SRC += kb_config.c
POINTING_DEVICE_ENABLE = yes
POINTING_DEVICE_DRIVER = custom
LIB_SRC += drivers/modular_adns5050.c
LIB_SRC += drivers/encoder_dynamic_res.c