@ -0,0 +1 @@ | |||
/.ccls-cache |
@ -0,0 +1,169 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#ifndef CONFIG_H | |||
#define CONFIG_H | |||
#include "config_common.h" | |||
/* USB Device descriptor parameter */ | |||
#define VENDOR_ID 0xFEED | |||
#define PRODUCT_ID 0x6060 | |||
#define DEVICE_VER 0x0001 | |||
#define MANUFACTURER Andrew Dunai | |||
#define PRODUCT Endgame48 (v2) | |||
#define DESCRIPTION A 12x4 split ortholinear keyboard with encoder | |||
/* | |||
┌────────┐ | |||
╭──────┤ ├──────╮ | |||
RGB> ┿ D3 │ │ RAW ┿ | |||
│ │ │ │ | |||
PS2D> ┿ D2 └────────┘ GND ┿ | |||
│ │ | |||
┿ GND RST ┿ | |||
│ [] [] │ | |||
┿ GND VCC ┿ | |||
│ │ | |||
I2C> ┿ D1 ||| || || F4 ┿ <ROW5 | |||
│ │ | |||
I2C> ┿ D0 F5 ┿ <COL1 | |||
│ ╱╲ │ | |||
ROW1> ┿ D4 ╱ ╲ F6 ┿ <COL2 | |||
│ ╱ ╲ │ | |||
ROW3> ┿ C6 ╲ ╱ F7 ┿ <COL3 | |||
│ ╲ ╱ │ | |||
COL7> ┿ D7 ╲╱ B1 ┿ <COL4 | |||
│ │ | |||
ENCA> ┿ E6 B3 ┿ <COL5 | |||
│ │ | |||
ENCB> ┿ B4 B2 ┿ <COL6 | |||
│ │ | |||
ROW2> │ B5 B7 D5 C7 F1 F0 B6 ┿ <ROW6 | |||
╰────╂──╂──╂──╂──╂─────╯ | |||
┃ ┃ ┃ ┃ | |||
ROW4 ┛ ┃ ┃ ┗ ROW8 | |||
┃ ┃ | |||
PS2C ┛ ┗ ROW7 | |||
*/ | |||
/* key matrix size */ | |||
#define MATRIX_ROWS 8 | |||
#define MATRIX_COLS 7 | |||
/* key matrix pins */ | |||
#define MATRIX_ROW_PINS { \ | |||
D4, B5, C6, B7, \ | |||
F4, B6, F1, F0 \ | |||
} | |||
#define MATRIX_COL_PINS { \ | |||
F5, F6, F7, B1, B3, B2, \ | |||
D7 \ | |||
} | |||
#define UNUSED_PINS | |||
/* COL2ROW or ROW2COL */ | |||
#define DIODE_DIRECTION ROW2COL | |||
/* Set 0 if debouncing isn't needed */ | |||
#define DEBOUNCE 20 | |||
/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */ | |||
#define LOCKING_SUPPORT_ENABLE | |||
/* Locking resynchronize hack */ | |||
#define LOCKING_RESYNC_ENABLE | |||
/* key combination for command */ | |||
#define IS_COMMAND() ( \ | |||
keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \ | |||
) | |||
/* prevent stuck modifiers */ | |||
#define PREVENT_STUCK_MODIFIERS | |||
/* RGB underglow */ | |||
#define RGB_DI_PIN D3 | |||
#ifdef RGB_DI_PIN | |||
#define RGBLED_NUM 12 | |||
#define RGBLIGHT_HUE_STEP 8 | |||
#define RGBLIGHT_SAT_STEP 8 | |||
#define RGBLIGHT_VAL_STEP 8 | |||
#define RGBLIGHT_DEFAULT_VAL 0 | |||
#endif | |||
/* Encoders */ | |||
#define ENCODERS_PAD_A { E6 } | |||
#define ENCODERS_PAD_B { B4 } | |||
#define ENCODER_RESOLUTION 4 | |||
/* Tapping config */ | |||
#define TAPPING_TERM 90 | |||
#define PERMISSIVE_HOLD | |||
#define TAPPING_FORCE_HOLD | |||
/* Reduce firmware size */ | |||
#define NO_ACTION_ONESHOT | |||
/* Font */ | |||
#define OLED_FONT_H "glcdfont_48_v2.c" | |||
/* Trackpoint */ | |||
#define PS2_MOUSE_INIT_DELAY 200 | |||
#ifdef PS2_USE_USART | |||
#define PS2_CLOCK_PIN D5 | |||
#define PS2_DATA_PIN D2 | |||
/* synchronous, odd parity, 1-bit stop, 8-bit data, sample at falling edge */ | |||
/* set DDR of CLOCK as input to be slave */ | |||
#define PS2_USART_INIT() do { \ | |||
PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT); \ | |||
PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT); \ | |||
UCSR1C = ((1 << UMSEL10) | \ | |||
(3 << UPM10) | \ | |||
(0 << USBS1) | \ | |||
(3 << UCSZ10) | \ | |||
(0 << UCPOL1)); \ | |||
UCSR1A = 0; \ | |||
UBRR1H = 0; \ | |||
UBRR1L = 0; \ | |||
} while (0) | |||
#define PS2_USART_RX_INT_ON() do { \ | |||
UCSR1B = ((1 << RXCIE1) | \ | |||
(1 << RXEN1)); \ | |||
} while (0) | |||
#define PS2_USART_RX_POLL_ON() do { \ | |||
UCSR1B = (1 << RXEN1); \ | |||
} while (0) | |||
#define PS2_USART_OFF() do { \ | |||
UCSR1C = 0; \ | |||
UCSR1B &= ~((1 << RXEN1) | \ | |||
(1 << TXEN1)); \ | |||
} while (0) | |||
#define PS2_USART_RX_READY (UCSR1A & (1<<RXC1)) | |||
#define PS2_USART_RX_DATA UDR1 | |||
#define PS2_USART_ERROR (UCSR1A & ((1<<FE1) | (1<<DOR1) | (1<<UPE1))) | |||
#define PS2_USART_RX_VECT USART1_RX_vect | |||
#endif | |||
#ifdef PS2_USE_INT | |||
/* Note: pins are swapped, so re-soldering is needed to test both modes */ | |||
#define PS2_CLOCK_PIN D2 | |||
#define PS2_DATA_PIN D5 | |||
#define PS2_INT_INIT() do { \ | |||
EICRA |= ((1<<ISC21) | \ | |||
(0<<ISC20)); \ | |||
} while (0) | |||
#define PS2_INT_ON() do { \ | |||
EIMSK |= (1<<INT2); \ | |||
} while (0) | |||
#define PS2_INT_OFF() do { \ | |||
EIMSK &= ~(1<<INT2); \ | |||
} while (0) | |||
#define PS2_INT_VECT INT2_vect | |||
#endif | |||
#endif |
@ -0,0 +1,110 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "endgame48.h" | |||
#include "quantum.h" | |||
#include "raw_hid.h" | |||
#include "bitwise.h" | |||
#include "oled/oled_driver.h" | |||
#include "symbols.h" | |||
#include "string.h" | |||
#include "stdio.h" | |||
#include "eeprom.h" | |||
#include "encoder.h" | |||
#include "menu.h" | |||
#include "rgblight.h" | |||
volatile static uint32_t reset_requested = 0; | |||
void oled_paint(void); | |||
void request_reset(void) { | |||
reset_requested = timer_read32(); | |||
oled_clear(); | |||
oled_write_P(logo, false); | |||
oled_write_P(resetIcon, false); | |||
oled_write("\nREADY\n FOR \nFLASH", false); | |||
for (int i = 0; i < 16; i++) { | |||
oled_render(); | |||
} | |||
rgblight_setrgb((DEBUG_COLOR >> 16) & 0xFF, (DEBUG_COLOR >> 8) & 0xFF, DEBUG_COLOR & 0xFF); | |||
reset_keyboard(); | |||
} | |||
void mute(void) { | |||
tap_code(KC_MUTE); | |||
} | |||
void init_eeprom_menu(menu_t *this) { | |||
for (uint16_t i = 0; i < this->count; i++) { | |||
uint8_t b = eeprom_read_byte((uint8_t *)i); | |||
sprintf(this->items[i].title, "%02X:%02X", i, b); | |||
} | |||
} | |||
static menu_t list_eeprom_menu = { | |||
.title = "EEPRM", | |||
.count = 64, | |||
.items = (menu_item_t[64]){}, | |||
.opened = init_eeprom_menu, | |||
}; | |||
menu_t main_menu = { | |||
.title = "CFG", | |||
.count = 5, | |||
.items = | |||
(menu_item_t[]){ | |||
{ | |||
.title = "Mute", | |||
.func = mute, | |||
}, | |||
{ | |||
.title = "RGB", | |||
.func = rgblight_toggle_noeeprom, | |||
}, | |||
{ | |||
.title = "Reset", | |||
.func = request_reset, | |||
}, | |||
{ | |||
.title = "LsEE", | |||
.submenu = &list_eeprom_menu, | |||
}, | |||
{ | |||
.title = "DelEE", | |||
.func = eeconfig_init, | |||
}, | |||
}, | |||
}; | |||
bool process_record_kb(uint16_t keycode, keyrecord_t *record) { | |||
if (!menu_process_record(keycode, record)) { | |||
return false; | |||
} | |||
return process_record_user(keycode, record); | |||
} | |||
bool encoder_update_kb(uint8_t index, bool clockwise) { | |||
if (!menu_encoder_update(index, clockwise)) { | |||
return false; | |||
} | |||
return encoder_update_user(index, clockwise); | |||
} | |||
oled_rotation_t oled_init_kb(oled_rotation_t rotation) { | |||
return OLED_ROTATION_270; | |||
} | |||
static uint32_t last_render = 0; | |||
bool oled_task_kb(void) { | |||
if (timer_elapsed32(last_render) > 100) { | |||
last_render = timer_read32(); | |||
if (!menu_oled_task()) { | |||
return false; | |||
} | |||
oled_task_user(); | |||
} | |||
return false; | |||
} |
@ -0,0 +1,37 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#ifndef ENDGAME48_H | |||
#define ENDGAME48_H | |||
#include "quantum.h" | |||
#include "config.h" | |||
#define DEBUG_COLOR 0xFFFF00 | |||
#define NOPE KC_NO | |||
enum eg_custom_keycodes { | |||
KC_EGMENU = SAFE_RANGE, | |||
EG_SAFE_RANGE, | |||
}; | |||
#define LAYOUT( \ | |||
K00, K01, K02, K03, K04, K05, K40, K41, K42, K43, K44, K45, \ | |||
K10, K11, K12, K13, K14, K15, K50, K51, K52, K53, K54, K55, \ | |||
K20, K21, K22, K23, K24, K25, K60, K61, K62, K63, K64, K65, \ | |||
K30, K31, K32, K33, K34, K35, K76, K70, K71, K72, K73, K74, K75 \ | |||
) { \ | |||
{K00, K01, K02, K03, K04, K05, NOPE}, \ | |||
{K10, K11, K12, K13, K14, K15, NOPE}, \ | |||
{K20, K21, K22, K23, K24, K25, NOPE}, \ | |||
{K30, K31, K32, K33, K34, K35, NOPE}, \ | |||
{K40, K41, K42, K43, K44, K45, NOPE}, \ | |||
{K50, K51, K52, K53, K54, K55, NOPE}, \ | |||
{K60, K61, K62, K63, K64, K65, NOPE}, \ | |||
{K70, K71, K72, K73, K74, K75, K76} \ | |||
} | |||
void request_reset(void); | |||
#endif |
@ -0,0 +1,240 @@ | |||
#pragma once | |||
#ifdef __AVR__ | |||
#include <avr/io.h> | |||
#include <avr/pgmspace.h> | |||
#elif defined(ESP8266) | |||
#include <pgmspace.h> | |||
#else | |||
#define PROGMEM | |||
#endif | |||
// Helidox 8x6 font with QMK Firmware Logo | |||
// Online editor: http://teripom.x0.com/ | |||
// New online editor: https://joric.github.io/qle/ | |||
static const unsigned char PROGMEM font[] = { | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x00, | |||
0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x00, | |||
0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x00, | |||
0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x00, | |||
0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x00, | |||
0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00, | |||
0x00, 0x00, 0x08, 0x08, 0x00, 0x00, | |||
0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, | |||
0x00, 0x18, 0x24, 0x18, 0x00, 0x00, | |||
0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x00, | |||
0x30, 0x48, 0x3A, 0x06, 0x0E, 0x00, | |||
0x26, 0x29, 0x79, 0x29, 0x26, 0x00, | |||
0x40, 0x7F, 0x05, 0x05, 0x07, 0x00, | |||
0x40, 0x7F, 0x05, 0x25, 0x3F, 0x00, | |||
0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x00, | |||
0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x00, | |||
0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x00, | |||
0x14, 0x22, 0x7F, 0x22, 0x14, 0x00, | |||
0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x00, | |||
0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, | |||
0x00, 0x66, 0x89, 0x95, 0x6A, 0x00, | |||
0x60, 0x60, 0x60, 0x60, 0x60, 0x00, | |||
0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x00, | |||
0x08, 0x04, 0x7E, 0x04, 0x08, 0x00, | |||
0x10, 0x20, 0x7E, 0x20, 0x10, 0x00, | |||
0x08, 0x08, 0x2A, 0x1C, 0x08, 0x00, | |||
0x08, 0x1C, 0x2A, 0x08, 0x08, 0x00, | |||
0x1E, 0x10, 0x10, 0x10, 0x10, 0x00, | |||
0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x00, | |||
0x30, 0x38, 0x3E, 0x38, 0x30, 0x00, | |||
0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, | |||
0x00, 0x07, 0x00, 0x07, 0x00, 0x00, | |||
0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, | |||
0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00, | |||
0x23, 0x13, 0x08, 0x64, 0x62, 0x00, | |||
0x36, 0x49, 0x56, 0x20, 0x50, 0x00, | |||
0x00, 0x08, 0x07, 0x03, 0x00, 0x00, | |||
0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, | |||
0x00, 0x41, 0x22, 0x1C, 0x00, 0x00, | |||
0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00, | |||
0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, | |||
0x00, 0x80, 0x70, 0x30, 0x00, 0x00, | |||
0x08, 0x08, 0x08, 0x08, 0x08, 0x00, | |||
0x00, 0x00, 0x60, 0x60, 0x00, 0x00, | |||
0x20, 0x10, 0x08, 0x04, 0x02, 0x00, | |||
0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, | |||
0x00, 0x42, 0x7F, 0x40, 0x00, 0x00, | |||
0x72, 0x49, 0x49, 0x49, 0x46, 0x00, | |||
0x21, 0x41, 0x49, 0x4D, 0x33, 0x00, | |||
0x18, 0x14, 0x12, 0x7F, 0x10, 0x00, | |||
0x27, 0x45, 0x45, 0x45, 0x39, 0x00, | |||
0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00, | |||
0x41, 0x21, 0x11, 0x09, 0x07, 0x00, | |||
0x36, 0x49, 0x49, 0x49, 0x36, 0x00, | |||
0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, | |||
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, | |||
0x00, 0x40, 0x34, 0x00, 0x00, 0x00, | |||
0x00, 0x08, 0x14, 0x22, 0x41, 0x00, | |||
0x14, 0x14, 0x14, 0x14, 0x14, 0x00, | |||
0x00, 0x41, 0x22, 0x14, 0x08, 0x00, | |||
0x02, 0x01, 0x59, 0x09, 0x06, 0x00, | |||
0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00, | |||
0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00, | |||
0x7F, 0x49, 0x49, 0x49, 0x36, 0x00, | |||
0x3E, 0x41, 0x41, 0x41, 0x22, 0x00, | |||
0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00, | |||
0x7F, 0x49, 0x49, 0x49, 0x41, 0x00, | |||
0x7F, 0x09, 0x09, 0x09, 0x01, 0x00, | |||
0x3E, 0x41, 0x41, 0x51, 0x73, 0x00, | |||
0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, | |||
0x00, 0x41, 0x7F, 0x41, 0x00, 0x00, | |||
0x20, 0x40, 0x41, 0x3F, 0x01, 0x00, | |||
0x7F, 0x08, 0x14, 0x22, 0x41, 0x00, | |||
0x7F, 0x40, 0x40, 0x40, 0x40, 0x00, | |||
0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00, | |||
0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00, | |||
0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, | |||
0x7F, 0x09, 0x09, 0x09, 0x06, 0x00, | |||
0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00, | |||
0x7F, 0x09, 0x19, 0x29, 0x46, 0x00, | |||
0x26, 0x49, 0x49, 0x49, 0x32, 0x00, | |||
0x03, 0x01, 0x7F, 0x01, 0x03, 0x00, | |||
0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00, | |||
0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00, | |||
0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00, | |||
0x63, 0x14, 0x08, 0x14, 0x63, 0x00, | |||
0x03, 0x04, 0x78, 0x04, 0x03, 0x00, | |||
0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, | |||
0x00, 0x7F, 0x41, 0x41, 0x41, 0x00, | |||
0x02, 0x04, 0x08, 0x10, 0x20, 0x00, | |||
0x00, 0x41, 0x41, 0x41, 0x7F, 0x00, | |||
0x04, 0x02, 0x01, 0x02, 0x04, 0x00, | |||
0x40, 0x40, 0x40, 0x40, 0x40, 0x00, | |||
0x00, 0x03, 0x07, 0x08, 0x00, 0x00, | |||
0x20, 0x54, 0x54, 0x78, 0x40, 0x00, | |||
0x7F, 0x28, 0x44, 0x44, 0x38, 0x00, | |||
0x38, 0x44, 0x44, 0x44, 0x28, 0x00, | |||
0x38, 0x44, 0x44, 0x28, 0x7F, 0x00, | |||
0x38, 0x54, 0x54, 0x54, 0x18, 0x00, | |||
0x00, 0x08, 0x7E, 0x09, 0x02, 0x00, | |||
0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x00, | |||
0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, | |||
0x00, 0x44, 0x7D, 0x40, 0x00, 0x00, | |||
0x20, 0x40, 0x40, 0x3D, 0x00, 0x00, | |||
0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, | |||
0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, | |||
0x7C, 0x04, 0x78, 0x04, 0x78, 0x00, | |||
0x7C, 0x08, 0x04, 0x04, 0x78, 0x00, | |||
0x38, 0x44, 0x44, 0x44, 0x38, 0x00, | |||
0xFC, 0x18, 0x24, 0x24, 0x18, 0x00, | |||
0x18, 0x24, 0x24, 0x18, 0xFC, 0x00, | |||
0x7C, 0x08, 0x04, 0x04, 0x08, 0x00, | |||
0x48, 0x54, 0x54, 0x54, 0x24, 0x00, | |||
0x04, 0x04, 0x3F, 0x44, 0x24, 0x00, | |||
0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00, | |||
0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00, | |||
0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00, | |||
0x44, 0x28, 0x10, 0x28, 0x44, 0x00, | |||
0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00, | |||
0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, | |||
0x00, 0x08, 0x36, 0x41, 0x00, 0x00, | |||
0x00, 0x00, 0x77, 0x00, 0x00, 0x00, | |||
0x00, 0x41, 0x36, 0x08, 0x00, 0x00, | |||
0x02, 0x01, 0x02, 0x04, 0x02, 0x00, | |||
0x3C, 0x26, 0x23, 0x26, 0x3C, 0x00, | |||
0x00, 0xF0, 0xFC, 0xFE, 0xFE, 0x3F, | |||
0x1F, 0x0F, 0x0F, 0x0F, 0x0F, 0x9F, | |||
0xFF, 0xFE, 0xFE, 0xFC, 0xF0, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, | |||
0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, | |||
0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, | |||
0x07, 0x07, 0x07, 0x07, 0x07, 0x0F, | |||
0x1F, 0xFE, 0xFE, 0xF8, 0x00, 0x00, | |||
0x00, 0xF0, 0xFC, 0xFE, 0xFE, 0x3F, | |||
0x1F, 0x0F, 0x0F, 0x0F, 0x0F, 0x1F, | |||
0x3F, 0xFE, 0xFE, 0xFC, 0xF0, 0x00, | |||
0x03, 0x0F, 0x3F, 0xFF, 0xFF, 0xFC, | |||
0xF0, 0xC0, 0x00, 0x00, 0xC0, 0xF0, | |||
0xFC, 0xFF, 0xFF, 0x3F, 0x0F, 0x03, | |||
0x3E, 0x2A, 0x22, 0x00, 0x3E, 0x0C, | |||
0x18, 0x3E, 0x00, 0x3E, 0x22, 0x1C, | |||
0x00, 0x1C, 0x22, 0x32, 0x00, 0x3C, | |||
0x0A, 0x3C, 0x00, 0x3E, 0x04, 0x08, | |||
0x04, 0x3E, 0x00, 0x3E, 0x2A, 0x22, | |||
0x00, 0x3E, 0x22, 0x22, 0x00, 0x02, | |||
0x3E, 0x02, 0x00, 0x3E, 0x20, 0x20, | |||
0x00, 0x3C, 0x0A, 0x3C, 0x00, 0x3E, | |||
0x20, 0x20, 0x02, 0x3E, 0x02, 0x00, | |||
0x00, 0x24, 0x2A, 0x12, 0x00, 0x3E, | |||
0x0A, 0x02, 0x00, 0x02, 0x3E, 0x02, | |||
0x00, 0x3E, 0x22, 0x32, 0x00, 0x3E, | |||
0x20, 0x3E, 0x00, 0x3E, 0x00, 0x00, | |||
0x00, 0x00, 0x08, 0x08, 0x08, 0x08, | |||
0x08, 0x08, 0x08, 0x08, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, | |||
0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, | |||
0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, | |||
0x00, 0x00, 0xC0, 0xF8, 0xFF, 0xFF, | |||
0xFF, 0xE7, 0xE0, 0xE0, 0xE7, 0xFF, | |||
0xFF, 0xFF, 0xF8, 0xC0, 0x00, 0x00, | |||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, | |||
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x7E, | |||
0xFF, 0xE7, 0xE7, 0x81, 0x00, 0x00, | |||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0xC3, 0xFF, | |||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |||
0xFF, 0xC3, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x07, 0x04, 0x1F, 0x00, 0x1F, 0x15, | |||
0x1F, 0x00, 0x00, 0x00, 0x00, 0x0C, | |||
0x10, 0x0C, 0x00, 0x1D, 0x15, 0x17, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x0F, 0x3F, 0x7F, 0x7F, 0xFF, | |||
0xF9, 0xF0, 0xF0, 0xF0, 0xF0, 0xF8, | |||
0xFC, 0x7F, 0x7F, 0x3F, 0x0F, 0x00, | |||
0xF0, 0xFE, 0xFF, 0xFF, 0x0F, 0x01, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x01, 0x0F, 0xFF, 0xFF, 0xFE, 0xF0, | |||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, | |||
0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, | |||
0xF8, 0x7F, 0x7F, 0x1F, 0x00, 0x00, | |||
0x00, 0x0F, 0x3F, 0x7F, 0x7F, 0xFC, | |||
0xF8, 0xF0, 0xF0, 0xF0, 0xF0, 0xF8, | |||
0xFC, 0x7F, 0x7F, 0x3F, 0x0F, 0x00, | |||
0xC0, 0xF0, 0xFC, 0xFF, 0xFF, 0x3F, | |||
0x0F, 0x03, 0x00, 0x00, 0x03, 0x0F, | |||
0x3F, 0xFF, 0xFF, 0xFC, 0xF0, 0xC0, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |||
}; |
@ -0,0 +1,60 @@ | |||
{ | |||
"keyboard_name": "Endgame48", | |||
"url": "", | |||
"maintainer": "and3rson", | |||
"layouts": { | |||
"LAYOUT": { | |||
"layout": [ | |||
{ "w": 1, "x": 0, "y": 0 }, | |||
{ "w": 1, "x": 1, "y": 0 }, | |||
{ "w": 1, "x": 2, "y": 0 }, | |||
{ "w": 1, "x": 3, "y": 0 }, | |||
{ "w": 1, "x": 4, "y": 0 }, | |||
{ "w": 1, "x": 5, "y": 0 }, | |||
{ "w": 1, "x": 7, "y": 0 }, | |||
{ "w": 1, "x": 8, "y": 0 }, | |||
{ "w": 1, "x": 9, "y": 0 }, | |||
{ "w": 1, "x": 10, "y": 0 }, | |||
{ "w": 1, "x": 11, "y": 0 }, | |||
{ "w": 1, "x": 12, "y": 0 }, | |||
{ "w": 1, "x": 0, "y": 1 }, | |||
{ "w": 1, "x": 1, "y": 1 }, | |||
{ "w": 1, "x": 2, "y": 1 }, | |||
{ "w": 1, "x": 3, "y": 1 }, | |||
{ "w": 1, "x": 4, "y": 1 }, | |||
{ "w": 1, "x": 5, "y": 1 }, | |||
{ "w": 1, "x": 7, "y": 1 }, | |||
{ "w": 1, "x": 8, "y": 1 }, | |||
{ "w": 1, "x": 9, "y": 1 }, | |||
{ "w": 1, "x": 10, "y": 1 }, | |||
{ "w": 1, "x": 11, "y": 1 }, | |||
{ "w": 1, "x": 12, "y": 1 }, | |||
{ "w": 1, "x": 0, "y": 2 }, | |||
{ "w": 1, "x": 1, "y": 2 }, | |||
{ "w": 1, "x": 2, "y": 2 }, | |||
{ "w": 1, "x": 3, "y": 2 }, | |||
{ "w": 1, "x": 4, "y": 2 }, | |||
{ "w": 1, "x": 5, "y": 2 }, | |||
{ "w": 1, "x": 7, "y": 2 }, | |||
{ "w": 1, "x": 8, "y": 2 }, | |||
{ "w": 1, "x": 9, "y": 2 }, | |||
{ "w": 1, "x": 10, "y": 2 }, | |||
{ "w": 1, "x": 11, "y": 2 }, | |||
{ "w": 1, "x": 12, "y": 2 }, | |||
{ "w": 1, "x": 0, "y": 3 }, | |||
{ "w": 1, "x": 1, "y": 3 }, | |||
{ "w": 1, "x": 2, "y": 3 }, | |||
{ "w": 1, "x": 3, "y": 3 }, | |||
{ "w": 1, "x": 4, "y": 3 }, | |||
{ "w": 1, "x": 5, "y": 3 }, | |||
{ "w": 1, "x": 6, "y": 3 }, | |||
{ "w": 1, "x": 7, "y": 3 }, | |||
{ "w": 1, "x": 8, "y": 3 }, | |||
{ "w": 1, "x": 9, "y": 3 }, | |||
{ "w": 1, "x": 10, "y": 3 }, | |||
{ "w": 1, "x": 11, "y": 3 }, | |||
{ "w": 1, "x": 12, "y": 3 } ] | |||
} | |||
} | |||
} | |||
@ -0,0 +1,4 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#define OLED_TIMEOUT 30000 |
@ -0,0 +1,221 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "endgame48.h" | |||
#include "config.h" | |||
#include "taphold.h" | |||
#include "rgblight.h" | |||
#include "oled/oled_driver.h" | |||
#include "smoothled.h" | |||
#include "stdio.h" | |||
#include "string.h" | |||
#include "symbols.h" | |||
#define ________ KC_TRNS | |||
#define _MAIN 0 | |||
#define _ALPHA 1 | |||
#define _BETA 2 | |||
#define _SIGMA 3 | |||
#define F_DASH "\x9c\x9d" | |||
uint32_t layer_colors[4] = { | |||
0x4020FF, | |||
0xFF0010, | |||
0x20FF00, | |||
0x40FFFF | |||
}; | |||
enum custom_keycodes { | |||
KC_MAIN = EG_SAFE_RANGE, | |||
KC_ALPHA, | |||
KC_BETA, | |||
}; | |||
/* TapHold is my own implementation of the `LT` macro. It's processed in `process_record_user()`. */ | |||
uint16_t taphold_config_size = 3; | |||
taphold_t taphold_config[] = { | |||
{.key=KC_ALPHA, .mode=TAPHOLD_LAYER, .shortAction=KC_ESC, .longAction=_ALPHA}, | |||
{.key=KC_BETA, .mode=TAPHOLD_LAYER, .shortAction=KC_EQL, .longAction=_BETA}, | |||
{.key=KC_LCTRL, .mode=TAPHOLD_MOD, .shortAction=KC_MINS, .longAction=KC_LCTRL}, | |||
}; | |||
uint32_t taphold_timeout = 100; | |||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { | |||
/* Main layer | |||
┏━━━━━┳━━━━━┯━━━━━┯━━━━━┯━━━━━┯━━━━━┓┏┳━┳┓┏━━━━━┯━━━━━┯━━━━━┯━━━━━┯━━━━━┳━━━━━┓ | |||
┃ TAB ┃ Q │ W │ E │ R │ T ┃┃┃ ┃┃┃ Y │ U │ I │ O │ P ┃ BSP ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨┃┃ ┃┃┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃𝛼/ESC┃ A │ S │ D │ F │ G ┃┃┃ ┃┃┃ H │ J │ K │ L │ ; ┃ RET ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨┗┻━┻┛┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃SHIFT┃ Z │ X │ C │ V │ B ┃ ┃ N │ M │ , │ . │ / ┃LCTRL┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨ ┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃CTRL ┃ │ │ ALT │ GUI │SPACE┃ (o) ┃SPACE│ 𝛽/= │ ' │ │ ┃ ┃ | |||
┗━━━━━┻━━━━━┷━━━━━┷━━━━━┷━━━━━┷━━━━━┛ ┗━━━━━┷━━━━━┷━━━━━┷━━━━━┷━━━━━┻━━━━━┛ | |||
*/ | |||
[_MAIN] = LAYOUT( \ | |||
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC, \ | |||
KC_ALPHA,KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_ENT, \ | |||
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_LCTRL,\ | |||
KC_LCTRL,________,________,KC_LALT, KC_LGUI, KC_SPC, KC_EGMENU,KC_SPC, KC_BETA, KC_QUOT, ________,________,________\ | |||
), | |||
/* 𝛼 layer | |||
┏━━━━━┳━━━━━┯━━━━━┯━━━━━┯━━━━━┯━━━━━┓┏┳━┳┓┏━━━━━┯━━━━━┯━━━━━┯━━━━━┯━━━━━┳━━━━━┓ | |||
┃ ┃PREV │PLAY │NEXT │ │ ┃┃┃ ┃┃┃ - │ ^^^ │ ^ │ vvv │ ~ ┃ DEL ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨┃┃ ┃┃┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃ │VOL -│VOL +│ │ ┃┃┃ ┃┃┃HOME │ <-- │ v │ --> │ ` ┃ \ ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨┗┻━┻┛┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃ │ │ │ │ ┃ ┃ END │ = │ [ │ ] │ ( ┃ ) ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨ ┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃ │ │ │ │ ┃ (o) ┃ │ │ │ │ ┃ ┃ | |||
┗━━━━━┻━━━━━┷━━━━━┷━━━━━┷━━━━━┷━━━━━┛ ┗━━━━━┷━━━━━┷━━━━━┷━━━━━┷━━━━━┻━━━━━┛ | |||
*/ | |||
[_ALPHA] = LAYOUT( \ | |||
________,KC_MPRV, KC_MPLY, KC_MNXT, ________,________, KC_MINS, KC_PGUP, KC_UP, KC_PGDN, KC_TILD, KC_DEL, \ | |||
________,________,KC_VOLD, KC_VOLU, ________,________, KC_HOME, KC_LEFT, KC_DOWN, KC_RIGHT,KC_GRV, KC_BSLS, \ | |||
________,________,________,________,________,________, KC_END, KC_EQL, KC_LBRC, KC_RBRC, LSFT(KC_9),LSFT(KC_0),\ | |||
________,________,________,________,________,________,________,________,________,________,________,________,________\ | |||
), | |||
/* 𝛽 layer | |||
┏━━━━━┳━━━━━┯━━━━━┯━━━━━┯━━━━━┯━━━━━┓┏┳━┳┓┏━━━━━┯━━━━━┯━━━━━┯━━━━━┯━━━━━┳━━━━━┓ | |||
┃ RGB ┃ 1 │ 2 │ 3 │ 4 │ 5 ┃┃┃ ┃┃┃ 6 │ 7 │ 8 │ 9 │ 0 ┃ F12 ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨┃┃ ┃┃┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃ F1 │ F2 │ F3 │ F4 │ F5 ┃┃┃ ┃┃┃ F6 │ F7 │ F8 │ F9 │ F10 ┃ F11 ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨┗┻━┻┛┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃RESET│DEBUG│SLEEP│WAKE │EGMNU ┃ ┃ │ │ { │ } │ ┃ ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨ ┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃ │ │ │ │ ┃ (o) ┃PTSCR│ │ │ │ ┃ ┃ | |||
┗━━━━━┻━━━━━┷━━━━━┷━━━━━┷━━━━━┷━━━━━┛ ┗━━━━━┷━━━━━┷━━━━━┷━━━━━┷━━━━━┻━━━━━┛ | |||
*/ | |||
[_BETA] = LAYOUT( \ | |||
RGB_TOG, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_F12, \ | |||
________,KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, \ | |||
________,RESET, DEBUG, KC_PWR, KC_WAKE, KC_EGMENU, ________,________,KC_LCBR, KC_RCBR, ________,________,\ | |||
________,________,________,________,________,KC_MUTE, ________,KC_PSCR, ________,________,________,________,________\ | |||
), | |||
/* Σ layer | |||
┏━━━━━┳━━━━━┯━━━━━┯━━━━━┯━━━━━┯━━━━━┓┏┳━┳┓┏━━━━━┯━━━━━┯━━━━━┯━━━━━┯━━━━━┳━━━━━┓ | |||
┃ ┃ │ │ │ │ ┃┃┃ ┃┃┃ │ │ │ │ ┃ ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨┃┃ ┃┃┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃ │ │ │ │ ┃┃┃ ┃┃┃ │ │ │ │ ┃ ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨┗┻━┻┛┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃ │ │ │ │ ┃ ┃ │ │ │ │ ┃ ┃ | |||
┣━━━━━╉─────┼─────┼─────┼─────┼─────┨ ┣─────┼─────┼─────┼─────┼─────╊━━━━━┫ | |||
┃ ┃ │ │ │ │ ┃ (o) ┃ │ │ │ │ ┃ ┃ | |||
┗━━━━━┻━━━━━┷━━━━━┷━━━━━┷━━━━━┷━━━━━┛ ┗━━━━━┷━━━━━┷━━━━━┷━━━━━┷━━━━━┻━━━━━┛ | |||
*/ | |||
[_SIGMA] = LAYOUT( \ | |||
________,________,________,________,________,________, ________,________,________,________,________,________,\ | |||
________,________,________,________,________,________, ________,________,________,________,________,________,\ | |||
________,________,________,________,________,________, ________,________,________,________,________,________,\ | |||
________,________,________,________,________,________,________,________,________,________,________,________,________\ | |||
), | |||
}; | |||
static bool alpha_pressed = false; | |||
static bool beta_pressed = false; | |||
static bool ctrl_pressed = false; | |||
static bool alt_pressed = false; | |||
static bool shift_pressed = false; | |||
static bool gui_pressed = false; | |||
static volatile uint32_t last_scan; | |||
static volatile uint32_t scan_interval; | |||
void matrix_init_user(void) { | |||
smoothled_set(layer_colors[0], 1500, swimOut); | |||
} | |||
void matrix_scan_user(void) { | |||
uint32_t now = timer_read32(); | |||
scan_interval = now - last_scan; | |||
last_scan = now; | |||
smoothled_process(); | |||
} | |||
void suspend_power_down_user(void) { | |||
smoothled_set(0, 0, swimIn); | |||
} | |||
void suspend_wakeup_init_user(void) { | |||
smoothled_set(layer_colors[_MAIN], 1500, swimOut); | |||
} | |||
layer_state_t layer_state_set_user(layer_state_t state) { | |||
layer_state_t prevState = update_tri_layer_state(layer_state, _ALPHA, _BETA, _SIGMA); | |||
state = update_tri_layer_state(state, _ALPHA, _BETA, _SIGMA); | |||
uint8_t prevLayer = biton32(prevState); | |||
uint8_t layer = biton32(state); | |||
smoothled_set(layer_colors[layer], 250, prevLayer < layer ? swimOut : swimIn); | |||
return state; | |||
} | |||
bool process_record_user(uint16_t keycode, keyrecord_t *record) { | |||
if (keycode == KC_LCTRL) ctrl_pressed = record->event.pressed; | |||
if (keycode == KC_LALT) alt_pressed = record->event.pressed; | |||
if (keycode == KC_LSFT) shift_pressed = record->event.pressed; | |||
if (keycode == KC_LGUI) gui_pressed = record->event.pressed; | |||
if (keycode == RESET) { | |||
request_reset(); | |||
return false; | |||
} | |||
if (keycode == KC_ALPHA) alpha_pressed = record->event.pressed; | |||
if (keycode == KC_BETA) beta_pressed = record->event.pressed; | |||
return taphold_process(keycode, record); | |||
} | |||
bool encoder_update_user(uint8_t index, bool clockwise) { | |||
if (index == 0) { | |||
if (alpha_pressed) { | |||
if (clockwise) { | |||
tap_code(KC_VOLU); | |||
} else { | |||
tap_code(KC_VOLD); | |||
} | |||
} else if (beta_pressed) { | |||
if (clockwise) { | |||
tap_code(KC_PGDN); | |||
} else { | |||
tap_code(KC_PGUP); | |||
} | |||
} else { | |||
if (clockwise) { | |||
tap_code(KC_MS_WH_DOWN); | |||
} else { | |||
tap_code(KC_MS_WH_UP); | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
bool oled_task_user(void) { | |||
oled_write_P(logo, false); | |||
uint8_t current_layer = biton32(layer_state); | |||
oled_write_P(layerIcons[current_layer], current_layer != _MAIN); | |||
oled_write_P(PSTR("\n\n"), false); | |||
char mod_data[12] = F_DASH " " F_DASH F_DASH " " F_DASH "\n\0"; | |||
if (ctrl_pressed) memcpy(mod_data, mods[0], 2); | |||
if (alt_pressed) memcpy(mod_data +3, mods[1], 2); | |||
if (shift_pressed) memcpy(mod_data + 5, mods[2], 2); | |||
if (gui_pressed) memcpy(mod_data + 8, mods[3], 2); | |||
oled_write(mod_data, false); | |||
char buf[10] = " "; | |||
buf[sprintf(buf, "%2lums", scan_interval ? scan_interval : 1L)] = ' '; | |||
buf[5] = 0; | |||
oled_write(buf, false); | |||
return false; | |||
} |
@ -0,0 +1,5 @@ | |||
ifndef QUANTUM_DIR | |||
include ../../../../Makefile | |||
endif | |||
SRC += smoothled.c |
@ -0,0 +1,80 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "endgame48.h" | |||
#include "smoothled.h" | |||
#include "math.h" | |||
#include "string.h" | |||
static LED_TYPE source[RGBLED_NUM] = {}; | |||
static LED_TYPE current[RGBLED_NUM] = {}; | |||
static LED_TYPE target[RGBLED_NUM] = {}; | |||
static uint32_t duration = 1500; | |||
static int32_t smoothledTimer = -1; | |||
static animFunc_t animFunc; | |||
const float RGBLED_STEP = 1.0f / ((float)RGBLED_NUM); | |||
void smoothled_set(uint32_t color, uint32_t newDuration, animFunc_t newAnimFunc) { | |||
smoothledTimer = timer_read32(); | |||
LED_TYPE next = {.r = (color >> 16) & 0xFF, .g = (color >> 8) & 0xFF, .b = color & 0xFF}; | |||
for (uint8_t p = 0; p < RGBLED_NUM; p++) { | |||
source[p] = current[p]; | |||
target[p] = next; | |||
} | |||
duration = newDuration; | |||
animFunc = newAnimFunc; | |||
} | |||
LED_TYPE swimOut(LED_TYPE a, LED_TYPE b, float k, uint8_t x) { | |||
k = pow(k, 2); | |||
/* Center */ | |||
x = fabs(5.5 - ((float)x)); | |||
/* Time range for current LED */ | |||
float start = RGBLED_STEP * x; | |||
float end = RGBLED_STEP * (x + 1); | |||
if (k <= start) { | |||
k = 0; | |||
} else if (k >= end) { | |||
k = 1; | |||
} else { | |||
k = (k - start) / RGBLED_STEP; | |||
} | |||
float rk = 1.0f - k; | |||
return (LED_TYPE){ | |||
.r = rk * a.r + k * b.r, | |||
.g = rk * a.g + k * b.g, | |||
.b = rk * a.b + k * b.b, | |||
}; | |||
} | |||
LED_TYPE swimIn(LED_TYPE a, LED_TYPE b, float k, uint8_t x) { | |||
if (x < RGBLED_NUM / 2) { | |||
x = RGBLED_NUM / 2 - x - 1; | |||
} else { | |||
x = RGBLED_NUM - (x - RGBLED_NUM / 2 - 1); | |||
} | |||
return swimOut(a, b, k, x); | |||
} | |||
void smoothled_process(void) { | |||
if (smoothledTimer == -1) { | |||
return; | |||
} | |||
smoothled_render(); | |||
} | |||
void smoothled_render(void) { | |||
uint32_t passed = timer_elapsed32(smoothledTimer); | |||
if (passed > duration) { | |||
passed = duration; | |||
smoothledTimer = -1; | |||
} | |||
float k = ((float)passed) / duration; | |||
for (uint8_t p = 0; p < RGBLED_NUM; p++) { | |||
current[p] = animFunc(source[p], target[p], k, p); | |||
/* This is much faster than calling rgblight_setrgb_at every iteration */ | |||
led[p] = current[p]; | |||
} | |||
rgblight_set(); | |||
} |
@ -0,0 +1,15 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "quantum.h" | |||
#include "config.h" | |||
#include "rgblight.h" | |||
typedef LED_TYPE (*animFunc_t)(LED_TYPE, LED_TYPE, float, uint8_t); | |||
LED_TYPE swimIn(LED_TYPE, LED_TYPE, float, uint8_t); | |||
LED_TYPE swimOut(LED_TYPE, LED_TYPE, float, uint8_t); | |||
void smoothled_set(uint32_t color, uint32_t newDuration, animFunc_t newAnimFunc); | |||
void smoothled_process(void); | |||
void smoothled_render(void); |
@ -0,0 +1,86 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "menu.h" | |||
static int8_t menu_depth = -1; | |||
static menu_t *menus[8]; | |||
#define MENU_OPENED menu_depth != -1 | |||
#define MENU_CURRENT menus[menu_depth] | |||
#define MENU_MAX_ITEMS 10 | |||
bool menu_process_record(uint16_t keycode, keyrecord_t *record) { | |||
if (MENU_OPENED) { | |||
/* Menu is open */ | |||
if (record->event.pressed) { | |||
if (keycode == KC_EGMENU) { | |||
menu_t *menu = MENU_CURRENT; | |||
menu_item_t item = menu->items[menu->current]; | |||
if (item.submenu != NULL) { | |||
item.submenu->current = 0; | |||
item.submenu->scroll = 0; | |||
menus[++menu_depth] = item.submenu; | |||
item.submenu->opened(item.submenu); | |||
} else if (item.func != NULL) { | |||
item.func(); | |||
menu_depth = -1; | |||
} | |||
} else { | |||
menu_depth--; | |||
} | |||
} | |||
return false; | |||
} else if (keycode == KC_EGMENU) { | |||
/* Menu is not open, KC_EGMENU pressed */ | |||
if (record->event.pressed) { | |||
main_menu.current = 0; | |||
main_menu.scroll = 0; | |||
menus[++menu_depth] = &main_menu; | |||
} | |||
return false; | |||
} | |||
return true; | |||
} | |||
bool menu_encoder_update(uint8_t index, bool clockwise) { | |||
if (MENU_OPENED) { | |||
menu_t *menu = MENU_CURRENT; | |||
if (clockwise) { | |||
if (menu->current < menu->count - 1) { | |||
menu->current++; | |||
} | |||
if (menu->current - menu->scroll >= MENU_MAX_ITEMS) { | |||
menu->scroll++; | |||
} | |||
} else { | |||
if (menu->current) { | |||
menu->current--; | |||
} | |||
if (menu->current < menu->scroll) { | |||
menu->scroll--; | |||
} | |||
} | |||
return false; | |||
} | |||
return true; | |||
} | |||
bool menu_oled_task(void) { | |||
if (MENU_OPENED) { | |||
oled_clear(); | |||
oled_write_P(logo, false); | |||
menu_t *menu = MENU_CURRENT; | |||
char title[7] = " \0\0"; | |||
sprintf(title, "%-5s", menu->title); | |||
title[5] = '\n'; | |||
oled_write(title, false); | |||
for (uint8_t i = menu->scroll; i < (menu->count >= MENU_MAX_ITEMS ? menu->scroll + MENU_MAX_ITEMS : menu->count); i++) { | |||
char buf[6] = " \0"; | |||
sprintf(buf, "%-5s", menu->items[i].title); | |||
oled_write(buf, menu->current == i); | |||
} | |||
return false; | |||
} | |||
return true; | |||
} |
@ -0,0 +1,30 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "quantum.h" | |||
#include "stdint.h" | |||
#include "endgame48.h" | |||
#include "symbols.h" | |||
#include "oled/oled_driver.h" | |||
#include "stdio.h" | |||
typedef struct menu_item_t { | |||
char title[6]; | |||
void (*func)(void); | |||
struct menu_t *submenu; | |||
} menu_item_t; | |||
typedef struct menu_t { | |||
char *title; | |||
uint8_t count; | |||
uint8_t current; | |||
uint8_t scroll; | |||
void (*opened)(struct menu_t *); | |||
menu_item_t *items; | |||
} menu_t; | |||
extern menu_t main_menu; | |||
bool menu_process_record(uint16_t keycode, keyrecord_t *record); | |||
bool menu_encoder_update(uint8_t index, bool clockwise); | |||
bool menu_oled_task(void); |
@ -0,0 +1,64 @@ | |||
# Endgame48 | |||
![Endgame48](https://i.imgur.com/asU6f9G.jpeg) | |||
Puchi-C based 40% keyboard with rotary encoder and I2C OLED. | |||
* Keyboard Maintainer: Andrew Dunai | |||
* Hardware Supported: Puchi-C handwired | |||
* Hardware Availability: [Keycapsss.com](https://keycapsss.com) | |||
Make example for this keyboard (after setting up your build environment): | |||
make handwired/endgame48:default:dfu | |||
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). | |||
## Details | |||
- Puchi-C based handwired keyboard | |||
- 2x custom 1.25mm stainless steel plates | |||
- Kailh Choc White (clicky) | |||
- Rotary encoder | |||
- 0.91" 128x32 I<sup>2</sup>C OLED | |||
- WS2812 strip (12 LEDs) | |||
- OLED menu (triggered with encoder press) which allows you to view EEPROM, reset keyboard, toggle RGB, etc. | |||
- Handwiring is fun! | |||
## Pinout | |||
`PS2D` and `PS2C` are PS/2 `DATA` & `CLOCK` lines. They are not used in this revision but are reserved for future. | |||
``` | |||
┌────────┐ | |||
╭──────┤ ├──────╮ | |||
RGB> ┿ D3 │ │ RAW ┿ | |||
│ │ │ │ | |||
PS2D> ┿ D2 └────────┘ GND ┿ | |||
│ │ | |||
┿ GND RST ┿ | |||
│ [] [] │ | |||
┿ GND VCC ┿ | |||
│ │ | |||
I2C> ┿ D1 ||| || || F4 ┿ <ROW5 | |||
│ │ | |||
I2C> ┿ D0 F5 ┿ <COL1 | |||
│ ╱╲ │ | |||
ROW1> ┿ D4 ╱ ╲ F6 ┿ <COL2 | |||
│ ╱ ╲ │ | |||
ROW3> ┿ C6 ╲ ╱ F7 ┿ <COL3 | |||
│ ╲ ╱ │ | |||
COL7> ┿ D7 ╲╱ B1 ┿ <COL4 | |||
│ │ | |||
ENCA> ┿ E6 B3 ┿ <COL5 | |||
│ │ | |||
ENCB> ┿ B4 B2 ┿ <COL6 | |||
│ │ | |||
ROW2> │ B5 B7 D5 C7 F1 F0 B6 ┿ <ROW6 | |||
╰────╂──╂──╂──╂──╂─────╯ | |||
┃ ┃ ┃ ┃ | |||
ROW4 ┛ ┃ ┃ ┗ ROW8 | |||
┃ ┃ | |||
PS2C ┛ ┗ ROW7 | |||
``` |
@ -0,0 +1,29 @@ | |||
# MCU name | |||
MCU = atmega32u4 | |||
BOOTLOADER = atmel-dfu | |||
BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration | |||
MOUSEKEY_ENABLE = yes # Mouse keys | |||
EXTRAKEY_ENABLE = yes # Audio control and System control | |||
CONSOLE_ENABLE = no # Console for debug | |||
# https://github.com/qmk/qmk_firmware/blob/master/docs/faq_debug.md | |||
DEBUG_ENABLE = no # HID prints | |||
COMMAND_ENABLE = no # Commands for debug and configuration | |||
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend | |||
NKRO_ENABLE = no # USB Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work | |||
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality | |||
AUDIO_ENABLE = no | |||
RGBLIGHT_ENABLE = yes | |||
ENCODER_ENABLE = yes | |||
OLED_ENABLE = yes | |||
PS2_MOUSE_ENABLE = no | |||
# PS2_USE_USART = yes | |||
# PS2_USE_INT = yes | |||
DEBOUNCE_TYPE = sym_eager_pk | |||
SRC += taphold.c symbols.c menu.c | |||
# https://thomasbaart.nl/2018/12/01/reducing-firmware-size-in-qmk/ | |||
EXTRAFLAGS += -flto |
@ -0,0 +1,58 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "symbols.h" | |||
const char PROGMEM logo[12] = { | |||
0x8f, 0x90, 0x91, 0x92, 0x93, | |||
0xaf, 0xb0, 0xb1, 0xb2, 0xb3, | |||
/* '4', '8', ' ', 'v', '2', */ | |||
'\n', | |||
0 | |||
}; | |||
const char PROGMEM layerIcons[4][26] = { | |||
{ | |||
' ', ' ', ' ', ' ', ' ', | |||
0x20, 0x80, 0x81, 0x82, 0x20, | |||
0x20, 0xa0, 0xa1, 0xa2, 0x20, | |||
0x20, 0xc0, 0xc1, 0xc2, 0x20, | |||
' ', ' ', ' ', ' ', ' ', 0 | |||
}, | |||
{ | |||
' ', ' ', ' ', ' ', ' ', | |||
0x20, 0x83, 0x84, 0x85, 0x20, | |||
0x20, 0xa3, 0xa4, 0xa5, 0x20, | |||
0x20, 0xc3, 0xc4, 0xc5, 0x20, | |||
' ', ' ', ' ', ' ', ' ', 0 | |||
}, | |||
{ | |||
' ', ' ', ' ', ' ', ' ', | |||
0x20, 0x86, 0x87, 0x88, 0x20, | |||
0x20, 0xa6, 0xa7, 0xa8, 0x20, | |||
0x20, 0xc6, 0xc7, 0xc8, 0x20, | |||
' ', ' ', ' ', ' ', ' ', 0 | |||
}, | |||
{ | |||
' ', ' ', ' ', ' ', ' ', | |||
0x20, 0x89, 0x8a, 0x8b, 0x20, | |||
0x20, 0xa9, 0xaa, 0xab, 0x20, | |||
0x20, 0xc9, 0xca, 0xcb, 0x20, | |||
' ', ' ', ' ', ' ', ' ', 0 | |||
}, | |||
}; | |||
const char PROGMEM resetIcon[26] = { | |||
' ', ' ', ' ', ' ', ' ', | |||
' ', 0x8c, 0x8d, 0x8e, ' ', | |||
' ', 0xac, 0xad, 0xae, ' ', | |||
' ', 0xcc, 0xcd, 0xce, ' ', | |||
' ', ' ', ' ', ' ', ' ', 0 | |||
}; | |||
const char PROGMEM mods[4][3] = { | |||
{0x94, 0x95}, | |||
{0x96, 0x97}, | |||
{0x98, 0x99}, | |||
{0x9a, 0x9b}, | |||
}; |
@ -0,0 +1,9 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "endgame48.h" | |||
extern const char PROGMEM logo[12]; | |||
extern const char PROGMEM layerIcons[4][26]; | |||
extern const char PROGMEM resetIcon[26]; | |||
extern const char PROGMEM mods[4][3]; |
@ -0,0 +1,33 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "taphold.h" | |||
bool taphold_process(uint16_t keycode, keyrecord_t *record) { | |||
for (int i = 0; i < taphold_config_size; i++) { | |||
taphold_t *config = &taphold_config[i]; | |||
if (config->key == keycode && record->event.pressed) { | |||
if (config->mode == TAPHOLD_LAYER) { | |||
layer_on(config->longAction); | |||
} else { | |||
register_code(config->longAction); | |||
} | |||
config->time = timer_read32(); | |||
config->keypos = record->event.key; | |||
return false; | |||
} else if (KEYEQ(record->event.key, config->keypos) && !record->event.pressed) { | |||
if (config->mode == TAPHOLD_LAYER) { | |||
layer_off(config->longAction); | |||
} else { | |||
unregister_code(config->longAction); | |||
} | |||
if (timer_elapsed32(config->time) < taphold_timeout) { | |||
register_code(config->shortAction); | |||
unregister_code(config->shortAction); | |||
} | |||
config->keypos.row = 255; | |||
return false; | |||
} | |||
} | |||
return true; | |||
} |
@ -0,0 +1,28 @@ | |||
// Copyright 2022 Andrew Dunai (@and3rson) | |||
// SPDX-License-Identifier: GPL-2.0-or-later | |||
#include "quantum.h" | |||
typedef enum taphold_mode_t { | |||
TAPHOLD_LAYER, | |||
TAPHOLD_MOD | |||
} taphold_mode_t; | |||
typedef struct taphold_t { | |||
uint16_t key; | |||
uint32_t time; | |||
taphold_mode_t mode; | |||
uint16_t shortAction; | |||
uint16_t longAction; | |||
keypos_t keypos; | |||
/* We store key pos to properly release the key */ | |||
/* even when a different layer is active and the key has a different action now */ | |||
} taphold_t; | |||
extern taphold_t taphold_config[]; | |||
extern uint16_t taphold_config_size; | |||
/* Dual keys tap/hold timeout. */ | |||
/* If key is tapped for less than this value, send key in addition to primary action after completing the action. */ | |||
extern uint32_t taphold_timeout; | |||
bool taphold_process(uint16_t keycode, keyrecord_t *record); |