From a3cbc8a004f6ec5b0e1df326353a2a2fc8221129 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sun, 28 Feb 2021 15:50:15 +0000 Subject: Overhaul bootmagic logic to have single entrypoint (#8532) * Relocate bootmagic logic to have single entrypoint * Align init of layer state --- tmk_core/common/bootmagic.c | 163 --------------------------------------- tmk_core/common/bootmagic.h | 102 ------------------------ tmk_core/common/bootmagic_lite.c | 49 ------------ tmk_core/common/keyboard.c | 10 --- tmk_core/common/magic.c | 39 ---------- tmk_core/common/magic.h | 3 - 6 files changed, 366 deletions(-) delete mode 100644 tmk_core/common/bootmagic.c delete mode 100644 tmk_core/common/bootmagic.h delete mode 100644 tmk_core/common/bootmagic_lite.c delete mode 100644 tmk_core/common/magic.c delete mode 100644 tmk_core/common/magic.h (limited to 'tmk_core/common') diff --git a/tmk_core/common/bootmagic.c b/tmk_core/common/bootmagic.c deleted file mode 100644 index c1b3adf94d..0000000000 --- a/tmk_core/common/bootmagic.c +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include -#include "wait.h" -#include "matrix.h" -#include "bootloader.h" -#include "debug.h" -#include "keymap.h" -#include "host.h" -#include "action_layer.h" -#include "eeconfig.h" -#include "bootmagic.h" - -keymap_config_t keymap_config; - -/** \brief Bootmagic - * - * FIXME: needs doc - */ -void bootmagic(void) { - /* check signature */ - if (!eeconfig_is_enabled()) { - eeconfig_init(); - } - - /* do scans in case of bounce */ - print("bootmagic scan: ... "); - uint8_t scan = 100; - while (scan--) { - matrix_scan(); - wait_ms(10); - } - print("done.\n"); - - /* bootmagic skip */ - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SKIP)) { - return; - } - - /* eeconfig clear */ - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_EEPROM_CLEAR)) { - eeconfig_init(); - } - - /* bootloader */ - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_BOOTLOADER)) { - bootloader_jump(); - } - - /* debug enable */ - debug_config.raw = eeconfig_read_debug(); - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEBUG_ENABLE)) { - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEBUG_MATRIX)) { - debug_config.matrix = !debug_config.matrix; - } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEBUG_KEYBOARD)) { - debug_config.keyboard = !debug_config.keyboard; - } else if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEBUG_MOUSE)) { - debug_config.mouse = !debug_config.mouse; - } else { - debug_config.enable = !debug_config.enable; - } - } - eeconfig_update_debug(debug_config.raw); - - /* keymap config */ - keymap_config.raw = eeconfig_read_keymap(); - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_CONTROL_CAPSLOCK)) { - keymap_config.swap_control_capslock = !keymap_config.swap_control_capslock; - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_CAPSLOCK_TO_CONTROL)) { - keymap_config.capslock_to_control = !keymap_config.capslock_to_control; - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_LALT_LGUI)) { - keymap_config.swap_lalt_lgui = !keymap_config.swap_lalt_lgui; - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_RALT_RGUI)) { - keymap_config.swap_ralt_rgui = !keymap_config.swap_ralt_rgui; - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_NO_GUI)) { - keymap_config.no_gui = !keymap_config.no_gui; - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_GRAVE_ESC)) { - keymap_config.swap_grave_esc = !keymap_config.swap_grave_esc; - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_SWAP_BACKSLASH_BACKSPACE)) { - keymap_config.swap_backslash_backspace = !keymap_config.swap_backslash_backspace; - } - if (bootmagic_scan_keycode(BOOTMAGIC_HOST_NKRO)) { - keymap_config.nkro = !keymap_config.nkro; - } - eeconfig_update_keymap(keymap_config.raw); - - /* default layer */ - uint8_t default_layer = 0; - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_0)) { - default_layer |= (1 << 0); - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_1)) { - default_layer |= (1 << 1); - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_2)) { - default_layer |= (1 << 2); - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_3)) { - default_layer |= (1 << 3); - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_4)) { - default_layer |= (1 << 4); - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_5)) { - default_layer |= (1 << 5); - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_6)) { - default_layer |= (1 << 6); - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_DEFAULT_LAYER_7)) { - default_layer |= (1 << 7); - } - if (default_layer) { - eeconfig_update_default_layer(default_layer); - default_layer_set((layer_state_t)default_layer); - } else { - default_layer = eeconfig_read_default_layer(); - default_layer_set((layer_state_t)default_layer); - } - /* Also initialize layer state to trigger callback functions for layer_state */ - layer_state_set_kb((layer_state_t)layer_state); - - /* EE_HANDS handedness */ - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_EE_HANDS_LEFT)) { - eeconfig_update_handedness(true); - } - if (bootmagic_scan_keycode(BOOTMAGIC_KEY_EE_HANDS_RIGHT)) { - eeconfig_update_handedness(false); - } -} - -/** \brief Scan Keycode - * - * FIXME: needs doc - */ -static bool scan_keycode(uint8_t keycode) { - for (uint8_t r = 0; r < MATRIX_ROWS; r++) { - matrix_row_t matrix_row = matrix_get_row(r); - for (uint8_t c = 0; c < MATRIX_COLS; c++) { - if (matrix_row & ((matrix_row_t)1 << c)) { - if (keycode == keymap_key_to_keycode(0, (keypos_t){.row = r, .col = c})) { - return true; - } - } - } - } - return false; -} - -/** \brief Bootmagic Scan Keycode - * - * FIXME: needs doc - */ -bool bootmagic_scan_keycode(uint8_t keycode) { - if (!scan_keycode(BOOTMAGIC_KEY_SALT)) return false; - - return scan_keycode(keycode); -} diff --git a/tmk_core/common/bootmagic.h b/tmk_core/common/bootmagic.h deleted file mode 100644 index 8209d0194f..0000000000 --- a/tmk_core/common/bootmagic.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -/* FIXME: Add special doxygen comments for defines here. */ - -/* bootmagic salt key */ -#ifndef BOOTMAGIC_KEY_SALT -# define BOOTMAGIC_KEY_SALT KC_SPACE -#endif - -/* skip bootmagic and eeconfig */ -#ifndef BOOTMAGIC_KEY_SKIP -# define BOOTMAGIC_KEY_SKIP KC_ESC -#endif - -/* eeprom clear */ -#ifndef BOOTMAGIC_KEY_EEPROM_CLEAR -# define BOOTMAGIC_KEY_EEPROM_CLEAR KC_BSPACE -#endif - -/* kick up bootloader */ -#ifndef BOOTMAGIC_KEY_BOOTLOADER -# define BOOTMAGIC_KEY_BOOTLOADER KC_B -#endif - -/* debug enable */ -#ifndef BOOTMAGIC_KEY_DEBUG_ENABLE -# define BOOTMAGIC_KEY_DEBUG_ENABLE KC_D -#endif -#ifndef BOOTMAGIC_KEY_DEBUG_MATRIX -# define BOOTMAGIC_KEY_DEBUG_MATRIX KC_X -#endif -#ifndef BOOTMAGIC_KEY_DEBUG_KEYBOARD -# define BOOTMAGIC_KEY_DEBUG_KEYBOARD KC_K -#endif -#ifndef BOOTMAGIC_KEY_DEBUG_MOUSE -# define BOOTMAGIC_KEY_DEBUG_MOUSE KC_M -#endif -#ifndef BOOTMAGIC_KEY_EE_HANDS_LEFT -# define BOOTMAGIC_KEY_EE_HANDS_LEFT KC_L -#endif -#ifndef BOOTMAGIC_KEY_EE_HANDS_RIGHT -# define BOOTMAGIC_KEY_EE_HANDS_RIGHT KC_R -#endif - -/* - * keymap config - */ -#ifndef BOOTMAGIC_KEY_SWAP_CONTROL_CAPSLOCK -# define BOOTMAGIC_KEY_SWAP_CONTROL_CAPSLOCK KC_LCTRL -#endif -#ifndef BOOTMAGIC_KEY_CAPSLOCK_TO_CONTROL -# define BOOTMAGIC_KEY_CAPSLOCK_TO_CONTROL KC_CAPSLOCK -#endif -#ifndef BOOTMAGIC_KEY_SWAP_LALT_LGUI -# define BOOTMAGIC_KEY_SWAP_LALT_LGUI KC_LALT -#endif -#ifndef BOOTMAGIC_KEY_SWAP_RALT_RGUI -# define BOOTMAGIC_KEY_SWAP_RALT_RGUI KC_RALT -#endif -#ifndef BOOTMAGIC_KEY_NO_GUI -# define BOOTMAGIC_KEY_NO_GUI KC_LGUI -#endif -#ifndef BOOTMAGIC_KEY_SWAP_GRAVE_ESC -# define BOOTMAGIC_KEY_SWAP_GRAVE_ESC KC_GRAVE -#endif -#ifndef BOOTMAGIC_KEY_SWAP_BACKSLASH_BACKSPACE -# define BOOTMAGIC_KEY_SWAP_BACKSLASH_BACKSPACE KC_BSLASH -#endif -#ifndef BOOTMAGIC_HOST_NKRO -# define BOOTMAGIC_HOST_NKRO KC_N -#endif - -/* - * change default layer - */ -#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_0 -# define BOOTMAGIC_KEY_DEFAULT_LAYER_0 KC_0 -#endif -#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_1 -# define BOOTMAGIC_KEY_DEFAULT_LAYER_1 KC_1 -#endif -#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_2 -# define BOOTMAGIC_KEY_DEFAULT_LAYER_2 KC_2 -#endif -#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_3 -# define BOOTMAGIC_KEY_DEFAULT_LAYER_3 KC_3 -#endif -#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_4 -# define BOOTMAGIC_KEY_DEFAULT_LAYER_4 KC_4 -#endif -#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_5 -# define BOOTMAGIC_KEY_DEFAULT_LAYER_5 KC_5 -#endif -#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_6 -# define BOOTMAGIC_KEY_DEFAULT_LAYER_6 KC_6 -#endif -#ifndef BOOTMAGIC_KEY_DEFAULT_LAYER_7 -# define BOOTMAGIC_KEY_DEFAULT_LAYER_7 KC_7 -#endif - -void bootmagic(void); -bool bootmagic_scan_keycode(uint8_t keycode); diff --git a/tmk_core/common/bootmagic_lite.c b/tmk_core/common/bootmagic_lite.c deleted file mode 100644 index cbf756a175..0000000000 --- a/tmk_core/common/bootmagic_lite.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "quantum.h" - -/** \brief Reset eeprom - * - * ...just incase someone wants to only change the eeprom behaviour - */ -__attribute__((weak)) void bootmagic_lite_reset_eeprom(void) { -#if defined(VIA_ENABLE) - via_eeprom_reset(); -#else - eeconfig_disable(); -#endif -} - -/** \brief The lite version of TMK's bootmagic based on Wilba. - * - * 100% less potential for accidentally making the keyboard do stupid things. - */ -__attribute__((weak)) void bootmagic_lite(void) { - // We need multiple scans because debouncing can't be turned off. - matrix_scan(); -#if defined(DEBOUNCE) && DEBOUNCE > 0 - wait_ms(DEBOUNCE * 2); -#else - wait_ms(30); -#endif - matrix_scan(); - - // If the configured key (commonly Esc) is held down on power up, - // reset the EEPROM valid state and jump to bootloader. - // This isn't very generalized, but we need something that doesn't - // rely on user's keymaps in firmware or EEPROM. - uint8_t row = BOOTMAGIC_LITE_ROW; - uint8_t col = BOOTMAGIC_LITE_COLUMN; - -#if defined(SPLIT_KEYBOARD) && defined(BOOTMAGIC_LITE_ROW_RIGHT) && defined(BOOTMAGIC_LITE_COLUMN_RIGHT) - if (!is_keyboard_left()) { - row = BOOTMAGIC_LITE_ROW_RIGHT; - col = BOOTMAGIC_LITE_COLUMN_RIGHT; - } -#endif - - if (matrix_get_row(row) & (1 << col)) { - bootmagic_lite_reset_eeprom(); - - // Jump to bootloader. - bootloader_jump(); - } -} diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index ce3255c069..65d9e00c7a 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -34,11 +34,6 @@ along with this program. If not, see . #ifdef BACKLIGHT_ENABLE # include "backlight.h" #endif -#ifdef BOOTMAGIC_ENABLE -# include "bootmagic.h" -#else -# include "magic.h" -#endif #ifdef MOUSEKEY_ENABLE # include "mousekey.h" #endif @@ -296,11 +291,6 @@ void keyboard_init(void) { #ifdef ADB_MOUSE_ENABLE adb_mouse_init(); #endif -#ifdef BOOTMAGIC_ENABLE - bootmagic(); -#else - magic(); -#endif #ifdef BACKLIGHT_ENABLE backlight_init(); #endif diff --git a/tmk_core/common/magic.c b/tmk_core/common/magic.c deleted file mode 100644 index e14994164e..0000000000 --- a/tmk_core/common/magic.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#if defined(__AVR__) -# include -#endif -#include "matrix.h" -#include "bootloader.h" -#include "debug.h" -#include "keymap.h" -#include "host.h" -#include "action_layer.h" -#include "eeconfig.h" -#include "magic.h" - -keymap_config_t keymap_config; - -/** \brief Magic - * - * FIXME: Needs doc - */ -void magic(void) { - /* check signature */ - if (!eeconfig_is_enabled()) { - eeconfig_init(); - } - - /* debug enable */ - debug_config.raw = eeconfig_read_debug(); - - /* keymap config */ - keymap_config.raw = eeconfig_read_keymap(); - - uint8_t default_layer = 0; - default_layer = eeconfig_read_default_layer(); - default_layer_set((layer_state_t)default_layer); - - /* Also initialize layer state to trigger callback functions for layer_state */ - layer_state_set_kb((layer_state_t)layer_state); -} diff --git a/tmk_core/common/magic.h b/tmk_core/common/magic.h deleted file mode 100644 index a6552c04dc..0000000000 --- a/tmk_core/common/magic.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void magic(void); -- cgit v1.2.3 From 415a8bc249d5c366487f69b3547198d17044a5de Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sun, 28 Feb 2021 15:52:43 +0000 Subject: ARM - Refactor SLEEP_LED to support more platforms (#8403) * Initial refactor of ARM SLEEP_LED to enable more platforms * fix build issues * Disable SLEEP_LED for boards with no caps lock code * Enable GPT14 for boards with caps lock code and SLEEP_LED enabled * Enable GPT for boards with caps lock code and SLEEP_LED enabled --- keyboards/aeboards/ext65/rev2/config.h | 2 + keyboards/aeboards/ext65/rev2/halconf.h | 2 + keyboards/aeboards/ext65/rev2/mcuconf.h | 2 + keyboards/cannonkeys/an_c/rules.mk | 2 +- keyboards/cannonkeys/chimera65/rules.mk | 2 +- keyboards/cannonkeys/instant60/rules.mk | 2 +- keyboards/cannonkeys/iron165/rules.mk | 2 +- keyboards/cannonkeys/satisfaction75/rules.mk | 2 +- keyboards/cannonkeys/savage65/rules.mk | 2 +- keyboards/cannonkeys/tmov2/rules.mk | 2 +- keyboards/cannonkeys/tsukuyomi/rules.mk | 2 +- keyboards/daji/seis_cinco/rules.mk | 2 +- keyboards/handwired/ck4x4/rules.mk | 2 +- keyboards/handwired/onekey/rules.mk | 4 +- keyboards/projectkb/alice/config.h | 3 + keyboards/projectkb/alice/halconf.h | 2 + keyboards/projectkb/alice/mcuconf.h | 2 + keyboards/projectkb/signature87/rules.mk | 2 +- keyboards/ramonimbao/wete/config.h | 2 + keyboards/ramonimbao/wete/halconf.h | 2 + keyboards/ramonimbao/wete/mcuconf.h | 2 + keyboards/westfoxtrot/prophet/config.h | 2 + keyboards/westfoxtrot/prophet/halconf.h | 27 +++++++++ keyboards/westfoxtrot/prophet/mcuconf.h | 5 +- tmk_core/common/chibios/sleep_led.c | 88 +++++++++++----------------- 25 files changed, 96 insertions(+), 71 deletions(-) create mode 100644 keyboards/projectkb/alice/config.h create mode 100644 keyboards/westfoxtrot/prophet/halconf.h (limited to 'tmk_core/common') diff --git a/keyboards/aeboards/ext65/rev2/config.h b/keyboards/aeboards/ext65/rev2/config.h index 541f07ee98..dc1bfb71c2 100644 --- a/keyboards/aeboards/ext65/rev2/config.h +++ b/keyboards/aeboards/ext65/rev2/config.h @@ -74,3 +74,5 @@ along with this program. If not, see . #define BACKLIGHT_LEVELS 6 #define BACKLIGHT_BREATHING #define BREATHING_PERIOD 6 + +#define SLEEP_LED_GPT_DRIVER GPTD1 diff --git a/keyboards/aeboards/ext65/rev2/halconf.h b/keyboards/aeboards/ext65/rev2/halconf.h index 6e2f536400..9fa1eca60b 100644 --- a/keyboards/aeboards/ext65/rev2/halconf.h +++ b/keyboards/aeboards/ext65/rev2/halconf.h @@ -27,5 +27,7 @@ #define HAL_USE_SPI TRUE +#define HAL_USE_GPT TRUE + #include_next diff --git a/keyboards/aeboards/ext65/rev2/mcuconf.h b/keyboards/aeboards/ext65/rev2/mcuconf.h index 26ce806150..43d53d4f2d 100644 --- a/keyboards/aeboards/ext65/rev2/mcuconf.h +++ b/keyboards/aeboards/ext65/rev2/mcuconf.h @@ -32,3 +32,5 @@ #undef STM32_SPI_USE_SPI2 #define STM32_SPI_USE_SPI2 TRUE +#undef STM32_GPT_USE_TIM1 +#define STM32_GPT_USE_TIM1 TRUE diff --git a/keyboards/cannonkeys/an_c/rules.mk b/keyboards/cannonkeys/an_c/rules.mk index 8ed2d0ead7..7309744d1e 100644 --- a/keyboards/cannonkeys/an_c/rules.mk +++ b/keyboards/cannonkeys/an_c/rules.mk @@ -11,7 +11,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = no # Custom matrix file BACKLIGHT_ENABLE = yes diff --git a/keyboards/cannonkeys/chimera65/rules.mk b/keyboards/cannonkeys/chimera65/rules.mk index 4d940da78d..a08f2fa49d 100644 --- a/keyboards/cannonkeys/chimera65/rules.mk +++ b/keyboards/cannonkeys/chimera65/rules.mk @@ -11,7 +11,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = no # Custom matrix file BACKLIGHT_ENABLE = yes diff --git a/keyboards/cannonkeys/instant60/rules.mk b/keyboards/cannonkeys/instant60/rules.mk index 0973ad9547..e5ba2903b6 100644 --- a/keyboards/cannonkeys/instant60/rules.mk +++ b/keyboards/cannonkeys/instant60/rules.mk @@ -10,7 +10,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = no # Custom matrix file BACKLIGHT_ENABLE = yes diff --git a/keyboards/cannonkeys/iron165/rules.mk b/keyboards/cannonkeys/iron165/rules.mk index 3925458dda..9de75a6149 100644 --- a/keyboards/cannonkeys/iron165/rules.mk +++ b/keyboards/cannonkeys/iron165/rules.mk @@ -9,7 +9,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = no # Custom matrix file BACKLIGHT_ENABLE = yes diff --git a/keyboards/cannonkeys/satisfaction75/rules.mk b/keyboards/cannonkeys/satisfaction75/rules.mk index e3abea5463..3861b4849f 100644 --- a/keyboards/cannonkeys/satisfaction75/rules.mk +++ b/keyboards/cannonkeys/satisfaction75/rules.mk @@ -15,7 +15,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = no # Custom matrix file ENCODER_ENABLE = yes diff --git a/keyboards/cannonkeys/savage65/rules.mk b/keyboards/cannonkeys/savage65/rules.mk index 8e1b984fd4..2454573d2f 100644 --- a/keyboards/cannonkeys/savage65/rules.mk +++ b/keyboards/cannonkeys/savage65/rules.mk @@ -11,7 +11,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = no # Custom matrix file BACKLIGHT_ENABLE = yes diff --git a/keyboards/cannonkeys/tmov2/rules.mk b/keyboards/cannonkeys/tmov2/rules.mk index 9b29e8fd17..294c873eec 100644 --- a/keyboards/cannonkeys/tmov2/rules.mk +++ b/keyboards/cannonkeys/tmov2/rules.mk @@ -11,7 +11,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = no # Custom matrix file BACKLIGHT_ENABLE = yes diff --git a/keyboards/cannonkeys/tsukuyomi/rules.mk b/keyboards/cannonkeys/tsukuyomi/rules.mk index ce881f1986..4e7791a79f 100644 --- a/keyboards/cannonkeys/tsukuyomi/rules.mk +++ b/keyboards/cannonkeys/tsukuyomi/rules.mk @@ -10,7 +10,7 @@ EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend # if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work NKRO_ENABLE = yes # USB Nkey Rollover BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality diff --git a/keyboards/daji/seis_cinco/rules.mk b/keyboards/daji/seis_cinco/rules.mk index fa34092cd7..768e50069c 100644 --- a/keyboards/daji/seis_cinco/rules.mk +++ b/keyboards/daji/seis_cinco/rules.mk @@ -11,7 +11,7 @@ EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = no # Console for debug COMMAND_ENABLE = no # Commands for debug and configuration # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend # if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work NKRO_ENABLE = yes # USB Nkey Rollover BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality diff --git a/keyboards/handwired/ck4x4/rules.mk b/keyboards/handwired/ck4x4/rules.mk index 0709f341bf..e5785270bd 100644 --- a/keyboards/handwired/ck4x4/rules.mk +++ b/keyboards/handwired/ck4x4/rules.mk @@ -10,7 +10,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = no # Custom matrix file diff --git a/keyboards/handwired/onekey/rules.mk b/keyboards/handwired/onekey/rules.mk index 259050d553..df2ec1ecf4 100644 --- a/keyboards/handwired/onekey/rules.mk +++ b/keyboards/handwired/onekey/rules.mk @@ -7,9 +7,9 @@ EXTRAKEY_ENABLE = yes # Audio control and System control(+450) CONSOLE_ENABLE = yes # Console for debug(+400) COMMAND_ENABLE = no # Commands for debug and configuration # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend # if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work -NKRO_ENABLE = yes # USB Nkey Rollover +NKRO_ENABLE = yes # USB Nkey Rollover BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality on B7 by default RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow MIDI_ENABLE = no # MIDI support (+2400 to 4200, depending on config) diff --git a/keyboards/projectkb/alice/config.h b/keyboards/projectkb/alice/config.h new file mode 100644 index 0000000000..95fb682e17 --- /dev/null +++ b/keyboards/projectkb/alice/config.h @@ -0,0 +1,3 @@ +#pragma once + +#define SLEEP_LED_GPT_DRIVER GPTD1 diff --git a/keyboards/projectkb/alice/halconf.h b/keyboards/projectkb/alice/halconf.h index b17808ffeb..0016e53ba8 100644 --- a/keyboards/projectkb/alice/halconf.h +++ b/keyboards/projectkb/alice/halconf.h @@ -27,5 +27,7 @@ #define HAL_USE_SPI TRUE +#define HAL_USE_GPT TRUE + #include_next diff --git a/keyboards/projectkb/alice/mcuconf.h b/keyboards/projectkb/alice/mcuconf.h index 4bfc7f9465..6e0f1a50d8 100644 --- a/keyboards/projectkb/alice/mcuconf.h +++ b/keyboards/projectkb/alice/mcuconf.h @@ -32,3 +32,5 @@ #undef STM32_SPI_USE_SPI2 #define STM32_SPI_USE_SPI2 TRUE +#undef STM32_GPT_USE_TIM1 +#define STM32_GPT_USE_TIM1 TRUE diff --git a/keyboards/projectkb/signature87/rules.mk b/keyboards/projectkb/signature87/rules.mk index fe0d976d11..caad4c83c2 100644 --- a/keyboards/projectkb/signature87/rules.mk +++ b/keyboards/projectkb/signature87/rules.mk @@ -9,7 +9,7 @@ MOUSEKEY_ENABLE = yes # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control CONSOLE_ENABLE = yes # Console for debug COMMAND_ENABLE = yes # Commands for debug and configuration -SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend NKRO_ENABLE = yes # USB Nkey Rollover BACKLIGHT_ENABLE = no diff --git a/keyboards/ramonimbao/wete/config.h b/keyboards/ramonimbao/wete/config.h index 5d68a7efd7..3d89550090 100644 --- a/keyboards/ramonimbao/wete/config.h +++ b/keyboards/ramonimbao/wete/config.h @@ -46,6 +46,8 @@ along with this program. If not, see . #define RGBLED_NUM 24 #define RGBLIGHT_ANIMATIONS +#define SLEEP_LED_GPT_DRIVER GPTD1 + /* define if matrix has ghost */ //#define MATRIX_HAS_GHOST diff --git a/keyboards/ramonimbao/wete/halconf.h b/keyboards/ramonimbao/wete/halconf.h index c43440a459..b3de873be1 100644 --- a/keyboards/ramonimbao/wete/halconf.h +++ b/keyboards/ramonimbao/wete/halconf.h @@ -23,5 +23,7 @@ #define HAL_USE_PWM TRUE +#define HAL_USE_GPT TRUE + #include_next diff --git a/keyboards/ramonimbao/wete/mcuconf.h b/keyboards/ramonimbao/wete/mcuconf.h index b7f2c481c6..7a2e877763 100644 --- a/keyboards/ramonimbao/wete/mcuconf.h +++ b/keyboards/ramonimbao/wete/mcuconf.h @@ -35,3 +35,5 @@ #undef STM32_SPI_USE_SPI2 #define STM32_SPI_USE_SPI2 TRUE +#undef STM32_GPT_USE_TIM1 +#define STM32_GPT_USE_TIM1 TRUE diff --git a/keyboards/westfoxtrot/prophet/config.h b/keyboards/westfoxtrot/prophet/config.h index 4f447e4ca0..0f2ddc1dc1 100644 --- a/keyboards/westfoxtrot/prophet/config.h +++ b/keyboards/westfoxtrot/prophet/config.h @@ -30,3 +30,5 @@ #define LOCKING_SUPPORT_ENABLE /* Locking resynchronize hack */ #define LOCKING_RESYNC_ENABLE + +#define SLEEP_LED_GPT_DRIVER GPTD1 diff --git a/keyboards/westfoxtrot/prophet/halconf.h b/keyboards/westfoxtrot/prophet/halconf.h new file mode 100644 index 0000000000..cbfd1307f1 --- /dev/null +++ b/keyboards/westfoxtrot/prophet/halconf.h @@ -0,0 +1,27 @@ +/* Copyright 2020 QMK + * + * 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 . + */ + +/* + * This file was auto-generated by: + * `qmk chibios-confmigrate -i keyboards/projectkb/alice/halconf.h -r platforms/chibios/common/configs/halconf.h` + */ + +#pragma once + +#define HAL_USE_GPT TRUE + +#include_next + diff --git a/keyboards/westfoxtrot/prophet/mcuconf.h b/keyboards/westfoxtrot/prophet/mcuconf.h index 2665f9c9e4..550141af6b 100644 --- a/keyboards/westfoxtrot/prophet/mcuconf.h +++ b/keyboards/westfoxtrot/prophet/mcuconf.h @@ -23,6 +23,5 @@ #include_next -#undef STM32_PWM_USE_TIM3 -#define STM32_PWM_USE_TIM3 TRUE - +#undef STM32_GPT_USE_TIM1 +#define STM32_GPT_USE_TIM1 TRUE diff --git a/tmk_core/common/chibios/sleep_led.c b/tmk_core/common/chibios/sleep_led.c index 5595eec0e5..477056a454 100644 --- a/tmk_core/common/chibios/sleep_led.c +++ b/tmk_core/common/chibios/sleep_led.c @@ -9,21 +9,13 @@ * Use LP timer on Kinetises, TIM14 on STM32F0. */ -#if defined(KL2x) || defined(K20x) - -/* Use Low Power Timer (LPTMR) */ -# define TIMER_INTERRUPT_VECTOR KINETIS_LPTMR0_IRQ_VECTOR -# define RESET_COUNTER LPTMR0->CSR |= LPTMRx_CSR_TCF - -#elif defined(STM32F0XX) - -/* Use TIM14 manually */ -# define TIMER_INTERRUPT_VECTOR STM32_TIM14_HANDLER -# define RESET_COUNTER STM32_TIM14->SR &= ~STM32_TIM_SR_UIF - +#ifndef SLEEP_LED_GPT_DRIVER +# if defined(STM32F0XX) +# define SLEEP_LED_GPT_DRIVER GPTD14 +# endif #endif -#if defined(KL2x) || defined(K20x) || defined(STM32F0XX) /* common parts for timers/interrupts */ +#if defined(KL2x) || defined(K20x) || defined(SLEEP_LED_GPT_DRIVER) /* common parts for timers/interrupts */ /* Breathing Sleep LED brighness(PWM On period) table * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle @@ -33,10 +25,7 @@ */ static const uint8_t breathing_table[64] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, 255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -/* interrupt handler */ -OSAL_IRQ_HANDLER(TIMER_INTERRUPT_VECTOR) { - OSAL_IRQ_PROLOGUE(); - +void sleep_led_timer_callback(void) { /* Software PWM * timer:1111 1111 1111 1111 * \_____/\/ \_______/____ count(0-255) @@ -64,17 +53,16 @@ OSAL_IRQ_HANDLER(TIMER_INTERRUPT_VECTOR) { if (timer.pwm.count == breathing_table[timer.pwm.index]) { led_set(0); } - - /* Reset the counter */ - RESET_COUNTER; - - OSAL_IRQ_EPILOGUE(); } #endif /* common parts for known platforms */ #if defined(KL2x) || defined(K20x) /* platform selection: familiar Kinetis chips */ +/* Use Low Power Timer (LPTMR) */ +# define TIMER_INTERRUPT_VECTOR KINETIS_LPTMR0_IRQ_VECTOR +# define RESET_COUNTER LPTMR0->CSR |= LPTMRx_CSR_TCF + /* LPTMR clock options */ # define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */ # define LPTMR_CLOCK_LPO 1 /* 1kHz clock */ @@ -86,6 +74,18 @@ OSAL_IRQ_HANDLER(TIMER_INTERRUPT_VECTOR) { # define SIM_SCGC5_LPTMR SIM_SCGC5_LPTIMER # endif +/* interrupt handler */ +OSAL_IRQ_HANDLER(TIMER_INTERRUPT_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + sleep_led_timer_callback(); + + /* Reset the counter */ + RESET_COUNTER; + + OSAL_IRQ_EPILOGUE(); +} + /* Initialise the timer */ void sleep_led_init(void) { /* Make sure the clock to the LPTMR is enabled */ @@ -159,45 +159,23 @@ void sleep_led_toggle(void) { LPTMR0->CSR ^= LPTMRx_CSR_TEN; } -#elif defined(STM32F0XX) /* platform selection: STM32F0XX */ - -/* Initialise the timer */ -void sleep_led_init(void) { - /* enable clock */ - rccEnableTIM14(FALSE); /* low power enable = FALSE */ - rccResetTIM14(); - - /* prescale */ - /* Assuming 48MHz internal clock */ - /* getting cca 65484 irqs/sec */ - STM32_TIM14->PSC = 733; +#elif defined(SLEEP_LED_GPT_DRIVER) - /* auto-reload */ - /* 0 => interrupt every time */ - STM32_TIM14->ARR = 3; +static void gptTimerCallback(GPTDriver *gptp) { + (void)gptp; + sleep_led_timer_callback(); +} - /* enable counter update event interrupt */ - STM32_TIM14->DIER |= STM32_TIM_DIER_UIE; +static const GPTConfig gptcfg = {1000000, gptTimerCallback, 0, 0}; - /* register interrupt vector */ - nvicEnableVector(STM32_TIM14_NUMBER, 2); /* vector, priority */ -} +/* Initialise the timer */ +void sleep_led_init(void) { gptStart(&SLEEP_LED_GPT_DRIVER, &gptcfg); } -void sleep_led_enable(void) { - /* Enable the timer */ - STM32_TIM14->CR1 = STM32_TIM_CR1_CEN | STM32_TIM_CR1_URS; - /* URS => update event only on overflow; setting UG bit disabled */ -} +void sleep_led_enable(void) { gptStartContinuous(&SLEEP_LED_GPT_DRIVER, gptcfg.frequency / 0xFFFF); } -void sleep_led_disable(void) { - /* Disable the timer */ - STM32_TIM14->CR1 = 0; -} +void sleep_led_disable(void) { gptStopTimer(&SLEEP_LED_GPT_DRIVER); } -void sleep_led_toggle(void) { - /* Toggle the timer */ - STM32_TIM14->CR1 ^= STM32_TIM_CR1_CEN; -} +void sleep_led_toggle(void) { (SLEEP_LED_GPT_DRIVER.state == GPT_READY) ? sleep_led_enable() : sleep_led_disable(); } #else /* platform selection: not on familiar chips */ -- cgit v1.2.3 From 5ba4391cf29ce624f17593417212b3dbca1609ad Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sun, 28 Feb 2021 15:52:58 +0000 Subject: Refactor of USB code within split_common (#11890) * Initial refactor of usb code within split_common * Add headers * Correct disable condition * Format * Align func name --- quantum/split_common/split_util.c | 70 +++++++++++--------------------- tmk_core/common.mk | 1 + tmk_core/common/chibios/chibios_config.h | 2 + tmk_core/common/usb_util.c | 29 +++++++++++++ tmk_core/common/usb_util.h | 22 ++++++++++ tmk_core/protocol/chibios.mk | 1 + tmk_core/protocol/chibios/usb_util.c | 21 ++++++++++ tmk_core/protocol/lufa.mk | 1 + tmk_core/protocol/lufa/usb_util.c | 34 ++++++++++++++++ tmk_core/protocol/vusb.mk | 1 + tmk_core/protocol/vusb/usb_util.c | 24 +++++++++++ 11 files changed, 160 insertions(+), 46 deletions(-) create mode 100644 tmk_core/common/usb_util.c create mode 100644 tmk_core/common/usb_util.h create mode 100644 tmk_core/protocol/chibios/usb_util.c create mode 100644 tmk_core/protocol/lufa/usb_util.c create mode 100644 tmk_core/protocol/vusb/usb_util.c (limited to 'tmk_core/common') diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c index 2ae44e6e15..9e75e19ce0 100644 --- a/quantum/split_common/split_util.c +++ b/quantum/split_common/split_util.c @@ -1,3 +1,18 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "split_util.h" #include "matrix.h" #include "keyboard.h" @@ -6,14 +21,7 @@ #include "transport.h" #include "quantum.h" #include "wait.h" - -#ifdef PROTOCOL_LUFA -# include -#endif - -#ifdef PROTOCOL_VUSB -# include -#endif +#include "usb_util.h" #ifdef EE_HANDS # include "eeconfig.h" @@ -31,56 +39,21 @@ # define SPLIT_USB_TIMEOUT_POLL 10 #endif -#ifdef PROTOCOL_CHIBIOS -# define SPLIT_USB_DETECT // Force this on for now -#endif - volatile bool isLeftHand = true; #if defined(SPLIT_USB_DETECT) -# if defined(PROTOCOL_LUFA) -static inline bool usbHasActiveConnection(void) { return USB_Device_IsAddressSet(); } -static inline void usbDisable(void) { - USB_Disable(); - USB_DeviceState = DEVICE_STATE_Unattached; -} -# elif defined(PROTOCOL_CHIBIOS) -static inline bool usbHasActiveConnection(void) { return usbGetDriverStateI(&USBD1) == USB_ACTIVE; } -static inline void usbDisable(void) { usbStop(&USBD1); } -# elif defined(PROTOCOL_VUSB) -static inline bool usbHasActiveConnection(void) { - usbPoll(); - return usbConfiguration; -} -static inline void usbDisable(void) { usbDeviceDisconnect(); } -# else -static inline bool usbHasActiveConnection(void) { return true; } -static inline void usbDisable(void) {} -# endif - -bool usbIsActive(void) { +static bool usbIsActive(void) { for (uint8_t i = 0; i < (SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL); i++) { // This will return true if a USB connection has been established - if (usbHasActiveConnection()) { + if (usb_connected_state()) { return true; } wait_ms(SPLIT_USB_TIMEOUT_POLL); } - - // Avoid NO_USB_STARTUP_CHECK - Disable USB as the previous checks seem to enable it somehow - usbDisable(); - return false; } -#elif defined(PROTOCOL_LUFA) && defined(OTGPADE) -static inline bool usbIsActive(void) { - USB_OTGPAD_On(); // enables VBUS pad - wait_us(5); - - return USB_VBUS_GetStatus(); // checks state of VBUS -} #else -static inline bool usbIsActive(void) { return true; } +static inline bool usbIsActive(void) { return usb_vbus_state(); } #endif #ifdef SPLIT_HAND_MATRIX_GRID @@ -126,6 +99,11 @@ __attribute__((weak)) bool is_keyboard_master(void) { // only check once, as this is called often if (usbstate == UNKNOWN) { usbstate = usbIsActive() ? MASTER : SLAVE; + + // Avoid NO_USB_STARTUP_CHECK - Disable USB as the previous checks seem to enable it somehow + if (usbstate == SLAVE) { + usb_disable(); + } } return (usbstate == MASTER); diff --git a/tmk_core/common.mk b/tmk_core/common.mk index c2fc522cea..2f8f81126a 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -12,6 +12,7 @@ TMK_COMMON_SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/sendchar_null.c \ $(COMMON_DIR)/eeconfig.c \ $(COMMON_DIR)/report.c \ + $(COMMON_DIR)/usb_util.c \ $(PLATFORM_COMMON_DIR)/suspend.c \ $(PLATFORM_COMMON_DIR)/timer.c \ $(COMMON_DIR)/sync_timer.c \ diff --git a/tmk_core/common/chibios/chibios_config.h b/tmk_core/common/chibios/chibios_config.h index b4d96465d1..bebf026de7 100644 --- a/tmk_core/common/chibios/chibios_config.h +++ b/tmk_core/common/chibios/chibios_config.h @@ -15,6 +15,8 @@ */ #pragma once +#define SPLIT_USB_DETECT // Force this on for now + #if defined(STM32F1XX) # define USE_GPIOV1 #endif diff --git a/tmk_core/common/usb_util.c b/tmk_core/common/usb_util.c new file mode 100644 index 0000000000..e4c50fcb10 --- /dev/null +++ b/tmk_core/common/usb_util.c @@ -0,0 +1,29 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "usb_util.h" +#include "wait.h" + +__attribute__((weak)) void usb_disable(void) {} +__attribute__((weak)) bool usb_connected_state(void) { return true; } +__attribute__((weak)) bool usb_vbus_state(void) { +#ifdef USB_VBUS_PIN + setPinInput(USB_VBUS_PIN); + wait_us(5); + return readPin(USB_VBUS_PIN); +#else + return true; +#endif +} diff --git a/tmk_core/common/usb_util.h b/tmk_core/common/usb_util.h new file mode 100644 index 0000000000..4ebedb1e71 --- /dev/null +++ b/tmk_core/common/usb_util.h @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +void usb_disable(void); +bool usb_connected_state(void); +bool usb_vbus_state(void); diff --git a/tmk_core/protocol/chibios.mk b/tmk_core/protocol/chibios.mk index 80554abb32..d01697835b 100644 --- a/tmk_core/protocol/chibios.mk +++ b/tmk_core/protocol/chibios.mk @@ -6,6 +6,7 @@ SRC += $(CHIBIOS_DIR)/usb_main.c SRC += $(CHIBIOS_DIR)/main.c SRC += usb_descriptor.c SRC += $(CHIBIOS_DIR)/usb_driver.c +SRC += $(CHIBIOS_DIR)/usb_util.c SRC += $(LIBSRC) VPATH += $(TMK_PATH)/$(PROTOCOL_DIR) diff --git a/tmk_core/protocol/chibios/usb_util.c b/tmk_core/protocol/chibios/usb_util.c new file mode 100644 index 0000000000..5945e8a8de --- /dev/null +++ b/tmk_core/protocol/chibios/usb_util.c @@ -0,0 +1,21 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "usb_util.h" + +void usb_disable(void) { usbStop(&USBD1); } + +bool usb_connected_state(void) { return usbGetDriverStateI(&USBD1) == USB_ACTIVE; } diff --git a/tmk_core/protocol/lufa.mk b/tmk_core/protocol/lufa.mk index 1cc1fa04e5..9d9fb728b1 100644 --- a/tmk_core/protocol/lufa.mk +++ b/tmk_core/protocol/lufa.mk @@ -44,6 +44,7 @@ ifeq ($(strip $(VIRTSER_ENABLE)), yes) endif SRC += $(LUFA_SRC) +SRC += $(LUFA_DIR)/usb_util.c # Search Path VPATH += $(TMK_PATH)/$(LUFA_DIR) diff --git a/tmk_core/protocol/lufa/usb_util.c b/tmk_core/protocol/lufa/usb_util.c new file mode 100644 index 0000000000..9e943a21b9 --- /dev/null +++ b/tmk_core/protocol/lufa/usb_util.c @@ -0,0 +1,34 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "usb_util.h" +#include "wait.h" + +void usb_disable(void) { + USB_Disable(); + USB_DeviceState = DEVICE_STATE_Unattached; +} + +bool usb_connected_state(void) { return USB_Device_IsAddressSet(); } + +#if defined(OTGPADE) +bool usb_vbus_state(void) { + USB_OTGPAD_On(); // enables VBUS pad + wait_us(5); + + return USB_VBUS_GetStatus(); // checks state of VBUS +} +#endif diff --git a/tmk_core/protocol/vusb.mk b/tmk_core/protocol/vusb.mk index 1de6003089..e4d013b38d 100644 --- a/tmk_core/protocol/vusb.mk +++ b/tmk_core/protocol/vusb.mk @@ -5,6 +5,7 @@ VUSB_PATH = $(LIB_PATH)/vusb SRC += $(VUSB_DIR)/main.c \ $(VUSB_DIR)/vusb.c \ + $(VUSB_DIR)/usb_util.c \ $(VUSB_PATH)/usbdrv/usbdrv.c \ $(VUSB_PATH)/usbdrv/usbdrvasm.S \ $(VUSB_PATH)/usbdrv/oddebug.c diff --git a/tmk_core/protocol/vusb/usb_util.c b/tmk_core/protocol/vusb/usb_util.c new file mode 100644 index 0000000000..602854dbe6 --- /dev/null +++ b/tmk_core/protocol/vusb/usb_util.c @@ -0,0 +1,24 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "usb_util.h" + +void usb_disable(void) { usbDeviceDisconnect(); } + +bool usb_connected_state(void) { + usbPoll(); + return usbConfiguration; +} -- cgit v1.2.3 From 0e984b6e7e216a62df0b5d53f6a8f0d4bc13dca3 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Mon, 1 Mar 2021 08:57:02 -0800 Subject: Add ability to toggle One Shot functionality (#4198) Co-authored-by: Nick Brassel Co-authored-by: Ryan --- docs/keycodes.md | 3 +++ docs/one_shot_keys.md | 3 +++ quantum/keycode_config.h | 1 + quantum/quantum.c | 11 ++++++++ quantum/quantum_keycodes.h | 9 +++++++ tmk_core/common/action_util.c | 58 ++++++++++++++++++++++++++++++++++++------- tmk_core/common/action_util.h | 5 ++++ 7 files changed, 81 insertions(+), 9 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/keycodes.md b/docs/keycodes.md index 9acf8b6839..f3c519b130 100644 --- a/docs/keycodes.md +++ b/docs/keycodes.md @@ -516,6 +516,9 @@ See also: [One Shot Keys](one_shot_keys.md) |------------|----------------------------------| |`OSM(mod)` |Hold `mod` for one keypress | |`OSL(layer)`|Switch to `layer` for one keypress| +|`OS_ON` |Turns One Shot keys on | +|`OS_OFF` |Turns One Shot keys off | +|`OS_TOGG` |Toggles One Shot keys status | ## Space Cadet :id=space-cadet diff --git a/docs/one_shot_keys.md b/docs/one_shot_keys.md index 9a082d7d6d..9fc5486299 100644 --- a/docs/one_shot_keys.md +++ b/docs/one_shot_keys.md @@ -17,6 +17,9 @@ You can control the behavior of one shot keys by defining these in `config.h`: * `OSM(mod)` - Momentarily hold down *mod*. You must use the `MOD_*` keycodes as shown in [Mod Tap](mod_tap.md), not the `KC_*` codes. * `OSL(layer)` - momentary switch to *layer*. +* `OS_ON` - Turns on One Shot keys. +* `OS_OFF` - Turns off One Shot keys. OSM act as regular mod keys, OSL act like `MO`. +* `ON_TOGG` - Toggles the one shot key status. Sometimes, you want to activate a one-shot key as part of a macro or tap dance routine. diff --git a/quantum/keycode_config.h b/quantum/keycode_config.h index f878168c5f..d7e334fdc8 100644 --- a/quantum/keycode_config.h +++ b/quantum/keycode_config.h @@ -37,6 +37,7 @@ typedef union { bool nkro : 1; bool swap_lctl_lgui : 1; bool swap_rctl_rgui : 1; + bool oneshot_disable : 1; }; } keymap_config_t; diff --git a/quantum/quantum.c b/quantum/quantum.c index ef751a2331..78601ce6bf 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -318,6 +318,17 @@ bool process_record_quantum(keyrecord_t *record) { case OUT_BT: set_output(OUTPUT_BLUETOOTH); return false; +#endif +#ifndef NO_ACTION_ONESHOT + case ONESHOT_TOGGLE: + oneshot_toggle(); + break; + case ONESHOT_ENABLE: + oneshot_enable(); + break; + case ONESHOT_DISABLE: + oneshot_disable(); + break; #endif } } diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index e0f5dbc61e..63945a17ca 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -578,6 +578,10 @@ enum quantum_keycodes { #endif + ONESHOT_ENABLE, + ONESHOT_DISABLE, + ONESHOT_TOGGLE, + // always leave at the end SAFE_RANGE }; @@ -885,3 +889,8 @@ enum quantum_keycodes { #define DM_RSTP DYN_REC_STOP #define DM_PLY1 DYN_MACRO_PLAY1 #define DM_PLY2 DYN_MACRO_PLAY2 + +// One Shot toggle +#define OS_TOGG ONESHOT_TOGGLE +#define OS_ON ONESHOT_ENABLE +#define OS_OFF ONESHOT_DISABLE diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c index 000503b082..a57c8bf66a 100644 --- a/tmk_core/common/action_util.c +++ b/tmk_core/common/action_util.c @@ -147,12 +147,16 @@ void clear_oneshot_swaphands(void) { * FIXME: needs doc */ void set_oneshot_layer(uint8_t layer, uint8_t state) { - oneshot_layer_data = layer << 3 | state; - layer_on(layer); + if (!keymap_config.oneshot_disable) { + oneshot_layer_data = layer << 3 | state; + layer_on(layer); # if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_layer_time = timer_read(); + oneshot_layer_time = timer_read(); # endif - oneshot_layer_changed_kb(get_oneshot_layer()); + oneshot_layer_changed_kb(get_oneshot_layer()); + } else { + layer_on(layer); + } } /** \brief Reset oneshot layer * @@ -172,7 +176,7 @@ void reset_oneshot_layer(void) { void clear_oneshot_layer_state(oneshot_fullfillment_t state) { uint8_t start_state = oneshot_layer_data; oneshot_layer_data &= ~state; - if (!get_oneshot_layer_state() && start_state != oneshot_layer_data) { + if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) { layer_off(get_oneshot_layer()); reset_oneshot_layer(); } @@ -182,6 +186,39 @@ void clear_oneshot_layer_state(oneshot_fullfillment_t state) { * FIXME: needs doc */ bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); } + +/** \brief set oneshot + * + * FIXME: needs doc + */ +void oneshot_set(bool active) { + if (keymap_config.oneshot_disable != active) { + keymap_config.oneshot_disable = active; + eeconfig_update_keymap(keymap_config.raw); + dprintf("Oneshot: active: %d\n", active); + } +} + +/** \brief toggle oneshot + * + * FIXME: needs doc + */ +void oneshot_toggle(void) { oneshot_set(!keymap_config.oneshot_disable); } + +/** \brief enable oneshot + * + * FIXME: needs doc + */ +void oneshot_enable(void) { oneshot_set(true); } + +/** \brief disable oneshot + * + * FIXME: needs doc + */ +void oneshot_disable(void) { oneshot_set(false); } + +bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; } + #endif /** \brief Send keyboard report @@ -321,14 +358,17 @@ void del_oneshot_mods(uint8_t mods) { * FIXME: needs doc */ void set_oneshot_mods(uint8_t mods) { - if (oneshot_mods != mods) { + if (!keymap_config.oneshot_disable) { + if (oneshot_mods != mods) { # if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_time = timer_read(); + oneshot_time = timer_read(); # endif - oneshot_mods = mods; - oneshot_mods_changed_kb(mods); + oneshot_mods = mods; + oneshot_mods_changed_kb(mods); + } } } + /** \brief clear oneshot mods * * FIXME: needs doc diff --git a/tmk_core/common/action_util.h b/tmk_core/common/action_util.h index ff29f79b09..f2b3897ae5 100644 --- a/tmk_core/common/action_util.h +++ b/tmk_core/common/action_util.h @@ -85,6 +85,11 @@ void oneshot_mods_changed_kb(uint8_t mods); void oneshot_layer_changed_user(uint8_t layer); void oneshot_layer_changed_kb(uint8_t layer); +void oneshot_toggle(void); +void oneshot_enable(void); +void oneshot_disable(void); +bool is_oneshot_enabled(void); + /* inspect */ uint8_t has_anymod(void); -- cgit v1.2.3 From cde2859a6591b1274da20978bd158f20229faa88 Mon Sep 17 00:00:00 2001 From: XScorpion2 Date: Tue, 2 Mar 2021 14:32:15 -0600 Subject: Split RGB Matrix (#11055) * Split RGB Matrix * Suspend State sync for rgb matrix --- docs/feature_rgb_matrix.md | 2 ++ quantum/rgb_matrix.c | 35 ++++++++++++++++++++++++++++++----- quantum/rgb_matrix_types.h | 1 + quantum/split_common/transport.c | 35 +++++++++++++++++++++++++++++++++++ tmk_core/common/avr/suspend.c | 4 ++++ tmk_core/common/chibios/suspend.c | 8 ++++++++ tmk_core/common/eeconfig.c | 2 +- tmk_core/common/eeconfig.h | 9 +++++---- 8 files changed, 86 insertions(+), 10 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md index fd866bd571..878acaf8b3 100644 --- a/docs/feature_rgb_matrix.md +++ b/docs/feature_rgb_matrix.md @@ -439,6 +439,8 @@ These are defined in [`rgblight_list.h`](https://github.com/qmk/qmk_firmware/blo #define RGB_MATRIX_STARTUP_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set #define RGB_MATRIX_STARTUP_SPD 127 // Sets the default animation speed, if none has been set #define RGB_MATRIX_DISABLE_KEYCODES // disables control of rgb matrix by keycodes (must use code functions to control the feature) +#define RGB_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right. + // If RGB_MATRIX_KEYPRESSES or RGB_MATRIX_KEYRELEASES is enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR ``` ## EEPROM storage :id=eeprom-storage diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index ec17b4d72c..69d4c02093 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c @@ -131,7 +131,7 @@ last_hit_t g_last_hit_tracker; // internals static uint8_t rgb_last_enable = UINT8_MAX; static uint8_t rgb_last_effect = UINT8_MAX; -static effect_params_t rgb_effect_params = {0, 0xFF}; +static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; static rgb_task_states rgb_task_state = SYNCING; #if RGB_DISABLE_TIMEOUT > 0 static uint32_t rgb_anykey_timer; @@ -143,6 +143,11 @@ static uint32_t rgb_timer_buffer; static last_hit_t last_hit_buffer; #endif // RGB_MATRIX_KEYREACTIVE_ENABLED +// split rgb matrix +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) +const uint8_t k_rgb_matrix_split[2] = RGB_MATRIX_SPLIT; +#endif + void eeconfig_read_rgb_matrix(void) { eeprom_read_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config)); } void eeconfig_update_rgb_matrix(void) { eeprom_update_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config)); } @@ -153,6 +158,7 @@ void eeconfig_update_rgb_matrix_default(void) { rgb_matrix_config.mode = RGB_MATRIX_STARTUP_MODE; rgb_matrix_config.hsv = (HSV){RGB_MATRIX_STARTUP_HUE, RGB_MATRIX_STARTUP_SAT, RGB_MATRIX_STARTUP_VAL}; rgb_matrix_config.speed = RGB_MATRIX_STARTUP_SPD; + rgb_matrix_config.flags = LED_FLAG_ALL; eeconfig_update_rgb_matrix(); } @@ -164,6 +170,7 @@ void eeconfig_debug_rgb_matrix(void) { dprintf("rgb_matrix_config.hsv.s = %d\n", rgb_matrix_config.hsv.s); dprintf("rgb_matrix_config.hsv.v = %d\n", rgb_matrix_config.hsv.v); dprintf("rgb_matrix_config.speed = %d\n", rgb_matrix_config.speed); + dprintf("rgb_matrix_config.flags = %d\n", rgb_matrix_config.flags); } __attribute__((weak)) uint8_t rgb_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i) { return 0; } @@ -180,9 +187,23 @@ uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *l void rgb_matrix_update_pwm_buffers(void) { rgb_matrix_driver.flush(); } -void rgb_matrix_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { rgb_matrix_driver.set_color(index, red, green, blue); } +void rgb_matrix_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + if (!is_keyboard_left() && index >= k_rgb_matrix_split[0]) + rgb_matrix_driver.set_color(index - k_rgb_matrix_split[0], red, green, blue); + else if (is_keyboard_left() && index < k_rgb_matrix_split[0]) +#endif + rgb_matrix_driver.set_color(index, red, green, blue); +} -void rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { rgb_matrix_driver.set_color_all(red, green, blue); } +void rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) + rgb_matrix_set_color(i, red, green, blue); +#else + rgb_matrix_driver.set_color_all(red, green, blue); +#endif +} void process_rgb_matrix(uint8_t row, uint8_t col, bool pressed) { #ifndef RGB_MATRIX_SPLIT @@ -315,6 +336,10 @@ static void rgb_task_start(void) { static void rgb_task_render(uint8_t effect) { bool rendering = false; rgb_effect_params.init = (effect != rgb_last_effect) || (rgb_matrix_config.enable != rgb_last_enable); + if (rgb_effect_params.flags != rgb_matrix_config.flags) { + rgb_effect_params.flags = rgb_matrix_config.flags; + rgb_matrix_set_color_all(0, 0, 0); + } // each effect can opt to do calculations // and/or request PWM buffer updates. @@ -618,6 +643,6 @@ void rgb_matrix_decrease_speed_helper(bool write_to_eeprom) { rgb_matrix_set_spe void rgb_matrix_decrease_speed_noeeprom(void) { rgb_matrix_decrease_speed_helper(false); } void rgb_matrix_decrease_speed(void) { rgb_matrix_decrease_speed_helper(true); } -led_flags_t rgb_matrix_get_flags(void) { return rgb_effect_params.flags; } +led_flags_t rgb_matrix_get_flags(void) { return rgb_matrix_config.flags; } -void rgb_matrix_set_flags(led_flags_t flags) { rgb_effect_params.flags = flags; } +void rgb_matrix_set_flags(led_flags_t flags) { rgb_matrix_config.flags = flags; } diff --git a/quantum/rgb_matrix_types.h b/quantum/rgb_matrix_types.h index 7b8171fb23..8cd5b17a77 100644 --- a/quantum/rgb_matrix_types.h +++ b/quantum/rgb_matrix_types.h @@ -89,6 +89,7 @@ typedef union { uint8_t mode : 6; HSV hsv; uint8_t speed; // EECONFIG needs to be increased to support this + led_flags_t flags; }; } rgb_config_t; diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index 61b61ea08c..daa60bb734 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c @@ -22,6 +22,10 @@ static pin_t encoders_pad[] = ENCODERS_PAD_A; # define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t)) #endif +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) +# include "rgb_matrix.h" +#endif + #if defined(USE_I2C) # include "i2c_master.h" @@ -54,6 +58,10 @@ typedef struct _I2C_slave_buffer_t { # ifdef WPM_ENABLE uint8_t current_wpm; # endif +# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + rgb_config_t rgb_matrix; + bool rgb_suspend_state; +# endif } I2C_slave_buffer_t; static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; @@ -68,6 +76,8 @@ static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_re # define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync) # define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state) # define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm) +# define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix) +# define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state) # define TIMEOUT 100 @@ -141,6 +151,11 @@ bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) # endif # endif +# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT); + i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)g_suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT); +# endif + # ifndef DISABLE_SYNC_TIMER i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT); @@ -186,6 +201,11 @@ void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) set_oneshot_mods(i2c_buffer->oneshot_mods); # endif # endif + +# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + memcpy((void*)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix)); + memcpy((void*)i2c_buffer->rgb_suspend_state, (void *)g_suspend_state, sizeof(i2c_buffer->rgb_suspend_state)); +# endif } void transport_master_init(void) { i2c_init(); } @@ -226,6 +246,10 @@ typedef struct _Serial_m2s_buffer_t { # ifdef WPM_ENABLE uint8_t current_wpm; # endif +# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + rgb_config_t rgb_matrix; + bool rgb_suspend_state; +# endif } Serial_m2s_buffer_t; # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) @@ -343,6 +367,12 @@ bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) serial_m2s_buffer.oneshot_mods = get_oneshot_mods(); # endif # endif + +# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + serial_m2s_buffer.rgb_matrix = rgb_matrix_config; + serial_m2s_buffer.rgb_suspend_state = g_suspend_state; +# endif + # ifndef DISABLE_SYNC_TIMER serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; # endif @@ -381,6 +411,11 @@ void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) set_oneshot_mods(serial_m2s_buffer.oneshot_mods); # endif # endif + +# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + rgb_matrix_config = serial_m2s_buffer.rgb_matrix; + g_suspend_state = serial_m2s_buffer.rgb_suspend_state; +# endif } #endif diff --git a/tmk_core/common/avr/suspend.c b/tmk_core/common/avr/suspend.c index 47a82a2eec..d52c8ac410 100644 --- a/tmk_core/common/avr/suspend.c +++ b/tmk_core/common/avr/suspend.c @@ -28,6 +28,10 @@ # include "rgblight.h" #endif +#ifdef RGB_MATRIX_ENABLE +# include "rgb_matrix.h" +#endif + /** \brief Suspend idle * * FIXME: needs doc diff --git a/tmk_core/common/chibios/suspend.c b/tmk_core/common/chibios/suspend.c index 49e20641fb..17f024caba 100644 --- a/tmk_core/common/chibios/suspend.c +++ b/tmk_core/common/chibios/suspend.c @@ -24,6 +24,10 @@ # include "rgblight.h" #endif +#ifdef RGB_MATRIX_ENABLE +# include "rgb_matrix.h" +#endif + /** \brief suspend idle * * FIXME: needs doc @@ -53,6 +57,10 @@ void suspend_power_down(void) { backlight_set(0); #endif +#ifdef RGB_MATRIX_ENABLE + rgb_matrix_task(); +#endif + // Turn off LED indicators uint8_t leds_off = 0; #if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE) diff --git a/tmk_core/common/eeconfig.c b/tmk_core/common/eeconfig.c index 5e3ebe6ee6..92a5092176 100644 --- a/tmk_core/common/eeconfig.c +++ b/tmk_core/common/eeconfig.c @@ -57,7 +57,7 @@ void eeconfig_init_quantum(void) { eeprom_update_dword(EECONFIG_HAPTIC, 0); eeprom_update_byte(EECONFIG_VELOCIKEY, 0); eeprom_update_dword(EECONFIG_RGB_MATRIX, 0); - eeprom_update_byte(EECONFIG_RGB_MATRIX_SPEED, 0); + eeprom_update_word(EECONFIG_RGB_MATRIX_EXTENDED, 0); // TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS // within the emulated eeprom via dfu-util or another tool diff --git a/tmk_core/common/eeconfig.h b/tmk_core/common/eeconfig.h index 86b9e6f99b..39bc51d5db 100644 --- a/tmk_core/common/eeconfig.h +++ b/tmk_core/common/eeconfig.h @@ -21,7 +21,7 @@ along with this program. If not, see . #include #ifndef EECONFIG_MAGIC_NUMBER -# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEB // When changing, decrement this value to avoid future re-init issues +# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA // When changing, decrement this value to avoid future re-init issues #endif #define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF @@ -44,11 +44,12 @@ along with this program. If not, see . #define EECONFIG_HAPTIC (uint32_t *)24 #define EECONFIG_RGB_MATRIX (uint32_t *)28 -#define EECONFIG_RGB_MATRIX_SPEED (uint8_t *)32 +// Speed & Flags +#define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32 // TODO: Combine these into a single word and single block of EEPROM -#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)33 +#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34 // Size of EEPROM being used, other code can refer to this for available EEPROM -#define EECONFIG_SIZE 34 +#define EECONFIG_SIZE 35 /* debug bit */ #define EECONFIG_DEBUG_ENABLE (1 << 0) #define EECONFIG_DEBUG_MATRIX (1 << 1) -- cgit v1.2.3 From 9155b59e1a496b64f7aa576e6e4cb84fd0a9607b Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 8 Mar 2021 16:55:00 +1100 Subject: LED Matrix: decouple from Backlight (#12054) --- common_features.mk | 5 ++--- quantum/led_matrix.c | 8 +------- quantum/led_matrix.h | 4 ---- quantum/process_keycode/process_backlight.c | 29 +++++++++++++++++++++++++++-- quantum/quantum.c | 9 ++++----- quantum/quantum.h | 12 ++++++------ tmk_core/common/eeconfig.h | 5 +++++ 7 files changed, 45 insertions(+), 27 deletions(-) (limited to 'tmk_core/common') diff --git a/common_features.mk b/common_features.mk index bdde278b98..fdc481fd2a 100644 --- a/common_features.mk +++ b/common_features.mk @@ -223,11 +223,10 @@ VALID_LED_MATRIX_TYPES := IS31FL3731 custom ifeq ($(strip $(LED_MATRIX_ENABLE)), yes) ifeq ($(filter $(LED_MATRIX_DRIVER),$(VALID_LED_MATRIX_TYPES)),) - $(error LED_MATRIX_DRIVER="$(LED_MATRIX_DRIVER)" is not a valid matrix type) + $(error "$(LED_MATRIX_DRIVER)" is not a valid matrix type) else - BACKLIGHT_ENABLE = yes - BACKLIGHT_DRIVER = custom OPT_DEFS += -DLED_MATRIX_ENABLE + SRC += $(QUANTUM_DIR)/process_keycode/process_backlight.c SRC += $(QUANTUM_DIR)/led_matrix.c SRC += $(QUANTUM_DIR)/led_matrix_drivers.c endif diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c index 4f1f06c7ac..39bccdd580 100644 --- a/quantum/led_matrix.c +++ b/quantum/led_matrix.c @@ -45,10 +45,6 @@ led_eeconfig_t led_matrix_eeconfig; # define LED_DISABLE_WHEN_USB_SUSPENDED false #endif -#ifndef EECONFIG_LED_MATRIX -# define EECONFIG_LED_MATRIX EECONFIG_RGBLIGHT -#endif - #if !defined(LED_MATRIX_MAXIMUM_BRIGHTNESS) || LED_MATRIX_MAXIMUM_BRIGHTNESS > 255 # define LED_MATRIX_MAXIMUM_BRIGHTNESS 255 #endif @@ -135,7 +131,7 @@ void led_matrix_set_suspend_state(bool state) { g_suspend_state = state; } void led_matrix_all_off(void) { led_matrix_set_index_value_all(0); } // Uniform brightness -void led_matrix_uniform_brightness(void) { led_matrix_set_index_value_all(LED_MATRIX_MAXIMUM_BRIGHTNESS / BACKLIGHT_LEVELS * led_matrix_eeconfig.val); } +void led_matrix_uniform_brightness(void) { led_matrix_set_index_value_all(led_matrix_eeconfig.val); } void led_matrix_custom(void) {} @@ -344,5 +340,3 @@ void led_matrix_set_value(uint8_t val) { led_matrix_set_value_noeeprom(val); eeconfig_update_led_matrix(led_matrix_eeconfig.raw); } - -void backlight_set(uint8_t val) { led_matrix_set_value(val); } diff --git a/quantum/led_matrix.h b/quantum/led_matrix.h index 85bae43c15..0817d13573 100644 --- a/quantum/led_matrix.h +++ b/quantum/led_matrix.h @@ -21,10 +21,6 @@ #include "led_matrix_types.h" -#ifndef BACKLIGHT_ENABLE -# error You must define BACKLIGHT_ENABLE with LED_MATRIX_ENABLE -#endif - enum led_matrix_effects { LED_MATRIX_UNIFORM_BRIGHTNESS = 1, // All new effects go above this line diff --git a/quantum/process_keycode/process_backlight.c b/quantum/process_keycode/process_backlight.c index 4d12f6813a..8b70339a55 100644 --- a/quantum/process_keycode/process_backlight.c +++ b/quantum/process_keycode/process_backlight.c @@ -16,11 +16,35 @@ #include "process_backlight.h" -#include "backlight.h" +#ifdef LED_MATRIX_ENABLE +# include "led_matrix.h" +#else +# include "backlight.h" +#endif bool process_backlight(uint16_t keycode, keyrecord_t *record) { if (record->event.pressed) { switch (keycode) { +#ifdef LED_MATRIX_ENABLE + case BL_ON: + led_matrix_enable(); + return false; + case BL_OFF: + led_matrix_disable(); + return false; + case BL_DEC: + led_matrix_decrease_val(); + return false; + case BL_INC: + led_matrix_increase_val(); + return false; + case BL_TOGG: + led_matrix_toggle(); + return false; + case BL_STEP: + led_matrix_step(); + return false; +#else case BL_ON: backlight_level(BACKLIGHT_LEVELS); return false; @@ -39,10 +63,11 @@ bool process_backlight(uint16_t keycode, keyrecord_t *record) { case BL_STEP: backlight_step(); return false; -#ifdef BACKLIGHT_BREATHING +# ifdef BACKLIGHT_BREATHING case BL_BRTG: backlight_toggle_breathing(); return false; +# endif #endif } } diff --git a/quantum/quantum.c b/quantum/quantum.c index 78601ce6bf..59d95f2f54 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -234,7 +234,7 @@ bool process_record_quantum(keyrecord_t *record) { #ifdef AUDIO_ENABLE process_audio(keycode, record) && #endif -#ifdef BACKLIGHT_ENABLE +#if defined(BACKLIGHT_ENABLE) || defined(LED_MATRIX_ENABLE) process_backlight(keycode, record) && #endif #ifdef STENO_ENABLE @@ -387,15 +387,14 @@ void matrix_init_quantum() { led_init_ports(); #endif #ifdef BACKLIGHT_ENABLE -# ifdef LED_MATRIX_ENABLE - led_matrix_init(); -# else backlight_init_ports(); -# endif #endif #ifdef AUDIO_ENABLE audio_init(); #endif +#ifdef LED_MATRIX_ENABLE + led_matrix_init(); +#endif #ifdef RGB_MATRIX_ENABLE rgb_matrix_init(); #endif diff --git a/quantum/quantum.h b/quantum/quantum.h index 070bd01317..7c2dcaa829 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -30,11 +30,11 @@ #include "keymap.h" #ifdef BACKLIGHT_ENABLE -# ifdef LED_MATRIX_ENABLE -# include "led_matrix.h" -# else -# include "backlight.h" -# endif +# include "backlight.h" +#endif + +#ifdef LED_MATRIX_ENABLE +# include "led_matrix.h" #endif #if defined(RGBLIGHT_ENABLE) @@ -98,7 +98,7 @@ extern layer_state_t layer_state; # include "process_music.h" #endif -#ifdef BACKLIGHT_ENABLE +#if defined(BACKLIGHT_ENABLE) || defined(LED_MATRIX_ENABLE) # include "process_backlight.h" #endif diff --git a/tmk_core/common/eeconfig.h b/tmk_core/common/eeconfig.h index 39bc51d5db..9e18fd4e15 100644 --- a/tmk_core/common/eeconfig.h +++ b/tmk_core/common/eeconfig.h @@ -43,9 +43,14 @@ along with this program. If not, see . #define EECONFIG_VELOCIKEY (uint8_t *)23 #define EECONFIG_HAPTIC (uint32_t *)24 + +// Mutually exclusive +#define EECONFIG_LED_MATRIX (uint32_t *)28 #define EECONFIG_RGB_MATRIX (uint32_t *)28 // Speed & Flags +#define EECONFIG_LED_MATRIX_EXTENDED (uint16_t *)32 #define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32 + // TODO: Combine these into a single word and single block of EEPROM #define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34 // Size of EEPROM being used, other code can refer to this for available EEPROM -- cgit v1.2.3 From 40c7ecfdeaf50ab76e10854a84aebfcb82ddb092 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Wed, 10 Mar 2021 22:47:36 +0000 Subject: Move gpio wait logic to wait.h (#12067) --- quantum/quantum.h | 33 -------- tmk_core/common/arm_atsam/_wait.h | 22 ++++++ tmk_core/common/avr/_wait.h | 29 +++++++ tmk_core/common/chibios/_wait.h | 55 ++++++++++++++ tmk_core/common/chibios/chibios_config.h | 1 + tmk_core/common/chibios/wait.c | 89 ++++++++++++++++++++++ tmk_core/common/test/_wait.h | 22 ++++++ tmk_core/common/wait.h | 125 +++++-------------------------- 8 files changed, 235 insertions(+), 141 deletions(-) create mode 100644 tmk_core/common/arm_atsam/_wait.h create mode 100644 tmk_core/common/avr/_wait.h create mode 100644 tmk_core/common/chibios/_wait.h create mode 100644 tmk_core/common/chibios/wait.c create mode 100644 tmk_core/common/test/_wait.h (limited to 'tmk_core/common') diff --git a/quantum/quantum.h b/quantum/quantum.h index 7c2dcaa829..7c546b5152 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -200,39 +200,6 @@ extern layer_state_t layer_state; # include "usbpd.h" #endif -// Function substitutions to ease GPIO manipulation -#if defined(__AVR__) - -/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal. - * But here's more margin to make it two clocks. */ -# if !defined(GPIO_INPUT_PIN_DELAY) -# define GPIO_INPUT_PIN_DELAY 2 -# endif -# define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) - -#elif defined(__ARMEL__) || defined(__ARMEB__) - -/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus - * to which the GPIO is connected. - * The connected buses differ depending on the various series of MCUs. - * And since the instruction execution clock of the CPU and the bus clock of GPIO are different, - * there is a delay of several clocks to read the change of the input signal. - * - * Define this delay with the GPIO_INPUT_PIN_DELAY macro. - * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used. - * (A fairly large value of 0.25 microseconds is set.) - */ -# if !defined(GPIO_INPUT_PIN_DELAY) -# if defined(STM32_SYSCLK) -# define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4) -# elif defined(KINETIS_SYSCLK_FREQUENCY) -# define GPIO_INPUT_PIN_DELAY (KINETIS_SYSCLK_FREQUENCY / 1000000L / 4) -# endif -# endif -# define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) - -#endif - // For tri-layer void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3); layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3); diff --git a/tmk_core/common/arm_atsam/_wait.h b/tmk_core/common/arm_atsam/_wait.h new file mode 100644 index 0000000000..41b686b56c --- /dev/null +++ b/tmk_core/common/arm_atsam/_wait.h @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "clks.h" + +#define wait_ms(ms) CLK_delay_ms(ms) +#define wait_us(us) CLK_delay_us(us) +#define waitInputPinDelay() diff --git a/tmk_core/common/avr/_wait.h b/tmk_core/common/avr/_wait.h new file mode 100644 index 0000000000..56eb316faf --- /dev/null +++ b/tmk_core/common/avr/_wait.h @@ -0,0 +1,29 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +#define wait_ms(ms) _delay_ms(ms) +#define wait_us(us) _delay_us(us) + +/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal. + * But here's more margin to make it two clocks. */ +#ifndef GPIO_INPUT_PIN_DELAY +# define GPIO_INPUT_PIN_DELAY 2 +#endif + +#define waitInputPinDelay() __builtin_avr_delay_cycles(GPIO_INPUT_PIN_DELAY) diff --git a/tmk_core/common/chibios/_wait.h b/tmk_core/common/chibios/_wait.h new file mode 100644 index 0000000000..5bface53e1 --- /dev/null +++ b/tmk_core/common/chibios/_wait.h @@ -0,0 +1,55 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +/* chThdSleepX of zero maps to infinite - so we map to a tiny delay to still yield */ +#define wait_ms(ms) \ + do { \ + if (ms != 0) { \ + chThdSleepMilliseconds(ms); \ + } else { \ + chThdSleepMicroseconds(1); \ + } \ + } while (0) +#define wait_us(us) \ + do { \ + if (us != 0) { \ + chThdSleepMicroseconds(us); \ + } else { \ + chThdSleepMicroseconds(1); \ + } \ + } while (0) + +/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus + * to which the GPIO is connected. + * The connected buses differ depending on the various series of MCUs. + * And since the instruction execution clock of the CPU and the bus clock of GPIO are different, + * there is a delay of several clocks to read the change of the input signal. + * + * Define this delay with the GPIO_INPUT_PIN_DELAY macro. + * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used. + * (A fairly large value of 0.25 microseconds is set.) + */ + +#include "wait.c" + +#ifndef GPIO_INPUT_PIN_DELAY +# define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4) +#endif + +#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) diff --git a/tmk_core/common/chibios/chibios_config.h b/tmk_core/common/chibios/chibios_config.h index bebf026de7..9a66ac3174 100644 --- a/tmk_core/common/chibios/chibios_config.h +++ b/tmk_core/common/chibios/chibios_config.h @@ -30,4 +30,5 @@ # define USE_I2CV1 # define USE_I2CV1_CONTRIB // for some reason a bunch of ChibiOS-Contrib boards only have clock_speed # define USE_GPIOV1 +# define STM32_SYSCLK KINETIS_SYSCLK_FREQUENCY #endif diff --git a/tmk_core/common/chibios/wait.c b/tmk_core/common/chibios/wait.c new file mode 100644 index 0000000000..c6270fd95e --- /dev/null +++ b/tmk_core/common/chibios/wait.c @@ -0,0 +1,89 @@ +/* Copyright 2021 QMK + * + * 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 3 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 . + */ + +#ifndef __OPTIMIZE__ +# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed" +#endif + +#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t" + +__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */ + /* The argument n must be a constant expression. + * That way, compiler optimization will remove unnecessary code. */ + if (n < 1) { + return; + } + if (n > 8) { + unsigned int n8 = n / 8; + n = n - n8 * 8; + switch (n8) { + case 16: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 15: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 14: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 13: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 12: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 11: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 10: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 9: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 8: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 7: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 6: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 5: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 4: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 3: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 2: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 1: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 0: + break; + } + } + switch (n) { + case 8: + asm volatile("nop" ::: "memory"); + case 7: + asm volatile("nop" ::: "memory"); + case 6: + asm volatile("nop" ::: "memory"); + case 5: + asm volatile("nop" ::: "memory"); + case 4: + asm volatile("nop" ::: "memory"); + case 3: + asm volatile("nop" ::: "memory"); + case 2: + asm volatile("nop" ::: "memory"); + case 1: + asm volatile("nop" ::: "memory"); + case 0: + break; + } +} \ No newline at end of file diff --git a/tmk_core/common/test/_wait.h b/tmk_core/common/test/_wait.h new file mode 100644 index 0000000000..4e22f593b7 --- /dev/null +++ b/tmk_core/common/test/_wait.h @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +void wait_ms(uint32_t ms); +#define wait_us(us) wait_ms(us / 1000) +#define waitInputPinDelay() diff --git a/tmk_core/common/wait.h b/tmk_core/common/wait.h index 28224fe3aa..cf7180fb07 100644 --- a/tmk_core/common/wait.h +++ b/tmk_core/common/wait.h @@ -1,3 +1,18 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #pragma once #include @@ -6,114 +21,8 @@ extern "C" { #endif -#if defined(__ARMEL__) || defined(__ARMEB__) -# ifndef __OPTIMIZE__ -# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed" -# endif - -# define wait_cpuclock(x) wait_cpuclock_allnop(x) - -# define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t" - -__attribute__((always_inline)) static inline void wait_cpuclock_allnop(unsigned int n) { /* n: 1..135 */ - /* The argument n must be a constant expression. - * That way, compiler optimization will remove unnecessary code. */ - if (n < 1) { - return; - } - if (n > 8) { - unsigned int n8 = n / 8; - n = n - n8 * 8; - switch (n8) { - case 16: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 15: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 14: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 13: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 12: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 11: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 10: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 9: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 8: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 7: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 6: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 5: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 4: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 3: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 2: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 1: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 0: - break; - } - } - switch (n) { - case 8: - asm volatile("nop" ::: "memory"); - case 7: - asm volatile("nop" ::: "memory"); - case 6: - asm volatile("nop" ::: "memory"); - case 5: - asm volatile("nop" ::: "memory"); - case 4: - asm volatile("nop" ::: "memory"); - case 3: - asm volatile("nop" ::: "memory"); - case 2: - asm volatile("nop" ::: "memory"); - case 1: - asm volatile("nop" ::: "memory"); - case 0: - break; - } -} -#endif - -#if defined(__AVR__) -# include -# define wait_ms(ms) _delay_ms(ms) -# define wait_us(us) _delay_us(us) -# define wait_cpuclock(x) __builtin_avr_delay_cycles(x) -#elif defined PROTOCOL_CHIBIOS -# include -# define wait_ms(ms) \ - do { \ - if (ms != 0) { \ - chThdSleepMilliseconds(ms); \ - } else { \ - chThdSleepMicroseconds(1); \ - } \ - } while (0) -# define wait_us(us) \ - do { \ - if (us != 0) { \ - chThdSleepMicroseconds(us); \ - } else { \ - chThdSleepMicroseconds(1); \ - } \ - } while (0) -#elif defined PROTOCOL_ARM_ATSAM -# include "clks.h" -# define wait_ms(ms) CLK_delay_ms(ms) -# define wait_us(us) CLK_delay_us(us) -#else // Unit tests -void wait_ms(uint32_t ms); -# define wait_us(us) wait_ms(us / 1000) +#if __has_include_next("_wait.h") +# include_next "_wait.h" /* Include the platforms _wait.h */ #endif #ifdef __cplusplus -- cgit v1.2.3 From 1d341ffbb0dfbf45139c103123549c3c0fec816e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 16 Mar 2021 20:45:21 +0100 Subject: core: add support for MK66F18 (Teensy 3.6) micro controller (#12258) This is in preparation for https://github.com/qmk/qmk_firmware/pull/10171 --- data/schemas/keyboard.jsonschema | 2 +- lib/python/qmk/constants.py | 2 +- quantum/mcu_selection.mk | 27 +++++++++++++++++++++++++++ tmk_core/common/chibios/bootloader.c | 2 +- tmk_core/common/chibios/chibios_config.h | 4 ++++ 5 files changed, 34 insertions(+), 3 deletions(-) (limited to 'tmk_core/common') diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index ec03a8828b..3034242fd9 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -25,7 +25,7 @@ }, "processor": { "type": "string", - "enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F411", "STM32G431", "STM32G474", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"] + "enum": ["cortex-m0", "cortex-m0plus", "cortex-m3", "cortex-m4", "MKL26Z64", "MK20DX128", "MK20DX256", "MK66F18", "STM32F042", "STM32F072", "STM32F103", "STM32F303", "STM32F401", "STM32F411", "STM32G431", "STM32G474", "atmega16u2", "atmega32u2", "atmega16u4", "atmega32u4", "at90usb162", "at90usb646", "at90usb647", "at90usb1286", "at90usb1287", "atmega32a", "atmega328p", "atmega328", "attiny85", "unknown"] }, "board": { "type": "string", diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index 3ed69f3bf9..b5cdaf6a60 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -10,7 +10,7 @@ QMK_FIRMWARE = Path.cwd() MAX_KEYBOARD_SUBFOLDERS = 5 # Supported processor types -CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F411', 'STM32G431', 'STM32G474' +CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK66F18', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F411', 'STM32G431', 'STM32G474' LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85' diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk index f7329fc4d9..53e03a8054 100644 --- a/quantum/mcu_selection.mk +++ b/quantum/mcu_selection.mk @@ -81,6 +81,33 @@ ifneq ($(findstring MK20DX256, $(MCU)),) BOARD ?= PJRC_TEENSY_3_1 endif +ifneq ($(findstring MK66F18, $(MCU)),) + # Cortex version + MCU = cortex-m4 + + # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 + ARMV = 7 + + ## chip/board settings + # - the next two should match the directories in + # /os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) + MCU_FAMILY = KINETIS + MCU_SERIES = MK66F18 + + # Linker script to use + # - it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ + # or /ld/ + MCU_LDSCRIPT ?= MK66FX1M0 + + # Startup code to use + # - it should exist in /os/common/startup/ARMCMx/compilers/GCC/mk/ + MCU_STARTUP ?= MK66F18 + + # Board: it should exist either in /os/hal/boards/, + # /boards/, or drivers/boards/ + BOARD ?= PJRC_TEENSY_3_6 +endif + ifneq ($(findstring STM32F042, $(MCU)),) # Cortex version MCU = cortex-m0 diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index 6cabcc4b81..4a175a6283 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -79,7 +79,7 @@ void enter_bootloader_mode_if_requested(void) { } } -#elif defined(KL2x) || defined(K20x) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS +#elif defined(KL2x) || defined(K20x) || defined(MK66F18) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS /* Kinetis */ # if defined(BOOTLOADER_KIIBOHD) diff --git a/tmk_core/common/chibios/chibios_config.h b/tmk_core/common/chibios/chibios_config.h index 9a66ac3174..1d8ace4955 100644 --- a/tmk_core/common/chibios/chibios_config.h +++ b/tmk_core/common/chibios/chibios_config.h @@ -32,3 +32,7 @@ # define USE_GPIOV1 # define STM32_SYSCLK KINETIS_SYSCLK_FREQUENCY #endif + +#if defined(MK66F18) +# define STM32_SYSCLK KINETIS_SYSCLK_FREQUENCY +#endif -- cgit v1.2.3 From 2ae38e9c43cc689be65f04ea5c101b9f46a38c5e Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 28 Mar 2021 17:59:44 +1100 Subject: LED Matrix: Config functions (#12361) --- quantum/led_matrix.c | 160 ++++++++++++++++++++------------------------- quantum/led_matrix.h | 32 +++++++-- tmk_core/common/keyboard.c | 6 ++ 3 files changed, 104 insertions(+), 94 deletions(-) (limited to 'tmk_core/common') diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c index ceb236809f..ec8ff852d5 100644 --- a/quantum/led_matrix.c +++ b/quantum/led_matrix.c @@ -24,6 +24,8 @@ #include #include +#include + led_eeconfig_t led_matrix_eeconfig; #ifndef MAX @@ -211,23 +213,6 @@ __attribute__((weak)) void led_matrix_indicators_kb(void) {} __attribute__((weak)) void led_matrix_indicators_user(void) {} -// void led_matrix_set_indicator_index(uint8_t *index, uint8_t row, uint8_t column) -// { -// if (row >= MATRIX_ROWS) -// { -// // Special value, 255=none, 254=all -// *index = row; -// } -// else -// { -// // This needs updated to something like -// // uint8_t led[8]; -// // uint8_t led_count = map_row_column_to_led(row, column, led); -// // for(uint8_t i = 0; i < led_count; i++) -// map_row_column_to_led(row, column, index); -// } -// } - void led_matrix_init(void) { led_matrix_driver.init(); @@ -254,109 +239,108 @@ void led_matrix_init(void) { eeconfig_debug_led_matrix(); // display current eeprom values } -// Deals with the messy details of incrementing an integer -static uint8_t increment(uint8_t value, uint8_t step, uint8_t min, uint8_t max) { - int16_t new_value = value; - new_value += step; - return MIN(MAX(new_value, min), max); -} - -static uint8_t decrement(uint8_t value, uint8_t step, uint8_t min, uint8_t max) { - int16_t new_value = value; - new_value -= step; - return MIN(MAX(new_value, min), max); -} - -// void *backlight_get_custom_key_value_eeprom_address(uint8_t led) { -// // 3 bytes per value -// return EECONFIG_LED_MATRIX + (led * 3); -// } - -// void backlight_get_key_value(uint8_t led, uint8_t *value) { -// void *address = backlight_get_custom_key_value_eeprom_address(led); -// value = eeprom_read_byte(address); -// } - -// void backlight_set_key_value(uint8_t row, uint8_t column, uint8_t value) { -// uint8_t led[8]; -// uint8_t led_count = map_row_column_to_led(row, column, led); -// for(uint8_t i = 0; i < led_count; i++) { -// if (led[i] < DRIVER_LED_TOTAL) { -// void *address = backlight_get_custom_key_value_eeprom_address(led[i]); -// eeprom_update_byte(address, value); -// } -// } -// } - uint32_t led_matrix_get_tick(void) { return g_tick; } -void led_matrix_toggle(void) { +void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) { led_matrix_eeconfig.enable ^= 1; - eeconfig_update_led_matrix(); + if (write_to_eeprom) { + eeconfig_update_led_matrix(); + } + dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable); } +void led_matrix_toggle_noeeprom(void) { led_matrix_toggle_eeprom_helper(false); } +void led_matrix_toggle(void) { led_matrix_toggle_eeprom_helper(true); } void led_matrix_enable(void) { - led_matrix_eeconfig.enable = 1; + led_matrix_enable_noeeprom(); eeconfig_update_led_matrix(); } void led_matrix_enable_noeeprom(void) { led_matrix_eeconfig.enable = 1; } void led_matrix_disable(void) { - led_matrix_eeconfig.enable = 0; + led_matrix_disable_noeeprom(); eeconfig_update_led_matrix(); } void led_matrix_disable_noeeprom(void) { led_matrix_eeconfig.enable = 0; } -void led_matrix_step(void) { - led_matrix_eeconfig.mode++; - if (led_matrix_eeconfig.mode >= LED_MATRIX_EFFECT_MAX) { - led_matrix_eeconfig.mode = 1; - } - eeconfig_update_led_matrix(); -} +uint8_t led_matrix_is_enabled(void) { return led_matrix_eeconfig.enable; } -void led_matrix_step_reverse(void) { - led_matrix_eeconfig.mode--; - if (led_matrix_eeconfig.mode < 1) { +void led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) { + if (!led_matrix_eeconfig.enable) { + return; + } + if (mode < 1) { + led_matrix_eeconfig.mode = 1; + } else if (mode >= LED_MATRIX_EFFECT_MAX) { led_matrix_eeconfig.mode = LED_MATRIX_EFFECT_MAX - 1; + } else { + led_matrix_eeconfig.mode = mode; } - eeconfig_update_led_matrix(); + if (write_to_eeprom) { + eeconfig_update_led_matrix(); + } + dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode); } +void led_matrix_mode_noeeprom(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, false); } +void led_matrix_mode(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, true); } -void led_matrix_increase_val(void) { - led_matrix_eeconfig.val = increment(led_matrix_eeconfig.val, 8, 0, LED_MATRIX_MAXIMUM_BRIGHTNESS); - eeconfig_update_led_matrix(); -} +uint8_t led_matrix_get_mode(void) { return led_matrix_eeconfig.mode; } -void led_matrix_decrease_val(void) { - led_matrix_eeconfig.val = decrement(led_matrix_eeconfig.val, 8, 0, LED_MATRIX_MAXIMUM_BRIGHTNESS); - eeconfig_update_led_matrix(); +void led_matrix_step_helper(bool write_to_eeprom) { + uint8_t mode = led_matrix_eeconfig.mode + 1; + led_matrix_mode_eeprom_helper((mode < LED_MATRIX_EFFECT_MAX) ? mode : 1, write_to_eeprom); } +void led_matrix_step_noeeprom(void) { led_matrix_step_helper(false); } +void led_matrix_step(void) { led_matrix_step_helper(true); } -void led_matrix_increase_speed(void) { - led_matrix_eeconfig.speed = increment(led_matrix_eeconfig.speed, 1, 0, 3); - eeconfig_update_led_matrix(); // EECONFIG needs to be increased to support this +void led_matrix_step_reverse_helper(bool write_to_eeprom) { + uint8_t mode = led_matrix_eeconfig.mode - 1; + led_matrix_mode_eeprom_helper((mode < 1) ? LED_MATRIX_EFFECT_MAX - 1 : mode, write_to_eeprom); } +void led_matrix_step_reverse_noeeprom(void) { led_matrix_step_reverse_helper(false); } +void led_matrix_step_reverse(void) { led_matrix_step_reverse_helper(true); } -void led_matrix_decrease_speed(void) { - led_matrix_eeconfig.speed = decrement(led_matrix_eeconfig.speed, 1, 0, 3); - eeconfig_update_led_matrix(); // EECONFIG needs to be increased to support this +void led_matrix_set_val_eeprom_helper(uint8_t val, bool write_to_eeprom) { + if (!led_matrix_eeconfig.enable) { + return; + } + led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val; + if (write_to_eeprom) { + eeconfig_update_led_matrix(); + } + dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val); } +void led_matrix_set_val_noeeprom(uint8_t val) { led_matrix_set_val_eeprom_helper(val, false); } +void led_matrix_set_val(uint8_t val) { led_matrix_set_val_eeprom_helper(val, true); } + +uint8_t led_matrix_get_val(void) { return led_matrix_eeconfig.val; } + +void led_matrix_increase_val_helper(bool write_to_eeprom) { led_matrix_set_val_eeprom_helper(qadd8(led_matrix_eeconfig.val, LED_MATRIX_VAL_STEP), write_to_eeprom); } +void led_matrix_increase_val_noeeprom(void) { led_matrix_increase_val_helper(false); } +void led_matrix_increase_val(void) { led_matrix_increase_val_helper(true); } -void led_matrix_mode(uint8_t mode, bool eeprom_write) { - led_matrix_eeconfig.mode = mode; - if (eeprom_write) { +void led_matrix_decrease_val_helper(bool write_to_eeprom) { led_matrix_set_val_eeprom_helper(qsub8(led_matrix_eeconfig.val, LED_MATRIX_VAL_STEP), write_to_eeprom); } +void led_matrix_decrease_val_noeeprom(void) { led_matrix_decrease_val_helper(false); } +void led_matrix_decrease_val(void) { led_matrix_decrease_val_helper(true); } + +void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) { + led_matrix_eeconfig.speed = speed; + if (write_to_eeprom) { eeconfig_update_led_matrix(); } + dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed); } +void led_matrix_set_speed_noeeprom(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, false); } +void led_matrix_set_speed(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, true); } -uint8_t led_matrix_get_mode(void) { return led_matrix_eeconfig.mode; } +uint8_t led_matrix_get_speed(void) { return led_matrix_eeconfig.speed; } -void led_matrix_set_value_noeeprom(uint8_t val) { led_matrix_eeconfig.val = val; } +void led_matrix_increase_speed_helper(bool write_to_eeprom) { led_matrix_set_speed_eeprom_helper(qadd8(led_matrix_eeconfig.speed, LED_MATRIX_SPD_STEP), write_to_eeprom); } +void led_matrix_increase_speed_noeeprom(void) { led_matrix_increase_speed_helper(false); } +void led_matrix_increase_speed(void) { led_matrix_increase_speed_helper(true); } -void led_matrix_set_value(uint8_t val) { - led_matrix_set_value_noeeprom(val); - eeconfig_update_led_matrix(); -} +void led_matrix_decrease_speed_helper(bool write_to_eeprom) { led_matrix_set_speed_eeprom_helper(qsub8(led_matrix_eeconfig.speed, LED_MATRIX_SPD_STEP), write_to_eeprom); } +void led_matrix_decrease_speed_noeeprom(void) { led_matrix_decrease_speed_helper(false); } +void led_matrix_decrease_speed(void) { led_matrix_decrease_speed_helper(true); } diff --git a/quantum/led_matrix.h b/quantum/led_matrix.h index e4322a1509..fd7ef7d29e 100644 --- a/quantum/led_matrix.h +++ b/quantum/led_matrix.h @@ -28,6 +28,14 @@ # include "is31fl3731-simple.h" #endif +#ifndef LED_MATRIX_LED_FLUSH_LIMIT +# define LED_MATRIX_LED_FLUSH_LIMIT 16 +#endif + +#ifndef LED_MATRIX_LED_PROCESS_LIMIT +# define LED_MATRIX_LED_PROCESS_LIMIT (DRIVER_LED_TOTAL + 4) / 5 +#endif + enum led_matrix_effects { LED_MATRIX_UNIFORM_BRIGHTNESS = 1, // All new effects go above this line @@ -38,7 +46,7 @@ void led_matrix_set_index_value(int index, uint8_t value); void led_matrix_set_index_value_all(uint8_t value); // This runs after another backlight effect and replaces -// colors already set +// values already set void led_matrix_indicators(void); void led_matrix_indicators_kb(void); void led_matrix_indicators_user(void); @@ -62,21 +70,33 @@ bool process_led_matrix(uint16_t keycode, keyrecord_t *record); uint32_t led_matrix_get_tick(void); void led_matrix_toggle(void); +void led_matrix_toggle_noeeprom(void); void led_matrix_enable(void); void led_matrix_enable_noeeprom(void); void led_matrix_disable(void); void led_matrix_disable_noeeprom(void); +uint8_t led_matrix_is_enabled(void); +void led_matrix_mode(uint8_t mode); +void led_matrix_mode_noeeprom(uint8_t mode); +uint8_t led_matrix_get_mode(void); void led_matrix_step(void); +void led_matrix_step_noeeprom(void); void led_matrix_step_reverse(void); +void led_matrix_step_reverse_noeeprom(void); +void led_matrix_set_val(uint8_t val); +void led_matrix_set_val_noeeprom(uint8_t val); +uint8_t led_matrix_get_val(void); void led_matrix_increase_val(void); +void led_matrix_increase_val_noeeprom(void); void led_matrix_decrease_val(void); +void led_matrix_decrease_val_noeeprom(void); +void led_matrix_set_speed(uint8_t speed); +void led_matrix_set_speed_noeeprom(uint8_t speed); +uint8_t led_matrix_get_speed(void); void led_matrix_increase_speed(void); +void led_matrix_increase_speed_noeeprom(void); void led_matrix_decrease_speed(void); -void led_matrix_mode(uint8_t mode, bool eeprom_write); -void led_matrix_mode_noeeprom(uint8_t mode); -uint8_t led_matrix_get_mode(void); -void led_matrix_set_value(uint8_t mode); -void led_matrix_set_value_noeeprom(uint8_t mode); +void led_matrix_decrease_speed_noeeprom(void); typedef struct { /* Perform any initialisation required for the other driver functions to work. */ diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 65d9e00c7a..e473806b21 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -49,6 +49,9 @@ along with this program. If not, see . #ifdef RGBLIGHT_ENABLE # include "rgblight.h" #endif +#ifdef LED_MATRIX_ENABLE +# include "led_matrix.h" +#endif #ifdef RGB_MATRIX_ENABLE # include "rgb_matrix.h" #endif @@ -412,6 +415,9 @@ MATRIX_LOOP_END: rgblight_task(); #endif +#ifdef LED_MATRIX_ENABLE + led_matrix_task(); +#endif #ifdef RGB_MATRIX_ENABLE rgb_matrix_task(); #endif -- cgit v1.2.3 From e95a4f4f52f7792927c480e76dba11c5a8cbd07e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 30 Mar 2021 12:11:40 +0200 Subject: core: add pin_defs for MK66F18 (#12419) This is in preparation for https://github.com/qmk/qmk_firmware/pull/10171 --- tmk_core/common/chibios/pin_defs.h | 81 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'tmk_core/common') diff --git a/tmk_core/common/chibios/pin_defs.h b/tmk_core/common/chibios/pin_defs.h index 86bc1076e8..c03f8de0c2 100644 --- a/tmk_core/common/chibios/pin_defs.h +++ b/tmk_core/common/chibios/pin_defs.h @@ -70,6 +70,23 @@ # define A13 PAL_LINE(GPIOA, 13) # define A14 PAL_LINE(GPIOA, 14) # define A15 PAL_LINE(GPIOA, 15) +# define A16 PAL_LINE(GPIOA, 16) +# define A17 PAL_LINE(GPIOA, 17) +# define A18 PAL_LINE(GPIOA, 18) +# define A19 PAL_LINE(GPIOA, 19) +# define A20 PAL_LINE(GPIOA, 20) +# define A21 PAL_LINE(GPIOA, 21) +# define A22 PAL_LINE(GPIOA, 22) +# define A23 PAL_LINE(GPIOA, 23) +# define A24 PAL_LINE(GPIOA, 24) +# define A25 PAL_LINE(GPIOA, 25) +# define A26 PAL_LINE(GPIOA, 26) +# define A27 PAL_LINE(GPIOA, 27) +# define A28 PAL_LINE(GPIOA, 28) +# define A29 PAL_LINE(GPIOA, 29) +# define A30 PAL_LINE(GPIOA, 30) +# define A31 PAL_LINE(GPIOA, 31) +# define A32 PAL_LINE(GPIOA, 32) # define B0 PAL_LINE(GPIOB, 0) # define B1 PAL_LINE(GPIOB, 1) # define B2 PAL_LINE(GPIOB, 2) @@ -90,6 +107,19 @@ # define B17 PAL_LINE(GPIOB, 17) # define B18 PAL_LINE(GPIOB, 18) # define B19 PAL_LINE(GPIOB, 19) +# define B20 PAL_LINE(GPIOB, 20) +# define B21 PAL_LINE(GPIOB, 21) +# define B22 PAL_LINE(GPIOB, 22) +# define B23 PAL_LINE(GPIOB, 23) +# define B24 PAL_LINE(GPIOB, 24) +# define B25 PAL_LINE(GPIOB, 25) +# define B26 PAL_LINE(GPIOB, 26) +# define B27 PAL_LINE(GPIOB, 27) +# define B28 PAL_LINE(GPIOB, 28) +# define B29 PAL_LINE(GPIOB, 29) +# define B30 PAL_LINE(GPIOB, 30) +# define B31 PAL_LINE(GPIOB, 31) +# define B32 PAL_LINE(GPIOB, 32) # define C0 PAL_LINE(GPIOC, 0) # define C1 PAL_LINE(GPIOC, 1) # define C2 PAL_LINE(GPIOC, 2) @@ -106,6 +136,23 @@ # define C13 PAL_LINE(GPIOC, 13) # define C14 PAL_LINE(GPIOC, 14) # define C15 PAL_LINE(GPIOC, 15) +# define C16 PAL_LINE(GPIOC, 16) +# define C17 PAL_LINE(GPIOC, 17) +# define C18 PAL_LINE(GPIOC, 18) +# define C19 PAL_LINE(GPIOC, 19) +# define C20 PAL_LINE(GPIOC, 20) +# define C21 PAL_LINE(GPIOC, 21) +# define C22 PAL_LINE(GPIOC, 22) +# define C23 PAL_LINE(GPIOC, 23) +# define C24 PAL_LINE(GPIOC, 24) +# define C25 PAL_LINE(GPIOC, 25) +# define C26 PAL_LINE(GPIOC, 26) +# define C27 PAL_LINE(GPIOC, 27) +# define C28 PAL_LINE(GPIOC, 28) +# define C29 PAL_LINE(GPIOC, 29) +# define C30 PAL_LINE(GPIOC, 30) +# define C31 PAL_LINE(GPIOC, 31) +# define C32 PAL_LINE(GPIOC, 32) # define D0 PAL_LINE(GPIOD, 0) # define D1 PAL_LINE(GPIOD, 1) # define D2 PAL_LINE(GPIOD, 2) @@ -122,6 +169,23 @@ # define D13 PAL_LINE(GPIOD, 13) # define D14 PAL_LINE(GPIOD, 14) # define D15 PAL_LINE(GPIOD, 15) +# define D16 PAL_LINE(GPIOD, 16) +# define D17 PAL_LINE(GPIOD, 17) +# define D18 PAL_LINE(GPIOD, 18) +# define D19 PAL_LINE(GPIOD, 19) +# define D20 PAL_LINE(GPIOD, 20) +# define D21 PAL_LINE(GPIOD, 21) +# define D22 PAL_LINE(GPIOD, 22) +# define D23 PAL_LINE(GPIOD, 23) +# define D24 PAL_LINE(GPIOD, 24) +# define D25 PAL_LINE(GPIOD, 25) +# define D26 PAL_LINE(GPIOD, 26) +# define D27 PAL_LINE(GPIOD, 27) +# define D28 PAL_LINE(GPIOD, 28) +# define D29 PAL_LINE(GPIOD, 29) +# define D30 PAL_LINE(GPIOD, 30) +# define D31 PAL_LINE(GPIOD, 31) +# define D32 PAL_LINE(GPIOD, 32) # define E0 PAL_LINE(GPIOE, 0) # define E1 PAL_LINE(GPIOE, 1) # define E2 PAL_LINE(GPIOE, 2) @@ -138,6 +202,23 @@ # define E13 PAL_LINE(GPIOE, 13) # define E14 PAL_LINE(GPIOE, 14) # define E15 PAL_LINE(GPIOE, 15) +# define E16 PAL_LINE(GPIOE, 16) +# define E17 PAL_LINE(GPIOE, 17) +# define E18 PAL_LINE(GPIOE, 18) +# define E19 PAL_LINE(GPIOE, 19) +# define E20 PAL_LINE(GPIOE, 20) +# define E21 PAL_LINE(GPIOE, 21) +# define E22 PAL_LINE(GPIOE, 22) +# define E23 PAL_LINE(GPIOE, 23) +# define E24 PAL_LINE(GPIOE, 24) +# define E25 PAL_LINE(GPIOE, 25) +# define E26 PAL_LINE(GPIOE, 26) +# define E27 PAL_LINE(GPIOE, 27) +# define E28 PAL_LINE(GPIOE, 28) +# define E29 PAL_LINE(GPIOE, 29) +# define E30 PAL_LINE(GPIOE, 30) +# define E31 PAL_LINE(GPIOE, 31) +# define E32 PAL_LINE(GPIOE, 32) # define F0 PAL_LINE(GPIOF, 0) # define F1 PAL_LINE(GPIOF, 1) # define F2 PAL_LINE(GPIOF, 2) -- cgit v1.2.3 From ce99f98bb5217ede628cfbf7e20924346b4279da Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 13 Apr 2021 19:51:03 +1000 Subject: LED Matrix: suspend code (#12509) --- quantum/led_matrix.c | 57 ++++++++++++++++++++++----------------- quantum/led_matrix.h | 7 +++-- tmk_core/common/avr/suspend.c | 3 +++ tmk_core/common/chibios/suspend.c | 6 +++++ tmk_core/common/keyboard.c | 3 +++ 5 files changed, 49 insertions(+), 27 deletions(-) (limited to 'tmk_core/common') diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c index e133764556..5258b3acfd 100644 --- a/quantum/led_matrix.c +++ b/quantum/led_matrix.c @@ -27,8 +27,6 @@ #include -led_eeconfig_t led_matrix_eeconfig; - #ifndef MAX # define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif @@ -74,7 +72,9 @@ led_eeconfig_t led_matrix_eeconfig; # define LED_MATRIX_STARTUP_SPD UINT8_MAX / 2 #endif -bool g_suspend_state = false; +// globals +bool g_suspend_state = false; +led_eeconfig_t led_matrix_eeconfig; // TODO: would like to prefix this with g_ for global consistancy, do this in another pr // Global tick at 20 Hz uint32_t g_tick = 0; @@ -139,10 +139,10 @@ void led_matrix_set_value_all(uint8_t value) { #endif } -bool process_led_matrix(uint16_t keycode, keyrecord_t *record) { - if (record->event.pressed) { +void process_led_matrix(uint8_t row, uint8_t col, bool pressed) { + if (pressed) { uint8_t led[8]; - uint8_t led_count = led_matrix_map_row_column_to_led(record->event.key.row, record->event.key.col, led); + uint8_t led_count = led_matrix_map_row_column_to_led(row, col, led); if (led_count > 0) { for (uint8_t i = LED_HITS_TO_REMEMBER; i > 1; i--) { g_last_led_hit[i - 1] = g_last_led_hit[i - 2]; @@ -155,35 +155,24 @@ bool process_led_matrix(uint16_t keycode, keyrecord_t *record) { } else { #ifdef LED_MATRIX_KEYRELEASES uint8_t led[8]; - uint8_t led_count = led_matrix_map_row_column_to_led(record->event.key.row, record->event.key.col, led); + uint8_t led_count = led_matrix_map_row_column_to_led(row, .col, led); for (uint8_t i = 0; i < led_count; i++) g_key_hit[led[i]] = 255; g_any_key_hit = 255; #endif } - return true; } -void led_matrix_set_suspend_state(bool state) { - if (LED_DISABLE_WHEN_USB_SUSPENDED && state) { - led_matrix_set_value_all(0); // turn off all LEDs when suspending - } - g_suspend_state = state; -} - -bool led_matrix_get_suspend_state(void) { return g_suspend_state; } - -// All LEDs off -void led_matrix_all_off(void) { led_matrix_set_index_value_all(0); } +static void led_matrix_none(void) { led_matrix_set_value_all(0); } // Uniform brightness -void led_matrix_uniform_brightness(void) { led_matrix_set_index_value_all(led_matrix_eeconfig.val); } +void led_matrix_uniform_brightness(void) { led_matrix_set_value_all(led_matrix_eeconfig.val); } void led_matrix_custom(void) {} void led_matrix_task(void) { if (!led_matrix_eeconfig.enable) { - led_matrix_all_off(); + led_matrix_none(); led_matrix_indicators(); return; } @@ -203,13 +192,23 @@ void led_matrix_task(void) { // Ideally we would also stop sending zeros to the LED driver PWM buffers // while suspended and just do a software shutdown. This is a cheap hack for now. - bool suspend_backlight = ((g_suspend_state && LED_DISABLE_WHEN_USB_SUSPENDED) || (LED_DISABLE_TIMEOUT > 0 && g_any_key_hit > LED_DISABLE_TIMEOUT)); - uint8_t effect = suspend_backlight ? 0 : led_matrix_eeconfig.mode; + bool suspend_backlight = +#if LED_DISABLE_WHEN_USB_SUSPENDED == true + g_suspend_state || +#endif // LED_DISABLE_WHEN_USB_SUSPENDED == true +#if LED_DISABLE_TIMEOUT > 0 + (g_any_key_hit > (uint32_t)LED_DISABLE_TIMEOUT) || +#endif // LED_DISABLE_TIMEOUT > 0 + false; + + uint8_t effect = suspend_backlight || !led_matrix_eeconfig.enable ? 0 : led_matrix_eeconfig.mode; // this gets ticked at 20 Hz. // each effect can opt to do calculations // and/or request PWM buffer updates. switch (effect) { + case LED_MATRIX_NONE: + led_matrix_none(); case LED_MATRIX_UNIFORM_BRIGHTNESS: led_matrix_uniform_brightness(); break; @@ -218,7 +217,7 @@ void led_matrix_task(void) { break; } - if (!suspend_backlight) { + if (effect) { led_matrix_indicators(); } @@ -257,10 +256,18 @@ void led_matrix_init(void) { dprintf("led_matrix_init_drivers led_matrix_eeconfig.mode = 0. Write default values to EEPROM.\n"); eeconfig_update_led_matrix_default(); } - eeconfig_debug_led_matrix(); // display current eeprom values } +void led_matrix_set_suspend_state(bool state) { + if (LED_DISABLE_WHEN_USB_SUSPENDED && state) { + led_matrix_set_value_all(0); // turn off all LEDs when suspending + } + g_suspend_state = state; +} + +bool led_matrix_get_suspend_state(void) { return g_suspend_state; } + void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) { led_matrix_eeconfig.enable ^= 1; if (write_to_eeprom) { diff --git a/quantum/led_matrix.h b/quantum/led_matrix.h index ba8f0279a6..48c9483b2d 100644 --- a/quantum/led_matrix.h +++ b/quantum/led_matrix.h @@ -48,8 +48,11 @@ #endif enum led_matrix_effects { - LED_MATRIX_UNIFORM_BRIGHTNESS = 1, + LED_MATRIX_NONE = 0, + + LED_MATRIX_UNIFORM_BRIGHTNESS, // All new effects go above this line + LED_MATRIX_EFFECT_MAX }; @@ -63,7 +66,7 @@ uint8_t led_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *l void led_matrix_set_value(int index, uint8_t value); void led_matrix_set_value_all(uint8_t value); -bool process_led_matrix(uint16_t keycode, keyrecord_t *record); +void process_led_matrix(uint8_t row, uint8_t col, bool pressed); void led_matrix_task(void); diff --git a/tmk_core/common/avr/suspend.c b/tmk_core/common/avr/suspend.c index d52c8ac410..96b19a77fd 100644 --- a/tmk_core/common/avr/suspend.c +++ b/tmk_core/common/avr/suspend.c @@ -28,6 +28,9 @@ # include "rgblight.h" #endif +#ifdef LED_MATRIX_ENABLE +# include "led_matrix.h" +#endif #ifdef RGB_MATRIX_ENABLE # include "rgb_matrix.h" #endif diff --git a/tmk_core/common/chibios/suspend.c b/tmk_core/common/chibios/suspend.c index 17f024caba..b3949185e9 100644 --- a/tmk_core/common/chibios/suspend.c +++ b/tmk_core/common/chibios/suspend.c @@ -24,6 +24,9 @@ # include "rgblight.h" #endif +#ifdef LED_MATRIX_ENABLE +# include "led_matrix.h" +#endif #ifdef RGB_MATRIX_ENABLE # include "rgb_matrix.h" #endif @@ -57,6 +60,9 @@ void suspend_power_down(void) { backlight_set(0); #endif +#ifdef LED_MATRIX_ENABLE + led_matrix_task(); +#endif #ifdef RGB_MATRIX_ENABLE rgb_matrix_task(); #endif diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index e473806b21..132affe7a8 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -330,6 +330,9 @@ void keyboard_init(void) { * This is differnet than keycode events as no layer processing, or filtering occurs. */ void switch_events(uint8_t row, uint8_t col, bool pressed) { +#if defined(LED_MATRIX_ENABLE) + process_led_matrix(row, col, pressed); +#endif #if defined(RGB_MATRIX_ENABLE) process_rgb_matrix(row, col, pressed); #endif -- cgit v1.2.3 From 891d18d3565090abd5f218a8787acc3e25349b73 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 25 Apr 2021 13:40:56 +1000 Subject: Add initial support for tinyuf2 bootloader (when hosted on F411 blackpill) (#12600) * Add support for jumping to tinyuf2 bootloader. Adds blackpill UF2 example. * Update flashing.md * Update chconf.h * Update config.h * Update halconf.h * Update mcuconf.h --- bootloader.mk | 3 + data/schemas/keyboard.jsonschema | 2 +- docs/flashing.md | 26 +++++++ .../onekey/blackpill_f411_tinyuf2/chconf.h | 21 +++++ .../onekey/blackpill_f411_tinyuf2/config.h | 31 ++++++++ .../onekey/blackpill_f411_tinyuf2/halconf.h | 22 ++++++ .../onekey/blackpill_f411_tinyuf2/mcuconf.h | 24 ++++++ .../onekey/blackpill_f411_tinyuf2/readme.md | 9 +++ .../onekey/blackpill_f411_tinyuf2/rules.mk | 10 +++ .../BLACKPILL_STM32_F411/ld/STM32F411xC_tinyuf2.ld | 89 ++++++++++++++++++++++ .../BLACKPILL_STM32_F411/ld/STM32F411xE_tinyuf2.ld | 89 ++++++++++++++++++++++ quantum/mcu_selection.mk | 7 +- tmk_core/chibios.mk | 2 + tmk_core/common/chibios/bootloader.c | 17 ++++- 14 files changed, 349 insertions(+), 3 deletions(-) create mode 100644 keyboards/handwired/onekey/blackpill_f411_tinyuf2/chconf.h create mode 100755 keyboards/handwired/onekey/blackpill_f411_tinyuf2/config.h create mode 100644 keyboards/handwired/onekey/blackpill_f411_tinyuf2/halconf.h create mode 100755 keyboards/handwired/onekey/blackpill_f411_tinyuf2/mcuconf.h create mode 100755 keyboards/handwired/onekey/blackpill_f411_tinyuf2/readme.md create mode 100755 keyboards/handwired/onekey/blackpill_f411_tinyuf2/rules.mk create mode 100644 platforms/chibios/BLACKPILL_STM32_F411/ld/STM32F411xC_tinyuf2.ld create mode 100644 platforms/chibios/BLACKPILL_STM32_F411/ld/STM32F411xE_tinyuf2.ld (limited to 'tmk_core/common') diff --git a/bootloader.mk b/bootloader.mk index fd76446e99..ead52cbe3d 100644 --- a/bootloader.mk +++ b/bootloader.mk @@ -137,3 +137,6 @@ ifeq ($(strip $(BOOTLOADER)), stm32duino) DFU_ARGS = -d 1EAF:0003 -a 2 -R DFU_SUFFIX_ARGS = -v 1EAF -p 0003 endif +ifeq ($(strip $(BOOTLOADER)), tinyuf2) + OPT_DEFS += -DBOOTLOADER_TINYUF2 +endif diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index 3034242fd9..276f66413e 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -34,7 +34,7 @@ }, "bootloader": { "type": "string", - "enum": ["atmel-dfu", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "stm32-dfu", "stm32duino", "unknown", "USBasp"] + "enum": ["atmel-dfu", "bootloadHID", "caterina", "halfkay", "kiibohd", "lufa-dfu", "lufa-ms", "micronucleus", "qmk-dfu", "stm32-dfu", "stm32duino", "unknown", "USBasp", "tinyuf2"] }, "diode_direction": { "type": "string", diff --git a/docs/flashing.md b/docs/flashing.md index 7804a6bad8..83c97444e1 100644 --- a/docs/flashing.md +++ b/docs/flashing.md @@ -249,3 +249,29 @@ Flashing sequence: 2. Wait for the OS to detect the device 3. Flash a .bin file 4. Reset the device into application mode (may be done automatically) + +## tinyuf2 + +Keyboards may opt into supporting the tinyuf2 bootloader. This is currently only supported on the F411 blackpill. + +The `rules.mk` setting for this bootloader is `tinyuf2`, and can be specified at the keymap or user level. + +To ensure compatibility with the tinyuf2 bootloader, make sure this block is present in your `rules.mk`: + +```make +# Bootloader selection +BOOTLOADER = tinyuf2 +``` + +Compatible flashers: + +* Any application able to copy a file from one place to another, such as _macOS Finder_ or _Windows Explorer_. + +Flashing sequence: + +1. Enter the bootloader using any of the following methods: + * Tap the `RESET` keycode + * Double-tap the `nRST` button on the PCB. +2. Wait for the OS to detect the device +3. Copy the .uf2 file to the new USB disk +4. Wait for the keyboard to become available diff --git a/keyboards/handwired/onekey/blackpill_f411_tinyuf2/chconf.h b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/chconf.h new file mode 100644 index 0000000000..3d9a393638 --- /dev/null +++ b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/chconf.h @@ -0,0 +1,21 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#define CH_CFG_ST_FREQUENCY 10000 + +#include_next diff --git a/keyboards/handwired/onekey/blackpill_f411_tinyuf2/config.h b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/config.h new file mode 100755 index 0000000000..95e99aacc1 --- /dev/null +++ b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/config.h @@ -0,0 +1,31 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "config_common.h" + +#define MATRIX_COL_PINS { B0 } +#define MATRIX_ROW_PINS { A7 } +#define UNUSED_PINS + +#define BACKLIGHT_PIN A0 +#define BACKLIGHT_PWM_DRIVER PWMD5 +#define BACKLIGHT_PWM_CHANNEL 1 + +#define RGB_DI_PIN A1 + +#define ADC_PIN A0 diff --git a/keyboards/handwired/onekey/blackpill_f411_tinyuf2/halconf.h b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/halconf.h new file mode 100644 index 0000000000..e15870984f --- /dev/null +++ b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/halconf.h @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#define HAL_USE_I2C TRUE +#define HAL_USE_PWM TRUE + +#include_next diff --git a/keyboards/handwired/onekey/blackpill_f411_tinyuf2/mcuconf.h b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/mcuconf.h new file mode 100755 index 0000000000..ad8aecdb10 --- /dev/null +++ b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/mcuconf.h @@ -0,0 +1,24 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include_next "mcuconf.h" + +#undef STM32_I2C_USE_I2C1 +#define STM32_I2C_USE_I2C1 TRUE + +#undef STM32_PWM_USE_TIM5 +#define STM32_PWM_USE_TIM5 TRUE diff --git a/keyboards/handwired/onekey/blackpill_f411_tinyuf2/readme.md b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/readme.md new file mode 100755 index 0000000000..ff43658f4c --- /dev/null +++ b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/readme.md @@ -0,0 +1,9 @@ +# f411 blackpill onekey + +Supported Hardware: *STM32F411CEU6 WeAct v1.3*. + +To trigger keypress, short together pins *B0* and *A7*. + +This variant requires the TinyUF2 bootloader to be installed. This can be downloaded from the [tinyuf2 releases page](https://github.com/adafruit/tinyuf2/releases). The F401 blackpill binary works for both F401- and F411-based blackpill devices. + +Double-tap reset to enter bootloader mode. Copy the built uf2 file to the device by dragging the file to the new USB disk. \ No newline at end of file diff --git a/keyboards/handwired/onekey/blackpill_f411_tinyuf2/rules.mk b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/rules.mk new file mode 100755 index 0000000000..ec38577b2b --- /dev/null +++ b/keyboards/handwired/onekey/blackpill_f411_tinyuf2/rules.mk @@ -0,0 +1,10 @@ +# MCU name +MCU = STM32F411 + +# Build Options +# change yes to no to disable +# +KEYBOARD_SHARED_EP = yes + +# We want to use the tinyuf2 bootloader... +BOOTLOADER = tinyuf2 \ No newline at end of file diff --git a/platforms/chibios/BLACKPILL_STM32_F411/ld/STM32F411xC_tinyuf2.ld b/platforms/chibios/BLACKPILL_STM32_F411/ld/STM32F411xC_tinyuf2.ld new file mode 100644 index 0000000000..82253d3de5 --- /dev/null +++ b/platforms/chibios/BLACKPILL_STM32_F411/ld/STM32F411xC_tinyuf2.ld @@ -0,0 +1,89 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* + * STM32F411xC memory setup. + */ +MEMORY +{ + flash0 (rx) : org = 0x08000000 + 64k, len = 256k - 64k /* tinyuf2 bootloader requires app to be located at 64k offset for this MCU */ + flash1 (rx) : org = 0x00000000, len = 0 + flash2 (rx) : org = 0x00000000, len = 0 + flash3 (rx) : org = 0x00000000, len = 0 + flash4 (rx) : org = 0x00000000, len = 0 + flash5 (rx) : org = 0x00000000, len = 0 + flash6 (rx) : org = 0x00000000, len = 0 + flash7 (rx) : org = 0x00000000, len = 0 + ram0 (wx) : org = 0x20000000, len = 128k + ram1 (wx) : org = 0x00000000, len = 0 + ram2 (wx) : org = 0x00000000, len = 0 + ram3 (wx) : org = 0x00000000, len = 0 + ram4 (wx) : org = 0x00000000, len = 0 + ram5 (wx) : org = 0x00000000, len = 0 + ram6 (wx) : org = 0x00000000, len = 0 + ram7 (wx) : org = 0x00000000, len = 0 +} + +/* For each data/text section two region are defined, a virtual region + and a load region (_LMA suffix).*/ + +/* Flash region to be used for exception vectors.*/ +REGION_ALIAS("VECTORS_FLASH", flash0); +REGION_ALIAS("VECTORS_FLASH_LMA", flash0); + +/* Flash region to be used for constructors and destructors.*/ +REGION_ALIAS("XTORS_FLASH", flash0); +REGION_ALIAS("XTORS_FLASH_LMA", flash0); + +/* Flash region to be used for code text.*/ +REGION_ALIAS("TEXT_FLASH", flash0); +REGION_ALIAS("TEXT_FLASH_LMA", flash0); + +/* Flash region to be used for read only data.*/ +REGION_ALIAS("RODATA_FLASH", flash0); +REGION_ALIAS("RODATA_FLASH_LMA", flash0); + +/* Flash region to be used for various.*/ +REGION_ALIAS("VARIOUS_FLASH", flash0); +REGION_ALIAS("VARIOUS_FLASH_LMA", flash0); + +/* Flash region to be used for RAM(n) initialization data.*/ +REGION_ALIAS("RAM_INIT_FLASH_LMA", flash0); + +/* RAM region to be used for Main stack. This stack accommodates the processing + of all exceptions and interrupts.*/ +REGION_ALIAS("MAIN_STACK_RAM", ram0); + +/* RAM region to be used for the process stack. This is the stack used by + the main() function.*/ +REGION_ALIAS("PROCESS_STACK_RAM", ram0); + +/* RAM region to be used for data segment.*/ +REGION_ALIAS("DATA_RAM", ram0); +REGION_ALIAS("DATA_RAM_LMA", flash0); + +/* RAM region to be used for BSS segment.*/ +REGION_ALIAS("BSS_RAM", ram0); + +/* RAM region to be used for the default heap.*/ +REGION_ALIAS("HEAP_RAM", ram0); + +/* Generic rules inclusion.*/ +INCLUDE rules.ld + +/* TinyUF2 bootloader reset support */ +_board_dfu_dbl_tap = ORIGIN(ram0) + 64k - 4; /* this is based off the linker file for tinyuf2 */ + diff --git a/platforms/chibios/BLACKPILL_STM32_F411/ld/STM32F411xE_tinyuf2.ld b/platforms/chibios/BLACKPILL_STM32_F411/ld/STM32F411xE_tinyuf2.ld new file mode 100644 index 0000000000..1656c67bf7 --- /dev/null +++ b/platforms/chibios/BLACKPILL_STM32_F411/ld/STM32F411xE_tinyuf2.ld @@ -0,0 +1,89 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* + * STM32F411xE memory setup. + */ +MEMORY +{ + flash0 (rx) : org = 0x08000000 + 64k, len = 512k - 64k /* tinyuf2 bootloader requires app to be located at 64k offset for this MCU */ + flash1 (rx) : org = 0x00000000, len = 0 + flash2 (rx) : org = 0x00000000, len = 0 + flash3 (rx) : org = 0x00000000, len = 0 + flash4 (rx) : org = 0x00000000, len = 0 + flash5 (rx) : org = 0x00000000, len = 0 + flash6 (rx) : org = 0x00000000, len = 0 + flash7 (rx) : org = 0x00000000, len = 0 + ram0 (wx) : org = 0x20000000, len = 128k + ram1 (wx) : org = 0x00000000, len = 0 + ram2 (wx) : org = 0x00000000, len = 0 + ram3 (wx) : org = 0x00000000, len = 0 + ram4 (wx) : org = 0x00000000, len = 0 + ram5 (wx) : org = 0x00000000, len = 0 + ram6 (wx) : org = 0x00000000, len = 0 + ram7 (wx) : org = 0x00000000, len = 0 +} + +/* For each data/text section two region are defined, a virtual region + and a load region (_LMA suffix).*/ + +/* Flash region to be used for exception vectors.*/ +REGION_ALIAS("VECTORS_FLASH", flash0); +REGION_ALIAS("VECTORS_FLASH_LMA", flash0); + +/* Flash region to be used for constructors and destructors.*/ +REGION_ALIAS("XTORS_FLASH", flash0); +REGION_ALIAS("XTORS_FLASH_LMA", flash0); + +/* Flash region to be used for code text.*/ +REGION_ALIAS("TEXT_FLASH", flash0); +REGION_ALIAS("TEXT_FLASH_LMA", flash0); + +/* Flash region to be used for read only data.*/ +REGION_ALIAS("RODATA_FLASH", flash0); +REGION_ALIAS("RODATA_FLASH_LMA", flash0); + +/* Flash region to be used for various.*/ +REGION_ALIAS("VARIOUS_FLASH", flash0); +REGION_ALIAS("VARIOUS_FLASH_LMA", flash0); + +/* Flash region to be used for RAM(n) initialization data.*/ +REGION_ALIAS("RAM_INIT_FLASH_LMA", flash0); + +/* RAM region to be used for Main stack. This stack accommodates the processing + of all exceptions and interrupts.*/ +REGION_ALIAS("MAIN_STACK_RAM", ram0); + +/* RAM region to be used for the process stack. This is the stack used by + the main() function.*/ +REGION_ALIAS("PROCESS_STACK_RAM", ram0); + +/* RAM region to be used for data segment.*/ +REGION_ALIAS("DATA_RAM", ram0); +REGION_ALIAS("DATA_RAM_LMA", flash0); + +/* RAM region to be used for BSS segment.*/ +REGION_ALIAS("BSS_RAM", ram0); + +/* RAM region to be used for the default heap.*/ +REGION_ALIAS("HEAP_RAM", ram0); + +/* Generic rules inclusion.*/ +INCLUDE rules.ld + +/* TinyUF2 bootloader reset support */ +_board_dfu_dbl_tap = ORIGIN(ram0) + 64k - 4; /* this is based off the linker file for tinyuf2 */ + diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk index 2e4d250089..437d354fbe 100644 --- a/quantum/mcu_selection.mk +++ b/quantum/mcu_selection.mk @@ -304,7 +304,12 @@ ifneq ($(findstring STM32F411, $(MCU)),) # Linker script to use # - it should exist either in /os/common/ports/ARMCMx/compilers/GCC/ld/ # or /ld/ - MCU_LDSCRIPT ?= STM32F411xE + ifeq ($(strip $(BOOTLOADER)), tinyuf2) + MCU_LDSCRIPT ?= STM32F411xE_tinyuf2 + FIRMWARE_FORMAT ?= uf2 + else + MCU_LDSCRIPT ?= STM32F411xE + endif # Startup code to use # - it should exist in /os/common/startup/ARMCMx/compilers/GCC/mk/ diff --git a/tmk_core/chibios.mk b/tmk_core/chibios.mk index 40595a1e3b..cdf9ba6495 100644 --- a/tmk_core/chibios.mk +++ b/tmk_core/chibios.mk @@ -190,6 +190,8 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld)","") LDSCRIPT = $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld)","") LDSCRIPT = $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/$(BOARD)/ld/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(TOP_DIR)/platforms/chibios/$(BOARD)/ld/$(MCU_LDSCRIPT).ld else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/common/ld/$(MCU_LDSCRIPT).ld)","") LDSCRIPT = $(TOP_DIR)/platforms/chibios/common/ld/$(MCU_LDSCRIPT).ld else ifneq ("$(wildcard $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld)","") diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index 4a175a6283..f91ac7d9eb 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -13,7 +13,22 @@ # define STM32_BOOTLOADER_DUAL_BANK FALSE #endif -#if STM32_BOOTLOADER_DUAL_BANK +#ifdef BOOTLOADER_TINYUF2 + +# define DBL_TAP_MAGIC 0xf01669ef // From tinyuf2's board_api.h + +// defined by linker script +extern uint32_t _board_dfu_dbl_tap[]; +# define DBL_TAP_REG _board_dfu_dbl_tap[0] + +void bootloader_jump(void) { + DBL_TAP_REG = DBL_TAP_MAGIC; + NVIC_SystemReset(); +} + +void enter_bootloader_mode_if_requested(void) { /* not needed, no two-stage reset */ } + +#elif STM32_BOOTLOADER_DUAL_BANK // Need pin definitions # include "config_common.h" -- cgit v1.2.3 From 412e7a03e49d044a2dea0fb38b05fb24c8d6eabe Mon Sep 17 00:00:00 2001 From: Purdea Andrei Date: Sun, 25 Apr 2021 11:57:49 +0300 Subject: eeprom driver: Refactor where eeprom driver initialisation (and EEPROM emulation initialisation) occurs to make it non-target-specific. (#12671) --- tmk_core/common/keyboard.c | 12 ++++++++++++ tmk_core/protocol/chibios/main.c | 13 ------------- 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'tmk_core/common') diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 132affe7a8..24baf41c00 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -94,6 +94,12 @@ along with this program. If not, see . #ifdef DIP_SWITCH_ENABLE # include "dip_switch.h" #endif +#ifdef STM32_EEPROM_ENABLE +# include "eeprom_stm32.h" +#endif +#ifdef EEPROM_DRIVER +# include "eeprom_driver.h" +#endif static uint32_t last_input_modification_time = 0; uint32_t last_input_activity_time(void) { return last_input_modification_time; } @@ -231,6 +237,12 @@ void keyboard_setup(void) { disable_jtag(); #endif print_set_sendchar(sendchar); +#ifdef STM32_EEPROM_ENABLE + EEPROM_Init(); +#endif +#ifdef EEPROM_DRIVER + eeprom_driver_init(); +#endif matrix_setup(); keyboard_pre_init_kb(); } diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index 63e4c99d21..e2ec011186 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -51,12 +51,6 @@ #ifdef MIDI_ENABLE # include "qmk_midi.h" #endif -#ifdef STM32_EEPROM_ENABLE -# include "eeprom_stm32.h" -#endif -#ifdef EEPROM_DRIVER -# include "eeprom_driver.h" -#endif #include "suspend.h" #include "wait.h" @@ -150,13 +144,6 @@ int main(void) { halInit(); chSysInit(); -#ifdef STM32_EEPROM_ENABLE - EEPROM_Init(); -#endif -#ifdef EEPROM_DRIVER - eeprom_driver_init(); -#endif - // TESTING // chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL); -- cgit v1.2.3 From 8c12fa2e59583b22969aea2c6eeab054a23ea064 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 25 Apr 2021 19:00:22 +1000 Subject: Format code according to conventions (#12682) Co-authored-by: QMK Bot --- tmk_core/common/chibios/bootloader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tmk_core/common') diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index f91ac7d9eb..11f7abf432 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -26,7 +26,8 @@ void bootloader_jump(void) { NVIC_SystemReset(); } -void enter_bootloader_mode_if_requested(void) { /* not needed, no two-stage reset */ } +void enter_bootloader_mode_if_requested(void) { /* not needed, no two-stage reset */ +} #elif STM32_BOOTLOADER_DUAL_BANK -- cgit v1.2.3 From d8167779cdfb243cb140782210d2cc6a7cb9b123 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Wed, 28 Apr 2021 19:39:54 -0700 Subject: Change RGB/LED Matrix to use a simple define for USB suspend (#12697) --- docs/feature_rgb_matrix.md | 2 +- keyboards/mt64rgb/keymaps/default/keymap.c | 32 +++++++++++++++--------------- keyboards/mt84/keymaps/default/keymap.c | 30 +++++++++++++--------------- quantum/led_matrix.c | 18 ++++++++--------- quantum/led_matrix.h | 1 - quantum/rgb_matrix.c | 18 ++++++++--------- quantum/rgb_matrix.h | 1 - tmk_core/common/avr/suspend.c | 7 +++++++ tmk_core/common/chibios/suspend.c | 6 ++++++ 9 files changed, 62 insertions(+), 53 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md index 046b1f17fa..63ff7d6ad6 100644 --- a/docs/feature_rgb_matrix.md +++ b/docs/feature_rgb_matrix.md @@ -437,7 +437,7 @@ These are defined in [`rgblight_list.h`](https://github.com/qmk/qmk_firmware/blo #define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses) #define RGB_DISABLE_TIMEOUT 0 // number of milliseconds to wait until rgb automatically turns off #define RGB_DISABLE_AFTER_TIMEOUT 0 // OBSOLETE: number of ticks to wait until disabling effects -#define RGB_DISABLE_WHEN_USB_SUSPENDED false // turn off effects when suspended +#define RGB_DISABLE_WHEN_USB_SUSPENDED // turn off effects when suspended #define RGB_MATRIX_LED_PROCESS_LIMIT (DRIVER_LED_TOTAL + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness) #define RGB_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness) #define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255 diff --git a/keyboards/mt64rgb/keymaps/default/keymap.c b/keyboards/mt64rgb/keymaps/default/keymap.c index c7e027ba73..84f3b1d35e 100644 --- a/keyboards/mt64rgb/keymaps/default/keymap.c +++ b/keyboards/mt64rgb/keymaps/default/keymap.c @@ -1,18 +1,18 @@ -/* Copyright 2020 MT - * - * 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 . - */ +/* Copyright 2020 MT + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include QMK_KEYBOARD_H @@ -37,7 +37,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { }; void rgb_matrix_indicators_user(void) { - if (!g_suspend_state && layer_state_is(1)) { + if (layer_state_is(1)) { rgb_matrix_set_color(77,0xFF, 0x80, 0x00); } if (host_keyboard_led_state().caps_lock) { diff --git a/keyboards/mt84/keymaps/default/keymap.c b/keyboards/mt84/keymaps/default/keymap.c index fc8481da9d..bb7d5b447f 100644 --- a/keyboards/mt84/keymaps/default/keymap.c +++ b/keyboards/mt84/keymaps/default/keymap.c @@ -1,18 +1,18 @@ /* Copyright 2020 mt<704340378@qq.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include QMK_KEYBOARD_H @@ -44,12 +44,10 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { void rgb_matrix_indicators_user(void) { led_t led_state = host_keyboard_led_state(); - if (!g_suspend_state) { switch (get_highest_layer(layer_state)) { case _FN: rgb_matrix_set_color(77,0xFF, 0x80, 0x00); break; - } } if (led_state.caps_lock) { rgb_matrix_set_color(46, 0xFF, 0xFF, 0xFF); diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c index 72eb5190b3..5dd37dff14 100644 --- a/quantum/led_matrix.c +++ b/quantum/led_matrix.c @@ -35,8 +35,8 @@ # define LED_DISABLE_TIMEOUT 0 #endif -#ifndef LED_DISABLE_WHEN_USB_SUSPENDED -# define LED_DISABLE_WHEN_USB_SUSPENDED false +#if LED_DISABLE_WHEN_USB_SUSPENDED == false +# undef LED_DISABLE_WHEN_USB_SUSPENDED #endif #if !defined(LED_MATRIX_MAXIMUM_BRIGHTNESS) || LED_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX @@ -65,7 +65,6 @@ #endif // globals -bool g_suspend_state = false; led_eeconfig_t led_matrix_eeconfig; // TODO: would like to prefix this with g_ for global consistancy, do this in another pr uint32_t g_led_timer; #ifdef LED_MATRIX_FRAMEBUFFER_EFFECTS @@ -76,6 +75,7 @@ last_hit_t g_last_hit_tracker; #endif // LED_MATRIX_KEYREACTIVE_ENABLED // internals +static bool suspend_state = false; static uint8_t led_last_enable = UINT8_MAX; static uint8_t led_last_effect = UINT8_MAX; static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false}; @@ -325,9 +325,7 @@ void led_matrix_task(void) { // Ideally we would also stop sending zeros to the LED driver PWM buffers // while suspended and just do a software shutdown. This is a cheap hack for now. bool suspend_backlight = -#if LED_DISABLE_WHEN_USB_SUSPENDED == true - g_suspend_state || -#endif // LED_DISABLE_WHEN_USB_SUSPENDED == true + suspend_state || #if LED_DISABLE_TIMEOUT > 0 (led_anykey_timer > (uint32_t)LED_DISABLE_TIMEOUT) || #endif // LED_DISABLE_TIMEOUT > 0 @@ -416,13 +414,15 @@ void led_matrix_init(void) { } void led_matrix_set_suspend_state(bool state) { - if (LED_DISABLE_WHEN_USB_SUSPENDED && state) { +#ifdef LED_DISABLE_WHEN_USB_SUSPENDED + if (state) { led_matrix_set_value_all(0); // turn off all LEDs when suspending } - g_suspend_state = state; + suspend_state = state; +#endif } -bool led_matrix_get_suspend_state(void) { return g_suspend_state; } +bool led_matrix_get_suspend_state(void) { return suspend_state; } void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) { led_matrix_eeconfig.enable ^= 1; diff --git a/quantum/led_matrix.h b/quantum/led_matrix.h index f35bbe2096..a3fa552b0a 100644 --- a/quantum/led_matrix.h +++ b/quantum/led_matrix.h @@ -134,7 +134,6 @@ extern const led_matrix_driver_t led_matrix_driver; extern led_eeconfig_t led_matrix_eeconfig; -extern bool g_suspend_state; extern uint32_t g_led_timer; extern led_config_t g_led_config; #ifdef LED_MATRIX_KEYREACTIVE_ENABLED diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index 8aae486034..1f76049430 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c @@ -67,8 +67,8 @@ __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv # define RGB_DISABLE_TIMEOUT 0 #endif -#ifndef RGB_DISABLE_WHEN_USB_SUSPENDED -# define RGB_DISABLE_WHEN_USB_SUSPENDED false +#if RGB_DISABLE_WHEN_USB_SUSPENDED == false +# undef RGB_DISABLE_WHEN_USB_SUSPENDED #endif #if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX @@ -118,7 +118,6 @@ __attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv #endif // globals -bool g_suspend_state = false; rgb_config_t rgb_matrix_config; // TODO: would like to prefix this with g_ for global consistancy, do this in another pr uint32_t g_rgb_timer; #ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS @@ -129,6 +128,7 @@ last_hit_t g_last_hit_tracker; #endif // RGB_MATRIX_KEYREACTIVE_ENABLED // internals +static bool suspend_state = false; static uint8_t rgb_last_enable = UINT8_MAX; static uint8_t rgb_last_effect = UINT8_MAX; static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; @@ -410,9 +410,7 @@ void rgb_matrix_task(void) { // Ideally we would also stop sending zeros to the LED driver PWM buffers // while suspended and just do a software shutdown. This is a cheap hack for now. bool suspend_backlight = -#if RGB_DISABLE_WHEN_USB_SUSPENDED == true - g_suspend_state || -#endif // RGB_DISABLE_WHEN_USB_SUSPENDED == true + suspend_state || #if RGB_DISABLE_TIMEOUT > 0 (rgb_anykey_timer > (uint32_t)RGB_DISABLE_TIMEOUT) || #endif // RGB_DISABLE_TIMEOUT > 0 @@ -501,13 +499,15 @@ void rgb_matrix_init(void) { } void rgb_matrix_set_suspend_state(bool state) { - if (RGB_DISABLE_WHEN_USB_SUSPENDED && state) { +#ifdef RGB_DISABLE_WHEN_USB_SUSPENDED + if (state) { rgb_matrix_set_color_all(0, 0, 0); // turn off all LEDs when suspending } - g_suspend_state = state; + suspend_state = state; +#endif } -bool rgb_matrix_get_suspend_state(void) { return g_suspend_state; } +bool rgb_matrix_get_suspend_state(void) { return suspend_state; } void rgb_matrix_toggle_eeprom_helper(bool write_to_eeprom) { rgb_matrix_config.enable ^= 1; diff --git a/quantum/rgb_matrix.h b/quantum/rgb_matrix.h index bb8bcfab68..a615b8422c 100644 --- a/quantum/rgb_matrix.h +++ b/quantum/rgb_matrix.h @@ -216,7 +216,6 @@ extern const rgb_matrix_driver_t rgb_matrix_driver; extern rgb_config_t rgb_matrix_config; -extern bool g_suspend_state; extern uint32_t g_rgb_timer; extern led_config_t g_led_config; #ifdef RGB_MATRIX_KEYREACTIVE_ENABLED diff --git a/tmk_core/common/avr/suspend.c b/tmk_core/common/avr/suspend.c index 96b19a77fd..1c2bf9cb46 100644 --- a/tmk_core/common/avr/suspend.c +++ b/tmk_core/common/avr/suspend.c @@ -163,6 +163,10 @@ void suspend_power_down(void) { rgblight_suspend(); # endif +# if defined(RGB_MATRIX_ENABLE) + rgb_matrix_set_suspend_state(true); +# endif + // Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt) # if defined(WDT_vect) power_down(WDTO_15MS); @@ -214,6 +218,9 @@ void suspend_wakeup_init(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_wakeup(); #endif +# if defined(RGB_MATRIX_ENABLE) + rgb_matrix_set_suspend_state(false); +# endif suspend_wakeup_init_kb(); } diff --git a/tmk_core/common/chibios/suspend.c b/tmk_core/common/chibios/suspend.c index b3949185e9..1f4f93c455 100644 --- a/tmk_core/common/chibios/suspend.c +++ b/tmk_core/common/chibios/suspend.c @@ -83,6 +83,9 @@ void suspend_power_down(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_suspend(); #endif +# if defined(RGB_MATRIX_ENABLE) + rgb_matrix_set_suspend_state(true); +# endif #ifdef AUDIO_ENABLE stop_all_notes(); #endif /* AUDIO_ENABLE */ @@ -151,5 +154,8 @@ void suspend_wakeup_init(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_wakeup(); #endif +# if defined(RGB_MATRIX_ENABLE) + rgb_matrix_set_suspend_state(false); +# endif suspend_wakeup_init_kb(); } -- cgit v1.2.3 From 39bc8163d013b5c7e148b7e95b3883dc0adb95ba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 13:36:47 +1000 Subject: [CI] Format code according to conventions (#12731) Co-authored-by: QMK Bot --- quantum/led_matrix.c | 9 ++++----- quantum/rgb_matrix.c | 9 ++++----- tmk_core/common/avr/suspend.c | 4 ++-- tmk_core/common/chibios/suspend.c | 8 ++++---- 4 files changed, 14 insertions(+), 16 deletions(-) (limited to 'tmk_core/common') diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c index 5dd37dff14..3674c9b97e 100644 --- a/quantum/led_matrix.c +++ b/quantum/led_matrix.c @@ -75,7 +75,7 @@ last_hit_t g_last_hit_tracker; #endif // LED_MATRIX_KEYREACTIVE_ENABLED // internals -static bool suspend_state = false; +static bool suspend_state = false; static uint8_t led_last_enable = UINT8_MAX; static uint8_t led_last_effect = UINT8_MAX; static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false}; @@ -324,12 +324,11 @@ void led_matrix_task(void) { // Ideally we would also stop sending zeros to the LED driver PWM buffers // while suspended and just do a software shutdown. This is a cheap hack for now. - bool suspend_backlight = - suspend_state || + bool suspend_backlight = suspend_state || #if LED_DISABLE_TIMEOUT > 0 - (led_anykey_timer > (uint32_t)LED_DISABLE_TIMEOUT) || + (led_anykey_timer > (uint32_t)LED_DISABLE_TIMEOUT) || #endif // LED_DISABLE_TIMEOUT > 0 - false; + false; uint8_t effect = suspend_backlight || !led_matrix_eeconfig.enable ? 0 : led_matrix_eeconfig.mode; diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index 1f76049430..097c5302df 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c @@ -128,7 +128,7 @@ last_hit_t g_last_hit_tracker; #endif // RGB_MATRIX_KEYREACTIVE_ENABLED // internals -static bool suspend_state = false; +static bool suspend_state = false; static uint8_t rgb_last_enable = UINT8_MAX; static uint8_t rgb_last_effect = UINT8_MAX; static effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false}; @@ -409,12 +409,11 @@ void rgb_matrix_task(void) { // Ideally we would also stop sending zeros to the LED driver PWM buffers // while suspended and just do a software shutdown. This is a cheap hack for now. - bool suspend_backlight = - suspend_state || + bool suspend_backlight = suspend_state || #if RGB_DISABLE_TIMEOUT > 0 - (rgb_anykey_timer > (uint32_t)RGB_DISABLE_TIMEOUT) || + (rgb_anykey_timer > (uint32_t)RGB_DISABLE_TIMEOUT) || #endif // RGB_DISABLE_TIMEOUT > 0 - false; + false; uint8_t effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode; diff --git a/tmk_core/common/avr/suspend.c b/tmk_core/common/avr/suspend.c index 1c2bf9cb46..e9ee0aefdd 100644 --- a/tmk_core/common/avr/suspend.c +++ b/tmk_core/common/avr/suspend.c @@ -218,9 +218,9 @@ void suspend_wakeup_init(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_wakeup(); #endif -# if defined(RGB_MATRIX_ENABLE) +#if defined(RGB_MATRIX_ENABLE) rgb_matrix_set_suspend_state(false); -# endif +#endif suspend_wakeup_init_kb(); } diff --git a/tmk_core/common/chibios/suspend.c b/tmk_core/common/chibios/suspend.c index 1f4f93c455..14a9aecb31 100644 --- a/tmk_core/common/chibios/suspend.c +++ b/tmk_core/common/chibios/suspend.c @@ -83,9 +83,9 @@ void suspend_power_down(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_suspend(); #endif -# if defined(RGB_MATRIX_ENABLE) +#if defined(RGB_MATRIX_ENABLE) rgb_matrix_set_suspend_state(true); -# endif +#endif #ifdef AUDIO_ENABLE stop_all_notes(); #endif /* AUDIO_ENABLE */ @@ -154,8 +154,8 @@ void suspend_wakeup_init(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_wakeup(); #endif -# if defined(RGB_MATRIX_ENABLE) +#if defined(RGB_MATRIX_ENABLE) rgb_matrix_set_suspend_state(false); -# endif +#endif suspend_wakeup_init_kb(); } -- cgit v1.2.3 From f0b30e0027cb890c7510fa22e5824a43c0d9e86f Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sun, 9 May 2021 23:21:09 -0700 Subject: Make Swap Hands use PROGMEM (#12284) This converts the array that the Swap Hands feature uses to use PROGMEM, and to read from that array, as such. Since this array never changes at runtime, there is no reason to keep it in memory. Especially for AVR boards, as memory is a precious resource. --- docs/feature_swap_hands.md | 2 +- docs/ja/feature_swap_hands.md | 2 +- keyboards/40percentclub/25/25.c | 2 +- keyboards/40percentclub/6lit/6lit.c | 2 +- keyboards/40percentclub/foobar/foobar.c | 2 +- keyboards/adkb96/adkb96.c | 2 +- keyboards/centromere/centromere.c | 10 +++++----- keyboards/crkbd/crkbd.c | 3 +-- keyboards/ergo42/ergo42.c | 2 +- keyboards/ergodox_ez/ergodox_ez.c | 2 +- keyboards/ergodox_infinity/ergodox_infinity.c | 2 +- keyboards/ergoslab/rev1/rev1.c | 2 +- keyboards/gergo/gergo.c | 2 +- keyboards/handwired/dactyl/dactyl.c | 2 +- .../dactyl_manuform/5x6_right_trackball/5x6_right_trackball.c | 2 +- keyboards/handwired/pterodactyl/pterodactyl.c | 2 +- keyboards/jian/handwired/handwired.c | 2 +- keyboards/jian/nsrev2/nsrev2.c | 2 +- keyboards/jian/rev1/rev1.c | 2 +- keyboards/jian/rev2/rev2.c | 2 +- keyboards/keebio/iris/rev1/rev1.c | 2 +- keyboards/keebio/iris/rev1_led/rev1_led.c | 2 +- keyboards/keebio/iris/rev2/rev2.c | 2 +- keyboards/keebio/iris/rev3/rev3.c | 2 +- keyboards/keebio/levinson/levinson.c | 2 +- keyboards/keebio/wavelet/wavelet.c | 2 +- keyboards/kyria/rev1/rev1.c | 4 ++-- keyboards/lets_split/lets_split.c | 2 +- keyboards/lets_split_eh/lets_split_eh.c | 2 +- keyboards/mitosis/mitosis.c | 2 +- keyboards/moonlander/moonlander.c | 2 +- keyboards/niu_mini/niu_mini.c | 2 +- keyboards/orthodox/rev1/rev1.c | 2 +- keyboards/orthodox/rev3/rev3.c | 2 +- keyboards/orthodox/rev3_teensy/rev3_teensy.c | 2 +- keyboards/planck/planck.c | 2 +- keyboards/preonic/rev1/rev1.c | 2 +- keyboards/preonic/rev2/rev2.c | 2 +- keyboards/preonic/rev3/rev3.c | 2 +- keyboards/redox_w/redox_w.c | 2 +- keyboards/sirius/uni660/rev1/rev1.c | 2 +- keyboards/sirius/uni660/rev2/rev2.c | 2 +- keyboards/splitty/splitty.c | 2 +- keyboards/telophase/telophase.c | 2 +- keyboards/vitamins_included/vitamins_included.c | 2 +- tmk_core/common/action.c | 3 ++- tmk_core/common/action.h | 2 +- 47 files changed, 53 insertions(+), 53 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/feature_swap_hands.md b/docs/feature_swap_hands.md index 009477d203..cbc574b6b8 100644 --- a/docs/feature_swap_hands.md +++ b/docs/feature_swap_hands.md @@ -7,7 +7,7 @@ The swap-hands action allows support for one-handed typing without requiring a s The configuration table is a simple 2-dimensional array to map from column/row to new column/row. Example `hand_swap_config` for Planck: ```C -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/docs/ja/feature_swap_hands.md b/docs/ja/feature_swap_hands.md index 3150801c55..5164584e8a 100644 --- a/docs/ja/feature_swap_hands.md +++ b/docs/ja/feature_swap_hands.md @@ -12,7 +12,7 @@ 設定テーブルは列/行から新しい列/行にマップするための単純な2次元配列です。Planck の `hand_swap_config` の例: ```C -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/40percentclub/25/25.c b/keyboards/40percentclub/25/25.c index 54a42d263c..de1b038aa8 100644 --- a/keyboards/40percentclub/25/25.c +++ b/keyboards/40percentclub/25/25.c @@ -44,7 +44,7 @@ void led_set_kb(uint8_t usb_led) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{4, 5}, {3, 5}, {2, 5}, {1, 5}, {0, 5}}, {{4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}}, {{4, 7}, {3, 7}, {2, 7}, {1, 7}, {0, 7}}, diff --git a/keyboards/40percentclub/6lit/6lit.c b/keyboards/40percentclub/6lit/6lit.c index fd3ae7d009..59d7e33bd6 100644 --- a/keyboards/40percentclub/6lit/6lit.c +++ b/keyboards/40percentclub/6lit/6lit.c @@ -44,7 +44,7 @@ void led_set_kb(uint8_t usb_led) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{2, 2}, {1, 2}, {0, 2}}, {{2, 3}, {1, 3}, {0, 3}}, {{0, 0}, {1, 0}, {2, 0}}, diff --git a/keyboards/40percentclub/foobar/foobar.c b/keyboards/40percentclub/foobar/foobar.c index c032056a15..b0a1518df1 100644 --- a/keyboards/40percentclub/foobar/foobar.c +++ b/keyboards/40percentclub/foobar/foobar.c @@ -44,7 +44,7 @@ void led_set_kb(uint8_t usb_led) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}}, {{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}}, {{4, 5}, {3, 5}, {2, 5}, {1, 5}, {0, 5}}, diff --git a/keyboards/adkb96/adkb96.c b/keyboards/adkb96/adkb96.c index 9a1c85a2ce..93230ee00c 100644 --- a/keyboards/adkb96/adkb96.c +++ b/keyboards/adkb96/adkb96.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}, {6, 6}, {7, 6}}, {{0, 7}, {1, 7}, {2, 7}, {3, 7}, {4, 7}, {5, 7}, {6, 7}, {7, 7}}, {{0, 8}, {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {6, 8}, {7, 8}}, diff --git a/keyboards/centromere/centromere.c b/keyboards/centromere/centromere.c index 269c60fd75..8d46520e38 100644 --- a/keyboards/centromere/centromere.c +++ b/keyboards/centromere/centromere.c @@ -5,21 +5,21 @@ void led_init(void) { setPinOutput(C4); // Set red LED pin as output setPinOutput(C5); // Set blue LED pin as output setPinOutput(D1); // Set green LED pin as output - + writePinHigh(C4); // Turn off red LED pin writePinHigh(C5); // Turn off blue LED pin writePinHigh(D1); // Turn off green LED pin - + #else setPinOutput(F4); // Set red LED pin as output setPinOutput(F5); // Set blue LED pin as output setPinOutput(D1); // Set green LED pin as output - + writePinHigh(F4); // Turn off red LED pin writePinHigh(F5); // Turn off blue LED pin writePinHigh(D1); // Turn off green LED pin - + #endif } @@ -33,7 +33,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/crkbd/crkbd.c b/keyboards/crkbd/crkbd.c index ab1381a39b..6220c9822a 100644 --- a/keyboards/crkbd/crkbd.c +++ b/keyboards/crkbd/crkbd.c @@ -20,7 +20,7 @@ along with this program. If not, see . #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { // Left {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}, @@ -33,4 +33,3 @@ const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3}, {5, 3}} }; #endif - diff --git a/keyboards/ergo42/ergo42.c b/keyboards/ergo42/ergo42.c index e42dcb2261..bd7a7ffe19 100644 --- a/keyboards/ergo42/ergo42.c +++ b/keyboards/ergo42/ergo42.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}, {6, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}, {6, 5}}, diff --git a/keyboards/ergodox_ez/ergodox_ez.c b/keyboards/ergodox_ez/ergodox_ez.c index e6f48c49fb..4d35675211 100644 --- a/keyboards/ergodox_ez/ergodox_ez.c +++ b/keyboards/ergodox_ez/ergodox_ez.c @@ -220,7 +220,7 @@ uint8_t ergodox_left_leds_update(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{0,13}, {1,13}, {2,13}, {3,13}, {4,13}, {5,13}}, {{0,12}, {1,12}, {2,12}, {3,12}, {4,12}, {5,12}}, diff --git a/keyboards/ergodox_infinity/ergodox_infinity.c b/keyboards/ergodox_infinity/ergodox_infinity.c index a9962f96a0..97b628470b 100644 --- a/keyboards/ergodox_infinity/ergodox_infinity.c +++ b/keyboards/ergodox_infinity/ergodox_infinity.c @@ -205,7 +205,7 @@ void ergodox_right_led_3_set(uint8_t n) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 9}, {1, 9}, {2, 9}, {3, 9}, {4, 9}}, {{0, 10}, {1, 10}, {2, 10}, {3, 10}, {4, 10}}, {{0, 11}, {1, 11}, {2, 11}, {3, 11}, {4, 11}}, diff --git a/keyboards/ergoslab/rev1/rev1.c b/keyboards/ergoslab/rev1/rev1.c index 0e3e0fe996..115dab51e4 100644 --- a/keyboards/ergoslab/rev1/rev1.c +++ b/keyboards/ergoslab/rev1/rev1.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}, {{0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}}, {{0, 7}, {1, 7}, {2, 7}, {3, 7}, {4, 7}, {5, 7}}, diff --git a/keyboards/gergo/gergo.c b/keyboards/gergo/gergo.c index fab8d0f44c..ba359f8145 100644 --- a/keyboards/gergo/gergo.c +++ b/keyboards/gergo/gergo.c @@ -65,7 +65,7 @@ out: return mcp23018_status; } -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { { {0,0}, {0,7}, {2,7}, {3,7} }, { {0,8}, {1,8}, {2,8}, {3,8} }, { {0,9}, {1,9}, {2,9}, {3,9} }, diff --git a/keyboards/handwired/dactyl/dactyl.c b/keyboards/handwired/dactyl/dactyl.c index 81b9dce0f9..ff9b4e08af 100644 --- a/keyboards/handwired/dactyl/dactyl.c +++ b/keyboards/handwired/dactyl/dactyl.c @@ -4,7 +4,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0,11}, {0,10}, {0,9}, {0,8}, {0,7}, {0,6}, {0,5}, {0,4}, {0,3}, {0,2}, {0,1}, {0,0}}, {{1,11}, {1,11}, {1,9}, {1,8}, {1,7}, {1,6}, {1,5}, {1,4}, {1,3}, {1,2}, {1,1}, {1,0}}, {{2,11}, {2,12}, {2,9}, {2,8}, {2,7}, {2,6}, {2,5}, {2,4}, {2,3}, {2,2}, {2,1}, {2,0}}, diff --git a/keyboards/handwired/dactyl_manuform/5x6_right_trackball/5x6_right_trackball.c b/keyboards/handwired/dactyl_manuform/5x6_right_trackball/5x6_right_trackball.c index 69f7500957..4f1b3f0bbf 100644 --- a/keyboards/handwired/dactyl_manuform/5x6_right_trackball/5x6_right_trackball.c +++ b/keyboards/handwired/dactyl_manuform/5x6_right_trackball/5x6_right_trackball.c @@ -206,7 +206,7 @@ void pointing_device_send(void) { #endif #ifdef SWAP_HANDS_ENABLE -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{5, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}}, {{5, 7}, {4, 7}, {3, 7}, {2, 7}, {1, 7}, {0, 7}}, diff --git a/keyboards/handwired/pterodactyl/pterodactyl.c b/keyboards/handwired/pterodactyl/pterodactyl.c index 736b0a62c4..f379569f5a 100644 --- a/keyboards/handwired/pterodactyl/pterodactyl.c +++ b/keyboards/handwired/pterodactyl/pterodactyl.c @@ -3,7 +3,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0,11}, {0,10}, {0,9}, {0,8}, {0,7}, {0,6}, {0,5}, {0,4}, {0,3}, {0,2}, {0,1}, {0,0}}, {{1,11}, {1,11}, {1,9}, {1,8}, {1,7}, {1,6}, {1,5}, {1,4}, {1,3}, {1,2}, {1,1}, {1,0}}, {{2,11}, {2,12}, {2,9}, {2,8}, {2,7}, {2,6}, {2,5}, {2,4}, {2,3}, {2,2}, {2,1}, {2,0}}, diff --git a/keyboards/jian/handwired/handwired.c b/keyboards/jian/handwired/handwired.c index bcb7ec7102..2c14518299 100644 --- a/keyboards/jian/handwired/handwired.c +++ b/keyboards/jian/handwired/handwired.c @@ -1,6 +1,6 @@ #include "handwired.h" -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{13, 0}, {12, 0}, {11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{13, 1}, {12, 1}, {11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{13, 2}, {12, 2}, {11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/jian/nsrev2/nsrev2.c b/keyboards/jian/nsrev2/nsrev2.c index c3acff55ee..48827416f1 100644 --- a/keyboards/jian/nsrev2/nsrev2.c +++ b/keyboards/jian/nsrev2/nsrev2.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}, {{0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}}, diff --git a/keyboards/jian/rev1/rev1.c b/keyboards/jian/rev1/rev1.c index 1c7e936270..40d9f3beda 100644 --- a/keyboards/jian/rev1/rev1.c +++ b/keyboards/jian/rev1/rev1.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}, {6, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}, {6, 5}}, {{0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}, {6, 6}}, diff --git a/keyboards/jian/rev2/rev2.c b/keyboards/jian/rev2/rev2.c index 09824c6652..b18676af38 100644 --- a/keyboards/jian/rev2/rev2.c +++ b/keyboards/jian/rev2/rev2.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}, {{0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}}, diff --git a/keyboards/keebio/iris/rev1/rev1.c b/keyboards/keebio/iris/rev1/rev1.c index 9694d122df..6249d2f0ac 100644 --- a/keyboards/keebio/iris/rev1/rev1.c +++ b/keyboards/keebio/iris/rev1/rev1.c @@ -10,7 +10,7 @@ void led_set_kb(uint8_t usb_led) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{0,5}, {1,5}, {2,5}, {3,5}, {4,5}, {5,5}}, {{0,6}, {1,6}, {2,6}, {3,6}, {4,6}, {5,6}}, diff --git a/keyboards/keebio/iris/rev1_led/rev1_led.c b/keyboards/keebio/iris/rev1_led/rev1_led.c index af5fc9440b..c330189c7c 100644 --- a/keyboards/keebio/iris/rev1_led/rev1_led.c +++ b/keyboards/keebio/iris/rev1_led/rev1_led.c @@ -10,7 +10,7 @@ void led_set_kb(uint8_t usb_led) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{0,5}, {1,5}, {2,5}, {3,5}, {4,5}, {5,5}}, {{0,6}, {1,6}, {2,6}, {3,6}, {4,6}, {5,6}}, diff --git a/keyboards/keebio/iris/rev2/rev2.c b/keyboards/keebio/iris/rev2/rev2.c index 70c30695ad..e8c2a66c98 100644 --- a/keyboards/keebio/iris/rev2/rev2.c +++ b/keyboards/keebio/iris/rev2/rev2.c @@ -3,7 +3,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{0,5}, {1,5}, {2,5}, {3,5}, {4,5}, {5,5}}, {{0,6}, {1,6}, {2,6}, {3,6}, {4,6}, {5,6}}, diff --git a/keyboards/keebio/iris/rev3/rev3.c b/keyboards/keebio/iris/rev3/rev3.c index f58c2093c4..b97b097b78 100644 --- a/keyboards/keebio/iris/rev3/rev3.c +++ b/keyboards/keebio/iris/rev3/rev3.c @@ -3,7 +3,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{0,5}, {1,5}, {2,5}, {3,5}, {4,5}, {5,5}}, {{0,6}, {1,6}, {2,6}, {3,6}, {4,6}, {5,6}}, diff --git a/keyboards/keebio/levinson/levinson.c b/keyboards/keebio/levinson/levinson.c index 95f3fe9ef8..b361640bcf 100644 --- a/keyboards/keebio/levinson/levinson.c +++ b/keyboards/keebio/levinson/levinson.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{5, 4}, {4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}}, {{5, 5}, {4, 5}, {3, 5}, {2, 5}, {1, 5}, {0, 5}}, diff --git a/keyboards/keebio/wavelet/wavelet.c b/keyboards/keebio/wavelet/wavelet.c index 2bc9c65a52..68011e266c 100644 --- a/keyboards/keebio/wavelet/wavelet.c +++ b/keyboards/keebio/wavelet/wavelet.c @@ -15,7 +15,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{5, 4}, {4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}}, {{5, 5}, {4, 5}, {3, 5}, {2, 5}, {1, 5}, {0, 5}}, diff --git a/keyboards/kyria/rev1/rev1.c b/keyboards/kyria/rev1/rev1.c index 970a359e71..622ac279bd 100644 --- a/keyboards/kyria/rev1/rev1.c +++ b/keyboards/kyria/rev1/rev1.c @@ -3,7 +3,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}, {6, 4}, {7, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}, {6, 5}, {7, 5}}, {{0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}, {6, 6}, {7, 6}}, @@ -12,6 +12,6 @@ const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1}}, {{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, {5, 2}, {6, 2}, {7, 2}}, {{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3}, {5, 3}, {6, 3}, {7, 3}} -}; +}; #endif diff --git a/keyboards/lets_split/lets_split.c b/keyboards/lets_split/lets_split.c index c400ab7bb8..ee0c931be2 100644 --- a/keyboards/lets_split/lets_split.c +++ b/keyboards/lets_split/lets_split.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}, diff --git a/keyboards/lets_split_eh/lets_split_eh.c b/keyboards/lets_split_eh/lets_split_eh.c index 7249f54cf2..37075aa67d 100644 --- a/keyboards/lets_split_eh/lets_split_eh.c +++ b/keyboards/lets_split_eh/lets_split_eh.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}, diff --git a/keyboards/mitosis/mitosis.c b/keyboards/mitosis/mitosis.c index 50b6d8452f..ea3d1c99d7 100644 --- a/keyboards/mitosis/mitosis.c +++ b/keyboards/mitosis/mitosis.c @@ -16,7 +16,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/moonlander/moonlander.c b/keyboards/moonlander/moonlander.c index 39f61b5c40..bc4047b847 100644 --- a/keyboards/moonlander/moonlander.c +++ b/keyboards/moonlander/moonlander.c @@ -370,7 +370,7 @@ bool music_mask_kb(uint16_t keycode) { #ifdef SWAP_HANDS_ENABLE // swap-hands action needs a matrix to define the swap // clang-format off -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{6,6}, {5,6}, {4,6}, {3,6}, {2,6}, {1,6},{0,6}}, {{6,7}, {5,7}, {4,7}, {3,7}, {2,7}, {1,7},{0,7}}, diff --git a/keyboards/niu_mini/niu_mini.c b/keyboards/niu_mini/niu_mini.c index 835625ee4f..cd9c0cdfed 100644 --- a/keyboards/niu_mini/niu_mini.c +++ b/keyboards/niu_mini/niu_mini.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/orthodox/rev1/rev1.c b/keyboards/orthodox/rev1/rev1.c index ecb908b3ae..db8d435c33 100644 --- a/keyboards/orthodox/rev1/rev1.c +++ b/keyboards/orthodox/rev1/rev1.c @@ -37,7 +37,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{0,3}, {1,3}, {2,3}, {3,3}, {4,3}, {5,3}, {6,3}, {7,3}, {8,3}}, {{0,4}, {1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, {8,4}}, diff --git a/keyboards/orthodox/rev3/rev3.c b/keyboards/orthodox/rev3/rev3.c index ecb908b3ae..db8d435c33 100644 --- a/keyboards/orthodox/rev3/rev3.c +++ b/keyboards/orthodox/rev3/rev3.c @@ -37,7 +37,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{0,3}, {1,3}, {2,3}, {3,3}, {4,3}, {5,3}, {6,3}, {7,3}, {8,3}}, {{0,4}, {1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, {8,4}}, diff --git a/keyboards/orthodox/rev3_teensy/rev3_teensy.c b/keyboards/orthodox/rev3_teensy/rev3_teensy.c index ecb908b3ae..db8d435c33 100644 --- a/keyboards/orthodox/rev3_teensy/rev3_teensy.c +++ b/keyboards/orthodox/rev3_teensy/rev3_teensy.c @@ -37,7 +37,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) // swap-hands action needs a matrix to define the swap -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { /* Left hand, matrix positions */ {{0,3}, {1,3}, {2,3}, {3,3}, {4,3}, {5,3}, {6,3}, {7,3}, {8,3}}, {{0,4}, {1,4}, {2,4}, {3,4}, {4,4}, {5,4}, {6,4}, {7,4}, {8,4}}, diff --git a/keyboards/planck/planck.c b/keyboards/planck/planck.c index 5a65d5e5f3..1f824efaae 100644 --- a/keyboards/planck/planck.c +++ b/keyboards/planck/planck.c @@ -2,7 +2,7 @@ #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/preonic/rev1/rev1.c b/keyboards/preonic/rev1/rev1.c index c9fd8330ef..bf83743605 100644 --- a/keyboards/preonic/rev1/rev1.c +++ b/keyboards/preonic/rev1/rev1.c @@ -26,7 +26,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) - const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { + const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/preonic/rev2/rev2.c b/keyboards/preonic/rev2/rev2.c index c0e72620ed..a3929249d0 100644 --- a/keyboards/preonic/rev2/rev2.c +++ b/keyboards/preonic/rev2/rev2.c @@ -26,7 +26,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) - const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { + const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/preonic/rev3/rev3.c b/keyboards/preonic/rev3/rev3.c index 161c1c9636..ec8a56108e 100644 --- a/keyboards/preonic/rev3/rev3.c +++ b/keyboards/preonic/rev3/rev3.c @@ -63,7 +63,7 @@ void dip_switch_update_user(uint8_t index, bool active) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{5, 4}, {4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}}, {{5, 5}, {4, 5}, {3, 5}, {2, 5}, {1, 5}, {0, 5}}, {{5, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}}, diff --git a/keyboards/redox_w/redox_w.c b/keyboards/redox_w/redox_w.c index 05fa339571..a94e731b39 100644 --- a/keyboards/redox_w/redox_w.c +++ b/keyboards/redox_w/redox_w.c @@ -17,7 +17,7 @@ void matrix_init_kb(void) { #ifdef ONEHAND_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{13, 0}, {12, 0}, {11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{13, 1}, {12, 1}, {11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{13, 2}, {12, 2}, {11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/sirius/uni660/rev1/rev1.c b/keyboards/sirius/uni660/rev1/rev1.c index d4adac24b5..7cc75ee22a 100644 --- a/keyboards/sirius/uni660/rev1/rev1.c +++ b/keyboards/sirius/uni660/rev1/rev1.c @@ -19,7 +19,7 @@ void matrix_init_kb(void) { /* #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/sirius/uni660/rev2/rev2.c b/keyboards/sirius/uni660/rev2/rev2.c index 048495b992..2c2515c0ed 100644 --- a/keyboards/sirius/uni660/rev2/rev2.c +++ b/keyboards/sirius/uni660/rev2/rev2.c @@ -19,7 +19,7 @@ void matrix_init_kb(void) { /* #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/splitty/splitty.c b/keyboards/splitty/splitty.c index 652d1f781b..707f289832 100644 --- a/keyboards/splitty/splitty.c +++ b/keyboards/splitty/splitty.c @@ -19,7 +19,7 @@ #ifdef SWAP_HANDS_ENABLE // clang-format off __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}, {6, 5}}, {{0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}, {6, 6}}, diff --git a/keyboards/telophase/telophase.c b/keyboards/telophase/telophase.c index 0e04debeb8..7484551c91 100644 --- a/keyboards/telophase/telophase.c +++ b/keyboards/telophase/telophase.c @@ -17,7 +17,7 @@ void matrix_init_kb(void) { #ifdef SWAP_HANDS_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{12, 0}, {11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, {{12, 1}, {11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, {{12, 2}, {11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, diff --git a/keyboards/vitamins_included/vitamins_included.c b/keyboards/vitamins_included/vitamins_included.c index 650f26001e..078f93e0e6 100644 --- a/keyboards/vitamins_included/vitamins_included.c +++ b/keyboards/vitamins_included/vitamins_included.c @@ -2,7 +2,7 @@ #ifdef ONEHAND_ENABLE __attribute__ ((weak)) -const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { {{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}, {5, 4}}, {{0, 5}, {1, 5}, {2, 5}, {3, 5}, {4, 5}, {5, 5}}, diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index aae5cbfa5f..f41665b06c 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -133,7 +133,8 @@ void process_hand_swap(keyevent_t *event) { bool do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit); if (do_swap) { - event->key = hand_swap_config[pos.row][pos.col]; + event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row); + event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col); swap_state[pos.row] |= col_bit; } else { swap_state[pos.row] &= ~(col_bit); diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h index 9a991de1c2..6c84561785 100644 --- a/tmk_core/common/action.h +++ b/tmk_core/common/action.h @@ -78,7 +78,7 @@ extern bool disable_action_cache; /* Code for handling one-handed key modifiers. */ #ifdef SWAP_HANDS_ENABLE extern bool swap_hands; -extern const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS]; +extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS]; # if (MATRIX_COLS <= 8) typedef uint8_t swap_state_row_t; # elif (MATRIX_COLS <= 16) -- cgit v1.2.3 From da0c551692b117e4f91ca3e446155027290e3c96 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Wed, 12 May 2021 18:24:44 -0700 Subject: Add missing LED Matrix suspend code to suspend.c (#12878) Co-authored-by: Ryan --- tmk_core/common/avr/suspend.c | 7 +++++++ tmk_core/common/chibios/suspend.c | 8 ++++++++ 2 files changed, 15 insertions(+) (limited to 'tmk_core/common') diff --git a/tmk_core/common/avr/suspend.c b/tmk_core/common/avr/suspend.c index e9ee0aefdd..690d7f38ca 100644 --- a/tmk_core/common/avr/suspend.c +++ b/tmk_core/common/avr/suspend.c @@ -163,6 +163,9 @@ void suspend_power_down(void) { rgblight_suspend(); # endif +# if defined(LED_MATRIX_ENABLE) + led_matrix_set_suspend_state(true); +# endif # if defined(RGB_MATRIX_ENABLE) rgb_matrix_set_suspend_state(true); # endif @@ -218,6 +221,10 @@ void suspend_wakeup_init(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_wakeup(); #endif + +#if defined(LED_MATRIX_ENABLE) + led_matrix_set_suspend_state(false); +#endif #if defined(RGB_MATRIX_ENABLE) rgb_matrix_set_suspend_state(false); #endif diff --git a/tmk_core/common/chibios/suspend.c b/tmk_core/common/chibios/suspend.c index 14a9aecb31..38517e06f0 100644 --- a/tmk_core/common/chibios/suspend.c +++ b/tmk_core/common/chibios/suspend.c @@ -83,6 +83,10 @@ void suspend_power_down(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_suspend(); #endif + +#if defined(LED_MATRIX_ENABLE) + led_matrix_set_suspend_state(true); +#endif #if defined(RGB_MATRIX_ENABLE) rgb_matrix_set_suspend_state(true); #endif @@ -154,6 +158,10 @@ void suspend_wakeup_init(void) { #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE) rgblight_wakeup(); #endif + +#if defined(LED_MATRIX_ENABLE) + led_matrix_set_suspend_state(false); +#endif #if defined(RGB_MATRIX_ENABLE) rgb_matrix_set_suspend_state(false); #endif -- cgit v1.2.3 From 58142f0726147d538167ff3ab793743348f40dcd Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 18 May 2021 17:02:28 +1000 Subject: Fixup housekeeping from being invoked twice per loop. (#12933) --- tmk_core/common/keyboard.c | 12 +++++++++--- tmk_core/common/keyboard.h | 5 +++-- tmk_core/protocol/arm_atsam/main_arm_atsam.c | 3 +++ tmk_core/protocol/chibios/main.c | 3 +-- tmk_core/protocol/lufa/lufa.c | 3 +-- tmk_core/protocol/vusb/main.c | 3 +-- 6 files changed, 18 insertions(+), 11 deletions(-) (limited to 'tmk_core/common') diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 24baf41c00..3d6092e71c 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -280,6 +280,15 @@ __attribute__((weak)) void housekeeping_task_kb(void) {} */ __attribute__((weak)) void housekeeping_task_user(void) {} +/** \brief housekeeping_task + * + * Invokes hooks for executing code after QMK is done after each loop iteration. + */ +void housekeeping_task(void) { + housekeeping_task_kb(); + housekeeping_task_user(); +} + /** \brief keyboard_init * * FIXME: needs doc @@ -374,9 +383,6 @@ void keyboard_task(void) { bool encoders_changed = false; #endif - housekeeping_task_kb(); - housekeeping_task_user(); - uint8_t matrix_changed = matrix_scan(); if (matrix_changed) last_matrix_activity_trigger(); diff --git a/tmk_core/common/keyboard.h b/tmk_core/common/keyboard.h index eaf74bac58..779973f1d6 100644 --- a/tmk_core/common/keyboard.h +++ b/tmk_core/common/keyboard.h @@ -70,8 +70,9 @@ void keyboard_pre_init_user(void); void keyboard_post_init_kb(void); void keyboard_post_init_user(void); -void housekeeping_task_kb(void); -void housekeeping_task_user(void); +void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol +void housekeeping_task_kb(void); // To be overridden by keyboard-level code +void housekeeping_task_user(void); // To be overridden by user/keymap-level code uint32_t last_input_activity_time(void); // Timestamp of the last matrix or encoder activity uint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder activity diff --git a/tmk_core/protocol/arm_atsam/main_arm_atsam.c b/tmk_core/protocol/arm_atsam/main_arm_atsam.c index e4e79d3510..ce0f54593c 100644 --- a/tmk_core/protocol/arm_atsam/main_arm_atsam.c +++ b/tmk_core/protocol/arm_atsam/main_arm_atsam.c @@ -305,6 +305,9 @@ int main(void) { // dprintf("5v=%u 5vu=%u dlow=%u dhi=%u gca=%u gcd=%u\r\n", v_5v, v_5v_avg, v_5v_avg - V5_LOW, v_5v_avg - V5_HIGH, gcr_actual, gcr_desired); } #endif // CONSOLE_ENABLE + + // Run housekeeping + housekeeping_task(); } return 1; diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index e2ec011186..199741594a 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -257,7 +257,6 @@ int main(void) { #endif // Run housekeeping - housekeeping_task_kb(); - housekeeping_task_user(); + housekeeping_task(); } } diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 85d71d0835..63619fdb3b 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -1107,8 +1107,7 @@ int main(void) { #endif // Run housekeeping - housekeeping_task_kb(); - housekeeping_task_user(); + housekeeping_task(); } } diff --git a/tmk_core/protocol/vusb/main.c b/tmk_core/protocol/vusb/main.c index 2de4f6a80a..53926a7493 100644 --- a/tmk_core/protocol/vusb/main.c +++ b/tmk_core/protocol/vusb/main.c @@ -173,8 +173,7 @@ int main(void) { #endif // Run housekeeping - housekeeping_task_kb(); - housekeeping_task_user(); + housekeeping_task(); } } } -- cgit v1.2.3 From 06aea834c420d5c11bbcf64d37596cb0cee9af98 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 27 May 2021 15:21:15 +1000 Subject: Backlight: add defines for default level and breathing state (#12560) --- docs/feature_backlight.md | 20 +++++++++++--------- quantum/backlight/backlight.c | 23 ++++++++++++++++++++--- quantum/backlight/backlight.h | 4 ++++ tmk_core/common/eeconfig.c | 11 ----------- tmk_core/common/eeconfig.h | 5 ----- 5 files changed, 35 insertions(+), 28 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/feature_backlight.md b/docs/feature_backlight.md index 74511dd43f..d47ecc6824 100644 --- a/docs/feature_backlight.md +++ b/docs/feature_backlight.md @@ -62,15 +62,17 @@ Valid driver values are `pwm`, `software`, `custom` or `no`. See below for help To configure the backlighting, `#define` these in your `config.h`: -| Define | Default | Description | -|------------------------|---------------|-------------------------------------------------------------------------------------------------------------------| -| `BACKLIGHT_PIN` | *Not defined* | The pin that controls the LED(s) | -| `BACKLIGHT_LEVELS` | `3` | The number of brightness levels (maximum 31 excluding off) | -| `BACKLIGHT_CAPS_LOCK` | *Not defined* | Enable Caps Lock indicator using backlight (for keyboards without dedicated LED) | -| `BACKLIGHT_BREATHING` | *Not defined* | Enable backlight breathing, if supported | -| `BREATHING_PERIOD` | `6` | The length of one backlight "breath" in seconds | -| `BACKLIGHT_ON_STATE` | `1` | The state of the backlight pin when the backlight is "on" - `1` for high, `0` for low | -| `BACKLIGHT_LIMIT_VAL ` | `255` | The maximum duty cycle of the backlight -- `255` allows for full brightness, any lower will decrease the maximum. | +|Define |Default |Description | +|-----------------------------|------------------|-----------------------------------------------------------------------------------------------------------------| +|`BACKLIGHT_PIN` |*Not defined* |The pin that controls the LED(s) | +|`BACKLIGHT_LEVELS` |`3` |The number of brightness levels (maximum 31 excluding off) | +|`BACKLIGHT_CAPS_LOCK` |*Not defined* |Enable Caps Lock indicator using backlight (for keyboards without dedicated LED) | +|`BACKLIGHT_BREATHING` |*Not defined* |Enable backlight breathing, if supported | +|`BREATHING_PERIOD` |`6` |The length of one backlight "breath" in seconds | +|`BACKLIGHT_ON_STATE` |`1` |The state of the backlight pin when the backlight is "on" - `1` for high, `0` for low | +|`BACKLIGHT_LIMIT_VAL` |`255` |The maximum duty cycle of the backlight -- `255` allows for full brightness, any lower will decrease the maximum.| +|`BACKLIGHT_DEFAULT_LEVEL` |`BACKLIGHT_LEVELS`|The default backlight level to use upon clearing the EEPROM | +|`BACKLIGHT_DEFAULT_BREATHING`|*Not defined* |Whether to enable backlight breathing upon clearing the EEPROM | Unless you are designing your own keyboard, you generally should not need to change the `BACKLIGHT_PIN` or `BACKLIGHT_ON_STATE`. diff --git a/quantum/backlight/backlight.c b/quantum/backlight/backlight.c index 113beb832f..1bc276899c 100644 --- a/quantum/backlight/backlight.c +++ b/quantum/backlight/backlight.c @@ -22,6 +22,10 @@ along with this program. If not, see . backlight_config_t backlight_config; +#ifndef BACKLIGHT_DEFAULT_LEVEL +# define BACKLIGHT_DEFAULT_LEVEL BACKLIGHT_LEVELS +#endif + #ifdef BACKLIGHT_BREATHING // TODO: migrate to backlight_config_t static uint8_t breathing_period = BREATHING_PERIOD; @@ -35,6 +39,7 @@ void backlight_init(void) { /* check signature */ if (!eeconfig_is_enabled()) { eeconfig_init(); + eeconfig_update_backlight_default(); } backlight_config.raw = eeconfig_read_backlight(); if (backlight_config.level > BACKLIGHT_LEVELS) { @@ -152,11 +157,23 @@ void backlight_level(uint8_t level) { eeconfig_update_backlight(backlight_config.raw); } -/** \brief Update current backlight state to EEPROM - * - */ +uint8_t eeconfig_read_backlight(void) { return eeprom_read_byte(EECONFIG_BACKLIGHT); } + +void eeconfig_update_backlight(uint8_t val) { eeprom_update_byte(EECONFIG_BACKLIGHT, val); } + void eeconfig_update_backlight_current(void) { eeconfig_update_backlight(backlight_config.raw); } +void eeconfig_update_backlight_default(void) { + backlight_config.enable = 1; +#ifdef BACKLIGHT_DEFAULT_BREATHING + backlight_config.breathing = 1; +#else + backlight_config.breathing = 0; +#endif + backlight_config.level = BACKLIGHT_DEFAULT_LEVEL; + eeconfig_update_backlight(backlight_config.raw); +} + /** \brief Get backlight level * * FIXME: needs doc diff --git a/quantum/backlight/backlight.h b/quantum/backlight/backlight.h index 3e506737d4..c30c70fd62 100644 --- a/quantum/backlight/backlight.h +++ b/quantum/backlight/backlight.h @@ -55,7 +55,11 @@ void backlight_decrease(void); void backlight_level_noeeprom(uint8_t level); void backlight_level(uint8_t level); uint8_t get_backlight_level(void); + +uint8_t eeconfig_read_backlight(void); +void eeconfig_update_backlight(uint8_t val); void eeconfig_update_backlight_current(void); +void eeconfig_update_backlight_default(void); // implementation specific void backlight_init_ports(void); diff --git a/tmk_core/common/eeconfig.c b/tmk_core/common/eeconfig.c index 92a5092176..ffa56ab56d 100644 --- a/tmk_core/common/eeconfig.c +++ b/tmk_core/common/eeconfig.c @@ -155,17 +155,6 @@ void eeconfig_update_keymap(uint16_t val) { eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, (val >> 8) & 0xFF); } -/** \brief eeconfig read backlight - * - * FIXME: needs doc - */ -uint8_t eeconfig_read_backlight(void) { return eeprom_read_byte(EECONFIG_BACKLIGHT); } -/** \brief eeconfig update backlight - * - * FIXME: needs doc - */ -void eeconfig_update_backlight(uint8_t val) { eeprom_update_byte(EECONFIG_BACKLIGHT, val); } - /** \brief eeconfig read audio * * FIXME: needs doc diff --git a/tmk_core/common/eeconfig.h b/tmk_core/common/eeconfig.h index 9e18fd4e15..a88071729d 100644 --- a/tmk_core/common/eeconfig.h +++ b/tmk_core/common/eeconfig.h @@ -94,11 +94,6 @@ void eeconfig_update_default_layer(uint8_t val); uint16_t eeconfig_read_keymap(void); void eeconfig_update_keymap(uint16_t val); -#ifdef BACKLIGHT_ENABLE -uint8_t eeconfig_read_backlight(void); -void eeconfig_update_backlight(uint8_t val); -#endif - #ifdef AUDIO_ENABLE uint8_t eeconfig_read_audio(void); void eeconfig_update_audio(uint8_t val); -- cgit v1.2.3 From 7d1194de01ea94f065bd5176607b4d66279ef51b Mon Sep 17 00:00:00 2001 From: James Young <18669334+noroadsleft@users.noreply.github.com> Date: Sat, 29 May 2021 13:53:10 -0700 Subject: run: qmk cformat --core-only --- drivers/chibios/analog.c | 2 +- drivers/chibios/serial_usart.h | 20 ++++-- quantum/led_matrix_animations/alpha_mods_anim.h | 2 +- quantum/led_matrix_animations/band_anim.h | 6 +- quantum/led_matrix_animations/band_pinwheel_anim.h | 10 +-- quantum/led_matrix_animations/band_spiral_anim.h | 10 +-- quantum/led_matrix_animations/breathing_anim.h | 2 +- .../led_matrix_animations/cycle_left_right_anim.h | 10 +-- quantum/led_matrix_animations/cycle_out_in_anim.h | 10 +-- quantum/led_matrix_animations/cycle_up_down_anim.h | 10 +-- quantum/led_matrix_animations/dual_beacon_anim.h | 10 +-- .../led_matrix_animations/solid_reactive_cross.h | 12 ++-- .../led_matrix_animations/solid_reactive_nexus.h | 12 ++-- .../solid_reactive_simple_anim.h | 12 ++-- .../led_matrix_animations/solid_reactive_wide.h | 12 ++-- quantum/led_matrix_animations/solid_splash_anim.h | 12 ++-- .../led_matrix_animations/wave_left_right_anim.h | 10 +-- quantum/led_matrix_animations/wave_up_down_anim.h | 10 +-- quantum/led_matrix_drivers.c | 2 +- quantum/quantum_keycodes.h | 12 ++-- quantum/rgb_matrix_drivers.c | 6 +- quantum/rgblight.c | 8 +-- quantum/split_common/transport.c | 34 ++++----- tmk_core/common/action.c | 83 ++++++++++------------ tmk_core/common/action.h | 2 +- tmk_core/common/chibios/sleep_led.c | 4 +- tmk_core/common/keyboard.h | 6 +- 27 files changed, 139 insertions(+), 190 deletions(-) (limited to 'tmk_core/common') diff --git a/drivers/chibios/analog.c b/drivers/chibios/analog.c index b1081623d3..8c476fcac2 100644 --- a/drivers/chibios/analog.c +++ b/drivers/chibios/analog.c @@ -123,7 +123,7 @@ static ADCConversionGroup adcConversionGroup = { .smpr = ADC_SAMPLING_RATE, #elif defined(USE_ADCV2) # if !defined(STM32F1XX) - .cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without... + .cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without... # endif .smpr2 = ADC_SMPR2_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN9(ADC_SAMPLING_RATE), .smpr1 = ADC_SMPR1_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN15(ADC_SAMPLING_RATE), diff --git a/drivers/chibios/serial_usart.h b/drivers/chibios/serial_usart.h index d35b5d12c6..fee7b4d159 100644 --- a/drivers/chibios/serial_usart.h +++ b/drivers/chibios/serial_usart.h @@ -40,13 +40,25 @@ #endif #if defined(USART1_REMAP) -# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); } while(0) +# define USART_REMAP \ + do { \ + (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); \ + } while (0) #elif defined(USART2_REMAP) -# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); } while(0) +# define USART_REMAP \ + do { \ + (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); \ + } while (0) #elif defined(USART3_PARTIALREMAP) -# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); } while(0) +# define USART_REMAP \ + do { \ + (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); \ + } while (0) #elif defined(USART3_FULLREMAP) -# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); } while(0) +# define USART_REMAP \ + do { \ + (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); \ + } while (0) #endif #ifndef SELECT_SOFT_SERIAL_SPEED diff --git a/quantum/led_matrix_animations/alpha_mods_anim.h b/quantum/led_matrix_animations/alpha_mods_anim.h index a4638fde69..6f69f6892b 100644 --- a/quantum/led_matrix_animations/alpha_mods_anim.h +++ b/quantum/led_matrix_animations/alpha_mods_anim.h @@ -21,4 +21,4 @@ bool ALPHAS_MODS(effect_params_t* params) { } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_ALPHAS_MODS +#endif // DISABLE_LED_MATRIX_ALPHAS_MODS diff --git a/quantum/led_matrix_animations/band_anim.h b/quantum/led_matrix_animations/band_anim.h index 4a2b746a76..523dba1b78 100644 --- a/quantum/led_matrix_animations/band_anim.h +++ b/quantum/led_matrix_animations/band_anim.h @@ -7,9 +7,7 @@ static uint8_t BAND_math(uint8_t val, uint8_t i, uint8_t time) { return scale8(v < 0 ? 0 : v, val); } -bool BAND(effect_params_t* params) { - return effect_runner_i(params, &BAND_math); -} +bool BAND(effect_params_t* params) { return effect_runner_i(params, &BAND_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_BAND +#endif // DISABLE_LED_MATRIX_BAND diff --git a/quantum/led_matrix_animations/band_pinwheel_anim.h b/quantum/led_matrix_animations/band_pinwheel_anim.h index 9836320d2a..fb3b835cad 100644 --- a/quantum/led_matrix_animations/band_pinwheel_anim.h +++ b/quantum/led_matrix_animations/band_pinwheel_anim.h @@ -2,13 +2,9 @@ LED_MATRIX_EFFECT(BAND_PINWHEEL) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t BAND_PINWHEEL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t time) { - return scale8(val - time - atan2_8(dy, dx) * 3, val); -} +static uint8_t BAND_PINWHEEL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t time) { return scale8(val - time - atan2_8(dy, dx) * 3, val); } -bool BAND_PINWHEEL(effect_params_t* params) { - return effect_runner_dx_dy(params, &BAND_PINWHEEL_math); -} +bool BAND_PINWHEEL(effect_params_t* params) { return effect_runner_dx_dy(params, &BAND_PINWHEEL_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_BAND_PINWHEEL +#endif // DISABLE_LED_MATRIX_BAND_PINWHEEL diff --git a/quantum/led_matrix_animations/band_spiral_anim.h b/quantum/led_matrix_animations/band_spiral_anim.h index be17c03aad..fca22aad9c 100644 --- a/quantum/led_matrix_animations/band_spiral_anim.h +++ b/quantum/led_matrix_animations/band_spiral_anim.h @@ -2,13 +2,9 @@ LED_MATRIX_EFFECT(BAND_SPIRAL) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t BAND_SPIRAL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) { - return scale8(val + dist - time - atan2_8(dy, dx), val); -} +static uint8_t BAND_SPIRAL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) { return scale8(val + dist - time - atan2_8(dy, dx), val); } -bool BAND_SPIRAL(effect_params_t* params) { - return effect_runner_dx_dy_dist(params, &BAND_SPIRAL_math); -} +bool BAND_SPIRAL(effect_params_t* params) { return effect_runner_dx_dy_dist(params, &BAND_SPIRAL_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_BAND_SPIRAL +#endif // DISABLE_LED_MATRIX_BAND_SPIRAL diff --git a/quantum/led_matrix_animations/breathing_anim.h b/quantum/led_matrix_animations/breathing_anim.h index 4f49f50690..00310e3f65 100644 --- a/quantum/led_matrix_animations/breathing_anim.h +++ b/quantum/led_matrix_animations/breathing_anim.h @@ -16,4 +16,4 @@ bool BREATHING(effect_params_t* params) { } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_BREATHING +#endif // DISABLE_LED_MATRIX_BREATHING diff --git a/quantum/led_matrix_animations/cycle_left_right_anim.h b/quantum/led_matrix_animations/cycle_left_right_anim.h index 404fda26f5..51e81d57ca 100644 --- a/quantum/led_matrix_animations/cycle_left_right_anim.h +++ b/quantum/led_matrix_animations/cycle_left_right_anim.h @@ -2,13 +2,9 @@ LED_MATRIX_EFFECT(CYCLE_LEFT_RIGHT) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t CYCLE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { - return scale8(g_led_config.point[i].x - time, val); -} +static uint8_t CYCLE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { return scale8(g_led_config.point[i].x - time, val); } -bool CYCLE_LEFT_RIGHT(effect_params_t* params) { - return effect_runner_i(params, &CYCLE_LEFT_RIGHT_math); -} +bool CYCLE_LEFT_RIGHT(effect_params_t* params) { return effect_runner_i(params, &CYCLE_LEFT_RIGHT_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT +#endif // DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT diff --git a/quantum/led_matrix_animations/cycle_out_in_anim.h b/quantum/led_matrix_animations/cycle_out_in_anim.h index 3f5fc5b187..f62061552c 100644 --- a/quantum/led_matrix_animations/cycle_out_in_anim.h +++ b/quantum/led_matrix_animations/cycle_out_in_anim.h @@ -2,13 +2,9 @@ LED_MATRIX_EFFECT(CYCLE_OUT_IN) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t CYCLE_OUT_IN_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) { - return scale8(3 * dist / 2 + time, val); -} +static uint8_t CYCLE_OUT_IN_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) { return scale8(3 * dist / 2 + time, val); } -bool CYCLE_OUT_IN(effect_params_t* params) { - return effect_runner_dx_dy_dist(params, &CYCLE_OUT_IN_math); -} +bool CYCLE_OUT_IN(effect_params_t* params) { return effect_runner_dx_dy_dist(params, &CYCLE_OUT_IN_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_CYCLE_OUT_IN +#endif // DISABLE_LED_MATRIX_CYCLE_OUT_IN diff --git a/quantum/led_matrix_animations/cycle_up_down_anim.h b/quantum/led_matrix_animations/cycle_up_down_anim.h index 25fc211b17..bd1d125672 100644 --- a/quantum/led_matrix_animations/cycle_up_down_anim.h +++ b/quantum/led_matrix_animations/cycle_up_down_anim.h @@ -2,13 +2,9 @@ LED_MATRIX_EFFECT(CYCLE_UP_DOWN) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t CYCLE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { - return scale8(g_led_config.point[i].y - time, val); -} +static uint8_t CYCLE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { return scale8(g_led_config.point[i].y - time, val); } -bool CYCLE_UP_DOWN(effect_params_t* params) { - return effect_runner_i(params, &CYCLE_UP_DOWN_math); -} +bool CYCLE_UP_DOWN(effect_params_t* params) { return effect_runner_i(params, &CYCLE_UP_DOWN_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_CYCLE_UP_DOWN +#endif // DISABLE_LED_MATRIX_CYCLE_UP_DOWN diff --git a/quantum/led_matrix_animations/dual_beacon_anim.h b/quantum/led_matrix_animations/dual_beacon_anim.h index 1fa1df1395..9b8a7877c9 100644 --- a/quantum/led_matrix_animations/dual_beacon_anim.h +++ b/quantum/led_matrix_animations/dual_beacon_anim.h @@ -2,13 +2,9 @@ LED_MATRIX_EFFECT(DUAL_BEACON) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t DUAL_BEACON_math(uint8_t val, int8_t sin, int8_t cos, uint8_t i, uint8_t time) { - return scale8(((g_led_config.point[i].y - k_led_matrix_center.y) * cos + (g_led_config.point[i].x - k_led_matrix_center.x) * sin) / 128, val); -} +static uint8_t DUAL_BEACON_math(uint8_t val, int8_t sin, int8_t cos, uint8_t i, uint8_t time) { return scale8(((g_led_config.point[i].y - k_led_matrix_center.y) * cos + (g_led_config.point[i].x - k_led_matrix_center.x) * sin) / 128, val); } -bool DUAL_BEACON(effect_params_t* params) { - return effect_runner_sin_cos_i(params, &DUAL_BEACON_math); -} +bool DUAL_BEACON(effect_params_t* params) { return effect_runner_sin_cos_i(params, &DUAL_BEACON_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_DUAL_BEACON +#endif // DISABLE_LED_MATRIX_DUAL_BEACON diff --git a/quantum/led_matrix_animations/solid_reactive_cross.h b/quantum/led_matrix_animations/solid_reactive_cross.h index a50d1fc629..f402d99b37 100644 --- a/quantum/led_matrix_animations/solid_reactive_cross.h +++ b/quantum/led_matrix_animations/solid_reactive_cross.h @@ -23,17 +23,13 @@ static uint8_t SOLID_REACTIVE_CROSS_math(uint8_t val, int16_t dx, int16_t dy, ui } # ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS -bool SOLID_REACTIVE_CROSS(effect_params_t* params) { - return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_CROSS_math); -} +bool SOLID_REACTIVE_CROSS(effect_params_t* params) { return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_CROSS_math); } # endif # ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS -bool SOLID_REACTIVE_MULTICROSS(effect_params_t* params) { - return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_CROSS_math); -} +bool SOLID_REACTIVE_MULTICROSS(effect_params_t* params) { return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_CROSS_math); } # endif # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS) -#endif // LED_MATRIX_KEYREACTIVE_ENABLED +# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS) +#endif // LED_MATRIX_KEYREACTIVE_ENABLED diff --git a/quantum/led_matrix_animations/solid_reactive_nexus.h b/quantum/led_matrix_animations/solid_reactive_nexus.h index 4638aac5a4..4d0d252263 100644 --- a/quantum/led_matrix_animations/solid_reactive_nexus.h +++ b/quantum/led_matrix_animations/solid_reactive_nexus.h @@ -20,17 +20,13 @@ static uint8_t SOLID_REACTIVE_NEXUS_math(uint8_t val, int16_t dx, int16_t dy, ui } # ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS -bool SOLID_REACTIVE_NEXUS(effect_params_t* params) { - return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_NEXUS_math); -} +bool SOLID_REACTIVE_NEXUS(effect_params_t* params) { return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_NEXUS_math); } # endif # ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS -bool SOLID_REACTIVE_MULTINEXUS(effect_params_t* params) { - return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_NEXUS_math); -} +bool SOLID_REACTIVE_MULTINEXUS(effect_params_t* params) { return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_NEXUS_math); } # endif # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS) -#endif // LED_MATRIX_KEYREACTIVE_ENABLED +# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS) +#endif // LED_MATRIX_KEYREACTIVE_ENABLED diff --git a/quantum/led_matrix_animations/solid_reactive_simple_anim.h b/quantum/led_matrix_animations/solid_reactive_simple_anim.h index e1166a4fb6..30e2527f60 100644 --- a/quantum/led_matrix_animations/solid_reactive_simple_anim.h +++ b/quantum/led_matrix_animations/solid_reactive_simple_anim.h @@ -3,14 +3,10 @@ LED_MATRIX_EFFECT(SOLID_REACTIVE_SIMPLE) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t SOLID_REACTIVE_SIMPLE_math(uint8_t val, uint16_t offset) { - return scale8(255 - offset, val); -} +static uint8_t SOLID_REACTIVE_SIMPLE_math(uint8_t val, uint16_t offset) { return scale8(255 - offset, val); } -bool SOLID_REACTIVE_SIMPLE(effect_params_t* params) { - return effect_runner_reactive(params, &SOLID_REACTIVE_SIMPLE_math); -} +bool SOLID_REACTIVE_SIMPLE(effect_params_t* params) { return effect_runner_reactive(params, &SOLID_REACTIVE_SIMPLE_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -# endif // DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE -#endif // LED_MATRIX_KEYREACTIVE_ENABLED +# endif // DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE +#endif // LED_MATRIX_KEYREACTIVE_ENABLED diff --git a/quantum/led_matrix_animations/solid_reactive_wide.h b/quantum/led_matrix_animations/solid_reactive_wide.h index 4bcaba331e..34a230c259 100644 --- a/quantum/led_matrix_animations/solid_reactive_wide.h +++ b/quantum/led_matrix_animations/solid_reactive_wide.h @@ -18,17 +18,13 @@ static uint8_t SOLID_REACTIVE_WIDE_math(uint8_t val, int16_t dx, int16_t dy, uin } # ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE -bool SOLID_REACTIVE_WIDE(effect_params_t* params) { - return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_WIDE_math); -} +bool SOLID_REACTIVE_WIDE(effect_params_t* params) { return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_WIDE_math); } # endif # ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE -bool SOLID_REACTIVE_MULTIWIDE(effect_params_t* params) { - return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_WIDE_math); -} +bool SOLID_REACTIVE_MULTIWIDE(effect_params_t* params) { return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_WIDE_math); } # endif # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE) -#endif // LED_MATRIX_KEYREACTIVE_ENABLED +# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE) +#endif // LED_MATRIX_KEYREACTIVE_ENABLED diff --git a/quantum/led_matrix_animations/solid_splash_anim.h b/quantum/led_matrix_animations/solid_splash_anim.h index 3e9046640e..4f6ba3d343 100644 --- a/quantum/led_matrix_animations/solid_splash_anim.h +++ b/quantum/led_matrix_animations/solid_splash_anim.h @@ -18,17 +18,13 @@ uint8_t SOLID_SPLASH_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uin } # ifndef DISABLE_LED_MATRIX_SOLID_SPLASH -bool SOLID_SPLASH(effect_params_t* params) { - return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_SPLASH_math); -} +bool SOLID_SPLASH(effect_params_t* params) { return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_SPLASH_math); } # endif # ifndef DISABLE_LED_MATRIX_SOLID_MULTISPLASH -bool SOLID_MULTISPLASH(effect_params_t* params) { - return effect_runner_reactive_splash(0, params, &SOLID_SPLASH_math); -} +bool SOLID_MULTISPLASH(effect_params_t* params) { return effect_runner_reactive_splash(0, params, &SOLID_SPLASH_math); } # endif # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -# endif // !defined(DISABLE_LED_MATRIX_SPLASH) && !defined(DISABLE_LED_MATRIX_MULTISPLASH) -#endif // LED_MATRIX_KEYREACTIVE_ENABLED +# endif // !defined(DISABLE_LED_MATRIX_SPLASH) && !defined(DISABLE_LED_MATRIX_MULTISPLASH) +#endif // LED_MATRIX_KEYREACTIVE_ENABLED diff --git a/quantum/led_matrix_animations/wave_left_right_anim.h b/quantum/led_matrix_animations/wave_left_right_anim.h index d547c72cee..736f22ddc5 100644 --- a/quantum/led_matrix_animations/wave_left_right_anim.h +++ b/quantum/led_matrix_animations/wave_left_right_anim.h @@ -2,13 +2,9 @@ LED_MATRIX_EFFECT(WAVE_LEFT_RIGHT) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t WAVE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { - return scale8(sin8(g_led_config.point[i].x - time), val); -} +static uint8_t WAVE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { return scale8(sin8(g_led_config.point[i].x - time), val); } -bool WAVE_LEFT_RIGHT(effect_params_t* params) { - return effect_runner_i(params, &WAVE_LEFT_RIGHT_math); -} +bool WAVE_LEFT_RIGHT(effect_params_t* params) { return effect_runner_i(params, &WAVE_LEFT_RIGHT_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT +#endif // DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT diff --git a/quantum/led_matrix_animations/wave_up_down_anim.h b/quantum/led_matrix_animations/wave_up_down_anim.h index b68ff9bbc6..3cab0597d4 100644 --- a/quantum/led_matrix_animations/wave_up_down_anim.h +++ b/quantum/led_matrix_animations/wave_up_down_anim.h @@ -2,13 +2,9 @@ LED_MATRIX_EFFECT(WAVE_UP_DOWN) # ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS -static uint8_t WAVE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { - return scale8(sin8(g_led_config.point[i].y - time), val); -} +static uint8_t WAVE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { return scale8(sin8(g_led_config.point[i].y - time), val); } -bool WAVE_UP_DOWN(effect_params_t* params) { - return effect_runner_i(params, &WAVE_UP_DOWN_math); -} +bool WAVE_UP_DOWN(effect_params_t* params) { return effect_runner_i(params, &WAVE_UP_DOWN_math); } # endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -#endif // DISABLE_LED_MATRIX_WAVE_UP_DOWN +#endif // DISABLE_LED_MATRIX_WAVE_UP_DOWN diff --git a/quantum/led_matrix_drivers.c b/quantum/led_matrix_drivers.c index f3d4bc896e..1d46b2c506 100644 --- a/quantum/led_matrix_drivers.c +++ b/quantum/led_matrix_drivers.c @@ -145,7 +145,7 @@ const led_matrix_driver_t led_matrix_driver = { .set_value = IS31FL3731_set_value, .set_value_all = IS31FL3731_set_value_all, # else - .set_value = IS31FL3733_set_value, + .set_value = IS31FL3733_set_value, .set_value_all = IS31FL3733_set_value_all, # endif }; diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 26021598a1..316c20fcef 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -760,12 +760,12 @@ enum quantum_keycodes { #define CMD_T(kc) LCMD_T(kc) #define WIN_T(kc) LWIN_T(kc) -#define C_S_T(kc) MT(MOD_LCTL | MOD_LSFT, kc) // Left Control + Shift e.g. for gnome-terminal -#define MEH_T(kc) MT(MOD_LCTL | MOD_LSFT | MOD_LALT, kc) // Meh is a less hyper version of the Hyper key -- doesn't include GUI, so just Left Control + Shift + Alt -#define LCAG_T(kc) MT(MOD_LCTL | MOD_LALT | MOD_LGUI, kc) // Left Control + Alt + GUI -#define RCAG_T(kc) MT(MOD_RCTL | MOD_RALT | MOD_RGUI, kc) // Right Control + Alt + GUI +#define C_S_T(kc) MT(MOD_LCTL | MOD_LSFT, kc) // Left Control + Shift e.g. for gnome-terminal +#define MEH_T(kc) MT(MOD_LCTL | MOD_LSFT | MOD_LALT, kc) // Meh is a less hyper version of the Hyper key -- doesn't include GUI, so just Left Control + Shift + Alt +#define LCAG_T(kc) MT(MOD_LCTL | MOD_LALT | MOD_LGUI, kc) // Left Control + Alt + GUI +#define RCAG_T(kc) MT(MOD_RCTL | MOD_RALT | MOD_RGUI, kc) // Right Control + Alt + GUI #define HYPR_T(kc) MT(MOD_LCTL | MOD_LSFT | MOD_LALT | MOD_LGUI, kc) // see http://brettterpstra.com/2012/12/08/a-useful-caps-lock-key/ -#define SGUI_T(kc) MT(MOD_LGUI | MOD_LSFT, kc) // Left Shift + GUI +#define SGUI_T(kc) MT(MOD_LGUI | MOD_LSFT, kc) // Left Shift + GUI #define SCMD_T(kc) SGUI_T(kc) #define SWIN_T(kc) SGUI_T(kc) #define LCA_T(kc) MT(MOD_LCTL | MOD_LALT, kc) // Left Control + Alt @@ -792,7 +792,7 @@ enum quantum_keycodes { #define UC_M_MA UNICODE_MODE_MAC #define UNICODE_MODE_OSX UNICODE_MODE_MAC // Deprecated alias -#define UC_M_OS UNICODE_MODE_MAC // Deprecated alias +#define UC_M_OS UNICODE_MODE_MAC // Deprecated alias #define UC_M_LN UNICODE_MODE_LNX #define UC_M_WI UNICODE_MODE_WIN #define UC_M_BS UNICODE_MODE_BSD diff --git a/quantum/rgb_matrix_drivers.c b/quantum/rgb_matrix_drivers.c index a4db86d196..896fa6d0ef 100644 --- a/quantum/rgb_matrix_drivers.c +++ b/quantum/rgb_matrix_drivers.c @@ -146,9 +146,9 @@ static void flush(void) { } const rgb_matrix_driver_t rgb_matrix_driver = { - .init = init, - .flush = flush, - .set_color = IS31FL3733_set_color, + .init = init, + .flush = flush, + .set_color = IS31FL3733_set_color, .set_color_all = IS31FL3733_set_color_all, }; # elif defined(IS31FL3737) diff --git a/quantum/rgblight.c b/quantum/rgblight.c index e4c4d555e5..baa10ec416 100644 --- a/quantum/rgblight.c +++ b/quantum/rgblight.c @@ -727,13 +727,11 @@ static uint16_t _repeat_timer; static uint8_t _times_remaining; static uint16_t _dur; -void rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) { - rgblight_blink_layer_repeat(layer, duration_ms, 1); -} +void rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) { rgblight_blink_layer_repeat(layer, duration_ms, 1); } void rgblight_blink_layer_repeat(uint8_t layer, uint16_t duration_ms, uint8_t times) { _times_remaining = times * 2; - _dur = duration_ms; + _dur = duration_ms; rgblight_set_layer_state(layer, true); _times_remaining--; @@ -892,7 +890,7 @@ void rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) { animation_status.restart = true; } # endif /* RGBLIGHT_SPLIT_NO_ANIMATION_SYNC */ -# endif /* RGBLIGHT_USE_TIMER */ +# endif /* RGBLIGHT_USE_TIMER */ } #endif /* RGBLIGHT_SPLIT */ diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index bf942c5260..9ed0f7591b 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c @@ -240,38 +240,38 @@ typedef struct _Serial_s2m_buffer_t { matrix_row_t smatrix[ROWS_PER_HAND]; # ifdef ENCODER_ENABLE - uint8_t encoder_state[NUMBER_OF_ENCODERS]; + uint8_t encoder_state[NUMBER_OF_ENCODERS]; # endif } Serial_s2m_buffer_t; typedef struct _Serial_m2s_buffer_t { # ifdef SPLIT_MODS_ENABLE - uint8_t real_mods; - uint8_t weak_mods; + uint8_t real_mods; + uint8_t weak_mods; # ifndef NO_ACTION_ONESHOT - uint8_t oneshot_mods; + uint8_t oneshot_mods; # endif # endif # ifndef DISABLE_SYNC_TIMER - uint32_t sync_timer; + uint32_t sync_timer; # endif # ifdef SPLIT_TRANSPORT_MIRROR - matrix_row_t mmatrix[ROWS_PER_HAND]; + matrix_row_t mmatrix[ROWS_PER_HAND]; # endif # ifdef BACKLIGHT_ENABLE - uint8_t backlight_level; + uint8_t backlight_level; # endif # ifdef WPM_ENABLE - uint8_t current_wpm; + uint8_t current_wpm; # endif # if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) led_eeconfig_t led_matrix; bool led_suspend_state; # endif # if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - rgb_config_t rgb_matrix; - bool rgb_suspend_state; + rgb_config_t rgb_matrix; + bool rgb_suspend_state; # endif } Serial_m2s_buffer_t; @@ -363,7 +363,7 @@ bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) // TODO: if MATRIX_COLS > 8 change to unpack() for (int i = 0; i < ROWS_PER_HAND; ++i) { - slave_matrix[i] = serial_s2m_buffer.smatrix[i]; + slave_matrix[i] = serial_s2m_buffer.smatrix[i]; # ifdef SPLIT_TRANSPORT_MIRROR serial_m2s_buffer.mmatrix[i] = master_matrix[i]; # endif @@ -380,14 +380,14 @@ bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) # ifdef WPM_ENABLE // Write wpm to slave - serial_m2s_buffer.current_wpm = get_current_wpm(); + serial_m2s_buffer.current_wpm = get_current_wpm(); # endif # ifdef SPLIT_MODS_ENABLE - serial_m2s_buffer.real_mods = get_mods(); - serial_m2s_buffer.weak_mods = get_weak_mods(); + serial_m2s_buffer.real_mods = get_mods(); + serial_m2s_buffer.weak_mods = get_weak_mods(); # ifndef NO_ACTION_ONESHOT - serial_m2s_buffer.oneshot_mods = get_oneshot_mods(); + serial_m2s_buffer.oneshot_mods = get_oneshot_mods(); # endif # endif @@ -401,7 +401,7 @@ bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) # endif # ifndef DISABLE_SYNC_TIMER - serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; + serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; # endif return true; } @@ -416,7 +416,7 @@ void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) for (int i = 0; i < ROWS_PER_HAND; ++i) { serial_s2m_buffer.smatrix[i] = slave_matrix[i]; # ifdef SPLIT_TRANSPORT_MIRROR - master_matrix[i] = serial_m2s_buffer.mmatrix[i]; + master_matrix[i] = serial_m2s_buffer.mmatrix[i]; # endif } # ifdef BACKLIGHT_ENABLE diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index f41665b06c..bd41d28b66 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -773,10 +773,9 @@ void register_code(uint8_t code) { } #endif - else if - IS_KEY(code) { - // TODO: should push command_proc out of this block? - if (command_proc(code)) return; + else if IS_KEY (code) { + // TODO: should push command_proc out of this block? + if (command_proc(code)) return; #ifndef NO_ACTION_ONESHOT /* TODO: remove @@ -793,35 +792,33 @@ void register_code(uint8_t code) { } else */ #endif - { - // Force a new key press if the key is already pressed - // without this, keys with the same keycode, but different - // modifiers will be reported incorrectly, see issue #1708 - if (is_key_pressed(keyboard_report, code)) { - del_key(code); - send_keyboard_report(); - } - add_key(code); + { + // Force a new key press if the key is already pressed + // without this, keys with the same keycode, but different + // modifiers will be reported incorrectly, see issue #1708 + if (is_key_pressed(keyboard_report, code)) { + del_key(code); send_keyboard_report(); } - } - else if - IS_MOD(code) { - add_mods(MOD_BIT(code)); + add_key(code); send_keyboard_report(); } + } else if IS_MOD (code) { + add_mods(MOD_BIT(code)); + send_keyboard_report(); + } #ifdef EXTRAKEY_ENABLE - else if - IS_SYSTEM(code) { host_system_send(KEYCODE2SYSTEM(code)); } - else if - IS_CONSUMER(code) { host_consumer_send(KEYCODE2CONSUMER(code)); } + else if IS_SYSTEM (code) { + host_system_send(KEYCODE2SYSTEM(code)); + } else if IS_CONSUMER (code) { + host_consumer_send(KEYCODE2CONSUMER(code)); + } #endif #ifdef MOUSEKEY_ENABLE - else if - IS_MOUSEKEY(code) { - mousekey_on(code); - mousekey_send(); - } + else if IS_MOUSEKEY (code) { + mousekey_on(code); + mousekey_send(); + } #endif } @@ -866,26 +863,22 @@ void unregister_code(uint8_t code) { } #endif - else if - IS_KEY(code) { - del_key(code); - send_keyboard_report(); - } - else if - IS_MOD(code) { - del_mods(MOD_BIT(code)); - send_keyboard_report(); - } - else if - IS_SYSTEM(code) { host_system_send(0); } - else if - IS_CONSUMER(code) { host_consumer_send(0); } + else if IS_KEY (code) { + del_key(code); + send_keyboard_report(); + } else if IS_MOD (code) { + del_mods(MOD_BIT(code)); + send_keyboard_report(); + } else if IS_SYSTEM (code) { + host_system_send(0); + } else if IS_CONSUMER (code) { + host_consumer_send(0); + } #ifdef MOUSEKEY_ENABLE - else if - IS_MOUSEKEY(code) { - mousekey_off(code); - mousekey_send(); - } + else if IS_MOUSEKEY (code) { + mousekey_off(code); + mousekey_send(); + } #endif } diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h index 6c84561785..8cb4722c6e 100644 --- a/tmk_core/common/action.h +++ b/tmk_core/common/action.h @@ -77,7 +77,7 @@ extern bool disable_action_cache; /* Code for handling one-handed key modifiers. */ #ifdef SWAP_HANDS_ENABLE -extern bool swap_hands; +extern bool swap_hands; extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS]; # if (MATRIX_COLS <= 8) typedef uint8_t swap_state_row_t; diff --git a/tmk_core/common/chibios/sleep_led.c b/tmk_core/common/chibios/sleep_led.c index 477056a454..1c65016a42 100644 --- a/tmk_core/common/chibios/sleep_led.c +++ b/tmk_core/common/chibios/sleep_led.c @@ -65,7 +65,7 @@ void sleep_led_timer_callback(void) { /* LPTMR clock options */ # define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */ -# define LPTMR_CLOCK_LPO 1 /* 1kHz clock */ +# define LPTMR_CLOCK_LPO 1 /* 1kHz clock */ # define LPTMR_CLOCK_ERCLK32K 2 /* external 32kHz crystal */ # define LPTMR_CLOCK_OSCERCLK 3 /* output from OSC */ @@ -121,7 +121,7 @@ void sleep_led_init(void) { MCG->C2 |= MCG_C2_IRCS; // fast (4MHz) internal ref clock # if defined(KL27) // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others MCG->MC |= MCG_MC_LIRC_DIV2_DIV2; -# endif /* KL27 */ +# endif /* KL27 */ MCG->C1 |= MCG_C1_IRCLKEN; // enable internal ref clock // to work in stop mode, also MCG_C1_IREFSTEN // Divide 4MHz by 2^N (N=6) => 62500 irqs/sec => diff --git a/tmk_core/common/keyboard.h b/tmk_core/common/keyboard.h index 779973f1d6..08f4e84f94 100644 --- a/tmk_core/common/keyboard.h +++ b/tmk_core/common/keyboard.h @@ -70,9 +70,9 @@ void keyboard_pre_init_user(void); void keyboard_post_init_kb(void); void keyboard_post_init_user(void); -void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol -void housekeeping_task_kb(void); // To be overridden by keyboard-level code -void housekeeping_task_user(void); // To be overridden by user/keymap-level code +void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol +void housekeeping_task_kb(void); // To be overridden by keyboard-level code +void housekeeping_task_user(void); // To be overridden by user/keymap-level code uint32_t last_input_activity_time(void); // Timestamp of the last matrix or encoder activity uint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder activity -- cgit v1.2.3 From b72f10c6357eee628ab1f3762f66ba8a0a279a4e Mon Sep 17 00:00:00 2001 From: Takeshi ISHII <2170248+mtei@users.noreply.github.com> Date: Mon, 7 Jun 2021 18:23:21 +0900 Subject: Add readPort() and some API to 'tmk_core/common/*/gpio.h' (#12754) * add readPort() and some API to 'tmk_core/common/*/gpio.h' The following macros have been added to gpio.h. * readPort(port) * setPortBitInput(port, bit) * setPortBitInputHigh(port, bit) * setPortBitOutput(port, bit) * writePortBitLow(port, bit) * writePortBitHigh(port, bit) * add data type 'port_data_t' into gpio.h * rename qmk_pin to pin --- tmk_core/common/avr/gpio.h | 15 +++++++++++++++ tmk_core/common/chibios/gpio.h | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'tmk_core/common') diff --git a/tmk_core/common/avr/gpio.h b/tmk_core/common/avr/gpio.h index 231556c29c..e9be68491d 100644 --- a/tmk_core/common/avr/gpio.h +++ b/tmk_core/common/avr/gpio.h @@ -20,6 +20,8 @@ typedef uint8_t pin_t; +/* Operation of GPIO by pin. */ + #define setPinInput(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF)) #define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF)) #define setPinInputLow(pin) _Static_assert(0, "AVR processors cannot implement an input as pull low") @@ -32,3 +34,16 @@ typedef uint8_t pin_t; #define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF))) #define togglePin(pin) (PORTx_ADDRESS(pin) ^= _BV((pin)&0xF)) + +/* Operation of GPIO by port. */ + +typedef uint8_t port_data_t; + +#define readPort(port) PINx_ADDRESS(port) + +#define setPortBitInput(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) &= ~_BV((bit)&0xF)) +#define setPortBitInputHigh(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) |= _BV((bit)&0xF)) +#define setPortBitOutput(port, bit) (DDRx_ADDRESS(port) |= _BV((bit)&0xF)) + +#define writePortBitLow(port, bit) (PORTx_ADDRESS(port) &= ~_BV((bit)&0xF)) +#define writePortBitHigh(port, bit) (PORTx_ADDRESS(port) |= _BV((bit)&0xF)) diff --git a/tmk_core/common/chibios/gpio.h b/tmk_core/common/chibios/gpio.h index 5d0e142abc..4d057f1cab 100644 --- a/tmk_core/common/chibios/gpio.h +++ b/tmk_core/common/chibios/gpio.h @@ -20,6 +20,8 @@ typedef ioline_t pin_t; +/* Operation of GPIO by pin. */ + #define setPinInput(pin) palSetLineMode(pin, PAL_MODE_INPUT) #define setPinInputHigh(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLUP) #define setPinInputLow(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN) @@ -32,3 +34,17 @@ typedef ioline_t pin_t; #define readPin(pin) palReadLine(pin) #define togglePin(pin) palToggleLine(pin) + +/* Operation of GPIO by port. */ + +typedef uint16_t port_data_t; + +#define readPort(pin) palReadPort(PAL_PORT(pin)) + +#define setPortBitInput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT) +#define setPortBitInputHigh(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLUP) +#define setPortBitInputLow(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLDOWN) +#define setPortBitOutput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_OUTPUT_PUSHPULL) + +#define writePortBitLow(pin, bit) palClearLine(PAL_LINE(PAL_PORT(pin), bit)) +#define writePortBitHigh(pin, bit) palSetLine(PAL_LINE(PAL_PORT(pin), bit)) -- cgit v1.2.3 From b829a1d2648abd0751e9d3505554547faf643425 Mon Sep 17 00:00:00 2001 From: Simon Arlott <70171+nomis@users.noreply.github.com> Date: Wed, 9 Jun 2021 08:23:21 +0100 Subject: Avoid 8-bit timer overflows in debounce algorithms (#12240) * Add fast_timer_t that is 16-bit or 32-bit based on architecture A 16-bit timer will overflow sooner but be faster to compare on AVR. * Avoid 8-bit timer overflows in debounce algorithms Count down remaining elapsed time instead of trying to do 8-bit timer comparisons. Add a "none" implementation that is automatically used if DEBOUNCE is 0 otherwise it will break the _pk/_pr count down. * Avoid unnecessary polling of the entire matrix in sym_eager_pk The matrix only needs to be updated when a debounce timer expires. * Avoid unnecessary polling of the entire matrix in sym_eager_pr The matrix only needs to be updated when a debounce timer expires. The use of the "needed_update" variable is trying to do what "matrix_need_update" was added to fix but didn't work because it only applied when all keys finished debouncing. * Fix sym_defer_g timing inconsistency compared to other debounce algorithms DEBOUNCE=5 should process the key after 5ms, not 6ms * Add debounce tests --- build_test.mk | 1 + quantum/debounce.h | 2 + quantum/debounce/none.c | 31 +++ quantum/debounce/sym_defer_g.c | 26 +-- quantum/debounce/sym_defer_pk.c | 67 ++++-- quantum/debounce/sym_eager_pk.c | 72 +++--- quantum/debounce/sym_eager_pr.c | 76 ++++--- quantum/debounce/tests/debounce_test_common.cpp | 229 +++++++++++++++++++ quantum/debounce/tests/debounce_test_common.h | 83 +++++++ quantum/debounce/tests/rules.mk | 39 ++++ quantum/debounce/tests/sym_defer_g_tests.cpp | 223 +++++++++++++++++++ quantum/debounce/tests/sym_defer_pk_tests.cpp | 225 +++++++++++++++++++ quantum/debounce/tests/sym_eager_pk_tests.cpp | 237 ++++++++++++++++++++ quantum/debounce/tests/sym_eager_pr_tests.cpp | 280 ++++++++++++++++++++++++ quantum/debounce/tests/testlist.mk | 5 + testlist.mk | 1 + tmk_core/common/arm_atsam/_timer.h | 19 ++ tmk_core/common/avr/_timer.h | 19 ++ tmk_core/common/chibios/_timer.h | 19 ++ tmk_core/common/timer.h | 24 +- 20 files changed, 1587 insertions(+), 91 deletions(-) create mode 100644 quantum/debounce/none.c create mode 100644 quantum/debounce/tests/debounce_test_common.cpp create mode 100644 quantum/debounce/tests/debounce_test_common.h create mode 100644 quantum/debounce/tests/rules.mk create mode 100644 quantum/debounce/tests/sym_defer_g_tests.cpp create mode 100644 quantum/debounce/tests/sym_defer_pk_tests.cpp create mode 100644 quantum/debounce/tests/sym_eager_pk_tests.cpp create mode 100644 quantum/debounce/tests/sym_eager_pr_tests.cpp create mode 100644 quantum/debounce/tests/testlist.mk create mode 100644 tmk_core/common/arm_atsam/_timer.h create mode 100644 tmk_core/common/avr/_timer.h create mode 100644 tmk_core/common/chibios/_timer.h (limited to 'tmk_core/common') diff --git a/build_test.mk b/build_test.mk index 77c4265f93..5171c58c36 100644 --- a/build_test.mk +++ b/build_test.mk @@ -49,6 +49,7 @@ endif include common_features.mk include $(TMK_PATH)/common.mk +include $(QUANTUM_PATH)/debounce/tests/rules.mk include $(QUANTUM_PATH)/sequencer/tests/rules.mk include $(QUANTUM_PATH)/serial_link/tests/rules.mk ifneq ($(filter $(FULL_TESTS),$(TEST)),) diff --git a/quantum/debounce.h b/quantum/debounce.h index 9ca05c6824..5043868289 100644 --- a/quantum/debounce.h +++ b/quantum/debounce.h @@ -9,3 +9,5 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool bool debounce_active(void); void debounce_init(uint8_t num_rows); + +void debounce_free(void); diff --git a/quantum/debounce/none.c b/quantum/debounce/none.c new file mode 100644 index 0000000000..b03892bc5b --- /dev/null +++ b/quantum/debounce/none.c @@ -0,0 +1,31 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "matrix.h" +#include "quantum.h" +#include + +void debounce_init(uint8_t num_rows) {} + +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { + for (int i = 0; i < num_rows; i++) { + cooked[i] = raw[i]; + } +} + +bool debounce_active(void) { return false; } + +void debounce_free(void) {} diff --git a/quantum/debounce/sym_defer_g.c b/quantum/debounce/sym_defer_g.c index 3ed9055d2a..fbefd55ede 100644 --- a/quantum/debounce/sym_defer_g.c +++ b/quantum/debounce/sym_defer_g.c @@ -1,5 +1,6 @@ /* Copyright 2017 Alex Ong +Copyright 2021 Simon Arlott 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 @@ -23,30 +24,29 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state. # define DEBOUNCE 5 #endif -void debounce_init(uint8_t num_rows) {} +#if DEBOUNCE > 0 static bool debouncing = false; +static fast_timer_t debouncing_time; -#if DEBOUNCE > 0 -static uint16_t debouncing_time; -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { +void debounce_init(uint8_t num_rows) {} + +void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { if (changed) { debouncing = true; - debouncing_time = timer_read(); + debouncing_time = timer_read_fast(); } - if (debouncing && timer_elapsed(debouncing_time) > DEBOUNCE) { + if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) { for (int i = 0; i < num_rows; i++) { cooked[i] = raw[i]; } debouncing = false; } } -#else // no debouncing. -void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - for (int i = 0; i < num_rows; i++) { - cooked[i] = raw[i]; - } -} -#endif bool debounce_active(void) { return debouncing; } + +void debounce_free(void) {} +#else // no debouncing. +# include "none.c" +#endif diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c index 60513f98e1..626a9be841 100644 --- a/quantum/debounce/sym_defer_pk.c +++ b/quantum/debounce/sym_defer_pk.c @@ -1,6 +1,7 @@ /* Copyright 2017 Alex Ong Copyright 2020 Andrei Purdea +Copyright 2021 Simon Arlott 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 @@ -33,28 +34,25 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state. # define DEBOUNCE 5 #endif +// Maximum debounce: 255ms +#if DEBOUNCE > UINT8_MAX +# undef DEBOUNCE +# define DEBOUNCE UINT8_MAX +#endif + #define ROW_SHIFTER ((matrix_row_t)1) -#define debounce_counter_t uint8_t +typedef uint8_t debounce_counter_t; +#if DEBOUNCE > 0 static debounce_counter_t *debounce_counters; +static fast_timer_t last_time; static bool counters_need_update; -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { - static uint16_t time = 0; - static uint8_t last_result = 0; - uint16_t new_time = timer_read(); - uint16_t diff = new_time - time; - time = new_time; - last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); - return last_result; -} +#define DEBOUNCE_ELAPSED 0 -void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); -void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); +static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time); +static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); // we use num_rows rather than MATRIX_ROWS to support split keyboards void debounce_init(uint8_t num_rows) { @@ -67,27 +65,49 @@ void debounce_init(uint8_t num_rows) { } } +void debounce_free(void) { + free(debounce_counters); + debounce_counters = NULL; +} + void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - uint8_t current_time = wrapping_timer_read(); + bool updated_last = false; + if (counters_need_update) { - update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, current_time); + fast_timer_t now = timer_read_fast(); + fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + + last_time = now; + updated_last = true; + if (elapsed_time > UINT8_MAX) { + elapsed_time = UINT8_MAX; + } + + if (elapsed_time > 0) { + update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time); + } } if (changed) { - start_debounce_counters(raw, cooked, num_rows, current_time); + if (!updated_last) { + last_time = timer_read_fast(); + } + + start_debounce_counters(raw, cooked, num_rows); } } -void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { +static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) { counters_need_update = false; debounce_counter_t *debounce_pointer = debounce_counters; for (uint8_t row = 0; row < num_rows; row++) { for (uint8_t col = 0; col < MATRIX_COLS; col++) { if (*debounce_pointer != DEBOUNCE_ELAPSED) { - if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { + if (*debounce_pointer <= elapsed_time) { *debounce_pointer = DEBOUNCE_ELAPSED; cooked[row] = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col)); } else { + *debounce_pointer -= elapsed_time; counters_need_update = true; } } @@ -96,14 +116,14 @@ void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix } } -void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { +static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { debounce_counter_t *debounce_pointer = debounce_counters; for (uint8_t row = 0; row < num_rows; row++) { matrix_row_t delta = raw[row] ^ cooked[row]; for (uint8_t col = 0; col < MATRIX_COLS; col++) { if (delta & (ROW_SHIFTER << col)) { if (*debounce_pointer == DEBOUNCE_ELAPSED) { - *debounce_pointer = current_time; + *debounce_pointer = DEBOUNCE; counters_need_update = true; } } else { @@ -115,3 +135,6 @@ void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t } bool debounce_active(void) { return true; } +#else +# include "none.c" +#endif diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c index e66cf92d79..15a3242e68 100644 --- a/quantum/debounce/sym_eager_pk.c +++ b/quantum/debounce/sym_eager_pk.c @@ -1,5 +1,6 @@ /* Copyright 2017 Alex Ong +Copyright 2021 Simon Arlott 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 @@ -33,29 +34,26 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred. # define DEBOUNCE 5 #endif +// Maximum debounce: 255ms +#if DEBOUNCE > UINT8_MAX +# undef DEBOUNCE +# define DEBOUNCE UINT8_MAX +#endif + #define ROW_SHIFTER ((matrix_row_t)1) -#define debounce_counter_t uint8_t +typedef uint8_t debounce_counter_t; +#if DEBOUNCE > 0 static debounce_counter_t *debounce_counters; +static fast_timer_t last_time; static bool counters_need_update; static bool matrix_need_update; -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { - static uint16_t time = 0; - static uint8_t last_result = 0; - uint16_t new_time = timer_read(); - uint16_t diff = new_time - time; - time = new_time; - last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); - return last_result; -} +#define DEBOUNCE_ELAPSED 0 -void update_debounce_counters(uint8_t num_rows, uint8_t current_time); -void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); // we use num_rows rather than MATRIX_ROWS to support split keyboards void debounce_init(uint8_t num_rows) { @@ -68,27 +66,51 @@ void debounce_init(uint8_t num_rows) { } } +void debounce_free(void) { + free(debounce_counters); + debounce_counters = NULL; +} + void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - uint8_t current_time = wrapping_timer_read(); + bool updated_last = false; + if (counters_need_update) { - update_debounce_counters(num_rows, current_time); + fast_timer_t now = timer_read_fast(); + fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + + last_time = now; + updated_last = true; + if (elapsed_time > UINT8_MAX) { + elapsed_time = UINT8_MAX; + } + + if (elapsed_time > 0) { + update_debounce_counters(num_rows, elapsed_time); + } } if (changed || matrix_need_update) { - transfer_matrix_values(raw, cooked, num_rows, current_time); + if (!updated_last) { + last_time = timer_read_fast(); + } + + transfer_matrix_values(raw, cooked, num_rows); } } // If the current time is > debounce counter, set the counter to enable input. -void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) { counters_need_update = false; + matrix_need_update = false; debounce_counter_t *debounce_pointer = debounce_counters; for (uint8_t row = 0; row < num_rows; row++) { for (uint8_t col = 0; col < MATRIX_COLS; col++) { if (*debounce_pointer != DEBOUNCE_ELAPSED) { - if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { + if (*debounce_pointer <= elapsed_time) { *debounce_pointer = DEBOUNCE_ELAPSED; + matrix_need_update = true; } else { + *debounce_pointer -= elapsed_time; counters_need_update = true; } } @@ -98,8 +120,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { } // upload from raw_matrix to final matrix; -void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { - matrix_need_update = false; +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { debounce_counter_t *debounce_pointer = debounce_counters; for (uint8_t row = 0; row < num_rows; row++) { matrix_row_t delta = raw[row] ^ cooked[row]; @@ -108,11 +129,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n matrix_row_t col_mask = (ROW_SHIFTER << col); if (delta & col_mask) { if (*debounce_pointer == DEBOUNCE_ELAPSED) { - *debounce_pointer = current_time; + *debounce_pointer = DEBOUNCE; counters_need_update = true; existing_row ^= col_mask; // flip the bit. - } else { - matrix_need_update = true; } } debounce_pointer++; @@ -122,3 +141,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n } bool debounce_active(void) { return true; } +#else +# include "none.c" +#endif diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c index 20ccb46f1d..2ad592c5a6 100644 --- a/quantum/debounce/sym_eager_pr.c +++ b/quantum/debounce/sym_eager_pr.c @@ -1,5 +1,6 @@ /* Copyright 2019 Alex Ong +Copyright 2021 Simon Arlott 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 @@ -33,27 +34,25 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred. # define DEBOUNCE 5 #endif -#define debounce_counter_t uint8_t +// Maximum debounce: 255ms +#if DEBOUNCE > UINT8_MAX +# undef DEBOUNCE +# define DEBOUNCE UINT8_MAX +#endif + +typedef uint8_t debounce_counter_t; + +#if DEBOUNCE > 0 static bool matrix_need_update; static debounce_counter_t *debounce_counters; +static fast_timer_t last_time; static bool counters_need_update; -#define DEBOUNCE_ELAPSED 251 -#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1) - -static uint8_t wrapping_timer_read(void) { - static uint16_t time = 0; - static uint8_t last_result = 0; - uint16_t new_time = timer_read(); - uint16_t diff = new_time - time; - time = new_time; - last_result = (last_result + diff) % (MAX_DEBOUNCE + 1); - return last_result; -} +#define DEBOUNCE_ELAPSED 0 -void update_debounce_counters(uint8_t num_rows, uint8_t current_time); -void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time); +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time); +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows); // we use num_rows rather than MATRIX_ROWS to support split keyboards void debounce_init(uint8_t num_rows) { @@ -63,27 +62,50 @@ void debounce_init(uint8_t num_rows) { } } +void debounce_free(void) { + free(debounce_counters); + debounce_counters = NULL; +} + void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) { - uint8_t current_time = wrapping_timer_read(); - bool needed_update = counters_need_update; + bool updated_last = false; + if (counters_need_update) { - update_debounce_counters(num_rows, current_time); + fast_timer_t now = timer_read_fast(); + fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time); + + last_time = now; + updated_last = true; + if (elapsed_time > UINT8_MAX) { + elapsed_time = UINT8_MAX; + } + + if (elapsed_time > 0) { + update_debounce_counters(num_rows, elapsed_time); + } } - if (changed || (needed_update && !counters_need_update) || matrix_need_update) { - transfer_matrix_values(raw, cooked, num_rows, current_time); + if (changed || matrix_need_update) { + if (!updated_last) { + last_time = timer_read_fast(); + } + + transfer_matrix_values(raw, cooked, num_rows); } } // If the current time is > debounce counter, set the counter to enable input. -void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { +static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) { counters_need_update = false; + matrix_need_update = false; debounce_counter_t *debounce_pointer = debounce_counters; for (uint8_t row = 0; row < num_rows; row++) { if (*debounce_pointer != DEBOUNCE_ELAPSED) { - if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) { + if (*debounce_pointer <= elapsed_time) { *debounce_pointer = DEBOUNCE_ELAPSED; + matrix_need_update = true; } else { + *debounce_pointer -= elapsed_time; counters_need_update = true; } } @@ -92,8 +114,7 @@ void update_debounce_counters(uint8_t num_rows, uint8_t current_time) { } // upload from raw_matrix to final matrix; -void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time) { - matrix_need_update = false; +static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) { debounce_counter_t *debounce_pointer = debounce_counters; for (uint8_t row = 0; row < num_rows; row++) { matrix_row_t existing_row = cooked[row]; @@ -102,11 +123,9 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n // determine new value basd on debounce pointer + raw value if (existing_row != raw_row) { if (*debounce_pointer == DEBOUNCE_ELAPSED) { - *debounce_pointer = current_time; + *debounce_pointer = DEBOUNCE; cooked[row] = raw_row; counters_need_update = true; - } else { - matrix_need_update = true; } } debounce_pointer++; @@ -114,3 +133,6 @@ void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t n } bool debounce_active(void) { return true; } +#else +# include "none.c" +#endif diff --git a/quantum/debounce/tests/debounce_test_common.cpp b/quantum/debounce/tests/debounce_test_common.cpp new file mode 100644 index 0000000000..1c5e7c9f4e --- /dev/null +++ b/quantum/debounce/tests/debounce_test_common.cpp @@ -0,0 +1,229 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +#include +#include +#include + +extern "C" { +#include "quantum.h" +#include "timer.h" +#include "debounce.h" + +void set_time(uint32_t t); +void advance_time(uint32_t ms); +} + +void DebounceTest::addEvents(std::initializer_list events) { + events_.insert(events_.end(), events.begin(), events.end()); +} + +void DebounceTest::runEvents() { + /* Run the test multiple times, from 1kHz to 10kHz scan rate */ + for (extra_iterations_ = 0; extra_iterations_ < 10; extra_iterations_++) { + if (time_jumps_) { + /* Don't advance time smoothly, jump to the next event (some tests require this) */ + auto_advance_time_ = false; + runEventsInternal(); + } else { + /* Run the test with both smooth and irregular time; it must produce the same result */ + auto_advance_time_ = true; + runEventsInternal(); + auto_advance_time_ = false; + runEventsInternal(); + } + } +} + +void DebounceTest::runEventsInternal() { + fast_timer_t previous = 0; + bool first = true; + + /* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */ + debounce_init(MATRIX_ROWS); + set_time(time_offset_); + std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0); + std::fill(std::begin(output_matrix_), std::end(output_matrix_), 0); + + for (auto &event : events_) { + if (!auto_advance_time_) { + /* Jump to the next event */ + set_time(time_offset_ + event.time_); + } else if (!first && event.time_ == previous + 1) { + /* This event immediately follows the previous one, don't make extra debounce() calls */ + advance_time(1); + } else { + /* Fast forward to the time for this event, calling debounce() with no changes */ + ASSERT_LT((time_offset_ + event.time_) - timer_read_fast(), 60000) << "Test tries to advance more than 1 minute of time"; + + while (timer_read_fast() != time_offset_ + event.time_) { + runDebounce(false); + checkCookedMatrix(false, "debounce() modified cooked matrix"); + advance_time(1); + } + } + + first = false; + previous = event.time_; + + /* Prepare input matrix */ + for (auto &input : event.inputs_) { + matrixUpdate(input_matrix_, "input", input); + } + + /* Call debounce */ + runDebounce(!event.inputs_.empty()); + + /* Prepare output matrix */ + for (auto &output : event.outputs_) { + matrixUpdate(output_matrix_, "output", output); + } + + /* Check output matrix has expected change events */ + for (auto &output : event.outputs_) { + EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_)) + << "Missing event at " << strTime() + << " expected key " << output.row_ << "," << output.col_ << " " << directionLabel(output.direction_) + << "\ninput_matrix: changed=" << !event.inputs_.empty() << "\n" << strMatrix(input_matrix_) + << "\nexpected_matrix:\n" << strMatrix(output_matrix_) + << "\nactual_matrix:\n" << strMatrix(cooked_matrix_); + } + + /* Check output matrix has no other changes */ + checkCookedMatrix(!event.inputs_.empty(), "debounce() cooked matrix does not match expected output matrix"); + + /* Perform some extra iterations of the matrix scan with no changes */ + for (int i = 0; i < extra_iterations_; i++) { + runDebounce(false); + checkCookedMatrix(false, "debounce() modified cooked matrix"); + } + } + + /* Check that no further changes happen for 1 minute */ + for (int i = 0; i < 60000; i++) { + runDebounce(false); + checkCookedMatrix(false, "debounce() modified cooked matrix"); + advance_time(1); + } + + debounce_free(); +} + +void DebounceTest::runDebounce(bool changed) { + std::copy(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_)); + std::copy(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_)); + + debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed); + + if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) { + FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() + << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) + << "\nraw_matrix:\n" << strMatrix(raw_matrix_); + } +} + +void DebounceTest::checkCookedMatrix(bool changed, const std::string &error_message) { + if (!std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_))) { + FAIL() << "Unexpected event: " << error_message << " at " << strTime() + << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) + << "\nexpected_matrix:\n" << strMatrix(output_matrix_) + << "\nactual_matrix:\n" << strMatrix(cooked_matrix_); + } +} + +std::string DebounceTest::strTime() { + std::stringstream text; + + text << "time " << (timer_read_fast() - time_offset_) + << " (extra_iterations=" << extra_iterations_ + << ", auto_advance_time=" << auto_advance_time_ << ")"; + + return text.str(); +} + +std::string DebounceTest::strMatrix(matrix_row_t matrix[]) { + std::stringstream text; + + text << "\t" << std::setw(3) << ""; + for (int col = 0; col < MATRIX_COLS; col++) { + text << " " << std::setw(2) << col; + } + text << "\n"; + + for (int row = 0; row < MATRIX_ROWS; row++) { + text << "\t" << std::setw(2) << row << ":"; + for (int col = 0; col < MATRIX_COLS; col++) { + text << ((matrix[row] & (1U << col)) ? " XX" : " __"); + } + + text << "\n"; + } + + return text.str(); +} + +bool DebounceTest::directionValue(Direction direction) { + switch (direction) { + case DOWN: + return true; + + case UP: + return false; + } +} + +std::string DebounceTest::directionLabel(Direction direction) { + switch (direction) { + case DOWN: + return "DOWN"; + + case UP: + return "UP"; + } +} + +/* Modify a matrix and verify that events always specify a change */ +void DebounceTest::matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event) { + ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_)) + << "Test " << name << " at " << strTime() + << " sets key " << event.row_ << "," << event.col_ << " " << directionLabel(event.direction_) + << " but it is already " << directionLabel(event.direction_) + << "\n" << name << "_matrix:\n" << strMatrix(matrix); + + switch (event.direction_) { + case DOWN: + matrix[event.row_] |= (1U << event.col_); + break; + + case UP: + matrix[event.row_] &= ~(1U << event.col_); + break; + } +} + +DebounceTestEvent::DebounceTestEvent(fast_timer_t time, + std::initializer_list inputs, + std::initializer_list outputs) + : time_(time), inputs_(inputs), outputs_(outputs) { +} + +MatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction) + : row_(row), col_(col), direction_(direction) { +} diff --git a/quantum/debounce/tests/debounce_test_common.h b/quantum/debounce/tests/debounce_test_common.h new file mode 100644 index 0000000000..d87e310594 --- /dev/null +++ b/quantum/debounce/tests/debounce_test_common.h @@ -0,0 +1,83 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +#include +#include +#include + +extern "C" { +#include "quantum.h" +#include "timer.h" +} + +enum Direction { + DOWN, + UP, +}; + +class MatrixTestEvent { +public: + MatrixTestEvent(int row, int col, Direction direction); + + const int row_; + const int col_; + const Direction direction_; +}; + +class DebounceTestEvent { +public: + // 0, {{0, 1, DOWN}}, {{0, 1, DOWN}}) + DebounceTestEvent(fast_timer_t time, + std::initializer_list inputs, + std::initializer_list outputs); + + const fast_timer_t time_; + const std::list inputs_; + const std::list outputs_; +}; + +class DebounceTest : public ::testing::Test { +protected: + void addEvents(std::initializer_list events); + void runEvents(); + + fast_timer_t time_offset_ = 7777; + bool time_jumps_ = false; + +private: + static bool directionValue(Direction direction); + static std::string directionLabel(Direction direction); + + void runEventsInternal(); + void runDebounce(bool changed); + void checkCookedMatrix(bool changed, const std::string &error_message); + void matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event); + + std::string strTime(); + std::string strMatrix(matrix_row_t matrix[]); + + std::list events_; + + matrix_row_t input_matrix_[MATRIX_ROWS]; + matrix_row_t raw_matrix_[MATRIX_ROWS]; + matrix_row_t cooked_matrix_[MATRIX_ROWS]; + matrix_row_t output_matrix_[MATRIX_ROWS]; + + int extra_iterations_; + bool auto_advance_time_; +}; diff --git a/quantum/debounce/tests/rules.mk b/quantum/debounce/tests/rules.mk new file mode 100644 index 0000000000..29fda7889f --- /dev/null +++ b/quantum/debounce/tests/rules.mk @@ -0,0 +1,39 @@ +# Copyright 2021 Simon Arlott +# +# 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 . + +DEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5 + +DEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \ + $(TMK_PATH)/common/test/timer.c + +debounce_sym_defer_g_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_sym_defer_g_SRC := $(DEBOUNCE_COMMON_SRC) \ + $(QUANTUM_PATH)/debounce/sym_defer_g.c \ + $(QUANTUM_PATH)/debounce/tests/sym_defer_g_tests.cpp + +debounce_sym_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_sym_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \ + $(QUANTUM_PATH)/debounce/sym_defer_pk.c \ + $(QUANTUM_PATH)/debounce/tests/sym_defer_pk_tests.cpp + +debounce_sym_eager_pk_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_sym_eager_pk_SRC := $(DEBOUNCE_COMMON_SRC) \ + $(QUANTUM_PATH)/debounce/sym_eager_pk.c \ + $(QUANTUM_PATH)/debounce/tests/sym_eager_pk_tests.cpp + +debounce_sym_eager_pr_DEFS := $(DEBOUNCE_COMMON_DEFS) +debounce_sym_eager_pr_SRC := $(DEBOUNCE_COMMON_SRC) \ + $(QUANTUM_PATH)/debounce/sym_eager_pr.c \ + $(QUANTUM_PATH)/debounce/tests/sym_eager_pr_tests.cpp diff --git a/quantum/debounce/tests/sym_defer_g_tests.cpp b/quantum/debounce/tests/sym_defer_g_tests.cpp new file mode 100644 index 0000000000..a56aecd8f3 --- /dev/null +++ b/quantum/debounce/tests/sym_defer_g_tests.cpp @@ -0,0 +1,223 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + /* 0ms delay (fast scan rate) */ + {5, {{0, 1, UP}}, {}}, + + {10, {}, {{0, 1, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + /* 1ms delay */ + {6, {{0, 1, UP}}, {}}, + + {11, {}, {{0, 1, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + /* 2ms delay */ + {7, {{0, 1, UP}}, {}}, + + {12, {}, {{0, 1, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyTooQuick1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + /* Release key exactly on the debounce time */ + {5, {{0, 1, UP}}, {}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyTooQuick2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + {6, {{0, 1, UP}}, {}}, + + /* Press key exactly on the debounce time */ + {11, {{0, 1, DOWN}}, {}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + {1, {{0, 1, UP}}, {}}, + {2, {{0, 1, DOWN}}, {}}, + {3, {{0, 1, UP}}, {}}, + {4, {{0, 1, DOWN}}, {}}, + {5, {{0, 1, UP}}, {}}, + {6, {{0, 1, DOWN}}, {}}, + {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + {5, {}, {{0, 1, DOWN}}}, + {6, {{0, 1, UP}}, {}}, + {7, {{0, 1, DOWN}}, {}}, + {8, {{0, 1, UP}}, {}}, + {9, {{0, 1, DOWN}}, {}}, + {10, {{0, 1, UP}}, {}}, + {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + + {25, {{0, 1, UP}}, {}}, + + {30, {}, {{0, 1, UP}}}, + + {50, {{0, 1, DOWN}}, {}}, + + {55, {}, {{0, 1, DOWN}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysShort) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + {1, {{0, 2, DOWN}}, {}}, + + {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}}, + + {7, {{0, 1, UP}}, {}}, + {8, {{0, 2, UP}}, {}}, + + {13, {}, {{0, 1, UP}, {0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}}, + {6, {{0, 1, UP}, {0, 2, UP}}, {}}, + + {11, {}, {{0, 1, UP}, {0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + {1, {{0, 2, DOWN}}, {}}, + + {5, {}, {}}, + {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}}, + {7, {{0, 1, UP}}, {}}, + {8, {{0, 2, UP}}, {}}, + + {13, {}, {{0, 1, UP}, {0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + /* Processing is very late */ + {300, {}, {{0, 1, DOWN}}}, + /* Immediately release key */ + {300, {{0, 1, UP}}, {}}, + + {305, {}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + /* Processing is very late */ + {300, {}, {{0, 1, DOWN}}}, + /* Release key after 1ms */ + {301, {{0, 1, UP}}, {}}, + + {306, {}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + /* Release key before debounce expires */ + {300, {{0, 1, UP}}, {}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + /* Processing is a bit late */ + {50, {}, {{0, 1, DOWN}}}, + /* Release key after 1ms */ + {51, {{0, 1, UP}}, {}}, + + {56, {}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} diff --git a/quantum/debounce/tests/sym_defer_pk_tests.cpp b/quantum/debounce/tests/sym_defer_pk_tests.cpp new file mode 100644 index 0000000000..1f3061e59c --- /dev/null +++ b/quantum/debounce/tests/sym_defer_pk_tests.cpp @@ -0,0 +1,225 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + /* 0ms delay (fast scan rate) */ + {5, {{0, 1, UP}}, {}}, + + {10, {}, {{0, 1, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + /* 1ms delay */ + {6, {{0, 1, UP}}, {}}, + + {11, {}, {{0, 1, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + /* 2ms delay */ + {7, {{0, 1, UP}}, {}}, + + {12, {}, {{0, 1, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyTooQuick1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + /* Release key exactly on the debounce time */ + {5, {{0, 1, UP}}, {}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyTooQuick2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + {6, {{0, 1, UP}}, {}}, + + /* Press key exactly on the debounce time */ + {11, {{0, 1, DOWN}}, {}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + {1, {{0, 1, UP}}, {}}, + {2, {{0, 1, DOWN}}, {}}, + {3, {{0, 1, UP}}, {}}, + {4, {{0, 1, DOWN}}, {}}, + {5, {{0, 1, UP}}, {}}, + {6, {{0, 1, DOWN}}, {}}, + {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + {5, {}, {{0, 1, DOWN}}}, + {6, {{0, 1, UP}}, {}}, + {7, {{0, 1, DOWN}}, {}}, + {8, {{0, 1, UP}}, {}}, + {9, {{0, 1, DOWN}}, {}}, + {10, {{0, 1, UP}}, {}}, + {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + + {25, {{0, 1, UP}}, {}}, + + {30, {}, {{0, 1, UP}}}, + + {50, {{0, 1, DOWN}}, {}}, + + {55, {}, {{0, 1, DOWN}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysShort) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + {1, {{0, 2, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + {6, {}, {{0, 2, DOWN}}}, + + {7, {{0, 1, UP}}, {}}, + {8, {{0, 2, UP}}, {}}, + + {12, {}, {{0, 1, UP}}}, + {13, {}, {{0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}}, + {6, {{0, 1, UP}, {0, 2, UP}}, {}}, + + {11, {}, {{0, 1, UP}, {0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + {1, {{0, 2, DOWN}}, {}}, + + {5, {}, {{0, 1, DOWN}}}, + {6, {{0, 1, UP}}, {{0, 2, DOWN}}}, + {7, {{0, 2, UP}}, {}}, + + {11, {}, {{0, 1, UP}}}, + {12, {}, {{0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + /* Processing is very late */ + {300, {}, {{0, 1, DOWN}}}, + /* Immediately release key */ + {300, {{0, 1, UP}}, {}}, + + {305, {}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + /* Processing is very late */ + {300, {}, {{0, 1, DOWN}}}, + /* Release key after 1ms */ + {301, {{0, 1, UP}}, {}}, + + {306, {}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + /* Release key before debounce expires */ + {300, {{0, 1, UP}}, {}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {}}, + + /* Processing is a bit late */ + {50, {}, {{0, 1, DOWN}}}, + /* Release key after 1ms */ + {51, {{0, 1, UP}}, {}}, + + {56, {}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} diff --git a/quantum/debounce/tests/sym_eager_pk_tests.cpp b/quantum/debounce/tests/sym_eager_pk_tests.cpp new file mode 100644 index 0000000000..e0fc205e33 --- /dev/null +++ b/quantum/debounce/tests/sym_eager_pk_tests.cpp @@ -0,0 +1,237 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 1ms delay (debounce has not yet finished) */ + {6, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 2ms delay (debounce has not yet finished) */ + {7, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 3ms delay (debounce has not yet finished) */ + {8, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort4) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 4ms delay (debounce has not yet finished) */ + {9, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort5) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 5ms delay (debounce has finished) */ + {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort6) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key after after 6ms delay (debounce has finished) */ + {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + {2, {{0, 1, DOWN}}, {}}, + {3, {{0, 1, UP}}, {}}, + {4, {{0, 1, DOWN}}, {}}, + {5, {{0, 1, UP}}, {{0, 1, UP}}}, + /* Press key again after 1ms delay (debounce has not yet finished) */ + {6, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + /* Change twice in the same time period */ + {1, {{0, 1, UP}}, {}}, + {1, {{0, 1, DOWN}}, {}}, + /* Change three times in the same time period */ + {2, {{0, 1, UP}}, {}}, + {2, {{0, 1, DOWN}}, {}}, + {2, {{0, 1, UP}}, {}}, + /* Change three times in the same time period */ + {3, {{0, 1, DOWN}}, {}}, + {3, {{0, 1, UP}}, {}}, + {3, {{0, 1, DOWN}}, {}}, + /* Change twice in the same time period */ + {4, {{0, 1, UP}}, {}}, + {4, {{0, 1, DOWN}}, {}}, + {5, {{0, 1, UP}}, {{0, 1, UP}}}, + /* Press key again after 1ms delay (debounce has not yet finished) */ + {6, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + {25, {{0, 1, UP}}, {{0, 1, UP}}}, + + {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysShort) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + {2, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, + {3, {{0, 2, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 1ms delay (debounce has not yet finished) */ + {6, {{0, 1, DOWN}}, {}}, + {7, {}, {{0, 2, UP}}}, + + /* Press key again after 1ms delay (debounce has not yet finished) */ + {9, {{0, 2, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + + {12, {}, {{0, 2, DOWN}}}, /* 5ms after UP at time 7 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted */ + {300, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted even with a 1 scan delay */ + {300, {}, {}}, + {300, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted even with a 1ms delay */ + {300, {}, {}}, + {301, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is a bit late but the change will now be accepted */ + {50, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan5) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted even with a 1 scan delay */ + {50, {}, {}}, + {50, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan6) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted even with a 1ms delay */ + {50, {}, {}}, + {51, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} diff --git a/quantum/debounce/tests/sym_eager_pr_tests.cpp b/quantum/debounce/tests/sym_eager_pr_tests.cpp new file mode 100644 index 0000000000..2c4bca127e --- /dev/null +++ b/quantum/debounce/tests/sym_eager_pr_tests.cpp @@ -0,0 +1,280 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +#include "debounce_test_common.h" + +TEST_F(DebounceTest, OneKeyShort1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 1ms delay (debounce has not yet finished) */ + {6, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 2ms delay (debounce has not yet finished) */ + {7, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort3) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 3ms delay (debounce has not yet finished) */ + {8, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort4) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 4ms delay (debounce has not yet finished) */ + {9, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort5) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 5ms delay (debounce has finished) */ + {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyShort6) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key after after 6ms delay (debounce has finished) */ + {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + {2, {{0, 1, DOWN}}, {}}, + {3, {{0, 1, UP}}, {}}, + {4, {{0, 1, DOWN}}, {}}, + {5, {{0, 1, UP}}, {{0, 1, UP}}}, + /* Press key again after 1ms delay (debounce has not yet finished) */ + {6, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyBouncing2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + /* Change twice in the same time period */ + {1, {{0, 1, UP}}, {}}, + {1, {{0, 1, DOWN}}, {}}, + /* Change three times in the same time period */ + {2, {{0, 1, UP}}, {}}, + {2, {{0, 1, DOWN}}, {}}, + {2, {{0, 1, UP}}, {}}, + /* Change three times in the same time period */ + {3, {{0, 1, DOWN}}, {}}, + {3, {{0, 1, UP}}, {}}, + {3, {{0, 1, DOWN}}, {}}, + /* Change twice in the same time period */ + {4, {{0, 1, UP}}, {}}, + {4, {{0, 1, DOWN}}, {}}, + {5, {{0, 1, UP}}, {{0, 1, UP}}}, + /* Press key again after 1ms delay (debounce has not yet finished) */ + {6, {{0, 1, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyLong) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + {25, {{0, 1, UP}}, {{0, 1, UP}}}, + + {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoRowsShort) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + {2, {{2, 0, DOWN}}, {{2, 0, DOWN}}}, + {3, {{2, 0, UP}}, {}}, + + {5, {}, {{0, 1, UP}}}, + /* Press key again after 1ms delay (debounce has not yet finished) */ + {6, {{0, 1, DOWN}}, {}}, + {7, {}, {{2, 0, UP}}}, + + /* Press key again after 1ms delay (debounce has not yet finished) */ + {9, {{2, 0, DOWN}}, {}}, + {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */ + + {12, {}, {{2, 0, DOWN}}}, /* 5ms after UP at time 7 */ + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysOverlap) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + {1, {{0, 1, UP}}, {}}, + /* Press a second key during the first debounce */ + {2, {{0, 2, DOWN}}, {}}, + + /* Key registers as soon as debounce finishes, 5ms after time 0 */ + {5, {}, {{0, 1, UP}, {0, 2, DOWN}}}, + {6, {{0, 1, DOWN}}, {}}, + + /* Key registers as soon as debounce finishes, 5ms after time 5 */ + {10, {}, {{0, 1, DOWN}}}, + /* Release both keys */ + {11, {{0, 1, UP}}, {}}, + {12, {{0, 2, UP}}, {}}, + + /* Keys register as soon as debounce finishes, 5ms after time 10 */ + {15, {}, {{0, 1, UP}, {0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}}, + {20, {{0, 1, UP}}, {{0, 1, UP}}}, + {21, {{0, 2, UP}}, {}}, + + /* Key registers as soon as debounce finishes, 5ms after time 20 */ + {25, {}, {{0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, TwoKeysSimultaneous2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}}, + {20, {{0, 1, UP}, {0, 2, UP}}, {{0, 1, UP}, {0, 2, UP}}}, + }); + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan1) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted */ + {300, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan2) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted even with a 1 scan delay */ + {300, {}, {}}, + {300, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan3) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted even with a 1ms delay */ + {300, {}, {}}, + {301, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan4) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is a bit late but the change will now be accepted */ + {50, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan5) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted even with a 1 scan delay */ + {50, {}, {}}, + {50, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} + +TEST_F(DebounceTest, OneKeyDelayedScan6) { + addEvents({ /* Time, Inputs, Outputs */ + {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}}, + + /* Processing is very late but the change will now be accepted even with a 1ms delay */ + {50, {}, {}}, + {51, {{0, 1, UP}}, {{0, 1, UP}}}, + }); + time_jumps_ = true; + runEvents(); +} diff --git a/quantum/debounce/tests/testlist.mk b/quantum/debounce/tests/testlist.mk new file mode 100644 index 0000000000..16ce8a0a8e --- /dev/null +++ b/quantum/debounce/tests/testlist.mk @@ -0,0 +1,5 @@ +TEST_LIST += \ + debounce_sym_defer_g \ + debounce_sym_defer_pk \ + debounce_sym_eager_pk \ + debounce_sym_eager_pr diff --git a/testlist.mk b/testlist.mk index 0d7609b9f0..d256f4c815 100644 --- a/testlist.mk +++ b/testlist.mk @@ -1,6 +1,7 @@ TEST_LIST = $(notdir $(patsubst %/rules.mk,%,$(wildcard $(ROOT_DIR)/tests/*/rules.mk))) FULL_TESTS := $(TEST_LIST) +include $(ROOT_DIR)/quantum/debounce/tests/testlist.mk include $(ROOT_DIR)/quantum/sequencer/tests/testlist.mk include $(ROOT_DIR)/quantum/serial_link/tests/testlist.mk diff --git a/tmk_core/common/arm_atsam/_timer.h b/tmk_core/common/arm_atsam/_timer.h new file mode 100644 index 0000000000..77402b612a --- /dev/null +++ b/tmk_core/common/arm_atsam/_timer.h @@ -0,0 +1,19 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// The platform is 32-bit, so prefer 32-bit timers to avoid overflow +#define FAST_TIMER_T_SIZE 32 diff --git a/tmk_core/common/avr/_timer.h b/tmk_core/common/avr/_timer.h new file mode 100644 index 0000000000..b81e0f68b7 --- /dev/null +++ b/tmk_core/common/avr/_timer.h @@ -0,0 +1,19 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// The platform is 8-bit, so prefer 16-bit timers to reduce code size +#define FAST_TIMER_T_SIZE 16 diff --git a/tmk_core/common/chibios/_timer.h b/tmk_core/common/chibios/_timer.h new file mode 100644 index 0000000000..77402b612a --- /dev/null +++ b/tmk_core/common/chibios/_timer.h @@ -0,0 +1,19 @@ +/* Copyright 2021 Simon Arlott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +// The platform is 32-bit, so prefer 32-bit timers to avoid overflow +#define FAST_TIMER_T_SIZE 32 diff --git a/tmk_core/common/timer.h b/tmk_core/common/timer.h index 58f637dd93..928811a2b7 100644 --- a/tmk_core/common/timer.h +++ b/tmk_core/common/timer.h @@ -1,5 +1,6 @@ /* Copyright 2011 Jun Wako +Copyright 2021 Simon Arlott 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 @@ -17,13 +18,13 @@ along with this program. If not, see . #pragma once +#if __has_include_next("_timer.h") +# include_next "_timer.h" /* Include the platform's _timer.h */ +#endif + #include #include -#if defined(__AVR__) -# include "avr/timer_avr.h" -#endif - #define TIMER_DIFF(a, b, max) ((max == UINT8_MAX) ? ((uint8_t)((a) - (b))) : ((max == UINT16_MAX) ? ((uint16_t)((a) - (b))) : ((max == UINT32_MAX) ? ((uint32_t)((a) - (b))) : ((a) >= (b) ? (a) - (b) : (max) + 1 - (b) + (a))))) #define TIMER_DIFF_8(a, b) TIMER_DIFF(a, b, UINT8_MAX) #define TIMER_DIFF_16(a, b) TIMER_DIFF(a, b, UINT16_MAX) @@ -47,6 +48,21 @@ uint32_t timer_elapsed32(uint32_t last); #define timer_expired(current, future) ((uint16_t)(current - future) < UINT16_MAX / 2) #define timer_expired32(current, future) ((uint32_t)(current - future) < UINT32_MAX / 2) +// Use an appropriate timer integer size based on architecture (16-bit will overflow sooner) +#if FAST_TIMER_T_SIZE < 32 +# define TIMER_DIFF_FAST(a, b) TIMER_DIFF_16(a, b) +# define timer_expired_fast(current, future) timer_expired(current, future) +typedef uint16_t fast_timer_t; +fast_timer_t inline timer_read_fast(void) { return timer_read(); } +fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed(last); } +#else +# define TIMER_DIFF_FAST(a, b) TIMER_DIFF_32(a, b) +# define timer_expired_fast(current, future) timer_expired32(current, future) +typedef uint32_t fast_timer_t; +fast_timer_t inline timer_read_fast(void) { return timer_read32(); } +fast_timer_t inline timer_elapsed_fast(fast_timer_t last) { return timer_elapsed32(last); } +#endif + #ifdef __cplusplus } #endif -- cgit v1.2.3 From b2fdd4874434ef6921a436fc82d9f24909c726f8 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 10 Jun 2021 17:16:09 +1000 Subject: Add ST7565 LCD driver (#13089) Co-authored-by: Joakim Tufvegren --- common_features.mk | 8 + docs/_summary.md | 1 + docs/feature_st7565.md | 270 +++++++++++++++++++++++++ drivers/lcd/st7565.c | 479 +++++++++++++++++++++++++++++++++++++++++++++ drivers/lcd/st7565.h | 215 ++++++++++++++++++++ quantum/quantum.h | 4 + tmk_core/common/keyboard.c | 18 ++ 7 files changed, 995 insertions(+) create mode 100644 docs/feature_st7565.md create mode 100644 drivers/lcd/st7565.c create mode 100644 drivers/lcd/st7565.h (limited to 'tmk_core/common') diff --git a/common_features.mk b/common_features.mk index 37ce928e2a..b259af46c0 100644 --- a/common_features.mk +++ b/common_features.mk @@ -587,6 +587,14 @@ ifeq ($(strip $(OLED_DRIVER_ENABLE)), yes) SRC += oled_driver.c endif +ifeq ($(strip $(ST7565_ENABLE)), yes) + OPT_DEFS += -DST7565_ENABLE + COMMON_VPATH += $(DRIVER_PATH)/oled # For glcdfont.h + COMMON_VPATH += $(DRIVER_PATH)/lcd + QUANTUM_LIB_SRC += spi_master.c + SRC += st7565.c +endif + include $(DRIVER_PATH)/qwiic/qwiic.mk ifeq ($(strip $(UCIS_ENABLE)), yes) diff --git a/docs/_summary.md b/docs/_summary.md index 9798ef5127..4141e01e77 100644 --- a/docs/_summary.md +++ b/docs/_summary.md @@ -93,6 +93,7 @@ * Hardware Features * Displays * [HD44780 LCD Controller](feature_hd44780.md) + * [ST7565 LCD Driver](feature_st7565.md) * [OLED Driver](feature_oled_driver.md) * Lighting * [Backlight](feature_backlight.md) diff --git a/docs/feature_st7565.md b/docs/feature_st7565.md new file mode 100644 index 0000000000..7db0f9ac4a --- /dev/null +++ b/docs/feature_st7565.md @@ -0,0 +1,270 @@ +# ST7565 LCD Driver + +## Supported Hardware + +LCD modules using ST7565 driver IC, communicating over SPI. + +|Module |IC |Size |Notes | +|------------------------------|-------|------|----------------------------------------------------------| +|Newhaven Display NHD-C12832A1Z|ST7565R|128x32|Used by Ergodox Infinity; primary consumer of this feature| +|Zolentech ZLE12864B |ST7565P|128x64|Requires contrast adjustment | + +## Usage + +To enable the feature, there are three steps. First, when compiling your keyboard, you'll need to add the following to your `rules.mk`: + +```make +ST7565_ENABLE = yes +``` + +Then in your `keymap.c` file, implement the ST7565 task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`: + +```c +#ifdef ST7565_ENABLE +void st7565_task_user(void) { + // Host Keyboard Layer Status + st7565_write_P(PSTR("Layer: "), false); + + switch (get_highest_layer(layer_state)) { + case _QWERTY: + st7565_write_P(PSTR("Default\n"), false); + break; + case _FN: + st7565_write_P(PSTR("FN\n"), false); + break; + case _ADJ: + st7565_write_P(PSTR("ADJ\n"), false); + break; + default: + // Or use the write_ln shortcut over adding '\n' to the end of your string + st7565_write_ln_P(PSTR("Undefined"), false); + } + + // Host Keyboard LED Status + led_t led_state = host_keyboard_led_state(); + st7565_write_P(led_state.num_lock ? PSTR("NUM ") : PSTR(" "), false); + st7565_write_P(led_state.caps_lock ? PSTR("CAP ") : PSTR(" "), false); + st7565_write_P(led_state.scroll_lock ? PSTR("SCR ") : PSTR(" "), false); +} +#endif +``` + +## Logo Example + +In the default font, certain ranges of characters are reserved for a QMK logo. To render this logo to the screen, use the following code example: + +```c +static void render_logo(void) { + static const char PROGMEM qmk_logo[] = { + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0x00 + }; + + st7565_write_P(qmk_logo, false); +} +``` + +## Buffer Read Example +For some purposes, you may need to read the current state of the display buffer. The `st7565_read_raw` function can be used to safely read bytes from the buffer. + +In this example, calling `fade_display` in the `st7565_task_user` function will slowly fade away whatever is on the screen by turning random pixels off over time. +```c +//Setup some mask which can be or'd with bytes to turn off pixels +const uint8_t single_bit_masks[8] = {127, 191, 223, 239, 247, 251, 253, 254}; + +static void fade_display(void) { + //Define the reader structure + display_buffer_reader_t reader; + uint8_t buff_char; + if (random() % 30 == 0) { + srand(timer_read()); + // Fetch a pointer for the buffer byte at index 0. The return structure + // will have the pointer and the number of bytes remaining from this + // index position if we want to perform a sequential read by + // incrementing the buffer pointer + reader = st7565_read_raw(0); + //Loop over the remaining buffer and erase pixels as we go + for (uint16_t i = 0; i < reader.remaining_element_count; i++) { + //Get the actual byte in the buffer by dereferencing the pointer + buff_char = *reader.current_element; + if (buff_char != 0) { + st7565_write_raw_byte(buff_char & single_bit_masks[rand() % 8], i); + } + //increment the pointer to fetch a new byte during the next loop + reader.current_element++; + } + } +} +``` + +## Other Examples + +In split keyboards, it is very common to have two displays that each render different content and are oriented or flipped differently. You can do this by switching which content to render by using the return value from `is_keyboard_master()` or `is_keyboard_left()` found in `split_util.h`, e.g: + +```c +#ifdef ST7565_ENABLE +display_rotation_t st7565_init_user(display_rotation_t rotation) { + if (!is_keyboard_master()) { + return DISPLAY_ROTATION_180; // flips the display 180 degrees if offhand + } + + return rotation; +} + +void st7565_task_user(void) { + if (is_keyboard_master()) { + render_status(); // Renders the current keyboard state (layer, lock, caps, scroll, etc) + } else { + render_logo(); // Renders a static logo + } +} +#endif +``` + +## Basic Configuration + +|Define |Default |Description | +|------------------------|--------------|-----------------------------------------------------------------------------------------------------| +|`ST7565_A0_PIN` |*Not defined* |(Required) The GPIO connected to the display's A0 (data/command) pin | +|`ST7565_RST_PIN` |*Not defined* |(Required) The GPIO connected to the display's reset pin | +|`ST7565_SS_PIN` |*Not defined* |(Required) The GPIO connected to the display's slave select pin | +|`ST7565_SPI_CLK_DIVISOR`|`4` |The SPI clock divisor to use | +|`ST7565_FONT_H` |`"glcdfont.c"`|The font code file to use for custom fonts | +|`ST7565_FONT_START` |`0` |The starting character index for custom fonts | +|`ST7565_FONT_END` |`223` |The ending character index for custom fonts | +|`ST7565_FONT_WIDTH` |`6` |The font width | +|`ST7565_FONT_HEIGHT` |`8` |The font height (untested) | +|`ST7565_TIMEOUT` |`60000` |Turns off the screen after 60000ms of keyboard inactivity. Helps reduce burn-in. Set to 0 to disable.| +|`ST7565_COLUMN_OFFSET` |`0` |Shift output to the right this many pixels. | +|`ST7565_CONTRAST` |`32` |The default contrast level of the display, from 0 to 255. | +|`ST7565_UPDATE_INTERVAL`|`0` |Set the time interval for updating the display in ms. This will improve the matrix scan rate. | + +## Custom sized displays + +The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind. + +|Define |Default |Description | +|-----------------------|----------|-----------------------------------------------------------------------------------------------------------| +|`ST7565_DISPLAY_WIDTH` |`128` |The width of the display. | +|`ST7565_DISPLAY_HEIGHT`|`32` |The height of the display. | +|`ST7565_MATRIX_SIZE` |`512` |The local buffer size to allocate.
`(ST7565_DISPLAY_HEIGHT / 8 * ST7565_DISPLAY_WIDTH)`. | +|`ST7565_BLOCK_TYPE` |`uint16_t`|The unsigned integer type to use for dirty rendering. | +|`ST7565_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.
`(sizeof(ST7565_BLOCK_TYPE) * 8)`.| +|`ST7565_BLOCK_SIZE` |`32` |The size of each block for dirty rendering
`(ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT)`. | + +## API + +```c +// Rotation enum values are flags +typedef enum { + DISPLAY_ROTATION_0, + DISPLAY_ROTATION_180 +} display_rotation_t; + +// Initialize the display, rotating the rendered output based on the define passed in. +// Returns true if the was initialized successfully +bool st7565_init(display_rotation_t rotation); + +// Called at the start of st7565_init, weak function overridable by the user +// rotation - the value passed into st7565_init +// Return new display_rotation_t if you want to override default rotation +display_rotation_t st7565_init_user(display_rotation_t rotation); + +// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering +void st7565_clear(void); + +// Renders the dirty chunks of the buffer to display +void st7565_render(void); + +// Moves cursor to character position indicated by column and line, wraps if out of bounds +// Max column denoted by 'st7565_max_chars()' and max lines by 'st7565_max_lines()' functions +void st7565_set_cursor(uint8_t col, uint8_t line); + +// Advances the cursor to the next page, writing ' ' if true +// Wraps to the begining when out of bounds +void st7565_advance_page(bool clearPageRemainder); + +// Moves the cursor forward 1 character length +// Advance page if there is not enough room for the next character +// Wraps to the begining when out of bounds +void st7565_advance_char(void); + +// Writes a single character to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Main handler that writes character data to the display buffer +void st7565_write_char(const char data, bool invert); + +// Writes a string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +void st7565_write(const char *data, bool invert); + +// Writes a string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Advances the cursor to the next page, wiring ' ' to the remainder of the current page +void st7565_write_ln(const char *data, bool invert); + +// Pans the buffer to the right (or left by passing true) by moving contents of the buffer +// Useful for moving the screen in preparation for new drawing +void st7565_pan(bool left); + +// Returns a pointer to the requested start index in the buffer plus remaining +// buffer length as struct +display_buffer_reader_t st7565_read_raw(uint16_t start_index); + +// Writes a string to the buffer at current cursor position +void st7565_write_raw(const char *data, uint16_t size); + +// Writes a single byte into the buffer at the specified index +void st7565_write_raw_byte(const char data, uint16_t index); + +// Sets a specific pixel on or off +// Coordinates start at top-left and go right and down for positive x and y +void st7565_write_pixel(uint8_t x, uint8_t y, bool on); + +// Writes a PROGMEM string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Remapped to call 'void st7565_write(const char *data, bool invert);' on ARM +void st7565_write_P(const char *data, bool invert); + +// Writes a PROGMEM string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Advances the cursor to the next page, wiring ' ' to the remainder of the current page +// Remapped to call 'void st7565_write_ln(const char *data, bool invert);' on ARM +void st7565_write_ln_P(const char *data, bool invert); + +// Writes a PROGMEM string to the buffer at current cursor position +void st7565_write_raw_P(const char *data, uint16_t size); + +// Can be used to manually turn on the screen if it is off +// Returns true if the screen was on or turns on +bool st7565_on(void); + +// Called when st7565_on() turns on the screen, weak function overridable by the user +// Not called if the screen is already on +void st7565_on_user(void); + +// Can be used to manually turn off the screen if it is on +// Returns true if the screen was off or turns off +bool st7565_off(void); + +// Called when st7565_off() turns off the screen, weak function overridable by the user +// Not called if the screen is already off +void st7565_off_user(void); + +// Returns true if the screen is currently on, false if it is +// not +bool st7565_is_on(void); + +// Basically it's st7565_render, but with timeout management and st7565_task_user calling! +void st7565_task(void); + +// Called at the start of st7565_task, weak function overridable by the user +void st7565_task_user(void); + +// Returns the maximum number of characters that will fit on a line +uint8_t st7565_max_chars(void); + +// Returns the maximum number of lines that will fit on the display +uint8_t st7565_max_lines(void); +``` diff --git a/drivers/lcd/st7565.c b/drivers/lcd/st7565.c new file mode 100644 index 0000000000..4b4891ce71 --- /dev/null +++ b/drivers/lcd/st7565.c @@ -0,0 +1,479 @@ +/* +Copyright 2021 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "st7565.h" + +#include + +#include "keyboard.h" +#include "progmem.h" +#include "timer.h" +#include "wait.h" + +#include ST7565_FONT_H + +// Fundamental Commands +#define CONTRAST 0x81 +#define DISPLAY_ALL_ON 0xA5 +#define DISPLAY_ALL_ON_RESUME 0xA4 +#define NORMAL_DISPLAY 0xA6 +#define DISPLAY_ON 0xAF +#define DISPLAY_OFF 0xAE +#define NOP 0xE3 + +// Addressing Setting Commands +#define PAM_SETCOLUMN_LSB 0x00 +#define PAM_SETCOLUMN_MSB 0x10 +#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7 + +// Hardware Configuration Commands +#define DISPLAY_START_LINE 0x40 +#define SEGMENT_REMAP 0xA0 +#define SEGMENT_REMAP_INV 0xA1 +#define COM_SCAN_INC 0xC0 +#define COM_SCAN_DEC 0xC8 +#define LCD_BIAS_7 0xA3 +#define LCD_BIAS_9 0xA2 +#define RESISTOR_RATIO 0x20 +#define POWER_CONTROL 0x28 + +// Misc defines +#ifndef ST7565_BLOCK_COUNT +# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8) +#endif +#ifndef ST7565_BLOCK_SIZE +# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT) +#endif + +#define ST7565_ALL_BLOCKS_MASK (((((ST7565_BLOCK_TYPE)1 << (ST7565_BLOCK_COUNT - 1)) - 1) << 1) | 1) + +#define HAS_FLAGS(bits, flags) ((bits & flags) == flags) + +// Display buffer's is the same as the display memory layout +// this is so we don't end up with rounding errors with +// parts of the display unusable or don't get cleared correctly +// and also allows for drawing & inverting +uint8_t st7565_buffer[ST7565_MATRIX_SIZE]; +uint8_t * st7565_cursor; +ST7565_BLOCK_TYPE st7565_dirty = 0; +bool st7565_initialized = false; +bool st7565_active = false; +display_rotation_t st7565_rotation = DISPLAY_ROTATION_0; +#if ST7565_TIMEOUT > 0 +uint32_t st7565_timeout; +#endif +#if ST7565_UPDATE_INTERVAL > 0 +uint16_t st7565_update_timeout; +#endif + +// Flips the rendering bits for a character at the current cursor position +static void InvertCharacter(uint8_t *cursor) { + const uint8_t *end = cursor + ST7565_FONT_WIDTH; + while (cursor < end) { + *cursor = ~(*cursor); + cursor++; + } +} + +bool st7565_init(display_rotation_t rotation) { + setPinOutput(ST7565_A0_PIN); + writePinHigh(ST7565_A0_PIN); + setPinOutput(ST7565_RST_PIN); + writePinHigh(ST7565_RST_PIN); + + st7565_rotation = st7565_init_user(rotation); + + spi_init(); + spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); + + st7565_reset(); + + st7565_send_cmd(LCD_BIAS_7); + if (!HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) { + st7565_send_cmd(SEGMENT_REMAP); + st7565_send_cmd(COM_SCAN_DEC); + } else { + st7565_send_cmd(SEGMENT_REMAP_INV); + st7565_send_cmd(COM_SCAN_INC); + } + st7565_send_cmd(DISPLAY_START_LINE | 0x00); + st7565_send_cmd(CONTRAST); + st7565_send_cmd(ST7565_CONTRAST); + st7565_send_cmd(RESISTOR_RATIO | 0x01); + st7565_send_cmd(POWER_CONTROL | 0x04); + wait_ms(50); + st7565_send_cmd(POWER_CONTROL | 0x06); + wait_ms(50); + st7565_send_cmd(POWER_CONTROL | 0x07); + wait_ms(10); + st7565_send_cmd(DISPLAY_ON); + st7565_send_cmd(DISPLAY_ALL_ON_RESUME); + st7565_send_cmd(NORMAL_DISPLAY); + + spi_stop(); + +#if ST7565_TIMEOUT > 0 + st7565_timeout = timer_read32() + ST7565_TIMEOUT; +#endif + + st7565_clear(); + st7565_initialized = true; + st7565_active = true; + return true; +} + +__attribute__((weak)) display_rotation_t st7565_init_user(display_rotation_t rotation) { return rotation; } + +void st7565_clear(void) { + memset(st7565_buffer, 0, sizeof(st7565_buffer)); + st7565_cursor = &st7565_buffer[0]; + st7565_dirty = ST7565_ALL_BLOCKS_MASK; +} + +uint8_t crot(uint8_t a, int8_t n) { + const uint8_t mask = 0x7; + n &= mask; + return a << n | a >> (-n & mask); +} + +void st7565_render(void) { + if (!st7565_initialized) { + return; + } + + // Do we have work to do? + st7565_dirty &= ST7565_ALL_BLOCKS_MASK; + if (!st7565_dirty) { + return; + } + + // Find first dirty block + uint8_t update_start = 0; + while (!(st7565_dirty & ((ST7565_BLOCK_TYPE)1 << update_start))) { + ++update_start; + } + + // Calculate commands to set memory addressing bounds. + uint8_t start_page = ST7565_BLOCK_SIZE * update_start / ST7565_DISPLAY_WIDTH; + uint8_t start_column = ST7565_BLOCK_SIZE * update_start % ST7565_DISPLAY_WIDTH; + // IC has 132 segment drivers, for panels with less width we need to offset the starting column + if (HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) { + start_column += (132 - ST7565_DISPLAY_WIDTH); + } + + spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); + + st7565_send_cmd(PAM_PAGE_ADDR | start_page); + st7565_send_cmd(PAM_SETCOLUMN_LSB | ((ST7565_COLUMN_OFFSET + start_column) & 0x0f)); + st7565_send_cmd(PAM_SETCOLUMN_MSB | ((ST7565_COLUMN_OFFSET + start_column) >> 4 & 0x0f)); + + st7565_send_data(&st7565_buffer[ST7565_BLOCK_SIZE * update_start], ST7565_BLOCK_SIZE); + + // Turn on display if it is off + st7565_on(); + + // Clear dirty flag + st7565_dirty &= ~((ST7565_BLOCK_TYPE)1 << update_start); +} + +void st7565_set_cursor(uint8_t col, uint8_t line) { + uint16_t index = line * ST7565_DISPLAY_WIDTH + col * ST7565_FONT_WIDTH; + + // Out of bounds? + if (index >= ST7565_MATRIX_SIZE) { + index = 0; + } + + st7565_cursor = &st7565_buffer[index]; +} + +void st7565_advance_page(bool clearPageRemainder) { + uint16_t index = st7565_cursor - &st7565_buffer[0]; + uint8_t remaining = ST7565_DISPLAY_WIDTH - (index % ST7565_DISPLAY_WIDTH); + + if (clearPageRemainder) { + // Remaining Char count + remaining = remaining / ST7565_FONT_WIDTH; + + // Write empty character until next line + while (remaining--) st7565_write_char(' ', false); + } else { + // Next page index out of bounds? + if (index + remaining >= ST7565_MATRIX_SIZE) { + index = 0; + remaining = 0; + } + + st7565_cursor = &st7565_buffer[index + remaining]; + } +} + +void st7565_advance_char(void) { + uint16_t nextIndex = st7565_cursor - &st7565_buffer[0] + ST7565_FONT_WIDTH; + uint8_t remainingSpace = ST7565_DISPLAY_WIDTH - (nextIndex % ST7565_DISPLAY_WIDTH); + + // Do we have enough space on the current line for the next character + if (remainingSpace < ST7565_FONT_WIDTH) { + nextIndex += remainingSpace; + } + + // Did we go out of bounds + if (nextIndex >= ST7565_MATRIX_SIZE) { + nextIndex = 0; + } + + // Update cursor position + st7565_cursor = &st7565_buffer[nextIndex]; +} + +// Main handler that writes character data to the display buffer +void st7565_write_char(const char data, bool invert) { + // Advance to the next line if newline + if (data == '\n') { + // Old source wrote ' ' until end of line... + st7565_advance_page(true); + return; + } + + if (data == '\r') { + st7565_advance_page(false); + return; + } + + // copy the current render buffer to check for dirty after + static uint8_t st7565_temp_buffer[ST7565_FONT_WIDTH]; + memcpy(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH); + + _Static_assert(sizeof(font) >= ((ST7565_FONT_END + 1 - ST7565_FONT_START) * ST7565_FONT_WIDTH), "ST7565_FONT_END references outside array"); + + // set the reder buffer data + uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index + if (cast_data < ST7565_FONT_START || cast_data > ST7565_FONT_END) { + memset(st7565_cursor, 0x00, ST7565_FONT_WIDTH); + } else { + const uint8_t *glyph = &font[(cast_data - ST7565_FONT_START) * ST7565_FONT_WIDTH]; + memcpy_P(st7565_cursor, glyph, ST7565_FONT_WIDTH); + } + + // Invert if needed + if (invert) { + InvertCharacter(st7565_cursor); + } + + // Dirty check + if (memcmp(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH)) { + uint16_t index = st7565_cursor - &st7565_buffer[0]; + st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE)); + // Edgecase check if the written data spans the 2 chunks + st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << ((index + ST7565_FONT_WIDTH - 1) / ST7565_BLOCK_SIZE)); + } + + // Finally move to the next char + st7565_advance_char(); +} + +void st7565_write(const char *data, bool invert) { + const char *end = data + strlen(data); + while (data < end) { + st7565_write_char(*data, invert); + data++; + } +} + +void st7565_write_ln(const char *data, bool invert) { + st7565_write(data, invert); + st7565_advance_page(true); +} + +void st7565_pan(bool left) { + uint16_t i = 0; + for (uint16_t y = 0; y < ST7565_DISPLAY_HEIGHT / 8; y++) { + if (left) { + for (uint16_t x = 0; x < ST7565_DISPLAY_WIDTH - 1; x++) { + i = y * ST7565_DISPLAY_WIDTH + x; + st7565_buffer[i] = st7565_buffer[i + 1]; + } + } else { + for (uint16_t x = ST7565_DISPLAY_WIDTH - 1; x > 0; x--) { + i = y * ST7565_DISPLAY_WIDTH + x; + st7565_buffer[i] = st7565_buffer[i - 1]; + } + } + } + st7565_dirty = ST7565_ALL_BLOCKS_MASK; +} + +display_buffer_reader_t st7565_read_raw(uint16_t start_index) { + if (start_index > ST7565_MATRIX_SIZE) start_index = ST7565_MATRIX_SIZE; + display_buffer_reader_t ret_reader; + ret_reader.current_element = &st7565_buffer[start_index]; + ret_reader.remaining_element_count = ST7565_MATRIX_SIZE - start_index; + return ret_reader; +} + +void st7565_write_raw_byte(const char data, uint16_t index) { + if (index > ST7565_MATRIX_SIZE) index = ST7565_MATRIX_SIZE; + if (st7565_buffer[index] == data) return; + st7565_buffer[index] = data; + st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE)); +} + +void st7565_write_raw(const char *data, uint16_t size) { + uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0]; + if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index; + for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { + if (st7565_buffer[i] == data[i]) continue; + st7565_buffer[i] = data[i]; + st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE)); + } +} + +void st7565_write_pixel(uint8_t x, uint8_t y, bool on) { + if (x >= ST7565_DISPLAY_WIDTH) { + return; + } + uint16_t index = x + (y / 8) * ST7565_DISPLAY_WIDTH; + if (index >= ST7565_MATRIX_SIZE) { + return; + } + uint8_t data = st7565_buffer[index]; + if (on) { + data |= (1 << (y % 8)); + } else { + data &= ~(1 << (y % 8)); + } + if (st7565_buffer[index] != data) { + st7565_buffer[index] = data; + st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE)); + } +} + +#if defined(__AVR__) +void st7565_write_P(const char *data, bool invert) { + uint8_t c = pgm_read_byte(data); + while (c != 0) { + st7565_write_char(c, invert); + c = pgm_read_byte(++data); + } +} + +void st7565_write_ln_P(const char *data, bool invert) { + st7565_write_P(data, invert); + st7565_advance_page(true); +} + +void st7565_write_raw_P(const char *data, uint16_t size) { + uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0]; + if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index; + for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { + uint8_t c = pgm_read_byte(data++); + if (st7565_buffer[i] == c) continue; + st7565_buffer[i] = c; + st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE)); + } +} +#endif // defined(__AVR__) + +bool st7565_on(void) { + if (!st7565_initialized) { + return st7565_active; + } + +#if ST7565_TIMEOUT > 0 + st7565_timeout = timer_read32() + ST7565_TIMEOUT; +#endif + + if (!st7565_active) { + spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); + st7565_send_cmd(DISPLAY_ON); + spi_stop(); + st7565_active = true; + st7565_on_user(); + } + return st7565_active; +} + +__attribute__((weak)) void st7565_on_user(void) {} + +bool st7565_off(void) { + if (!st7565_initialized) { + return !st7565_active; + } + + if (st7565_active) { + spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); + st7565_send_cmd(DISPLAY_OFF); + spi_stop(); + st7565_active = false; + st7565_off_user(); + } + return !st7565_active; +} + +__attribute__((weak)) void st7565_off_user(void) {} + +bool st7565_is_on(void) { return st7565_active; } + +uint8_t st7565_max_chars(void) { return ST7565_DISPLAY_WIDTH / ST7565_FONT_WIDTH; } + +uint8_t st7565_max_lines(void) { return ST7565_DISPLAY_HEIGHT / ST7565_FONT_HEIGHT; } + +void st7565_task(void) { + if (!st7565_initialized) { + return; + } + +#if ST7565_UPDATE_INTERVAL > 0 + if (timer_elapsed(st7565_update_timeout) >= ST7565_UPDATE_INTERVAL) { + st7565_update_timeout = timer_read(); + st7565_set_cursor(0, 0); + st7565_task_user(); + } +#else + st7565_set_cursor(0, 0); + st7565_task_user(); +#endif + + // Smart render system, no need to check for dirty + st7565_render(); + + // Display timeout check +#if ST7565_TIMEOUT > 0 + if (st7565_active && timer_expired32(timer_read32(), st7565_timeout)) { + st7565_off(); + } +#endif +} + +__attribute__((weak)) void st7565_task_user(void) {} + +void st7565_reset(void) { + writePinLow(ST7565_RST_PIN); + wait_ms(20); + writePinHigh(ST7565_RST_PIN); + wait_ms(20); +} + +spi_status_t st7565_send_cmd(uint8_t cmd) { + writePinLow(ST7565_A0_PIN); + return spi_write(cmd); +} + +spi_status_t st7565_send_data(uint8_t *data, uint16_t length) { + writePinHigh(ST7565_A0_PIN); + return spi_transmit(data, length); +} diff --git a/drivers/lcd/st7565.h b/drivers/lcd/st7565.h new file mode 100644 index 0000000000..53cfc9a810 --- /dev/null +++ b/drivers/lcd/st7565.h @@ -0,0 +1,215 @@ +/* +Copyright 2021 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include + +#include "spi_master.h" + +#ifndef ST7565_DISPLAY_WIDTH +# define ST7565_DISPLAY_WIDTH 128 +#endif +#ifndef ST7565_DISPLAY_HEIGHT +# define ST7565_DISPLAY_HEIGHT 32 +#endif +#ifndef ST7565_MATRIX_SIZE +# define ST7565_MATRIX_SIZE (ST7565_DISPLAY_HEIGHT / 8 * ST7565_DISPLAY_WIDTH) // 1024 (compile time mathed) +#endif +#ifndef ST7565_BLOCK_TYPE +# define ST7565_BLOCK_TYPE uint16_t +#endif +#ifndef ST7565_BLOCK_COUNT +# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8) // 32 (compile time mathed) +#endif +#ifndef ST7565_BLOCK_SIZE +# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT) // 32 (compile time mathed) +#endif + +// the column address corresponding to the first column in the display hardware +#if !defined(ST7565_COLUMN_OFFSET) +# define ST7565_COLUMN_OFFSET 0 +#endif + +// spi clock divisor +#if !defined(ST7565_SPI_CLK_DIVISOR) +# define ST7565_SPI_CLK_DIVISOR 4 +#endif + +// Custom font file to use +#if !defined(ST7565_FONT_H) +# define ST7565_FONT_H "glcdfont.c" +#endif +// unsigned char value of the first character in the font file +#if !defined(ST7565_FONT_START) +# define ST7565_FONT_START 0 +#endif +// unsigned char value of the last character in the font file +#if !defined(ST7565_FONT_END) +# define ST7565_FONT_END 223 +#endif +// Font render width +#if !defined(ST7565_FONT_WIDTH) +# define ST7565_FONT_WIDTH 6 +#endif +// Font render height +#if !defined(ST7565_FONT_HEIGHT) +# define ST7565_FONT_HEIGHT 8 +#endif +// Default contrast level +#if !defined(ST7565_CONTRAST) +# define ST7565_CONTRAST 32 +#endif + +#if !defined(ST7565_TIMEOUT) +# if defined(ST7565_DISABLE_TIMEOUT) +# define ST7565_TIMEOUT 0 +# else +# define ST7565_TIMEOUT 60000 +# endif +#endif + +#if !defined(ST7565_UPDATE_INTERVAL) && defined(SPLIT_KEYBOARD) +# define ST7565_UPDATE_INTERVAL 50 +#endif + +typedef struct __attribute__((__packed__)) { + uint8_t *current_element; + uint16_t remaining_element_count; +} display_buffer_reader_t; + +// Rotation enum values are flags +typedef enum { DISPLAY_ROTATION_0, DISPLAY_ROTATION_180 } display_rotation_t; + +// Initialize the display, rotating the rendered output based on the define passed in. +// Returns true if the display was initialized successfully +bool st7565_init(display_rotation_t rotation); + +// Called at the start of st7565_init, weak function overridable by the user +// rotation - the value passed into st7565_init +// Return new display_rotation_t if you want to override default rotation +display_rotation_t st7565_init_user(display_rotation_t rotation); + +// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering +void st7565_clear(void); + +// Renders the dirty chunks of the buffer to display +void st7565_render(void); + +// Moves cursor to character position indicated by column and line, wraps if out of bounds +// Max column denoted by 'st7565_max_chars()' and max lines by 'st7565_max_lines()' functions +void st7565_set_cursor(uint8_t col, uint8_t line); + +// Advances the cursor to the next page, writing ' ' if true +// Wraps to the begining when out of bounds +void st7565_advance_page(bool clearPageRemainder); + +// Moves the cursor forward 1 character length +// Advance page if there is not enough room for the next character +// Wraps to the begining when out of bounds +void st7565_advance_char(void); + +// Writes a single character to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Main handler that writes character data to the display buffer +void st7565_write_char(const char data, bool invert); + +// Writes a string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +void st7565_write(const char *data, bool invert); + +// Writes a string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Advances the cursor to the next page, wiring ' ' to the remainder of the current page +void st7565_write_ln(const char *data, bool invert); + +// Pans the buffer to the right (or left by passing true) by moving contents of the buffer +// Useful for moving the screen in preparation for new drawing +void st7565_pan(bool left); + +// Returns a pointer to the requested start index in the buffer plus remaining +// buffer length as struct +display_buffer_reader_t st7565_read_raw(uint16_t start_index); + +// Writes a string to the buffer at current cursor position +void st7565_write_raw(const char *data, uint16_t size); + +// Writes a single byte into the buffer at the specified index +void st7565_write_raw_byte(const char data, uint16_t index); + +// Sets a specific pixel on or off +// Coordinates start at top-left and go right and down for positive x and y +void st7565_write_pixel(uint8_t x, uint8_t y, bool on); + +#if defined(__AVR__) +// Writes a PROGMEM string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Remapped to call 'void st7565_write(const char *data, bool invert);' on ARM +void st7565_write_P(const char *data, bool invert); + +// Writes a PROGMEM string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Advances the cursor to the next page, wiring ' ' to the remainder of the current page +// Remapped to call 'void st7565_write_ln(const char *data, bool invert);' on ARM +void st7565_write_ln_P(const char *data, bool invert); + +// Writes a PROGMEM string to the buffer at current cursor position +void st7565_write_raw_P(const char *data, uint16_t size); +#else +# define st7565_write_P(data, invert) st7565_write(data, invert) +# define st7565_write_ln_P(data, invert) st7565_write_ln(data, invert) +# define st7565_write_raw_P(data, size) st7565_write_raw(data, size) +#endif // defined(__AVR__) + +// Can be used to manually turn on the screen if it is off +// Returns true if the screen was on or turns on +bool st7565_on(void); + +// Called when st7565_on() turns on the screen, weak function overridable by the user +// Not called if the screen is already on +void st7565_on_user(void); + +// Can be used to manually turn off the screen if it is on +// Returns true if the screen was off or turns off +bool st7565_off(void); + +// Called when st7565_off() turns off the screen, weak function overridable by the user +// Not called if the screen is already off +void st7565_off_user(void); + +// Returns true if the screen is currently on, false if it is +// not +bool st7565_is_on(void); + +// Basically it's st7565_render, but with timeout management and st7565_task_user calling! +void st7565_task(void); + +// Called at the start of st7565_task, weak function overridable by the user +void st7565_task_user(void); + +// Returns the maximum number of characters that will fit on a line +uint8_t st7565_max_chars(void); + +// Returns the maximum number of lines that will fit on the display +uint8_t st7565_max_lines(void); + +void st7565_reset(void); + +spi_status_t st7565_send_cmd(uint8_t cmd); + +spi_status_t st7565_send_data(uint8_t *data, uint16_t length); diff --git a/quantum/quantum.h b/quantum/quantum.h index e4a7c5723c..66ba96fde8 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -176,6 +176,10 @@ extern layer_state_t layer_state; # include "oled_driver.h" #endif +#ifdef ST7565_ENABLE +# include "st7565.h" +#endif + #ifdef DIP_SWITCH_ENABLE # include "dip_switch.h" #endif diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 3d6092e71c..249da85bd2 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -85,6 +85,9 @@ along with this program. If not, see . #ifdef OLED_DRIVER_ENABLE # include "oled_driver.h" #endif +#ifdef ST7565_ENABLE +# include "st7565.h" +#endif #ifdef VELOCIKEY_ENABLE # include "velocikey.h" #endif @@ -306,6 +309,9 @@ void keyboard_init(void) { #ifdef OLED_DRIVER_ENABLE oled_init(OLED_ROTATION_0); #endif +#ifdef ST7565_ENABLE + st7565_init(DISPLAY_ROTATION_0); +#endif #ifdef PS2_MOUSE_ENABLE ps2_mouse_init(); #endif @@ -470,6 +476,18 @@ MATRIX_LOOP_END: # endif #endif +#ifdef ST7565_ENABLE + st7565_task(); +# ifndef ST7565_DISABLE_TIMEOUT + // Wake up display if user is using those fabulous keys or spinning those encoders! +# ifdef ENCODER_ENABLE + if (matrix_changed || encoders_changed) st7565_on(); +# else + if (matrix_changed) st7565_on(); +# endif +# endif +#endif + #ifdef MOUSEKEY_ENABLE // mousekey repeat & acceleration mousekey_task(); -- cgit v1.2.3 From e2c58934052e2ca93a92cd97ef8b9c0425fa3cf6 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 16 Jun 2021 06:53:27 +0200 Subject: core: make RESET key work with Teensy 4.x (#13076) related to https://github.com/kinx-project/kint/issues/5 --- tmk_core/common/chibios/bootloader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tmk_core/common') diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index 11f7abf432..76d52ea608 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -95,7 +95,7 @@ void enter_bootloader_mode_if_requested(void) { } } -#elif defined(KL2x) || defined(K20x) || defined(MK66F18) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS +#elif defined(KL2x) || defined(K20x) || defined(MK66F18) || defined(MIMXRT1062) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS /* Kinetis */ # if defined(BOOTLOADER_KIIBOHD) -- cgit v1.2.3 From ef92c9ee2cf4745637635ec1895399e4f013914c Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Fri, 18 Jun 2021 01:09:43 +0200 Subject: Add CRC8 calculation subsystem to quantum (#12641) * Intended usage is data validation in split transport code. * Default space efficient algorithm. * Opt-in fast table based algorithmn with #define CRC8_USE_TABLE switch. * Define switches for size and speed optimized versions, the default is size optimized by using uint_least8_t as datatype for calculations. * #define CRC8_OPTIMIZE_SPEED uses uint_fast8_t as datatype for calculations, this only affects 32-bit Archs like ARM and RISC-V. * Placeholder crc_init() function for hardware backed crc calculation, not implemented yet. --- common_features.mk | 6 +++++ quantum/crc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ quantum/crc.h | 44 ++++++++++++++++++++++++++++++++++ tmk_core/common/keyboard.c | 6 +++++ 4 files changed, 115 insertions(+) create mode 100644 quantum/crc.c create mode 100644 quantum/crc.h (limited to 'tmk_core/common') diff --git a/common_features.mk b/common_features.mk index b259af46c0..1a079a8df2 100644 --- a/common_features.mk +++ b/common_features.mk @@ -530,6 +530,7 @@ endif ifeq ($(strip $(SPLIT_KEYBOARD)), yes) POST_CONFIG_H += $(QUANTUM_DIR)/split_common/post_config.h OPT_DEFS += -DSPLIT_KEYBOARD + CRC_ENABLE := yes # Include files used by all split keyboards QUANTUM_SRC += $(QUANTUM_DIR)/split_common/split_util.c @@ -557,6 +558,11 @@ ifeq ($(strip $(SPLIT_KEYBOARD)), yes) COMMON_VPATH += $(QUANTUM_PATH)/split_common endif +ifeq ($(strip $(CRC_ENABLE)), yes) + OPT_DEFS += -DCRC_ENABLE + QUANTUM_LIB_SRC += crc.c +endif + HAPTIC_ENABLE ?= no ifneq ($(strip $(HAPTIC_ENABLE)),no) COMMON_VPATH += $(DRIVER_PATH)/haptic diff --git a/quantum/crc.c b/quantum/crc.c new file mode 100644 index 0000000000..0d8b9d6017 --- /dev/null +++ b/quantum/crc.c @@ -0,0 +1,59 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "crc.h" + +__attribute__((weak)) void crc_init(void){ + /* Software implementation nothing todo here. */ +}; + +#if defined(CRC8_USE_TABLE) +/** + * Static table used for the table_driven implementation. + */ +static const crc_t crc_table[256] = {0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, + 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3}; + +__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len) { + const uint8_t *d = (const uint8_t *)data; + crc_t crc = 0xff; + size_t tbl_idx; + + while (data_len--) { + tbl_idx = crc ^ *d; + crc = crc_table[tbl_idx] & 0xff; + d++; + } + return crc & 0xff; +} +#else +__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len) { + const uint8_t *d = (const uint8_t *)data; + crc_t crc = 0xff; + size_t i, j; + + for (i = 0; i < data_len; i++) { + crc ^= d[i]; + for (j = 0; j < 8; j++) { + if ((crc & 0x80) != 0) + crc = (crc_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} +#endif \ No newline at end of file diff --git a/quantum/crc.h b/quantum/crc.h new file mode 100644 index 0000000000..c17f5888e2 --- /dev/null +++ b/quantum/crc.h @@ -0,0 +1,44 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "quantum.h" + +/** + * The type of the CRC values. + * + * This type must be big enough to contain at least 8 bits. + */ +#if defined(CRC8_OPTIMIZE_SPEED) +typedef uint_fast8_t crc_t; +#else +typedef uint_least8_t crc_t; +#endif + +/** + * Initialize crc subsystem. + */ +__attribute__((weak)) void crc_init(void); + +/** + * Generate CRC8 value from given data. + * + * \param[in] data Pointer to a buffer of \a data_len bytes. + * \param[in] data_len Number of bytes in the \a data buffer. + * \return The calculated crc value. + */ +__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len); \ No newline at end of file diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 249da85bd2..28fa97bc8f 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -103,6 +103,9 @@ along with this program. If not, see . #ifdef EEPROM_DRIVER # include "eeprom_driver.h" #endif +#if defined(CRC_ENABLE) +# include "crc.h" +#endif static uint32_t last_input_modification_time = 0; uint32_t last_input_activity_time(void) { return last_input_modification_time; } @@ -300,6 +303,9 @@ void keyboard_init(void) { timer_init(); sync_timer_init(); matrix_init(); +#if defined(CRC_ENABLE) + crc_init(); +#endif #ifdef VIA_ENABLE via_init(); #endif -- cgit v1.2.3 From 172e6a703041363decd6fc829542f33180c13beb Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Fri, 18 Jun 2021 09:10:06 +1000 Subject: Extensible split data sync (#11930) * Extensible split data sync capability through transactions. - Split common transport has been split up between the transport layer and data layer. - Split "transactions" model used, with convergence between I2C and serial data definitions. - Slave matrix "generation count" is used to determine if the full slave matrix needs to be retrieved. - Encoders get the same "generation count" treatment. - All other blocks of data are synchronised when a change is detected. - All transmissions have a globally-configurable deadline before a transmission is forced (`FORCED_SYNC_THROTTLE_MS`, default 100ms). - Added atomicity for all core-synced data, preventing partial updates - Added retries to AVR i2c_master's i2c_start, to minimise the number of failed transactions when interrupts are disabled on the slave due to atomicity checks. - Some keyboards have had slight modifications made in order to ensure that they still build due to firmware size restrictions. * Fixup LED_MATRIX compile. * Parameterise ERROR_DISCONNECT_COUNT. --- common_features.mk | 6 +- docs/config_options.md | 26 +- docs/feature_split_keyboard.md | 114 ++++- drivers/avr/i2c_master.c | 19 +- drivers/avr/i2c_slave.c | 29 +- drivers/avr/i2c_slave.h | 13 +- drivers/avr/serial.c | 82 +--- drivers/avr/serial.h | 62 --- drivers/chibios/serial.c | 52 +- drivers/chibios/serial.h | 62 --- drivers/chibios/serial_usart.c | 47 +- drivers/serial.h | 46 ++ keyboards/draculad/config.h | 2 + keyboards/handwired/freoduo/rules.mk | 4 +- keyboards/helix/rev2/keymaps/default/rules.mk | 2 +- keyboards/keebio/iris/rev2/rules.mk | 2 + keyboards/keebio/iris/rev3/rules.mk | 1 + keyboards/keebio/iris/rev4/rules.mk | 2 + keyboards/keebio/quefrency/rules.mk | 1 + keyboards/keebio/viterbi/rev2/rules.mk | 2 + keyboards/keebio/viterbi/rules.mk | 8 +- quantum/split_common/matrix.c | 6 +- quantum/split_common/post_config.h | 9 - quantum/split_common/transaction_id_define.h | 94 ++++ quantum/split_common/transactions.c | 670 ++++++++++++++++++++++++++ quantum/split_common/transactions.h | 54 +++ quantum/split_common/transport.c | 484 +++---------------- quantum/split_common/transport.h | 165 +++++++ tmk_core/common/host.c | 14 +- 29 files changed, 1387 insertions(+), 691 deletions(-) delete mode 100644 drivers/avr/serial.h delete mode 100644 drivers/chibios/serial.h create mode 100644 drivers/serial.h create mode 100644 quantum/split_common/transaction_id_define.h create mode 100644 quantum/split_common/transactions.c create mode 100644 quantum/split_common/transactions.h (limited to 'tmk_core/common') diff --git a/common_features.mk b/common_features.mk index 1a079a8df2..15637d7608 100644 --- a/common_features.mk +++ b/common_features.mk @@ -537,7 +537,11 @@ ifeq ($(strip $(SPLIT_KEYBOARD)), yes) # Determine which (if any) transport files are required ifneq ($(strip $(SPLIT_TRANSPORT)), custom) - QUANTUM_LIB_SRC += $(QUANTUM_DIR)/split_common/transport.c + QUANTUM_SRC += $(QUANTUM_DIR)/split_common/transport.c \ + $(QUANTUM_DIR)/split_common/transactions.c + + OPT_DEFS += -DSPLIT_COMMON_TRANSACTIONS + # Functions added via QUANTUM_LIB_SRC are only included in the final binary if they're called. # Unused functions are pruned away, which is why we can add multiple drivers here without bloat. ifeq ($(PLATFORM),AVR) diff --git a/docs/config_options.md b/docs/config_options.md index 26fe8cea55..980195ac68 100644 --- a/docs/config_options.md +++ b/docs/config_options.md @@ -274,7 +274,7 @@ There are a few different ways to set handedness for split keyboards (listed in ### Other Options * `#define USE_I2C` - * For using I2C instead of Serial (defaults to serial) + * For using I2C instead of Serial (default is serial; serial transport is supported on ARM -- I2C is AVR-only) * `#define SOFT_SERIAL_PIN D0` * When using serial, define this. `D0` or `D1`,`D2`,`D3`,`E6`. @@ -303,7 +303,7 @@ There are a few different ways to set handedness for split keyboards (listed in * `#define SPLIT_USB_DETECT` * Detect (with timeout) USB connection when delegating master/slave * Default behavior for ARM - * Required for AVR Teensy + * Required for AVR Teensy (without hardware mods) * `#define SPLIT_USB_TIMEOUT 2000` * Maximum timeout when detecting master/slave when using `SPLIT_USB_DETECT` @@ -311,6 +311,28 @@ There are a few different ways to set handedness for split keyboards (listed in * `#define SPLIT_USB_TIMEOUT_POLL 10` * Poll frequency when detecting master/slave when using `SPLIT_USB_DETECT` +* `#define FORCED_SYNC_THROTTLE_MS 100` + * Deadline for synchronizing data from master to slave when using the QMK-provided split transport. + +* `#define SPLIT_TRANSPORT_MIRROR` + * Mirrors the master-side matrix on the slave when using the QMK-provided split transport. + +* `#define SPLIT_LAYER_STATE_ENABLE` + * Ensures the current layer state is available on the slave when using the QMK-provided split transport. + +* `#define SPLIT_LED_STATE_ENABLE` + * Ensures the current host indicator state (caps/num/scroll) is available on the slave when using the QMK-provided split transport. + +* `#define SPLIT_MODS_ENABLE` + * Ensures the current modifier state (normal, weak, and oneshot) is available on the slave when using the QMK-provided split transport. + +* `#define SPLIT_WPM_ENABLE` + * Ensures the current WPM is available on the slave when using the QMK-provided split transport. + +* `#define SPLIT_TRANSACTION_IDS_KB .....` +* `#define SPLIT_TRANSACTION_IDS_USER .....` + * Allows for custom data sync with the slave when using the QMK-provided split transport. See [custom data sync between sides](feature_split_keyboard.md#custom-data-sync) for more information. + # The `rules.mk` File This is a [make](https://www.gnu.org/software/make/manual/make.html) file that is included by the top-level `Makefile`. It is used to set some information about the MCU that we will be compiling for as well as enabling and disabling certain features. diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md index 4ebf585f5c..603c387c2d 100644 --- a/docs/feature_split_keyboard.md +++ b/docs/feature_split_keyboard.md @@ -8,8 +8,7 @@ QMK Firmware has a generic implementation that is usable by any board, as well a For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards. -!> ARM is not yet fully supported for Split Keyboards and has many limitations. Progress is being made, but we have not yet reached 100% feature parity. - +!> ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported. ## Compatibility Overview @@ -169,7 +168,7 @@ Because not every split keyboard is identical, there are a number of additional #define USE_I2C ``` -This enables I2C support for split keyboards. This isn't strictly for communication, but can be used for OLED or other I2C-based devices. +This configures the use of I2C support for split keyboard transport (AVR only). ```c #define SOFT_SERIAL_PIN D0 @@ -193,20 +192,115 @@ If you're having issues with serial communication, you can change this value, as * **`5`**: about 20kbps ```c -#define SPLIT_MODS_ENABLE +#define FORCED_SYNC_THROTTLE_MS 100 ``` -This enables transmitting modifier state (normal, weak and oneshot) to the non -primary side of the split keyboard. This adds a few bytes of data to the split -communication protocol and may impact the matrix scan speed when enabled. -The purpose of this feature is to support cosmetic use of modifer state (e.g. -displaying status on an OLED screen). +This sets the maximum number of milliseconds before forcing a synchronization of data from master to slave. Under normal circumstances this sync occurs whenever the data _changes_, for safety a data transfer occurs after this number of milliseconds if no change has been detected since the last sync. ```c #define SPLIT_TRANSPORT_MIRROR ``` -This mirrors the master side matrix to the slave side for features that react or require knowledge of master side key presses on the slave side. This adds a few bytes of data to the split communication protocol and may impact the matrix scan speed when enabled. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to Keypresses). +This mirrors the master side matrix to the slave side for features that react or require knowledge of master side key presses on the slave side. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to keypresses). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled. + +```c +#define SPLIT_LAYER_STATE_ENABLE +``` + +This enables syncing of the layer state between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the currently active layer. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled. + +```c +#define SPLIT_LED_STATE_ENABLE +``` + +This enables syncing of the Host LED status (caps lock, num lock, etc) between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the Host LED status. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled. + +```c +#define SPLIT_MODS_ENABLE +``` + +This enables transmitting modifier state (normal, weak and oneshot) to the non primary side of the split keyboard. The purpose of this feature is to support cosmetic use of modifer state (e.g. displaying status on an OLED screen). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled. + +```c +#define SPLIT_WPM_ENABLE +``` + +This enables transmitting the current WPM to the slave side of the split keyboard. The purpose of this feature is to support cosmetic use of WPM (e.g. displaying the current value on an OLED screen). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled. + +### Custom data sync between sides :id=custom-data-sync + +QMK's split transport allows for arbitrary data transactions at both the keyboard and user levels. This is modelled on a remote procedure call, with the master invoking a function on the slave side, with the ability to send data from master to slave, process it slave side, and send data back from slave to master. + +To leverage this, a keyboard or user/keymap can define a comma-separated list of _transaction IDs_: + +```c +// for keyboard-level data sync: +#define SPLIT_TRANSACTION_IDS_KB KEYBOARD_SYNC_A, KEYBOARD_SYNC_B +// or, for user: +#define SPLIT_TRANSACTION_IDS_USER USER_SYNC_A, USER_SYNC_B, USER_SYNC_C +``` + +These _transaction IDs_ then need a slave-side handler function to be registered with the split transport, for example: + +```c +typedef struct _master_to_slave_t { + int m2s_data; +} master_to_slave_t; + +typedef struct _slave_to_master_t { + int s2m_data; +} slave_to_master_t; + +void user_sync_a_slave_handler(uint8_t in_buflen, const void* in_data, uint8_t out_buflen, void* out_data) { + const master_to_slave_t *m2s = (const master_to_slave_t*)in_data; + slave_to_master_t *s2m = (slave_to_master_t*)out_data; + s2m->s2m_data = m2s->m2s_data + 5; // whatever comes in, add 5 so it can be sent back +} + +void keyboard_post_init_user(void) { + transaction_register_rpc(USER_SYNC_A, user_sync_a_slave_handler); +} +``` + +The master side can then invoke the slave-side handler - for normal keyboard functionality to be minimally affected, any keyboard- or user-level code attempting to sync data should be throttled: + +```c +void housekeeping_task_user(void) { + if (is_keyboard_master()) { + // Interact with slave every 500ms + static uint32_t last_sync = 0; + if (timer_elapsed32(last_sync) > 500) { + master_to_slave_t m2s = {6}; + slave_to_master_t s2m = {0}; + if(transaction_rpc_exec(USER_SYNC_A, sizeof(m2s), &m2s, sizeof(s2m), &s2m)) { + last_sync = timer_read32(); + dprintf("Slave value: %d\n", s2m.s2m_data); // this will now be 11, as the slave adds 5 + } else { + dprint("Slave sync failed!\n"); + } + } + } +} +``` + +!> It is recommended that any data sync between halves happens during the master side's _housekeeping task_. This ensures timely retries should failures occur. + +If only one-way data transfer is needed, helper methods are provided: + +```c +bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); +bool transaction_rpc_send(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer); +bool transaction_rpc_recv(int8_t transaction_id, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); +``` + +By default, the inbound and outbound data is limited to a maximum of 32 bytes each. The sizes can be altered if required: + +```c +// Master to slave: +#define RPC_M2S_BUFFER_SIZE 48 +// Slave to master: +#define RPC_S2M_BUFFER_SIZE 48 +``` ### Hardware Configuration Options diff --git a/drivers/avr/i2c_master.c b/drivers/avr/i2c_master.c index b1e4885298..2773e00778 100644 --- a/drivers/avr/i2c_master.c +++ b/drivers/avr/i2c_master.c @@ -28,8 +28,14 @@ # define F_SCL 400000UL // SCL frequency #endif +#ifndef I2C_START_RETRY_COUNT +# define I2C_START_RETRY_COUNT 20 +#endif // I2C_START_RETRY_COUNT + #define TWBR_val (((F_CPU / F_SCL) - 16) / 2) +#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) + void i2c_init(void) { TWSR = 0; /* no prescaler */ TWBR = (uint8_t)TWBR_val; @@ -47,7 +53,7 @@ void i2c_init(void) { #endif } -i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { +static i2c_status_t i2c_start_impl(uint8_t address, uint16_t timeout) { // reset TWI control register TWCR = 0; // transmit START condition @@ -86,6 +92,17 @@ i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { return I2C_STATUS_SUCCESS; } +i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { + // Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled. + uint16_t timeout_timer = timer_read(); + uint16_t time_slice = MAX(1, (timeout == (I2C_TIMEOUT_INFINITE)) ? 5 : (timeout / (I2C_START_RETRY_COUNT))); // if it's infinite, wait 1ms between attempts, otherwise split up the entire timeout into the number of retries + i2c_status_t status; + do { + status = i2c_start_impl(address, time_slice); + } while ((status < 0) && ((timeout == I2C_TIMEOUT_INFINITE) || (timer_elapsed(timeout_timer) < timeout))); + return status; +} + i2c_status_t i2c_write(uint8_t data, uint16_t timeout) { // load data into data register TWDR = data; diff --git a/drivers/avr/i2c_slave.c b/drivers/avr/i2c_slave.c index 62a378165a..2907f164c0 100644 --- a/drivers/avr/i2c_slave.c +++ b/drivers/avr/i2c_slave.c @@ -17,6 +17,7 @@ * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib */ +#include #include #include #include @@ -24,6 +25,12 @@ #include "i2c_slave.h" +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +# include "transactions.h" + +static volatile bool is_callback_executor = false; +#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; static volatile uint8_t buffer_address; @@ -48,11 +55,14 @@ ISR(TWI_vect) { case TW_SR_SLA_ACK: // The device is now a slave receiver slave_has_register_set = false; +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + is_callback_executor = false; +#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) break; case TW_SR_DATA_ACK: // This device is a slave receiver and has received data - // First byte is the location then the bytes will be writen in buffer with auto-incriment + // First byte is the location then the bytes will be writen in buffer with auto-increment if (!slave_has_register_set) { buffer_address = TWDR; @@ -60,10 +70,25 @@ ISR(TWI_vect) { ack = 0; buffer_address = 0; } - slave_has_register_set = true; // address has been receaved now fill in buffer + slave_has_register_set = true; // address has been received now fill in buffer + +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + // Work out if we're attempting to execute a callback + is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset; +#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) } else { i2c_slave_reg[buffer_address] = TWDR; buffer_address++; + +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID + if (is_callback_executor) { + split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id]; + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); + } + } +#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) } break; diff --git a/drivers/avr/i2c_slave.h b/drivers/avr/i2c_slave.h index 1cd0625ef4..a8647c9da3 100644 --- a/drivers/avr/i2c_slave.h +++ b/drivers/avr/i2c_slave.h @@ -22,7 +22,18 @@ #pragma once -#define I2C_SLAVE_REG_COUNT 30 +#ifndef I2C_SLAVE_REG_COUNT + +# if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +# include "transport.h" +# define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t) +# else // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +# define I2C_SLAVE_REG_COUNT 30 +# endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + +#endif // I2C_SLAVE_REG_COUNT + +_Static_assert(I2C_SLAVE_REG_COUNT < 256, "I2C target registers must be single byte"); extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; diff --git a/drivers/avr/serial.c b/drivers/avr/serial.c index 3647bee0d3..9a7345a53d 100644 --- a/drivers/avr/serial.c +++ b/drivers/avr/serial.c @@ -224,15 +224,8 @@ # define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2) # define SLAVE_INT_WIDTH_US 1 -# ifndef SERIAL_USE_MULTI_TRANSACTION -# define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY -# else -# define SLAVE_INT_ACK_WIDTH_UNIT 2 -# define SLAVE_INT_ACK_WIDTH 4 -# endif - -static SSTD_t *Transaction_table = NULL; -static uint8_t Transaction_table_size = 0; +# define SLAVE_INT_ACK_WIDTH_UNIT 2 +# define SLAVE_INT_ACK_WIDTH 4 inline static void serial_delay(void) ALWAYS_INLINE; inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); } @@ -259,16 +252,12 @@ inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); } inline static void serial_high(void) ALWAYS_INLINE; inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); } -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; +void soft_serial_initiator_init(void) { serial_output(); serial_high(); } -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; +void soft_serial_target_init(void) { serial_input_with_pullup(); // Enable INT0-INT7 @@ -395,19 +384,14 @@ static inline uint8_t nibble_bits_count(uint8_t bits) { // interrupt handle to be used by the target device ISR(SERIAL_PIN_INTERRUPT) { -# ifndef SERIAL_USE_MULTI_TRANSACTION - serial_low(); - serial_output(); - SSTD_t *trans = Transaction_table; -# else // recive transaction table index uint8_t tid, bits; uint8_t pecount = 0; sync_recv(); - bits = serial_read_chunk(&pecount, 7); + bits = serial_read_chunk(&pecount, 8); tid = bits >> 3; - bits = (bits & 7) != nibble_bits_count(tid); - if (bits || pecount > 0 || tid > Transaction_table_size) { + bits = (bits & 7) != (nibble_bits_count(tid) & 7); + if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) { return; } serial_delay_half1(); @@ -415,18 +399,22 @@ ISR(SERIAL_PIN_INTERRUPT) { serial_high(); // response step1 low->high serial_output(); _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH); - SSTD_t *trans = &Transaction_table[tid]; + split_transaction_desc_t *trans = &split_transaction_table[tid]; serial_low(); // response step2 ack high->low -# endif + + // If the transaction has a callback, we can execute it now + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); + } // target send phase - if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size); + if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size); // target switch to input change_sender2reciver(); // target recive phase if (trans->initiator2target_buffer_size > 0) { - if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size)) { + if (serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) { *trans->status = TRANSACTION_ACCEPTED; } else { *trans->status = TRANSACTION_DATA_ERROR; @@ -448,14 +436,12 @@ ISR(SERIAL_PIN_INTERRUPT) { // TRANSACTION_NO_RESPONSE // TRANSACTION_DATA_ERROR // this code is very time dependent, so we need to disable interrupts -# ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { - SSTD_t *trans = Transaction_table; -# else int soft_serial_transaction(int sstd_index) { - if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; - SSTD_t *trans = &Transaction_table[sstd_index]; -# endif + if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; + + if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered + cli(); // signal to the target that we want to start a transaction @@ -463,27 +449,11 @@ int soft_serial_transaction(int sstd_index) { serial_low(); _delay_us(SLAVE_INT_WIDTH_US); -# ifndef SERIAL_USE_MULTI_TRANSACTION - // wait for the target response - serial_input_with_pullup(); - _delay_us(SLAVE_INT_RESPONSE_TIME); - - // check if the target is present - if (serial_read_pin()) { - // target failed to pull the line low, assume not present - serial_output(); - serial_high(); - *trans->status = TRANSACTION_NO_RESPONSE; - sei(); - return TRANSACTION_NO_RESPONSE; - } - -# else // send transaction table index int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index)); sync_send(); _delay_sub_us(TID_SEND_ADJUST); - serial_write_chunk(tid, 7); + serial_write_chunk(tid, 8); serial_delay_half1(); // wait for the target response (step1 low->high) @@ -504,12 +474,11 @@ int soft_serial_transaction(int sstd_index) { } _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT); } -# endif // initiator recive phase // if the target is present syncronize with it if (trans->target2initiator_buffer_size > 0) { - if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size)) { + if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) { serial_output(); serial_high(); *trans->status = TRANSACTION_DATA_ERROR; @@ -523,7 +492,7 @@ int soft_serial_transaction(int sstd_index) { // initiator send phase if (trans->initiator2target_buffer_size > 0) { - serial_send_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size); + serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size); } // always, release the line when not in use @@ -534,9 +503,8 @@ int soft_serial_transaction(int sstd_index) { return TRANSACTION_END; } -# ifdef SERIAL_USE_MULTI_TRANSACTION int soft_serial_get_and_clean_status(int sstd_index) { - SSTD_t *trans = &Transaction_table[sstd_index]; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; cli(); int retval = *trans->status; *trans->status = 0; @@ -544,8 +512,6 @@ int soft_serial_get_and_clean_status(int sstd_index) { sei(); return retval; } -# endif - #endif // Helix serial.c history diff --git a/drivers/avr/serial.h b/drivers/avr/serial.h deleted file mode 100644 index 53e66cf905..0000000000 --- a/drivers/avr/serial.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include - -// ///////////////////////////////////////////////////////////////// -// Need Soft Serial defines in config.h -// ///////////////////////////////////////////////////////////////// -// ex. -// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6 -// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5 -// // 1: about 137kbps (default) -// // 2: about 75kbps -// // 3: about 39kbps -// // 4: about 26kbps -// // 5: about 20kbps -// -// //// USE simple API (using signle-type transaction function) -// /* nothing */ -// //// USE flexible API (using multi-type transaction function) -// #define SERIAL_USE_MULTI_TRANSACTION -// -// ///////////////////////////////////////////////////////////////// - -// Soft Serial Transaction Descriptor -typedef struct _SSTD_t { - uint8_t *status; - uint8_t initiator2target_buffer_size; - uint8_t *initiator2target_buffer; - uint8_t target2initiator_buffer_size; - uint8_t *target2initiator_buffer; -} SSTD_t; -#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t)) - -// initiator is transaction start side -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size); -// target is interrupt accept side -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size); - -// initiator resullt -#define TRANSACTION_END 0 -#define TRANSACTION_NO_RESPONSE 0x1 -#define TRANSACTION_DATA_ERROR 0x2 -#define TRANSACTION_TYPE_ERROR 0x4 -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void); -#else -int soft_serial_transaction(int sstd_index); -#endif - -// target status -// *SSTD_t.status has -// initiator: -// TRANSACTION_END -// or TRANSACTION_NO_RESPONSE -// or TRANSACTION_DATA_ERROR -// target: -// TRANSACTION_DATA_ERROR -// or TRANSACTION_ACCEPTED -#define TRANSACTION_ACCEPTED 0x8 -#ifdef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_get_and_clean_status(int sstd_index); -#endif diff --git a/drivers/chibios/serial.c b/drivers/chibios/serial.c index 54f7e1321f..f54fbcee4e 100644 --- a/drivers/chibios/serial.c +++ b/drivers/chibios/serial.c @@ -74,21 +74,12 @@ static THD_FUNCTION(Thread1, arg) { } } -static SSTD_t *Transaction_table = NULL; -static uint8_t Transaction_table_size = 0; - -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - +void soft_serial_initiator_init(void) { serial_output(); serial_high(); } -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - +void soft_serial_target_init(void) { serial_input(); palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE); @@ -154,16 +145,14 @@ void interrupt_handler(void *arg) { uint8_t checksum_computed = 0; int sstd_index = 0; -#ifdef SERIAL_USE_MULTI_TRANSACTION sstd_index = serial_read_byte(); sync_send(); -#endif - SSTD_t *trans = &Transaction_table[sstd_index]; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { - trans->initiator2target_buffer[i] = serial_read_byte(); + split_trans_initiator2target_buffer(trans)[i] = serial_read_byte(); sync_send(); - checksum_computed += trans->initiator2target_buffer[i]; + checksum_computed += split_trans_initiator2target_buffer(trans)[i]; } checksum_computed ^= 7; uint8_t checksum_received = serial_read_byte(); @@ -172,12 +161,17 @@ void interrupt_handler(void *arg) { // wait for the sync to finish sending serial_delay(); + // Allow any slave processing to occur + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); + } + uint8_t checksum = 0; for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { - serial_write_byte(trans->target2initiator_buffer[i]); + serial_write_byte(split_trans_target2initiator_buffer(trans)[i]); sync_send(); serial_delay_half(); - checksum += trans->target2initiator_buffer[i]; + checksum += split_trans_target2initiator_buffer(trans)[i]; } serial_write_byte(checksum ^ 7); sync_send(); @@ -206,15 +200,10 @@ void interrupt_handler(void *arg) { // TRANSACTION_NO_RESPONSE // TRANSACTION_DATA_ERROR // this code is very time dependent, so we need to disable interrupts -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { - int sstd_index = 0; -#else int soft_serial_transaction(int sstd_index) { -#endif - - if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; - SSTD_t *trans = &Transaction_table[sstd_index]; + if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; + split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; + if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered // TODO: remove extra delay between transactions serial_delay(); @@ -244,14 +233,13 @@ int soft_serial_transaction(int sstd_index) { uint8_t checksum = 0; // send data to the slave -#ifdef SERIAL_USE_MULTI_TRANSACTION serial_write_byte(sstd_index); // first chunk is transaction id sync_recv(); -#endif + for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { - serial_write_byte(trans->initiator2target_buffer[i]); + serial_write_byte(split_trans_initiator2target_buffer(trans)[i]); sync_recv(); - checksum += trans->initiator2target_buffer[i]; + checksum += split_trans_initiator2target_buffer(trans)[i]; } serial_write_byte(checksum ^ 7); sync_recv(); @@ -262,9 +250,9 @@ int soft_serial_transaction(int sstd_index) { // receive data from the slave uint8_t checksum_computed = 0; for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { - trans->target2initiator_buffer[i] = serial_read_byte(); + split_trans_target2initiator_buffer(trans)[i] = serial_read_byte(); sync_recv(); - checksum_computed += trans->target2initiator_buffer[i]; + checksum_computed += split_trans_target2initiator_buffer(trans)[i]; } checksum_computed ^= 7; uint8_t checksum_received = serial_read_byte(); diff --git a/drivers/chibios/serial.h b/drivers/chibios/serial.h deleted file mode 100644 index 0c1857d52e..0000000000 --- a/drivers/chibios/serial.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include - -// ///////////////////////////////////////////////////////////////// -// Need Soft Serial defines in config.h -// ///////////////////////////////////////////////////////////////// -// ex. -// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6 -// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5 -// // 1: about 137kbps (default) -// // 2: about 75kbps -// // 3: about 39kbps -// // 4: about 26kbps -// // 5: about 20kbps -// -// //// USE simple API (using signle-type transaction function) -// /* nothing */ -// //// USE flexible API (using multi-type transaction function) -// #define SERIAL_USE_MULTI_TRANSACTION -// -// ///////////////////////////////////////////////////////////////// - -// Soft Serial Transaction Descriptor -typedef struct _SSTD_t { - uint8_t *status; - uint8_t initiator2target_buffer_size; - uint8_t *initiator2target_buffer; - uint8_t target2initiator_buffer_size; - uint8_t *target2initiator_buffer; -} SSTD_t; -#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t)) - -// initiator is transaction start side -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size); -// target is interrupt accept side -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size); - -// initiator result -#define TRANSACTION_END 0 -#define TRANSACTION_NO_RESPONSE 0x1 -#define TRANSACTION_DATA_ERROR 0x2 -#define TRANSACTION_TYPE_ERROR 0x4 -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void); -#else -int soft_serial_transaction(int sstd_index); -#endif - -// target status -// *SSTD_t.status has -// initiator: -// TRANSACTION_END -// or TRANSACTION_NO_RESPONSE -// or TRANSACTION_DATA_ERROR -// target: -// TRANSACTION_DATA_ERROR -// or TRANSACTION_ACCEPTED -#define TRANSACTION_ACCEPTED 0x8 -#ifdef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_get_and_clean_status(int sstd_index); -#endif diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c index cae29388c3..9f180d2d74 100644 --- a/drivers/chibios/serial_usart.c +++ b/drivers/chibios/serial_usart.c @@ -113,37 +113,29 @@ void usart_slave_init(void) { chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); } -static SSTD_t* Transaction_table = NULL; -static uint8_t Transaction_table_size = 0; +void soft_serial_initiator_init(void) { usart_master_init(); } -void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - - usart_master_init(); -} - -void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) { - Transaction_table = sstd_table; - Transaction_table_size = (uint8_t)sstd_table_size; - - usart_slave_init(); -} +void soft_serial_target_init(void) { usart_slave_init(); } void handle_soft_serial_slave(void) { - uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id - SSTD_t* trans = &Transaction_table[sstd_index]; + uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id + split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; // Always write back the sstd_index as part of a basic handshake sstd_index ^= HANDSHAKE_MAGIC; sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); if (trans->initiator2target_buffer_size) { - sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); + sdRead(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size); + } + + // Allow any slave processing to occur + if (trans->slave_callback) { + trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); } if (trans->target2initiator_buffer_size) { - sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); + sdWrite(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size); } if (trans->status) { @@ -160,17 +152,14 @@ void handle_soft_serial_slave(void) { // TRANSACTION_END // TRANSACTION_NO_RESPONSE // TRANSACTION_DATA_ERROR -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { - uint8_t sstd_index = 0; -#else int soft_serial_transaction(int index) { uint8_t sstd_index = index; -#endif - if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; - SSTD_t* trans = &Transaction_table[sstd_index]; - msg_t res = 0; + if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; + split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; + msg_t res = 0; + + if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered sdClear(&SERIAL_USART_DRIVER); @@ -189,7 +178,7 @@ int soft_serial_transaction(int index) { } if (trans->initiator2target_buffer_size) { - res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); + res = sdWriteTimeout(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); if (res < 0) { dprintf("serial::usart_transmit NO_RESPONSE\n"); return TRANSACTION_NO_RESPONSE; @@ -197,7 +186,7 @@ int soft_serial_transaction(int index) { } if (trans->target2initiator_buffer_size) { - res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); + res = sdReadTimeout(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); if (res < 0) { dprintf("serial::usart_receive NO_RESPONSE\n"); return TRANSACTION_NO_RESPONSE; diff --git a/drivers/serial.h b/drivers/serial.h new file mode 100644 index 0000000000..d9c2a69e96 --- /dev/null +++ b/drivers/serial.h @@ -0,0 +1,46 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include + +// initiator is transaction start side +void soft_serial_initiator_init(void); +// target is interrupt accept side +void soft_serial_target_init(void); + +// initiator result +#define TRANSACTION_END 0 +#define TRANSACTION_NO_RESPONSE 0x1 +#define TRANSACTION_DATA_ERROR 0x2 +#define TRANSACTION_TYPE_ERROR 0x4 +int soft_serial_transaction(int sstd_index); + +// target status +// *SSTD_t.status has +// initiator: +// TRANSACTION_END +// or TRANSACTION_NO_RESPONSE +// or TRANSACTION_DATA_ERROR +// target: +// TRANSACTION_DATA_ERROR +// or TRANSACTION_ACCEPTED +#define TRANSACTION_ACCEPTED 0x8 +int soft_serial_get_and_clean_status(int sstd_index); diff --git a/keyboards/draculad/config.h b/keyboards/draculad/config.h index 8a27fdea4d..d8a9fbd37c 100644 --- a/keyboards/draculad/config.h +++ b/keyboards/draculad/config.h @@ -65,3 +65,5 @@ along with this program. If not, see . #define UNUSED_PINS #define EE_HANDS + +#define LAYER_STATE_8BIT \ No newline at end of file diff --git a/keyboards/handwired/freoduo/rules.mk b/keyboards/handwired/freoduo/rules.mk index e3da3753e4..d8923557d6 100644 --- a/keyboards/handwired/freoduo/rules.mk +++ b/keyboards/handwired/freoduo/rules.mk @@ -11,7 +11,7 @@ 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 -COMMAND_ENABLE = yes # Commands for debug and configuration +COMMAND_ENABLE = no # Commands for debug and configuration # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend # if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work @@ -21,4 +21,4 @@ RGBLIGHT_ENABLE = yes # Enable keyboard RGB underglow BLUETOOTH_ENABLE = no # Enable Bluetooth AUDIO_ENABLE = no # Audio output VELOCIKEY_ENABLE = yes -SPLIT_KEYBOARD = yes \ No newline at end of file +SPLIT_KEYBOARD = yes diff --git a/keyboards/helix/rev2/keymaps/default/rules.mk b/keyboards/helix/rev2/keymaps/default/rules.mk index 206e836ec0..c16f3e2b87 100644 --- a/keyboards/helix/rev2/keymaps/default/rules.mk +++ b/keyboards/helix/rev2/keymaps/default/rules.mk @@ -5,7 +5,7 @@ # See TOP/keyboards/helix/rules.mk for a list of options that can be set. # See TOP/docs/config_options.md for more information. # -LTO_ENABLE = no # if firmware size over limit, try this option +LTO_ENABLE = yes # if firmware size over limit, try this option # Helix Spacific Build Options # you can uncomment and edit follows 7 Variables diff --git a/keyboards/keebio/iris/rev2/rules.mk b/keyboards/keebio/iris/rev2/rules.mk index 765fa7f07a..197bad4764 100644 --- a/keyboards/keebio/iris/rev2/rules.mk +++ b/keyboards/keebio/iris/rev2/rules.mk @@ -32,3 +32,5 @@ RGBLIGHT_ENABLE = yes # Enable WS2812 RGB underlight. SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend SPLIT_KEYBOARD = yes + +LTO_ENABLE = yes \ No newline at end of file diff --git a/keyboards/keebio/iris/rev3/rules.mk b/keyboards/keebio/iris/rev3/rules.mk index ea92bc98f5..b64eab5a4b 100644 --- a/keyboards/keebio/iris/rev3/rules.mk +++ b/keyboards/keebio/iris/rev3/rules.mk @@ -33,3 +33,4 @@ SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend SPLIT_KEYBOARD = yes ENCODER_ENABLE = yes +LTO_ENABLE = yes \ No newline at end of file diff --git a/keyboards/keebio/iris/rev4/rules.mk b/keyboards/keebio/iris/rev4/rules.mk index ea92bc98f5..9bf5688da0 100644 --- a/keyboards/keebio/iris/rev4/rules.mk +++ b/keyboards/keebio/iris/rev4/rules.mk @@ -33,3 +33,5 @@ SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend SPLIT_KEYBOARD = yes ENCODER_ENABLE = yes + +LTO_ENABLE = yes \ No newline at end of file diff --git a/keyboards/keebio/quefrency/rules.mk b/keyboards/keebio/quefrency/rules.mk index 2c5ad0c36b..c674e10162 100644 --- a/keyboards/keebio/quefrency/rules.mk +++ b/keyboards/keebio/quefrency/rules.mk @@ -19,3 +19,4 @@ SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend SPLIT_KEYBOARD = yes DEFAULT_FOLDER = keebio/quefrency/rev1 +LTO_ENABLE = yes diff --git a/keyboards/keebio/viterbi/rev2/rules.mk b/keyboards/keebio/viterbi/rev2/rules.mk index f95e7ae6a4..829d6a56e0 100644 --- a/keyboards/keebio/viterbi/rev2/rules.mk +++ b/keyboards/keebio/viterbi/rev2/rules.mk @@ -1,3 +1,5 @@ BACKLIGHT_ENABLE = yes LAYOUTS = ortho_5x14 + +LTO_ENABLE = yes \ No newline at end of file diff --git a/keyboards/keebio/viterbi/rules.mk b/keyboards/keebio/viterbi/rules.mk index 0b746d1720..92576d33e8 100644 --- a/keyboards/keebio/viterbi/rules.mk +++ b/keyboards/keebio/viterbi/rules.mk @@ -19,16 +19,16 @@ 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 -COMMAND_ENABLE = yes # Commands for debug and configuration +COMMAND_ENABLE = no # Commands for debug and configuration NKRO_ENABLE = no # 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 +BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality MIDI_ENABLE = no # MIDI controls AUDIO_ENABLE = no # Audio output on port C6 UNICODE_ENABLE = no # Unicode BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID -RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. +RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE -SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend SPLIT_KEYBOARD = yes diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index 2cf7b70582..56d91b07fe 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c @@ -23,9 +23,11 @@ along with this program. If not, see . #include "quantum.h" #include "split_util.h" #include "config.h" -#include "transport.h" +#include "transactions.h" -#define ERROR_DISCONNECT_COUNT 5 +#ifndef ERROR_DISCONNECT_COUNT +# define ERROR_DISCONNECT_COUNT 5 +#endif // ERROR_DISCONNECT_COUNT #define ROWS_PER_HAND (MATRIX_ROWS / 2) diff --git a/quantum/split_common/post_config.h b/quantum/split_common/post_config.h index 4ae1d52732..a4c0a1956b 100644 --- a/quantum/split_common/post_config.h +++ b/quantum/split_common/post_config.h @@ -7,13 +7,4 @@ # ifndef F_SCL # define F_SCL 100000UL // SCL frequency # endif - -#else // use serial -// When using serial, the user must define RGBLIGHT_SPLIT explicitly -// in config.h as needed. -// see quantum/rgblight_post_config.h -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) -// When using serial and RGBLIGHT_SPLIT need separate transaction -# define SERIAL_USE_MULTI_TRANSACTION -# endif #endif diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h new file mode 100644 index 0000000000..464c73478a --- /dev/null +++ b/quantum/split_common/transaction_id_define.h @@ -0,0 +1,94 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +enum serial_transaction_id { +#ifdef USE_I2C + I2C_EXECUTE_CALLBACK, +#endif // USE_I2C + + GET_SLAVE_MATRIX_CHECKSUM, + GET_SLAVE_MATRIX_DATA, + +#ifdef SPLIT_TRANSPORT_MIRROR + PUT_MASTER_MATRIX, +#endif // SPLIT_TRANSPORT_MIRROR + +#ifdef ENCODER_ENABLE + GET_ENCODERS_CHECKSUM, + GET_ENCODERS_DATA, +#endif // ENCODER_ENABLE + +#ifndef DISABLE_SYNC_TIMER + PUT_SYNC_TIMER, +#endif // DISABLE_SYNC_TIMER + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + PUT_LAYER_STATE, + PUT_DEFAULT_LAYER_STATE, +#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +#ifdef SPLIT_LED_STATE_ENABLE + PUT_LED_STATE, +#endif // SPLIT_LED_STATE_ENABLE + +#ifdef SPLIT_MODS_ENABLE + PUT_MODS, +#endif // SPLIT_MODS_ENABLE + +#ifdef BACKLIGHT_ENABLE + PUT_BACKLIGHT, +#endif // BACKLIGHT_ENABLE + +#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + PUT_RGBLIGHT, +#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + PUT_LED_MATRIX, +#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + PUT_RGB_MATRIX, +#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + PUT_WPM, +#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + PUT_RPC_INFO, + PUT_RPC_REQ_DATA, + EXECUTE_RPC, + GET_RPC_RESP_DATA, +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +// keyboard-specific +#ifdef SPLIT_TRANSACTION_IDS_KB + SPLIT_TRANSACTION_IDS_KB, +#endif // SPLIT_TRANSACTION_IDS_KB + +// user/keymap-specific +#ifdef SPLIT_TRANSACTION_IDS_USER + SPLIT_TRANSACTION_IDS_USER, +#endif // SPLIT_TRANSACTION_IDS_USER + + NUM_TOTAL_TRANSACTIONS +}; + +// Ensure we only use 5 bits for transaction +_Static_assert(NUM_TOTAL_TRANSACTIONS <= (1 << 5), "Max number of usable transactions exceeded"); diff --git a/quantum/split_common/transactions.c b/quantum/split_common/transactions.c new file mode 100644 index 0000000000..99a8623b3c --- /dev/null +++ b/quantum/split_common/transactions.c @@ -0,0 +1,670 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "debug.h" +#include "matrix.h" +#include "quantum.h" +#include "transactions.h" +#include "transport.h" +#include "transaction_id_define.h" + +#define SYNC_TIMER_OFFSET 2 + +#ifndef FORCED_SYNC_THROTTLE_MS +# define FORCED_SYNC_THROTTLE_MS 100 +#endif // FORCED_SYNC_THROTTLE_MS + +#define sizeof_member(type, member) sizeof(((type *)NULL)->member) + +#define trans_initiator2target_initializer_cb(member, cb) \ + { &dummy, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb } +#define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL) + +#define trans_target2initiator_initializer_cb(member, cb) \ + { &dummy, 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb } +#define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL) + +#define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0) +#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length) + +static uint8_t crc8(const void *data, size_t len) { + const uint8_t *p = (const uint8_t *)data; + uint8_t crc = 0xff; + size_t i, j; + for (i = 0; i < len; i++) { + crc ^= p[i]; + for (j = 0; j < 8; j++) { + if ((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +// Forward-declare the RPC callback handlers +void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); +void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +//////////////////////////////////////////////////// +// Helpers + +bool transaction_handler_master(bool okay, matrix_row_t master_matrix[], matrix_row_t slave_matrix[], const char *prefix, bool (*handler)(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])) { + if (okay) { + bool this_okay = true; + for (int iter = 1; iter <= 10; ++iter) { + if (!this_okay) { + for (int i = 0; i < iter * iter; ++i) { + wait_us(10); + } + } + ATOMIC_BLOCK_FORCEON { this_okay = handler(master_matrix, slave_matrix); }; + if (this_okay) break; + } + okay &= this_okay; + if (!okay) { + dprintf("Failed to execute %s\n", prefix); + } + } + return okay; +} + +#define TRANSACTION_HANDLER_MASTER(prefix) \ + do { \ + okay &= transaction_handler_master(okay, master_matrix, slave_matrix, #prefix, &prefix##_master); \ + } while (0) + +#define TRANSACTION_HANDLER_SLAVE(prefix) \ + do { \ + ATOMIC_BLOCK_FORCEON { prefix##_slave(master_matrix, slave_matrix); }; \ + } while (0) + +inline static bool read_if_checksum_mismatch(int8_t trans_id_checksum, int8_t trans_id_retrieve, uint32_t *last_update, void *destination, const void *equiv_shmem, size_t length) { + uint8_t curr_checksum; + bool okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum)); + if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) { + okay &= transport_read(trans_id_retrieve, destination, length); + okay &= curr_checksum == crc8(equiv_shmem, length); + if (okay) { + *last_update = timer_read32(); + } + } else { + memcpy(destination, equiv_shmem, length); + } + return okay; +} + +inline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) { + bool okay = true; + if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) { + okay &= transport_write(trans_id, source, length); + if (okay) { + *last_update = timer_read32(); + } + } + return okay; +} + +inline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) { + // Just run a memcmp to compare the source and equivalent shmem location + return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length); +} + +//////////////////////////////////////////////////// +// Slave matrix + +static bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors + matrix_row_t temp_matrix[(MATRIX_ROWS) / 2]; // holding area while we test whether or not checksum is correct + + bool okay = read_if_checksum_mismatch(GET_SLAVE_MATRIX_CHECKSUM, GET_SLAVE_MATRIX_DATA, &last_update, temp_matrix, split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix)); + if (okay) { + // Checksum matches the received data, save as the last matrix state + memcpy(last_matrix, temp_matrix, sizeof(temp_matrix)); + } + // Copy out the last-known-good matrix state to the slave matrix + memcpy(slave_matrix, last_matrix, sizeof(last_matrix)); + return okay; +} + +static void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix)); + split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix)); +} + +// clang-format off +#define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix_handlers) +#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(slave_matrix_handlers) +#define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \ + [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \ + [GET_SLAVE_MATRIX_DATA] = trans_target2initiator_initializer(smatrix.matrix), +// clang-format on + +//////////////////////////////////////////////////// +// Master matrix + +#ifdef SPLIT_TRANSPORT_MIRROR + +static bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix)); +} + +static void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + // Always copy to the master matrix + memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix)); +} + +# define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix_handlers) +# define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(master_matrix_handlers) +# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix), + +#else // SPLIT_TRANSPORT_MIRROR + +# define TRANSACTIONS_MASTER_MATRIX_MASTER() +# define TRANSACTIONS_MASTER_MATRIX_SLAVE() +# define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS + +#endif // SPLIT_TRANSPORT_MIRROR + +//////////////////////////////////////////////////// +// Encoders + +#ifdef ENCODER_ENABLE + +static bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + uint8_t temp_state[NUMBER_OF_ENCODERS]; + + bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, temp_state, split_shmem->encoders.state, sizeof(temp_state)); + if (okay) encoder_update_raw(temp_state); + return okay; +} + +static void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + uint8_t encoder_state[NUMBER_OF_ENCODERS]; + encoder_state_raw(encoder_state); + // Always prepare the encoder state for read. + memcpy(split_shmem->encoders.state, encoder_state, sizeof(encoder_state)); + // Now update the checksum given that the encoders has been written to + split_shmem->encoders.checksum = crc8(encoder_state, sizeof(encoder_state)); +} + +// clang-format off +# define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder_handlers) +# define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE(encoder_handlers) +# define TRANSACTIONS_ENCODERS_REGISTRATIONS \ + [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \ + [GET_ENCODERS_DATA] = trans_target2initiator_initializer(encoders.state), +// clang-format on + +#else // ENCODER_ENABLE + +# define TRANSACTIONS_ENCODERS_MASTER() +# define TRANSACTIONS_ENCODERS_SLAVE() +# define TRANSACTIONS_ENCODERS_REGISTRATIONS + +#endif // ENCODER_ENABLE + +//////////////////////////////////////////////////// +// Sync timer + +#ifndef DISABLE_SYNC_TIMER + +static bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + + bool okay = true; + if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) { + uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; + okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer)); + if (okay) { + last_update = timer_read32(); + } + } + return okay; +} + +static void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_sync_timer = 0; + if (last_sync_timer != split_shmem->sync_timer) { + last_sync_timer = split_shmem->sync_timer; + sync_timer_update(last_sync_timer); + } +} + +# define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer_handlers) +# define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE(sync_timer_handlers) +# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer), + +#else // DISABLE_SYNC_TIMER + +# define TRANSACTIONS_SYNC_TIMER_MASTER() +# define TRANSACTIONS_SYNC_TIMER_SLAVE() +# define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS + +#endif // DISABLE_SYNC_TIMER + +//////////////////////////////////////////////////// +// Layer state + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +static bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_layer_state_update = 0; + static uint32_t last_default_layer_state_update = 0; + + bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state)); + if (okay) { + okay &= send_if_condition(PUT_DEFAULT_LAYER_STATE, &last_default_layer_state_update, (default_layer_state != split_shmem->layers.default_layer_state), &default_layer_state, sizeof(default_layer_state)); + } + return okay; +} + +static void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + layer_state = split_shmem->layers.layer_state; + default_layer_state = split_shmem->layers.default_layer_state; +} + +// clang-format off +# define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state_handlers) +# define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(layer_state_handlers) +# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \ + [PUT_LAYER_STATE] = trans_initiator2target_initializer(layers.layer_state), \ + [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state), +// clang-format on + +#else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +# define TRANSACTIONS_LAYER_STATE_MASTER() +# define TRANSACTIONS_LAYER_STATE_SLAVE() +# define TRANSACTIONS_LAYER_STATE_REGISTRATIONS + +#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +//////////////////////////////////////////////////// +// LED state + +#ifdef SPLIT_LED_STATE_ENABLE + +static bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + uint8_t led_state = host_keyboard_leds(); + return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state)); +} + +static void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + void set_split_host_keyboard_leds(uint8_t led_state); + set_split_host_keyboard_leds(split_shmem->led_state); +} + +# define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state_handlers) +# define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE(led_state_handlers) +# define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state), + +#else // SPLIT_LED_STATE_ENABLE + +# define TRANSACTIONS_LED_STATE_MASTER() +# define TRANSACTIONS_LED_STATE_SLAVE() +# define TRANSACTIONS_LED_STATE_REGISTRATIONS + +#endif // SPLIT_LED_STATE_ENABLE + +//////////////////////////////////////////////////// +// Mods + +#ifdef SPLIT_MODS_ENABLE + +static bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + bool mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS; + split_mods_sync_t new_mods; + new_mods.real_mods = get_mods(); + if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) { + mods_need_sync = true; + } + + new_mods.weak_mods = get_weak_mods(); + if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) { + mods_need_sync = true; + } + +# ifndef NO_ACTION_ONESHOT + new_mods.oneshot_mods = get_oneshot_mods(); + if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) { + mods_need_sync = true; + } +# endif // NO_ACTION_ONESHOT + + bool okay = true; + if (mods_need_sync) { + okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods)); + if (okay) { + last_update = timer_read32(); + } + } + + return okay; +} + +static void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + set_mods(split_shmem->mods.real_mods); + set_weak_mods(split_shmem->mods.weak_mods); +# ifndef NO_ACTION_ONESHOT + set_oneshot_mods(split_shmem->mods.oneshot_mods); +# endif +} + +# define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods_handlers) +# define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods_handlers) +# define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods), + +#else // SPLIT_MODS_ENABLE + +# define TRANSACTIONS_MODS_MASTER() +# define TRANSACTIONS_MODS_SLAVE() +# define TRANSACTIONS_MODS_REGISTRATIONS + +#endif // SPLIT_MODS_ENABLE + +//////////////////////////////////////////////////// +// Backlight + +#ifdef BACKLIGHT_ENABLE + +static bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0; + return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level)); +} + +static void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { backlight_set(split_shmem->backlight_level); } + +# define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight_handlers) +# define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight_handlers) +# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level), + +#else // BACKLIGHT_ENABLE + +# define TRANSACTIONS_BACKLIGHT_MASTER() +# define TRANSACTIONS_BACKLIGHT_SLAVE() +# define TRANSACTIONS_BACKLIGHT_REGISTRATIONS + +#endif // BACKLIGHT_ENABLE + +//////////////////////////////////////////////////// +// RGBLIGHT + +#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +static bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + rgblight_syncinfo_t rgblight_sync; + rgblight_get_syncinfo(&rgblight_sync); + if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) { + rgblight_clear_change_flags(); + } else { + return false; + } + return true; +} + +static void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + // Update the RGB with the new data + if (split_shmem->rgblight_sync.status.change_flags != 0) { + rgblight_update_sync(&split_shmem->rgblight_sync, false); + split_shmem->rgblight_sync.status.change_flags = 0; + } +} + +# define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight_handlers) +# define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight_handlers) +# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync), + +#else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +# define TRANSACTIONS_RGBLIGHT_MASTER() +# define TRANSACTIONS_RGBLIGHT_SLAVE() +# define TRANSACTIONS_RGBLIGHT_REGISTRATIONS + +#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +//////////////////////////////////////////////////// +// LED Matrix + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +static bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + led_matrix_sync_t led_matrix_sync; + memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t)); + led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state(); + return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync)); +} + +static void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t)); + led_matrix_set_suspend_state(split_shmem->led_matrix_sync.led_suspend_state); +} + +# define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix_handlers) +# define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix_handlers) +# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync), + +#else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +# define TRANSACTIONS_LED_MATRIX_MASTER() +# define TRANSACTIONS_LED_MATRIX_SLAVE() +# define TRANSACTIONS_LED_MATRIX_REGISTRATIONS + +#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +//////////////////////////////////////////////////// +// RGB Matrix + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +static bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + rgb_matrix_sync_t rgb_matrix_sync; + memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t)); + rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state(); + return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync)); +} + +static void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t)); + rgb_matrix_set_suspend_state(split_shmem->rgb_matrix_sync.rgb_suspend_state); +} + +# define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix_handlers) +# define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix_handlers) +# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync), + +#else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +# define TRANSACTIONS_RGB_MATRIX_MASTER() +# define TRANSACTIONS_RGB_MATRIX_SLAVE() +# define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS + +#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +//////////////////////////////////////////////////// +// WPM + +#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +static bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + static uint32_t last_update = 0; + uint8_t current_wpm = get_current_wpm(); + return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), ¤t_wpm, sizeof(current_wpm)); +} + +static void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { set_current_wpm(split_shmem->current_wpm); } + +# define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm_handlers) +# define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE(wpm_handlers) +# define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm), + +#else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +# define TRANSACTIONS_WPM_MASTER() +# define TRANSACTIONS_WPM_SLAVE() +# define TRANSACTIONS_WPM_REGISTRATIONS + +#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +//////////////////////////////////////////////////// + +uint8_t dummy; +split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = { + // Set defaults + [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {NULL, 0, 0, 0, 0, 0}, + +#ifdef USE_I2C + [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id), +#endif // USE_I2C + + // clang-format off + TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS + TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS + TRANSACTIONS_ENCODERS_REGISTRATIONS + TRANSACTIONS_SYNC_TIMER_REGISTRATIONS + TRANSACTIONS_LAYER_STATE_REGISTRATIONS + TRANSACTIONS_LED_STATE_REGISTRATIONS + TRANSACTIONS_MODS_REGISTRATIONS + TRANSACTIONS_BACKLIGHT_REGISTRATIONS + TRANSACTIONS_RGBLIGHT_REGISTRATIONS + TRANSACTIONS_LED_MATRIX_REGISTRATIONS + TRANSACTIONS_RGB_MATRIX_REGISTRATIONS + TRANSACTIONS_WPM_REGISTRATIONS +// clang-format on + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + [PUT_RPC_INFO] = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback), + [PUT_RPC_REQ_DATA] = trans_initiator2target_initializer(rpc_m2s_buffer), + [EXECUTE_RPC] = trans_initiator2target_initializer_cb(rpc_info.transaction_id, slave_rpc_exec_callback), + [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer), +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +}; + +bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + bool okay = true; + TRANSACTIONS_SLAVE_MATRIX_MASTER(); + TRANSACTIONS_MASTER_MATRIX_MASTER(); + TRANSACTIONS_ENCODERS_MASTER(); + TRANSACTIONS_SYNC_TIMER_MASTER(); + TRANSACTIONS_LAYER_STATE_MASTER(); + TRANSACTIONS_LED_STATE_MASTER(); + TRANSACTIONS_MODS_MASTER(); + TRANSACTIONS_BACKLIGHT_MASTER(); + TRANSACTIONS_RGBLIGHT_MASTER(); + TRANSACTIONS_LED_MATRIX_MASTER(); + TRANSACTIONS_RGB_MATRIX_MASTER(); + TRANSACTIONS_WPM_MASTER(); + return okay; +} + +void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { + TRANSACTIONS_SLAVE_MATRIX_SLAVE(); + TRANSACTIONS_MASTER_MATRIX_SLAVE(); + TRANSACTIONS_ENCODERS_SLAVE(); + TRANSACTIONS_SYNC_TIMER_SLAVE(); + TRANSACTIONS_LAYER_STATE_SLAVE(); + TRANSACTIONS_LED_STATE_SLAVE(); + TRANSACTIONS_MODS_SLAVE(); + TRANSACTIONS_BACKLIGHT_SLAVE(); + TRANSACTIONS_RGBLIGHT_SLAVE(); + TRANSACTIONS_LED_MATRIX_SLAVE(); + TRANSACTIONS_RGB_MATRIX_SLAVE(); + TRANSACTIONS_WPM_SLAVE(); +} + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) { + // Prevent invoking RPC on QMK core sync data + if (transaction_id <= GET_RPC_RESP_DATA) return; + + // Set the callback + split_transaction_table[transaction_id].slave_callback = callback; + split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer); + split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer); +} + +bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { + // Prevent invoking RPC on QMK core sync data + if (transaction_id <= GET_RPC_RESP_DATA) return false; + // Prevent sizing issues + if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false; + if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false; + + // Prepare the metadata block + rpc_sync_info_t info = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size}; + + // Make sure the local side knows that we're not sending the full block of data + split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = initiator2target_buffer_size; + split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size; + + // Run through the sequence: + // * set the transaction ID and lengths + // * send the request data + // * execute RPC callback + // * retrieve the response data + if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) { + return false; + } + if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) { + return false; + } + if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) { + return false; + } + if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) { + return false; + } + return true; +} + +void slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { + // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data. + // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it. + // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime. + + split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size = split_shmem->rpc_info.m2s_length; + split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.s2m_length; +} + +void slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) { + // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed. + // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs. + int8_t transaction_id = split_shmem->rpc_info.transaction_id; + if (transaction_id < NUM_TOTAL_TRANSACTIONS) { + split_transaction_desc_t *trans = &split_transaction_table[transaction_id]; + if (trans->slave_callback) { + trans->slave_callback(split_shmem->rpc_info.m2s_length, split_shmem->rpc_m2s_buffer, split_shmem->rpc_info.s2m_length, split_shmem->rpc_s2m_buffer); + } + } +} + +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) diff --git a/quantum/split_common/transactions.h b/quantum/split_common/transactions.h new file mode 100644 index 0000000000..4306ba1d87 --- /dev/null +++ b/quantum/split_common/transactions.h @@ -0,0 +1,54 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "stdint.h" +#include "stdbool.h" + +#include "matrix.h" +#include "transaction_id_define.h" +#include "transport.h" + +typedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); + +// Split transaction Descriptor +typedef struct _split_transaction_desc_t { + uint8_t * status; + uint8_t initiator2target_buffer_size; + uint16_t initiator2target_offset; + uint8_t target2initiator_buffer_size; + uint16_t target2initiator_offset; + slave_callback_t slave_callback; +} split_transaction_desc_t; + +// Forward declaration for the split transactions +extern split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS]; + +#define split_shmem_offset_ptr(offset) ((void *)(((uint8_t *)split_shmem) + (offset))) +#define split_trans_initiator2target_buffer(trans) (split_shmem_offset_ptr((trans)->initiator2target_offset)) +#define split_trans_target2initiator_buffer(trans) (split_shmem_offset_ptr((trans)->target2initiator_offset)) + +// returns false if valid data not received from slave +bool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); +void transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); + +void transaction_register_rpc(int8_t transaction_id, slave_callback_t callback); + +bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer); + +#define transaction_rpc_send(transaction_id, initiator2target_buffer_size, initiator2target_buffer) transaction_rpc_exec(transaction_id, initiator2target_buffer_size, initiator2target_buffer, 0, NULL) +#define transaction_rpc_recv(transaction_id, target2initiator_buffer_size, target2initiator_buffer) transaction_rpc_exec(transaction_id, 0, NULL, target2initiator_buffer_size, target2initiator_buffer) diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index 9ed0f7591b..a711ef85f0 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c @@ -1,452 +1,118 @@ -#include -#include - -#include "config.h" -#include "matrix.h" -#include "quantum.h" - -#define ROWS_PER_HAND (MATRIX_ROWS / 2) -#define SYNC_TIMER_OFFSET 2 - -#ifdef RGBLIGHT_ENABLE -# include "rgblight.h" -#endif - -#ifdef BACKLIGHT_ENABLE -# include "backlight.h" -#endif - -#ifdef ENCODER_ENABLE -# include "encoder.h" -static pin_t encoders_pad[] = ENCODERS_PAD_A; -# define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t)) -#endif - -#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) -# include "led_matrix.h" -#endif -#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) -# include "rgb_matrix.h" -#endif - -#if defined(USE_I2C) +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -# include "i2c_master.h" -# include "i2c_slave.h" - -typedef struct _I2C_slave_buffer_t { -# ifndef DISABLE_SYNC_TIMER - uint32_t sync_timer; -# endif -# ifdef SPLIT_TRANSPORT_MIRROR - matrix_row_t mmatrix[ROWS_PER_HAND]; -# endif - matrix_row_t smatrix[ROWS_PER_HAND]; -# ifdef SPLIT_MODS_ENABLE - uint8_t real_mods; - uint8_t weak_mods; -# ifndef NO_ACTION_ONESHOT - uint8_t oneshot_mods; -# endif -# endif -# ifdef BACKLIGHT_ENABLE - uint8_t backlight_level; -# endif -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - rgblight_syncinfo_t rgblight_sync; -# endif -# ifdef ENCODER_ENABLE - uint8_t encoder_state[NUMBER_OF_ENCODERS]; -# endif -# ifdef WPM_ENABLE - uint8_t current_wpm; -# endif -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - led_eeconfig_t led_matrix; - bool led_suspend_state; -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - rgb_config_t rgb_matrix; - bool rgb_suspend_state; -# endif -} I2C_slave_buffer_t; +#include +#include -static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; +#include "transactions.h" +#include "transport.h" +#include "transaction_id_define.h" +#include "atomic_util.h" -# define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer) -# define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix) -# define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix) -# define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods) -# define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods) -# define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods) -# define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level) -# define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync) -# define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state) -# define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm) -# define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix) -# define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state) -# define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix) -# define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state) +#ifdef USE_I2C -# define TIMEOUT 100 +# ifndef SLAVE_I2C_TIMEOUT +# define SLAVE_I2C_TIMEOUT 100 +# endif // SLAVE_I2C_TIMEOUT # ifndef SLAVE_I2C_ADDRESS # define SLAVE_I2C_ADDRESS 0x32 # endif -// Get rows from other half over i2c -bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { - i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT); -# ifdef SPLIT_TRANSPORT_MIRROR - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT); -# endif +# include "i2c_master.h" +# include "i2c_slave.h" - // write backlight info -# ifdef BACKLIGHT_ENABLE - uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0; - if (level != i2c_buffer->backlight_level) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) { - i2c_buffer->backlight_level = level; - } - } -# endif +// Ensure the I2C buffer has enough space +_Static_assert(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT"); -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - if (rgblight_get_change_flags()) { - rgblight_syncinfo_t rgblight_sync; - rgblight_get_syncinfo(&rgblight_sync); - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) { - rgblight_clear_change_flags(); - } - } -# endif +split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg; -# ifdef ENCODER_ENABLE - i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT); - encoder_update_raw(i2c_buffer->encoder_state); -# endif +void transport_master_init(void) { i2c_init(); } +void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); } -# ifdef WPM_ENABLE - uint8_t current_wpm = get_current_wpm(); - if (current_wpm != i2c_buffer->current_wpm) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)¤t_wpm, sizeof(current_wpm), TIMEOUT) >= 0) { - i2c_buffer->current_wpm = current_wpm; - } +i2c_status_t transport_trigger_callback(int8_t id) { + // If there's no callback, indicate that we were successful + if (!split_transaction_table[id].slave_callback) { + return I2C_STATUS_SUCCESS; } -# endif -# ifdef SPLIT_MODS_ENABLE - uint8_t real_mods = get_mods(); - if (real_mods != i2c_buffer->real_mods) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) { - i2c_buffer->real_mods = real_mods; + // Kick off the "callback executor", now that data has been written to the slave + split_shmem->transaction_id = id; + split_transaction_desc_t *trans = &split_transaction_table[I2C_EXECUTE_CALLBACK]; + return i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, SLAVE_I2C_TIMEOUT); +} + +bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) { + i2c_status_t status; + split_transaction_desc_t *trans = &split_transaction_table[id]; + if (initiator2target_length > 0) { + size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length; + memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len); + if ((status = i2c_writeReg(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) { + return false; } } - uint8_t weak_mods = get_weak_mods(); - if (weak_mods != i2c_buffer->weak_mods) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) { - i2c_buffer->weak_mods = weak_mods; - } + // If we need to execute a callback on the slave, do so + if ((status = transport_trigger_callback(id)) < 0) { + return false; } -# ifndef NO_ACTION_ONESHOT - uint8_t oneshot_mods = get_oneshot_mods(); - if (oneshot_mods != i2c_buffer->oneshot_mods) { - if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) { - i2c_buffer->oneshot_mods = oneshot_mods; + if (target2initiator_length > 0) { + size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length; + if ((status = i2c_readReg(SLAVE_I2C_ADDRESS, trans->target2initiator_offset, split_trans_target2initiator_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) { + return false; } + memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len); } -# endif -# endif -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT); - bool suspend_state = led_matrix_get_suspend_state(); - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT); -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT); - bool suspend_state = rgb_matrix_get_suspend_state(); - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT); -# endif - -# ifndef DISABLE_SYNC_TIMER - i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; - i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT); -# endif return true; } -void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { -# ifndef DISABLE_SYNC_TIMER - sync_timer_update(i2c_buffer->sync_timer); -# endif - // Copy matrix to I2C buffer - memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix)); -# ifdef SPLIT_TRANSPORT_MIRROR - memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix)); -# endif - -// Read Backlight Info -# ifdef BACKLIGHT_ENABLE - backlight_set(i2c_buffer->backlight_level); -# endif - -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - // Update the RGB with the new data - if (i2c_buffer->rgblight_sync.status.change_flags != 0) { - rgblight_update_sync(&i2c_buffer->rgblight_sync, false); - i2c_buffer->rgblight_sync.status.change_flags = 0; - } -# endif - -# ifdef ENCODER_ENABLE - encoder_state_raw(i2c_buffer->encoder_state); -# endif - -# ifdef WPM_ENABLE - set_current_wpm(i2c_buffer->current_wpm); -# endif - -# ifdef SPLIT_MODS_ENABLE - set_mods(i2c_buffer->real_mods); - set_weak_mods(i2c_buffer->weak_mods); -# ifndef NO_ACTION_ONESHOT - set_oneshot_mods(i2c_buffer->oneshot_mods); -# endif -# endif - -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix)); - led_matrix_set_suspend_state(i2c_buffer->led_suspend_state); -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix)); - rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state); -# endif -} - -void transport_master_init(void) { i2c_init(); } - -void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); } - -#else // USE_SERIAL +#else // USE_I2C # include "serial.h" -typedef struct _Serial_s2m_buffer_t { - // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack - matrix_row_t smatrix[ROWS_PER_HAND]; - -# ifdef ENCODER_ENABLE - uint8_t encoder_state[NUMBER_OF_ENCODERS]; -# endif - -} Serial_s2m_buffer_t; - -typedef struct _Serial_m2s_buffer_t { -# ifdef SPLIT_MODS_ENABLE - uint8_t real_mods; - uint8_t weak_mods; -# ifndef NO_ACTION_ONESHOT - uint8_t oneshot_mods; -# endif -# endif -# ifndef DISABLE_SYNC_TIMER - uint32_t sync_timer; -# endif -# ifdef SPLIT_TRANSPORT_MIRROR - matrix_row_t mmatrix[ROWS_PER_HAND]; -# endif -# ifdef BACKLIGHT_ENABLE - uint8_t backlight_level; -# endif -# ifdef WPM_ENABLE - uint8_t current_wpm; -# endif -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - led_eeconfig_t led_matrix; - bool led_suspend_state; -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - rgb_config_t rgb_matrix; - bool rgb_suspend_state; -# endif -} Serial_m2s_buffer_t; - -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) -// When MCUs on both sides drive their respective RGB LED chains, -// it is necessary to synchronize, so it is necessary to communicate RGB -// information. In that case, define RGBLIGHT_SPLIT with info on the number -// of LEDs on each half. -// -// Otherwise, if the master side MCU drives both sides RGB LED chains, -// there is no need to communicate. - -typedef struct _Serial_rgblight_t { - rgblight_syncinfo_t rgblight_sync; -} Serial_rgblight_t; +static split_shared_memory_t shared_memory; +split_shared_memory_t *const split_shmem = &shared_memory; -volatile Serial_rgblight_t serial_rgblight = {}; -uint8_t volatile status_rgblight = 0; -# endif - -volatile Serial_s2m_buffer_t serial_s2m_buffer = {}; -volatile Serial_m2s_buffer_t serial_m2s_buffer = {}; -uint8_t volatile status0 = 0; - -enum serial_transaction_id { - GET_SLAVE_MATRIX = 0, -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - PUT_RGBLIGHT, -# endif -}; - -SSTD_t transactions[] = { - [GET_SLAVE_MATRIX] = - { - (uint8_t *)&status0, - sizeof(serial_m2s_buffer), - (uint8_t *)&serial_m2s_buffer, - sizeof(serial_s2m_buffer), - (uint8_t *)&serial_s2m_buffer, - }, -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - [PUT_RGBLIGHT] = - { - (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL // no slave to master transfer - }, -# endif -}; - -void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); } - -void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); } +void transport_master_init(void) { soft_serial_initiator_init(); } +void transport_slave_init(void) { soft_serial_target_init(); } -# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) - -// rgblight synchronization information communication. - -void transport_rgblight_master(void) { - if (rgblight_get_change_flags()) { - rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync); - if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) { - rgblight_clear_change_flags(); - } - } -} - -void transport_rgblight_slave(void) { - if (status_rgblight == TRANSACTION_ACCEPTED) { - rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false); - status_rgblight = TRANSACTION_END; +bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) { + split_transaction_desc_t *trans = &split_transaction_table[id]; + if (initiator2target_length > 0) { + size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length; + memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len); } -} -# else -# define transport_rgblight_master() -# define transport_rgblight_slave() -# endif - -bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { -# ifndef SERIAL_USE_MULTI_TRANSACTION - if (soft_serial_transaction() != TRANSACTION_END) { - return false; - } -# else - transport_rgblight_master(); - if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) { + if (soft_serial_transaction(id) != TRANSACTION_END) { return false; } -# endif - // TODO: if MATRIX_COLS > 8 change to unpack() - for (int i = 0; i < ROWS_PER_HAND; ++i) { - slave_matrix[i] = serial_s2m_buffer.smatrix[i]; -# ifdef SPLIT_TRANSPORT_MIRROR - serial_m2s_buffer.mmatrix[i] = master_matrix[i]; -# endif + if (target2initiator_length > 0) { + size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length; + memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len); } -# ifdef BACKLIGHT_ENABLE - // Write backlight level for slave to read - serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0; -# endif - -# ifdef ENCODER_ENABLE - encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state); -# endif - -# ifdef WPM_ENABLE - // Write wpm to slave - serial_m2s_buffer.current_wpm = get_current_wpm(); -# endif - -# ifdef SPLIT_MODS_ENABLE - serial_m2s_buffer.real_mods = get_mods(); - serial_m2s_buffer.weak_mods = get_weak_mods(); -# ifndef NO_ACTION_ONESHOT - serial_m2s_buffer.oneshot_mods = get_oneshot_mods(); -# endif -# endif - -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - serial_m2s_buffer.led_matrix = led_matrix_eeconfig; - serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state(); -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - serial_m2s_buffer.rgb_matrix = rgb_matrix_config; - serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state(); -# endif - -# ifndef DISABLE_SYNC_TIMER - serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; -# endif return true; } -void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { - transport_rgblight_slave(); -# ifndef DISABLE_SYNC_TIMER - sync_timer_update(serial_m2s_buffer.sync_timer); -# endif - - // TODO: if MATRIX_COLS > 8 change to pack() - for (int i = 0; i < ROWS_PER_HAND; ++i) { - serial_s2m_buffer.smatrix[i] = slave_matrix[i]; -# ifdef SPLIT_TRANSPORT_MIRROR - master_matrix[i] = serial_m2s_buffer.mmatrix[i]; -# endif - } -# ifdef BACKLIGHT_ENABLE - backlight_set(serial_m2s_buffer.backlight_level); -# endif - -# ifdef ENCODER_ENABLE - encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state); -# endif +#endif // USE_I2C -# ifdef WPM_ENABLE - set_current_wpm(serial_m2s_buffer.current_wpm); -# endif - -# ifdef SPLIT_MODS_ENABLE - set_mods(serial_m2s_buffer.real_mods); - set_weak_mods(serial_m2s_buffer.weak_mods); -# ifndef NO_ACTION_ONESHOT - set_oneshot_mods(serial_m2s_buffer.oneshot_mods); -# endif -# endif - -# if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) - led_matrix_eeconfig = serial_m2s_buffer.led_matrix; - led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state); -# endif -# if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) - rgb_matrix_config = serial_m2s_buffer.rgb_matrix; - rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state); -# endif -} +bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { return transactions_master(master_matrix, slave_matrix); } -#endif +void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { transactions_slave(master_matrix, slave_matrix); } \ No newline at end of file diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h index a9f66301bf..2e07f6b25c 100644 --- a/quantum/split_common/transport.h +++ b/quantum/split_common/transport.h @@ -1,10 +1,175 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once +#include "stdint.h" +#include "stdbool.h" + +#include "progmem.h" +#include "action_layer.h" #include "matrix.h" +#ifndef RPC_M2S_BUFFER_SIZE +# define RPC_M2S_BUFFER_SIZE 32 +#endif // RPC_M2S_BUFFER_SIZE + +#ifndef RPC_S2M_BUFFER_SIZE +# define RPC_S2M_BUFFER_SIZE 32 +#endif // RPC_S2M_BUFFER_SIZE + void transport_master_init(void); void transport_slave_init(void); // returns false if valid data not received from slave bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]); + +bool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length); + +#ifdef ENCODER_ENABLE +# include "encoder.h" +# define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t)) +#endif // ENCODER_ENABLE + +#ifdef BACKLIGHT_ENABLE +# include "backlight.h" +#endif // BACKLIGHT_ENABLE + +#ifdef RGBLIGHT_ENABLE +# include "rgblight.h" +#endif // RGBLIGHT_ENABLE + +typedef struct _split_slave_matrix_sync_t { + uint8_t checksum; + matrix_row_t matrix[(MATRIX_ROWS) / 2]; +} split_slave_matrix_sync_t; + +#ifdef SPLIT_TRANSPORT_MIRROR +typedef struct _split_master_matrix_sync_t { + matrix_row_t matrix[(MATRIX_ROWS) / 2]; +} split_master_matrix_sync_t; +#endif // SPLIT_TRANSPORT_MIRROR + +#ifdef ENCODER_ENABLE +typedef struct _split_slave_encoder_sync_t { + uint8_t checksum; + uint8_t state[NUMBER_OF_ENCODERS]; +} split_slave_encoder_sync_t; +#endif // ENCODER_ENABLE + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) +typedef struct _split_layers_sync_t { + layer_state_t layer_state; + layer_state_t default_layer_state; +} split_layers_sync_t; +#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) +# include "led_matrix.h" + +typedef struct _led_matrix_sync_t { + led_eeconfig_t led_matrix; + bool led_suspend_state; +} led_matrix_sync_t; +#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) +# include "rgb_matrix.h" + +typedef struct _rgb_matrix_sync_t { + rgb_config_t rgb_matrix; + bool rgb_suspend_state; +} rgb_matrix_sync_t; +#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +#ifdef SPLIT_MODS_ENABLE +typedef struct _split_mods_sync_t { + uint8_t real_mods; + uint8_t weak_mods; +# ifndef NO_ACTION_ONESHOT + uint8_t oneshot_mods; +# endif // NO_ACTION_ONESHOT +} split_mods_sync_t; +#endif // SPLIT_MODS_ENABLE + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +typedef struct _rpc_sync_info_t { + int8_t transaction_id; + uint8_t m2s_length; + uint8_t s2m_length; +} rpc_sync_info_t; +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + +typedef struct _split_shared_memory_t { +#ifdef USE_I2C + int8_t transaction_id; +#endif // USE_I2C + + split_slave_matrix_sync_t smatrix; + +#ifdef SPLIT_TRANSPORT_MIRROR + split_master_matrix_sync_t mmatrix; +#endif // SPLIT_TRANSPORT_MIRROR + +#ifdef ENCODER_ENABLE + split_slave_encoder_sync_t encoders; +#endif // ENCODER_ENABLE + +#ifndef DISABLE_SYNC_TIMER + uint32_t sync_timer; +#endif // DISABLE_SYNC_TIMER + +#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + split_layers_sync_t layers; +#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE) + +#ifdef SPLIT_LED_STATE_ENABLE + uint8_t led_state; +#endif // SPLIT_LED_STATE_ENABLE + +#ifdef SPLIT_MODS_ENABLE + split_mods_sync_t mods; +#endif // SPLIT_MODS_ENABLE + +#ifdef BACKLIGHT_ENABLE + uint8_t backlight_level; +#endif // BACKLIGHT_ENABLE + +#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + rgblight_syncinfo_t rgblight_sync; +#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) + +#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + led_matrix_sync_t led_matrix_sync; +#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT) + +#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + rgb_matrix_sync_t rgb_matrix_sync; +#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT) + +#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + uint8_t current_wpm; +#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE) + +#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) + rpc_sync_info_t rpc_info; + uint8_t rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE]; + uint8_t rpc_s2m_buffer[RPC_S2M_BUFFER_SIZE]; +#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER) +} split_shared_memory_t; + +extern split_shared_memory_t *const split_shmem; \ No newline at end of file diff --git a/tmk_core/common/host.c b/tmk_core/common/host.c index e7d92cfac6..a8b391e893 100644 --- a/tmk_core/common/host.c +++ b/tmk_core/common/host.c @@ -17,6 +17,7 @@ along with this program. If not, see . #include //#include +#include "keyboard.h" #include "keycode.h" #include "host.h" #include "util.h" @@ -35,15 +36,20 @@ void host_set_driver(host_driver_t *d) { driver = d; } host_driver_t *host_get_driver(void) { return driver; } +#ifdef SPLIT_KEYBOARD +uint8_t split_led_state = 0; +void set_split_host_keyboard_leds(uint8_t led_state) { split_led_state = led_state; } +#endif + uint8_t host_keyboard_leds(void) { +#ifdef SPLIT_KEYBOARD + if (!is_keyboard_master()) return split_led_state; +#endif if (!driver) return 0; return (*driver->keyboard_leds)(); } -led_t host_keyboard_led_state(void) { - if (!driver) return (led_t){0}; - return (led_t)((*driver->keyboard_leds)()); -} +led_t host_keyboard_led_state(void) { return (led_t)host_keyboard_leds(); } /* send report */ void host_keyboard_send(report_keyboard_t *report) { -- cgit v1.2.3 From 8d9f527081d3a5fc8a0286e47022a70df8eb9111 Mon Sep 17 00:00:00 2001 From: Joakim Tufvegren Date: Sun, 20 Jun 2021 04:18:00 +0200 Subject: Add sync_timer support over serial_link (i.e. Ergodox Infinity) (#12845) --- quantum/serial_link/system/serial_link.c | 22 ++++++++++++++++++++++ tmk_core/common/sync_timer.c | 2 +- tmk_core/common/sync_timer.h | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) (limited to 'tmk_core/common') diff --git a/quantum/serial_link/system/serial_link.c b/quantum/serial_link/system/serial_link.c index f77483ad8c..6363f8ff3b 100644 --- a/quantum/serial_link/system/serial_link.c +++ b/quantum/serial_link/system/serial_link.c @@ -29,10 +29,13 @@ SOFTWARE. #include "serial_link/protocol/transport.h" #include "serial_link/protocol/frame_router.h" #include "matrix.h" +#include "sync_timer.h" #include #include "print.h" #include "config.h" +#define SYNC_TIMER_OFFSET 2 + static event_source_t new_data_event; static bool serial_link_connected; static bool is_master = false; @@ -159,10 +162,16 @@ static matrix_object_t last_matrix = {}; SLAVE_TO_MASTER_OBJECT(keyboard_matrix, matrix_object_t); MASTER_TO_ALL_SLAVES_OBJECT(serial_link_connected, bool); +#ifndef DISABLE_SYNC_TIMER +MASTER_TO_ALL_SLAVES_OBJECT(sync_timer, uint32_t); +#endif static remote_object_t* remote_objects[] = { REMOTE_OBJECT(serial_link_connected), REMOTE_OBJECT(keyboard_matrix), +#ifndef DISABLE_SYNC_TIMER + REMOTE_OBJECT(sync_timer), +#endif }; void init_serial_link(void) { @@ -200,14 +209,27 @@ void serial_link_update(void) { m->rows[i] = matrix.rows[i]; } end_write_keyboard_matrix(); + *begin_write_serial_link_connected() = true; end_write_serial_link_connected(); + +#ifndef DISABLE_SYNC_TIMER + *begin_write_sync_timer() = sync_timer_read32() + SYNC_TIMER_OFFSET; + end_write_sync_timer(); +#endif } matrix_object_t* m = read_keyboard_matrix(0); if (m) { matrix_set_remote(m->rows, 0); } + +#ifndef DISABLE_SYNC_TIMER + uint32_t* t = read_sync_timer(); + if (t) { + sync_timer_update(*t); + } +#endif } void signal_data_written(void) { chEvtBroadcast(&new_data_event); } diff --git a/tmk_core/common/sync_timer.c b/tmk_core/common/sync_timer.c index de24b463b6..68b92d8b43 100644 --- a/tmk_core/common/sync_timer.c +++ b/tmk_core/common/sync_timer.c @@ -26,7 +26,7 @@ SOFTWARE. #include "sync_timer.h" #include "keyboard.h" -#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER) +#if (defined(SPLIT_KEYBOARD) || defined(SERIAL_LINK_ENABLE)) && !defined(DISABLE_SYNC_TIMER) volatile int32_t sync_timer_ms; void sync_timer_init(void) { sync_timer_ms = 0; } diff --git a/tmk_core/common/sync_timer.h b/tmk_core/common/sync_timer.h index 9ddef45bb2..744e2b50d5 100644 --- a/tmk_core/common/sync_timer.h +++ b/tmk_core/common/sync_timer.h @@ -32,7 +32,7 @@ SOFTWARE. extern "C" { #endif -#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER) +#if (defined(SPLIT_KEYBOARD) || defined(SERIAL_LINK_ENABLE)) && !defined(DISABLE_SYNC_TIMER) void sync_timer_init(void); void sync_timer_update(uint32_t time); uint16_t sync_timer_read(void); -- cgit v1.2.3 From 8da8aabbe5796232c0f17f849badd455d42b0277 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Sat, 3 Jul 2021 00:19:52 -0700 Subject: Improve layer mask handling (#13065) --- quantum/command.c | 2 +- quantum/quantum.c | 8 ++++---- tmk_core/common/action_layer.c | 12 ++++++------ tmk_core/common/action_layer.h | 29 +++++++++++++++++++++++++++-- 4 files changed, 38 insertions(+), 13 deletions(-) (limited to 'tmk_core/common') diff --git a/quantum/command.c b/quantum/command.c index 34c4b36b1c..3a7dc0f8ca 100644 --- a/quantum/command.c +++ b/quantum/command.c @@ -781,6 +781,6 @@ uint8_t numkey2num(uint8_t code) { static void switch_default_layer(uint8_t layer) { xprintf("L%d\n", layer); - default_layer_set(1UL << layer); + default_layer_set((layer_state_t)1 << layer); clear_keyboard(); } diff --git a/quantum/quantum.c b/quantum/quantum.c index 8ccdb774bd..b4cfa28d7d 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -340,13 +340,13 @@ void set_single_persistent_default_layer(uint8_t default_layer) { #if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS) PLAY_SONG(default_layer_songs[default_layer]); #endif - eeconfig_update_default_layer(1U << default_layer); - default_layer_set(1U << default_layer); + eeconfig_update_default_layer((layer_state_t)1 << default_layer); + default_layer_set((layer_state_t)1 << default_layer); } layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3) { - layer_state_t mask12 = (1UL << layer1) | (1UL << layer2); - layer_state_t mask3 = 1UL << layer3; + layer_state_t mask12 = ((layer_state_t)1 << layer1) | ((layer_state_t)1 << layer2); + layer_state_t mask3 = (layer_state_t)1 << layer3; return (state & mask12) == mask12 ? (state | mask3) : (state & ~mask3); } diff --git a/tmk_core/common/action_layer.c b/tmk_core/common/action_layer.c index af2d7d964b..ed1a4bd20d 100644 --- a/tmk_core/common/action_layer.c +++ b/tmk_core/common/action_layer.c @@ -131,32 +131,32 @@ bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) { if (!cmp_layer_state) { return layer == 0; } - return (cmp_layer_state & (1UL << layer)) != 0; + return (cmp_layer_state & ((layer_state_t)1 << layer)) != 0; } /** \brief Layer move * * Turns on the given layer and turn off all other layers */ -void layer_move(uint8_t layer) { layer_state_set(1UL << layer); } +void layer_move(uint8_t layer) { layer_state_set((layer_state_t)1 << layer); } /** \brief Layer on * * Turns on given layer */ -void layer_on(uint8_t layer) { layer_state_set(layer_state | (1UL << layer)); } +void layer_on(uint8_t layer) { layer_state_set(layer_state | ((layer_state_t)1 << layer)); } /** \brief Layer off * * Turns off given layer */ -void layer_off(uint8_t layer) { layer_state_set(layer_state & ~(1UL << layer)); } +void layer_off(uint8_t layer) { layer_state_set(layer_state & ~((layer_state_t)1 << layer)); } /** \brief Layer invert * * Toggle the given layer (set it if it's unset, or unset it if it's set) */ -void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ (1UL << layer)); } +void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ ((layer_state_t)1 << layer)); } /** \brief Layer or * @@ -258,7 +258,7 @@ uint8_t layer_switch_get_layer(keypos_t key) { layer_state_t layers = layer_state | default_layer_state; /* check top layer first */ for (int8_t i = MAX_LAYER - 1; i >= 0; i--) { - if (layers & (1UL << i)) { + if (layers & ((layer_state_t)1 << i)) { action = action_for_key(i, key); if (action.code != ACTION_TRANSPARENT) { return i; diff --git a/tmk_core/common/action_layer.h b/tmk_core/common/action_layer.h index d72cd3e3a5..b87d096eed 100644 --- a/tmk_core/common/action_layer.h +++ b/tmk_core/common/action_layer.h @@ -21,6 +21,29 @@ along with this program. If not, see . #include "keyboard.h" #include "action.h" +#ifdef DYNAMIC_KEYMAP_ENABLE +# ifndef DYNAMIC_KEYMAP_LAYER_COUNT +# define DYNAMIC_KEYMAP_LAYER_COUNT 4 +# endif +# if DYNAMIC_KEYMAP_LAYER_COUNT <= 8 +# ifndef LAYER_STATE_8BIT +# define LAYER_STATE_8BIT +# endif +# elif DYNAMIC_KEYMAP_LAYER_COUNT <= 16 +# ifndef LAYER_STATE_16BIT +# define LAYER_STATE_16BIT +# endif +# else +# ifndef LAYER_STATE_32BIT +# define LAYER_STATE_32BIT +# endif +# endif +#endif + +#if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT) +# define LAYER_STATE_32BIT +#endif + #if defined(LAYER_STATE_8BIT) typedef uint8_t layer_state_t; # define MAX_LAYER_BITS 3 @@ -35,13 +58,15 @@ typedef uint16_t layer_state_t; # define MAX_LAYER 16 # endif # define get_highest_layer(state) biton16(state) -#else +#elif defined(LAYER_STATE_32BIT) typedef uint32_t layer_state_t; # define MAX_LAYER_BITS 5 # ifndef MAX_LAYER # define MAX_LAYER 32 # endif # define get_highest_layer(state) biton32(state) +#else +# error Layer Mask size not specified. HOW?! #endif /* @@ -92,7 +117,7 @@ layer_state_t layer_state_set_kb(layer_state_t state); # define layer_state_set(layer) # define layer_state_is(layer) (layer == 0) -# define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & 1UL << layer) != 0) +# define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & (layer_state_t)1 << layer) != 0) # define layer_debug() # define layer_clear() -- cgit v1.2.3 From a535d24ecfd465124cdb255e53b5cc301cbda6d9 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 6 Jul 2021 01:24:48 +0200 Subject: core: chibios: bootloader: use integer pointers as volatile (#13450) This prevents gcc from incorrectly trying to validate array bounds. ``` tmk_core/common/chibios/bootloader.c: error: '__builtin_memcpy' offset [0, 21] is out of the bounds [0, 0] [-Werror=array-bounds] 107 | __builtin_memcpy((void *) VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99578#c16 Fixes #12925 --- tmk_core/common/chibios/bootloader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tmk_core/common') diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index 76d52ea608..f9514ee5f3 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -103,7 +103,8 @@ void enter_bootloader_mode_if_requested(void) { # define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000 const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff"; __attribute__((weak)) void bootloader_jump(void) { - __builtin_memcpy((void *)VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); + void *volatile vbat = (void *)VBAT; + __builtin_memcpy(vbat, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); // request reset SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk; } -- cgit v1.2.3 From 9c74fd14bc9777ec45cc93fa57bf83ab17b988db Mon Sep 17 00:00:00 2001 From: Chris Cullin Date: Tue, 13 Jul 2021 01:51:23 +1000 Subject: Enable g_is31_leds PROGMEM for RGB Matrix IS31FL3737 driver (#13480) --- docs/feature_rgb_matrix.md | 2 +- drivers/issi/is31fl3737.c | 9 +++++++-- keyboards/mt84/mt84.c | 40 ++++++++++++++++++++-------------------- keyboards/planck/ez/ez.c | 2 +- tmk_core/common/progmem.h | 1 + 5 files changed, 30 insertions(+), 24 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md index 675b7a1be0..be763ee025 100644 --- a/docs/feature_rgb_matrix.md +++ b/docs/feature_rgb_matrix.md @@ -171,7 +171,7 @@ Currently only a single drivers is supported, but it would be trivial to support Define these arrays listing all the LEDs in your `.c`: ```c -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/drivers/issi/is31fl3737.c b/drivers/issi/is31fl3737.c index 8647c93cc1..e40bfa0d79 100644 --- a/drivers/issi/is31fl3737.c +++ b/drivers/issi/is31fl3737.c @@ -19,6 +19,7 @@ #include "is31fl3737.h" #include "i2c_master.h" #include "wait.h" +#include "progmem.h" // This is a 7-bit address, that gets left-shifted and bit 0 // set to 0 for write, 1 for read (as per I2C protocol) @@ -153,7 +154,9 @@ void IS31FL3737_init(uint8_t addr) { void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { if (index >= 0 && index < DRIVER_LED_TOTAL) { - is31_led led = g_is31_leds[index]; + // copy the led config from progmem to SRAM + is31_led led; + memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); g_pwm_buffer[led.driver][led.r] = red; g_pwm_buffer[led.driver][led.g] = green; @@ -169,7 +172,9 @@ void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { } void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { - is31_led led = g_is31_leds[index]; + // copy the led config from progmem to SRAM + is31_led led; + memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); uint8_t control_register_r = led.r / 8; uint8_t control_register_g = led.g / 8; diff --git a/keyboards/mt84/mt84.c b/keyboards/mt84/mt84.c index d9f509e14d..e15a1ff951 100644 --- a/keyboards/mt84/mt84.c +++ b/keyboards/mt84/mt84.c @@ -1,22 +1,22 @@ - /* Copyright 2020 MT<704340378@qq.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ + /* Copyright 2020 MT<704340378@qq.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "mt84.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -40,7 +40,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { {1, D_12, E_12, F_12}, {1, G_12, H_12, I_12}, {1, J_12, K_12, L_12}, - + {0, A_1, B_1, C_1}, {0, D_1, E_1, F_1}, {0, G_1, H_1, I_1}, @@ -72,7 +72,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { {1, A_7, B_7, C_7}, {1, D_7, E_7, F_7}, {1, G_7, H_7, I_7}, - + {0, A_3, B_3, C_3}, {0, D_3, E_3, F_3}, {0, G_3, H_3, I_3}, @@ -87,7 +87,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { {1, J_3, K_3, L_3}, {1, A_8, B_8, C_8}, {1, G_8, H_8, I_8}, - + {0, A_4, B_4, C_4}, {0, D_4, E_4, F_4}, {0, G_4, H_4, I_4}, @@ -130,7 +130,7 @@ led_config_t g_led_config = {{ { 9, 52 }, { 27, 52 }, { 43, 52 }, { 59, 52 }, { 75, 52 }, { 91, 52 }, { 107, 52 }, { 123, 52 }, { 139, 52 }, { 155, 52 }, { 171, 52 }, { 187, 52 }, { 212, 52 }, { 224, 52 }, { 2, 64 }, { 18, 64 }, { 33, 64 }, { 93, 64 }, { 150, 64 }, { 165, 64 }, { 180, 64 }, { 195, 64 }, { 210, 64 }, { 224, 64 } }, { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, diff --git a/keyboards/planck/ez/ez.c b/keyboards/planck/ez/ez.c index 8c498a5520..818dc66587 100644 --- a/keyboards/planck/ez/ez.c +++ b/keyboards/planck/ez/ez.c @@ -19,7 +19,7 @@ keyboard_config_t keyboard_config; #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/tmk_core/common/progmem.h b/tmk_core/common/progmem.h index 4e4771e523..3a7a169682 100644 --- a/tmk_core/common/progmem.h +++ b/tmk_core/common/progmem.h @@ -3,6 +3,7 @@ #if defined(__AVR__) # include #else +# include # define PROGMEM # define PSTR(x) x # define PGM_P const char* -- cgit v1.2.3 From 52cfc9259b58a3a11a244fbe35c49c7dd1a9cae0 Mon Sep 17 00:00:00 2001 From: Jonas Gessner Date: Tue, 13 Jul 2021 19:13:51 +0200 Subject: [Feature] Key Overrides (#11422) --- common_features.mk | 5 + docs/_summary.md | 1 + docs/config_options.md | 4 + docs/feature_key_overrides.md | 229 +++++++++ docs/syllabus.md | 1 + docs/understanding_qmk.md | 1 + quantum/process_keycode/process_key_override.c | 518 +++++++++++++++++++++ quantum/process_keycode/process_key_override.h | 147 ++++++ .../process_keycode/process_key_override_private.h | 24 + quantum/quantum.c | 19 +- quantum/quantum.h | 4 + quantum/quantum_keycodes.h | 5 + show_options.mk | 1 + tmk_core/common/action_util.c | 28 ++ 14 files changed, 984 insertions(+), 3 deletions(-) create mode 100644 docs/feature_key_overrides.md create mode 100644 quantum/process_keycode/process_key_override.c create mode 100644 quantum/process_keycode/process_key_override.h create mode 100644 quantum/process_keycode/process_key_override_private.h (limited to 'tmk_core/common') diff --git a/common_features.mk b/common_features.mk index 8080113efd..74b94ecd77 100644 --- a/common_features.mk +++ b/common_features.mk @@ -335,6 +335,11 @@ ifeq ($(strip $(PRINTING_ENABLE)), yes) SRC += $(TMK_DIR)/protocol/serial_uart.c endif +ifeq ($(strip $(KEY_OVERRIDE_ENABLE)), yes) + OPT_DEFS += -DKEY_OVERRIDE_ENABLE + SRC += $(QUANTUM_DIR)/process_keycode/process_key_override.c +endif + ifeq ($(strip $(SERIAL_LINK_ENABLE)), yes) SERIAL_SRC := $(wildcard $(SERIAL_PATH)/protocol/*.c) SERIAL_SRC += $(wildcard $(SERIAL_PATH)/system/*.c) diff --git a/docs/_summary.md b/docs/_summary.md index 4141e01e77..6c39aeda09 100644 --- a/docs/_summary.md +++ b/docs/_summary.md @@ -77,6 +77,7 @@ * [Combos](feature_combo.md) * [Debounce API](feature_debounce_type.md) * [Key Lock](feature_key_lock.md) + * [Key Overrides](feature_key_overrides.md) * [Layers](feature_layers.md) * [One Shot Keys](one_shot_keys.md) * [Pointing Device](feature_pointing_device.md) diff --git a/docs/config_options.md b/docs/config_options.md index 980195ac68..0c98b31010 100644 --- a/docs/config_options.md +++ b/docs/config_options.md @@ -195,6 +195,8 @@ If you define these options you will enable the associated feature, which may in * Sets the delay between `register_code` and `unregister_code`, if you're having issues with it registering properly (common on VUSB boards). The value is in milliseconds. * `#define TAP_HOLD_CAPS_DELAY 80` * Sets the delay for Tap Hold keys (`LT`, `MT`) when using `KC_CAPSLOCK` keycode, as this has some special handling on MacOS. The value is in milliseconds, and defaults to 80 ms if not defined. For macOS, you may want to set this to 200 or higher. +* `#define KEY_OVERRIDE_REPEAT_DELAY 500` + * Sets the key repeat interval for [key overrides](feature_key_overrides.md). ## RGB Light Configuration @@ -400,6 +402,8 @@ Use these to enable or disable building certain features. The more you have enab * USB N-Key Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work * `AUDIO_ENABLE` * Enable the audio subsystem. +* `KEY_OVERRIDE_ENABLE` + * Enable the key override feature * `RGBLIGHT_ENABLE` * Enable keyboard underlight functionality * `LEADER_ENABLE` diff --git a/docs/feature_key_overrides.md b/docs/feature_key_overrides.md new file mode 100644 index 0000000000..861c4bef5d --- /dev/null +++ b/docs/feature_key_overrides.md @@ -0,0 +1,229 @@ +# Key Overrides + +Key overrides allow you to override modifier-key combinations to send a different modifier-key combination or perform completely custom actions. Don't want `shift` + `1` to type `!` on your computer? Use a key override to make your keyboard type something different when you press `shift` + `1`. The general behavior is like this: If `modifiers w` + `key x` are pressed, replace these keys with `modifiers y` + `key z` in the keyboard report. + +You can use key overrides in a similar way to momentary layer/fn keys to activate custom keycodes/shortcuts, with a number of benefits: You completely keep the original use of the modifier keys, while being able to save space by removing fn keys from your keyboard. You can also easily configure _combinations of modifiers_ to trigger different actions than individual modifiers, and much more. The possibilities are quite vast and this documentation contains a few examples for inspiration throughout. + +##### A few more examples to get started: You could use key overrides to... +- Send `brightness up/down` when pressing `ctrl` + `volume up/down`. +- Send `delete` when pressing `shift` + `backspace`. +- Create custom shortcuts or change existing ones: E.g. Send `ctrl`+`shift`+`z` when `ctrl`+`y` is pressed. +- Run custom code when `ctrl` + `alt` + `esc` is pressed. + +## Setup + +To enable this feature, you need to add `KEY_OVERRIDE_ENABLE = yes` to your `rules.mk`. + +Then, in your `keymap.c` file, you'll need to define the array `key_overrides`, which defines all key overrides to be used. Each override is a value of type `key_override_t`. The array `key_overrides` is `NULL`-terminated and contains pointers to `key_override_t` values (`const key_override_t **`). + +## Creating Key Overrides + +The `key_override_t` struct has many options that allow you to precisely tune your overrides. The full reference is shown below. Instead of manually creating a `key_override_t` value, it is recommended to use these dedicated initializers: + +#### `ko_make_basic(modifiers, key, replacement)` +Returns a `key_override_t`, which sends `replacement` (can be a key-modifer combination), when `key` and `modifiers` are all pressed down. This override still activates if any additional modifiers not specified in `modifiers` are also pressed down. See `ko_make_with_layers_and_negmods` to customize this behavior. + +#### `ko_make_with_layers(modifiers, key, replacement, layers)` +Additionally takes a bitmask `layers` that defines on which layers the override is used. + +#### `ko_make_with_layers_and_negmods(modifiers, key, replacement, layers, negative_mods)` +Additionally takes a bitmask `negative_mods` that defines which modifiers may not be pressed for this override to activate. + +#### `ko_make_with_layers_negmods_and_options(modifiers, key, replacement, layers, negative_mods, options)` +Additionally takes a bitmask `options` that specifies additional options. See `ko_option_t` for available options. + +For more customization possibilities, you may directly create a `key_override_t`, which allows you to customize even more behavior. Read further below for details and examples. + +## Simple Example + +This shows how the mentioned example of sending `delete` when `shift` + `backspace` are pressed is realized: + +```c +const key_override_t delete_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_BSPACE, KC_DELETE); + +// This globally defines all key overrides to be used +const key_override_t **key_overrides = (const key_override_t *[]){ + &delete_key_override, + NULL // Null terminate the array of overrides! +}; +``` + +## Intermediate Difficulty Examples + +### Media Controls & Screen Brightness + +In this example a single key is configured to control media, volume and screen brightness by using key overrides. + +- The key is set to send `play/pause` in the keymap. + +The following key overrides will be configured: + +- `Ctrl` + `play/pause` will send `next track`. +- `Ctrl` + `Shift` + `play/pause` will send `previous track`. +- `Alt` + `play/pause` will send `volume up`. +- `Alt` + `Shift` + `play/pause` will send `volume down`. +- `Ctrl` + `Alt` + `play/pause` will send `brightness up`. +- `Ctrl` + `Alt` + `Shift` + `play/pause` will send `brightness down`. + + +```c +const key_override_t next_track_override = + ko_make_with_layers_negmods_and_options( + MOD_MASK_CTRL, // Trigger modifiers: ctrl + KC_MPLY, // Trigger key: play/pause + KC_MNXT, // Replacement key + ~0, // Activate on all layers + MOD_MASK_SA, // Do not activate when shift or alt are pressed + ko_option_no_reregister_trigger); // Specifies that the play key is not registered again after lifting ctrl + +const key_override_t prev_track_override = ko_make_with_layers_negmods_and_options(MOD_MASK_CS, KC_MPLY, + KC_MPRV, ~0, MOD_MASK_ALT, ko_option_no_reregister_trigger); + +const key_override_t vol_up_override = ko_make_with_layers_negmods_and_options(MOD_MASK_ALT, KC_MPLY, + KC_VOLU, ~0, MOD_MASK_CS, ko_option_no_reregister_trigger); + +const key_override_t vol_down_override = ko_make_with_layers_negmods_and_options(MOD_MASK_SA, KC_MPLY, + KC_VOLD, ~0, MOD_MASK_CTRL, ko_option_no_reregister_trigger); + +const key_override_t brightness_up_override = ko_make_with_layers_negmods_and_options(MOD_MASK_CA, KC_MPLY, + KC_BRIU, ~0, MOD_MASK_SHIFT, ko_option_no_reregister_trigger); + +const key_override_t brightness_down_override = ko_make_basic(MOD_MASK_CSA, KC_MPLY, KC_BRID); + +// This globally defines all key overrides to be used +const key_override_t **key_overrides = (const key_override_t *[]){ + &next_track_override, + &prev_track_override, + &vol_up_override, + &vol_down_override, + &brightness_up_override, + &brightness_down_override, + NULL +}; +``` + +### Flexible macOS-friendly Grave Escape +The [Grave Escape feature](https://docs.qmk.fm/using-qmk/advanced-keycodes/feature_grave_esc) is limited in its configurability and has [bugs when used on macOS](https://docs.qmk.fm/using-qmk/advanced-keycodes/feature_grave_esc#caveats). Key overrides can be used to achieve a similar functionality as Grave Escape, but with more customization and without bugs on macOS. + +```c +// Shift + esc = ~ +const key_override_t tilde_esc_override = ko_make_basic(MOD_MASK_SHIFT, KC_ESC, S(KC_GRAVE)); + +// GUI + esc = ` +const key_override_t grave_esc_override = ko_make_basic(MOD_MASK_GUI, KC_ESC, KC_GRAVE); + +const key_override_t **key_overrides = (const key_override_t *[]){ + &tilde_esc_override, + &grave_esc_override, + NULL +}; +``` + +In addition to not encountering unexpected bugs on macOS, you can also change the behavior as you wish. Instead setting `GUI` + `ESC` = `` ` `` you may change it to an arbitrary other modifier, for example `Ctrl` + `ESC` = `` ` ``. + +## Advanced Examples +### Modifiers as Layer Keys + +Do you really need a dedicated key to toggle your fn layer? With key overrides, perhaps not. This example shows how you can configure to use `rGUI` + `rAlt` (right GUI and right alt) to access a momentary layer like an fn layer. With this you completely eliminate the need to use a dedicated layer key. Of course the choice of modifier keys can be changed as needed, `rGUI` + `rAlt` is just an example here. + +```c +// This is called when the override activates and deactivates. Enable the fn layer on activation and disable on deactivation +bool momentary_layer(bool key_down, void *layer) { + if (key_down) { + layer_on((uint8_t)(uintptr_t)layer); + } else { + layer_off((uint8_t)(uintptr_t)layer); + } + + return false; +} + +const key_override_t fn_override = {.trigger_mods = MOD_BIT(KC_RGUI) | MOD_BIT(KC_RCTL), // + .layers = ~(1 << LAYER_FN), // + .suppressed_mods = MOD_BIT(KC_RGUI) | MOD_BIT(KC_RCTL), // + .options = ko_option_no_unregister_on_other_key_down, // + .negative_mod_mask = (uint8_t) ~(MOD_BIT(KC_RGUI) | MOD_BIT(KC_RCTL)), // + .custom_action = momentary_layer, // + .context = (void *)LAYER_FN, // + .trigger = KC_NO, // + .replacement = KC_NO, // + .enabled = NULL}; +``` + +## Keycodes + +You can enable, disable and toggle all key overrides on the fly. + +|Keycode |Description |Function Equivalent| +|----------|---------------------------------|--------| +|`KEY_OVERRIDE_ON` |Turns on Key Override feature | `key_override_on(void)`| +|`KEY_OVERRIDE_OFF` |Turns off Key Override feature |`key_override_off(void)`| +|`KEY_OVERRIDE_TOGGLE` |Toggles Key Override feature on and off |`key_override_toggle(void)`| + +## Reference for `key_override_t` + +Advanced users may need more customization than what is offered by the simple `ko_make` initializers. For this, directly create a `key_override_t` value and set all members. Below is a reference for all members of `key_override_t`. + +| Member | Description | +|--------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `uint16_t trigger` | The non-modifier keycode that triggers the override. This keycode, and the necessary modifiers (`trigger_mods`) must be pressed to activate this override. Set this to the keycode of the key that should activate the override. Set to `KC_NO` to require only the necessary modifiers to be pressed and no non-modifier. | +| `uint8_t trigger_mods` | Which mods need to be down for activation. If both sides of a modifier are set (e.g. left ctrl and right ctrl) then only one is required to be pressed (e.g. left ctrl suffices). Use the `MOD_MASK_XXX` and `MOD_BIT()` macros for this. | +| `layer_state_t layers` | This is a BITMASK (!), defining which layers this override applies to. To use this override on layer i set the ith bit `(1 << i)`. | +| `uint8_t negative_mod_mask` | Which modifiers cannot be down. It must hold that `(active_modifiers & negative_mod_mask) == 0`, otherwise the key override will not be activated. An active override will be deactivated once this is no longer true. | +| `uint8_t suppressed_mods` | Modifiers to 'suppress' while the override is active. To suppress a modifier means that even though the modifier key is held down, the host OS sees the modifier as not pressed. Can be used to suppress the trigger modifiers, as a trivial example. | +| `uint16_t replacement` | The complex keycode to send as replacement when this override is triggered. This can be a simple keycode, a key-modifier combination (e.g. `C(KC_A)`), or `KC_NO` (to register no replacement keycode). Use in combination with suppressed_mods to get the correct modifiers to be sent. | +| `ko_option_t options` | Options controlling the behavior of the override, such as what actions are allowed to activate the override. | +| `bool (*custom_action)(bool activated, void *context)` | If not NULL, this function will be called right before the replacement key is registered, along with the provided context and a flag indicating whether the override was activated or deactivated. This function allows you to run some custom actions for specific key overrides. If you return `false`, the replacement key is not registered/unregistered as it would normally. Return `true` to register and unregister the override normally. | +| `void *context` | A context that will be passed to the custom action function. | +| `bool *enabled` | If this points to false this override will not be used. Set to NULL to always have this override enabled. | + +### Reference for `ko_option_t` + +Bitfield with various options controlling the behavior of a key override. + +| Value | Description | +|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ko_option_activation_trigger_down` | Allow activating when the trigger key is pressed down. | +| `ko_option_activation_required_mod_down` | Allow activating when a necessary modifier is pressed down. | +| `ko_option_activation_negative_mod_up` | Allow activating when a negative modifier is released. | +| `ko_option_one_mod` | If set, any of the modifiers in `trigger_mods` will be enough to activate the override (logical OR of modifiers). If not set, all the modifiers in `trigger_mods` have to be pressed (logical AND of modifiers). | +| `ko_option_no_unregister_on_other_key_down` | If set, the override will not deactivate when another key is pressed down. Use only if you really know you need this. | +| `ko_option_no_reregister_trigger` | If set, the trigger key will never be registered again after the override is deactivated. | +| `ko_options_default` | The default options used by the `ko_make_xxx` functions | + +## For Advanced Users: Inner Workings + +This section explains how a key override works in detail, explaining where each member of `key_override_t` comes into play. Understanding this is essential to be able to take full advantage of all the options offered by key overrides. + +#### Activation + +When the necessary keys are pressed (`trigger_mods` + `trigger`), the override is 'activated' and the replacement key is registered in the keyboard report (`replacement`), while the `trigger` key is removed from the keyboard report. The trigger modifiers may also be removed from the keyboard report upon activation of an override (`suppressed_mods`). The override will not activate if any of the `negative_modifiers` are pressed. + +Overrides can activate in three different cases: + +1. The trigger key is pressed down and necessary modifiers are already down. +2. A necessary modifier is pressed down, while the trigger key and other necessary modifiers are already down. +3. A negative modifier is released, while all necessary modifiers and the trigger key are already down. + +Use the `option` member to customize which of these events are allowed to activate your overrides (default: all three). + +In any case, a key override can only activate if the `trigger` key is the _last_ non-modifier key that was pressed down. This emulates the behavior of how standard OSes (macOS, Windows, Linux) handle normal key input (to understand: Hold down `a`, then also hold down `b`, then hold down `shift`; `B` will be typed but not `A`). + +#### Deactivation + +An override is 'deactivated' when one of the trigger keys (`trigger_mods`, `trigger`) is lifted, another non-modifier key is pressed down, or one of the `negative_modifiers` is pressed down. When an override deactivates, the `replacement` key is removed from the keyboard report, while the `suppressed_mods` that are still held down are re-added to the keyboard report. By default, the `trigger` key is re-added to the keyboard report if it is still held down and no other non-modifier key has been pressed since. This again emulates the behavior of how standard OSes handle normal key input (To understand: hold down `a`, then also hold down `b`, then also `shift`, then release `b`; `A` will not be typed even though you are holding the `a` and `shift` keys). Use the `option` field `ko_option_no_reregister_trigger` to prevent re-registering the trigger key in all cases. + +#### Key Repeat Delay + +A third way in which standard OS-handling of modifier-key input is emulated in key overrides is with a ['key repeat delay'](https://www.dummies.com/computers/pcs/set-your-keyboards-repeat-delay-and-repeat-rate/). To explain what this is, let's look at how normal keyboard input is handled by mainstream OSes again: If you hold down `a`, followed by `shift`, you will see the letter `a` is first typed, then for a short moment nothing is typed and then repeating `A`s are typed. Take note that, although shift is pressed down just after `a` is pressed, it takes a moment until `A` is typed. This is caused by the aforementioned key repeat delay, and it is a feature that prevents unwanted repeated characters from being typed. + +This applies equally to releasing a modifier: When you hold `shift`, then press `a`, the letter `A` is typed. Now if you release `shift` first, followed by `a` shortly after, you will not see the letter `a` being typed, even though for a short moment of time you were just holding down the key `a`. This is because no modified characters are typed until the key repeat delay has passed. + + This exact behavior is implemented in key overrides as well: If a key override for `shift` + `a` = `b` exists, and `a` is pressed and held, followed by `shift`, you will not immediately see the letter `b` being typed. Instead, this event is deferred for a short moment, until the key repeat delay has passed, measured from the moment when the trigger key (`a`) was pressed down. + +The duration of the key repeat delay is controlled with the `KEY_OVERRIDE_REPEAT_DELAY` macro. Define this value in your `config.h` file to change it. It is 500ms by default. + + +## Difference to Combos + +Note that key overrides are very different from [combos](https://docs.qmk.fm/#/feature_combo). Combos require that you press down several keys almost _at the same time_ and can work with any combination of non-modifier keys. Key overrides work like keyboard shortcuts (e.g. `ctrl` + `z`): They take combinations of _multiple_ modifiers and _one_ non-modifier key to then perform some custom action. Key overrides are implemented with much care to behave just like normal keyboard shortcuts would in regards to the order of pressed keys, timing, and interacton with other pressed keys. There are a number of optional settings that can be used to really fine-tune the behavior of each key override as well. Using key overrides also does not delay key input for regular key presses, which inherently happens in combos and may be undesirable. diff --git a/docs/syllabus.md b/docs/syllabus.md index ec7f66ba78..b33bd9e727 100644 --- a/docs/syllabus.md +++ b/docs/syllabus.md @@ -40,6 +40,7 @@ These topics start to dig into some of the features that QMK supports. You don't * [Tap Dance](feature_tap_dance.md) * [Combos](feature_combo.md) * [Userspace](feature_userspace.md) + * [Key Overrides](feature_key_overrides.md) # Advanced Topics diff --git a/docs/understanding_qmk.md b/docs/understanding_qmk.md index 331b1c893c..e3dd5cb780 100644 --- a/docs/understanding_qmk.md +++ b/docs/understanding_qmk.md @@ -146,6 +146,7 @@ The `process_record()` function itself is deceptively simple, but hidden within * [`bool process_audio(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_audio.c#L19) * [`bool process_steno(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_steno.c#L160) * [`bool process_music(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_music.c#L114) + * [`bool process_key_override(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/5a1b857dea45a17698f6baa7dd1b7a7ea907fb0a/quantum/process_keycode/process_key_override.c#L397) * [`bool process_tap_dance(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_tap_dance.c#L141) * [`bool process_unicode_common(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_unicode_common.c#L169) calls one of: diff --git a/quantum/process_keycode/process_key_override.c b/quantum/process_keycode/process_key_override.c new file mode 100644 index 0000000000..fe43eacc40 --- /dev/null +++ b/quantum/process_keycode/process_key_override.c @@ -0,0 +1,518 @@ +/* + * Copyright 2021 Jonas Gessner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "quantum.h" +#include "report.h" +#include "timer.h" +#include "process_key_override_private.h" + +#include + +#ifndef KEY_OVERRIDE_REPEAT_DELAY +# define KEY_OVERRIDE_REPEAT_DELAY 500 +#endif + +// For benchmarking the time it takes to call process_key_override on every key press (needs keyboard debugging enabled as well) +// #define BENCH_KEY_OVERRIDE + +// For debug output (needs keyboard debugging enabled as well) +// #define DEBUG_KEY_OVERRIDE + +#ifdef DEBUG_KEY_OVERRIDE +# define key_override_printf dprintf +#else +# define key_override_printf(str, ...) \ + {} +#endif + +// Helpers + +// Private functions implemented elsewhere in qmk/tmk +extern uint8_t extract_mod_bits(uint16_t code); +extern void set_weak_override_mods(uint8_t mods); +extern void clear_weak_override_mods(void); +extern void set_suppressed_override_mods(uint8_t mods); +extern void clear_suppressed_override_mods(void); + +static uint16_t clear_mods_from(uint16_t keycode) { + switch (keycode) { + case QK_MODS ... QK_MODS_MAX: + break; + default: + return keycode; + } + + static const uint16_t all_mods = QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI | QK_RCTL | QK_RSFT | QK_RALT | QK_RGUI; + + return (keycode & ~(all_mods)); +} + +// Internal variables +static const key_override_t *active_override = NULL; +static bool active_override_trigger_is_down = false; + +// Used to keep track of what non-modifier key was last pressed down. We never want to activate an override for a trigger key that is not the last non-mod key that was pressed down. OSes internally completely unregister a key that is held when a different key is held down after. We want to respect this here. +static uint16_t last_key_down = 0; +// When was the last key pressed down? +static uint32_t last_key_down_time = 0; + +// What timestamp are we comparing to when waiting to register a deferred key? +static uint32_t defer_reference_time = 0; +// What delay should pass until deferred key is registered? +static uint32_t defer_delay = 0; + +// Holds the keycode that should be registered at a later time, in order to not get false key presses +static uint16_t deferred_register = 0; + +// TODO: in future maybe save in EEPROM? +static bool enabled = true; + +// Public variables +__attribute__((weak)) const key_override_t **key_overrides = NULL; + +// Forward decls +static const key_override_t *clear_active_override(const bool allow_reregister); + +void key_override_on(void) { + enabled = true; + key_override_printf("Key override ON\n"); +} + +void key_override_off(void) { + enabled = false; + clear_active_override(false); + key_override_printf("Key override OFF\n"); +} + +void key_override_toggle(void) { + if (key_override_is_enabled()) { + key_override_off(); + } else { + key_override_on(); + } +} + +bool key_override_is_enabled(void) { return enabled; } + +// Returns whether the modifiers that are pressed are such that the override should activate +static bool key_override_matches_active_modifiers(const key_override_t *override, const uint8_t mods) { + // Check that negative keys pass + if ((override->negative_mod_mask & mods) != 0) { + return false; + } + + // Immediately return true if the override requires no mods down + if (override->trigger_mods == 0) { + return true; + } + + if ((override->options & ko_option_one_mod) != 0) { + // At least one of the trigger modifiers must be down + return (override->trigger_mods & mods) != 0; + } else { + // All trigger modifiers must be down, but each mod can be active on either side (if both sides are specified). + + // Which mods, regardless of side, are required? + uint8_t one_sided_required_mods = (override->trigger_mods & 0b1111) | (override->trigger_mods >> 4); + + // Which of the required modifiers are active? + uint8_t active_required_mods = override->trigger_mods & mods; + + // Move the active requird mods to one side + uint8_t one_sided_active_required_mods = (active_required_mods & 0b1111) | (active_required_mods >> 4); + + // Check that there is a full match between the required one-sided mods and active required one sided mods + return one_sided_active_required_mods == one_sided_required_mods; + } + + return false; +} + +static void schedule_deferred_register(const uint16_t keycode) { + if (timer_elapsed32(last_key_down_time) < KEY_OVERRIDE_REPEAT_DELAY) { + // Defer until KEY_OVERRIDE_REPEAT_DELAY has passed since the trigger key was pressed down. This emulates the behavior as holding down a key x, then holding down shift shortly after. Usually the shifted key X is not immediately produced, but rather a 'key repeat delay' passes before any repeated character is output. + defer_reference_time = last_key_down_time; + defer_delay = KEY_OVERRIDE_REPEAT_DELAY; + } else { + // Wait a very short time when a modifier event triggers the override to avoid false activations when e.g. a modifier is pressed just before a key is released (with the intention of pairing the modifier with a different key), or a modifier is lifted shortly before the trigger key is lifted. Operating systems by default reject modifier-events that happen very close to a non-modifier event. + defer_reference_time = timer_read32(); + defer_delay = 50; // 50ms + } + deferred_register = keycode; +} + +const key_override_t *clear_active_override(const bool allow_reregister) { + if (active_override == NULL) { + return NULL; + } + + key_override_printf("Deactivating override\n"); + + deferred_register = 0; + + // Clear the suppressed mods + clear_suppressed_override_mods(); + + // Unregister the replacement. First remove the weak override mods + clear_weak_override_mods(); + + const key_override_t *const old = active_override; + + const uint8_t mod_free_replacement = clear_mods_from(active_override->replacement); + + bool unregister_replacement = mod_free_replacement != KC_NO && // KC_NO is never registered + mod_free_replacement < SAFE_RANGE; // Custom keycodes are never registered + + // Try firing the custom handler + if (active_override->custom_action != NULL) { + unregister_replacement &= active_override->custom_action(false, active_override->context); + } + + // Then unregister the mod-free replacement key if desired + if (unregister_replacement) { + if (IS_KEY(mod_free_replacement)) { + del_key(mod_free_replacement); + } else { + key_override_printf("NOT KEY 1\n"); + send_keyboard_report(); + unregister_code(mod_free_replacement); + } + } + + const uint16_t trigger = active_override->trigger; + + const bool reregister_trigger = allow_reregister && // Check if allowed from caller + (active_override->options & ko_option_no_reregister_trigger) == 0 && // Check if override allows + active_override_trigger_is_down && // Check if trigger is even down + trigger != KC_NO && // KC_NO is never registered + trigger < SAFE_RANGE; // A custom keycode should not be registered + + // Optionally re-register the trigger if it is still down + if (reregister_trigger) { + key_override_printf("Re-registering trigger deferred: %u\n", trigger); + + // This will always be a modifier event, so defer always + schedule_deferred_register(trigger); + } + + send_keyboard_report(); + + active_override = NULL; + active_override_trigger_is_down = false; + + return old; +} + +/** Checks if the key event is an allowed activation event for the provided override. Does not check things like whether the correct mods or correct trigger key is down. */ +static bool check_activation_event(const key_override_t *override, const bool key_down, const bool is_mod) { + ko_option_t options = override->options; + + if ((options & ko_options_all_activations) == 0) { + // No activation option provided at all. This is wrong, but let's assume the default activations (ko_options_all_activations) were meant... + options = ko_options_all_activations; + } + + if (is_mod) { + if (key_down) { + return (options & ko_option_activation_required_mod_down) != 0; + } else { + return (options & ko_option_activation_negative_mod_up) != 0; + } + } else { + if (key_down) { + return (options & ko_option_activation_trigger_down) != 0; + } else { + return false; + } + } +} + +/** Iterates through the list of key overrides and tries activating each, until it finds one that activates or reaches the end of overrides. Returns true if the key action for `keycode` should be sent */ +static bool try_activating_override(const uint16_t keycode, const uint8_t layer, const bool key_down, const bool is_mod, const uint8_t active_mods, bool *activated) { + if (key_overrides == NULL) { + return true; + } + + for (uint8_t i = 0;; i++) { + const key_override_t *const override = key_overrides[i]; + + // End of array + if (override == NULL) { + break; + } + + // Fast, but not full mods check. Most key presses will not have any mods down, and most overrides will require mods. Hence here we filter overrides that require mods to be down while no mods are down + if (active_mods == 0 && override->trigger_mods != 0) { + key_override_printf("Not activating override: Modifiers don't match\n"); + continue; + } + + // Check layer + if ((override->layers & (1 << layer)) == 0) { + key_override_printf("Not activating override: Not set to activate on pressed layer\n"); + continue; + } + + // Check allowed activation events + if (!check_activation_event(override, key_down, is_mod)) { + key_override_printf("Not activating override: Activation event not allowed\n"); + continue; + } + + const bool is_trigger = override->trigger == keycode; + + // Check if trigger lifted. This is a small optimization in order to skip the remaining checks + if (is_trigger && !key_down) { + key_override_printf("Not activating override: Trigger lifted\n"); + continue; + } + + // If the trigger is KC_NO it means 'no key', so only the required modifiers need to be down. + const bool no_trigger = override->trigger == KC_NO; + + // Check if aleady active + if (override == active_override) { + key_override_printf("Not activating override: Alerady actived\n"); + continue; + } + + // Check if enabled + if (override->enabled != NULL && !((*(override->enabled) & 1))) { + key_override_printf("Not activating override: Not enabled\n"); + continue; + } + + // Check mods precisely + if (!key_override_matches_active_modifiers(override, active_mods)) { + key_override_printf("Not activating override: Modifiers don't match\n"); + continue; + } + + // Check if trigger key is down. + const bool trigger_down = is_trigger && key_down; + + // At this point, all requirements for activation are checked, except whether the trigger key is pressed. Now we check if the required trigger is down + // If no trigger key is required, yes. + // If the trigger was just pressed, yes. + // If the last non-mod key that was pressed down is the trigger key, yes. + bool should_activate = no_trigger || trigger_down || last_key_down == override->trigger; + + if (!should_activate) { + key_override_printf("Not activating override. Trigger not down\n"); + continue; + } + + key_override_printf("Activating override\n"); + + clear_active_override(false); + + active_override = override; + active_override_trigger_is_down = true; + + set_suppressed_override_mods(override->suppressed_mods); + + if (!trigger_down && !no_trigger) { + // When activating a key override the trigger is is always unregistered. In the case where the key that newly pressed is not the trigger key, we have to explicitly remove the trigger key from the keyboard report. If the trigger was just pressed down we simply suppress the event which also has the effect of the trigger key not being registered in the keyboard report. + if (IS_KEY(override->trigger)) { + del_key(override->trigger); + } else { + unregister_code(override->trigger); + } + } + + const uint16_t mod_free_replacement = clear_mods_from(override->replacement); + + bool register_replacement = mod_free_replacement != KC_NO && // KC_NO is never registered + mod_free_replacement < SAFE_RANGE; // Custom keycodes are never registered + + // Try firing the custom handler + if (override->custom_action != NULL) { + register_replacement &= override->custom_action(true, override->context); + } + + if (register_replacement) { + const uint8_t override_mods = extract_mod_bits(override->replacement); + set_weak_override_mods(override_mods); + + // If this is a modifier event that activates the key override we _always_ defer the actual full activation of the override + if (is_mod) { + key_override_printf("Deferring register replacement key\n"); + schedule_deferred_register(mod_free_replacement); + send_keyboard_report(); + } else { + if (IS_KEY(mod_free_replacement)) { + add_key(mod_free_replacement); + } else { + key_override_printf("NOT KEY 2\n"); + send_keyboard_report(); + // On macOS there seems to be a race condition when it comes to the keyboard report and consumer keycodes. It seems the OS may recognize a consumer keycode before an updated keyboard report, even if the keyboard report is actually sent before the consumer key. I assume it is some sort of race condition because it happens infrequently and very irregularly. Waiting for about at least 10ms between sending the keyboard report and sending the consumer code has shown to fix this. + wait_ms(10); + register_code(mod_free_replacement); + } + } + } else { + // If not registering the replacement key send keyboard report to update the unregistered keys. + send_keyboard_report(); + } + + *activated = true; + + // If the trigger is down, suppress the event so that it does not get added to the keyboard report. + return !trigger_down; + } + + *activated = false; + + return true; +} + +void matrix_scan_key_override(void) { + if (deferred_register == 0) { + return; + } + + if (timer_elapsed32(defer_reference_time) >= defer_delay) { + key_override_printf("Registering deferred key\n"); + register_code16(deferred_register); + deferred_register = 0; + defer_reference_time = 0; + defer_delay = 0; + } +} + +bool process_key_override(const uint16_t keycode, const keyrecord_t *const record) { +#ifdef BENCH_KEY_OVERRIDE + uint16_t start = timer_read(); +#endif + + const bool key_down = record->event.pressed; + const bool is_mod = IS_MOD(keycode); + + if (key_down) { + switch (keycode) { + case KEY_OVERRIDE_TOGGLE: + key_override_toggle(); + return false; + + case KEY_OVERRIDE_ON: + key_override_on(); + return false; + + case KEY_OVERRIDE_OFF: + key_override_off(); + return false; + + default: + break; + } + } + + if (!enabled) { + return true; + } + + uint8_t effective_mods = get_mods(); + +#ifdef KEY_OVERRIDE_INCLUDE_WEAK_MODS + effective_mods |= get_weak_mods(); +#endif + +#ifndef NO_ACTION_ONESHOT + // Locked one shot mods are added to get_mods(), I think (why??) while oneshot mods are in get_oneshot_mods(). Still OR with get_locked_oneshot_mods because that's where those mods _should_ be saved. + effective_mods |= get_oneshot_locked_mods() | get_oneshot_mods(); +#endif + + if (is_mod) { + // The mods returned from get_mods() will be updated with this new event _after_ this code runs. Hence we manually update the effective mods here to really know the effective mods. + if (key_down) { + effective_mods |= MOD_BIT(keycode); + } else { + effective_mods &= ~MOD_BIT(keycode); + } + } else { + if (key_down) { + last_key_down = keycode; + last_key_down_time = timer_read32(); + deferred_register = 0; + } + + // The last key that was pressed was just released. No more keys are therefore sending input + if (!key_down && keycode == last_key_down) { + last_key_down = 0; + last_key_down_time = 0; + // We also cancel any deferred registers because, again, no keys are sending any input. Only the last key that is pressed creates an input – this key was just lifted. + deferred_register = 0; + } + } + + key_override_printf("key down: %u keycode: %u is mod: %u effective mods: %u\n", key_down, keycode, is_mod, effective_mods); + + bool send_key_action = true; + bool activated = false; + + // Non-mod key up events never activate a key override + if (is_mod || key_down) { + // Get the exact layer that was hit. It will be cached at this point + const uint8_t layer = read_source_layers_cache(record->event.key); + + // Use blocked to ensure the same override is not activated again immediately after it is deactivated + send_key_action = try_activating_override(keycode, layer, key_down, is_mod, effective_mods, &activated); + + if (!send_key_action) { + send_keyboard_report(); + } + } + + if (!activated && active_override != NULL) { + if (is_mod) { + // Check if necessary modifier of current override goes up or a negative mod goes down + if (!key_override_matches_active_modifiers(active_override, effective_mods)) { + key_override_printf("Deactivating override because necessary modifier lifted or negative mod pressed\n"); + clear_active_override(true); + } + } else { + // Check if trigger of current override goes up or if override does not allow additional keys to be down and another key goes down + const bool is_trigger = keycode == active_override->trigger; + bool should_deactivate = false; + + // Check if trigger key lifted + if (is_trigger && !key_down) { + should_deactivate = true; + active_override_trigger_is_down = false; + key_override_printf("Deactivating override because trigger key up\n"); + } + + // Check if another key was pressed + if (key_down && (active_override->options & ko_option_no_unregister_on_other_key_down) == 0) { + should_deactivate = true; + key_override_printf("Deactivating override because another key was pressed\n"); + } + + if (should_deactivate) { + clear_active_override(false); + } + } + } + +#ifdef BENCH_KEY_OVERRIDE + uint16_t elapsed = timer_elapsed(start); + + dprintf("Processing key overrides took: %u ms\n", elapsed); +#endif + + return send_key_action; +} diff --git a/quantum/process_keycode/process_key_override.h b/quantum/process_keycode/process_key_override.h new file mode 100644 index 0000000000..9ba59e4e9b --- /dev/null +++ b/quantum/process_keycode/process_key_override.h @@ -0,0 +1,147 @@ +/* + * Copyright 2021 Jonas Gessner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include "action_layer.h" + +/** + * Key overrides allow you to send a different key-modifier combination or perform a custom action when a certain modifier-key combination is pressed. + * + * For example, you may configure a key override to send the delete key when shift + backspace are pressed together, or that your volume keys become screen brightness keys when holding ctrl. The possibilities are quite vast and the documentation contains a few examples for inspiration. + * + * See the documentation and examples here: https://docs.qmk.fm/#/feature_key_overrides + */ + +/** Bitfield with various options controlling the behavior of a key override. */ +typedef enum { + /** Allow activating when the trigger key is pressed down. */ + ko_option_activation_trigger_down = (1 << 0), + /** Allow activating when a necessary modifier is pressed down. */ + ko_option_activation_required_mod_down = (1 << 1), + /** Allow activating when a negative modifier is released. */ + ko_option_activation_negative_mod_up = (1 << 2), + + ko_options_all_activations = ko_option_activation_negative_mod_up | ko_option_activation_required_mod_down | ko_option_activation_trigger_down, + + /** If set, any of the modifiers in trigger_mods will be enough to activate the override (logical OR of modifiers). If not set, all the modifiers in trigger_mods have to be pressed (logical AND of modifiers). */ + ko_option_one_mod = (1 << 3), + + /** If set, the trigger key will never be registered again after the override is deactivated. */ + ko_option_no_reregister_trigger = (1 << 4), + + /** If set, the override will not deactivate when another key is pressed down. Use only if you really know you need this. */ + ko_option_no_unregister_on_other_key_down = (1 << 5), + + /** The default options used by the ko_make_xxx functions. */ + ko_options_default = ko_options_all_activations, +} ko_option_t; + +/** Defines a single key override */ +typedef struct { + // The non-modifier keycode that triggers the override. This keycode, and the necessary modifiers (trigger_mods) must be pressed to activate this override. Set this to the keycode of the key that should activate the override. Set to KC_NO to require only the necessary modifiers to be pressed and no non-modifier. + uint16_t trigger; + + // Which mods need to be down for activation. If both sides of a modifier are set (e.g. left ctrl and right ctrl) then only one is required to be pressed (e.g. left ctrl suffices). Use the MOD_MASK_XXX and MOD_BIT() macros for this. + uint8_t trigger_mods; + + // This is a BITMASK (!), defining which layers this override applies to. To use this override on layer i set the ith bit (1 << i). + layer_state_t layers; + + // Which modifiers cannot be down. It must hold that (active_mods & negative_mod_mask) == 0, otherwise the key override will not be activated. An active override will be deactivated once this is no longer true. + uint8_t negative_mod_mask; + + // Modifiers to 'suppress' while the override is active. To suppress a modifier means that even though the modifier key is held down, the host OS sees the modifier as not pressed. Can be used to suppress the trigger modifiers, as a trivial example. + uint8_t suppressed_mods; + + // The complex keycode to send as replacement when this override is triggered. This can be a simple keycode, a key-modifier combination (e.g. C(KC_A)), or KC_NO (to register no replacement keycode). Use in combination with suppressed_mods to get the correct modifiers to be sent. + uint16_t replacement; + + // Options controlling the behavior of the override, such as what actions are allowed to activate the override. + ko_option_t options; + + // If not NULL, this function will be called right before the replacement key is registered, along with the provided context and a flag indicating whether the override was activated or deactivated. This function allows you to run some custom actions for specific key overrides. If you return `false`, the replacement key is not registered/unregistered as it would normally. Return `true` to register and unregister the override normally. + bool (*custom_action)(bool activated, void *context); + + // A context that will be passed to the custom action function. + void *context; + + // If this points to false this override will not be used. Set to NULL to always have this override enabled. + bool *enabled; +} key_override_t; + +/** Define this as a null-terminated array of pointers to key overrides. These key overrides will be used by qmk. */ +extern const key_override_t **key_overrides; + +/** Turns key overrides on */ +extern void key_override_on(void); + +/** Turns key overrides off */ +extern void key_override_off(void); + +/** Toggles key overrides on */ +extern void key_override_toggle(void); + +/** Returns whether key overrides are enabled */ +extern bool key_override_is_enabled(void); + +/** + * Preferrably use these macros to create key overrides. They fix many of the options to a standard setting that should satisfy most basic use-cases. Only directly create a key_override_t struct when you really need to. + */ + +// clang-format off + +/** + * Convenience initializer to create a basic key override. Activates the override on all layers. + */ +#define ko_make_basic(trigger_mods, trigger_key, replacement_key) \ + ko_make_with_layers(trigger_mods, trigger_key, replacement_key, ~0) + +/** + * Convenience initializer to create a basic key override. Provide a bitmap (of type layer_state_t) with the bits set for each layer on which the override should activate. + */ +#define ko_make_with_layers(trigger_mods, trigger_key, replacement_key, layers) \ + ko_make_with_layers_and_negmods(trigger_mods, trigger_key, replacement_key, layers, 0) + +/** + * Convenience initializer to create a basic key override. Provide a bitmap with the bits set for each layer on which the override should activate. Also provide a negative modifier mask, that is used to define which modifiers may not be pressed. + */ +#define ko_make_with_layers_and_negmods(trigger_mods, trigger_key, replacement_key, layers, negative_mask) \ + ko_make_with_layers_negmods_and_options(trigger_mods, trigger_key, replacement_key, layers, negative_mask, ko_options_default) + + /** + * Convenience initializer to create a basic key override. Provide a bitmap with the bits set for each layer on which the override should activate. Also provide a negative modifier mask, that is used to define which modifiers may not be pressed. Provide options for additional control of the behavior of the override. + */ +#define ko_make_with_layers_negmods_and_options(trigger_mods_, trigger_key, replacement_key, layer_mask, negative_mask, options_) \ + ((const key_override_t){ \ + .trigger_mods = (trigger_mods_), \ + .layers = (layer_mask), \ + .suppressed_mods = (trigger_mods_), \ + .options = (options_), \ + .negative_mod_mask = (negative_mask), \ + .custom_action = NULL, \ + .context = NULL, \ + .trigger = (trigger_key), \ + .replacement = (replacement_key), \ + .enabled = NULL \ + }) + +// clang-format on diff --git a/quantum/process_keycode/process_key_override_private.h b/quantum/process_keycode/process_key_override_private.h new file mode 100644 index 0000000000..1d0e134a19 --- /dev/null +++ b/quantum/process_keycode/process_key_override_private.h @@ -0,0 +1,24 @@ +/* + * Copyright 2021 Jonas Gessner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include "action.h" + +bool process_key_override(const uint16_t keycode, const keyrecord_t *const record); +void matrix_scan_key_override(void); diff --git a/quantum/quantum.c b/quantum/quantum.c index b4cfa28d7d..9cfffd9317 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -58,12 +58,16 @@ float bell_song[][2] = SONG(TERMINAL_SOUND); # include "process_auto_shift.h" #endif -static void do_code16(uint16_t code, void (*f)(uint8_t)) { +#ifdef KEY_OVERRIDE_ENABLE +# include "process_key_override_private.h" +#endif + +uint8_t extract_mod_bits(uint16_t code) { switch (code) { case QK_MODS ... QK_MODS_MAX: break; default: - return; + return 0; } uint8_t mods_to_send = 0; @@ -80,9 +84,11 @@ static void do_code16(uint16_t code, void (*f)(uint8_t)) { if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_LGUI); } - f(mods_to_send); + return mods_to_send; } +static void do_code16(uint16_t code, void (*f)(uint8_t)) { f(extract_mod_bits(code)); } + void register_code16(uint16_t code) { if (IS_MOD(code) || code == KC_NO) { do_code16(code, register_mods); @@ -243,6 +249,9 @@ bool process_record_quantum(keyrecord_t *record) { #if (defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))) && !defined(NO_MUSIC_MODE) process_music(keycode, record) && #endif +#ifdef KEY_OVERRIDE_ENABLE + process_key_override(keycode, record) && +#endif #ifdef TAP_DANCE_ENABLE process_tap_dance(keycode, record) && #endif @@ -408,6 +417,10 @@ void matrix_scan_quantum() { matrix_scan_music(); #endif +#ifdef KEY_OVERRIDE_ENABLE + matrix_scan_key_override(); +#endif + #ifdef SEQUENCER_ENABLE matrix_scan_sequencer(); #endif diff --git a/quantum/quantum.h b/quantum/quantum.h index 66ba96fde8..673796e6b1 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -118,6 +118,10 @@ extern layer_state_t layer_state; # include "process_unicodemap.h" #endif +#ifdef KEY_OVERRIDE_ENABLE +# include "process_key_override.h" +#endif + #ifdef TAP_DANCE_ENABLE # include "process_tap_dance.h" #endif diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index c361dd670e..3d2dbde922 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -514,6 +514,11 @@ enum quantum_keycodes { // RGB underglow/matrix (continued) RGB_MODE_TWINKLE, + // Key Overrides + KEY_OVERRIDE_TOGGLE, + KEY_OVERRIDE_ON, + KEY_OVERRIDE_OFF, + // Start of custom keycode range for keyboards and keymaps - always leave at the end SAFE_RANGE }; diff --git a/show_options.mk b/show_options.mk index af38bcb24b..dd1ad5171f 100644 --- a/show_options.mk +++ b/show_options.mk @@ -44,6 +44,7 @@ OTHER_OPTION_NAMES = \ AUTO_SHIFT_MODIFIERS \ COMBO_ENABLE \ KEY_LOCK_ENABLE \ + KEY_OVERRIDE_ENABLE \ LEADER_ENABLE \ PRINTING_ENABLE \ STENO_ENABLE \ diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c index a57c8bf66a..2b3c00cba0 100644 --- a/tmk_core/common/action_util.c +++ b/tmk_core/common/action_util.c @@ -27,6 +27,10 @@ extern keymap_config_t keymap_config; static uint8_t real_mods = 0; static uint8_t weak_mods = 0; static uint8_t macro_mods = 0; +#ifdef KEY_OVERRIDE_ENABLE +static uint8_t weak_override_mods = 0; +static uint8_t suppressed_mods = 0; +#endif #ifdef USB_6KRO_ENABLE # define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS) @@ -229,6 +233,7 @@ void send_keyboard_report(void) { keyboard_report->mods = real_mods; keyboard_report->mods |= weak_mods; keyboard_report->mods |= macro_mods; + #ifndef NO_ACTION_ONESHOT if (oneshot_mods) { # if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) @@ -244,6 +249,13 @@ void send_keyboard_report(void) { } #endif + +#ifdef KEY_OVERRIDE_ENABLE + // These need to be last to be able to properly control key overrides + keyboard_report->mods &= ~suppressed_mods; + keyboard_report->mods |= weak_override_mods; +#endif + host_keyboard_send(keyboard_report); } @@ -299,6 +311,22 @@ void set_weak_mods(uint8_t mods) { weak_mods = mods; } */ void clear_weak_mods(void) { weak_mods = 0; } +#ifdef KEY_OVERRIDE_ENABLE +/** \brief set weak mods used by key overrides. DO not call this manually + */ +void set_weak_override_mods(uint8_t mods) { weak_override_mods = mods; } +/** \brief clear weak mods used by key overrides. DO not call this manually + */ +void clear_weak_override_mods(void) { weak_override_mods = 0; } + +/** \brief set suppressed mods used by key overrides. DO not call this manually + */ +void set_suppressed_override_mods(uint8_t mods) { suppressed_mods = mods; } +/** \brief clear suppressed mods used by key overrides. DO not call this manually + */ +void clear_suppressed_override_mods(void) { suppressed_mods = 0; } +#endif + /* macro modifier */ /** \brief get macro mods * -- cgit v1.2.3 From 567da49ed027a936a6f56aa069aad0db4605a198 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sat, 24 Jul 2021 12:13:06 +0100 Subject: Avoid LTO conficts on arm_atsam (#13676) --- quantum/split_common/split_util.c | 2 +- tmk_core/common/usb_util.c | 2 +- tmk_core/common/usb_util.h | 2 +- tmk_core/protocol/chibios/usb_util.c | 2 +- tmk_core/protocol/lufa/usb_util.c | 2 +- tmk_core/protocol/vusb/usb_util.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tmk_core/common') diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c index 989829d2dc..8d414f6fe6 100644 --- a/quantum/split_common/split_util.c +++ b/quantum/split_common/split_util.c @@ -106,7 +106,7 @@ __attribute__((weak)) bool is_keyboard_master(void) { // Avoid NO_USB_STARTUP_CHECK - Disable USB as the previous checks seem to enable it somehow if (usbstate == SLAVE) { - usb_disable(); + usb_disconnect(); } } diff --git a/tmk_core/common/usb_util.c b/tmk_core/common/usb_util.c index d4134a0446..dd1deeaa11 100644 --- a/tmk_core/common/usb_util.c +++ b/tmk_core/common/usb_util.c @@ -16,7 +16,7 @@ #include "quantum.h" #include "usb_util.h" -__attribute__((weak)) void usb_disable(void) {} +__attribute__((weak)) void usb_disconnect(void) {} __attribute__((weak)) bool usb_connected_state(void) { return true; } __attribute__((weak)) bool usb_vbus_state(void) { #ifdef USB_VBUS_PIN diff --git a/tmk_core/common/usb_util.h b/tmk_core/common/usb_util.h index 4ebedb1e71..13db9fbfbd 100644 --- a/tmk_core/common/usb_util.h +++ b/tmk_core/common/usb_util.h @@ -17,6 +17,6 @@ #include -void usb_disable(void); +void usb_disconnect(void); bool usb_connected_state(void); bool usb_vbus_state(void); diff --git a/tmk_core/protocol/chibios/usb_util.c b/tmk_core/protocol/chibios/usb_util.c index 5945e8a8de..e32d6ebfa4 100644 --- a/tmk_core/protocol/chibios/usb_util.c +++ b/tmk_core/protocol/chibios/usb_util.c @@ -16,6 +16,6 @@ #include #include "usb_util.h" -void usb_disable(void) { usbStop(&USBD1); } +void usb_disconnect(void) { usbStop(&USBD1); } bool usb_connected_state(void) { return usbGetDriverStateI(&USBD1) == USB_ACTIVE; } diff --git a/tmk_core/protocol/lufa/usb_util.c b/tmk_core/protocol/lufa/usb_util.c index 9e943a21b9..9691eff1e4 100644 --- a/tmk_core/protocol/lufa/usb_util.c +++ b/tmk_core/protocol/lufa/usb_util.c @@ -17,7 +17,7 @@ #include "usb_util.h" #include "wait.h" -void usb_disable(void) { +void usb_disconnect(void) { USB_Disable(); USB_DeviceState = DEVICE_STATE_Unattached; } diff --git a/tmk_core/protocol/vusb/usb_util.c b/tmk_core/protocol/vusb/usb_util.c index 602854dbe6..4ee2d3188b 100644 --- a/tmk_core/protocol/vusb/usb_util.c +++ b/tmk_core/protocol/vusb/usb_util.c @@ -16,7 +16,7 @@ #include #include "usb_util.h" -void usb_disable(void) { usbDeviceDisconnect(); } +void usb_disconnect(void) { usbDeviceDisconnect(); } bool usb_connected_state(void) { usbPoll(); -- cgit v1.2.3 From 70fb3e1aaf406cbbd5137916a93ed814d6891ef2 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sat, 31 Jul 2021 14:35:30 +0100 Subject: __flash? (#13799) --- docs/feature_led_matrix.md | 2 +- docs/feature_rgb_matrix.md | 8 ++++---- docs/ja/feature_led_matrix.md | 2 +- drivers/led/aw20216.h | 3 ++- drivers/led/issi/is31fl3731-simple.h | 3 ++- drivers/led/issi/is31fl3731.h | 3 ++- drivers/led/issi/is31fl3733.h | 3 ++- drivers/led/issi/is31fl3736.h | 3 ++- drivers/led/issi/is31fl3737.c | 9 ++------- drivers/led/issi/is31fl3737.h | 3 ++- drivers/led/issi/is31fl3741.h | 3 ++- keyboards/clueboard/66_hotswap/gen1/gen1.c | 2 +- keyboards/dp60/dp60.c | 2 +- keyboards/durgod/dgk6x/galaxy/galaxy.c | 2 +- keyboards/durgod/dgk6x/hades/hades.c | 2 +- keyboards/durgod/dgk6x/venus/venus.c | 2 +- keyboards/dztech/dz60rgb/dz60rgb.c | 2 +- keyboards/dztech/dz60rgb_ansi/dz60rgb_ansi.c | 2 +- keyboards/dztech/dz60rgb_wkl/dz60rgb_wkl.c | 2 +- keyboards/dztech/dz65rgb/v1/v1.c | 2 +- keyboards/dztech/dz65rgb/v2/v2.c | 2 +- keyboards/dztech/dz65rgb/v3/v3.c | 2 +- keyboards/ergodox_ez/ergodox_ez.c | 2 +- keyboards/ergodox_infinity/ergodox_infinity.c | 2 +- keyboards/exclusive/e6_rgb/e6_rgb.c | 2 +- keyboards/fallacy/indicators.c | 2 +- keyboards/geekboards/tester/tester.c | 2 +- keyboards/hs60/v1/v1.c | 4 ++-- keyboards/inett_studio/sqx/hotswap/hotswap.c | 2 +- keyboards/inett_studio/sqx/universal/universal.c | 2 +- keyboards/k_type/is31fl3733-dual.h | 2 +- keyboards/k_type/k_type.c | 2 +- keyboards/kbdfans/bella/rgb/rgb.c | 2 +- keyboards/kbdfans/bella/rgb_iso/rgb_iso.c | 2 +- keyboards/kbdfans/kbd67/mkiirgb/mkiirgb.c | 2 +- keyboards/kbdfans/kbdmini/kbdmini.c | 2 +- keyboards/kbdfans/maja/maja.c | 2 +- keyboards/keychron/q1/q1.c | 2 +- keyboards/latin17rgb/latin17rgb.c | 2 +- keyboards/latin60rgb/latin60rgb.c | 2 +- keyboards/latin6rgb/latin6rgb.c | 2 +- keyboards/matrix/m20add/rgb_ring.c | 2 +- keyboards/matrix/noah/noah.c | 2 +- keyboards/mechlovin/adelais/rgb_led/rev2/rev2.c | 2 +- keyboards/mechlovin/delphine/rgb_led/rgb_led.c | 2 +- keyboards/mechlovin/hannah60rgb/rev2/rev2.c | 2 +- keyboards/mechlovin/infinity87/rgb_rev1/rgb_rev1.c | 2 +- keyboards/melgeek/mj61/rev1/rev1.c | 2 +- keyboards/melgeek/mj61/rev2/rev2.c | 2 +- keyboards/melgeek/mj63/rev1/rev1.c | 2 +- keyboards/melgeek/mj63/rev2/rev2.c | 2 +- keyboards/melgeek/mj64/rev1/rev1.c | 2 +- keyboards/melgeek/mj64/rev2/rev2.c | 2 +- keyboards/melgeek/mj64/rev3/rev3.c | 2 +- keyboards/melgeek/mj65/rev3/rev3.c | 2 +- keyboards/melgeek/mojo75/rev1/rev1.c | 2 +- keyboards/melgeek/z70ultra/z70ultra.c | 2 +- keyboards/miller/gm862/gm862.c | 2 +- keyboards/moonlander/moonlander.c | 2 +- keyboards/mt64rgb/mt64rgb.c | 2 +- keyboards/mt84/mt84.c | 2 +- keyboards/opendeck/32/rev1/rev1.c | 2 +- keyboards/planck/ez/ez.c | 2 +- keyboards/planck/light/light.c | 2 +- keyboards/terrazzo/terrazzo.c | 2 +- keyboards/tkc/portico/portico.c | 2 +- keyboards/whitefox/whitefox.c | 2 +- keyboards/wilba_tech/wt_rgb_backlight.c | 16 ++++++++-------- keyboards/xbows/knight/knight.c | 2 +- keyboards/xbows/knight_plus/knight_plus.c | 2 +- keyboards/xbows/nature/nature.c | 2 +- keyboards/xbows/numpad/numpad.c | 2 +- keyboards/xbows/woody/woody.c | 2 +- keyboards/xelus/dawn60/rev1_qmk/rev1_qmk.c | 2 +- keyboards/xelus/pachi/rgb/rgb.c | 2 +- tmk_core/common/progmem.h | 1 + 76 files changed, 95 insertions(+), 92 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/feature_led_matrix.md b/docs/feature_led_matrix.md index afa11e7232..7d7971bbed 100644 --- a/docs/feature_led_matrix.md +++ b/docs/feature_led_matrix.md @@ -52,7 +52,7 @@ Here is an example using 2 drivers. Define these arrays listing all the LEDs in your `.c`: ```c -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | LED address diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md index 18e38955ec..670d7e09bf 100644 --- a/docs/feature_rgb_matrix.md +++ b/docs/feature_rgb_matrix.md @@ -52,7 +52,7 @@ Here is an example using 2 drivers. Define these arrays listing all the LEDs in your `.c`: ```c -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -122,7 +122,7 @@ Currently only 4 drivers are supported, but it would be trivial to support all 8 Define these arrays listing all the LEDs in your `.c`: ```c -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -186,7 +186,7 @@ Currently only 2 drivers are supported, but it would be trivial to support all 4 Define these arrays listing all the LEDs in your `.c`: ```c -const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -287,7 +287,7 @@ Here is an example using 2 drivers. Define these arrays listing all the LEDs in your `.c`: ```c -const aw_led g_aw_leds[DRIVER_LED_TOTAL] = { +const aw_led __flash g_aw_leds[DRIVER_LED_TOTAL] = { /* Each AW20216 channel is controlled by a register at some offset between 0x00 * and 0xD7 inclusive. * See drivers/awinic/aw20216.h for the mapping between register offsets and diff --git a/docs/ja/feature_led_matrix.md b/docs/ja/feature_led_matrix.md index f132d716f6..6511b28249 100644 --- a/docs/ja/feature_led_matrix.md +++ b/docs/ja/feature_led_matrix.md @@ -52,7 +52,7 @@ I2C IS31FL3731 RGB コントローラを使ったアドレス指定可能な LED `.c` に全ての LED を列挙する配列を定義します: - const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { + const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* これらの位置については IS31 マニュアルを参照してください * driver * | LED address diff --git a/drivers/led/aw20216.h b/drivers/led/aw20216.h index c55d9605fc..97ac6dc5bf 100644 --- a/drivers/led/aw20216.h +++ b/drivers/led/aw20216.h @@ -18,6 +18,7 @@ #include #include +#include "progmem.h" #include "gpio.h" typedef struct aw_led { @@ -27,7 +28,7 @@ typedef struct aw_led { uint8_t b; } aw_led; -extern const aw_led g_aw_leds[DRIVER_LED_TOTAL]; +extern const aw_led __flash g_aw_leds[DRIVER_LED_TOTAL]; void AW20216_init(pin_t cs_pin, pin_t en_pin); void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); diff --git a/drivers/led/issi/is31fl3731-simple.h b/drivers/led/issi/is31fl3731-simple.h index 9665d6ed35..ecde31eed5 100644 --- a/drivers/led/issi/is31fl3731-simple.h +++ b/drivers/led/issi/is31fl3731-simple.h @@ -20,13 +20,14 @@ #include #include +#include "progmem.h" typedef struct is31_led { uint8_t driver : 2; uint8_t v; } __attribute__((packed)) is31_led; -extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; +extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL]; void IS31FL3731_init(uint8_t addr); void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data); diff --git a/drivers/led/issi/is31fl3731.h b/drivers/led/issi/is31fl3731.h index 19e8e6251f..803ea3ea12 100644 --- a/drivers/led/issi/is31fl3731.h +++ b/drivers/led/issi/is31fl3731.h @@ -19,6 +19,7 @@ #include #include +#include "progmem.h" typedef struct is31_led { uint8_t driver : 2; @@ -27,7 +28,7 @@ typedef struct is31_led { uint8_t b; } __attribute__((packed)) is31_led; -extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; +extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL]; void IS31FL3731_init(uint8_t addr); void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data); diff --git a/drivers/led/issi/is31fl3733.h b/drivers/led/issi/is31fl3733.h index 603d505a13..64fd38eb19 100644 --- a/drivers/led/issi/is31fl3733.h +++ b/drivers/led/issi/is31fl3733.h @@ -20,6 +20,7 @@ #include #include +#include "progmem.h" typedef struct is31_led { uint8_t driver : 2; @@ -28,7 +29,7 @@ typedef struct is31_led { uint8_t b; } __attribute__((packed)) is31_led; -extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; +extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL]; void IS31FL3733_init(uint8_t addr, uint8_t sync); bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data); diff --git a/drivers/led/issi/is31fl3736.h b/drivers/led/issi/is31fl3736.h index e48e31c279..c956c87f7c 100644 --- a/drivers/led/issi/is31fl3736.h +++ b/drivers/led/issi/is31fl3736.h @@ -18,6 +18,7 @@ #include #include +#include "progmem.h" // Simple interface option. // If these aren't defined, just define them to make it compile @@ -37,7 +38,7 @@ typedef struct is31_led { uint8_t b; } __attribute__((packed)) is31_led; -extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; +extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL]; void IS31FL3736_init(uint8_t addr); void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data); diff --git a/drivers/led/issi/is31fl3737.c b/drivers/led/issi/is31fl3737.c index 30906b4840..0bb4ddd425 100644 --- a/drivers/led/issi/is31fl3737.c +++ b/drivers/led/issi/is31fl3737.c @@ -19,7 +19,6 @@ #include "is31fl3737.h" #include "i2c_master.h" #include "wait.h" -#include "progmem.h" // This is a 7-bit address, that gets left-shifted and bit 0 // set to 0 for write, 1 for read (as per I2C protocol) @@ -155,9 +154,7 @@ void IS31FL3737_init(uint8_t addr) { void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { if (index >= 0 && index < DRIVER_LED_TOTAL) { - // copy the led config from progmem to SRAM - is31_led led; - memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); + is31_led led = g_is31_leds[index]; g_pwm_buffer[led.driver][led.r] = red; g_pwm_buffer[led.driver][led.g] = green; @@ -173,9 +170,7 @@ void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { } void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { - // copy the led config from progmem to SRAM - is31_led led; - memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); + is31_led led = g_is31_leds[index]; uint8_t control_register_r = led.r / 8; uint8_t control_register_g = led.g / 8; diff --git a/drivers/led/issi/is31fl3737.h b/drivers/led/issi/is31fl3737.h index a1d2281778..06886e9c9b 100644 --- a/drivers/led/issi/is31fl3737.h +++ b/drivers/led/issi/is31fl3737.h @@ -20,6 +20,7 @@ #include #include +#include "progmem.h" typedef struct is31_led { uint8_t driver : 2; @@ -28,7 +29,7 @@ typedef struct is31_led { uint8_t b; } __attribute__((packed)) is31_led; -extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; +extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL]; void IS31FL3737_init(uint8_t addr); void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data); diff --git a/drivers/led/issi/is31fl3741.h b/drivers/led/issi/is31fl3741.h index 2df0c5b1a7..cea6761ca8 100644 --- a/drivers/led/issi/is31fl3741.h +++ b/drivers/led/issi/is31fl3741.h @@ -21,6 +21,7 @@ #include #include +#include "progmem.h" typedef struct is31_led { uint32_t driver : 2; @@ -29,7 +30,7 @@ typedef struct is31_led { uint32_t b : 10; } __attribute__((packed)) is31_led; -extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; +extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL]; void IS31FL3741_init(uint8_t addr); void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data); diff --git a/keyboards/clueboard/66_hotswap/gen1/gen1.c b/keyboards/clueboard/66_hotswap/gen1/gen1.c index 339bd78d5a..8e3db70d07 100644 --- a/keyboards/clueboard/66_hotswap/gen1/gen1.c +++ b/keyboards/clueboard/66_hotswap/gen1/gen1.c @@ -16,7 +16,7 @@ #include "gen1.h" #ifdef LED_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | LED address diff --git a/keyboards/dp60/dp60.c b/keyboards/dp60/dp60.c index 475084b049..87543b2a1c 100644 --- a/keyboards/dp60/dp60.c +++ b/keyboards/dp60/dp60.c @@ -17,7 +17,7 @@ #include "dp60.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/durgod/dgk6x/galaxy/galaxy.c b/keyboards/durgod/dgk6x/galaxy/galaxy.c index d101533b16..5f793ed85b 100644 --- a/keyboards/durgod/dgk6x/galaxy/galaxy.c +++ b/keyboards/durgod/dgk6x/galaxy/galaxy.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/durgod/dgk6x/hades/hades.c b/keyboards/durgod/dgk6x/hades/hades.c index 21b2722913..3e235683f1 100644 --- a/keyboards/durgod/dgk6x/hades/hades.c +++ b/keyboards/durgod/dgk6x/hades/hades.c @@ -19,7 +19,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/durgod/dgk6x/venus/venus.c b/keyboards/durgod/dgk6x/venus/venus.c index 2d49b34daf..3398acb19d 100644 --- a/keyboards/durgod/dgk6x/venus/venus.c +++ b/keyboards/durgod/dgk6x/venus/venus.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/dztech/dz60rgb/dz60rgb.c b/keyboards/dztech/dz60rgb/dz60rgb.c index aa02fb902d..a7fc3dbc5c 100644 --- a/keyboards/dztech/dz60rgb/dz60rgb.c +++ b/keyboards/dztech/dz60rgb/dz60rgb.c @@ -1,7 +1,7 @@ #include "dz60rgb.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, K_14, J_14, L_14 }, { 0, K_13, J_13, L_13 }, { 0, K_12, J_12, L_12 }, diff --git a/keyboards/dztech/dz60rgb_ansi/dz60rgb_ansi.c b/keyboards/dztech/dz60rgb_ansi/dz60rgb_ansi.c index 4bb5938b66..ab24410912 100644 --- a/keyboards/dztech/dz60rgb_ansi/dz60rgb_ansi.c +++ b/keyboards/dztech/dz60rgb_ansi/dz60rgb_ansi.c @@ -1,7 +1,7 @@ #include "dz60rgb_ansi.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, K_14, J_14, L_14 }, { 0, K_13, J_13, L_13 }, { 0, K_12, J_12, L_12 }, diff --git a/keyboards/dztech/dz60rgb_wkl/dz60rgb_wkl.c b/keyboards/dztech/dz60rgb_wkl/dz60rgb_wkl.c index b57560bacd..455624471d 100644 --- a/keyboards/dztech/dz60rgb_wkl/dz60rgb_wkl.c +++ b/keyboards/dztech/dz60rgb_wkl/dz60rgb_wkl.c @@ -1,7 +1,7 @@ #include "dz60rgb_wkl.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, H_15, G_15, I_15 }, { 0, K_14, J_14, L_14 }, { 0, K_13, J_13, L_13 }, diff --git a/keyboards/dztech/dz65rgb/v1/v1.c b/keyboards/dztech/dz65rgb/v1/v1.c index 8a3f0e27bf..aabe41c313 100644 --- a/keyboards/dztech/dz65rgb/v1/v1.c +++ b/keyboards/dztech/dz65rgb/v1/v1.c @@ -16,7 +16,7 @@ #include "v1.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, C8_8, C7_8, C6_8 }, { 0, C9_8, C7_7, C6_7 }, { 0, C9_7, C8_7, C6_6 }, diff --git a/keyboards/dztech/dz65rgb/v2/v2.c b/keyboards/dztech/dz65rgb/v2/v2.c index 788709d989..dce167c73f 100644 --- a/keyboards/dztech/dz65rgb/v2/v2.c +++ b/keyboards/dztech/dz65rgb/v2/v2.c @@ -16,7 +16,7 @@ #include "v2.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, C8_8, C7_8, C6_8 }, { 0, C9_8, C7_7, C6_7 }, { 0, C9_7, C8_7, C6_6 }, diff --git a/keyboards/dztech/dz65rgb/v3/v3.c b/keyboards/dztech/dz65rgb/v3/v3.c index dec75814ca..c3719bfa51 100755 --- a/keyboards/dztech/dz65rgb/v3/v3.c +++ b/keyboards/dztech/dz65rgb/v3/v3.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS21_SW1, CS20_SW1, CS19_SW1}, {0, CS21_SW2, CS20_SW2, CS19_SW2}, {0, CS21_SW3, CS20_SW3, CS19_SW3}, diff --git a/keyboards/ergodox_ez/ergodox_ez.c b/keyboards/ergodox_ez/ergodox_ez.c index 47dd1b0cc9..7af76cb624 100644 --- a/keyboards/ergodox_ez/ergodox_ez.c +++ b/keyboards/ergodox_ez/ergodox_ez.c @@ -243,7 +243,7 @@ const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { #ifdef RGB_MATRIX_ENABLE // clang-format off -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* driver * | R location * | | G location diff --git a/keyboards/ergodox_infinity/ergodox_infinity.c b/keyboards/ergodox_infinity/ergodox_infinity.c index 76cbca07f8..90aa522427 100644 --- a/keyboards/ergodox_infinity/ergodox_infinity.c +++ b/keyboards/ergodox_infinity/ergodox_infinity.c @@ -191,7 +191,7 @@ const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { #endif #ifdef LED_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { // The numbers in the comments are the led numbers DXX on the PCB /* Refer to IS31 manual for these locations * driver diff --git a/keyboards/exclusive/e6_rgb/e6_rgb.c b/keyboards/exclusive/e6_rgb/e6_rgb.c index 0e248a2023..e0d313f835 100644 --- a/keyboards/exclusive/e6_rgb/e6_rgb.c +++ b/keyboards/exclusive/e6_rgb/e6_rgb.c @@ -11,7 +11,7 @@ void matrix_init_kb(void) { matrix_init_user(); } -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/fallacy/indicators.c b/keyboards/fallacy/indicators.c index 08c500064d..dcf1b4de07 100755 --- a/keyboards/fallacy/indicators.c +++ b/keyboards/fallacy/indicators.c @@ -54,7 +54,7 @@ void set_fallacy_led(int index, bool state) { /* define LED matrix */ -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C1_1}, {0, C2_1}, {0, C3_1}, diff --git a/keyboards/geekboards/tester/tester.c b/keyboards/geekboards/tester/tester.c index 4fab1a7012..0c5c056cc0 100644 --- a/keyboards/geekboards/tester/tester.c +++ b/keyboards/geekboards/tester/tester.c @@ -1,5 +1,5 @@ #include "tester.h" -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/hs60/v1/v1.c b/keyboards/hs60/v1/v1.c index 70c1c2128e..58b19b6408 100644 --- a/keyboards/hs60/v1/v1.c +++ b/keyboards/hs60/v1/v1.c @@ -91,7 +91,7 @@ void raw_hid_receive( uint8_t *data, uint8_t length ) #ifdef HS60_ANSI -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -199,7 +199,7 @@ led_config_t g_led_config = { { #else -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/inett_studio/sqx/hotswap/hotswap.c b/keyboards/inett_studio/sqx/hotswap/hotswap.c index be8985c87e..5b3c8df822 100644 --- a/keyboards/inett_studio/sqx/hotswap/hotswap.c +++ b/keyboards/inett_studio/sqx/hotswap/hotswap.c @@ -20,7 +20,7 @@ #include "hotswap.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/inett_studio/sqx/universal/universal.c b/keyboards/inett_studio/sqx/universal/universal.c index 42d99330d5..dd431a7f45 100644 --- a/keyboards/inett_studio/sqx/universal/universal.c +++ b/keyboards/inett_studio/sqx/universal/universal.c @@ -20,7 +20,7 @@ #include "universal.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/k_type/is31fl3733-dual.h b/keyboards/k_type/is31fl3733-dual.h index 272bcdc417..aab170a3fd 100644 --- a/keyboards/k_type/is31fl3733-dual.h +++ b/keyboards/k_type/is31fl3733-dual.h @@ -28,7 +28,7 @@ typedef struct is31_led { uint8_t b; } __attribute__((packed)) is31_led; -extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; +extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL]; void IS31FL3733_init(uint8_t bus, uint8_t addr, uint8_t sync); bool IS31FL3733_write_register(uint8_t index, uint8_t addr, uint8_t reg, uint8_t data); diff --git a/keyboards/k_type/k_type.c b/keyboards/k_type/k_type.c index 61c5881454..924862277f 100644 --- a/keyboards/k_type/k_type.c +++ b/keyboards/k_type/k_type.c @@ -23,7 +23,7 @@ along with this program. If not, see . #include "is31fl3733-dual.h" -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, B_1, A_1, C_1 }, { 0, B_2, A_2, C_2 }, { 0, B_3, A_3, C_3 }, diff --git a/keyboards/kbdfans/bella/rgb/rgb.c b/keyboards/kbdfans/bella/rgb/rgb.c index e197d9b538..17cf992146 100644 --- a/keyboards/kbdfans/bella/rgb/rgb.c +++ b/keyboards/kbdfans/bella/rgb/rgb.c @@ -15,7 +15,7 @@ */ #include "rgb.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS18_SW1, CS17_SW1, CS16_SW1}, /* RGB6 */ {0, CS18_SW3, CS17_SW3, CS16_SW3}, /* RGB32 */ {0, CS18_SW4, CS17_SW4, CS16_SW4}, /* RGB45 */ diff --git a/keyboards/kbdfans/bella/rgb_iso/rgb_iso.c b/keyboards/kbdfans/bella/rgb_iso/rgb_iso.c index 1a853ac8af..0c431b1543 100644 --- a/keyboards/kbdfans/bella/rgb_iso/rgb_iso.c +++ b/keyboards/kbdfans/bella/rgb_iso/rgb_iso.c @@ -15,7 +15,7 @@ */ #include "rgb_iso.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS18_SW1, CS17_SW1, CS16_SW1}, /* RGB6 */ {0, CS18_SW3, CS17_SW3, CS16_SW3}, /* RGB32 */ {0, CS18_SW4, CS17_SW4, CS16_SW4}, /* RGB45 */ diff --git a/keyboards/kbdfans/kbd67/mkiirgb/mkiirgb.c b/keyboards/kbdfans/kbd67/mkiirgb/mkiirgb.c index 323cb23848..2826c08988 100644 --- a/keyboards/kbdfans/kbd67/mkiirgb/mkiirgb.c +++ b/keyboards/kbdfans/kbd67/mkiirgb/mkiirgb.c @@ -1,6 +1,6 @@ #include "mkiirgb.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C8_8, C7_8, C6_8}, // LA17 {0, C9_8, C7_7, C6_7}, // LA16 diff --git a/keyboards/kbdfans/kbdmini/kbdmini.c b/keyboards/kbdfans/kbdmini/kbdmini.c index 0e0df2f9af..799803c6fa 100644 --- a/keyboards/kbdfans/kbdmini/kbdmini.c +++ b/keyboards/kbdfans/kbdmini/kbdmini.c @@ -1,6 +1,6 @@ #include "kbdmini.h" -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, B_9, A_9, C_9 }, //LA33 { 0, B_10, A_10, C_10 }, //LA37 { 0, B_11, A_11, C_11 }, //LA41 diff --git a/keyboards/kbdfans/maja/maja.c b/keyboards/kbdfans/maja/maja.c index a0afcbda8a..9619a84b48 100755 --- a/keyboards/kbdfans/maja/maja.c +++ b/keyboards/kbdfans/maja/maja.c @@ -1,6 +1,6 @@ #include "maja.h" -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C2_1, C3_1, C4_1}, // LA0 {0, C1_1, C3_2, C4_2}, // LA1 {0, C1_2, C2_2, C4_3}, // LA2 diff --git a/keyboards/keychron/q1/q1.c b/keyboards/keychron/q1/q1.c index 520e6e4c57..6beb96e7eb 100644 --- a/keyboards/keychron/q1/q1.c +++ b/keyboards/keychron/q1/q1.c @@ -33,7 +33,7 @@ bool dip_switch_update_kb(uint8_t index, bool active) { return true; } -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/latin17rgb/latin17rgb.c b/keyboards/latin17rgb/latin17rgb.c index 8d9eaae8ae..f7bbe125c2 100644 --- a/keyboards/latin17rgb/latin17rgb.c +++ b/keyboards/latin17rgb/latin17rgb.c @@ -17,7 +17,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/latin60rgb/latin60rgb.c b/keyboards/latin60rgb/latin60rgb.c index cdd6fed44d..fe5d2eea8f 100644 --- a/keyboards/latin60rgb/latin60rgb.c +++ b/keyboards/latin60rgb/latin60rgb.c @@ -16,7 +16,7 @@ #include "latin60rgb.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, K_13, J_13, L_13 }, { 0, K_12, J_12, L_12 }, { 0, K_11, J_11, L_11 }, diff --git a/keyboards/latin6rgb/latin6rgb.c b/keyboards/latin6rgb/latin6rgb.c index 76b39c86bb..9ea4721ecb 100644 --- a/keyboards/latin6rgb/latin6rgb.c +++ b/keyboards/latin6rgb/latin6rgb.c @@ -17,7 +17,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/matrix/m20add/rgb_ring.c b/keyboards/matrix/m20add/rgb_ring.c index fa70dea7eb..3a8af7cd3b 100644 --- a/keyboards/matrix/m20add/rgb_ring.c +++ b/keyboards/matrix/m20add/rgb_ring.c @@ -30,7 +30,7 @@ #endif // rgb ring leds setting -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/matrix/noah/noah.c b/keyboards/matrix/noah/noah.c index 1e2f4bb7d9..6b624b9e09 100644 --- a/keyboards/matrix/noah/noah.c +++ b/keyboards/matrix/noah/noah.c @@ -66,7 +66,7 @@ __attribute__((weak)) void matrix_scan_user(void) { } #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/mechlovin/adelais/rgb_led/rev2/rev2.c b/keyboards/mechlovin/adelais/rgb_led/rev2/rev2.c index 5087174b96..fe946169a7 100644 --- a/keyboards/mechlovin/adelais/rgb_led/rev2/rev2.c +++ b/keyboards/mechlovin/adelais/rgb_led/rev2/rev2.c @@ -17,7 +17,7 @@ #include "adelais.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C2_1, C3_1, C4_1}, //D102-A0-0 {0, C5_1, C6_1, C7_1}, //D108-A1-1 diff --git a/keyboards/mechlovin/delphine/rgb_led/rgb_led.c b/keyboards/mechlovin/delphine/rgb_led/rgb_led.c index 6f3e3ec731..29b6d1f783 100644 --- a/keyboards/mechlovin/delphine/rgb_led/rgb_led.c +++ b/keyboards/mechlovin/delphine/rgb_led/rgb_led.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { // left CA {0, C5_2, C6_2, C7_2}, //D2-0 {0, C1_1, C3_2, C4_2}, //D20-1 diff --git a/keyboards/mechlovin/hannah60rgb/rev2/rev2.c b/keyboards/mechlovin/hannah60rgb/rev2/rev2.c index 05469a1bbe..8b30538cc2 100644 --- a/keyboards/mechlovin/hannah60rgb/rev2/rev2.c +++ b/keyboards/mechlovin/hannah60rgb/rev2/rev2.c @@ -17,7 +17,7 @@ #include "rev2.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/mechlovin/infinity87/rgb_rev1/rgb_rev1.c b/keyboards/mechlovin/infinity87/rgb_rev1/rgb_rev1.c index f13fee8add..bd1fd8abf7 100644 --- a/keyboards/mechlovin/infinity87/rgb_rev1/rgb_rev1.c +++ b/keyboards/mechlovin/infinity87/rgb_rev1/rgb_rev1.c @@ -17,7 +17,7 @@ #include "rgb_rev1.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS34_SW1, CS35_SW1, CS36_SW1}, //D92-K00-0 {0, CS37_SW1, CS38_SW1, CS39_SW1}, //D94-K01-1 {0, CS31_SW1, CS32_SW1, CS33_SW1}, //D96-K02-2 diff --git a/keyboards/melgeek/mj61/rev1/rev1.c b/keyboards/melgeek/mj61/rev1/rev1.c index be58f57a95..e51c57548f 100644 --- a/keyboards/melgeek/mj61/rev1/rev1.c +++ b/keyboards/melgeek/mj61/rev1/rev1.c @@ -17,7 +17,7 @@ #include "mj61.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS12_SW1, CS11_SW1, CS10_SW1}, /* RGB1 */ {0, CS12_SW2, CS11_SW2, CS10_SW2}, /* RGB2 */ {0, CS12_SW3, CS11_SW3, CS10_SW3}, /* RGB3 */ diff --git a/keyboards/melgeek/mj61/rev2/rev2.c b/keyboards/melgeek/mj61/rev2/rev2.c index 236b003972..3f6b1bff77 100644 --- a/keyboards/melgeek/mj61/rev2/rev2.c +++ b/keyboards/melgeek/mj61/rev2/rev2.c @@ -19,7 +19,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS9_SW1, CS8_SW1, CS7_SW1}, /* RGB1 */ {0, CS9_SW2, CS8_SW2, CS7_SW2}, /* RGB3 */ {0, CS9_SW3, CS8_SW3, CS7_SW3}, /* RGB4 */ diff --git a/keyboards/melgeek/mj63/rev1/rev1.c b/keyboards/melgeek/mj63/rev1/rev1.c index 34ff28d25f..a6ee8859b0 100644 --- a/keyboards/melgeek/mj63/rev1/rev1.c +++ b/keyboards/melgeek/mj63/rev1/rev1.c @@ -19,7 +19,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS9_SW1, CS8_SW1, CS7_SW1}, /* RGB1 */ {0, CS9_SW2, CS8_SW2, CS7_SW2}, /* RGB2 */ {0, CS9_SW3, CS8_SW3, CS7_SW3}, /* RGB3 */ diff --git a/keyboards/melgeek/mj63/rev2/rev2.c b/keyboards/melgeek/mj63/rev2/rev2.c index c2379dabe9..8cacc689ae 100644 --- a/keyboards/melgeek/mj63/rev2/rev2.c +++ b/keyboards/melgeek/mj63/rev2/rev2.c @@ -19,7 +19,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS9_SW1, CS8_SW1, CS7_SW1}, /* RGB1 */ {0, CS9_SW2, CS8_SW2, CS7_SW2}, /* RGB3 */ {0, CS9_SW3, CS8_SW3, CS7_SW3}, /* RGB4 */ diff --git a/keyboards/melgeek/mj64/rev1/rev1.c b/keyboards/melgeek/mj64/rev1/rev1.c index e01765b694..446ba779d0 100644 --- a/keyboards/melgeek/mj64/rev1/rev1.c +++ b/keyboards/melgeek/mj64/rev1/rev1.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS9_SW1, CS8_SW1, CS7_SW1}, /* RGB1 */ {0, CS9_SW2, CS8_SW2, CS7_SW2}, /* RGB2 */ {0, CS9_SW3, CS8_SW3, CS7_SW3}, /* RGB3 */ diff --git a/keyboards/melgeek/mj64/rev2/rev2.c b/keyboards/melgeek/mj64/rev2/rev2.c index d5486b9086..444e8f60ac 100644 --- a/keyboards/melgeek/mj64/rev2/rev2.c +++ b/keyboards/melgeek/mj64/rev2/rev2.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS9_SW1, CS8_SW1, CS7_SW1}, /* RGB1 */ {0, CS9_SW2, CS8_SW2, CS7_SW2}, /* RGB2 */ {0, CS9_SW3, CS8_SW3, CS7_SW3}, /* RGB3 */ diff --git a/keyboards/melgeek/mj64/rev3/rev3.c b/keyboards/melgeek/mj64/rev3/rev3.c index 03ed9fe007..2a1283ec70 100644 --- a/keyboards/melgeek/mj64/rev3/rev3.c +++ b/keyboards/melgeek/mj64/rev3/rev3.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS9_SW1, CS8_SW1, CS7_SW1}, /* RGB1 */ {0, CS9_SW2, CS8_SW2, CS7_SW2}, /* RGB3 */ {0, CS9_SW3, CS8_SW3, CS7_SW3}, /* RGB4 */ diff --git a/keyboards/melgeek/mj65/rev3/rev3.c b/keyboards/melgeek/mj65/rev3/rev3.c index 43fae53a36..6335edd796 100644 --- a/keyboards/melgeek/mj65/rev3/rev3.c +++ b/keyboards/melgeek/mj65/rev3/rev3.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS9_SW1, CS8_SW1, CS7_SW1}, /* RGB1 */ {0, CS9_SW2, CS8_SW2, CS7_SW2}, /* RGB2 */ {0, CS9_SW3, CS8_SW3, CS7_SW3}, /* RGB3 */ diff --git a/keyboards/melgeek/mojo75/rev1/rev1.c b/keyboards/melgeek/mojo75/rev1/rev1.c index 670116fcd6..07ae916da2 100644 --- a/keyboards/melgeek/mojo75/rev1/rev1.c +++ b/keyboards/melgeek/mojo75/rev1/rev1.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS6_SW1, CS5_SW1, CS4_SW1}, /* RGB1 */ {0, CS6_SW2, CS5_SW2, CS4_SW2}, /* RGB2 */ {0, CS6_SW3, CS5_SW3, CS4_SW3}, /* RGB3 */ diff --git a/keyboards/melgeek/z70ultra/z70ultra.c b/keyboards/melgeek/z70ultra/z70ultra.c index 740720681a..552576cd7c 100644 --- a/keyboards/melgeek/z70ultra/z70ultra.c +++ b/keyboards/melgeek/z70ultra/z70ultra.c @@ -18,7 +18,7 @@ #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, CS28_SW1, CS30_SW1, CS29_SW1}, /* RGB10 */ {0, CS28_SW2, CS30_SW2, CS29_SW2}, /* RGB11 */ {0, CS28_SW3, CS30_SW3, CS29_SW3}, /* RGB12 */ diff --git a/keyboards/miller/gm862/gm862.c b/keyboards/miller/gm862/gm862.c index 5af66dd398..d67104838b 100644 --- a/keyboards/miller/gm862/gm862.c +++ b/keyboards/miller/gm862/gm862.c @@ -1,7 +1,7 @@ #include "gm862.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, B_1, A_1, C_1}, {0, B_2, A_2, C_2}, {0, B_3, A_3, C_3}, diff --git a/keyboards/moonlander/moonlander.c b/keyboards/moonlander/moonlander.c index d66b74d973..8688b9efa4 100644 --- a/keyboards/moonlander/moonlander.c +++ b/keyboards/moonlander/moonlander.c @@ -203,7 +203,7 @@ layer_state_t layer_state_set_kb(layer_state_t state) { #ifdef RGB_MATRIX_ENABLE // clang-format off -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/mt64rgb/mt64rgb.c b/keyboards/mt64rgb/mt64rgb.c index 73f0e562e7..a0b8e8da71 100644 --- a/keyboards/mt64rgb/mt64rgb.c +++ b/keyboards/mt64rgb/mt64rgb.c @@ -16,7 +16,7 @@ #include "mt64rgb.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/mt84/mt84.c b/keyboards/mt84/mt84.c index e15a1ff951..9b00aa635e 100644 --- a/keyboards/mt84/mt84.c +++ b/keyboards/mt84/mt84.c @@ -16,7 +16,7 @@ #include "mt84.h" #ifdef RGB_MATRIX_ENABLE -const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/opendeck/32/rev1/rev1.c b/keyboards/opendeck/32/rev1/rev1.c index 297af907e2..91f59a60b7 100644 --- a/keyboards/opendeck/32/rev1/rev1.c +++ b/keyboards/opendeck/32/rev1/rev1.c @@ -17,7 +17,7 @@ #include "rev1.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/planck/ez/ez.c b/keyboards/planck/ez/ez.c index 5c68726a01..27cb2a1d1d 100644 --- a/keyboards/planck/ez/ez.c +++ b/keyboards/planck/ez/ez.c @@ -21,7 +21,7 @@ keyboard_config_t keyboard_config; #ifdef RGB_MATRIX_ENABLE -const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/planck/light/light.c b/keyboards/planck/light/light.c index 896ec44587..3a53e39985 100644 --- a/keyboards/planck/light/light.c +++ b/keyboards/planck/light/light.c @@ -16,7 +16,7 @@ #include "light.h" -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/terrazzo/terrazzo.c b/keyboards/terrazzo/terrazzo.c index e6ba1f6c39..19ac6be491 100644 --- a/keyboards/terrazzo/terrazzo.c +++ b/keyboards/terrazzo/terrazzo.c @@ -21,7 +21,7 @@ #include "print.h" #include "quantum.h" -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * https://cdn-learn.adafruit.com/downloads/pdf/adafruit-15x7-7x15-charlieplex-led-matrix-charliewing-featherwing.pdf */ diff --git a/keyboards/tkc/portico/portico.c b/keyboards/tkc/portico/portico.c index a65a85d242..e3d3a14880 100644 --- a/keyboards/tkc/portico/portico.c +++ b/keyboards/tkc/portico/portico.c @@ -19,7 +19,7 @@ along with this program. If not, see . #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { { 0, C2_1, C3_1, C4_1 }, { 0, C1_1, C3_2, C4_2 }, { 0, C1_2, C2_2, C4_3 }, diff --git a/keyboards/whitefox/whitefox.c b/keyboards/whitefox/whitefox.c index f10f0fd5c4..b17af15127 100644 --- a/keyboards/whitefox/whitefox.c +++ b/keyboards/whitefox/whitefox.c @@ -18,7 +18,7 @@ along with this program. If not, see . #include "whitefox.h" #ifdef LED_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { // The numbers in the comments are the led numbers DXX on the PCB /* Refer to IS31 manual for these locations * driver diff --git a/keyboards/wilba_tech/wt_rgb_backlight.c b/keyboards/wilba_tech/wt_rgb_backlight.c index e0359daab4..77613a3338 100644 --- a/keyboards/wilba_tech/wt_rgb_backlight.c +++ b/keyboards/wilba_tech/wt_rgb_backlight.c @@ -158,7 +158,7 @@ uint32_t g_any_key_hit = 0; // ADDR_2 is not needed. it is here as a dummy #define ISSI_ADDR_1 0x50 -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -239,7 +239,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { #define ISSI_ADDR_1 0x50 #define ISSI_ADDR_2 0x52 -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -382,7 +382,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { // set to 0 for write, 1 for read (as per I2C protocol) #define ISSI_ADDR_1 0x74 -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -414,7 +414,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { #define ISSI_ADDR_2 0x76 // 11101[10] <- SDA #define ISSI_ADDR_3 0x75 // 11101[01] <- SCL -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -541,7 +541,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { #define ISSI_ADDR_1 0x74 #define ISSI_ADDR_2 0x76 -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -622,7 +622,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { #define ISSI_ADDR_1 0x74 #define ISSI_ADDR_2 0x77 -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location @@ -709,7 +709,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { #define ISSI_ADDR_1 0x74 #define ISSI_ADDR_2 -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C1_9, C3_10, C4_10}, // LB1 {0, C1_10, C2_10, C4_11}, // LB2 {0, C1_11, C2_11, C3_11}, // LB3 @@ -729,7 +729,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { #define ISSI_ADDR_1 0x74 #define ISSI_ADDR_2 0x76 -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/xbows/knight/knight.c b/keyboards/xbows/knight/knight.c index 1a1aebf18f..539ecb653e 100644 --- a/keyboards/xbows/knight/knight.c +++ b/keyboards/xbows/knight/knight.c @@ -15,7 +15,7 @@ */ #include "knight.h" #ifdef RGB_MATRIX_ENABLE - const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { + const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C1_3, C2_3, C3_3}, // L01 {0, C1_4, C2_4, C3_4}, // L02 diff --git a/keyboards/xbows/knight_plus/knight_plus.c b/keyboards/xbows/knight_plus/knight_plus.c index f2cf5399b6..c5dd1a5fd6 100644 --- a/keyboards/xbows/knight_plus/knight_plus.c +++ b/keyboards/xbows/knight_plus/knight_plus.c @@ -15,7 +15,7 @@ */ #include "knight_plus.h" #ifdef RGB_MATRIX_ENABLE - const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { + const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C1_3, C2_3, C3_3}, // L01 {0, C1_4, C2_4, C3_4}, // L02 diff --git a/keyboards/xbows/nature/nature.c b/keyboards/xbows/nature/nature.c index f1b0615845..b7b10d5abb 100644 --- a/keyboards/xbows/nature/nature.c +++ b/keyboards/xbows/nature/nature.c @@ -15,7 +15,7 @@ */ #include "nature.h" #ifdef RGB_MATRIX_ENABLE - const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { + const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C1_3, C2_3, C3_3}, // L01 {0, C1_4, C2_4, C3_4}, // L02 diff --git a/keyboards/xbows/numpad/numpad.c b/keyboards/xbows/numpad/numpad.c index 0ab677c769..4e05473b67 100644 --- a/keyboards/xbows/numpad/numpad.c +++ b/keyboards/xbows/numpad/numpad.c @@ -15,7 +15,7 @@ */ #include "numpad.h" #ifdef RGB_MATRIX_ENABLE - const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { + const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C3_3, C2_3, C1_3}, // L01 {0, C3_4, C2_4, C1_4}, // L02 diff --git a/keyboards/xbows/woody/woody.c b/keyboards/xbows/woody/woody.c index 9821f5f655..1027e3ca37 100644 --- a/keyboards/xbows/woody/woody.c +++ b/keyboards/xbows/woody/woody.c @@ -1,6 +1,6 @@ #include "woody.h" #ifdef RGB_MATRIX_ENABLE -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { {0, C8_8, C7_8, C6_8}, // LA17 {0, C9_8, C7_7, C6_7}, // LA16 diff --git a/keyboards/xelus/dawn60/rev1_qmk/rev1_qmk.c b/keyboards/xelus/dawn60/rev1_qmk/rev1_qmk.c index c305ecae14..c6ddef0931 100644 --- a/keyboards/xelus/dawn60/rev1_qmk/rev1_qmk.c +++ b/keyboards/xelus/dawn60/rev1_qmk/rev1_qmk.c @@ -25,7 +25,7 @@ #ifdef RGB_MATRIX_ENABLE LED_TYPE rgb_matrix_ws2812_array[WS2812_LED_TOTAL]; -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/keyboards/xelus/pachi/rgb/rgb.c b/keyboards/xelus/pachi/rgb/rgb.c index ea9812d6b1..6f44b13e12 100644 --- a/keyboards/xelus/pachi/rgb/rgb.c +++ b/keyboards/xelus/pachi/rgb/rgb.c @@ -24,7 +24,7 @@ void matrix_io_delay(void) { __asm__ volatile("nop\nnop\nnop\n"); } #ifdef RGB_MATRIX_ENABLE #include #include "drivers/led/issi/is31fl3741.h" -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { /* Refer to IS31 manual for these locations * driver * | R location diff --git a/tmk_core/common/progmem.h b/tmk_core/common/progmem.h index 3a7a169682..a70d8e299f 100644 --- a/tmk_core/common/progmem.h +++ b/tmk_core/common/progmem.h @@ -5,6 +5,7 @@ #else # include # define PROGMEM +# define __flash # define PSTR(x) x # define PGM_P const char* # define memcpy_P(dest, src, n) memcpy(dest, src, n) -- cgit v1.2.3 From 34de7ca224d613e1ae19a45860e27c15d40254dd Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Tue, 3 Aug 2021 18:49:33 +0100 Subject: Move print/debug files to quantum (#12069) * move print/debug files to quantum * Update comments --- build_test.mk | 2 +- common_features.mk | 10 ++- quantum/logging/debug.c | 25 ++++++ quantum/logging/debug.h | 169 ++++++++++++++++++++++++++++++++++++++++ quantum/logging/nodebug.h | 26 +++++++ quantum/logging/print.c | 27 +++++++ quantum/logging/print.h | 135 ++++++++++++++++++++++++++++++++ quantum/logging/print.mk | 9 +++ quantum/logging/sendchar.c | 20 +++++ quantum/logging/sendchar.h | 33 ++++++++ tmk_core/common.mk | 13 +--- tmk_core/common/debug.c | 25 ------ tmk_core/common/debug.h | 169 ---------------------------------------- tmk_core/common/lib_printf.mk | 9 --- tmk_core/common/nodebug.h | 26 ------- tmk_core/common/print.h | 135 -------------------------------- tmk_core/common/printf.c | 27 ------- tmk_core/common/sendchar.h | 33 -------- tmk_core/common/sendchar_null.c | 19 ----- tmk_core/common/sendchar_uart.c | 23 ------ 20 files changed, 458 insertions(+), 477 deletions(-) create mode 100644 quantum/logging/debug.c create mode 100644 quantum/logging/debug.h create mode 100644 quantum/logging/nodebug.h create mode 100644 quantum/logging/print.c create mode 100644 quantum/logging/print.h create mode 100644 quantum/logging/print.mk create mode 100644 quantum/logging/sendchar.c create mode 100644 quantum/logging/sendchar.h delete mode 100644 tmk_core/common/debug.c delete mode 100644 tmk_core/common/debug.h delete mode 100644 tmk_core/common/lib_printf.mk delete mode 100644 tmk_core/common/nodebug.h delete mode 100644 tmk_core/common/print.h delete mode 100644 tmk_core/common/printf.c delete mode 100644 tmk_core/common/sendchar.h delete mode 100644 tmk_core/common/sendchar_null.c delete mode 100644 tmk_core/common/sendchar_uart.c (limited to 'tmk_core/common') diff --git a/build_test.mk b/build_test.mk index 4c09bf0278..b6b8782174 100644 --- a/build_test.mk +++ b/build_test.mk @@ -63,7 +63,7 @@ endif $(TEST)_SRC += \ tests/test_common/main.c \ $(LIB_PATH)/printf/printf.c \ - $(COMMON_DIR)/printf.c + $(QUANTUM_PATH)/logging/print.c $(TEST_OBJ)/$(TEST)_SRC := $($(TEST)_SRC) $(TEST_OBJ)/$(TEST)_INC := $($(TEST)_INC) $(VPATH) $(GTEST_INC) diff --git a/common_features.mk b/common_features.mk index 75a9e1f2eb..15b7b464fd 100644 --- a/common_features.mk +++ b/common_features.mk @@ -21,7 +21,15 @@ QUANTUM_SRC += \ $(QUANTUM_DIR)/bitwise.c \ $(QUANTUM_DIR)/led.c \ $(QUANTUM_DIR)/keymap_common.c \ - $(QUANTUM_DIR)/keycode_config.c + $(QUANTUM_DIR)/keycode_config.c \ + $(QUANTUM_DIR)/logging/debug.c \ + $(QUANTUM_DIR)/logging/sendchar.c \ + +VPATH += $(QUANTUM_DIR)/logging +# Fall back to lib/printf if there is no platform provided print +ifeq ("$(wildcard $(TMK_PATH)/common/$(PLATFORM_KEY)/printf.mk)","") + include $(QUANTUM_PATH)/logging/print.mk +endif ifeq ($(strip $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), yes) OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE diff --git a/quantum/logging/debug.c b/quantum/logging/debug.c new file mode 100644 index 0000000000..ea62deaa8c --- /dev/null +++ b/quantum/logging/debug.c @@ -0,0 +1,25 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "debug.h" + +debug_config_t debug_config = { + .enable = false, // + .matrix = false, // + .keyboard = false, // + .mouse = false, // + .reserved = 0 // +}; diff --git a/quantum/logging/debug.h b/quantum/logging/debug.h new file mode 100644 index 0000000000..8415310356 --- /dev/null +++ b/quantum/logging/debug.h @@ -0,0 +1,169 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include "print.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Debug output control + */ +typedef union { + struct { + bool enable : 1; + bool matrix : 1; + bool keyboard : 1; + bool mouse : 1; + uint8_t reserved : 4; + }; + uint8_t raw; +} debug_config_t; + +extern debug_config_t debug_config; + +#ifdef __cplusplus +} +#endif + +/* for backward compatibility */ +#define debug_enable (debug_config.enable) +#define debug_matrix (debug_config.matrix) +#define debug_keyboard (debug_config.keyboard) +#define debug_mouse (debug_config.mouse) + +/* + * Debug print utils + */ +#ifndef NO_DEBUG + +# define dprint(s) \ + do { \ + if (debug_enable) print(s); \ + } while (0) +# define dprintln(s) \ + do { \ + if (debug_enable) println(s); \ + } while (0) +# define dprintf(fmt, ...) \ + do { \ + if (debug_enable) xprintf(fmt, ##__VA_ARGS__); \ + } while (0) +# define dmsg(s) dprintf("%s at %d: %s\n", __FILE__, __LINE__, s) + +/* Deprecated. DO NOT USE these anymore, use dprintf instead. */ +# define debug(s) \ + do { \ + if (debug_enable) print(s); \ + } while (0) +# define debugln(s) \ + do { \ + if (debug_enable) println(s); \ + } while (0) +# define debug_msg(s) \ + do { \ + if (debug_enable) { \ + print(__FILE__); \ + print(" at "); \ + print_dec(__LINE__); \ + print(" in "); \ + print(": "); \ + print(s); \ + } \ + } while (0) +# define debug_dec(data) \ + do { \ + if (debug_enable) print_dec(data); \ + } while (0) +# define debug_decs(data) \ + do { \ + if (debug_enable) print_decs(data); \ + } while (0) +# define debug_hex4(data) \ + do { \ + if (debug_enable) print_hex4(data); \ + } while (0) +# define debug_hex8(data) \ + do { \ + if (debug_enable) print_hex8(data); \ + } while (0) +# define debug_hex16(data) \ + do { \ + if (debug_enable) print_hex16(data); \ + } while (0) +# define debug_hex32(data) \ + do { \ + if (debug_enable) print_hex32(data); \ + } while (0) +# define debug_bin8(data) \ + do { \ + if (debug_enable) print_bin8(data); \ + } while (0) +# define debug_bin16(data) \ + do { \ + if (debug_enable) print_bin16(data); \ + } while (0) +# define debug_bin32(data) \ + do { \ + if (debug_enable) print_bin32(data); \ + } while (0) +# define debug_bin_reverse8(data) \ + do { \ + if (debug_enable) print_bin_reverse8(data); \ + } while (0) +# define debug_bin_reverse16(data) \ + do { \ + if (debug_enable) print_bin_reverse16(data); \ + } while (0) +# define debug_bin_reverse32(data) \ + do { \ + if (debug_enable) print_bin_reverse32(data); \ + } while (0) +# define debug_hex(data) debug_hex8(data) +# define debug_bin(data) debug_bin8(data) +# define debug_bin_reverse(data) debug_bin8(data) + +#else /* NO_DEBUG */ + +# define dprint(s) +# define dprintln(s) +# define dprintf(fmt, ...) +# define dmsg(s) +# define debug(s) +# define debugln(s) +# define debug_msg(s) +# define debug_dec(data) +# define debug_decs(data) +# define debug_hex4(data) +# define debug_hex8(data) +# define debug_hex16(data) +# define debug_hex32(data) +# define debug_bin8(data) +# define debug_bin16(data) +# define debug_bin32(data) +# define debug_bin_reverse8(data) +# define debug_bin_reverse16(data) +# define debug_bin_reverse32(data) +# define debug_hex(data) +# define debug_bin(data) +# define debug_bin_reverse(data) + +#endif /* NO_DEBUG */ diff --git a/quantum/logging/nodebug.h b/quantum/logging/nodebug.h new file mode 100644 index 0000000000..0b176684bd --- /dev/null +++ b/quantum/logging/nodebug.h @@ -0,0 +1,26 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#ifndef NO_DEBUG +# define NO_DEBUG +# include "debug.h" +# undef NO_DEBUG +#else +# include "debug.h" +#endif diff --git a/quantum/logging/print.c b/quantum/logging/print.c new file mode 100644 index 0000000000..e8440e55ee --- /dev/null +++ b/quantum/logging/print.c @@ -0,0 +1,27 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include "sendchar.h" + +// bind lib/printf to console interface - sendchar + +static int8_t null_sendchar_func(uint8_t c) { return 0; } +static sendchar_func_t func = null_sendchar_func; + +void print_set_sendchar(sendchar_func_t send) { func = send; } + +void _putchar(char character) { func(character); } diff --git a/quantum/logging/print.h b/quantum/logging/print.h new file mode 100644 index 0000000000..8c055f549e --- /dev/null +++ b/quantum/logging/print.h @@ -0,0 +1,135 @@ +/* Copyright 2012 Jun Wako */ +/* Very basic print functions, intended to be used with usb_debug_only.c + * http://www.pjrc.com/teensy/ + * Copyright (c) 2008 PJRC.COM, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include "util.h" +#include "sendchar.h" +#include "progmem.h" + +void print_set_sendchar(sendchar_func_t func); + +#ifndef NO_PRINT +# if __has_include_next("_print.h") +# include_next "_print.h" /* Include the platforms print.h */ +# else +// Fall back to lib/printf +# include "printf.h" // lib/printf/printf.h + +// Create user & normal print defines +# define print(s) printf(s) +# define println(s) printf(s "\r\n") +# define xprintf printf +# define uprint(s) printf(s) +# define uprintln(s) printf(s "\r\n") +# define uprintf printf + +# endif /* __has_include_next("_print.h") */ +#else /* NO_PRINT */ +# undef xprintf +// Remove print defines +# define print(s) +# define println(s) +# define xprintf(fmt, ...) +# define uprintf(fmt, ...) +# define uprint(s) +# define uprintln(s) + +#endif /* NO_PRINT */ + +#ifdef USER_PRINT +// Remove normal print defines +# undef print +# undef println +# undef xprintf +# define print(s) +# define println(s) +# define xprintf(fmt, ...) +#endif + +#define print_dec(i) xprintf("%u", i) +#define print_decs(i) xprintf("%d", i) +/* hex */ +#define print_hex4(i) xprintf("%X", i) +#define print_hex8(i) xprintf("%02X", i) +#define print_hex16(i) xprintf("%04X", i) +#define print_hex32(i) xprintf("%08lX", i) +/* binary */ +#define print_bin4(i) xprintf("%04b", i) +#define print_bin8(i) xprintf("%08b", i) +#define print_bin16(i) xprintf("%016b", i) +#define print_bin32(i) xprintf("%032lb", i) +#define print_bin_reverse8(i) xprintf("%08b", bitrev(i)) +#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i)) +#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i)) +/* print value utility */ +#define print_val_dec(v) xprintf(#v ": %u\n", v) +#define print_val_decs(v) xprintf(#v ": %d\n", v) +#define print_val_hex8(v) xprintf(#v ": %X\n", v) +#define print_val_hex16(v) xprintf(#v ": %02X\n", v) +#define print_val_hex32(v) xprintf(#v ": %04lX\n", v) +#define print_val_bin8(v) xprintf(#v ": %08b\n", v) +#define print_val_bin16(v) xprintf(#v ": %016b\n", v) +#define print_val_bin32(v) xprintf(#v ": %032lb\n", v) +#define print_val_bin_reverse8(v) xprintf(#v ": %08b\n", bitrev(v)) +#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v)) +#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v)) + +// User print disables the normal print messages in the body of QMK/TMK code and +// is meant as a lightweight alternative to NOPRINT. Use it when you only want to do +// a spot of debugging but lack flash resources for allowing all of the codebase to +// print (and store their wasteful strings). +// +// !!! DO NOT USE USER PRINT CALLS IN THE BODY OF QMK/TMK !!! + +/* decimal */ +#define uprint_dec(i) uprintf("%u", i) +#define uprint_decs(i) uprintf("%d", i) +/* hex */ +#define uprint_hex4(i) uprintf("%X", i) +#define uprint_hex8(i) uprintf("%02X", i) +#define uprint_hex16(i) uprintf("%04X", i) +#define uprint_hex32(i) uprintf("%08lX", i) +/* binary */ +#define uprint_bin4(i) uprintf("%04b", i) +#define uprint_bin8(i) uprintf("%08b", i) +#define uprint_bin16(i) uprintf("%016b", i) +#define uprint_bin32(i) uprintf("%032lb", i) +#define uprint_bin_reverse8(i) uprintf("%08b", bitrev(i)) +#define uprint_bin_reverse16(i) uprintf("%016b", bitrev16(i)) +#define uprint_bin_reverse32(i) uprintf("%032lb", bitrev32(i)) +/* print value utility */ +#define uprint_val_dec(v) uprintf(#v ": %u\n", v) +#define uprint_val_decs(v) uprintf(#v ": %d\n", v) +#define uprint_val_hex8(v) uprintf(#v ": %X\n", v) +#define uprint_val_hex16(v) uprintf(#v ": %02X\n", v) +#define uprint_val_hex32(v) uprintf(#v ": %04lX\n", v) +#define uprint_val_bin8(v) uprintf(#v ": %08b\n", v) +#define uprint_val_bin16(v) uprintf(#v ": %016b\n", v) +#define uprint_val_bin32(v) uprintf(#v ": %032lb\n", v) +#define uprint_val_bin_reverse8(v) uprintf(#v ": %08b\n", bitrev(v)) +#define uprint_val_bin_reverse16(v) uprintf(#v ": %016b\n", bitrev16(v)) +#define uprint_val_bin_reverse32(v) uprintf(#v ": %032lb\n", bitrev32(v)) diff --git a/quantum/logging/print.mk b/quantum/logging/print.mk new file mode 100644 index 0000000000..67c004192d --- /dev/null +++ b/quantum/logging/print.mk @@ -0,0 +1,9 @@ +PRINTF_PATH = $(LIB_PATH)/printf + +VPATH += $(PRINTF_PATH) +SRC += $(PRINTF_PATH)/printf.c +QUANTUM_SRC +=$(QUANTUM_DIR)/logging/print.c +OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_FLOAT +OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL +OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_LONG_LONG +OPT_DEFS += -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T diff --git a/quantum/logging/sendchar.c b/quantum/logging/sendchar.c new file mode 100644 index 0000000000..9422382f6f --- /dev/null +++ b/quantum/logging/sendchar.c @@ -0,0 +1,20 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "sendchar.h" + +/* default noop "null" implementation */ +__attribute__((weak)) int8_t sendchar(uint8_t c) { return 0; } diff --git a/quantum/logging/sendchar.h b/quantum/logging/sendchar.h new file mode 100644 index 0000000000..edcddaa6bb --- /dev/null +++ b/quantum/logging/sendchar.h @@ -0,0 +1,33 @@ +/* +Copyright 2011 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int8_t (*sendchar_func_t)(uint8_t c); + +/* transmit a character. return 0 on success, -1 on error. */ +int8_t sendchar(uint8_t c); + +#ifdef __cplusplus +} +#endif diff --git a/tmk_core/common.mk b/tmk_core/common.mk index 2f8f81126a..bd4142364c 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -8,22 +8,16 @@ TMK_COMMON_SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/action_macro.c \ $(COMMON_DIR)/action_layer.c \ $(COMMON_DIR)/action_util.c \ - $(COMMON_DIR)/debug.c \ - $(COMMON_DIR)/sendchar_null.c \ $(COMMON_DIR)/eeconfig.c \ $(COMMON_DIR)/report.c \ + $(COMMON_DIR)/sync_timer.c \ $(COMMON_DIR)/usb_util.c \ $(PLATFORM_COMMON_DIR)/suspend.c \ $(PLATFORM_COMMON_DIR)/timer.c \ - $(COMMON_DIR)/sync_timer.c \ $(PLATFORM_COMMON_DIR)/bootloader.c \ -# Use platform provided print - fall back to lib/printf -ifneq ("$(wildcard $(TMK_PATH)/$(PLATFORM_COMMON_DIR)/printf.mk)","") - include $(TMK_PATH)/$(PLATFORM_COMMON_DIR)/printf.mk -else - include $(TMK_PATH)/$(COMMON_DIR)/lib_printf.mk -endif +# Use platform provided print if it exists +-include $(TMK_PATH)/$(PLATFORM_COMMON_DIR)/printf.mk SHARED_EP_ENABLE = no MOUSE_SHARED_EP ?= yes @@ -55,6 +49,7 @@ endif ifeq ($(strip $(CONSOLE_ENABLE)), yes) TMK_COMMON_DEFS += -DCONSOLE_ENABLE else + # TODO: decouple this so other print backends can exist TMK_COMMON_DEFS += -DNO_PRINT TMK_COMMON_DEFS += -DNO_DEBUG endif diff --git a/tmk_core/common/debug.c b/tmk_core/common/debug.c deleted file mode 100644 index ea62deaa8c..0000000000 --- a/tmk_core/common/debug.c +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2011 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "debug.h" - -debug_config_t debug_config = { - .enable = false, // - .matrix = false, // - .keyboard = false, // - .mouse = false, // - .reserved = 0 // -}; diff --git a/tmk_core/common/debug.h b/tmk_core/common/debug.h deleted file mode 100644 index 3d2e2315ef..0000000000 --- a/tmk_core/common/debug.h +++ /dev/null @@ -1,169 +0,0 @@ -/* -Copyright 2011 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include -#include "print.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Debug output control - */ -typedef union { - struct { - bool enable : 1; - bool matrix : 1; - bool keyboard : 1; - bool mouse : 1; - uint8_t reserved : 4; - }; - uint8_t raw; -} debug_config_t; - -extern debug_config_t debug_config; - -#ifdef __cplusplus -} -#endif - -/* for backward compatibility */ -#define debug_enable (debug_config.enable) -#define debug_matrix (debug_config.matrix) -#define debug_keyboard (debug_config.keyboard) -#define debug_mouse (debug_config.mouse) - -/* - * Debug print utils - */ -#ifndef NO_DEBUG - -# define dprint(s) \ - do { \ - if (debug_enable) print(s); \ - } while (0) -# define dprintln(s) \ - do { \ - if (debug_enable) println(s); \ - } while (0) -# define dprintf(fmt, ...) \ - do { \ - if (debug_enable) xprintf(fmt, ##__VA_ARGS__); \ - } while (0) -# define dmsg(s) dprintf("%s at %s: %S\n", __FILE__, __LINE__, PSTR(s)) - -/* Deprecated. DO NOT USE these anymore, use dprintf instead. */ -# define debug(s) \ - do { \ - if (debug_enable) print(s); \ - } while (0) -# define debugln(s) \ - do { \ - if (debug_enable) println(s); \ - } while (0) -# define debug_msg(s) \ - do { \ - if (debug_enable) { \ - print(__FILE__); \ - print(" at "); \ - print_dec(__LINE__); \ - print(" in "); \ - print(": "); \ - print(s); \ - } \ - } while (0) -# define debug_dec(data) \ - do { \ - if (debug_enable) print_dec(data); \ - } while (0) -# define debug_decs(data) \ - do { \ - if (debug_enable) print_decs(data); \ - } while (0) -# define debug_hex4(data) \ - do { \ - if (debug_enable) print_hex4(data); \ - } while (0) -# define debug_hex8(data) \ - do { \ - if (debug_enable) print_hex8(data); \ - } while (0) -# define debug_hex16(data) \ - do { \ - if (debug_enable) print_hex16(data); \ - } while (0) -# define debug_hex32(data) \ - do { \ - if (debug_enable) print_hex32(data); \ - } while (0) -# define debug_bin8(data) \ - do { \ - if (debug_enable) print_bin8(data); \ - } while (0) -# define debug_bin16(data) \ - do { \ - if (debug_enable) print_bin16(data); \ - } while (0) -# define debug_bin32(data) \ - do { \ - if (debug_enable) print_bin32(data); \ - } while (0) -# define debug_bin_reverse8(data) \ - do { \ - if (debug_enable) print_bin_reverse8(data); \ - } while (0) -# define debug_bin_reverse16(data) \ - do { \ - if (debug_enable) print_bin_reverse16(data); \ - } while (0) -# define debug_bin_reverse32(data) \ - do { \ - if (debug_enable) print_bin_reverse32(data); \ - } while (0) -# define debug_hex(data) debug_hex8(data) -# define debug_bin(data) debug_bin8(data) -# define debug_bin_reverse(data) debug_bin8(data) - -#else /* NO_DEBUG */ - -# define dprint(s) -# define dprintln(s) -# define dprintf(fmt, ...) -# define dmsg(s) -# define debug(s) -# define debugln(s) -# define debug_msg(s) -# define debug_dec(data) -# define debug_decs(data) -# define debug_hex4(data) -# define debug_hex8(data) -# define debug_hex16(data) -# define debug_hex32(data) -# define debug_bin8(data) -# define debug_bin16(data) -# define debug_bin32(data) -# define debug_bin_reverse8(data) -# define debug_bin_reverse16(data) -# define debug_bin_reverse32(data) -# define debug_hex(data) -# define debug_bin(data) -# define debug_bin_reverse(data) - -#endif /* NO_DEBUG */ diff --git a/tmk_core/common/lib_printf.mk b/tmk_core/common/lib_printf.mk deleted file mode 100644 index 10d2d8468d..0000000000 --- a/tmk_core/common/lib_printf.mk +++ /dev/null @@ -1,9 +0,0 @@ -PRINTF_PATH = $(LIB_PATH)/printf - -TMK_COMMON_SRC += $(PRINTF_PATH)/printf.c -TMK_COMMON_SRC += $(COMMON_DIR)/printf.c -TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_FLOAT -TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL -TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_LONG_LONG -TMK_COMMON_DEFS += -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T -VPATH += $(PRINTF_PATH) diff --git a/tmk_core/common/nodebug.h b/tmk_core/common/nodebug.h deleted file mode 100644 index 0b176684bd..0000000000 --- a/tmk_core/common/nodebug.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#ifndef NO_DEBUG -# define NO_DEBUG -# include "debug.h" -# undef NO_DEBUG -#else -# include "debug.h" -#endif diff --git a/tmk_core/common/print.h b/tmk_core/common/print.h deleted file mode 100644 index 8c055f549e..0000000000 --- a/tmk_core/common/print.h +++ /dev/null @@ -1,135 +0,0 @@ -/* Copyright 2012 Jun Wako */ -/* Very basic print functions, intended to be used with usb_debug_only.c - * http://www.pjrc.com/teensy/ - * Copyright (c) 2008 PJRC.COM, LLC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#pragma once - -#include -#include -#include "util.h" -#include "sendchar.h" -#include "progmem.h" - -void print_set_sendchar(sendchar_func_t func); - -#ifndef NO_PRINT -# if __has_include_next("_print.h") -# include_next "_print.h" /* Include the platforms print.h */ -# else -// Fall back to lib/printf -# include "printf.h" // lib/printf/printf.h - -// Create user & normal print defines -# define print(s) printf(s) -# define println(s) printf(s "\r\n") -# define xprintf printf -# define uprint(s) printf(s) -# define uprintln(s) printf(s "\r\n") -# define uprintf printf - -# endif /* __has_include_next("_print.h") */ -#else /* NO_PRINT */ -# undef xprintf -// Remove print defines -# define print(s) -# define println(s) -# define xprintf(fmt, ...) -# define uprintf(fmt, ...) -# define uprint(s) -# define uprintln(s) - -#endif /* NO_PRINT */ - -#ifdef USER_PRINT -// Remove normal print defines -# undef print -# undef println -# undef xprintf -# define print(s) -# define println(s) -# define xprintf(fmt, ...) -#endif - -#define print_dec(i) xprintf("%u", i) -#define print_decs(i) xprintf("%d", i) -/* hex */ -#define print_hex4(i) xprintf("%X", i) -#define print_hex8(i) xprintf("%02X", i) -#define print_hex16(i) xprintf("%04X", i) -#define print_hex32(i) xprintf("%08lX", i) -/* binary */ -#define print_bin4(i) xprintf("%04b", i) -#define print_bin8(i) xprintf("%08b", i) -#define print_bin16(i) xprintf("%016b", i) -#define print_bin32(i) xprintf("%032lb", i) -#define print_bin_reverse8(i) xprintf("%08b", bitrev(i)) -#define print_bin_reverse16(i) xprintf("%016b", bitrev16(i)) -#define print_bin_reverse32(i) xprintf("%032lb", bitrev32(i)) -/* print value utility */ -#define print_val_dec(v) xprintf(#v ": %u\n", v) -#define print_val_decs(v) xprintf(#v ": %d\n", v) -#define print_val_hex8(v) xprintf(#v ": %X\n", v) -#define print_val_hex16(v) xprintf(#v ": %02X\n", v) -#define print_val_hex32(v) xprintf(#v ": %04lX\n", v) -#define print_val_bin8(v) xprintf(#v ": %08b\n", v) -#define print_val_bin16(v) xprintf(#v ": %016b\n", v) -#define print_val_bin32(v) xprintf(#v ": %032lb\n", v) -#define print_val_bin_reverse8(v) xprintf(#v ": %08b\n", bitrev(v)) -#define print_val_bin_reverse16(v) xprintf(#v ": %016b\n", bitrev16(v)) -#define print_val_bin_reverse32(v) xprintf(#v ": %032lb\n", bitrev32(v)) - -// User print disables the normal print messages in the body of QMK/TMK code and -// is meant as a lightweight alternative to NOPRINT. Use it when you only want to do -// a spot of debugging but lack flash resources for allowing all of the codebase to -// print (and store their wasteful strings). -// -// !!! DO NOT USE USER PRINT CALLS IN THE BODY OF QMK/TMK !!! - -/* decimal */ -#define uprint_dec(i) uprintf("%u", i) -#define uprint_decs(i) uprintf("%d", i) -/* hex */ -#define uprint_hex4(i) uprintf("%X", i) -#define uprint_hex8(i) uprintf("%02X", i) -#define uprint_hex16(i) uprintf("%04X", i) -#define uprint_hex32(i) uprintf("%08lX", i) -/* binary */ -#define uprint_bin4(i) uprintf("%04b", i) -#define uprint_bin8(i) uprintf("%08b", i) -#define uprint_bin16(i) uprintf("%016b", i) -#define uprint_bin32(i) uprintf("%032lb", i) -#define uprint_bin_reverse8(i) uprintf("%08b", bitrev(i)) -#define uprint_bin_reverse16(i) uprintf("%016b", bitrev16(i)) -#define uprint_bin_reverse32(i) uprintf("%032lb", bitrev32(i)) -/* print value utility */ -#define uprint_val_dec(v) uprintf(#v ": %u\n", v) -#define uprint_val_decs(v) uprintf(#v ": %d\n", v) -#define uprint_val_hex8(v) uprintf(#v ": %X\n", v) -#define uprint_val_hex16(v) uprintf(#v ": %02X\n", v) -#define uprint_val_hex32(v) uprintf(#v ": %04lX\n", v) -#define uprint_val_bin8(v) uprintf(#v ": %08b\n", v) -#define uprint_val_bin16(v) uprintf(#v ": %016b\n", v) -#define uprint_val_bin32(v) uprintf(#v ": %032lb\n", v) -#define uprint_val_bin_reverse8(v) uprintf(#v ": %08b\n", bitrev(v)) -#define uprint_val_bin_reverse16(v) uprintf(#v ": %016b\n", bitrev16(v)) -#define uprint_val_bin_reverse32(v) uprintf(#v ": %032lb\n", bitrev32(v)) diff --git a/tmk_core/common/printf.c b/tmk_core/common/printf.c deleted file mode 100644 index e8440e55ee..0000000000 --- a/tmk_core/common/printf.c +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2011 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include -#include "sendchar.h" - -// bind lib/printf to console interface - sendchar - -static int8_t null_sendchar_func(uint8_t c) { return 0; } -static sendchar_func_t func = null_sendchar_func; - -void print_set_sendchar(sendchar_func_t send) { func = send; } - -void _putchar(char character) { func(character); } diff --git a/tmk_core/common/sendchar.h b/tmk_core/common/sendchar.h deleted file mode 100644 index edcddaa6bb..0000000000 --- a/tmk_core/common/sendchar.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2011 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef int8_t (*sendchar_func_t)(uint8_t c); - -/* transmit a character. return 0 on success, -1 on error. */ -int8_t sendchar(uint8_t c); - -#ifdef __cplusplus -} -#endif diff --git a/tmk_core/common/sendchar_null.c b/tmk_core/common/sendchar_null.c deleted file mode 100644 index fb67f70866..0000000000 --- a/tmk_core/common/sendchar_null.c +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2011 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "sendchar.h" - -__attribute__((weak)) int8_t sendchar(uint8_t c) { return 0; } diff --git a/tmk_core/common/sendchar_uart.c b/tmk_core/common/sendchar_uart.c deleted file mode 100644 index 2fc48bafff..0000000000 --- a/tmk_core/common/sendchar_uart.c +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2011 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "uart.h" -#include "sendchar.h" - -int8_t sendchar(uint8_t c) { - uart_putchar(c); - return 0; -} -- cgit v1.2.3 From 7e983796e18e7401c062c158f23966aeb7b1405b Mon Sep 17 00:00:00 2001 From: Pete Sevander Date: Fri, 6 Aug 2021 02:44:57 +0300 Subject: Process combos earlier & overlapping combos (#8591) * Combo processing improvements. Now it is possible to use ModTap and LayerTap keys as part of combos. Overlapping combos also don't trigger all the combos, just exactly the one that you press. New settings: - COMBO_MUST_HOLD_MODS - COMBO_MOD_TERM - COMBO_TERM_PER_COMBO - COMBO_MUST_HOLD_PER_COMBO - COMBO_STRICT_TIMER - COMBO_NO_TIMER * Remove the size flags from combo_t struct boolean members. This in the end actually saves space as the members are accessed so many times. The amount of operations needed to access the bits uses more memory than setting the size saves. * Fix `process_combo_key_release` not called correctly with tap-only combos * Fix not passing a pointer when NO_ACTION_TAPPING is defined. * Docs for `COMBO_ONLY_FROM_LAYER` * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update quantum/process_keycode/process_combo.c Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Add `EXTRA_SHORT_COMBOS` option. Stuff combo's `disabled` and `active` flags into `state`. Possibly can save some space. * Add more examples and clarify things with dict management system. - Simple examples now has a combo that has modifiers included. - The slightly more advanced examples now are actually more advanced instead of just `tap_code16()`. - Added a note that `COMBO_ACTION`s are not needed anymore as you can just use custom keycodes. - Added a note that the `g/keymap_combo.h` macros use the `process_combo_event` function and that it is not usable in one's keymap afterwards. * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Change "the" combo action example to "email" example. * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Fix sneaky infinite loop with `combo_disable()` No need to call `dump_key_buffer` when disabling combos because the buffer is either being dumped if a combo-key was pressed, or the buffer is empty if a non-combo-key is pressed. * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> Co-authored-by: Drashna Jaelre --- docs/config_options.md | 14 +- docs/feature_combo.md | 292 +++++++++++++++-- keyboards/gboards/g/keymap_combo.h | 6 +- quantum/keymap_common.c | 3 + quantum/process_keycode/process_combo.c | 548 ++++++++++++++++++++++++++------ quantum/process_keycode/process_combo.h | 36 ++- quantum/quantum.c | 23 +- tmk_core/common/action.c | 37 ++- tmk_core/common/action.h | 5 + tmk_core/common/action_tapping.c | 41 ++- tmk_core/common/action_tapping.h | 1 + 11 files changed, 841 insertions(+), 165 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/config_options.md b/docs/config_options.md index 0c98b31010..78c1f70fd7 100644 --- a/docs/config_options.md +++ b/docs/config_options.md @@ -188,9 +188,21 @@ If you define these options you will enable the associated feature, which may in few ms of delay from this. But if you're doing chording on something with 3-4ms scan times? You probably want this. * `#define COMBO_COUNT 2` - * Set this to the number of combos that you're using in the [Combo](feature_combo.md) feature. + * Set this to the number of combos that you're using in the [Combo](feature_combo.md) feature. Or leave it undefined and programmatically set the count. * `#define COMBO_TERM 200` * how long for the Combo keys to be detected. Defaults to `TAPPING_TERM` if not defined. +* `#define COMBO_MUST_HOLD_MODS` + * Flag for enabling extending timeout on Combos containing modifers +* `#define COMBO_MOD_TERM 200` + * Allows for extending COMBO_TERM for mod keys while mid-combo. +* `#define COMBO_MUST_HOLD_PER_COMBO` + * Flag to enable per-combo COMBO_TERM extension and `get_combo_must_hold()` function +* `#define COMBO_TERM_PER_COMBO` + * Flag to enable per-combo COMBO_TERM extension and `get_combo_term()` function +* `#define COMBO_STRICT_TIMER` + * Only start the combo timer on the first key press instead of on all key presses. +* `#define COMBO_NO_TIMER` + * Disable the combo timer completely for relaxed combos. * `#define TAP_CODE_DELAY 100` * Sets the delay between `register_code` and `unregister_code`, if you're having issues with it registering properly (common on VUSB boards). The value is in milliseconds. * `#define TAP_HOLD_CAPS_DELAY 80` diff --git a/docs/feature_combo.md b/docs/feature_combo.md index d831328f69..d98e6f2ac7 100644 --- a/docs/feature_combo.md +++ b/docs/feature_combo.md @@ -1,24 +1,39 @@ # Combos -The Combo feature is a chording type solution for adding custom actions. It lets you hit multiple keys at once and produce a different effect. For instance, hitting `A` and `S` within the tapping term would hit `ESC` instead, or have it perform even more complex tasks. +The Combo feature is a chording type solution for adding custom actions. It lets you hit multiple keys at once and produce a different effect. For instance, hitting `A` and `S` within the combo term would hit `ESC` instead, or have it perform even more complex tasks. To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`. -Additionally, in your `config.h`, you'll need to specify the number of combos that you'll be using, by adding `#define COMBO_COUNT 1` (replacing 1 with the number that you're using). - +Additionally, in your `config.h`, you'll need to specify the number of combos that you'll be using, by adding `#define COMBO_COUNT 1` (replacing 1 with the number that you're using). It is also possible to not define this and instead set the variable `COMBO_LEN` yourself. There's a trick where we don't need to think about this variable at all. More on this later. -Also, by default, the tapping term for the Combos is set to the same value as `TAPPING_TERM` (200 by default on most boards). But you can specify a different value by defining it in your `config.h`. For instance: `#define COMBO_TERM 300` would set the time out period for combos to 300ms. -Then, your `keymap.c` file, you'll need to define a sequence of keys, terminated with `COMBO_END`, and a structure to list the combination of keys, and it's resulting action. +Then, in your `keymap.c` file, you'll need to define a sequence of keys, terminated with `COMBO_END`, and a structure to list the combination of keys, and its resulting action. ```c -const uint16_t PROGMEM test_combo[] = {KC_A, KC_B, COMBO_END}; -combo_t key_combos[COMBO_COUNT] = {COMBO(test_combo, KC_ESC)}; +const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END}; +const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END}; +combo_t key_combos[COMBO_COUNT] = { + COMBO(test_combo1, KC_ESC), + COMBO(test_combo2, LCTL(KC_Z)), // keycodes with modifiers are possible too! +}; ``` -This will send "Escape" if you hit the A and B keys. +This will send "Escape" if you hit the A and B keys, and Ctrl+Z when you hit the C and D keys. + +As of [PR#8591](https://github.com/qmk/qmk_firmware/pull/8591/), it is possible to fire combos from ModTap keys and LayerTap keys. So in the above example you could have keys `LSFT_T(KC_A)` and `LT(_LAYER, KC_B)` and it would work. So Home Row Mods and Home Row Combos at same time is now a thing! -!> This method only supports [basic keycodes](keycodes_basic.md). See the examples for more control. +It is also now possible to overlap combos. Before, with the example below both combos would activate when all three keys were pressed. Now only the three key combo will activate. + +```c +const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(_LAYER, KC_B), COMBO_END}; +const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(_LAYER, KC_B), KC_C, COMBO_END}; +combo_t key_combos[COMBO_COUNT] = { + COMBO(test_combo1, KC_ESC) + COMBO(test_combo2, KC_TAB) +}; +``` + +Executing more complex keycodes like ModTaps and LayerTaps is now also possible. ## Examples @@ -27,63 +42,68 @@ If you want to add a list, then you'd use something like this: ```c enum combos { AB_ESC, - JK_TAB + JK_TAB, + QW_SFT, + SD_LAYER, }; const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END}; +const uint16_t PROGMEM qw_combo[] = {KC_Q, KC_W, COMBO_END}; +const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END}; combo_t key_combos[COMBO_COUNT] = { [AB_ESC] = COMBO(ab_combo, KC_ESC), - [JK_TAB] = COMBO(jk_combo, KC_TAB) + [JK_TAB] = COMBO(jk_combo, KC_TAB), + [QW_SFT] = COMBO(qw_combo, KC_LSFT) + [SD_LAYER] = COMBO(layer_combo, MO(_LAYER)), }; ``` For a more complicated implementation, you can use the `process_combo_event` function to add custom handling. +Additionally, this example shows how you can leave `COMBO_COUNT` undefined. ```c enum combo_events { - ZC_COPY, - XV_PASTE + EM_EMAIL, + BSPC_LSFT_CLEAR, + COMBO_LENGTH }; +uint16_t COMBO_LEN = COMBO_LENGTH; // remove the COMBO_COUNT define and use this instead! -const uint16_t PROGMEM copy_combo[] = {KC_Z, KC_C, COMBO_END}; -const uint16_t PROGMEM paste_combo[] = {KC_X, KC_V, COMBO_END}; +const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END}; +const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END}; -combo_t key_combos[COMBO_COUNT] = { - [ZC_COPY] = COMBO_ACTION(copy_combo), - [XV_PASTE] = COMBO_ACTION(paste_combo), +combo_t key_combos[] = { + [EM_EMAIL] = COMBO_ACTION(email_combo), + [BSPC_LSFT_CLEAR] = COMBO_ACTION(clear_line_combo), }; +/* COMBO_ACTION(x) is same as COMBO(x, KC_NO) */ void process_combo_event(uint16_t combo_index, bool pressed) { switch(combo_index) { - case ZC_COPY: + case EM_EMAIL: if (pressed) { - tap_code16(LCTL(KC_C)); + SEND_STRING("john.doe@example.com"); } break; - case XV_PASTE: + case BSPC_LSFT_CLEAR: if (pressed) { - tap_code16(LCTL(KC_V)); + tap_code16(KC_END); + tap_code16(S(KC_HOME)); + tap_code16(KC_BSPC); } break; } } ``` -This will send Ctrl+C if you hit Z and C, and Ctrl+V if you hit X and V. But you could change this to do stuff like change layers, play sounds, or change settings. - -## Additional Configuration +This will send "john.doe@example.com" if you chord E and M together, and clear the current line with Backspace and Left-Shift. You could change this to do stuff like play sounds or change settings. -If you're using long combos, or even longer combos, you may run into issues with this, as the structure may not be large enough to accommodate what you're doing. +It is worth noting that `COMBO_ACTION`s are not needed anymore. As of [PR#8591](https://github.com/qmk/qmk_firmware/pull/8591/), it is possible to run your own custom keycodes from combos. Just define the custom keycode, program its functionality in `process_record_user`, and define a combo with `COMBO(, )`. -In this case, you can add either `#define EXTRA_LONG_COMBOS` or `#define EXTRA_EXTRA_LONG_COMBOS` in your `config.h` file. - -You may also be able to enable action keys by defining `COMBO_ALLOW_ACTION_KEYS`. - -## Keycodes - -You can enable, disable and toggle the Combo feature on the fly. This is useful if you need to disable them temporarily, such as for a game. +## Keycodes +You can enable, disable and toggle the Combo feature on the fly. This is useful if you need to disable them temporarily, such as for a game. The following keycodes are available for use in your `keymap.c` |Keycode |Description | |----------|---------------------------------| @@ -91,6 +111,187 @@ You can enable, disable and toggle the Combo feature on the fly. This is useful |`CMB_OFF` |Turns off Combo feature | |`CMB_TOG` |Toggles Combo feature on and off | +# Advanced Configuration +These configuration settings can be set in your `config.h` file. + +## Combo Term +By default, the timeout for the Combos to be recognized is set to 50ms. This can be changed if accidental combo misfires are happening or if you're having difficulties pressing keys at the same time. For instance, `#define COMBO_TERM 40` would set the timeout period for combos to 40ms. + +## Buffer and state sizes +If you're using long combos, or you have a lot of overlapping combos, you may run into issues with this, as the buffers may not be large enough to accommodate what you're doing. In this case, you can configure the sizes of the buffers used. Be aware, larger combo sizes and larger buffers will increase memory usage! + +To configure the amount of keys a combo can be composed of, change the following: + +| Keys | Define to be set | +|------|-----------------------------------| +| 6 | `#define EXTRA_SHORT_COMBOS` | +| 8 | QMK Default | +| 16 | `#define EXTRA_LONG_COMBOS` | +| 32 | `#define EXTRA_EXTRA_LONG_COMBOS` | + +Defining `EXTRA_SHORT_COMBOS` combines a combo's internal state into just one byte. This can, in some cases, save some memory. If it doesn't, no point using it. If you do, you also have to make sure you don't define combos with more than 6 keys. + +Processing combos has two buffers, one for the key presses, another for the combos being activated. Use the following options to configure the sizes of these buffers: + +| Define | Default | +|-------------------------------------|------------------------------------------------------| +| `#define COMBO_KEY_BUFFER_LENGTH 8` | 8 (the key amount `(EXTRA_)EXTRA_LONG_COMBOS` gives) | +| `#define COMBO_BUFFER_LENGTH 4` | 4 | + +## Modifier Combos +If a combo resolves to a Modifier, the window for processing the combo can be extended independently from normal combos. By default, this is disabled but can be enabled with `#define COMBO_MUST_HOLD_MODS`, and the time window can be configured with `#define COMBO_HOLD_TERM 150` (default: `TAPPING_TERM`). With `COMBO_MUST_HOLD_MODS`, you cannot tap the combo any more which makes the combo less prone to misfires. + +## Per Combo Timing, Holding and Tapping +For each combo, it is possible to configure the time window it has to pressed in, if it needs to be held down, or if it needs to be tapped. + +For example, tap-only combos are useful if any (or all) of the underlying keys is a Mod-Tap or a Layer-Tap key. When you tap the combo, you get the combo result. When you press the combo and hold it down, the combo doesn't actually activate. Instead the keys are processed separately as if the combo wasn't even there. + +In order to use these features, the following configuration options and functions need to be defined. Coming up with useful timings and configuration is left as an exercise for the reader. + +| Config Flag | Function | Description | +|-----------------------------|-----------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| `COMBO_TERM_PER_COMBO` | uint16_t get_combo_term(uint16_t index, combo_t \*combo) | Optional per-combo timeout window. (default: `COMBO_TERM`) | +| `COMBO_MUST_HOLD_PER_COMBO` | bool get_combo_must_hold(uint16_t index, combo_t \*combo) | Controls if a given combo should fire immediately on tap or if it needs to be held. (default: `false`) | +| `COMBO_MUST_TAP_PER_COMBO` | bool get_combo_must_tap(uint16_t index, combo_t \*combo) | Controls if a given combo should fire only if tapped within `COMBO_HOLD_TERM`. (default: `false`) | + +Examples: +```c +uint16_t get_combo_term(uint16_t index, combo_t *combo) { + // decide by combo->keycode + switch (combo->keycode) { + case KC_X: + return 50; + } + + // or with combo index, i.e. its name from enum. + switch (index) { + case COMBO_NAME_HERE: + return 9001; + } + + // And if you're feeling adventurous, you can even decide by the keys in the chord, + // i.e. the exact array of keys you defined for the combo. + // This can be useful if your combos have a common key and you want to apply the + // same combo term for all of them. + if (combo->keys[0] == KC_ENTER) { // if first key in the array is KC_ENTER + return 150; + } + + return COMBO_TERM; +} + +bool get_combo_must_hold(uint16_t index, combo_t *combo) { + // Same as above, decide by keycode, the combo index, or by the keys in the chord. + + if (KEYCODE_IS_MOD(combo->keycode) || + (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX) // MO(kc) keycodes + ) { + return true; + } + + switch (index) { + case COMBO_NAME_HERE: + return true; + } + + return false; +} + +bool get_combo_must_tap(uint16_t index, combo_t *combo) { + // If you want all combos to be tap-only, just uncomment the next line + // return true + + // If you want *all* combos, that have Mod-Tap/Layer-Tap/Momentary keys in its chord, to be tap-only, this is for you: + uint16_t key; + uint8_t idx = 0; + while ((key = pgm_read_word(&combo->keys[idx])) != COMBO_END) { + switch (key) { + case QK_MOD_TAP...QK_MOD_TAP_MAX: + case QK_LAYER_TAP...QK_LAYER_TAP_MAX: + case QK_MOMENTARY...QK_MOMENTARY_MAX: + return true; + } + idx += 1; + } + return false; + +} +``` + +## Variable Length Combos +If you leave `COMBO_COUNT` undefined in `config.h`, it allows you to programmatically declare the size of the Combo data structure and avoid updating `COMBO_COUNT`. Instead a variable called `COMBO_LEN` has to be set. It can be set with something similar to the following in `keymap.c`: `uint16_t COMBO_LEN = sizeof(key_combos) / sizeof(key_combos[0]);` or by adding `COMBO_LENGTH` as the *last* entry in the combo enum and then `uint16_t COMBO_LEN = COMBO_LENGTH;` as such: +```c +enum myCombos { + ..., + COMBO_LENGTH +}; +uint16_t COMBO_LEN = COMBO_LENGTH; +``` +Regardless of the method used to declare `COMBO_LEN`, this also requires to convert the `combo_t key_combos[COMBO_COUNT] = {...};` line to `combo_t key_combos[] = {...};`. + + +## Combo timer + +Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`. +Inputting combos is relaxed like this, but also slightly more prone to accidental misfires. + +The next two options alter the behaviour of the timer. + +### `#define COMBO_STRICT_TIMER` + +With `COMBO_STRICT_TIMER`, the timer is started only on the first key press. +Inputting combos is now less relaxed; you need to make sure the full chord is pressed within the `COMBO_TERM`. +Misfires are less common but if you type multiple combos fast, there is a +chance that the latter ones might not activate properly. + +### `#define COMBO_NO_TIMER` + +By defining `COMBO_NO_TIMER`, the timer is disabled completely and combos are activated on the first key release. +This also disables the "must hold" functionalities as they just wouldn't work at all. + +## Customizable key releases + +By defining `COMBO_PROCESS_KEY_RELEASE` and implementing the function `bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode)`, you can run your custom code on each key release after a combo was activated. For example you could change the RGB colors, activate haptics, or alter the modifiers. + +You can also release a combo early by returning `true` from the function. + +Here's an example where a combo resolves to two modifiers, and on key releases the modifiers are unregistered one by one, depending on which key was released. + +```c +enum combos { + AB_MODS, + COMBO_LENGTH +}; +uint16_t COMBO_LEN = COMBO_LENGTH; + +const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; + +combo_t key_combos[] = { + [AB_MODS] = COMBO(ab_combo, LCTL(KC_LSFT)), +}; + +bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { + switch (combo_index) { + case AB_MODS: + switch(keycode) { + case KC_A: + unregister_mods(MOD_MASK_CTRL); + break; + case KC_B: + unregister_mods(MOD_MASK_SHIFT); + break; + } + return false; // do not release combo + } + return false; +} +``` +## Layer independent combos + +If you, for example, use multiple base layers for different key layouts, one for QWERTY, and another one for Colemak, you might want your combos to work from the same key positions on all layers. Defining the same combos again for another layout is redundant and takes more memory. The solution is to just check the keycodes from one layer. + +With `#define COMBO_ONLY_FROM_LAYER _LAYER_A` the combos' keys are always checked from layer `_LAYER_A` even though the active layer would be `_LAYER_B`. + ## User callbacks In addition to the keycodes, there are a few functions that you can use to set the status, or check it: @@ -101,3 +302,28 @@ In addition to the keycodes, there are a few functions that you can use to set t | `combo_disable()` | Disables the combo feature, and clears the combo buffer | | `combo_toggle()` | Toggles the state of the combo feature | | `is_combo_enabled()` | Returns the status of the combo feature state (true or false) | + + +# Dictionary Management + +Having 3 places to update when adding new combos or altering old ones does become cumbersome when you have a lot of combos. We can alleviate this with some magic! ... If you consider C macros magic. +First, you need to add `VPATH += keyboards/gboards` to your `rules.mk`. Next, include the file `g/keymap_combo.h` in your `keymap.c`. + +!> This functionality uses the same `process_combo_event` function as `COMBO_ACTION` macros do, so you cannot use the function yourself in your keymap. Instead, you have to define the `case`s of the `switch` statement by themselves within `inject.h`, which `g/keymap_combo.h` will then include into the function. + +Then, write your combos in `combos.def` file in the following manner: + +```c +// name result chord keys +COMB(AB_ESC, KC_ESC, KC_A, KC_B) +COMB(JK_TAB, KC_TAB, KC_J, KC_K) +COMB(JKL_SPC, KC_SPC, KC_J, KC_K, KC_L) +COMB(BSSL_CLR, KC_NO, KC_BSPC, KC_LSFT) // using KC_NO as the resulting keycode is the same as COMBO_ACTION before. +COMB(QW_UNDO, C(KC_Z), KC_Q, KC_W) +SUBS(TH_THE, "the", KC_T, KC_H) // SUBS uses SEND_STRING to output the given string. +... +``` + +Now, you can update only one place to add or alter combos. You don't even need to remember to update the `COMBO_COUNT` or the `COMBO_LEN` variables at all. Everything is taken care of. Magic! + +For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/. diff --git a/keyboards/gboards/g/keymap_combo.h b/keyboards/gboards/g/keymap_combo.h index 58e99863ea..b92b6a4bc4 100644 --- a/keyboards/gboards/g/keymap_combo.h +++ b/keyboards/gboards/g/keymap_combo.h @@ -28,7 +28,10 @@ #define TOGG A_ENUM enum combos { #include "combos.def" + COMBO_LENGTH }; +// Export length to combo module +uint16_t COMBO_LEN = COMBO_LENGTH; // Bake combos into mem #undef COMB @@ -53,9 +56,6 @@ combo_t key_combos[] = { #undef SUBS #undef TOGG -// Export length to combo module -int COMBO_LEN = sizeof(key_combos) / sizeof(key_combos[0]); - // Fill QMK hook #define COMB BLANK #define SUBS A_ACTI diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c index e0fd6d4793..780c71ab9b 100644 --- a/quantum/keymap_common.c +++ b/quantum/keymap_common.c @@ -40,7 +40,10 @@ extern keymap_config_t keymap_config; action_t action_for_key(uint8_t layer, keypos_t key) { // 16bit keycodes - important uint16_t keycode = keymap_key_to_keycode(layer, key); + return action_for_keycode(keycode); +}; +action_t action_for_keycode(uint16_t keycode) { // keycode remapping keycode = keycode_config(keycode); diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c index 9e16292486..b4e698decd 100644 --- a/quantum/process_keycode/process_combo.c +++ b/quantum/process_keycode/process_combo.c @@ -16,114 +16,445 @@ #include "print.h" #include "process_combo.h" +#include "action_tapping.h" -#ifndef COMBO_VARIABLE_LEN -__attribute__((weak)) combo_t key_combos[COMBO_COUNT] = {}; + +#ifdef COMBO_COUNT +__attribute__((weak)) combo_t key_combos[COMBO_COUNT]; +uint16_t COMBO_LEN = COMBO_COUNT; #else extern combo_t key_combos[]; -extern int COMBO_LEN; +extern uint16_t COMBO_LEN; #endif __attribute__((weak)) void process_combo_event(uint16_t combo_index, bool pressed) {} -static uint16_t timer = 0; -static uint16_t current_combo_index = 0; -static bool drop_buffer = false; -static bool is_active = false; -static bool b_combo_enable = true; // defaults to enabled +#ifdef COMBO_MUST_HOLD_PER_COMBO +__attribute__((weak)) bool get_combo_must_hold(uint16_t index, combo_t *combo) { return false; } +#endif + +#ifdef COMBO_MUST_TAP_PER_COMBO +__attribute__((weak)) bool get_combo_must_tap(uint16_t index, combo_t *combo) { return false; } +#endif -static uint8_t buffer_size = 0; -#ifdef COMBO_ALLOW_ACTION_KEYS -static keyrecord_t key_buffer[MAX_COMBO_LENGTH]; +#ifdef COMBO_TERM_PER_COMBO +__attribute__((weak)) uint16_t get_combo_term(uint16_t index, combo_t *combo) { return COMBO_TERM; } +#endif + +#ifdef COMBO_PROCESS_KEY_RELEASE +__attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { return false; } +#endif + +#ifndef COMBO_NO_TIMER +static uint16_t timer = 0; +#endif +static bool b_combo_enable = true; // defaults to enabled +static uint16_t longest_term = 0; + +typedef struct { + keyrecord_t record; + uint16_t combo_index; + uint16_t keycode; +} queued_record_t; +static uint8_t key_buffer_size = 0; +static queued_record_t key_buffer[COMBO_KEY_BUFFER_LENGTH]; + +typedef struct { + uint16_t combo_index; +} queued_combo_t; +static uint8_t combo_buffer_write= 0; +static uint8_t combo_buffer_read = 0; +static queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH]; + +#define INCREMENT_MOD(i) i = (i + 1) % COMBO_BUFFER_LENGTH + +#define COMBO_KEY_POS ((keypos_t){.col=254, .row=254}) + + +#ifndef EXTRA_SHORT_COMBOS +/* flags are their own elements in combo_t struct. */ +# define COMBO_ACTIVE(combo) (combo->active) +# define COMBO_DISABLED(combo) (combo->disabled) +# define COMBO_STATE(combo) (combo->state) + +# define ACTIVATE_COMBO(combo) do {combo->active = true;}while(0) +# define DEACTIVATE_COMBO(combo) do {combo->active = false;}while(0) +# define DISABLE_COMBO(combo) do {combo->disabled = true;}while(0) +# define RESET_COMBO_STATE(combo) do { \ + combo->disabled = false; \ + combo->state = 0; \ +}while(0) #else -static uint16_t key_buffer[MAX_COMBO_LENGTH]; +/* flags are at the two high bits of state. */ +# define COMBO_ACTIVE(combo) (combo->state & 0x80) +# define COMBO_DISABLED(combo) (combo->state & 0x40) +# define COMBO_STATE(combo) (combo->state & 0x3F) + +# define ACTIVATE_COMBO(combo) do {combo->state |= 0x80;}while(0) +# define DEACTIVATE_COMBO(combo) do {combo->state &= ~0x80;}while(0) +# define DISABLE_COMBO(combo) do {combo->state |= 0x40;}while(0) +# define RESET_COMBO_STATE(combo) do {combo->state &= ~0x7F;}while(0) #endif -static inline void send_combo(uint16_t action, bool pressed) { - if (action) { - if (pressed) { - register_code16(action); - } else { - unregister_code16(action); - } +static inline void release_combo(uint16_t combo_index, combo_t *combo) { + if (combo->keycode) { + keyrecord_t record = { + .event = { + .key = COMBO_KEY_POS, + .time = timer_read()|1, + .pressed = false, + }, + .keycode = combo->keycode, + }; +#ifndef NO_ACTION_TAPPING + action_tapping_process(record); +#else + process_record(&record); +#endif } else { - process_combo_event(current_combo_index, pressed); + process_combo_event(combo_index, false); + } + DEACTIVATE_COMBO(combo); +} + +static inline bool _get_combo_must_hold(uint16_t combo_index, combo_t *combo) { +#ifdef COMBO_NO_TIMER + return false; +#elif defined(COMBO_MUST_HOLD_PER_COMBO) + return get_combo_must_hold(combo_index, combo); +#elif defined(COMBO_MUST_HOLD_MODS) + return (KEYCODE_IS_MOD(combo->keycode) || + (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX)); +#endif + return false; +} + +static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo ) { + if (_get_combo_must_hold(combo_index, combo) +#ifdef COMBO_MUST_TAP_PER_COMBO + || get_combo_must_tap(combo_index, combo) +#endif + ) { + if (longest_term < COMBO_HOLD_TERM) { + return COMBO_HOLD_TERM; + } + } + + return longest_term; +} + +static inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) { + +#if defined(COMBO_TERM_PER_COMBO) + return get_combo_term(combo_index, combo); +#endif + + return COMBO_TERM; +} + +void clear_combos(void) { + uint16_t index = 0; + longest_term = 0; + for (index = 0; index < COMBO_LEN; ++index) { + combo_t *combo = &key_combos[index]; + if (!COMBO_ACTIVE(combo)) { + RESET_COMBO_STATE(combo); + } } } -static inline void dump_key_buffer(bool emit) { - if (buffer_size == 0) { +static inline void dump_key_buffer(void) { + if (key_buffer_size == 0) { return; } - if (emit) { - for (uint8_t i = 0; i < buffer_size; i++) { -#ifdef COMBO_ALLOW_ACTION_KEYS - const action_t action = store_or_get_action(key_buffer[i].event.pressed, key_buffer[i].event.key); - process_action(&(key_buffer[i]), action); + for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) { + + queued_record_t *qrecord = &key_buffer[key_buffer_i]; + keyrecord_t *record = &qrecord->record; + + if (IS_NOEVENT(record->event)) { + continue; + } + + if (!record->keycode && qrecord->combo_index != (uint16_t)-1) { + process_combo_event(qrecord->combo_index, true); + } else { +#ifndef NO_ACTION_TAPPING + action_tapping_process(*record); #else - register_code16(key_buffer[i]); - send_keyboard_report(); + process_record(record); #endif } + record->event.time = 0; } - buffer_size = 0; + key_buffer_size = 0; } -#define ALL_COMBO_KEYS_ARE_DOWN (((1 << count) - 1) == combo->state) -#define KEY_STATE_DOWN(key) \ - do { \ - combo->state |= (1 << key); \ +#define NO_COMBO_KEYS_ARE_DOWN (0 == COMBO_STATE(combo)) +#define ALL_COMBO_KEYS_ARE_DOWN(state, key_count) (((1 << key_count) - 1) == state) +#define ONLY_ONE_KEY_IS_DOWN(state) !(state & (state - 1)) +#define KEY_NOT_YET_RELEASED(state, key_index) ((1 << key_index) & state) +#define KEY_STATE_DOWN(state, key_index) \ + do { \ + state |= (1 << key_index); \ } while (0) -#define KEY_STATE_UP(key) \ - do { \ - combo->state &= ~(1 << key); \ +#define KEY_STATE_UP(state, key_index) \ + do { \ + state &= ~(1 << key_index); \ } while (0) -static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) { - uint8_t count = 0; - uint16_t index = -1; - /* Find index of keycode and number of combo keys */ - for (const uint16_t *keys = combo->keys;; ++count) { - uint16_t key = pgm_read_word(&keys[count]); - if (keycode == key) index = count; +static inline void _find_key_index_and_count(const uint16_t *keys, uint16_t keycode, uint16_t *key_index, uint8_t *key_count) { + while (true) { + uint16_t key = pgm_read_word(&keys[*key_count]); + if (keycode == key) *key_index = *key_count; if (COMBO_END == key) break; + (*key_count)++; + } +} + +void drop_combo_from_buffer(uint16_t combo_index) { + /* Mark a combo as processed from the buffer. If the buffer is in the + * beginning of the buffer, drop it. */ + uint8_t i = combo_buffer_read; + while (i != combo_buffer_write) { + queued_combo_t *qcombo = &combo_buffer[i]; + + if (qcombo->combo_index == combo_index) { + combo_t *combo = &key_combos[combo_index]; + DISABLE_COMBO(combo); + + if (i == combo_buffer_read) { + INCREMENT_MOD(combo_buffer_read); + } + break; + } + INCREMENT_MOD(i); } +} + +void apply_combo(uint16_t combo_index, combo_t *combo) { + /* Apply combo's result keycode to the last chord key of the combo and + * disable the other keys. */ - /* Continue processing if not a combo key */ - if (-1 == (int8_t)index) return false; + if (COMBO_DISABLED(combo)) { return; } - bool is_combo_active = is_active; + // state to check against so we find the last key of the combo from the buffer +#if defined(EXTRA_EXTRA_LONG_COMBOS) + uint32_t state = 0; +#elif defined(EXTRA_LONG_COMBOS) + uint16_t state = 0; +#else + uint8_t state = 0; +#endif + + for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) { + + queued_record_t *qrecord = &key_buffer[key_buffer_i]; + keyrecord_t *record = &qrecord->record; + uint16_t keycode = qrecord->keycode; + + uint8_t key_count = 0; + uint16_t key_index = -1; + _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count); + + if (-1 == (int16_t)key_index) { + // key not part of this combo + continue; + } - if (record->event.pressed) { - KEY_STATE_DOWN(index); + KEY_STATE_DOWN(state, key_index); + if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) { + // this in the end executes the combo when the key_buffer is dumped. + record->keycode = combo->keycode; + record->event.key = COMBO_KEY_POS; - if (is_combo_active) { - if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */ - send_combo(combo->keycode, true); - drop_buffer = true; + qrecord->combo_index = combo_index; + ACTIVATE_COMBO(combo); + + break; + } else { + // key was part of the combo but not the last one, "disable" it + // by making it a TICK event. + record->event.time = 0; + } + + } + drop_combo_from_buffer(combo_index); +} + +static inline void apply_combos(void) { + // Apply all buffered normal combos. + for (uint8_t i = combo_buffer_read; + i != combo_buffer_write; + INCREMENT_MOD(i)) { + + queued_combo_t *buffered_combo = &combo_buffer[i]; + combo_t *combo = &key_combos[buffered_combo->combo_index]; + +#ifdef COMBO_MUST_TAP_PER_COMBO + if (get_combo_must_tap(buffered_combo->combo_index, combo)) { + // Tap-only combos are applied on key release only, so let's drop 'em here. + drop_combo_from_buffer(buffered_combo->combo_index); + continue; + } +#endif + apply_combo(buffered_combo->combo_index, combo); + } + dump_key_buffer(); + clear_combos(); +} + +combo_t* overlaps(combo_t *combo1, combo_t *combo2) { + /* Checks if the combos overlap and returns the combo that should be + * dropped from the combo buffer. + * The combo that has less keys will be dropped. If they have the same + * amount of keys, drop combo1. */ + + uint8_t idx1 = 0, idx2 = 0; + uint16_t key1, key2; + bool overlaps = false; + + while ((key1 = pgm_read_word(&combo1->keys[idx1])) != COMBO_END) { + idx2 = 0; + while ((key2 = pgm_read_word(&combo2->keys[idx2])) != COMBO_END) { + if (key1 == key2) overlaps = true; + idx2 += 1; + } + idx1 += 1; + } + + if (!overlaps) return NULL; + if (idx2 < idx1) return combo2; + return combo1; +} + +static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) { + uint8_t key_count = 0; + uint16_t key_index = -1; + _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count); + + /* Continue processing if key isn't part of current combo. */ + if (-1 == (int16_t)key_index) { + return false; + } + + bool key_is_part_of_combo = !COMBO_DISABLED(combo); + + if (record->event.pressed && !COMBO_DISABLED(combo)) { + uint16_t time = _get_combo_term(combo_index, combo); + if (!COMBO_ACTIVE(combo)) { + KEY_STATE_DOWN(combo->state, key_index); + if (longest_term < time) { + longest_term = time; } } + if (ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) { + /* Combo was fully pressed */ + /* Buffer the combo so we can fire it after COMBO_TERM */ + +#ifndef COMBO_NO_TIMER + /* Don't buffer this combo if its combo term has passed. */ + if (timer && timer_elapsed(timer) > time) { + DISABLE_COMBO(combo); + return true; + } else +#endif + { + + // disable readied combos that overlap with this combo + combo_t *drop = NULL; + for (uint8_t combo_buffer_i = combo_buffer_read; + combo_buffer_i != combo_buffer_write; + INCREMENT_MOD(combo_buffer_i)) { + + queued_combo_t *qcombo = &combo_buffer[combo_buffer_i]; + combo_t *buffered_combo = &key_combos[qcombo->combo_index]; + + if ((drop = overlaps(buffered_combo, combo))) { + DISABLE_COMBO(drop); + if (drop == combo) { + // stop checking for overlaps if dropped combo was current combo. + break; + } else if (combo_buffer_i == combo_buffer_read && drop == buffered_combo) { + /* Drop the disabled buffered combo from the buffer if + * it is in the beginning of the buffer. */ + INCREMENT_MOD(combo_buffer_read); + } + } + + } + + if (drop != combo) { + // save this combo to buffer + combo_buffer[combo_buffer_write] = (queued_combo_t){ + .combo_index=combo_index, + }; + INCREMENT_MOD(combo_buffer_write); + + // get possible longer waiting time for tap-/hold-only combos. + longest_term = _get_wait_time(combo_index, combo); + } + } // if timer elapsed end + + } } else { - if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */ - send_combo(combo->keycode, false); + // chord releases + if (!COMBO_ACTIVE(combo) && ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) { + /* First key quickly released */ + if (COMBO_DISABLED(combo) || _get_combo_must_hold(combo_index, combo)) { + // combo wasn't tappable, disable it and drop it from buffer. + drop_combo_from_buffer(combo_index); + key_is_part_of_combo = false; + } +#ifdef COMBO_MUST_TAP_PER_COMBO + else if (get_combo_must_tap(combo_index, combo)) { + // immediately apply tap-only combo + apply_combo(combo_index, combo); + apply_combos(); // also apply other prepared combos and dump key buffer +# ifdef COMBO_PROCESS_KEY_RELEASE + if (process_combo_key_release(combo_index, combo, key_index, keycode)) { + release_combo(combo_index, combo); + } +# endif + } +#endif + } else if (COMBO_ACTIVE(combo) + && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo)) + && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index) + ) { + /* last key released */ + release_combo(combo_index, combo); + key_is_part_of_combo = true; + +#ifdef COMBO_PROCESS_KEY_RELEASE + process_combo_key_release(combo_index, combo, key_index, keycode); +#endif + } else if (COMBO_ACTIVE(combo) + && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index) + ) { + /* first or middle key released */ + key_is_part_of_combo = true; + +#ifdef COMBO_PROCESS_KEY_RELEASE + if (process_combo_key_release(combo_index, combo, key_index, keycode)) { + release_combo(combo_index, combo); + } +#endif } else { - /* continue processing without immediately returning */ - is_combo_active = false; + /* The released key was part of an incomplete combo */ + key_is_part_of_combo = false; } - KEY_STATE_UP(index); + KEY_STATE_UP(combo->state, key_index); } - return is_combo_active; + return key_is_part_of_combo; } -#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state) - bool process_combo(uint16_t keycode, keyrecord_t *record) { bool is_combo_key = false; - drop_buffer = false; bool no_combo_keys_pressed = true; if (keycode == CMB_ON && record->event.pressed) { @@ -144,62 +475,81 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) { if (!is_combo_enabled()) { return true; } -#ifndef COMBO_VARIABLE_LEN - for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) { -#else - for (current_combo_index = 0; current_combo_index < COMBO_LEN; ++current_combo_index) { + +#ifdef COMBO_ONLY_FROM_LAYER + /* Only check keycodes from one layer. */ + keycode = keymap_key_to_keycode(COMBO_ONLY_FROM_LAYER, record->event.key); #endif - combo_t *combo = &key_combos[current_combo_index]; - is_combo_key |= process_single_combo(combo, keycode, record); - no_combo_keys_pressed = no_combo_keys_pressed && NO_COMBO_KEYS_ARE_DOWN; + + for (uint16_t idx = 0; idx < COMBO_LEN; ++idx) { + combo_t *combo = &key_combos[idx]; + is_combo_key |= process_single_combo(combo, keycode, record, idx); + no_combo_keys_pressed = no_combo_keys_pressed && (NO_COMBO_KEYS_ARE_DOWN || COMBO_ACTIVE(combo) || COMBO_DISABLED(combo)); } - if (drop_buffer) { - /* buffer is only dropped when we complete a combo, so we refresh the timer - * here */ - timer = timer_read(); - dump_key_buffer(false); - } else if (!is_combo_key) { - /* if no combos claim the key we need to emit the keybuffer */ - dump_key_buffer(true); - - // reset state if there are no combo keys pressed at all - if (no_combo_keys_pressed) { - timer = 0; - is_active = true; - } - } else if (record->event.pressed && is_active) { - /* otherwise the key is consumed and placed in the buffer */ + if (record->event.pressed && is_combo_key) { +#ifndef COMBO_NO_TIMER +# ifdef COMBO_STRICT_TIMER + if (!timer) { + // timer is set only on the first key + timer = timer_read(); + } +# else timer = timer_read(); +# endif +#endif - if (buffer_size < MAX_COMBO_LENGTH) { -#ifdef COMBO_ALLOW_ACTION_KEYS - key_buffer[buffer_size++] = *record; -#else - key_buffer[buffer_size++] = keycode; + if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) { + key_buffer[key_buffer_size++] = (queued_record_t){ + .record = *record, + .keycode = keycode, + .combo_index = -1, // this will be set when applying combos + }; + } + } else { + if (combo_buffer_read != combo_buffer_write) { + // some combo is prepared + apply_combos(); + } else { + // reset state if there are no combo keys pressed at all + dump_key_buffer(); +#ifndef COMBO_NO_TIMER + timer = 0; #endif + clear_combos(); } } - return !is_combo_key; } void combo_task(void) { - if (b_combo_enable && is_active && timer && timer_elapsed(timer) > COMBO_TERM) { - /* This disables the combo, meaning key events for this - * combo will be handled by the next processors in the chain - */ - is_active = false; - dump_key_buffer(true); + if (!b_combo_enable) { + return; } + +#ifndef COMBO_NO_TIMER + if (timer && timer_elapsed(timer) > longest_term) { + if (combo_buffer_read != combo_buffer_write) { + apply_combos(); + longest_term = 0; + timer = 0; + } else { + dump_key_buffer(); + timer = 0; + clear_combos(); + } + } +#endif } void combo_enable(void) { b_combo_enable = true; } void combo_disable(void) { - b_combo_enable = is_active = false; +#ifndef COMBO_NO_TIMER timer = 0; - dump_key_buffer(true); +#endif + b_combo_enable = false; + combo_buffer_read = combo_buffer_write; } void combo_toggle(void) { diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h index 9af97588b2..43c36d79e6 100644 --- a/quantum/process_keycode/process_combo.h +++ b/quantum/process_keycode/process_combo.h @@ -20,23 +20,38 @@ #include "quantum.h" #include -#ifdef EXTRA_EXTRA_LONG_COMBOS +#ifdef EXTRA_SHORT_COMBOS +# define MAX_COMBO_LENGTH 6 +#elif defined(EXTRA_EXTRA_LONG_COMBOS) # define MAX_COMBO_LENGTH 32 -#elif EXTRA_LONG_COMBOS +#elif defined(EXTRA_LONG_COMBOS) # define MAX_COMBO_LENGTH 16 #else # define MAX_COMBO_LENGTH 8 #endif +#ifndef COMBO_KEY_BUFFER_LENGTH +# define COMBO_KEY_BUFFER_LENGTH MAX_COMBO_LENGTH +#endif +#ifndef COMBO_BUFFER_LENGTH +# define COMBO_BUFFER_LENGTH 4 +#endif + typedef struct { const uint16_t *keys; uint16_t keycode; -#ifdef EXTRA_EXTRA_LONG_COMBOS +#ifdef EXTRA_SHORT_COMBOS + uint8_t state; +#else + bool disabled; + bool active; +# if defined(EXTRA_EXTRA_LONG_COMBOS) uint32_t state; -#elif EXTRA_LONG_COMBOS +# elif defined(EXTRA_LONG_COMBOS) uint16_t state; -#else +# else uint8_t state; +# endif #endif } combo_t; @@ -46,12 +61,15 @@ typedef struct { { .keys = &(ck)[0] } #define COMBO_END 0 -#ifndef COMBO_COUNT -# define COMBO_COUNT 0 -#endif #ifndef COMBO_TERM -# define COMBO_TERM TAPPING_TERM +# define COMBO_TERM 50 #endif +#ifndef COMBO_HOLD_TERM +# define COMBO_HOLD_TERM TAPPING_TERM +#endif + +/* check if keycode is only modifiers */ +#define KEYCODE_IS_MOD(code) (IS_MOD(code) || (code >= QK_MODS && code <= QK_MODS_MAX && !(code & QK_BASIC_MAX))) bool process_combo(uint16_t keycode, keyrecord_t *record); void combo_task(void); diff --git a/quantum/quantum.c b/quantum/quantum.c index 3329c1146b..d173388714 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -143,7 +143,13 @@ void reset_keyboard(void) { } /* Convert record into usable keycode via the contained event. */ -uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache) { return get_event_keycode(record->event, update_layer_cache); } +uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache) { +#ifdef COMBO_ENABLE + if (record->keycode) { return record->keycode; } +#endif + return get_event_keycode(record->event, update_layer_cache); +} + /* Convert event into usable keycode. Checks the layer cache to ensure that it * retains the correct keycode after a layer change, if the key is still pressed. @@ -169,6 +175,18 @@ uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache) { return keymap_key_to_keycode(layer_switch_get_layer(event.key), event.key); } +/* Get keycode, and then process pre tapping functionality */ +bool pre_process_record_quantum(keyrecord_t *record) { + if (!( +#ifdef COMBO_ENABLE + process_combo(get_record_keycode(record, true), record) && +#endif + true)) { + return false; + } + return true; // continue processing +} + /* Get keycode, and then call keyboard function */ void post_process_record_quantum(keyrecord_t *record) { uint16_t keycode = get_record_keycode(record, false); @@ -254,9 +272,6 @@ bool process_record_quantum(keyrecord_t *record) { #ifdef LEADER_ENABLE process_leader(keycode, record) && #endif -#ifdef COMBO_ENABLE - process_combo(keycode, record) && -#endif #ifdef PRINTING_ENABLE process_printer(keycode, record) && #endif diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index bd41d28b66..d19fd2a045 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -55,6 +55,8 @@ __attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrec __attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; } #endif +__attribute__((weak)) bool pre_process_record_quantum(keyrecord_t *record) { return true; } + #ifndef TAP_CODE_DELAY # define TAP_CODE_DELAY 0 #endif @@ -106,9 +108,13 @@ void action_exec(keyevent_t event) { #endif #ifndef NO_ACTION_TAPPING - action_tapping_process(record); + if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { + action_tapping_process(record); + } #else - process_record(&record); + if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { + process_record(&record); + } if (!IS_NOEVENT(record.event)) { dprint("processed: "); debug_record(record); @@ -206,7 +212,16 @@ void process_record(keyrecord_t *record) { } void process_record_handler(keyrecord_t *record) { +#ifdef COMBO_ENABLE + action_t action; + if (record->keycode) { + action = action_for_keycode(record->keycode); + } else { + action = store_or_get_action(record->event.pressed, record->event.key); + } +#else action_t action = store_or_get_action(record->event.pressed, record->event.key); +#endif dprint("ACTION: "); debug_action(action); #ifndef NO_ACTION_LAYER @@ -990,6 +1005,24 @@ bool is_tap_key(keypos_t key) { return is_tap_action(action); } +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +bool is_tap_record(keyrecord_t *record) { +#ifdef COMBO_ENABLE + action_t action; + if (record->keycode) { + action = action_for_keycode(record->keycode); + } else { + action = layer_switch_get_action(record->event.key); + } +#else + action_t action = layer_switch_get_action(record->event.key); +#endif + return is_tap_action(action); +} + /** \brief Utilities for actions. (FIXME: Needs better description) * * FIXME: Needs documentation. diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h index 8cb4722c6e..3d357b33b8 100644 --- a/tmk_core/common/action.h +++ b/tmk_core/common/action.h @@ -53,6 +53,9 @@ typedef struct { #ifndef NO_ACTION_TAPPING tap_t tap; #endif +#ifdef COMBO_ENABLE + uint16_t keycode; +#endif } keyrecord_t; /* Execute action per keyevent */ @@ -60,6 +63,7 @@ void action_exec(keyevent_t event); /* action for key */ action_t action_for_key(uint8_t layer, keypos_t key); +action_t action_for_keycode(uint16_t keycode); /* macro */ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); @@ -111,6 +115,7 @@ void clear_keyboard_but_mods(void); void clear_keyboard_but_mods_and_keys(void); void layer_switch(uint8_t new_layer); bool is_tap_key(keypos_t key); +bool is_tap_record(keyrecord_t *record); bool is_tap_action(action_t action); #ifndef NO_ACTION_TAPPING diff --git a/tmk_core/common/action_tapping.c b/tmk_core/common/action_tapping.c index 56044e096d..1701ae4717 100644 --- a/tmk_core/common/action_tapping.c +++ b/tmk_core/common/action_tapping.c @@ -18,11 +18,16 @@ # define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed) # define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed) # define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k))) +#ifndef COMBO_ENABLE +# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key))) +#else +# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode) +#endif __attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; } # ifdef TAPPING_TERM_PER_KEY -# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_event_keycode(tapping_key.event, false), &tapping_key)) +# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key)) # else # define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM) # endif @@ -103,7 +108,7 @@ bool process_tapping(keyrecord_t *keyp) { if (IS_TAPPING_PRESSED()) { if (WITHIN_TAPPING_TERM(event)) { if (tapping_key.tap.count == 0) { - if (IS_TAPPING_KEY(event.key) && !event.pressed) { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { // first tap! debug("Tapping: First tap(0->1).\n"); tapping_key.tap.count = 1; @@ -122,14 +127,14 @@ bool process_tapping(keyrecord_t *keyp) { # if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) else if ((( # ifdef TAPPING_TERM_PER_KEY - get_tapping_term(get_event_keycode(tapping_key.event, false), keyp) + get_tapping_term(get_record_keycode(&tapping_key, false), keyp) # else TAPPING_TERM # endif >= 500) # ifdef PERMISSIVE_HOLD_PER_KEY - || get_permissive_hold(get_event_keycode(tapping_key.event, false), keyp) + || get_permissive_hold(get_record_keycode(&tapping_key, false), keyp) # elif defined(PERMISSIVE_HOLD) || true # endif @@ -177,7 +182,7 @@ bool process_tapping(keyrecord_t *keyp) { } // tap_count > 0 else { - if (IS_TAPPING_KEY(event.key) && !event.pressed) { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { debug("Tapping: Tap release("); debug_dec(tapping_key.tap.count); debug(")\n"); @@ -186,11 +191,15 @@ bool process_tapping(keyrecord_t *keyp) { tapping_key = *keyp; debug_tapping_key(); return true; - } else if (is_tap_key(event.key) && event.pressed) { + } else if (is_tap_record(keyp) && event.pressed) { if (tapping_key.tap.count > 1) { debug("Tapping: Start new tap with releasing last tap(>1).\n"); // unregister key - process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false}); + process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, +#ifdef COMBO_ENABLE + .keycode = tapping_key.keycode, +#endif + }); } else { debug("Tapping: Start while last tap(1).\n"); } @@ -218,17 +227,21 @@ bool process_tapping(keyrecord_t *keyp) { debug_tapping_key(); return false; } else { - if (IS_TAPPING_KEY(event.key) && !event.pressed) { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { debug("Tapping: End. last timeout tap release(>0)."); keyp->tap = tapping_key.tap; process_record(keyp); tapping_key = (keyrecord_t){}; return true; - } else if (is_tap_key(event.key) && event.pressed) { + } else if (is_tap_record(keyp) && event.pressed) { if (tapping_key.tap.count > 1) { debug("Tapping: Start new tap with releasing last timeout tap(>1).\n"); // unregister key - process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false}); + process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, +#ifdef COMBO_ENABLE + .keycode = tapping_key.keycode, +#endif + }); } else { debug("Tapping: Start while last timeout tap(1).\n"); } @@ -248,12 +261,12 @@ bool process_tapping(keyrecord_t *keyp) { } else if (IS_TAPPING_RELEASED()) { if (WITHIN_TAPPING_TERM(event)) { if (event.pressed) { - if (IS_TAPPING_KEY(event.key)) { + if (IS_TAPPING_RECORD(keyp)) { //# ifndef TAPPING_FORCE_HOLD # if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY) if ( # ifdef TAPPING_FORCE_HOLD_PER_KEY - !get_tapping_force_hold(get_event_keycode(tapping_key.event, false), keyp) && + !get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) && # endif !tapping_key.tap.interrupted && tapping_key.tap.count > 0) { // sequential tap. @@ -271,7 +284,7 @@ bool process_tapping(keyrecord_t *keyp) { // FIX: start new tap again tapping_key = *keyp; return true; - } else if (is_tap_key(event.key)) { + } else if (is_tap_record(keyp)) { // Sequential tap can be interfered with other tap key. debug("Tapping: Start with interfering other tap.\n"); tapping_key = *keyp; @@ -303,7 +316,7 @@ bool process_tapping(keyrecord_t *keyp) { } // not tapping state else { - if (event.pressed && is_tap_key(event.key)) { + if (event.pressed && is_tap_record(keyp)) { debug("Tapping: Start(Press tap key).\n"); tapping_key = *keyp; process_record_tap_hint(&tapping_key); diff --git a/tmk_core/common/action_tapping.h b/tmk_core/common/action_tapping.h index 893ccb1ce1..7de8049c7f 100644 --- a/tmk_core/common/action_tapping.h +++ b/tmk_core/common/action_tapping.h @@ -30,6 +30,7 @@ along with this program. If not, see . #define WAITING_BUFFER_SIZE 8 #ifndef NO_ACTION_TAPPING +uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache); uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache); void action_tapping_process(keyrecord_t record); -- cgit v1.2.3 From 610035dce8f4f7010779e61e1cb590eebc8cea46 Mon Sep 17 00:00:00 2001 From: Sergey Vlasov Date: Sat, 7 Aug 2021 02:16:26 +0300 Subject: Add HOLD_ON_OTHER_KEY_PRESS option for dual-role keys (#9404) * Add HOLD_ON_OTHER_KEY_PRESS option for dual-role keys Implement an additional option for dual-role keys which converts the dual-role key press into a hold action immediately when another key is pressed (this is different from the existing PERMISSIVE_HOLD option, which selects the hold action when another key is tapped (pressed and then released) while the dual-role key is pressed). The Mod-Tap keys already behave in a similar way, unless the IGNORE_MOD_TAP_INTERRUPT option is enabled (but with some additional delays); the added option makes this behavior available for all other kinds of dual-role keys. * [Docs] Update tap-hold docs for HOLD_ON_OTHER_KEY_PRESS Document the newly added HOLD_ON_OTHER_KEY_PRESS option and update the documentation for closely related options (PERMISSIVE_HOLD and IGNORE_MOD_TAP_INTERRUPT). Use Layer Tap instead of Mod Tap in examples for PERMISSIVE_HOLD and HOLD_ON_OTHER_KEY_PRESS, because the effect of using these options with Mod Tap keys is mostly invisible without IGNORE_MOD_TAP_INTERRUPT. Add comments before return statements in sample implementations of `get_ignore_mod_tap_interrupt()`, `get_hold_on_other_key_press()` and `get_permissive_hold()`. Thanks to @Erovia and @precondition for comments and suggestions to improve the documentation. --- docs/tap_hold.md | 108 ++++++++++++++++++++++++++++++++------- tmk_core/common/action_tapping.c | 17 ++++++ 2 files changed, 106 insertions(+), 19 deletions(-) (limited to 'tmk_core/common') diff --git a/docs/tap_hold.md b/docs/tap_hold.md index 085bbde16a..71bff30baa 100644 --- a/docs/tap_hold.md +++ b/docs/tap_hold.md @@ -36,29 +36,49 @@ uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { } ``` +## Tap-Or-Hold Decision Modes -## Permissive Hold +The code which decides between the tap and hold actions of dual-role keys supports three different modes, in increasing order of preference for the hold action: -As of [PR#1359](https://github.com/qmk/qmk_firmware/pull/1359/), there is a new `config.h` option: +1. The default mode selects the hold action only if the dual-role key is held down longer than the tapping term. In this mode pressing other keys while the dual-role key is held down does not influence the tap-or-hold decision. + +2. The “permissive hold” mode, in addition to the default behavior, immediately selects the hold action when another key is tapped (pressed and then released) while the dual-role key is held down, even if this happens earlier than the tapping term. If another key is just pressed, but then the dual-role key is released before that other key (and earlier than the tapping term), this mode will still select the tap action. + +3. The “hold on other key press” mode, in addition to the default behavior, immediately selects the hold action when another key is pressed while the dual-role key is held down, even if this happens earlier than the tapping term. + +Note that until the tap-or-hold decision completes (which happens when either the dual-role key is released, or the tapping term has expired, or the extra condition for the selected decision mode is satisfied), key events are delayed and not transmitted to the host immediately. The default mode gives the most delay (if the dual-role key is held down, this mode always waits for the whole tapping term), and the other modes may give less delay when other keys are pressed, because the hold action may be selected earlier. + +### Permissive Hold + +The “permissive hold” mode can be enabled for all dual-role keys by adding the corresponding option to `config.h`: ```c #define PERMISSIVE_HOLD ``` -This makes tap and hold keys (like Mod Tap) work better for fast typists, or for high `TAPPING_TERM` settings. +This makes tap and hold keys (like Layer Tap) work better for fast typists, or for high `TAPPING_TERM` settings. -If you press a Mod Tap key, tap another key (press and release) and then release the Mod Tap key, all within the tapping term, it will output the tapping function for both keys. +If you press a dual-role key, tap another key (press and release) and then release the dual-role key, all within the tapping term, by default the dual-role key will perform its tap action. If the `PERMISSIVE_HOLD` option is enabled, the dual-role key will perform its hold action instead. -For Instance: +An example of a sequence which is affected by the “permissive hold” mode: -- `SFT_T(KC_A)` Down -- `KC_X` Down -- `KC_X` Up -- `SFT_T(KC_A)` Up +- `LT(2, KC_A)` Down +- `KC_L` Down (the `L` key is also mapped to `KC_RGHT` on layer 2) +- `KC_L` Up +- `LT(2, KC_A)` Up + +Normally, if you do all this within the `TAPPING_TERM` (default: 200ms), this will be registered as `al` by the firmware and host system. With the `PERMISSIVE_HOLD` option enabled, the Layer Tap key is considered as a layer switch if another key is tapped, and the above sequence would be registered as `KC_RGHT` (the mapping of `L` on layer 2). + +However, this slightly different sequence will not be affected by the “permissive hold” mode: -Normally, if you do all this within the `TAPPING_TERM` (default: 200ms) this will be registered as `ax` by the firmware and host system. With permissive hold enabled, this modifies how this is handled by considering the Mod Tap keys as a Mod if another key is tapped, and would registered as `X` (`SHIFT`+`x`). +- `LT(2, KC_A)` Down +- `KC_L` Down (the `L` key is also mapped to `KC_RGHT` on layer 2) +- `LT(2, KC_A)` Up +- `KC_L` Up -?> If you have `Ignore Mod Tap Interrupt` enabled, as well, this will modify how both work. The regular key has the modifier added if the first key is released first or if both keys are held longer than the `TAPPING_TERM`. +In the sequence above the dual-role key is released before the other key is released, and if that happens within the tapping term, the “permissive hold” mode will still choose the tap action for the dual-role key, and the sequence will be registered as `al` by the host. + +?> The `PERMISSIVE_HOLD` option also affects Mod Tap keys, but this may not be noticeable if you do not also enable the `IGNORE_MOD_TAP_INTERRUPT` option for those keys, because the default handler for Mod Tap keys also considers both the “nested press” and “rolling press” sequences like shown above as a modifier hold, not the tap action. If you do not enable `IGNORE_MOD_TAP_INTERRUPT`, the effect of `PERMISSIVE_HOLD` on Mod Tap keys would be limited to reducing the delay before the key events are made visible to the host. For more granular control of this feature, you can add the following to your `config.h`: @@ -72,13 +92,60 @@ You can then add the following function to your keymap: bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { switch (keycode) { case LT(1, KC_BSPC): + // Immediately select the hold action when another key is tapped. + return true; + default: + // Do not select the hold action when another key is tapped. + return false; + } +} +``` + +### Hold On Other Key Press + +The “hold on other key press” mode can be enabled for all dual-role keys by adding the corresponding option to `config.h`: + +```c +#define HOLD_ON_OTHER_KEY_PRESS +``` + +This mode makes tap and hold keys (like Layer Tap) work better for fast typists, or for high `TAPPING_TERM` settings. Compared to the “permissive hold” mode, this mode selects the hold action in more cases. + +If you press a dual-role key, press another key, and then release the dual-role key, all within the tapping term, by default the dual-role key will perform its tap action. If the `HOLD_ON_OTHER_KEY_PRESS` option is enabled, the dual-role key will perform its hold action instead. + +An example of a sequence which is affected by the “hold on other key press” mode, but not by the “permissive hold” mode: + +- `LT(2, KC_A)` Down +- `KC_L` Down (the `L` key is also mapped to `KC_RGHT` on layer 2) +- `LT(2, KC_A)` Up +- `KC_L` Up + +Normally, if you do all this within the `TAPPING_TERM` (default: 200ms), this will be registered as `al` by the firmware and host system. With the `HOLD_ON_OTHER_KEY_PRESS` option enabled, the Layer Tap key is considered as a layer switch if another key is pressed, and the above sequence would be registered as `KC_RGHT` (the mapping of `L` on layer 2). + +?> The `HOLD_ON_OTHER_KEY_PRESS` option also affects Mod Tap keys, but this may not be noticeable if you do not also enable the `IGNORE_MOD_TAP_INTERRUPT` option for those keys, because the default handler for Mod Tap keys also considers the “rolling press” sequence like shown above as a modifier hold, not the tap action. If you do not enable `IGNORE_MOD_TAP_INTERRUPT`, the effect of `HOLD_ON_OTHER_KEY_PRESS` on Mod Tap keys would be limited to reducing the delay before the key events are made visible to the host. + +For more granular control of this feature, you can add the following to your `config.h`: + +```c +#define HOLD_ON_OTHER_KEY_PRESS_PER_KEY +``` + +You can then add the following function to your keymap: + +```c +bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case LT(1, KC_BSPC): + // Immediately select the hold action when another key is pressed. return true; default: + // Do not select the hold action when another key is pressed. return false; } } ``` + ## Ignore Mod Tap Interrupt To enable this setting, add this to your `config.h`: @@ -87,23 +154,22 @@ To enable this setting, add this to your `config.h`: #define IGNORE_MOD_TAP_INTERRUPT ``` -Similar to Permissive Hold, this alters how the firmware processes inputs for fast typists. If you press a Mod Tap key, press another key, release the Mod Tap key, and then release the normal key, it would normally output the Mod plus the normal key, even if pressed within the `TAPPING_TERM`. This may not be desirable for rolling combo keys, or for fast typists who have a Mod Tap on a frequently used key (`RCTL_T(KC_QUOT)`, for example). +?> This option affects only the Mod Tap keys; it does not affect other dual-role keys such as Layer Tap. + +By default the tap-or-hold decision for Mod Tap keys strongly prefers the hold action. If you press a Mod Tap key, then press another key while still holding the Mod Tap key down, the Mod Tap press will be handled as a modifier hold even if the Mod Tap key is then released within the tapping term, and irrespective of the order in which those keys are released. Using options such as `PERMISSIVE_HOLD` or `HOLD_ON_OTHER_KEY_PRESS` will not affect the functionality of Mod Tap keys in a major way (these options would still affect the delay until the common code for dual-role keys finishes its tap-or-hold decision, but then the special code for Mod Tap keys will override the result of that decision and choose the hold action if another key was pressed). In fact, by default the tap-or-hold decision for Mod Tap keys is done in the same way as if the `HOLD_ON_OTHER_KEY_PRESS` option was enabled, but without the decreased delay provided by `HOLD_ON_OTHER_KEY_PRESS`. -Setting `Ignore Mod Tap Interrupt` requires holding both keys for the `TAPPING_TERM` to trigger the hold function (the mod). +If the `IGNORE_MOD_TAP_INTERRUPT` option is enabled, Mod Tap keys are no longer treated as a special case, and their behavior will match the behavior of other dual-role keys such as Layer Tap. Then the behavior of Mod Tap keys can be further tuned using other options such as `PERMISSIVE_HOLD` or `HOLD_ON_OTHER_KEY_PRESS`. -For Instance: +An example of a sequence which will be affected by the `IGNORE_MOD_TAP_INTERRUPT` option (assuming that options like `PERMISSIVE_HOLD` or `HOLD_ON_OTHER_KEY_PRESS` are not enabled): - `SFT_T(KC_A)` Down - `KC_X` Down - `SFT_T(KC_A)` Up - `KC_X` Up -Normally, this would send a capital `X` (`SHIFT`+`x`), or, Mod + key. With `Ignore Mod Tap Interrupt` enabled, holding both keys are required for the `TAPPING_TERM` to register the hold action. A quick tap will output `ax` in this case, while a hold on both will still output capital `X` (`SHIFT`+`x`). - - -?> __Note__: This only concerns modifiers and not layer switching keys. +Normally, this would send a capital `X` (`SHIFT`+`x`), even if the sequence is performed faster than the `TAPPING_TERM`. However, if the `IGNORE_MOD_TAP_INTERRUPT` option is enabled, the `SFT_T(KC_A)` key must be held longer than the `TAPPING_TERM` to register the hold action. A quick tap will output `ax` in this case, while a hold will still output a capital `X` (`SHIFT`+`x`). -?> If you have `Permissive Hold` enabled, as well, this will modify how both work. The regular key has the modifier added if the first key is released first or if both keys are held longer than the `TAPPING_TERM`. +However, if the `HOLD_ON_OTHER_KEY_PRESS` option is enabled in addition to `IGNORE_MOD_TAP_INTERRUPT`, the above sequence will again send a capital `X` (`SHIFT`+`x`) even if performed faster that the `TAPPING_TERM`. The difference from the default configuration is that by default the host will receive the key events only after the `SFT_T(KC_A)` key is released, but with the `HOLD_ON_OTHER_KEY_PRESS` option the host will start receiving key events when the `KC_X` key is pressed. For more granular control of this feature, you can add the following to your `config.h`: @@ -117,8 +183,12 @@ You can then add the following function to your keymap: bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { switch (keycode) { case SFT_T(KC_SPC): + // Do not force the mod-tap key press to be handled as a modifier + // if any other key was pressed while the mod-tap key is held down. return true; default: + // Force the mod-tap key press to be handled as a modifier if any + // other key was pressed while the mod-tap key is held down. return false; } } diff --git a/tmk_core/common/action_tapping.c b/tmk_core/common/action_tapping.c index 1701ae4717..36839f9faf 100644 --- a/tmk_core/common/action_tapping.c +++ b/tmk_core/common/action_tapping.c @@ -40,6 +40,10 @@ __attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { return false; } # endif +# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY +__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; } +# endif + static keyrecord_t tapping_key = {}; static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {}; static uint8_t waiting_buffer_head = 0; @@ -175,6 +179,19 @@ bool process_tapping(keyrecord_t *keyp) { // set interrupted flag when other key preesed during tapping if (event.pressed) { tapping_key.tap.interrupted = true; +# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) +# if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) + if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp)) +# endif + { + debug("Tapping: End. No tap. Interfered by pressed key\n"); + process_record(&tapping_key); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + // enqueue + return false; + } +# endif } // enqueue return false; -- cgit v1.2.3 From 2bc8215ce5f5c7473bb75de95cef4ab468d7e837 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Mon, 16 Aug 2021 17:28:12 +0100 Subject: Unify behaviour of wait on AVR (#14025) --- tmk_core/common/avr/_wait.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'tmk_core/common') diff --git a/tmk_core/common/avr/_wait.h b/tmk_core/common/avr/_wait.h index 56eb316faf..ae1a25131a 100644 --- a/tmk_core/common/avr/_wait.h +++ b/tmk_core/common/avr/_wait.h @@ -17,8 +17,26 @@ #include -#define wait_ms(ms) _delay_ms(ms) -#define wait_us(us) _delay_us(us) +#define wait_ms(ms) \ + do { \ + if (__builtin_constant_p(ms)) { \ + _delay_ms(ms); \ + } else { \ + for (uint16_t i = ms; i > 0; i--) { \ + _delay_ms(1); \ + } \ + } \ + } while (0) +#define wait_us(us) \ + do { \ + if (__builtin_constant_p(us)) { \ + _delay_us(us); \ + } else { \ + for (uint16_t i = us; i > 0; i--) { \ + _delay_us(1); \ + } \ + } \ + } while (0) /* The AVR series GPIOs have a one clock read delay for changes in the digital input signal. * But here's more margin to make it two clocks. */ -- cgit v1.2.3 From 75b49aff56436c57a424e622c91f6d80e1d0ecc2 Mon Sep 17 00:00:00 2001 From: a-chol Date: Tue, 17 Aug 2021 20:52:44 +0200 Subject: Digitizer HID interface : absolute coordinates for mouse cursor (#12851) * Add digitizer HID interface for setting the mouse cursor position at absolute screen coordinates. Tested on Pro Micro, Proton C and Blackpill. * Update docs/feature_digitizer.md Co-authored-by: Ryan * Update tmk_core/protocol/usb_descriptor.c Co-authored-by: Ryan * Add missing copyrights Add V-USB support * Add support for digitizer dedicated endpoint for lufa and chibios. Fix formatting issues Move digitizer_task definition to the feature's base implementation file * Run cformat on modified files * Change digitizer report usage to Digitizer instead of Pen to avoid pointer disappearing on Windows. * Update tmk_core/protocol/vusb/vusb.c Co-authored-by: Ryan * Run cformat from docker image * Remove send_digitizer from host_driver_t and instead rely on the declaration being the interface to the implementation in each HW-specific usb implementation. * Fix build : send_digitizer shouldn't be static in vusb and add weak-linkage implementation for tests without usb implementation * Change digitizer user interface to match pointing device's * Update documentation with new API Co-authored-by: a-chol Co-authored-by: Ryan --- common_features.mk | 5 + docs/_summary.md | 1 + docs/feature_digitizer.md | 35 +++++++ .../handwired/onekey/keymaps/digitizer/keymap.c | 38 ++++++++ .../handwired/onekey/keymaps/digitizer/rules.mk | 1 + quantum/digitizer.c | 34 +++++++ quantum/digitizer.h | 41 +++++++++ tmk_core/common.mk | 13 +++ tmk_core/common/host.c | 19 ++++ tmk_core/common/host_driver.h | 2 + tmk_core/common/keyboard.c | 7 ++ tmk_core/common/report.h | 14 ++- tmk_core/protocol/chibios/main.c | 1 + tmk_core/protocol/chibios/usb_main.c | 28 ++++++ tmk_core/protocol/lufa/lufa.c | 26 +++++- tmk_core/protocol/usb_descriptor.c | 101 +++++++++++++++++++++ tmk_core/protocol/usb_descriptor.h | 27 +++++- tmk_core/protocol/vusb/vusb.c | 48 +++++++++- 18 files changed, 435 insertions(+), 6 deletions(-) create mode 100644 docs/feature_digitizer.md create mode 100644 keyboards/handwired/onekey/keymaps/digitizer/keymap.c create mode 100644 keyboards/handwired/onekey/keymaps/digitizer/rules.mk create mode 100644 quantum/digitizer.c create mode 100644 quantum/digitizer.h (limited to 'tmk_core/common') diff --git a/common_features.mk b/common_features.mk index e442222eae..493aab6353 100644 --- a/common_features.mk +++ b/common_features.mk @@ -695,6 +695,11 @@ ifeq ($(strip $(JOYSTICK_ENABLE)), digital) OPT_DEFS += -DDIGITAL_JOYSTICK_ENABLE endif +DIGITIZER_ENABLE ?= no +ifneq ($(strip $(DIGITIZER_ENABLE)), no) + SRC += $(QUANTUM_DIR)/digitizer.c +endif + USBPD_ENABLE ?= no VALID_USBPD_DRIVER_TYPES = custom vendor USBPD_DRIVER ?= vendor diff --git a/docs/_summary.md b/docs/_summary.md index 48115cef46..2f6309e41d 100644 --- a/docs/_summary.md +++ b/docs/_summary.md @@ -108,6 +108,7 @@ * [Bluetooth](feature_bluetooth.md) * [Bootmagic Lite](feature_bootmagic.md) * [Custom Matrix](custom_matrix.md) + * [Digitizer](feature_digitizer.md) * [DIP Switch](feature_dip_switch.md) * [Encoders](feature_encoders.md) * [Haptic Feedback](feature_haptic_feedback.md) diff --git a/docs/feature_digitizer.md b/docs/feature_digitizer.md new file mode 100644 index 0000000000..9b6aeddbaa --- /dev/null +++ b/docs/feature_digitizer.md @@ -0,0 +1,35 @@ +## Digitizer + +The digitizer HID interface allows setting the mouse cursor position at absolute coordinates, unlike the Pointing Device feature that applies relative displacements. + +To enable the digitizer interface, add the following line to your rules.mk: + +```makefile +DIGITIZER_ENABLE = yes +``` + +In order to change the mouse cursor position from your keymap.c file, include the digitizer header : + +```c +#include "digitizer.h" +``` + +This gives you access to the `digitizer` structure which members allow you to change the cursor position. + +The coordinates are normalized, meaning there value must be set between 0 and 1. For the `x` coordinate, the value `0` is the leftmost position, whereas the value `1` is the rightmost position. +For the `y` coordinate, `0` is at the top and `1` at the bottom. + +Here is an example setting the cursor in the middle of the screen: + +```c +digitizer_t digitizer; +digitizer.x = 0.5; +digitizer.y = 0.5; +digitizer.tipswitch = 0; +digitizer.inrange = 1; +digitizer_set_report(digitizer); +``` + +The `tipswitch` member triggers what equates to a click when set to `1`. The `inrange` member is required for the change in coordinates to be taken. It can then be set to `0` in a new report to signal the end of the digitizer interaction, but it is not strictly required. + +Once all members are set to the desired value, the `status` member needs its bitmask `DZ_UPDATED` to be set so the report is sent during the next main loop iteration. diff --git a/keyboards/handwired/onekey/keymaps/digitizer/keymap.c b/keyboards/handwired/onekey/keymaps/digitizer/keymap.c new file mode 100644 index 0000000000..6b304e86fe --- /dev/null +++ b/keyboards/handwired/onekey/keymaps/digitizer/keymap.c @@ -0,0 +1,38 @@ + /* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include QMK_KEYBOARD_H + +#include "digitizer.h" + +#include "math.h" + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {LAYOUT_ortho_1x1(KC_A)}; + +uint32_t timer = 0; + +void matrix_scan_user() { + if (timer_elapsed32(timer) < 200) { + return; + } + + timer = timer_read32(); + digitizer_t digitizer; + digitizer.x = 0.5 - 0.2 * cos(timer_read() / 250. / 6.28); + digitizer.y = 0.5 - 0.2 * sin(timer_read() / 250. / 6.28); + digitizer.tipswitch = 0; + digitizer.inrange = 1; + digitizer_set_report(digitizer); +} diff --git a/keyboards/handwired/onekey/keymaps/digitizer/rules.mk b/keyboards/handwired/onekey/keymaps/digitizer/rules.mk new file mode 100644 index 0000000000..ea9f9bd9ac --- /dev/null +++ b/keyboards/handwired/onekey/keymaps/digitizer/rules.mk @@ -0,0 +1 @@ +DIGITIZER_ENABLE = yes \ No newline at end of file diff --git a/quantum/digitizer.c b/quantum/digitizer.c new file mode 100644 index 0000000000..e299867429 --- /dev/null +++ b/quantum/digitizer.c @@ -0,0 +1,34 @@ +/* Copyright 2021 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "digitizer.h" + +digitizer_t digitizerReport = {.tipswitch = 0, .inrange = 0, .id = 0, .x = 0, .y = 0, .status = DZ_INITIALIZED}; + +__attribute__((weak)) void digitizer_send(void) { + if (digitizerReport.status & DZ_UPDATED) { + host_digitizer_send(&digitizerReport); + digitizerReport.status &= ~DZ_UPDATED; + } +} + +__attribute__((weak)) void digitizer_task(void) { digitizer_send(); } + +digitizer_t digitizer_get_report(void) { return digitizerReport; } + +void digitizer_set_report(digitizer_t newDigitizerReport) { + digitizerReport = newDigitizerReport; + digitizerReport.status |= DZ_UPDATED; +} \ No newline at end of file diff --git a/quantum/digitizer.h b/quantum/digitizer.h new file mode 100644 index 0000000000..cef551567e --- /dev/null +++ b/quantum/digitizer.h @@ -0,0 +1,41 @@ +/* Copyright 2021 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "quantum.h" + +#include + +enum digitizer_status { DZ_INITIALIZED = 1, DZ_UPDATED = 2 }; + +typedef struct { + int8_t tipswitch; + int8_t inrange; + uint8_t id; + float x; + float y; + uint8_t status : 2; +} digitizer_t; + +extern digitizer_t digitizer; + +digitizer_t digitizer_get_report(void); + +void digitizer_set_report(digitizer_t newDigitizerReport); + +void digitizer_task(void); + +void host_digitizer_send(digitizer_t *digitizer); diff --git a/tmk_core/common.mk b/tmk_core/common.mk index 6c8cf69356..fa907b47b8 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -106,6 +106,19 @@ ifeq ($(strip $(NO_USB_STARTUP_CHECK)), yes) TMK_COMMON_DEFS += -DNO_USB_STARTUP_CHECK endif +ifeq ($(strip $(DIGITIZER_SHARED_EP)), yes) + TMK_COMMON_DEFS += -DDIGITIZER_SHARED_EP + SHARED_EP_ENABLE = yes +endif + +ifeq ($(strip $(DIGITIZER_ENABLE)), yes) + TMK_COMMON_DEFS += -DDIGITIZER_ENABLE + ifeq ($(strip $(SHARED_EP_ENABLE)), yes) + TMK_COMMON_DEFS += -DDIGITIZER_SHARED_EP + SHARED_EP_ENABLE = yes + endif +endif + ifeq ($(strip $(SHARED_EP_ENABLE)), yes) TMK_COMMON_DEFS += -DSHARED_EP_ENABLE endif diff --git a/tmk_core/common/host.c b/tmk_core/common/host.c index a8b391e893..f0c396b189 100644 --- a/tmk_core/common/host.c +++ b/tmk_core/common/host.c @@ -22,6 +22,7 @@ along with this program. If not, see . #include "host.h" #include "util.h" #include "debug.h" +#include "digitizer.h" #ifdef NKRO_ENABLE # include "keycode_config.h" @@ -103,6 +104,24 @@ void host_consumer_send(uint16_t report) { (*driver->send_consumer)(report); } +void host_digitizer_send(digitizer_t *digitizer) { + if (!driver) return; + + report_digitizer_t report = { +#ifdef DIGITIZER_SHARED_EP + .report_id = REPORT_ID_DIGITIZER, +#endif + .tip = digitizer->tipswitch & 0x1, + .inrange = digitizer->inrange & 0x1, + .x = (uint16_t)(digitizer->x * 0x7FFF), + .y = (uint16_t)(digitizer->y * 0x7FFF), + }; + + send_digitizer(&report); +} + +__attribute__((weak)) void send_digitizer(report_digitizer_t *report) {} + uint16_t host_last_system_report(void) { return last_system_report; } uint16_t host_last_consumer_report(void) { return last_consumer_report; } diff --git a/tmk_core/common/host_driver.h b/tmk_core/common/host_driver.h index f34a220530..2aebca043d 100644 --- a/tmk_core/common/host_driver.h +++ b/tmk_core/common/host_driver.h @@ -30,3 +30,5 @@ typedef struct { void (*send_system)(uint16_t); void (*send_consumer)(uint16_t); } host_driver_t; + +void send_digitizer(report_digitizer_t *report); \ No newline at end of file diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 28fa97bc8f..fcc51973fd 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -106,6 +106,9 @@ along with this program. If not, see . #if defined(CRC_ENABLE) # include "crc.h" #endif +#ifdef DIGITIZER_ENABLE +# include "digitizer.h" +#endif static uint32_t last_input_modification_time = 0; uint32_t last_input_activity_time(void) { return last_input_modification_time; } @@ -537,6 +540,10 @@ MATRIX_LOOP_END: joystick_task(); #endif +#ifdef DIGITIZER_ENABLE + digitizer_task(); +#endif + // update LED if (led_status != host_keyboard_leds()) { led_status = host_keyboard_leds(); diff --git a/tmk_core/common/report.h b/tmk_core/common/report.h index db6370657d..f2223e8063 100644 --- a/tmk_core/common/report.h +++ b/tmk_core/common/report.h @@ -30,7 +30,8 @@ enum hid_report_ids { REPORT_ID_SYSTEM, REPORT_ID_CONSUMER, REPORT_ID_NKRO, - REPORT_ID_JOYSTICK + REPORT_ID_JOYSTICK, + REPORT_ID_DIGITIZER }; /* Mouse buttons */ @@ -205,6 +206,17 @@ typedef struct { int8_t h; } __attribute__((packed)) report_mouse_t; +typedef struct { +#ifdef DIGITIZER_SHARED_EP + uint8_t report_id; +#endif + uint8_t tip : 1; + uint8_t inrange : 1; + uint8_t pad2 : 6; + uint16_t x; + uint16_t y; +} __attribute__((packed)) report_digitizer_t; + typedef struct { #if JOYSTICK_AXES_COUNT > 0 # if JOYSTICK_AXES_RESOLUTION > 8 diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index 199741594a..e41d6ff195 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -65,6 +65,7 @@ void send_keyboard(report_keyboard_t *report); void send_mouse(report_mouse_t *report); void send_system(uint16_t data); void send_consumer(uint16_t data); +void send_digitizer(report_digitizer_t *report); /* host struct */ host_driver_t chibios_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer}; diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index e5edd74dcb..cc282e6a9b 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -315,6 +315,9 @@ typedef struct { #endif #ifdef JOYSTICK_ENABLE usb_driver_config_t joystick_driver; +#endif +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) + usb_driver_config_t digitizer_driver; #endif }; usb_driver_config_t array[0]; @@ -360,6 +363,14 @@ static usb_driver_configs_t drivers = { # define JOYSTICK_OUT_MODE USB_EP_MODE_TYPE_BULK .joystick_driver = QMK_USB_DRIVER_CONFIG(JOYSTICK, 0, false), #endif + +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) +# define DIGITIZER_IN_CAPACITY 4 +# define DIGITIZER_OUT_CAPACITY 4 +# define DIGITIZER_IN_MODE USB_EP_MODE_TYPE_BULK +# define DIGITIZER_OUT_MODE USB_EP_MODE_TYPE_BULK + .digitizer_driver = QMK_USB_DRIVER_CONFIG(DIGITIZER, 0, false), +#endif }; #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t)) @@ -930,6 +941,23 @@ void send_consumer(uint16_t data) { #endif } +void send_digitizer(report_digitizer_t *report) { +#ifdef DIGITIZER_ENABLE +# ifdef DIGITIZER_SHARED_EP + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + usbStartTransmitI(&USB_DRIVER, DIGITIZER_IN_EPNUM, (uint8_t *)report, sizeof(report_digitizer_t)); + osalSysUnlock(); +# else + chnWrite(&drivers.digitizer_driver.driver, (uint8_t *)report, sizeof(report_digitizer_t)); +# endif +#endif +} + /* --------------------------------------------------------- * Console functions * --------------------------------------------------------- diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 4ac079e168..8ddb8b1c4b 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -142,9 +142,7 @@ static void send_keyboard(report_keyboard_t *report); static void send_mouse(report_mouse_t *report); static void send_system(uint16_t data); static void send_consumer(uint16_t data); -host_driver_t lufa_driver = { - keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer, -}; +host_driver_t lufa_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer}; #ifdef VIRTSER_ENABLE // clang-format off @@ -525,6 +523,11 @@ void EVENT_USB_Device_ConfigurationChanged(void) { /* Setup joystick endpoint */ ConfigSuccess &= Endpoint_ConfigureEndpoint((JOYSTICK_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1); #endif + +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) + /* Setup digitizer endpoint */ + ConfigSuccess &= Endpoint_ConfigureEndpoint((DIGITIZER_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, DIGITIZER_EPSIZE, 1); +#endif } /* FIXME: Expose this table in the docs somehow @@ -983,6 +986,23 @@ void virtser_send(const uint8_t byte) { } #endif +void send_digitizer(report_digitizer_t *report) { +#ifdef DIGITIZER_ENABLE + uint8_t timeout = 255; + + if (USB_DeviceState != DEVICE_STATE_Configured) return; + + Endpoint_SelectEndpoint(DIGITIZER_IN_EPNUM); + + /* Check if write ready for a polling interval around 10ms */ + while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40); + if (!Endpoint_IsReadWriteAllowed()) return; + + Endpoint_Write_Stream_LE(report, sizeof(report_digitizer_t), NULL); + Endpoint_ClearIN(); +#endif +} + /******************************************************************************* * main ******************************************************************************/ diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index 7a4a790315..099964ae56 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -158,6 +158,53 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { # endif #endif +#ifdef DIGITIZER_ENABLE +# ifndef DIGITIZER_SHARED_EP +const USB_Descriptor_HIDReport_Datatype_t PROGMEM DigitizerReport[] = { +# elif !defined(SHARED_REPORT_STARTED) +const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { +# define SHARED_REPORT_STARTED +# endif + HID_RI_USAGE_PAGE(8, 0x0D), // Digitizers + HID_RI_USAGE(8, 0x01), // Digitizer + HID_RI_COLLECTION(8, 0x01), // Application +# ifdef DIGITIZER_SHARED_EP + HID_RI_REPORT_ID(8, REPORT_ID_DIGITIZER), +# endif + HID_RI_USAGE(8, 0x20), // Stylus + HID_RI_COLLECTION(8, 0x00), // Physical + // Tip Switch (1 bit) + HID_RI_USAGE(8, 0x42), // Tip Switch + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_INPUT(8, HID_IOF_VARIABLE), + // In Range (1 bit) + HID_RI_USAGE(8, 0x32), // In Range + HID_RI_INPUT(8, HID_IOF_VARIABLE), + // Padding (6 bits) + HID_RI_REPORT_COUNT(8, 0x06), + HID_RI_INPUT(8, HID_IOF_CONSTANT | HID_IOF_VARIABLE), + + // X/Y Position (4 bytes) + HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop + HID_RI_LOGICAL_MAXIMUM(16, 0x7FFF), + HID_RI_REPORT_SIZE(8, 0x10), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_UNIT(8, 0x33), // Inch, English Linear + HID_RI_UNIT_EXPONENT(8, 0x0E), // -2 + HID_RI_USAGE(8, 0x30), // X + HID_RI_INPUT(8, HID_IOF_VARIABLE), + HID_RI_USAGE(8, 0x31), // Y + HID_RI_INPUT(8, HID_IOF_VARIABLE), + HID_RI_END_COLLECTION(0), + HID_RI_END_COLLECTION(0), +# ifndef DIGITIZER_SHARED_EP +}; +# endif +#endif + #if defined(SHARED_EP_ENABLE) && !defined(SHARED_REPORT_STARTED) const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { #endif @@ -227,6 +274,7 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { HID_RI_OUTPUT(8, HID_IOF_CONSTANT), HID_RI_END_COLLECTION(0), #endif + #ifdef SHARED_EP_ENABLE }; #endif @@ -921,6 +969,46 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { .PollingIntervalMS = USB_POLLING_INTERVAL_MS } #endif + +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) + /* + * Digitizer + */ + .Digitizer_Interface = { + .Header = { + .Size = sizeof(USB_Descriptor_Interface_t), + .Type = DTYPE_Interface + }, + .InterfaceNumber = DIGITIZER_INTERFACE, + .AlternateSetting = 0x00, + .TotalEndpoints = 1, + .Class = HID_CSCP_HIDClass, + .SubClass = HID_CSCP_NonBootSubclass, + .Protocol = HID_CSCP_NonBootProtocol, + .InterfaceStrIndex = NO_DESCRIPTOR + }, + .Digitizer_HID = { + .Header = { + .Size = sizeof(USB_HID_Descriptor_HID_t), + .Type = HID_DTYPE_HID + }, + .HIDSpec = VERSION_BCD(1, 1, 1), + .CountryCode = 0x00, + .TotalReportDescriptors = 1, + .HIDReportType = HID_DTYPE_Report, + .HIDReportLength = sizeof(DigitizerReport) + }, + .Digitizer_INEndpoint = { + .Header = { + .Size = sizeof(USB_Descriptor_Endpoint_t), + .Type = DTYPE_Endpoint + }, + .EndpointAddress = (ENDPOINT_DIR_IN | DIGITIZER_IN_EPNUM), + .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = DIGITIZER_EPSIZE, + .PollingIntervalMS = USB_POLLING_INTERVAL_MS + }, +#endif }; /* @@ -1059,6 +1147,13 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const Size = sizeof(USB_HID_Descriptor_HID_t); break; #endif +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) + case DIGITIZER_INTERFACE: + Address = &ConfigurationDescriptor.Digitizer_HID; + Size = sizeof(USB_HID_Descriptor_HID_t); + + break; +#endif } break; @@ -1108,6 +1203,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const Address = &JoystickReport; Size = sizeof(JoystickReport); break; +#endif +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) + case DIGITIZER_INTERFACE: + Address = &DigitizerReport; + Size = sizeof(DigitizerReport); + break; #endif } diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 867e549b4f..0f0c78f66c 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -135,6 +135,13 @@ typedef struct { USB_HID_Descriptor_HID_t Joystick_HID; USB_Descriptor_Endpoint_t Joystick_INEndpoint; #endif + +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) + // Digitizer HID Interface + USB_Descriptor_Interface_t Digitizer_Interface; + USB_HID_Descriptor_HID_t Digitizer_HID; + USB_Descriptor_Endpoint_t Digitizer_INEndpoint; +#endif } USB_Descriptor_Configuration_t; /* @@ -180,6 +187,10 @@ enum usb_interfaces { #if defined(JOYSTICK_ENABLE) JOYSTICK_INTERFACE, #endif + +#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) + DIGITIZER_INTERFACE, +#endif TOTAL_INTERFACES }; @@ -226,7 +237,7 @@ enum usb_endpoints { # if STM32_USB_USE_OTG1 # define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM # else - CONSOLE_OUT_EPNUM = NEXT_EPNUM, + CONSOLE_OUT_EPNUM = NEXT_EPNUM, # endif # else # define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM @@ -259,6 +270,19 @@ enum usb_endpoints { JOYSTICK_OUT_EPNUM = NEXT_EPNUM, # endif #endif + +#ifdef DIGITIZER_ENABLE +# if !defined(DIGITIZER_SHARED_EP) + DIGITIZER_IN_EPNUM = NEXT_EPNUM, +# if STM32_USB_USE_OTG1 + DIGITIZER_OUT_EPNUM = DIGITIZER_IN_EPNUM, +# else + DIGITIZER_OUT_EPNUM = NEXT_EPNUM, +# endif +# else +# define DIGITIZER_IN_EPNUM SHARED_IN_EPNUM +# endif +#endif }; #ifdef PROTOCOL_LUFA @@ -284,5 +308,6 @@ enum usb_endpoints { #define CDC_NOTIFICATION_EPSIZE 8 #define CDC_EPSIZE 16 #define JOYSTICK_EPSIZE 8 +#define DIGITIZER_EPSIZE 8 uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress); diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index 98cebf6012..485b20c900 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -292,6 +292,14 @@ static void send_consumer(uint16_t data) { #endif } +void send_digitizer(report_digitizer_t *report) { +#ifdef DIGITIZER_ENABLE + if (usbInterruptIsReadyShared()) { + usbSetInterruptShared((void *)report, sizeof(report_digitizer_t)); + } +#endif +} + /*------------------------------------------------------------------* * Request from host * *------------------------------------------------------------------*/ @@ -510,8 +518,46 @@ const PROGMEM uchar shared_hid_report[] = { 0x95, 0x01, // Report Count (1) 0x75, 0x10, // Report Size (16) 0x81, 0x00, // Input (Data, Array, Absolute) - 0xC0 // End Collection + 0xC0, // End Collection +#endif + +#ifdef DIGITIZER_ENABLE + // Digitizer report descriptor + 0x05, 0x0D, // Usage Page (Digitizers) + 0x09, 0x01, // Usage (Digitizer) + 0xA1, 0x01, // Collection (Application) + 0x85, REPORT_ID_DIGITIZER, // Report ID + 0x09, 0x22, // Usage (Finger) + 0xA1, 0x00, // Collection (Physical) + // Tip Switch (1 bit) + 0x09, 0x42, // Usage (Tip Switch) + 0x15, 0x00, // Logical Minimum + 0x25, 0x01, // Logical Maximum + 0x95, 0x01, // Report Count (1) + 0x75, 0x01, // Report Size (16) + 0x81, 0x02, // Input (Data, Variable, Absolute) + // In Range (1 bit) + 0x09, 0x32, // Usage (In Range) + 0x81, 0x02, // Input (Data, Variable, Absolute) + // Padding (6 bits) + 0x95, 0x06, // Report Count (6) + 0x81, 0x03, // Input (Constant) + + // X/Y Position (4 bytes) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x26, 0xFF, 0x7F, // Logical Maximum (32767) + 0x95, 0x01, // Report Count (1) + 0x75, 0x10, // Report Size (16) + 0x65, 0x33, // Unit (Inch, English Linear) + 0x55, 0x0E, // Unit Exponent (-2) + 0x09, 0x30, // Usage (X) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x09, 0x31, // Usage (Y) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0xC0, // End Collection + 0xC0 // End Collection #endif + #ifdef SHARED_EP_ENABLE }; #endif -- cgit v1.2.3 From 96e2b13d1de227cdc2b918fb0292bd832d346a25 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Wed, 18 Aug 2021 00:11:07 +0100 Subject: Begin to carve out platform/protocol API - Single main loop (#13843) * Begin to carve out platform/protocol API * Fix up after rebase --- build_keyboard.mk | 6 +- quantum/main.c | 41 ++++++ tmk_core/common.mk | 1 + tmk_core/common/arm_atsam/platform.c | 21 +++ tmk_core/common/avr/platform.c | 21 +++ tmk_core/common/chibios/platform.c | 22 +++ tmk_core/common/test/platform.c | 21 +++ tmk_core/protocol/chibios.mk | 2 +- tmk_core/protocol/chibios/chibios.c | 255 +++++++++++++++++++++++++++++++++ tmk_core/protocol/chibios/main.c | 263 ----------------------------------- tmk_core/protocol/lufa/lufa.c | 64 ++++----- tmk_core/protocol/vusb.mk | 2 +- tmk_core/protocol/vusb/main.c | 179 ------------------------ tmk_core/protocol/vusb/protocol.c | 178 ++++++++++++++++++++++++ 14 files changed, 596 insertions(+), 480 deletions(-) create mode 100644 quantum/main.c create mode 100644 tmk_core/common/arm_atsam/platform.c create mode 100644 tmk_core/common/avr/platform.c create mode 100644 tmk_core/common/chibios/platform.c create mode 100644 tmk_core/common/test/platform.c create mode 100644 tmk_core/protocol/chibios/chibios.c delete mode 100644 tmk_core/protocol/chibios/main.c delete mode 100644 tmk_core/protocol/vusb/main.c create mode 100644 tmk_core/protocol/vusb/protocol.c (limited to 'tmk_core/common') diff --git a/build_keyboard.mk b/build_keyboard.mk index 62a7ba6a0d..46d1e45667 100644 --- a/build_keyboard.mk +++ b/build_keyboard.mk @@ -338,9 +338,11 @@ ifneq ("$(wildcard $(KEYMAP_PATH)/config.h)","") endif # project specific files -SRC += $(KEYBOARD_SRC) \ +SRC += \ + $(KEYBOARD_SRC) \ $(KEYMAP_C) \ - $(QUANTUM_SRC) + $(QUANTUM_SRC) \ + $(QUANTUM_DIR)/main.c \ # Optimize size but this may cause error "relocation truncated to fit" #EXTRALDFLAGS = -Wl,--relax diff --git a/quantum/main.c b/quantum/main.c new file mode 100644 index 0000000000..2cbcd73d8f --- /dev/null +++ b/quantum/main.c @@ -0,0 +1,41 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "keyboard.h" + +void platform_setup(void); + +void protocol_setup(void); +void protocol_init(void); +void protocol_task(void); + +/** \brief Main + * + * FIXME: Needs doc + */ +int main(void) __attribute__((weak)); +int main(void) { + platform_setup(); + protocol_setup(); + + protocol_init(); + + /* Main loop */ + while (true) { + protocol_task(); + housekeeping_task(); + } +} diff --git a/tmk_core/common.mk b/tmk_core/common.mk index 923a736d35..57de8d95e8 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -12,6 +12,7 @@ TMK_COMMON_SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/report.c \ $(COMMON_DIR)/sync_timer.c \ $(COMMON_DIR)/usb_util.c \ + $(PLATFORM_COMMON_DIR)/platform.c \ $(PLATFORM_COMMON_DIR)/suspend.c \ $(PLATFORM_COMMON_DIR)/timer.c \ $(PLATFORM_COMMON_DIR)/bootloader.c \ diff --git a/tmk_core/common/arm_atsam/platform.c b/tmk_core/common/arm_atsam/platform.c new file mode 100644 index 0000000000..3e35b4fe4c --- /dev/null +++ b/tmk_core/common/arm_atsam/platform.c @@ -0,0 +1,21 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform_deps.h" + +void platform_setup(void) { + // do nothing +} diff --git a/tmk_core/common/avr/platform.c b/tmk_core/common/avr/platform.c new file mode 100644 index 0000000000..3e35b4fe4c --- /dev/null +++ b/tmk_core/common/avr/platform.c @@ -0,0 +1,21 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform_deps.h" + +void platform_setup(void) { + // do nothing +} diff --git a/tmk_core/common/chibios/platform.c b/tmk_core/common/chibios/platform.c new file mode 100644 index 0000000000..d4a229f278 --- /dev/null +++ b/tmk_core/common/chibios/platform.c @@ -0,0 +1,22 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform_deps.h" + +void platform_setup(void) { + halInit(); + chSysInit(); +} \ No newline at end of file diff --git a/tmk_core/common/test/platform.c b/tmk_core/common/test/platform.c new file mode 100644 index 0000000000..8ddceeda8f --- /dev/null +++ b/tmk_core/common/test/platform.c @@ -0,0 +1,21 @@ +/* Copyright 2021 QMK + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform_deps.h" + +void platform_setup(void) { + // do nothing +} \ No newline at end of file diff --git a/tmk_core/protocol/chibios.mk b/tmk_core/protocol/chibios.mk index d01697835b..a7f2d8e93d 100644 --- a/tmk_core/protocol/chibios.mk +++ b/tmk_core/protocol/chibios.mk @@ -3,7 +3,7 @@ CHIBIOS_DIR = $(PROTOCOL_DIR)/chibios SRC += $(CHIBIOS_DIR)/usb_main.c -SRC += $(CHIBIOS_DIR)/main.c +SRC += $(CHIBIOS_DIR)/chibios.c SRC += usb_descriptor.c SRC += $(CHIBIOS_DIR)/usb_driver.c SRC += $(CHIBIOS_DIR)/usb_util.c diff --git a/tmk_core/protocol/chibios/chibios.c b/tmk_core/protocol/chibios/chibios.c new file mode 100644 index 0000000000..78a2e3fcbb --- /dev/null +++ b/tmk_core/protocol/chibios/chibios.c @@ -0,0 +1,255 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + +#include +#include + +#include "usb_main.h" + +/* TMK includes */ +#include "report.h" +#include "host.h" +#include "host_driver.h" +#include "keyboard.h" +#include "action.h" +#include "action_util.h" +#include "mousekey.h" +#include "led.h" +#include "sendchar.h" +#include "debug.h" +#include "print.h" + +#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP +// Change this to be TRUE once we've migrated keyboards to the new init system +// Remember to change docs/platformdev_chibios_earlyinit.md as well. +# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP FALSE +#endif + +#ifdef SLEEP_LED_ENABLE +# include "sleep_led.h" +#endif +#ifdef SERIAL_LINK_ENABLE +# include "serial_link/system/serial_link.h" +#endif +#ifdef VISUALIZER_ENABLE +# include "visualizer/visualizer.h" +#endif +#ifdef MIDI_ENABLE +# include "qmk_midi.h" +#endif +#include "suspend.h" +#include "wait.h" + +/* ------------------------- + * TMK host driver defs + * ------------------------- + */ + +/* declarations */ +uint8_t keyboard_leds(void); +void send_keyboard(report_keyboard_t *report); +void send_mouse(report_mouse_t *report); +void send_system(uint16_t data); +void send_consumer(uint16_t data); +void send_digitizer(report_digitizer_t *report); + +/* host struct */ +host_driver_t chibios_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer}; + +#ifdef VIRTSER_ENABLE +void virtser_task(void); +#endif + +#ifdef RAW_ENABLE +void raw_hid_task(void); +#endif + +#ifdef CONSOLE_ENABLE +void console_task(void); +#endif +#ifdef MIDI_ENABLE +void midi_ep_task(void); +#endif + +/* TESTING + * Amber LED blinker thread, times are in milliseconds. + */ +/* set this variable to non-zero anywhere to blink once */ +// static THD_WORKING_AREA(waThread1, 128); +// static THD_FUNCTION(Thread1, arg) { + +// (void)arg; +// chRegSetThreadName("blinker"); +// while (true) { +// systime_t time; + +// time = USB_DRIVER.state == USB_ACTIVE ? 250 : 500; +// palClearLine(LINE_CAPS_LOCK); +// chSysPolledDelayX(MS2RTC(STM32_HCLK, time)); +// palSetLine(LINE_CAPS_LOCK); +// chSysPolledDelayX(MS2RTC(STM32_HCLK, time)); +// } +// } + +/* Early initialisation + */ +__attribute__((weak)) void early_hardware_init_pre(void) { +#if EARLY_INIT_PERFORM_BOOTLOADER_JUMP + void enter_bootloader_mode_if_requested(void); + enter_bootloader_mode_if_requested(); +#endif // EARLY_INIT_PERFORM_BOOTLOADER_JUMP +} + +__attribute__((weak)) void early_hardware_init_post(void) {} + +__attribute__((weak)) void board_init(void) {} + +// This overrides what's normally in ChibiOS board definitions +void __early_init(void) { + early_hardware_init_pre(); + + // This is the renamed equivalent of __early_init in the board.c file + void __chibios_override___early_init(void); + __chibios_override___early_init(); + + early_hardware_init_post(); +} + +// This overrides what's normally in ChibiOS board definitions +void boardInit(void) { + // This is the renamed equivalent of boardInit in the board.c file + void __chibios_override_boardInit(void); + __chibios_override_boardInit(); + + board_init(); +} + +void protocol_setup(void) { + // TESTING + // chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL); + + keyboard_setup(); +} + +void protocol_init(void) { + /* Init USB */ + usb_event_queue_init(); + init_usb_driver(&USB_DRIVER); + +#ifdef MIDI_ENABLE + setup_midi(); +#endif + +#ifdef SERIAL_LINK_ENABLE + init_serial_link(); +#endif + +#ifdef VISUALIZER_ENABLE + visualizer_init(); +#endif + + host_driver_t *driver = NULL; + + /* Wait until the USB or serial link is active */ + while (true) { +#if defined(WAIT_FOR_USB) || defined(SERIAL_LINK_ENABLE) + if (USB_DRIVER.state == USB_ACTIVE) { + driver = &chibios_driver; + break; + } +#else + driver = &chibios_driver; + break; +#endif +#ifdef SERIAL_LINK_ENABLE + if (is_serial_link_connected()) { + driver = get_serial_link_driver(); + break; + } + serial_link_update(); +#endif + wait_ms(50); + } + + /* Do need to wait here! + * Otherwise the next print might start a transfer on console EP + * before the USB is completely ready, which sometimes causes + * HardFaults. + */ + wait_ms(50); + + print("USB configured.\n"); + + /* init TMK modules */ + keyboard_init(); + host_set_driver(driver); + +#ifdef SLEEP_LED_ENABLE + sleep_led_init(); +#endif + + print("Keyboard start.\n"); +} + +void protocol_task(void) { + usb_event_queue_task(); + +#if !defined(NO_USB_STARTUP_CHECK) + if (USB_DRIVER.state == USB_SUSPENDED) { + print("[s]"); +# ifdef VISUALIZER_ENABLE + visualizer_suspend(); +# endif + while (USB_DRIVER.state == USB_SUSPENDED) { + /* Do this in the suspended state */ +# ifdef SERIAL_LINK_ENABLE + serial_link_update(); +# endif + suspend_power_down(); // on AVR this deep sleeps for 15ms + /* Remote wakeup */ + if (suspend_wakeup_condition()) { + usbWakeupHost(&USB_DRIVER); + restart_usb_driver(&USB_DRIVER); + } + } + /* Woken up */ + // variables has been already cleared by the wakeup hook + send_keyboard_report(); +# ifdef MOUSEKEY_ENABLE + mousekey_send(); +# endif /* MOUSEKEY_ENABLE */ + +# ifdef VISUALIZER_ENABLE + visualizer_resume(); +# endif + } +#endif + + keyboard_task(); +#ifdef CONSOLE_ENABLE + console_task(); +#endif +#ifdef MIDI_ENABLE + midi_ep_task(); +#endif +#ifdef VIRTSER_ENABLE + virtser_task(); +#endif +#ifdef RAW_ENABLE + raw_hid_task(); +#endif +} diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c deleted file mode 100644 index e41d6ff195..0000000000 --- a/tmk_core/protocol/chibios/main.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * (c) 2015 flabberast - * - * Based on the following work: - * - Guillaume Duc's raw hid example (MIT License) - * https://github.com/guiduc/usb-hid-chibios-example - * - PJRC Teensy examples (MIT License) - * https://www.pjrc.com/teensy/usb_keyboard.html - * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) - * https://github.com/tmk/tmk_keyboard/ - * - ChibiOS demo code (Apache 2.0 License) - * http://www.chibios.org - * - * Since some GPL'd code is used, this work is licensed under - * GPL v2 or later. - */ - -#include -#include - -#include "usb_main.h" - -/* TMK includes */ -#include "report.h" -#include "host.h" -#include "host_driver.h" -#include "keyboard.h" -#include "action.h" -#include "action_util.h" -#include "mousekey.h" -#include "led.h" -#include "sendchar.h" -#include "debug.h" -#include "print.h" - -#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP -// Change this to be TRUE once we've migrated keyboards to the new init system -// Remember to change docs/platformdev_chibios_earlyinit.md as well. -# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP FALSE -#endif - -#ifdef SLEEP_LED_ENABLE -# include "sleep_led.h" -#endif -#ifdef SERIAL_LINK_ENABLE -# include "serial_link/system/serial_link.h" -#endif -#ifdef VISUALIZER_ENABLE -# include "visualizer/visualizer.h" -#endif -#ifdef MIDI_ENABLE -# include "qmk_midi.h" -#endif -#include "suspend.h" -#include "wait.h" - -/* ------------------------- - * TMK host driver defs - * ------------------------- - */ - -/* declarations */ -uint8_t keyboard_leds(void); -void send_keyboard(report_keyboard_t *report); -void send_mouse(report_mouse_t *report); -void send_system(uint16_t data); -void send_consumer(uint16_t data); -void send_digitizer(report_digitizer_t *report); - -/* host struct */ -host_driver_t chibios_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer}; - -#ifdef VIRTSER_ENABLE -void virtser_task(void); -#endif - -#ifdef RAW_ENABLE -void raw_hid_task(void); -#endif - -#ifdef CONSOLE_ENABLE -void console_task(void); -#endif -#ifdef MIDI_ENABLE -void midi_ep_task(void); -#endif - -/* TESTING - * Amber LED blinker thread, times are in milliseconds. - */ -/* set this variable to non-zero anywhere to blink once */ -// static THD_WORKING_AREA(waThread1, 128); -// static THD_FUNCTION(Thread1, arg) { - -// (void)arg; -// chRegSetThreadName("blinker"); -// while (true) { -// systime_t time; - -// time = USB_DRIVER.state == USB_ACTIVE ? 250 : 500; -// palClearLine(LINE_CAPS_LOCK); -// chSysPolledDelayX(MS2RTC(STM32_HCLK, time)); -// palSetLine(LINE_CAPS_LOCK); -// chSysPolledDelayX(MS2RTC(STM32_HCLK, time)); -// } -// } - -/* Early initialisation - */ -__attribute__((weak)) void early_hardware_init_pre(void) { -#if EARLY_INIT_PERFORM_BOOTLOADER_JUMP - void enter_bootloader_mode_if_requested(void); - enter_bootloader_mode_if_requested(); -#endif // EARLY_INIT_PERFORM_BOOTLOADER_JUMP -} - -__attribute__((weak)) void early_hardware_init_post(void) {} - -__attribute__((weak)) void board_init(void) {} - -// This overrides what's normally in ChibiOS board definitions -void __early_init(void) { - early_hardware_init_pre(); - - // This is the renamed equivalent of __early_init in the board.c file - void __chibios_override___early_init(void); - __chibios_override___early_init(); - - early_hardware_init_post(); -} - -// This overrides what's normally in ChibiOS board definitions -void boardInit(void) { - // This is the renamed equivalent of boardInit in the board.c file - void __chibios_override_boardInit(void); - __chibios_override_boardInit(); - - board_init(); -} - -/* Main thread - */ -int main(void) { - /* ChibiOS/RT init */ - halInit(); - chSysInit(); - - // TESTING - // chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL); - - keyboard_setup(); - - /* Init USB */ - usb_event_queue_init(); - init_usb_driver(&USB_DRIVER); - -#ifdef MIDI_ENABLE - setup_midi(); -#endif - -#ifdef SERIAL_LINK_ENABLE - init_serial_link(); -#endif - -#ifdef VISUALIZER_ENABLE - visualizer_init(); -#endif - - host_driver_t *driver = NULL; - - /* Wait until the USB or serial link is active */ - while (true) { -#if defined(WAIT_FOR_USB) || defined(SERIAL_LINK_ENABLE) - if (USB_DRIVER.state == USB_ACTIVE) { - driver = &chibios_driver; - break; - } -#else - driver = &chibios_driver; - break; -#endif -#ifdef SERIAL_LINK_ENABLE - if (is_serial_link_connected()) { - driver = get_serial_link_driver(); - break; - } - serial_link_update(); -#endif - wait_ms(50); - } - - /* Do need to wait here! - * Otherwise the next print might start a transfer on console EP - * before the USB is completely ready, which sometimes causes - * HardFaults. - */ - wait_ms(50); - - print("USB configured.\n"); - - /* init TMK modules */ - keyboard_init(); - host_set_driver(driver); - -#ifdef SLEEP_LED_ENABLE - sleep_led_init(); -#endif - - print("Keyboard start.\n"); - - /* Main loop */ - while (true) { - usb_event_queue_task(); - -#if !defined(NO_USB_STARTUP_CHECK) - if (USB_DRIVER.state == USB_SUSPENDED) { - print("[s]"); -# ifdef VISUALIZER_ENABLE - visualizer_suspend(); -# endif - while (USB_DRIVER.state == USB_SUSPENDED) { - /* Do this in the suspended state */ -# ifdef SERIAL_LINK_ENABLE - serial_link_update(); -# endif - suspend_power_down(); // on AVR this deep sleeps for 15ms - /* Remote wakeup */ - if (suspend_wakeup_condition()) { - usbWakeupHost(&USB_DRIVER); - restart_usb_driver(&USB_DRIVER); - } - } - /* Woken up */ - // variables has been already cleared by the wakeup hook - send_keyboard_report(); -# ifdef MOUSEKEY_ENABLE - mousekey_send(); -# endif /* MOUSEKEY_ENABLE */ - -# ifdef VISUALIZER_ENABLE - visualizer_resume(); -# endif - } -#endif - - keyboard_task(); -#ifdef CONSOLE_ENABLE - console_task(); -#endif -#ifdef MIDI_ENABLE - midi_ep_task(); -#endif -#ifdef VIRTSER_ENABLE - virtser_task(); -#endif -#ifdef RAW_ENABLE - raw_hid_task(); -#endif - - // Run housekeeping - housekeeping_task(); - } -} diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 8ddb8b1c4b..e638dbc0fb 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -1033,18 +1033,16 @@ static void setup_usb(void) { USB_Device_EnableSOFEvents(); } -/** \brief Main - * - * FIXME: Needs doc - */ -int main(void) __attribute__((weak)); -int main(void) { +void protocol_setup(void) { #ifdef MIDI_ENABLE setup_midi(); #endif setup_mcu(); keyboard_setup(); +} + +void protocol_init(void) { setup_usb(); sei(); @@ -1078,57 +1076,55 @@ int main(void) { #endif print("Keyboard start.\n"); - while (1) { +} + +void protocol_task(void) { #if !defined(NO_USB_STARTUP_CHECK) - if (USB_DeviceState == DEVICE_STATE_Suspended) { - print("[s]"); - while (USB_DeviceState == DEVICE_STATE_Suspended) { - suspend_power_down(); - if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) { - USB_Device_SendRemoteWakeup(); - clear_keyboard(); + if (USB_DeviceState == DEVICE_STATE_Suspended) { + print("[s]"); + while (USB_DeviceState == DEVICE_STATE_Suspended) { + suspend_power_down(); + if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) { + USB_Device_SendRemoteWakeup(); + clear_keyboard(); # if USB_SUSPEND_WAKEUP_DELAY > 0 - // Some hubs, kvm switches, and monitors do - // weird things, with USB device state bouncing - // around wildly on wakeup, yielding race - // conditions that can corrupt the keyboard state. - // - // Pause for a while to let things settle... - wait_ms(USB_SUSPEND_WAKEUP_DELAY); + // Some hubs, kvm switches, and monitors do + // weird things, with USB device state bouncing + // around wildly on wakeup, yielding race + // conditions that can corrupt the keyboard state. + // + // Pause for a while to let things settle... + wait_ms(USB_SUSPEND_WAKEUP_DELAY); # endif - } } - suspend_wakeup_init(); } + suspend_wakeup_init(); + } #endif - keyboard_task(); + keyboard_task(); #ifdef MIDI_ENABLE - MIDI_Device_USBTask(&USB_MIDI_Interface); + MIDI_Device_USBTask(&USB_MIDI_Interface); #endif #ifdef MODULE_ADAFRUIT_BLE - adafruit_ble_task(); + adafruit_ble_task(); #endif #ifdef VIRTSER_ENABLE - virtser_task(); - CDC_Device_USBTask(&cdc_device); + virtser_task(); + CDC_Device_USBTask(&cdc_device); #endif #ifdef RAW_ENABLE - raw_hid_task(); + raw_hid_task(); #endif #if !defined(INTERRUPT_CONTROL_ENDPOINT) - USB_USBTask(); + USB_USBTask(); #endif - - // Run housekeeping - housekeeping_task(); - } } uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint16_t wIndex, const void **const DescriptorAddress) { return get_usb_descriptor(wValue, wIndex, DescriptorAddress); } diff --git a/tmk_core/protocol/vusb.mk b/tmk_core/protocol/vusb.mk index e4d013b38d..5572597e21 100644 --- a/tmk_core/protocol/vusb.mk +++ b/tmk_core/protocol/vusb.mk @@ -3,7 +3,7 @@ VUSB_DIR = protocol/vusb # Path to the V-USB library VUSB_PATH = $(LIB_PATH)/vusb -SRC += $(VUSB_DIR)/main.c \ +SRC += $(VUSB_DIR)/protocol.c \ $(VUSB_DIR)/vusb.c \ $(VUSB_DIR)/usb_util.c \ $(VUSB_PATH)/usbdrv/usbdrv.c \ diff --git a/tmk_core/protocol/vusb/main.c b/tmk_core/protocol/vusb/main.c deleted file mode 100644 index 53926a7493..0000000000 --- a/tmk_core/protocol/vusb/main.c +++ /dev/null @@ -1,179 +0,0 @@ -/* Name: main.c - * Project: hid-mouse, a very simple HID example - * Author: Christian Starkjohann - * Creation Date: 2008-04-07 - * Tabsize: 4 - * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH - * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) - * This Revision: $Id: main.c 790 2010-05-30 21:00:26Z cs $ - */ - -#include - -#include -#include -#include -#include - -#include - -#include "vusb.h" - -#include "keyboard.h" -#include "host.h" -#include "timer.h" -#include "print.h" -#include "suspend.h" -#include "wait.h" -#include "sendchar.h" - -#ifdef SLEEP_LED_ENABLE -# include "sleep_led.h" -#endif - -#ifdef CONSOLE_ENABLE -void console_task(void); -#endif - -#ifdef RAW_ENABLE -void raw_hid_task(void); -#endif - -/* This is from main.c of USBaspLoader */ -static void initForUsbConnectivity(void) { - uint8_t i = 0; - - usbInit(); - /* enforce USB re-enumerate: */ - usbDeviceDisconnect(); /* do this while interrupts are disabled */ - while (--i) { /* fake USB disconnect for > 250 ms */ - wdt_reset(); - wait_ms(1); - } - usbDeviceConnect(); -} - -static void vusb_send_remote_wakeup(void) { - cli(); - - uint8_t ddr_orig = USBDDR; - USBOUT |= (1 << USBMINUS); - USBDDR = ddr_orig | USBMASK; - USBOUT ^= USBMASK; - - wait_ms(25); - - USBOUT ^= USBMASK; - USBDDR = ddr_orig; - USBOUT &= ~(1 << USBMINUS); - - sei(); -} - -bool vusb_suspended = false; - -static void vusb_suspend(void) { - vusb_suspended = true; - -#ifdef SLEEP_LED_ENABLE - sleep_led_enable(); -#endif - - suspend_power_down(); -} - -#if USB_COUNT_SOF -static void vusb_wakeup(void) { - vusb_suspended = false; - suspend_wakeup_init(); - -# ifdef SLEEP_LED_ENABLE - sleep_led_disable(); -# endif -} -#endif - -/** \brief Setup USB - * - * FIXME: Needs doc - */ -static void setup_usb(void) { initForUsbConnectivity(); } - -/** \brief Main - * - * FIXME: Needs doc - */ -int main(void) __attribute__((weak)); -int main(void) { -#if USB_COUNT_SOF - uint16_t sof_timer = timer_read(); -#endif - -#ifdef CLKPR - // avoid unintentional changes of clock frequency in devices that have a - // clock prescaler - clock_prescale_set(clock_div_1); -#endif - keyboard_setup(); - setup_usb(); - sei(); - keyboard_init(); - host_set_driver(vusb_driver()); - - wait_ms(50); - -#ifdef SLEEP_LED_ENABLE - sleep_led_init(); -#endif - - while (1) { -#if USB_COUNT_SOF - if (usbSofCount != 0) { - usbSofCount = 0; - sof_timer = timer_read(); - if (vusb_suspended) { - vusb_wakeup(); - } - } else { - // Suspend when no SOF in 3ms-10ms(7.1.7.4 Suspending of USB1.1) - if (!vusb_suspended && timer_elapsed(sof_timer) > 5) { - vusb_suspend(); - } - } -#endif - if (vusb_suspended) { - vusb_suspend(); - if (suspend_wakeup_condition()) { - vusb_send_remote_wakeup(); - } - } else { - usbPoll(); - - // TODO: configuration process is inconsistent. it sometime fails. - // To prevent failing to configure NOT scan keyboard during configuration - if (usbConfiguration && usbInterruptIsReady()) { - keyboard_task(); - } - vusb_transfer_keyboard(); - -#ifdef RAW_ENABLE - usbPoll(); - - if (usbConfiguration && usbInterruptIsReady3()) { - raw_hid_task(); - } -#endif - -#ifdef CONSOLE_ENABLE - usbPoll(); - - if (usbConfiguration && usbInterruptIsReady3()) { - console_task(); - } -#endif - - // Run housekeeping - housekeeping_task(); - } - } -} diff --git a/tmk_core/protocol/vusb/protocol.c b/tmk_core/protocol/vusb/protocol.c new file mode 100644 index 0000000000..89dc795b21 --- /dev/null +++ b/tmk_core/protocol/vusb/protocol.c @@ -0,0 +1,178 @@ +/* Name: main.c + * Project: hid-mouse, a very simple HID example + * Author: Christian Starkjohann + * Creation Date: 2008-04-07 + * Tabsize: 4 + * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id: main.c 790 2010-05-30 21:00:26Z cs $ + */ + +#include + +#include +#include +#include +#include + +#include + +#include "vusb.h" + +#include "keyboard.h" +#include "host.h" +#include "timer.h" +#include "print.h" +#include "suspend.h" +#include "wait.h" +#include "sendchar.h" + +#ifdef SLEEP_LED_ENABLE +# include "sleep_led.h" +#endif + +#ifdef CONSOLE_ENABLE +void console_task(void); +#endif + +#ifdef RAW_ENABLE +void raw_hid_task(void); +#endif + +/* This is from main.c of USBaspLoader */ +static void initForUsbConnectivity(void) { + uint8_t i = 0; + + usbInit(); + /* enforce USB re-enumerate: */ + usbDeviceDisconnect(); /* do this while interrupts are disabled */ + while (--i) { /* fake USB disconnect for > 250 ms */ + wdt_reset(); + wait_ms(1); + } + usbDeviceConnect(); +} + +static void vusb_send_remote_wakeup(void) { + cli(); + + uint8_t ddr_orig = USBDDR; + USBOUT |= (1 << USBMINUS); + USBDDR = ddr_orig | USBMASK; + USBOUT ^= USBMASK; + + wait_ms(25); + + USBOUT ^= USBMASK; + USBDDR = ddr_orig; + USBOUT &= ~(1 << USBMINUS); + + sei(); +} + +bool vusb_suspended = false; + +static void vusb_suspend(void) { + vusb_suspended = true; + +#ifdef SLEEP_LED_ENABLE + sleep_led_enable(); +#endif + + suspend_power_down(); +} + +#if USB_COUNT_SOF +static void vusb_wakeup(void) { + vusb_suspended = false; + suspend_wakeup_init(); + +# ifdef SLEEP_LED_ENABLE + sleep_led_disable(); +# endif +} +#endif + +/** \brief Setup USB + * + * FIXME: Needs doc + */ +static void setup_usb(void) { initForUsbConnectivity(); } + +uint16_t sof_timer = 0; + +void protocol_setup(void) { +#if USB_COUNT_SOF + sof_timer = timer_read(); +#endif + +#ifdef CLKPR + // avoid unintentional changes of clock frequency in devices that have a + // clock prescaler + clock_prescale_set(clock_div_1); +#endif + keyboard_setup(); +} + +void protocol_init(void) { + setup_usb(); + sei(); + + keyboard_init(); + + host_set_driver(vusb_driver()); + + wait_ms(50); + +#ifdef SLEEP_LED_ENABLE + sleep_led_init(); +#endif +} + +void protocol_task(void) { +#if USB_COUNT_SOF + if (usbSofCount != 0) { + usbSofCount = 0; + sof_timer = timer_read(); + if (vusb_suspended) { + vusb_wakeup(); + } + } else { + // Suspend when no SOF in 3ms-10ms(7.1.7.4 Suspending of USB1.1) + if (!vusb_suspended && timer_elapsed(sof_timer) > 5) { + vusb_suspend(); + } + } +#endif + if (vusb_suspended) { + vusb_suspend(); + if (suspend_wakeup_condition()) { + vusb_send_remote_wakeup(); + } + } else { + usbPoll(); + + // TODO: configuration process is inconsistent. it sometime fails. + // To prevent failing to configure NOT scan keyboard during configuration + if (usbConfiguration && usbInterruptIsReady()) { + keyboard_task(); + } + vusb_transfer_keyboard(); + +#ifdef RAW_ENABLE + usbPoll(); + + if (usbConfiguration && usbInterruptIsReady3()) { + raw_hid_task(); + } +#endif + +#ifdef CONSOLE_ENABLE + usbPoll(); + + if (usbConfiguration && usbInterruptIsReady3()) { + console_task(); + } +#endif + } +} -- cgit v1.2.3 From b8e913c8db73ebf890e4604ee41991a34354a600 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Wed, 18 Aug 2021 00:18:58 +0100 Subject: Migrate platform independent code from tmk_core -> quantum (#13673) * Migrate action|keyboard|keycode|eeconfig from tmk_core -> quantum --- common_features.mk | 7 + quantum/action.c | 1126 ++++++++++++++++++++++++++++++++++++++ quantum/action.h | 132 +++++ quantum/action_code.h | 308 +++++++++++ quantum/action_layer.c | 279 ++++++++++ quantum/action_layer.h | 147 +++++ quantum/action_macro.c | 93 ++++ quantum/action_macro.h | 123 +++++ quantum/action_tapping.c | 456 +++++++++++++++ quantum/action_tapping.h | 42 ++ quantum/action_util.c | 455 +++++++++++++++ quantum/action_util.h | 105 ++++ quantum/eeconfig.c | 211 +++++++ quantum/eeconfig.h | 113 ++++ quantum/keyboard.c | 569 +++++++++++++++++++ quantum/keyboard.h | 90 +++ quantum/keycode.h | 560 +++++++++++++++++++ quantum/via.h | 2 +- tmk_core/common.mk | 10 +- tmk_core/common/action.c | 1126 -------------------------------------- tmk_core/common/action.h | 132 ----- tmk_core/common/action_code.h | 308 ----------- tmk_core/common/action_layer.c | 279 ---------- tmk_core/common/action_layer.h | 147 ----- tmk_core/common/action_macro.c | 93 ---- tmk_core/common/action_macro.h | 123 ----- tmk_core/common/action_tapping.c | 456 --------------- tmk_core/common/action_tapping.h | 42 -- tmk_core/common/action_util.c | 455 --------------- tmk_core/common/action_util.h | 105 ---- tmk_core/common/eeconfig.c | 211 ------- tmk_core/common/eeconfig.h | 113 ---- tmk_core/common/keyboard.c | 565 ------------------- tmk_core/common/keyboard.h | 90 --- tmk_core/common/keycode.h | 560 ------------------- 35 files changed, 4819 insertions(+), 4814 deletions(-) create mode 100644 quantum/action.c create mode 100644 quantum/action.h create mode 100644 quantum/action_code.h create mode 100644 quantum/action_layer.c create mode 100644 quantum/action_layer.h create mode 100644 quantum/action_macro.c create mode 100644 quantum/action_macro.h create mode 100644 quantum/action_tapping.c create mode 100644 quantum/action_tapping.h create mode 100644 quantum/action_util.c create mode 100644 quantum/action_util.h create mode 100644 quantum/eeconfig.c create mode 100644 quantum/eeconfig.h create mode 100644 quantum/keyboard.c create mode 100644 quantum/keyboard.h create mode 100644 quantum/keycode.h delete mode 100644 tmk_core/common/action.c delete mode 100644 tmk_core/common/action.h delete mode 100644 tmk_core/common/action_code.h delete mode 100644 tmk_core/common/action_layer.c delete mode 100644 tmk_core/common/action_layer.h delete mode 100644 tmk_core/common/action_macro.c delete mode 100644 tmk_core/common/action_macro.h delete mode 100644 tmk_core/common/action_tapping.c delete mode 100644 tmk_core/common/action_tapping.h delete mode 100644 tmk_core/common/action_util.c delete mode 100644 tmk_core/common/action_util.h delete mode 100644 tmk_core/common/eeconfig.c delete mode 100644 tmk_core/common/eeconfig.h delete mode 100644 tmk_core/common/keyboard.c delete mode 100644 tmk_core/common/keyboard.h delete mode 100644 tmk_core/common/keycode.h (limited to 'tmk_core/common') diff --git a/common_features.mk b/common_features.mk index 1eece0242e..d09d4f1eff 100644 --- a/common_features.mk +++ b/common_features.mk @@ -20,6 +20,13 @@ QUANTUM_SRC += \ $(QUANTUM_DIR)/send_string.c \ $(QUANTUM_DIR)/bitwise.c \ $(QUANTUM_DIR)/led.c \ + $(QUANTUM_DIR)/action.c \ + $(QUANTUM_DIR)/action_layer.c \ + $(QUANTUM_DIR)/action_macro.c \ + $(QUANTUM_DIR)/action_tapping.c \ + $(QUANTUM_DIR)/action_util.c \ + $(QUANTUM_DIR)/eeconfig.c \ + $(QUANTUM_DIR)/keyboard.c \ $(QUANTUM_DIR)/keymap_common.c \ $(QUANTUM_DIR)/keycode_config.c \ $(QUANTUM_DIR)/logging/debug.c \ diff --git a/quantum/action.c b/quantum/action.c new file mode 100644 index 0000000000..d19fd2a045 --- /dev/null +++ b/quantum/action.c @@ -0,0 +1,1126 @@ +/* +Copyright 2012,2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "host.h" +#include "keycode.h" +#include "keyboard.h" +#include "mousekey.h" +#include "command.h" +#include "led.h" +#include "action_layer.h" +#include "action_tapping.h" +#include "action_macro.h" +#include "action_util.h" +#include "action.h" +#include "wait.h" + +#ifdef BACKLIGHT_ENABLE +# include "backlight.h" +#endif + +#ifdef DEBUG_ACTION +# include "debug.h" +#else +# include "nodebug.h" +#endif + +#ifdef POINTING_DEVICE_ENABLE +# include "pointing_device.h" +#endif + +int tp_buttons; + +#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) +int retro_tapping_counter = 0; +#endif + +#ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY +__attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; } +#endif + +#ifdef RETRO_TAPPING_PER_KEY +__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; } +#endif + +__attribute__((weak)) bool pre_process_record_quantum(keyrecord_t *record) { return true; } + +#ifndef TAP_CODE_DELAY +# define TAP_CODE_DELAY 0 +#endif +#ifndef TAP_HOLD_CAPS_DELAY +# define TAP_HOLD_CAPS_DELAY 80 +#endif +/** \brief Called to execute an action. + * + * FIXME: Needs documentation. + */ +void action_exec(keyevent_t event) { + if (!IS_NOEVENT(event)) { + dprint("\n---- action_exec: start -----\n"); + dprint("EVENT: "); + debug_event(event); + dprintln(); +#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) + retro_tapping_counter++; +#endif + } + + if (event.pressed) { + // clear the potential weak mods left by previously pressed keys + clear_weak_mods(); + } + +#ifdef SWAP_HANDS_ENABLE + if (!IS_NOEVENT(event)) { + process_hand_swap(&event); + } +#endif + + keyrecord_t record = {.event = event}; + +#ifndef NO_ACTION_ONESHOT +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + if (has_oneshot_layer_timed_out()) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + } + if (has_oneshot_mods_timed_out()) { + clear_oneshot_mods(); + } +# ifdef SWAP_HANDS_ENABLE + if (has_oneshot_swaphands_timed_out()) { + clear_oneshot_swaphands(); + } +# endif +# endif +#endif + +#ifndef NO_ACTION_TAPPING + if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { + action_tapping_process(record); + } +#else + if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { + process_record(&record); + } + if (!IS_NOEVENT(record.event)) { + dprint("processed: "); + debug_record(record); + dprintln(); + } +#endif +} + +#ifdef SWAP_HANDS_ENABLE +bool swap_hands = false; +bool swap_held = false; + +/** \brief Process Hand Swap + * + * FIXME: Needs documentation. + */ +void process_hand_swap(keyevent_t *event) { + static swap_state_row_t swap_state[MATRIX_ROWS]; + + keypos_t pos = event->key; + swap_state_row_t col_bit = (swap_state_row_t)1 << pos.col; + bool do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit); + + if (do_swap) { + event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row); + event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col); + swap_state[pos.row] |= col_bit; + } else { + swap_state[pos.row] &= ~(col_bit); + } +} +#endif + +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) +bool disable_action_cache = false; + +void process_record_nocache(keyrecord_t *record) { + disable_action_cache = true; + process_record(record); + disable_action_cache = false; +} +#else +void process_record_nocache(keyrecord_t *record) { process_record(record); } +#endif + +__attribute__((weak)) bool process_record_quantum(keyrecord_t *record) { return true; } + +__attribute__((weak)) void post_process_record_quantum(keyrecord_t *record) {} + +#ifndef NO_ACTION_TAPPING +/** \brief Allows for handling tap-hold actions immediately instead of waiting for TAPPING_TERM or another keypress. + * + * FIXME: Needs documentation. + */ +void process_record_tap_hint(keyrecord_t *record) { + action_t action = layer_switch_get_action(record->event.key); + + switch (action.kind.id) { +# ifdef SWAP_HANDS_ENABLE + case ACT_SWAP_HANDS: + switch (action.swap.code) { + case OP_SH_ONESHOT: + break; + case OP_SH_TAP_TOGGLE: + default: + swap_hands = !swap_hands; + swap_held = true; + } + break; +# endif + } +} +#endif + +/** \brief Take a key event (key press or key release) and processes it. + * + * FIXME: Needs documentation. + */ +void process_record(keyrecord_t *record) { + if (IS_NOEVENT(record->event)) { + return; + } + + if (!process_record_quantum(record)) { +#ifndef NO_ACTION_ONESHOT + if (is_oneshot_layer_active() && record->event.pressed) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + } +#endif + return; + } + + process_record_handler(record); + post_process_record_quantum(record); +} + +void process_record_handler(keyrecord_t *record) { +#ifdef COMBO_ENABLE + action_t action; + if (record->keycode) { + action = action_for_keycode(record->keycode); + } else { + action = store_or_get_action(record->event.pressed, record->event.key); + } +#else + action_t action = store_or_get_action(record->event.pressed, record->event.key); +#endif + dprint("ACTION: "); + debug_action(action); +#ifndef NO_ACTION_LAYER + dprint(" layer_state: "); + layer_debug(); + dprint(" default_layer_state: "); + default_layer_debug(); +#endif + dprintln(); + + process_action(record, action); +} + +#if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) +void register_button(bool pressed, enum mouse_buttons button) { +# ifdef PS2_MOUSE_ENABLE + tp_buttons = pressed ? tp_buttons | button : tp_buttons & ~button; +# endif +# ifdef POINTING_DEVICE_ENABLE + report_mouse_t currentReport = pointing_device_get_report(); + currentReport.buttons = pressed ? currentReport.buttons | button : currentReport.buttons & ~button; + pointing_device_set_report(currentReport); +# endif +} +#endif + +/** \brief Take an action and processes it. + * + * FIXME: Needs documentation. + */ +void process_action(keyrecord_t *record, action_t action) { + keyevent_t event = record->event; +#ifndef NO_ACTION_TAPPING + uint8_t tap_count = record->tap.count; +#endif + +#ifndef NO_ACTION_ONESHOT + bool do_release_oneshot = false; + // notice we only clear the one shot layer if the pressed key is not a modifier. + if (is_oneshot_layer_active() && event.pressed && (action.kind.id == ACT_USAGE || !IS_MOD(action.key.code)) +# ifdef SWAP_HANDS_ENABLE + && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT) +# endif + ) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + do_release_oneshot = !is_oneshot_layer_active(); + } +#endif + + switch (action.kind.id) { + /* Key and Mods */ + case ACT_LMODS: + case ACT_RMODS: { + uint8_t mods = (action.kind.id == ACT_LMODS) ? action.key.mods : action.key.mods << 4; + if (event.pressed) { + if (mods) { + if (IS_MOD(action.key.code) || action.key.code == KC_NO) { + // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless. + // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT). + // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO). + add_mods(mods); + } else { + add_weak_mods(mods); + } + send_keyboard_report(); + } + register_code(action.key.code); + } else { + unregister_code(action.key.code); + if (mods) { + if (IS_MOD(action.key.code) || action.key.code == KC_NO) { + del_mods(mods); + } else { + del_weak_mods(mods); + } + send_keyboard_report(); + } + } + } break; +#ifndef NO_ACTION_TAPPING + case ACT_LMODS_TAP: + case ACT_RMODS_TAP: { + uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ? action.key.mods : action.key.mods << 4; + switch (action.layer_tap.code) { +# ifndef NO_ACTION_ONESHOT + case MODS_ONESHOT: + // Oneshot modifier + if (event.pressed) { + if (tap_count == 0) { + dprint("MODS_TAP: Oneshot: 0\n"); + register_mods(mods | get_oneshot_mods()); + } else if (tap_count == 1) { + dprint("MODS_TAP: Oneshot: start\n"); + set_oneshot_mods(mods | get_oneshot_mods()); +# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 + } else if (tap_count == ONESHOT_TAP_TOGGLE) { + dprint("MODS_TAP: Toggling oneshot"); + clear_oneshot_mods(); + set_oneshot_locked_mods(mods); + register_mods(mods); +# endif + } else { + register_mods(mods | get_oneshot_mods()); + } + } else { + if (tap_count == 0) { + clear_oneshot_mods(); + unregister_mods(mods); + } else if (tap_count == 1) { + // Retain Oneshot mods +# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 + if (mods & get_mods()) { + clear_oneshot_locked_mods(); + clear_oneshot_mods(); + unregister_mods(mods); + } + } else if (tap_count == ONESHOT_TAP_TOGGLE) { + // Toggle Oneshot Layer +# endif + } else { + clear_oneshot_mods(); + unregister_mods(mods); + } + } + break; +# endif + case MODS_TAP_TOGGLE: + if (event.pressed) { + if (tap_count <= TAPPING_TOGGLE) { + register_mods(mods); + } + } else { + if (tap_count < TAPPING_TOGGLE) { + unregister_mods(mods); + } + } + break; + default: + if (event.pressed) { + if (tap_count > 0) { +# if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY) + if ( +# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY + !get_ignore_mod_tap_interrupt(get_event_keycode(record->event, false), record) && +# endif + record->tap.interrupted) { + dprint("mods_tap: tap: cancel: add_mods\n"); + // ad hoc: set 0 to cancel tap + record->tap.count = 0; + register_mods(mods); + } else +# endif + { + dprint("MODS_TAP: Tap: register_code\n"); + register_code(action.key.code); + } + } else { + dprint("MODS_TAP: No tap: add_mods\n"); + register_mods(mods); + } + } else { + if (tap_count > 0) { + dprint("MODS_TAP: Tap: unregister_code\n"); + if (action.layer_tap.code == KC_CAPS) { + wait_ms(TAP_HOLD_CAPS_DELAY); + } else { + wait_ms(TAP_CODE_DELAY); + } + unregister_code(action.key.code); + } else { + dprint("MODS_TAP: No tap: add_mods\n"); + unregister_mods(mods); + } + } + break; + } + } break; +#endif +#ifdef EXTRAKEY_ENABLE + /* other HID usage */ + case ACT_USAGE: + switch (action.usage.page) { + case PAGE_SYSTEM: + if (event.pressed) { + host_system_send(action.usage.code); + } else { + host_system_send(0); + } + break; + case PAGE_CONSUMER: + if (event.pressed) { + host_consumer_send(action.usage.code); + } else { + host_consumer_send(0); + } + break; + } + break; +#endif +#ifdef MOUSEKEY_ENABLE + /* Mouse key */ + case ACT_MOUSEKEY: + if (event.pressed) { + mousekey_on(action.key.code); + } else { + mousekey_off(action.key.code); + } + switch (action.key.code) { +# if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) +# ifdef POINTING_DEVICE_ENABLE + case KC_MS_BTN1 ... KC_MS_BTN8: +# else + case KC_MS_BTN1 ... KC_MS_BTN3: +# endif + register_button(event.pressed, MOUSE_BTN_MASK(action.key.code - KC_MS_BTN1)); + break; +# endif + default: + mousekey_send(); + break; + } + break; +#endif +#ifndef NO_ACTION_LAYER + case ACT_LAYER: + if (action.layer_bitop.on == 0) { + /* Default Layer Bitwise Operation */ + if (!event.pressed) { + uint8_t shift = action.layer_bitop.part * 4; + layer_state_t bits = ((layer_state_t)action.layer_bitop.bits) << shift; + layer_state_t mask = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0; + switch (action.layer_bitop.op) { + case OP_BIT_AND: + default_layer_and(bits | mask); + break; + case OP_BIT_OR: + default_layer_or(bits | mask); + break; + case OP_BIT_XOR: + default_layer_xor(bits | mask); + break; + case OP_BIT_SET: + default_layer_set(bits | mask); + break; + } + } + } else { + /* Layer Bitwise Operation */ + if (event.pressed ? (action.layer_bitop.on & ON_PRESS) : (action.layer_bitop.on & ON_RELEASE)) { + uint8_t shift = action.layer_bitop.part * 4; + layer_state_t bits = ((layer_state_t)action.layer_bitop.bits) << shift; + layer_state_t mask = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0; + switch (action.layer_bitop.op) { + case OP_BIT_AND: + layer_and(bits | mask); + break; + case OP_BIT_OR: + layer_or(bits | mask); + break; + case OP_BIT_XOR: + layer_xor(bits | mask); + break; + case OP_BIT_SET: + layer_state_set(bits | mask); + break; + } + } + } + break; + case ACT_LAYER_MODS: + if (event.pressed) { + layer_on(action.layer_mods.layer); + register_mods(action.layer_mods.mods); + } else { + unregister_mods(action.layer_mods.mods); + layer_off(action.layer_mods.layer); + } + break; +# ifndef NO_ACTION_TAPPING + case ACT_LAYER_TAP: + case ACT_LAYER_TAP_EXT: + switch (action.layer_tap.code) { + case OP_TAP_TOGGLE: + /* tap toggle */ + if (event.pressed) { + if (tap_count < TAPPING_TOGGLE) { + layer_invert(action.layer_tap.val); + } + } else { + if (tap_count <= TAPPING_TOGGLE) { + layer_invert(action.layer_tap.val); + } + } + break; + case OP_ON_OFF: + event.pressed ? layer_on(action.layer_tap.val) : layer_off(action.layer_tap.val); + break; + case OP_OFF_ON: + event.pressed ? layer_off(action.layer_tap.val) : layer_on(action.layer_tap.val); + break; + case OP_SET_CLEAR: + event.pressed ? layer_move(action.layer_tap.val) : layer_clear(); + break; +# ifndef NO_ACTION_ONESHOT + case OP_ONESHOT: + // Oneshot modifier +# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 + do_release_oneshot = false; + if (event.pressed) { + del_mods(get_oneshot_locked_mods()); + if (get_oneshot_layer_state() == ONESHOT_TOGGLED) { + reset_oneshot_layer(); + layer_off(action.layer_tap.val); + break; + } else if (tap_count < ONESHOT_TAP_TOGGLE) { + layer_on(action.layer_tap.val); + set_oneshot_layer(action.layer_tap.val, ONESHOT_START); + } + } else { + add_mods(get_oneshot_locked_mods()); + if (tap_count >= ONESHOT_TAP_TOGGLE) { + reset_oneshot_layer(); + clear_oneshot_locked_mods(); + set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED); + } else { + clear_oneshot_layer_state(ONESHOT_PRESSED); + } + } +# else + if (event.pressed) { + layer_on(action.layer_tap.val); + set_oneshot_layer(action.layer_tap.val, ONESHOT_START); + } else { + clear_oneshot_layer_state(ONESHOT_PRESSED); + if (tap_count > 1) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + } + } +# endif + break; +# endif + default: + /* tap key */ + if (event.pressed) { + if (tap_count > 0) { + dprint("KEYMAP_TAP_KEY: Tap: register_code\n"); + register_code(action.layer_tap.code); + } else { + dprint("KEYMAP_TAP_KEY: No tap: On on press\n"); + layer_on(action.layer_tap.val); + } + } else { + if (tap_count > 0) { + dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n"); + if (action.layer_tap.code == KC_CAPS) { + wait_ms(TAP_HOLD_CAPS_DELAY); + } else { + wait_ms(TAP_CODE_DELAY); + } + unregister_code(action.layer_tap.code); + } else { + dprint("KEYMAP_TAP_KEY: No tap: Off on release\n"); + layer_off(action.layer_tap.val); + } + } + break; + } + break; +# endif +#endif + /* Extentions */ +#ifndef NO_ACTION_MACRO + case ACT_MACRO: + action_macro_play(action_get_macro(record, action.func.id, action.func.opt)); + break; +#endif +#ifdef SWAP_HANDS_ENABLE + case ACT_SWAP_HANDS: + switch (action.swap.code) { + case OP_SH_TOGGLE: + if (event.pressed) { + swap_hands = !swap_hands; + } + break; + case OP_SH_ON_OFF: + swap_hands = event.pressed; + break; + case OP_SH_OFF_ON: + swap_hands = !event.pressed; + break; + case OP_SH_ON: + if (!event.pressed) { + swap_hands = true; + } + break; + case OP_SH_OFF: + if (!event.pressed) { + swap_hands = false; + } + break; +# ifndef NO_ACTION_ONESHOT + case OP_SH_ONESHOT: + if (event.pressed) { + set_oneshot_swaphands(); + } else { + release_oneshot_swaphands(); + } + break; +# endif + +# ifndef NO_ACTION_TAPPING + case OP_SH_TAP_TOGGLE: + /* tap toggle */ + + if (event.pressed) { + if (swap_held) { + swap_held = false; + } else { + swap_hands = !swap_hands; + } + } else { + if (tap_count < TAPPING_TOGGLE) { + swap_hands = !swap_hands; + } + } + break; + default: + /* tap key */ + if (tap_count > 0) { + if (swap_held) { + swap_hands = !swap_hands; // undo hold set up in _tap_hint + swap_held = false; + } + if (event.pressed) { + register_code(action.swap.code); + } else { + wait_ms(TAP_CODE_DELAY); + unregister_code(action.swap.code); + *record = (keyrecord_t){}; // hack: reset tap mode + } + } else { + if (swap_held && !event.pressed) { + swap_hands = !swap_hands; // undo hold set up in _tap_hint + swap_held = false; + } + } +# endif + } +#endif +#ifndef NO_ACTION_FUNCTION + case ACT_FUNCTION: + action_function(record, action.func.id, action.func.opt); + break; +#endif + default: + break; + } + +#ifndef NO_ACTION_LAYER + // if this event is a layer action, update the leds + switch (action.kind.id) { + case ACT_LAYER: + case ACT_LAYER_MODS: +# ifndef NO_ACTION_TAPPING + case ACT_LAYER_TAP: + case ACT_LAYER_TAP_EXT: +# endif + led_set(host_keyboard_leds()); + break; + default: + break; + } +#endif + +#ifndef NO_ACTION_TAPPING +# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) + if (!is_tap_action(action)) { + retro_tapping_counter = 0; + } else { + if (event.pressed) { + if (tap_count > 0) { + retro_tapping_counter = 0; + } + } else { + if (tap_count > 0) { + retro_tapping_counter = 0; + } else { + if ( +# ifdef RETRO_TAPPING_PER_KEY + get_retro_tapping(get_event_keycode(record->event, false), record) && +# endif + retro_tapping_counter == 2) { + tap_code(action.layer_tap.code); + } + retro_tapping_counter = 0; + } + } + } +# endif +#endif + +#ifdef SWAP_HANDS_ENABLE +# ifndef NO_ACTION_ONESHOT + if (event.pressed && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)) { + use_oneshot_swaphands(); + } +# endif +#endif + +#ifndef NO_ACTION_ONESHOT + /* Because we switch layers after a oneshot event, we need to release the + * key before we leave the layer or no key up event will be generated. + */ + if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED)) { + record->event.pressed = false; + layer_on(get_oneshot_layer()); + process_record(record); + layer_off(get_oneshot_layer()); + } +#endif +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void register_code(uint8_t code) { + if (code == KC_NO) { + return; + } +#ifdef LOCKING_SUPPORT_ENABLE + else if (KC_LOCKING_CAPS == code) { +# ifdef LOCKING_RESYNC_ENABLE + // Resync: ignore if caps lock already is on + if (host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK)) return; +# endif + add_key(KC_CAPSLOCK); + send_keyboard_report(); + wait_ms(100); + del_key(KC_CAPSLOCK); + send_keyboard_report(); + } + + else if (KC_LOCKING_NUM == code) { +# ifdef LOCKING_RESYNC_ENABLE + if (host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)) return; +# endif + add_key(KC_NUMLOCK); + send_keyboard_report(); + wait_ms(100); + del_key(KC_NUMLOCK); + send_keyboard_report(); + } + + else if (KC_LOCKING_SCROLL == code) { +# ifdef LOCKING_RESYNC_ENABLE + if (host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK)) return; +# endif + add_key(KC_SCROLLLOCK); + send_keyboard_report(); + wait_ms(100); + del_key(KC_SCROLLLOCK); + send_keyboard_report(); + } +#endif + + else if IS_KEY (code) { + // TODO: should push command_proc out of this block? + if (command_proc(code)) return; + +#ifndef NO_ACTION_ONESHOT +/* TODO: remove + if (oneshot_state.mods && !oneshot_state.disabled) { + uint8_t tmp_mods = get_mods(); + add_mods(oneshot_state.mods); + + add_key(code); + send_keyboard_report(); + + set_mods(tmp_mods); + send_keyboard_report(); + oneshot_cancel(); + } else +*/ +#endif + { + // Force a new key press if the key is already pressed + // without this, keys with the same keycode, but different + // modifiers will be reported incorrectly, see issue #1708 + if (is_key_pressed(keyboard_report, code)) { + del_key(code); + send_keyboard_report(); + } + add_key(code); + send_keyboard_report(); + } + } else if IS_MOD (code) { + add_mods(MOD_BIT(code)); + send_keyboard_report(); + } +#ifdef EXTRAKEY_ENABLE + else if IS_SYSTEM (code) { + host_system_send(KEYCODE2SYSTEM(code)); + } else if IS_CONSUMER (code) { + host_consumer_send(KEYCODE2CONSUMER(code)); + } +#endif +#ifdef MOUSEKEY_ENABLE + else if IS_MOUSEKEY (code) { + mousekey_on(code); + mousekey_send(); + } +#endif +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void unregister_code(uint8_t code) { + if (code == KC_NO) { + return; + } +#ifdef LOCKING_SUPPORT_ENABLE + else if (KC_LOCKING_CAPS == code) { +# ifdef LOCKING_RESYNC_ENABLE + // Resync: ignore if caps lock already is off + if (!(host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK))) return; +# endif + add_key(KC_CAPSLOCK); + send_keyboard_report(); + del_key(KC_CAPSLOCK); + send_keyboard_report(); + } + + else if (KC_LOCKING_NUM == code) { +# ifdef LOCKING_RESYNC_ENABLE + if (!(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) return; +# endif + add_key(KC_NUMLOCK); + send_keyboard_report(); + del_key(KC_NUMLOCK); + send_keyboard_report(); + } + + else if (KC_LOCKING_SCROLL == code) { +# ifdef LOCKING_RESYNC_ENABLE + if (!(host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK))) return; +# endif + add_key(KC_SCROLLLOCK); + send_keyboard_report(); + del_key(KC_SCROLLLOCK); + send_keyboard_report(); + } +#endif + + else if IS_KEY (code) { + del_key(code); + send_keyboard_report(); + } else if IS_MOD (code) { + del_mods(MOD_BIT(code)); + send_keyboard_report(); + } else if IS_SYSTEM (code) { + host_system_send(0); + } else if IS_CONSUMER (code) { + host_consumer_send(0); + } +#ifdef MOUSEKEY_ENABLE + else if IS_MOUSEKEY (code) { + mousekey_off(code); + mousekey_send(); + } +#endif +} + +/** \brief Tap a keycode with a delay. + * + * \param code The basic keycode to tap. + * \param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it. + */ +void tap_code_delay(uint8_t code, uint16_t delay) { + register_code(code); + for (uint16_t i = delay; i > 0; i--) { + wait_ms(1); + } + unregister_code(code); +} + +/** \brief Tap a keycode with the default delay. + * + * \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined. + */ +void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); } + +/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately. + * + * \param mods A bitfield of modifiers to register. + */ +void register_mods(uint8_t mods) { + if (mods) { + add_mods(mods); + send_keyboard_report(); + } +} + +/** \brief Removes the given physically pressed modifiers and sends a keyboard report immediately. + * + * \param mods A bitfield of modifiers to unregister. + */ +void unregister_mods(uint8_t mods) { + if (mods) { + del_mods(mods); + send_keyboard_report(); + } +} + +/** \brief Adds the given weak modifiers and sends a keyboard report immediately. + * + * \param mods A bitfield of modifiers to register. + */ +void register_weak_mods(uint8_t mods) { + if (mods) { + add_weak_mods(mods); + send_keyboard_report(); + } +} + +/** \brief Removes the given weak modifiers and sends a keyboard report immediately. + * + * \param mods A bitfield of modifiers to unregister. + */ +void unregister_weak_mods(uint8_t mods) { + if (mods) { + del_weak_mods(mods); + send_keyboard_report(); + } +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void clear_keyboard(void) { + clear_mods(); + clear_keyboard_but_mods(); +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void clear_keyboard_but_mods(void) { + clear_keys(); + clear_keyboard_but_mods_and_keys(); +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void clear_keyboard_but_mods_and_keys() { +#ifdef EXTRAKEY_ENABLE + host_system_send(0); + host_consumer_send(0); +#endif + clear_weak_mods(); + clear_macro_mods(); + send_keyboard_report(); +#ifdef MOUSEKEY_ENABLE + mousekey_clear(); + mousekey_send(); +#endif +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +bool is_tap_key(keypos_t key) { + action_t action = layer_switch_get_action(key); + return is_tap_action(action); +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +bool is_tap_record(keyrecord_t *record) { +#ifdef COMBO_ENABLE + action_t action; + if (record->keycode) { + action = action_for_keycode(record->keycode); + } else { + action = layer_switch_get_action(record->event.key); + } +#else + action_t action = layer_switch_get_action(record->event.key); +#endif + return is_tap_action(action); +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +bool is_tap_action(action_t action) { + switch (action.kind.id) { + case ACT_LMODS_TAP: + case ACT_RMODS_TAP: + case ACT_LAYER_TAP: + case ACT_LAYER_TAP_EXT: + switch (action.layer_tap.code) { + case KC_NO ... KC_RGUI: + case OP_TAP_TOGGLE: + case OP_ONESHOT: + return true; + } + return false; + case ACT_SWAP_HANDS: + switch (action.swap.code) { + case KC_NO ... KC_RGUI: + case OP_SH_TAP_TOGGLE: + return true; + } + return false; + case ACT_MACRO: + case ACT_FUNCTION: + if (action.func.opt & FUNC_TAP) { + return true; + } + return false; + } + return false; +} + +/** \brief Debug print (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void debug_event(keyevent_t event) { dprintf("%04X%c(%u)", (event.key.row << 8 | event.key.col), (event.pressed ? 'd' : 'u'), event.time); } +/** \brief Debug print (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void debug_record(keyrecord_t record) { + debug_event(record.event); +#ifndef NO_ACTION_TAPPING + dprintf(":%u%c", record.tap.count, (record.tap.interrupted ? '-' : ' ')); +#endif +} + +/** \brief Debug print (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void debug_action(action_t action) { + switch (action.kind.id) { + case ACT_LMODS: + dprint("ACT_LMODS"); + break; + case ACT_RMODS: + dprint("ACT_RMODS"); + break; + case ACT_LMODS_TAP: + dprint("ACT_LMODS_TAP"); + break; + case ACT_RMODS_TAP: + dprint("ACT_RMODS_TAP"); + break; + case ACT_USAGE: + dprint("ACT_USAGE"); + break; + case ACT_MOUSEKEY: + dprint("ACT_MOUSEKEY"); + break; + case ACT_LAYER: + dprint("ACT_LAYER"); + break; + case ACT_LAYER_MODS: + dprint("ACT_LAYER_MODS"); + break; + case ACT_LAYER_TAP: + dprint("ACT_LAYER_TAP"); + break; + case ACT_LAYER_TAP_EXT: + dprint("ACT_LAYER_TAP_EXT"); + break; + case ACT_MACRO: + dprint("ACT_MACRO"); + break; + case ACT_FUNCTION: + dprint("ACT_FUNCTION"); + break; + case ACT_SWAP_HANDS: + dprint("ACT_SWAP_HANDS"); + break; + default: + dprint("UNKNOWN"); + break; + } + dprintf("[%X:%02X]", action.kind.param >> 8, action.kind.param & 0xff); +} diff --git a/quantum/action.h b/quantum/action.h new file mode 100644 index 0000000000..3d357b33b8 --- /dev/null +++ b/quantum/action.h @@ -0,0 +1,132 @@ +/* +Copyright 2012,2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include +#include "keyboard.h" +#include "keycode.h" +#include "action_code.h" +#include "action_macro.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Disable macro and function features when LTO is enabled, since they break */ +#ifdef LTO_ENABLE +# ifndef NO_ACTION_MACRO +# define NO_ACTION_MACRO +# endif +# ifndef NO_ACTION_FUNCTION +# define NO_ACTION_FUNCTION +# endif +#endif + +/* tapping count and state */ +typedef struct { + bool interrupted : 1; + bool reserved2 : 1; + bool reserved1 : 1; + bool reserved0 : 1; + uint8_t count : 4; +} tap_t; + +/* Key event container for recording */ +typedef struct { + keyevent_t event; +#ifndef NO_ACTION_TAPPING + tap_t tap; +#endif +#ifdef COMBO_ENABLE + uint16_t keycode; +#endif +} keyrecord_t; + +/* Execute action per keyevent */ +void action_exec(keyevent_t event); + +/* action for key */ +action_t action_for_key(uint8_t layer, keypos_t key); +action_t action_for_keycode(uint16_t keycode); + +/* macro */ +const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); + +/* user defined special function */ +void action_function(keyrecord_t *record, uint8_t id, uint8_t opt); + +/* keyboard-specific key event (pre)processing */ +bool process_record_quantum(keyrecord_t *record); + +/* Utilities for actions. */ +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) +extern bool disable_action_cache; +#endif + +/* Code for handling one-handed key modifiers. */ +#ifdef SWAP_HANDS_ENABLE +extern bool swap_hands; +extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS]; +# if (MATRIX_COLS <= 8) +typedef uint8_t swap_state_row_t; +# elif (MATRIX_COLS <= 16) +typedef uint16_t swap_state_row_t; +# elif (MATRIX_COLS <= 32) +typedef uint32_t swap_state_row_t; +# else +# error "MATRIX_COLS: invalid value" +# endif + +void process_hand_swap(keyevent_t *record); +#endif + +void process_record_nocache(keyrecord_t *record); +void process_record(keyrecord_t *record); +void process_record_handler(keyrecord_t *record); +void post_process_record_quantum(keyrecord_t *record); +void process_action(keyrecord_t *record, action_t action); +void register_code(uint8_t code); +void unregister_code(uint8_t code); +void tap_code(uint8_t code); +void tap_code_delay(uint8_t code, uint16_t delay); +void register_mods(uint8_t mods); +void unregister_mods(uint8_t mods); +void register_weak_mods(uint8_t mods); +void unregister_weak_mods(uint8_t mods); +// void set_mods(uint8_t mods); +void clear_keyboard(void); +void clear_keyboard_but_mods(void); +void clear_keyboard_but_mods_and_keys(void); +void layer_switch(uint8_t new_layer); +bool is_tap_key(keypos_t key); +bool is_tap_record(keyrecord_t *record); +bool is_tap_action(action_t action); + +#ifndef NO_ACTION_TAPPING +void process_record_tap_hint(keyrecord_t *record); +#endif + +/* debug */ +void debug_event(keyevent_t event); +void debug_record(keyrecord_t record); +void debug_action(action_t action); + +#ifdef __cplusplus +} +#endif diff --git a/quantum/action_code.h b/quantum/action_code.h new file mode 100644 index 0000000000..eb18c36ae8 --- /dev/null +++ b/quantum/action_code.h @@ -0,0 +1,308 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +/** \brief Action codes + * + * 16bit code: action_kind(4bit) + action_parameter(12bit) + * + * Key Actions(00xx) + * ----------------- + * ACT_MODS(000r): + * 000r|0000|0000 0000 No action code + * 000r|0000|0000 0001 Transparent code + * 000r|0000| keycode Key + * 000r|mods|0000 0000 Modifiers + * 000r|mods| keycode Modifiers+Key(Modified key) + * r: Left/Right flag(Left:0, Right:1) + * + * ACT_MODS_TAP(001r): + * 001r|mods|0000 0000 Modifiers with OneShot + * 001r|mods|0000 0001 Modifiers with tap toggle + * 001r|mods|0000 00xx (reserved) + * 001r|mods| keycode Modifiers with Tap Key(Dual role) + * + * Other Keys(01xx) + * ---------------- + * ACT_USAGE(0100): TODO: Not needed? + * 0100|00| usage(10) System control(0x80) - General Desktop page(0x01) + * 0100|01| usage(10) Consumer control(0x01) - Consumer page(0x0C) + * 0100|10| usage(10) (reserved) + * 0100|11| usage(10) (reserved) + * + * ACT_MOUSEKEY(0101): TODO: Merge these two actions to conserve space? + * 0101|xxxx| keycode Mouse key + * + * ACT_SWAP_HANDS(0110): + * 0110|xxxx| keycode Swap hands (keycode on tap, or options) + * + * 0111|xxxx xxxx xxxx (reserved) + * + * Layer Actions(10xx) + * ------------------- + * ACT_LAYER(1000): + * 1000|oo00|pppE BBBB Default Layer Bitwise operation + * oo: operation(00:AND, 01:OR, 10:XOR, 11:SET) + * ppp: 4-bit chunk part(0-7) + * EBBBB: bits and extra bit + * 1000|ooee|pppE BBBB Layer Bitwise Operation + * oo: operation(00:AND, 01:OR, 10:XOR, 11:SET) + * ppp: 4-bit chunk part(0-7) + * EBBBB: bits and extra bit + * ee: on event(01:press, 10:release, 11:both) + * + * ACT_LAYER_MODS(1001): + * 1001|LLLL| mods Layer with modifiers held + * + * ACT_LAYER_TAP(101x): + * 101E|LLLL| keycode On/Off with tap key (0x00-DF)[TAP] + * 101E|LLLL|1110 mods On/Off with modifiers (0xE0-EF)[NOT TAP] + * 101E|LLLL|1111 0000 Invert with tap toggle (0xF0) [TAP] + * 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP] + * 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP] + * 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP] + * 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP] + * 101E|LLLL|1111 xxxx Reserved (0xF5-FF) + * ELLLL: layer 0-31(E: extra bit for layer 16-31) + * + * Extensions(11xx) + * ---------------- + * ACT_MACRO(1100): + * 1100|opt | id(8) Macro play? + * 1100|1111| id(8) Macro record? + * + * 1101|xxxx xxxx xxxx (reserved) + * 1110|xxxx xxxx xxxx (reserved) + * + * ACT_FUNCTION(1111): + * 1111| address(12) Function? + * 1111|opt | id(8) Function? + */ +enum action_kind_id { + /* Key Actions */ + ACT_MODS = 0b0000, + ACT_LMODS = 0b0000, + ACT_RMODS = 0b0001, + ACT_MODS_TAP = 0b0010, + ACT_LMODS_TAP = 0b0010, + ACT_RMODS_TAP = 0b0011, + /* Other Keys */ + ACT_USAGE = 0b0100, + ACT_MOUSEKEY = 0b0101, + /* One-hand Support */ + ACT_SWAP_HANDS = 0b0110, + /* Layer Actions */ + ACT_LAYER = 0b1000, + ACT_LAYER_MODS = 0b1001, + ACT_LAYER_TAP = 0b1010, /* Layer 0-15 */ + ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */ + /* Extensions */ + ACT_MACRO = 0b1100, + ACT_FUNCTION = 0b1111 +}; + +/** \brief Action Code Struct + * + * NOTE: + * In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15). + * AVR looks like a little endian in avr-gcc. + * Not portable across compiler/endianness? + * + * Byte order and bit order of 0x1234: + * Big endian: Little endian: + * -------------------- -------------------- + * FEDC BA98 7654 3210 0123 4567 89AB CDEF + * 0001 0010 0011 0100 0010 1100 0100 1000 + * 0x12 0x34 0x34 0x12 + */ +typedef union { + uint16_t code; + struct action_kind { + uint16_t param : 12; + uint8_t id : 4; + } kind; + struct action_key { + uint8_t code : 8; + uint8_t mods : 4; + uint8_t kind : 4; + } key; + struct action_layer_bitop { + uint8_t bits : 4; + uint8_t xbit : 1; + uint8_t part : 3; + uint8_t on : 2; + uint8_t op : 2; + uint8_t kind : 4; + } layer_bitop; + struct action_layer_mods { + uint8_t mods : 8; + uint8_t layer : 4; + uint8_t kind : 4; + } layer_mods; + struct action_layer_tap { + uint8_t code : 8; + uint8_t val : 5; + uint8_t kind : 3; + } layer_tap; + struct action_usage { + uint16_t code : 10; + uint8_t page : 2; + uint8_t kind : 4; + } usage; + struct action_function { + uint8_t id : 8; + uint8_t opt : 4; + uint8_t kind : 4; + } func; + struct action_swap { + uint8_t code : 8; + uint8_t opt : 4; + uint8_t kind : 4; + } swap; +} action_t; + +/* action utility */ +#define ACTION_NO 0 +#define ACTION_TRANSPARENT 1 +#define ACTION(kind, param) ((kind) << 12 | (param)) + +/** \brief Key Actions + * + * Mod bits: 43210 + * bit 0 ||||+- Control + * bit 1 |||+-- Shift + * bit 2 ||+--- Alt + * bit 3 |+---- Gui + * bit 4 +----- LR flag(Left:0, Right:1) + */ +enum mods_bit { + MOD_LCTL = 0x01, + MOD_LSFT = 0x02, + MOD_LALT = 0x04, + MOD_LGUI = 0x08, + MOD_RCTL = 0x11, + MOD_RSFT = 0x12, + MOD_RALT = 0x14, + MOD_RGUI = 0x18, +}; +enum mods_codes { + MODS_ONESHOT = 0x00, + MODS_TAP_TOGGLE = 0x01, +}; +#define ACTION_KEY(key) ACTION(ACT_MODS, (key)) +#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | 0) +#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | (key)) +#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | (key)) +#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_ONESHOT) +#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_TAP_TOGGLE) + +/** \brief Other Keys + */ +enum usage_pages { PAGE_SYSTEM, PAGE_CONSUMER }; +#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id)) +#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id)) +#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key) + +/** \brief Layer Actions + */ +enum layer_param_on { + ON_PRESS = 1, + ON_RELEASE = 2, + ON_BOTH = 3, +}; + +/** \brief Layer Actions + */ +enum layer_param_bit_op { + OP_BIT_AND = 0, + OP_BIT_OR = 1, + OP_BIT_XOR = 2, + OP_BIT_SET = 3, +}; + +/** \brief Layer Actions + */ +enum layer_param_tap_op { + OP_TAP_TOGGLE = 0xF0, + OP_ON_OFF, + OP_OFF_ON, + OP_SET_CLEAR, + OP_ONESHOT, +}; +#define ACTION_LAYER_BITOP(op, part, bits, on) ACTION(ACT_LAYER, (op) << 10 | (on) << 8 | (part) << 5 | ((bits)&0x1f)) +#define ACTION_LAYER_TAP(layer, key) ACTION(ACT_LAYER_TAP, (layer) << 8 | (key)) +/* Default Layer */ +#define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4)) +/* Layer Operation */ +#define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on)) +#define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer) +#define ACTION_LAYER_TOGGLE(layer) ACTION_LAYER_INVERT(layer, ON_RELEASE) +#define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on)) +#define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on)) +#define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on)) +#define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on)) +#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF) +#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON) +#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR) +#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT) +#define ACTION_LAYER_MODS(layer, mods) ACTION(ACT_LAYER_MODS, (layer) << 8 | (mods)) +/* With Tapping */ +#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key)) +#define ACTION_LAYER_TAP_TOGGLE(layer) ACTION_LAYER_TAP((layer), OP_TAP_TOGGLE) +/* Bitwise Operation */ +#define ACTION_LAYER_BIT_AND(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), (on)) +#define ACTION_LAYER_BIT_OR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), (on)) +#define ACTION_LAYER_BIT_XOR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), (on)) +#define ACTION_LAYER_BIT_SET(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), (on)) +/* Default Layer Bitwise Operation */ +#define ACTION_DEFAULT_LAYER_BIT_AND(part, bits) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), 0) +#define ACTION_DEFAULT_LAYER_BIT_OR(part, bits) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), 0) +#define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0) +#define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0) + +/* Macro */ +#define ACTION_MACRO(id) ACTION(ACT_MACRO, (id)) +#define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP << 8 | (id)) +#define ACTION_MACRO_OPT(id, opt) ACTION(ACT_MACRO, (opt) << 8 | (id)) +/* Function */ +enum function_opts { + FUNC_TAP = 0x8, /* indciates function is tappable */ +}; +#define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id)) +#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP << 8 | (id)) +#define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt) << 8 | (id)) +/* OneHand Support */ +enum swap_hands_param_tap_op { + OP_SH_TOGGLE = 0xF0, + OP_SH_TAP_TOGGLE, + OP_SH_ON_OFF, + OP_SH_OFF_ON, + OP_SH_OFF, + OP_SH_ON, + OP_SH_ONESHOT, +}; + +#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF() +#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE) +#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE) +#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT) +#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key) +#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF) +#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON) +#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON) +#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF) diff --git a/quantum/action_layer.c b/quantum/action_layer.c new file mode 100644 index 0000000000..ed1a4bd20d --- /dev/null +++ b/quantum/action_layer.c @@ -0,0 +1,279 @@ +#include +#include "keyboard.h" +#include "action.h" +#include "util.h" +#include "action_layer.h" + +#ifdef DEBUG_ACTION +# include "debug.h" +#else +# include "nodebug.h" +#endif + +/** \brief Default Layer State + */ +layer_state_t default_layer_state = 0; + +/** \brief Default Layer State Set At user Level + * + * Run user code on default layer state change + */ +__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) { return state; } + +/** \brief Default Layer State Set At Keyboard Level + * + * Run keyboard code on default layer state change + */ +__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) { return default_layer_state_set_user(state); } + +/** \brief Default Layer State Set + * + * Static function to set the default layer state, prints debug info and clears keys + */ +static void default_layer_state_set(layer_state_t state) { + state = default_layer_state_set_kb(state); + debug("default_layer_state: "); + default_layer_debug(); + debug(" to "); + default_layer_state = state; + default_layer_debug(); + debug("\n"); +#ifdef STRICT_LAYER_RELEASE + clear_keyboard_but_mods(); // To avoid stuck keys +#else + clear_keyboard_but_mods_and_keys(); // Don't reset held keys +#endif +} + +/** \brief Default Layer Print + * + * Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit. + */ +void default_layer_debug(void) { dprintf("%08lX(%u)", default_layer_state, get_highest_layer(default_layer_state)); } + +/** \brief Default Layer Set + * + * Sets the default layer state. + */ +void default_layer_set(layer_state_t state) { default_layer_state_set(state); } + +#ifndef NO_ACTION_LAYER +/** \brief Default Layer Or + * + * Turns on the default layer based on matching bits between specifed layer and existing layer state + */ +void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); } +/** \brief Default Layer And + * + * Turns on default layer based on matching enabled bits between specifed layer and existing layer state + */ +void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); } +/** \brief Default Layer Xor + * + * Turns on default layer based on non-matching bits between specifed layer and existing layer state + */ +void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); } +#endif + +#ifndef NO_ACTION_LAYER +/** \brief Keymap Layer State + */ +layer_state_t layer_state = 0; + +/** \brief Layer state set user + * + * Runs user code on layer state change + */ +__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) { return state; } + +/** \brief Layer state set keyboard + * + * Runs keyboard code on layer state change + */ +__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { return layer_state_set_user(state); } + +/** \brief Layer state set + * + * Sets the layer to match the specifed state (a bitmask) + */ +void layer_state_set(layer_state_t state) { + state = layer_state_set_kb(state); + dprint("layer_state: "); + layer_debug(); + dprint(" to "); + layer_state = state; + layer_debug(); + dprintln(); +# ifdef STRICT_LAYER_RELEASE + clear_keyboard_but_mods(); // To avoid stuck keys +# else + clear_keyboard_but_mods_and_keys(); // Don't reset held keys +# endif +} + +/** \brief Layer clear + * + * Turn off all layers + */ +void layer_clear(void) { layer_state_set(0); } + +/** \brief Layer state is + * + * Return whether the given state is on (it might still be shadowed by a higher state, though) + */ +bool layer_state_is(uint8_t layer) { return layer_state_cmp(layer_state, layer); } + +/** \brief Layer state compare + * + * Used for comparing layers {mostly used for unit testing} + */ +bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) { + if (!cmp_layer_state) { + return layer == 0; + } + return (cmp_layer_state & ((layer_state_t)1 << layer)) != 0; +} + +/** \brief Layer move + * + * Turns on the given layer and turn off all other layers + */ +void layer_move(uint8_t layer) { layer_state_set((layer_state_t)1 << layer); } + +/** \brief Layer on + * + * Turns on given layer + */ +void layer_on(uint8_t layer) { layer_state_set(layer_state | ((layer_state_t)1 << layer)); } + +/** \brief Layer off + * + * Turns off given layer + */ +void layer_off(uint8_t layer) { layer_state_set(layer_state & ~((layer_state_t)1 << layer)); } + +/** \brief Layer invert + * + * Toggle the given layer (set it if it's unset, or unset it if it's set) + */ +void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ ((layer_state_t)1 << layer)); } + +/** \brief Layer or + * + * Turns on layers based on matching bits between specifed layer and existing layer state + */ +void layer_or(layer_state_t state) { layer_state_set(layer_state | state); } +/** \brief Layer and + * + * Turns on layers based on matching enabled bits between specifed layer and existing layer state + */ +void layer_and(layer_state_t state) { layer_state_set(layer_state & state); } +/** \brief Layer xor + * + * Turns on layers based on non-matching bits between specifed layer and existing layer state + */ +void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); } + +/** \brief Layer debug printing + * + * Print out the hex value of the 32-bit layer state, as well as the value of the highest bit. + */ +void layer_debug(void) { dprintf("%08lX(%u)", layer_state, get_highest_layer(layer_state)); } +#endif + +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) +/** \brief source layer cache + */ + +uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}}; + +/** \brief update source layers cache + * + * Updates the cached keys when changing layers + */ +void update_source_layers_cache(keypos_t key, uint8_t layer) { + const uint8_t key_number = key.col + (key.row * MATRIX_COLS); + const uint8_t storage_row = key_number / 8; + const uint8_t storage_bit = key_number % 8; + + for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { + source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit); + } +} + +/** \brief read source layers cache + * + * reads the cached keys stored when the layer was changed + */ +uint8_t read_source_layers_cache(keypos_t key) { + const uint8_t key_number = key.col + (key.row * MATRIX_COLS); + const uint8_t storage_row = key_number / 8; + const uint8_t storage_bit = key_number % 8; + uint8_t layer = 0; + + for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { + layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number; + } + + return layer; +} +#endif + +/** \brief Store or get action (FIXME: Needs better summary) + * + * Make sure the action triggered when the key is released is the same + * one as the one triggered on press. It's important for the mod keys + * when the layer is switched after the down event but before the up + * event as they may get stuck otherwise. + */ +action_t store_or_get_action(bool pressed, keypos_t key) { +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) + if (disable_action_cache) { + return layer_switch_get_action(key); + } + + uint8_t layer; + + if (pressed) { + layer = layer_switch_get_layer(key); + update_source_layers_cache(key, layer); + } else { + layer = read_source_layers_cache(key); + } + return action_for_key(layer, key); +#else + return layer_switch_get_action(key); +#endif +} + +/** \brief Layer switch get layer + * + * Gets the layer based on key info + */ +uint8_t layer_switch_get_layer(keypos_t key) { +#ifndef NO_ACTION_LAYER + action_t action; + action.code = ACTION_TRANSPARENT; + + layer_state_t layers = layer_state | default_layer_state; + /* check top layer first */ + for (int8_t i = MAX_LAYER - 1; i >= 0; i--) { + if (layers & ((layer_state_t)1 << i)) { + action = action_for_key(i, key); + if (action.code != ACTION_TRANSPARENT) { + return i; + } + } + } + /* fall back to layer 0 */ + return 0; +#else + return get_highest_layer(default_layer_state); +#endif +} + +/** \brief Layer switch get layer + * + * Gets action code based on key position + */ +action_t layer_switch_get_action(keypos_t key) { return action_for_key(layer_switch_get_layer(key), key); } diff --git a/quantum/action_layer.h b/quantum/action_layer.h new file mode 100644 index 0000000000..b87d096eed --- /dev/null +++ b/quantum/action_layer.h @@ -0,0 +1,147 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include "keyboard.h" +#include "action.h" + +#ifdef DYNAMIC_KEYMAP_ENABLE +# ifndef DYNAMIC_KEYMAP_LAYER_COUNT +# define DYNAMIC_KEYMAP_LAYER_COUNT 4 +# endif +# if DYNAMIC_KEYMAP_LAYER_COUNT <= 8 +# ifndef LAYER_STATE_8BIT +# define LAYER_STATE_8BIT +# endif +# elif DYNAMIC_KEYMAP_LAYER_COUNT <= 16 +# ifndef LAYER_STATE_16BIT +# define LAYER_STATE_16BIT +# endif +# else +# ifndef LAYER_STATE_32BIT +# define LAYER_STATE_32BIT +# endif +# endif +#endif + +#if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT) +# define LAYER_STATE_32BIT +#endif + +#if defined(LAYER_STATE_8BIT) +typedef uint8_t layer_state_t; +# define MAX_LAYER_BITS 3 +# ifndef MAX_LAYER +# define MAX_LAYER 8 +# endif +# define get_highest_layer(state) biton(state) +#elif defined(LAYER_STATE_16BIT) +typedef uint16_t layer_state_t; +# define MAX_LAYER_BITS 4 +# ifndef MAX_LAYER +# define MAX_LAYER 16 +# endif +# define get_highest_layer(state) biton16(state) +#elif defined(LAYER_STATE_32BIT) +typedef uint32_t layer_state_t; +# define MAX_LAYER_BITS 5 +# ifndef MAX_LAYER +# define MAX_LAYER 32 +# endif +# define get_highest_layer(state) biton32(state) +#else +# error Layer Mask size not specified. HOW?! +#endif + +/* + * Default Layer + */ +extern layer_state_t default_layer_state; +void default_layer_debug(void); +void default_layer_set(layer_state_t state); + +__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state); +__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state); + +#ifndef NO_ACTION_LAYER +/* bitwise operation */ +void default_layer_or(layer_state_t state); +void default_layer_and(layer_state_t state); +void default_layer_xor(layer_state_t state); +#else +# define default_layer_or(state) +# define default_layer_and(state) +# define default_layer_xor(state) +#endif + +/* + * Keymap Layer + */ +#ifndef NO_ACTION_LAYER +extern layer_state_t layer_state; + +void layer_state_set(layer_state_t state); +bool layer_state_is(uint8_t layer); +bool layer_state_cmp(layer_state_t layer1, uint8_t layer2); + +void layer_debug(void); +void layer_clear(void); +void layer_move(uint8_t layer); +void layer_on(uint8_t layer); +void layer_off(uint8_t layer); +void layer_invert(uint8_t layer); +/* bitwise operation */ +void layer_or(layer_state_t state); +void layer_and(layer_state_t state); +void layer_xor(layer_state_t state); +layer_state_t layer_state_set_user(layer_state_t state); +layer_state_t layer_state_set_kb(layer_state_t state); +#else +# define layer_state 0 + +# define layer_state_set(layer) +# define layer_state_is(layer) (layer == 0) +# define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & (layer_state_t)1 << layer) != 0) + +# define layer_debug() +# define layer_clear() +# define layer_move(layer) (void)layer +# define layer_on(layer) (void)layer +# define layer_off(layer) (void)layer +# define layer_invert(layer) (void)layer +# define layer_or(state) (void)state +# define layer_and(state) (void)state +# define layer_xor(state) (void)state +# define layer_state_set_kb(state) (void)state +# define layer_state_set_user(state) (void)state +#endif + +/* pressed actions cache */ +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) + +void update_source_layers_cache(keypos_t key, uint8_t layer); +uint8_t read_source_layers_cache(keypos_t key); +#endif +action_t store_or_get_action(bool pressed, keypos_t key); + +/* return the topmost non-transparent layer currently associated with key */ +uint8_t layer_switch_get_layer(keypos_t key); + +/* return action depending on current layer status */ +action_t layer_switch_get_action(keypos_t key); diff --git a/quantum/action_macro.c b/quantum/action_macro.c new file mode 100644 index 0000000000..92228c0ba8 --- /dev/null +++ b/quantum/action_macro.c @@ -0,0 +1,93 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "action.h" +#include "action_util.h" +#include "action_macro.h" +#include "wait.h" + +#ifdef DEBUG_ACTION +# include "debug.h" +#else +# include "nodebug.h" +#endif + +#ifndef NO_ACTION_MACRO + +# define MACRO_READ() (macro = MACRO_GET(macro_p++)) +/** \brief Action Macro Play + * + * FIXME: Needs doc + */ +void action_macro_play(const macro_t *macro_p) { + macro_t macro = END; + uint8_t interval = 0; + + if (!macro_p) return; + while (true) { + switch (MACRO_READ()) { + case KEY_DOWN: + MACRO_READ(); + dprintf("KEY_DOWN(%02X)\n", macro); + if (IS_MOD(macro)) { + add_macro_mods(MOD_BIT(macro)); + send_keyboard_report(); + } else { + register_code(macro); + } + break; + case KEY_UP: + MACRO_READ(); + dprintf("KEY_UP(%02X)\n", macro); + if (IS_MOD(macro)) { + del_macro_mods(MOD_BIT(macro)); + send_keyboard_report(); + } else { + unregister_code(macro); + } + break; + case WAIT: + MACRO_READ(); + dprintf("WAIT(%u)\n", macro); + { + uint8_t ms = macro; + while (ms--) wait_ms(1); + } + break; + case INTERVAL: + interval = MACRO_READ(); + dprintf("INTERVAL(%u)\n", interval); + break; + case 0x04 ... 0x73: + dprintf("DOWN(%02X)\n", macro); + register_code(macro); + break; + case 0x84 ... 0xF3: + dprintf("UP(%02X)\n", macro); + unregister_code(macro & 0x7F); + break; + case END: + default: + return; + } + // interval + { + uint8_t ms = interval; + while (ms--) wait_ms(1); + } + } +} +#endif diff --git a/quantum/action_macro.h b/quantum/action_macro.h new file mode 100644 index 0000000000..685e2c6ffc --- /dev/null +++ b/quantum/action_macro.h @@ -0,0 +1,123 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include "progmem.h" + +typedef uint8_t macro_t; + +#define MACRO_NONE (macro_t *)0 +#define MACRO(...) \ + ({ \ + static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \ + &__m[0]; \ + }) +#define MACRO_GET(p) pgm_read_byte(p) + +// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped +#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release))) + +// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped +#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro) + +// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #) +#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod) + +// Momentary switch layer when held, sends macro if tapped +#define MACRO_TAP_HOLD_LAYER(record, macro, layer) \ + (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({ \ + layer_on((layer)); \ + MACRO_NONE; \ + }) \ + : MACRO_NONE) \ + : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \ + layer_off((layer)); \ + MACRO_NONE; \ + }))) + +// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #) +#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer) + +#ifndef NO_ACTION_MACRO +void action_macro_play(const macro_t *macro_p); +#else +# define action_macro_play(macro) +#endif + +/* Macro commands + * code(0x04-73) // key down(1byte) + * code(0x04-73) | 0x80 // key up(1byte) + * { KEY_DOWN, code(0x04-0xff) } // key down(2bytes) + * { KEY_UP, code(0x04-0xff) } // key up(2bytes) + * WAIT // wait milli-seconds + * INTERVAL // set interval between macro commands + * END // stop macro execution + * + * Ideas(Not implemented): + * modifiers + * system usage + * consumer usage + * unicode usage + * function call + * conditionals + * loop + */ +enum macro_command_id { + /* 0x00 - 0x03 */ + END = 0x00, + KEY_DOWN, + KEY_UP, + + /* 0x04 - 0x73 (reserved for keycode down) */ + + /* 0x74 - 0x83 */ + WAIT = 0x74, + INTERVAL, + + /* 0x84 - 0xf3 (reserved for keycode up) */ + + /* 0xf4 - 0xff */ +}; + +/* TODO: keycode:0x04-0x73 can be handled by 1byte command else 2bytes are needed + * if keycode between 0x04 and 0x73 + * keycode / (keycode|0x80) + * else + * {KEY_DOWN, keycode} / {KEY_UP, keycode} + */ +#define DOWN(key) KEY_DOWN, (key) +#define UP(key) KEY_UP, (key) +#define TYPE(key) DOWN(key), UP(key) +#define WAIT(ms) WAIT, (ms) +#define INTERVAL(ms) INTERVAL, (ms) + +/* key down */ +#define D(key) DOWN(KC_##key) +/* key up */ +#define U(key) UP(KC_##key) +/* key type */ +#define T(key) TYPE(KC_##key) +/* wait */ +#define W(ms) WAIT(ms) +/* interval */ +#define I(ms) INTERVAL(ms) + +/* for backward comaptibility */ +#define MD(key) DOWN(KC_##key) +#define MU(key) UP(KC_##key) diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c new file mode 100644 index 0000000000..36839f9faf --- /dev/null +++ b/quantum/action_tapping.c @@ -0,0 +1,456 @@ +#include +#include +#include "action.h" +#include "action_layer.h" +#include "action_tapping.h" +#include "keycode.h" +#include "timer.h" + +#ifdef DEBUG_ACTION +# include "debug.h" +#else +# include "nodebug.h" +#endif + +#ifndef NO_ACTION_TAPPING + +# define IS_TAPPING() !IS_NOEVENT(tapping_key.event) +# define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed) +# define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed) +# define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k))) +#ifndef COMBO_ENABLE +# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key))) +#else +# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode) +#endif + +__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; } + +# ifdef TAPPING_TERM_PER_KEY +# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key)) +# else +# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM) +# endif + +# ifdef TAPPING_FORCE_HOLD_PER_KEY +__attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) { return false; } +# endif + +# ifdef PERMISSIVE_HOLD_PER_KEY +__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { return false; } +# endif + +# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY +__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; } +# endif + +static keyrecord_t tapping_key = {}; +static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {}; +static uint8_t waiting_buffer_head = 0; +static uint8_t waiting_buffer_tail = 0; + +static bool process_tapping(keyrecord_t *record); +static bool waiting_buffer_enq(keyrecord_t record); +static void waiting_buffer_clear(void); +static bool waiting_buffer_typed(keyevent_t event); +static bool waiting_buffer_has_anykey_pressed(void); +static void waiting_buffer_scan_tap(void); +static void debug_tapping_key(void); +static void debug_waiting_buffer(void); + +/** \brief Action Tapping Process + * + * FIXME: Needs doc + */ +void action_tapping_process(keyrecord_t record) { + if (process_tapping(&record)) { + if (!IS_NOEVENT(record.event)) { + debug("processed: "); + debug_record(record); + debug("\n"); + } + } else { + if (!waiting_buffer_enq(record)) { + // clear all in case of overflow. + debug("OVERFLOW: CLEAR ALL STATES\n"); + clear_keyboard(); + waiting_buffer_clear(); + tapping_key = (keyrecord_t){}; + } + } + + // process waiting_buffer + if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) { + debug("---- action_exec: process waiting_buffer -----\n"); + } + for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) { + if (process_tapping(&waiting_buffer[waiting_buffer_tail])) { + debug("processed: waiting_buffer["); + debug_dec(waiting_buffer_tail); + debug("] = "); + debug_record(waiting_buffer[waiting_buffer_tail]); + debug("\n\n"); + } else { + break; + } + } + if (!IS_NOEVENT(record.event)) { + debug("\n"); + } +} + +/** \brief Tapping + * + * Rule: Tap key is typed(pressed and released) within TAPPING_TERM. + * (without interfering by typing other key) + */ +/* return true when key event is processed or consumed. */ +bool process_tapping(keyrecord_t *keyp) { + keyevent_t event = keyp->event; + + // if tapping + if (IS_TAPPING_PRESSED()) { + if (WITHIN_TAPPING_TERM(event)) { + if (tapping_key.tap.count == 0) { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { + // first tap! + debug("Tapping: First tap(0->1).\n"); + tapping_key.tap.count = 1; + debug_tapping_key(); + process_record(&tapping_key); + + // copy tapping state + keyp->tap = tapping_key.tap; + // enqueue + return false; + } + /* Process a key typed within TAPPING_TERM + * This can register the key before settlement of tapping, + * useful for long TAPPING_TERM but may prevent fast typing. + */ +# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) + else if ((( +# ifdef TAPPING_TERM_PER_KEY + get_tapping_term(get_record_keycode(&tapping_key, false), keyp) +# else + TAPPING_TERM +# endif + >= 500) + +# ifdef PERMISSIVE_HOLD_PER_KEY + || get_permissive_hold(get_record_keycode(&tapping_key, false), keyp) +# elif defined(PERMISSIVE_HOLD) + || true +# endif + ) && + IS_RELEASED(event) && waiting_buffer_typed(event)) { + debug("Tapping: End. No tap. Interfered by typing key\n"); + process_record(&tapping_key); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + // enqueue + return false; + } +# endif + /* Process release event of a key pressed before tapping starts + * Without this unexpected repeating will occur with having fast repeating setting + * https://github.com/tmk/tmk_keyboard/issues/60 + */ + else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) { + // Modifier should be retained till end of this tapping. + action_t action = layer_switch_get_action(event.key); + switch (action.kind.id) { + case ACT_LMODS: + case ACT_RMODS: + if (action.key.mods && !action.key.code) return false; + if (IS_MOD(action.key.code)) return false; + break; + case ACT_LMODS_TAP: + case ACT_RMODS_TAP: + if (action.key.mods && keyp->tap.count == 0) return false; + if (IS_MOD(action.key.code)) return false; + break; + } + // Release of key should be process immediately. + debug("Tapping: release event of a key pressed before tapping\n"); + process_record(keyp); + return true; + } else { + // set interrupted flag when other key preesed during tapping + if (event.pressed) { + tapping_key.tap.interrupted = true; +# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) +# if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) + if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp)) +# endif + { + debug("Tapping: End. No tap. Interfered by pressed key\n"); + process_record(&tapping_key); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + // enqueue + return false; + } +# endif + } + // enqueue + return false; + } + } + // tap_count > 0 + else { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { + debug("Tapping: Tap release("); + debug_dec(tapping_key.tap.count); + debug(")\n"); + keyp->tap = tapping_key.tap; + process_record(keyp); + tapping_key = *keyp; + debug_tapping_key(); + return true; + } else if (is_tap_record(keyp) && event.pressed) { + if (tapping_key.tap.count > 1) { + debug("Tapping: Start new tap with releasing last tap(>1).\n"); + // unregister key + process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, +#ifdef COMBO_ENABLE + .keycode = tapping_key.keycode, +#endif + }); + } else { + debug("Tapping: Start while last tap(1).\n"); + } + tapping_key = *keyp; + waiting_buffer_scan_tap(); + debug_tapping_key(); + return true; + } else { + if (!IS_NOEVENT(event)) { + debug("Tapping: key event while last tap(>0).\n"); + } + process_record(keyp); + return true; + } + } + } + // after TAPPING_TERM + else { + if (tapping_key.tap.count == 0) { + debug("Tapping: End. Timeout. Not tap(0): "); + debug_event(event); + debug("\n"); + process_record(&tapping_key); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + return false; + } else { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { + debug("Tapping: End. last timeout tap release(>0)."); + keyp->tap = tapping_key.tap; + process_record(keyp); + tapping_key = (keyrecord_t){}; + return true; + } else if (is_tap_record(keyp) && event.pressed) { + if (tapping_key.tap.count > 1) { + debug("Tapping: Start new tap with releasing last timeout tap(>1).\n"); + // unregister key + process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, +#ifdef COMBO_ENABLE + .keycode = tapping_key.keycode, +#endif + }); + } else { + debug("Tapping: Start while last timeout tap(1).\n"); + } + tapping_key = *keyp; + waiting_buffer_scan_tap(); + debug_tapping_key(); + return true; + } else { + if (!IS_NOEVENT(event)) { + debug("Tapping: key event while last timeout tap(>0).\n"); + } + process_record(keyp); + return true; + } + } + } + } else if (IS_TAPPING_RELEASED()) { + if (WITHIN_TAPPING_TERM(event)) { + if (event.pressed) { + if (IS_TAPPING_RECORD(keyp)) { +//# ifndef TAPPING_FORCE_HOLD +# if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY) + if ( +# ifdef TAPPING_FORCE_HOLD_PER_KEY + !get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) && +# endif + !tapping_key.tap.interrupted && tapping_key.tap.count > 0) { + // sequential tap. + keyp->tap = tapping_key.tap; + if (keyp->tap.count < 15) keyp->tap.count += 1; + debug("Tapping: Tap press("); + debug_dec(keyp->tap.count); + debug(")\n"); + process_record(keyp); + tapping_key = *keyp; + debug_tapping_key(); + return true; + } +# endif + // FIX: start new tap again + tapping_key = *keyp; + return true; + } else if (is_tap_record(keyp)) { + // Sequential tap can be interfered with other tap key. + debug("Tapping: Start with interfering other tap.\n"); + tapping_key = *keyp; + waiting_buffer_scan_tap(); + debug_tapping_key(); + return true; + } else { + // should none in buffer + // FIX: interrupted when other key is pressed + tapping_key.tap.interrupted = true; + process_record(keyp); + return true; + } + } else { + if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n"); + process_record(keyp); + return true; + } + } else { + // FIX: process_action here? + // timeout. no sequential tap. + debug("Tapping: End(Timeout after releasing last tap): "); + debug_event(event); + debug("\n"); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + return false; + } + } + // not tapping state + else { + if (event.pressed && is_tap_record(keyp)) { + debug("Tapping: Start(Press tap key).\n"); + tapping_key = *keyp; + process_record_tap_hint(&tapping_key); + waiting_buffer_scan_tap(); + debug_tapping_key(); + return true; + } else { + process_record(keyp); + return true; + } + } +} + +/** \brief Waiting buffer enq + * + * FIXME: Needs docs + */ +bool waiting_buffer_enq(keyrecord_t record) { + if (IS_NOEVENT(record.event)) { + return true; + } + + if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) { + debug("waiting_buffer_enq: Over flow.\n"); + return false; + } + + waiting_buffer[waiting_buffer_head] = record; + waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE; + + debug("waiting_buffer_enq: "); + debug_waiting_buffer(); + return true; +} + +/** \brief Waiting buffer clear + * + * FIXME: Needs docs + */ +void waiting_buffer_clear(void) { + waiting_buffer_head = 0; + waiting_buffer_tail = 0; +} + +/** \brief Waiting buffer typed + * + * FIXME: Needs docs + */ +bool waiting_buffer_typed(keyevent_t event) { + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) { + return true; + } + } + return false; +} + +/** \brief Waiting buffer has anykey pressed + * + * FIXME: Needs docs + */ +__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) { + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + if (waiting_buffer[i].event.pressed) return true; + } + return false; +} + +/** \brief Scan buffer for tapping + * + * FIXME: Needs docs + */ +void waiting_buffer_scan_tap(void) { + // tapping already is settled + if (tapping_key.tap.count > 0) return; + // invalid state: tapping_key released && tap.count == 0 + if (!tapping_key.event.pressed) return; + + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) { + tapping_key.tap.count = 1; + waiting_buffer[i].tap.count = 1; + process_record(&tapping_key); + + debug("waiting_buffer_scan_tap: found at ["); + debug_dec(i); + debug("]\n"); + debug_waiting_buffer(); + return; + } + } +} + +/** \brief Tapping key debug print + * + * FIXME: Needs docs + */ +static void debug_tapping_key(void) { + debug("TAPPING_KEY="); + debug_record(tapping_key); + debug("\n"); +} + +/** \brief Waiting buffer debug print + * + * FIXME: Needs docs + */ +static void debug_waiting_buffer(void) { + debug("{ "); + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + debug("["); + debug_dec(i); + debug("]="); + debug_record(waiting_buffer[i]); + debug(" "); + } + debug("}\n"); +} + +#endif diff --git a/quantum/action_tapping.h b/quantum/action_tapping.h new file mode 100644 index 0000000000..7de8049c7f --- /dev/null +++ b/quantum/action_tapping.h @@ -0,0 +1,42 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +/* period of tapping(ms) */ +#ifndef TAPPING_TERM +# define TAPPING_TERM 200 +#endif + +/* tap count needed for toggling a feature */ +#ifndef TAPPING_TOGGLE +# define TAPPING_TOGGLE 5 +#endif + +#define WAITING_BUFFER_SIZE 8 + +#ifndef NO_ACTION_TAPPING +uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache); +uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache); +void action_tapping_process(keyrecord_t record); + +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record); +bool get_permissive_hold(uint16_t keycode, keyrecord_t *record); +bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record); +bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record); +bool get_retro_tapping(uint16_t keycode, keyrecord_t *record); +#endif diff --git a/quantum/action_util.c b/quantum/action_util.c new file mode 100644 index 0000000000..2b3c00cba0 --- /dev/null +++ b/quantum/action_util.c @@ -0,0 +1,455 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "host.h" +#include "report.h" +#include "debug.h" +#include "action_util.h" +#include "action_layer.h" +#include "timer.h" +#include "keycode_config.h" + +extern keymap_config_t keymap_config; + +static uint8_t real_mods = 0; +static uint8_t weak_mods = 0; +static uint8_t macro_mods = 0; +#ifdef KEY_OVERRIDE_ENABLE +static uint8_t weak_override_mods = 0; +static uint8_t suppressed_mods = 0; +#endif + +#ifdef USB_6KRO_ENABLE +# define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS) +# define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS) +# define RO_INC(a) RO_ADD(a, 1) +# define RO_DEC(a) RO_SUB(a, 1) +static int8_t cb_head = 0; +static int8_t cb_tail = 0; +static int8_t cb_count = 0; +#endif + +// TODO: pointer variable is not needed +// report_keyboard_t keyboard_report = {}; +report_keyboard_t *keyboard_report = &(report_keyboard_t){}; + +extern inline void add_key(uint8_t key); +extern inline void del_key(uint8_t key); +extern inline void clear_keys(void); + +#ifndef NO_ACTION_ONESHOT +static uint8_t oneshot_mods = 0; +static uint8_t oneshot_locked_mods = 0; +uint8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; } +void set_oneshot_locked_mods(uint8_t mods) { + if (mods != oneshot_locked_mods) { + oneshot_locked_mods = mods; + oneshot_locked_mods_changed_kb(oneshot_locked_mods); + } +} +void clear_oneshot_locked_mods(void) { + if (oneshot_locked_mods) { + oneshot_locked_mods = 0; + oneshot_locked_mods_changed_kb(oneshot_locked_mods); + } +} +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) +static uint16_t oneshot_time = 0; +bool has_oneshot_mods_timed_out(void) { return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT; } +# else +bool has_oneshot_mods_timed_out(void) { return false; } +# endif +#endif + +/* oneshot layer */ +#ifndef NO_ACTION_ONESHOT +/** \brief oneshot_layer_data bits + * LLLL LSSS + * where: + * L => are layer bits + * S => oneshot state bits + */ +static int8_t oneshot_layer_data = 0; + +inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; } +inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; } + +# ifdef SWAP_HANDS_ENABLE +enum { + SHO_OFF, + SHO_ACTIVE, // Swap hands button was pressed, and we didn't send any swapped keys yet + SHO_PRESSED, // Swap hands button is currently pressed + SHO_USED, // Swap hands button is still pressed, and we already sent swapped keys +} swap_hands_oneshot = SHO_OFF; +# endif + +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) +static uint16_t oneshot_layer_time = 0; +inline bool has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); } +# ifdef SWAP_HANDS_ENABLE +static uint16_t oneshot_swaphands_time = 0; +inline bool has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE); } +# endif +# endif + +# ifdef SWAP_HANDS_ENABLE + +void set_oneshot_swaphands(void) { + swap_hands_oneshot = SHO_PRESSED; + swap_hands = true; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_swaphands_time = timer_read(); + if (oneshot_layer_time != 0) { + oneshot_layer_time = oneshot_swaphands_time; + } +# endif +} + +void release_oneshot_swaphands(void) { + if (swap_hands_oneshot == SHO_PRESSED) { + swap_hands_oneshot = SHO_ACTIVE; + } + if (swap_hands_oneshot == SHO_USED) { + clear_oneshot_swaphands(); + } +} + +void use_oneshot_swaphands(void) { + if (swap_hands_oneshot == SHO_PRESSED) { + swap_hands_oneshot = SHO_USED; + } + if (swap_hands_oneshot == SHO_ACTIVE) { + clear_oneshot_swaphands(); + } +} + +void clear_oneshot_swaphands(void) { + swap_hands_oneshot = SHO_OFF; + swap_hands = false; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_swaphands_time = 0; +# endif +} + +# endif + +/** \brief Set oneshot layer + * + * FIXME: needs doc + */ +void set_oneshot_layer(uint8_t layer, uint8_t state) { + if (!keymap_config.oneshot_disable) { + oneshot_layer_data = layer << 3 | state; + layer_on(layer); +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_layer_time = timer_read(); +# endif + oneshot_layer_changed_kb(get_oneshot_layer()); + } else { + layer_on(layer); + } +} +/** \brief Reset oneshot layer + * + * FIXME: needs doc + */ +void reset_oneshot_layer(void) { + oneshot_layer_data = 0; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_layer_time = 0; +# endif + oneshot_layer_changed_kb(get_oneshot_layer()); +} +/** \brief Clear oneshot layer + * + * FIXME: needs doc + */ +void clear_oneshot_layer_state(oneshot_fullfillment_t state) { + uint8_t start_state = oneshot_layer_data; + oneshot_layer_data &= ~state; + if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) { + layer_off(get_oneshot_layer()); + reset_oneshot_layer(); + } +} +/** \brief Is oneshot layer active + * + * FIXME: needs doc + */ +bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); } + +/** \brief set oneshot + * + * FIXME: needs doc + */ +void oneshot_set(bool active) { + if (keymap_config.oneshot_disable != active) { + keymap_config.oneshot_disable = active; + eeconfig_update_keymap(keymap_config.raw); + dprintf("Oneshot: active: %d\n", active); + } +} + +/** \brief toggle oneshot + * + * FIXME: needs doc + */ +void oneshot_toggle(void) { oneshot_set(!keymap_config.oneshot_disable); } + +/** \brief enable oneshot + * + * FIXME: needs doc + */ +void oneshot_enable(void) { oneshot_set(true); } + +/** \brief disable oneshot + * + * FIXME: needs doc + */ +void oneshot_disable(void) { oneshot_set(false); } + +bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; } + +#endif + +/** \brief Send keyboard report + * + * FIXME: needs doc + */ +void send_keyboard_report(void) { + keyboard_report->mods = real_mods; + keyboard_report->mods |= weak_mods; + keyboard_report->mods |= macro_mods; + +#ifndef NO_ACTION_ONESHOT + if (oneshot_mods) { +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + if (has_oneshot_mods_timed_out()) { + dprintf("Oneshot: timeout\n"); + clear_oneshot_mods(); + } +# endif + keyboard_report->mods |= oneshot_mods; + if (has_anykey(keyboard_report)) { + clear_oneshot_mods(); + } + } + +#endif + +#ifdef KEY_OVERRIDE_ENABLE + // These need to be last to be able to properly control key overrides + keyboard_report->mods &= ~suppressed_mods; + keyboard_report->mods |= weak_override_mods; +#endif + + host_keyboard_send(keyboard_report); +} + +/** \brief Get mods + * + * FIXME: needs doc + */ +uint8_t get_mods(void) { return real_mods; } +/** \brief add mods + * + * FIXME: needs doc + */ +void add_mods(uint8_t mods) { real_mods |= mods; } +/** \brief del mods + * + * FIXME: needs doc + */ +void del_mods(uint8_t mods) { real_mods &= ~mods; } +/** \brief set mods + * + * FIXME: needs doc + */ +void set_mods(uint8_t mods) { real_mods = mods; } +/** \brief clear mods + * + * FIXME: needs doc + */ +void clear_mods(void) { real_mods = 0; } + +/** \brief get weak mods + * + * FIXME: needs doc + */ +uint8_t get_weak_mods(void) { return weak_mods; } +/** \brief add weak mods + * + * FIXME: needs doc + */ +void add_weak_mods(uint8_t mods) { weak_mods |= mods; } +/** \brief del weak mods + * + * FIXME: needs doc + */ +void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; } +/** \brief set weak mods + * + * FIXME: needs doc + */ +void set_weak_mods(uint8_t mods) { weak_mods = mods; } +/** \brief clear weak mods + * + * FIXME: needs doc + */ +void clear_weak_mods(void) { weak_mods = 0; } + +#ifdef KEY_OVERRIDE_ENABLE +/** \brief set weak mods used by key overrides. DO not call this manually + */ +void set_weak_override_mods(uint8_t mods) { weak_override_mods = mods; } +/** \brief clear weak mods used by key overrides. DO not call this manually + */ +void clear_weak_override_mods(void) { weak_override_mods = 0; } + +/** \brief set suppressed mods used by key overrides. DO not call this manually + */ +void set_suppressed_override_mods(uint8_t mods) { suppressed_mods = mods; } +/** \brief clear suppressed mods used by key overrides. DO not call this manually + */ +void clear_suppressed_override_mods(void) { suppressed_mods = 0; } +#endif + +/* macro modifier */ +/** \brief get macro mods + * + * FIXME: needs doc + */ +uint8_t get_macro_mods(void) { return macro_mods; } +/** \brief add macro mods + * + * FIXME: needs doc + */ +void add_macro_mods(uint8_t mods) { macro_mods |= mods; } +/** \brief del macro mods + * + * FIXME: needs doc + */ +void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; } +/** \brief set macro mods + * + * FIXME: needs doc + */ +void set_macro_mods(uint8_t mods) { macro_mods = mods; } +/** \brief clear macro mods + * + * FIXME: needs doc + */ +void clear_macro_mods(void) { macro_mods = 0; } + +#ifndef NO_ACTION_ONESHOT +/** \brief get oneshot mods + * + * FIXME: needs doc + */ +uint8_t get_oneshot_mods(void) { return oneshot_mods; } + +void add_oneshot_mods(uint8_t mods) { + if ((oneshot_mods & mods) != mods) { +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = timer_read(); +# endif + oneshot_mods |= mods; + oneshot_mods_changed_kb(mods); + } +} + +void del_oneshot_mods(uint8_t mods) { + if (oneshot_mods & mods) { + oneshot_mods &= ~mods; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = oneshot_mods ? timer_read() : 0; +# endif + oneshot_mods_changed_kb(oneshot_mods); + } +} + +/** \brief set oneshot mods + * + * FIXME: needs doc + */ +void set_oneshot_mods(uint8_t mods) { + if (!keymap_config.oneshot_disable) { + if (oneshot_mods != mods) { +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = timer_read(); +# endif + oneshot_mods = mods; + oneshot_mods_changed_kb(mods); + } + } +} + +/** \brief clear oneshot mods + * + * FIXME: needs doc + */ +void clear_oneshot_mods(void) { + if (oneshot_mods) { + oneshot_mods = 0; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = 0; +# endif + oneshot_mods_changed_kb(oneshot_mods); + } +} +#endif + +/** \brief Called when the one shot modifiers have been changed. + * + * \param mods Contains the active modifiers active after the change. + */ +__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {} + +/** \brief Called when the locked one shot modifiers have been changed. + * + * \param mods Contains the active modifiers active after the change. + */ +__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) { oneshot_locked_mods_changed_user(mods); } + +/** \brief Called when the one shot modifiers have been changed. + * + * \param mods Contains the active modifiers active after the change. + */ +__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {} + +/** \brief Called when the one shot modifiers have been changed. + * + * \param mods Contains the active modifiers active after the change. + */ +__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) { oneshot_mods_changed_user(mods); } + +/** \brief Called when the one shot layers have been changed. + * + * \param layer Contains the layer that is toggled on, or zero when toggled off. + */ +__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {} + +/** \brief Called when the one shot layers have been changed. + * + * \param layer Contains the layer that is toggled on, or zero when toggled off. + */ +__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) { oneshot_layer_changed_user(layer); } + +/** \brief inspect keyboard state + * + * FIXME: needs doc + */ +uint8_t has_anymod(void) { return bitpop(real_mods); } diff --git a/quantum/action_util.h b/quantum/action_util.h new file mode 100644 index 0000000000..f2b3897ae5 --- /dev/null +++ b/quantum/action_util.h @@ -0,0 +1,105 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include "report.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern report_keyboard_t *keyboard_report; + +void send_keyboard_report(void); + +/* key */ +inline void add_key(uint8_t key) { add_key_to_report(keyboard_report, key); } + +inline void del_key(uint8_t key) { del_key_from_report(keyboard_report, key); } + +inline void clear_keys(void) { clear_keys_from_report(keyboard_report); } + +/* modifier */ +uint8_t get_mods(void); +void add_mods(uint8_t mods); +void del_mods(uint8_t mods); +void set_mods(uint8_t mods); +void clear_mods(void); + +/* weak modifier */ +uint8_t get_weak_mods(void); +void add_weak_mods(uint8_t mods); +void del_weak_mods(uint8_t mods); +void set_weak_mods(uint8_t mods); +void clear_weak_mods(void); + +/* macro modifier */ +uint8_t get_macro_mods(void); +void add_macro_mods(uint8_t mods); +void del_macro_mods(uint8_t mods); +void set_macro_mods(uint8_t mods); +void clear_macro_mods(void); + +/* oneshot modifier */ +uint8_t get_oneshot_mods(void); +void add_oneshot_mods(uint8_t mods); +void del_oneshot_mods(uint8_t mods); +void set_oneshot_mods(uint8_t mods); +void clear_oneshot_mods(void); +bool has_oneshot_mods_timed_out(void); + +uint8_t get_oneshot_locked_mods(void); +void set_oneshot_locked_mods(uint8_t mods); +void clear_oneshot_locked_mods(void); + +typedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t; +void set_oneshot_layer(uint8_t layer, uint8_t state); +uint8_t get_oneshot_layer(void); +void clear_oneshot_layer_state(oneshot_fullfillment_t state); +void reset_oneshot_layer(void); +bool is_oneshot_layer_active(void); +uint8_t get_oneshot_layer_state(void); +bool has_oneshot_layer_timed_out(void); +bool has_oneshot_swaphands_timed_out(void); + +void oneshot_locked_mods_changed_user(uint8_t mods); +void oneshot_locked_mods_changed_kb(uint8_t mods); +void oneshot_mods_changed_user(uint8_t mods); +void oneshot_mods_changed_kb(uint8_t mods); +void oneshot_layer_changed_user(uint8_t layer); +void oneshot_layer_changed_kb(uint8_t layer); + +void oneshot_toggle(void); +void oneshot_enable(void); +void oneshot_disable(void); +bool is_oneshot_enabled(void); + +/* inspect */ +uint8_t has_anymod(void); + +#ifdef SWAP_HANDS_ENABLE +void set_oneshot_swaphands(void); +void release_oneshot_swaphands(void); +void use_oneshot_swaphands(void); +void clear_oneshot_swaphands(void); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c new file mode 100644 index 0000000000..ffa56ab56d --- /dev/null +++ b/quantum/eeconfig.c @@ -0,0 +1,211 @@ +#include +#include +#include "eeprom.h" +#include "eeconfig.h" +#include "action_layer.h" + +#ifdef STM32_EEPROM_ENABLE +# include +# include "eeprom_stm32.h" +#endif + +#if defined(EEPROM_DRIVER) +# include "eeprom_driver.h" +#endif + +#if defined(HAPTIC_ENABLE) +# include "haptic.h" +#endif + +/** \brief eeconfig enable + * + * FIXME: needs doc + */ +__attribute__((weak)) void eeconfig_init_user(void) { + // Reset user EEPROM value to blank, rather than to a set value + eeconfig_update_user(0); +} + +__attribute__((weak)) void eeconfig_init_kb(void) { + // Reset Keyboard EEPROM value to blank, rather than to a set value + eeconfig_update_kb(0); + + eeconfig_init_user(); +} + +/* + * FIXME: needs doc + */ +void eeconfig_init_quantum(void) { +#ifdef STM32_EEPROM_ENABLE + EEPROM_Erase(); +#endif +#if defined(EEPROM_DRIVER) + eeprom_driver_erase(); +#endif + eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); + eeprom_update_byte(EECONFIG_DEBUG, 0); + eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0); + default_layer_state = 0; + eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0); + eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0); + eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0); + eeprom_update_byte(EECONFIG_BACKLIGHT, 0); + eeprom_update_byte(EECONFIG_AUDIO, 0xFF); // On by default + eeprom_update_dword(EECONFIG_RGBLIGHT, 0); + eeprom_update_byte(EECONFIG_STENOMODE, 0); + eeprom_update_dword(EECONFIG_HAPTIC, 0); + eeprom_update_byte(EECONFIG_VELOCIKEY, 0); + eeprom_update_dword(EECONFIG_RGB_MATRIX, 0); + eeprom_update_word(EECONFIG_RGB_MATRIX_EXTENDED, 0); + + // TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS + // within the emulated eeprom via dfu-util or another tool +#if defined INIT_EE_HANDS_LEFT +# pragma message "Faking EE_HANDS for left hand" + eeprom_update_byte(EECONFIG_HANDEDNESS, 1); +#elif defined INIT_EE_HANDS_RIGHT +# pragma message "Faking EE_HANDS for right hand" + eeprom_update_byte(EECONFIG_HANDEDNESS, 0); +#endif + +#if defined(HAPTIC_ENABLE) + haptic_reset(); +#else + // this is used in case haptic is disabled, but we still want sane defaults + // in the haptic configuration eeprom. All zero will trigger a haptic_reset + // when a haptic-enabled firmware is loaded onto the keyboard. + eeprom_update_dword(EECONFIG_HAPTIC, 0); +#endif + + eeconfig_init_kb(); +} + +/** \brief eeconfig initialization + * + * FIXME: needs doc + */ +void eeconfig_init(void) { eeconfig_init_quantum(); } + +/** \brief eeconfig enable + * + * FIXME: needs doc + */ +void eeconfig_enable(void) { eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); } + +/** \brief eeconfig disable + * + * FIXME: needs doc + */ +void eeconfig_disable(void) { +#ifdef STM32_EEPROM_ENABLE + EEPROM_Erase(); +#endif +#if defined(EEPROM_DRIVER) + eeprom_driver_erase(); +#endif + eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF); +} + +/** \brief eeconfig is enabled + * + * FIXME: needs doc + */ +bool eeconfig_is_enabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); } + +/** \brief eeconfig is disabled + * + * FIXME: needs doc + */ +bool eeconfig_is_disabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF); } + +/** \brief eeconfig read debug + * + * FIXME: needs doc + */ +uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); } +/** \brief eeconfig update debug + * + * FIXME: needs doc + */ +void eeconfig_update_debug(uint8_t val) { eeprom_update_byte(EECONFIG_DEBUG, val); } + +/** \brief eeconfig read default layer + * + * FIXME: needs doc + */ +uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); } +/** \brief eeconfig update default layer + * + * FIXME: needs doc + */ +void eeconfig_update_default_layer(uint8_t val) { eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val); } + +/** \brief eeconfig read keymap + * + * FIXME: needs doc + */ +uint16_t eeconfig_read_keymap(void) { return (eeprom_read_byte(EECONFIG_KEYMAP_LOWER_BYTE) | (eeprom_read_byte(EECONFIG_KEYMAP_UPPER_BYTE) << 8)); } +/** \brief eeconfig update keymap + * + * FIXME: needs doc + */ +void eeconfig_update_keymap(uint16_t val) { + eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, val & 0xFF); + eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, (val >> 8) & 0xFF); +} + +/** \brief eeconfig read audio + * + * FIXME: needs doc + */ +uint8_t eeconfig_read_audio(void) { return eeprom_read_byte(EECONFIG_AUDIO); } +/** \brief eeconfig update audio + * + * FIXME: needs doc + */ +void eeconfig_update_audio(uint8_t val) { eeprom_update_byte(EECONFIG_AUDIO, val); } + +/** \brief eeconfig read kb + * + * FIXME: needs doc + */ +uint32_t eeconfig_read_kb(void) { return eeprom_read_dword(EECONFIG_KEYBOARD); } +/** \brief eeconfig update kb + * + * FIXME: needs doc + */ +void eeconfig_update_kb(uint32_t val) { eeprom_update_dword(EECONFIG_KEYBOARD, val); } + +/** \brief eeconfig read user + * + * FIXME: needs doc + */ +uint32_t eeconfig_read_user(void) { return eeprom_read_dword(EECONFIG_USER); } +/** \brief eeconfig update user + * + * FIXME: needs doc + */ +void eeconfig_update_user(uint32_t val) { eeprom_update_dword(EECONFIG_USER, val); } + +/** \brief eeconfig read haptic + * + * FIXME: needs doc + */ +uint32_t eeconfig_read_haptic(void) { return eeprom_read_dword(EECONFIG_HAPTIC); } +/** \brief eeconfig update haptic + * + * FIXME: needs doc + */ +void eeconfig_update_haptic(uint32_t val) { eeprom_update_dword(EECONFIG_HAPTIC, val); } + +/** \brief eeconfig read split handedness + * + * FIXME: needs doc + */ +bool eeconfig_read_handedness(void) { return !!eeprom_read_byte(EECONFIG_HANDEDNESS); } +/** \brief eeconfig update split handedness + * + * FIXME: needs doc + */ +void eeconfig_update_handedness(bool val) { eeprom_update_byte(EECONFIG_HANDEDNESS, !!val); } diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h new file mode 100644 index 0000000000..a88071729d --- /dev/null +++ b/quantum/eeconfig.h @@ -0,0 +1,113 @@ +/* +Copyright 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include + +#ifndef EECONFIG_MAGIC_NUMBER +# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA // When changing, decrement this value to avoid future re-init issues +#endif +#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF + +/* EEPROM parameter address */ +#define EECONFIG_MAGIC (uint16_t *)0 +#define EECONFIG_DEBUG (uint8_t *)2 +#define EECONFIG_DEFAULT_LAYER (uint8_t *)3 +#define EECONFIG_KEYMAP (uint8_t *)4 +#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5 +#define EECONFIG_BACKLIGHT (uint8_t *)6 +#define EECONFIG_AUDIO (uint8_t *)7 +#define EECONFIG_RGBLIGHT (uint32_t *)8 +#define EECONFIG_UNICODEMODE (uint8_t *)12 +#define EECONFIG_STENOMODE (uint8_t *)13 +// EEHANDS for two handed boards +#define EECONFIG_HANDEDNESS (uint8_t *)14 +#define EECONFIG_KEYBOARD (uint32_t *)15 +#define EECONFIG_USER (uint32_t *)19 +#define EECONFIG_VELOCIKEY (uint8_t *)23 + +#define EECONFIG_HAPTIC (uint32_t *)24 + +// Mutually exclusive +#define EECONFIG_LED_MATRIX (uint32_t *)28 +#define EECONFIG_RGB_MATRIX (uint32_t *)28 +// Speed & Flags +#define EECONFIG_LED_MATRIX_EXTENDED (uint16_t *)32 +#define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32 + +// TODO: Combine these into a single word and single block of EEPROM +#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34 +// Size of EEPROM being used, other code can refer to this for available EEPROM +#define EECONFIG_SIZE 35 +/* debug bit */ +#define EECONFIG_DEBUG_ENABLE (1 << 0) +#define EECONFIG_DEBUG_MATRIX (1 << 1) +#define EECONFIG_DEBUG_KEYBOARD (1 << 2) +#define EECONFIG_DEBUG_MOUSE (1 << 3) + +/* keyconf bit */ +#define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0) +#define EECONFIG_KEYMAP_CAPSLOCK_TO_CONTROL (1 << 1) +#define EECONFIG_KEYMAP_SWAP_LALT_LGUI (1 << 2) +#define EECONFIG_KEYMAP_SWAP_RALT_RGUI (1 << 3) +#define EECONFIG_KEYMAP_NO_GUI (1 << 4) +#define EECONFIG_KEYMAP_SWAP_GRAVE_ESC (1 << 5) +#define EECONFIG_KEYMAP_SWAP_BACKSLASH_BACKSPACE (1 << 6) +#define EECONFIG_KEYMAP_NKRO (1 << 7) + +#define EECONFIG_KEYMAP_LOWER_BYTE EECONFIG_KEYMAP + +bool eeconfig_is_enabled(void); +bool eeconfig_is_disabled(void); + +void eeconfig_init(void); +void eeconfig_init_quantum(void); +void eeconfig_init_kb(void); +void eeconfig_init_user(void); + +void eeconfig_enable(void); + +void eeconfig_disable(void); + +uint8_t eeconfig_read_debug(void); +void eeconfig_update_debug(uint8_t val); + +uint8_t eeconfig_read_default_layer(void); +void eeconfig_update_default_layer(uint8_t val); + +uint16_t eeconfig_read_keymap(void); +void eeconfig_update_keymap(uint16_t val); + +#ifdef AUDIO_ENABLE +uint8_t eeconfig_read_audio(void); +void eeconfig_update_audio(uint8_t val); +#endif + +uint32_t eeconfig_read_kb(void); +void eeconfig_update_kb(uint32_t val); +uint32_t eeconfig_read_user(void); +void eeconfig_update_user(uint32_t val); + +#ifdef HAPTIC_ENABLE +uint32_t eeconfig_read_haptic(void); +void eeconfig_update_haptic(uint32_t val); +#endif + +bool eeconfig_read_handedness(void); +void eeconfig_update_handedness(bool val); diff --git a/quantum/keyboard.c b/quantum/keyboard.c new file mode 100644 index 0000000000..0eb41e7d30 --- /dev/null +++ b/quantum/keyboard.c @@ -0,0 +1,569 @@ +/* +Copyright 2011, 2012, 2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include "keyboard.h" +#include "matrix.h" +#include "keymap.h" +#include "host.h" +#include "led.h" +#include "keycode.h" +#include "timer.h" +#include "sync_timer.h" +#include "print.h" +#include "debug.h" +#include "command.h" +#include "util.h" +#include "sendchar.h" +#include "eeconfig.h" +#include "action_layer.h" +#ifdef BACKLIGHT_ENABLE +# include "backlight.h" +#endif +#ifdef MOUSEKEY_ENABLE +# include "mousekey.h" +#endif +#ifdef PS2_MOUSE_ENABLE +# include "ps2_mouse.h" +#endif +#ifdef SERIAL_MOUSE_ENABLE +# include "serial_mouse.h" +#endif +#ifdef ADB_MOUSE_ENABLE +# include "adb.h" +#endif +#ifdef RGBLIGHT_ENABLE +# include "rgblight.h" +#endif +#ifdef LED_MATRIX_ENABLE +# include "led_matrix.h" +#endif +#ifdef RGB_MATRIX_ENABLE +# include "rgb_matrix.h" +#endif +#ifdef ENCODER_ENABLE +# include "encoder.h" +#endif +#ifdef STENO_ENABLE +# include "process_steno.h" +#endif +#ifdef SERIAL_LINK_ENABLE +# include "serial_link/system/serial_link.h" +#endif +#ifdef VISUALIZER_ENABLE +# include "visualizer/visualizer.h" +#endif +#ifdef POINTING_DEVICE_ENABLE +# include "pointing_device.h" +#endif +#ifdef MIDI_ENABLE +# include "process_midi.h" +#endif +#ifdef JOYSTICK_ENABLE +# include "process_joystick.h" +#endif +#ifdef HD44780_ENABLE +# include "hd44780.h" +#endif +#ifdef QWIIC_ENABLE +# include "qwiic.h" +#endif +#ifdef OLED_DRIVER_ENABLE +# include "oled_driver.h" +#endif +#ifdef ST7565_ENABLE +# include "st7565.h" +#endif +#ifdef VELOCIKEY_ENABLE +# include "velocikey.h" +#endif +#ifdef VIA_ENABLE +# include "via.h" +#endif +#ifdef DIP_SWITCH_ENABLE +# include "dip_switch.h" +#endif +#ifdef STM32_EEPROM_ENABLE +# include "eeprom_stm32.h" +#endif +#ifdef EEPROM_DRIVER +# include "eeprom_driver.h" +#endif +#if defined(CRC_ENABLE) +# include "crc.h" +#endif +#ifdef DIGITIZER_ENABLE +# include "digitizer.h" +#endif + +static uint32_t last_input_modification_time = 0; +uint32_t last_input_activity_time(void) { return last_input_modification_time; } +uint32_t last_input_activity_elapsed(void) { return timer_elapsed32(last_input_modification_time); } + +static uint32_t last_matrix_modification_time = 0; +uint32_t last_matrix_activity_time(void) { return last_matrix_modification_time; } +uint32_t last_matrix_activity_elapsed(void) { return timer_elapsed32(last_matrix_modification_time); } +void last_matrix_activity_trigger(void) { last_matrix_modification_time = last_input_modification_time = timer_read32(); } + +static uint32_t last_encoder_modification_time = 0; +uint32_t last_encoder_activity_time(void) { return last_encoder_modification_time; } +uint32_t last_encoder_activity_elapsed(void) { return timer_elapsed32(last_encoder_modification_time); } +void last_encoder_activity_trigger(void) { last_encoder_modification_time = last_input_modification_time = timer_read32(); } + +// Only enable this if console is enabled to print to +#if defined(DEBUG_MATRIX_SCAN_RATE) +static uint32_t matrix_timer = 0; +static uint32_t matrix_scan_count = 0; +static uint32_t last_matrix_scan_count = 0; + +void matrix_scan_perf_task(void) { + matrix_scan_count++; + + uint32_t timer_now = timer_read32(); + if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) { +# if defined(CONSOLE_ENABLE) + dprintf("matrix scan frequency: %lu\n", matrix_scan_count); +# endif + last_matrix_scan_count = matrix_scan_count; + matrix_timer = timer_now; + matrix_scan_count = 0; + } +} + +uint32_t get_matrix_scan_rate(void) { return last_matrix_scan_count; } +#else +# define matrix_scan_perf_task() +#endif + +#ifdef MATRIX_HAS_GHOST +extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; +static matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) { + matrix_row_t out = 0; + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + // read each key in the row data and check if the keymap defines it as a real key + if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) { + // this creates new row data, if a key is defined in the keymap, it will be set here + out |= 1 << col; + } + } + return out; +} + +static inline bool popcount_more_than_one(matrix_row_t rowdata) { + rowdata &= rowdata - 1; // if there are less than two bits (keys) set, rowdata will become zero + return rowdata; +} + +static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) { + /* No ghost exists when less than 2 keys are down on the row. + If there are "active" blanks in the matrix, the key can't be pressed by the user, + there is no doubt as to which keys are really being pressed. + The ghosts will be ignored, they are KC_NO. */ + rowdata = get_real_keys(row, rowdata); + if ((popcount_more_than_one(rowdata)) == 0) { + return false; + } + /* Ghost occurs when the row shares a column line with other row, + and two columns are read on each row. Blanks in the matrix don't matter, + so they are filtered out. + If there are two or more real keys pressed and they match columns with + at least two of another row's real keys, the row will be ignored. Keep in mind, + we are checking one row at a time, not all of them at once. + */ + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) { + return true; + } + } + return false; +} + +#endif + +void disable_jtag(void) { +// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles. +#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) + MCUCR |= _BV(JTD); + MCUCR |= _BV(JTD); +#elif defined(__AVR_ATmega32A__) + MCUCSR |= _BV(JTD); + MCUCSR |= _BV(JTD); +#endif +} + +/** \brief matrix_setup + * + * FIXME: needs doc + */ +__attribute__((weak)) void matrix_setup(void) {} + +/** \brief keyboard_pre_init_user + * + * FIXME: needs doc + */ +__attribute__((weak)) void keyboard_pre_init_user(void) {} + +/** \brief keyboard_pre_init_kb + * + * FIXME: needs doc + */ +__attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); } + +/** \brief keyboard_post_init_user + * + * FIXME: needs doc + */ + +__attribute__((weak)) void keyboard_post_init_user() {} + +/** \brief keyboard_post_init_kb + * + * FIXME: needs doc + */ + +__attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); } + +/** \brief keyboard_setup + * + * FIXME: needs doc + */ +void keyboard_setup(void) { +#ifndef NO_JTAG_DISABLE + disable_jtag(); +#endif + print_set_sendchar(sendchar); +#ifdef STM32_EEPROM_ENABLE + EEPROM_Init(); +#endif +#ifdef EEPROM_DRIVER + eeprom_driver_init(); +#endif + matrix_setup(); + keyboard_pre_init_kb(); +} + +#ifndef SPLIT_KEYBOARD + +/** \brief is_keyboard_master + * + * FIXME: needs doc + */ +__attribute__((weak)) bool is_keyboard_master(void) { return true; } + +/** \brief is_keyboard_left + * + * FIXME: needs doc + */ +__attribute__((weak)) bool is_keyboard_left(void) { return true; } + +#endif + +/** \brief should_process_keypress + * + * Override this function if you have a condition where keypresses processing should change: + * - splits where the slave side needs to process for rgb/oled functionality + */ +__attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); } + +/** \brief housekeeping_task_kb + * + * Override this function if you have a need to execute code for every keyboard main loop iteration. + * This is specific to keyboard-level functionality. + */ +__attribute__((weak)) void housekeeping_task_kb(void) {} + +/** \brief housekeeping_task_user + * + * Override this function if you have a need to execute code for every keyboard main loop iteration. + * This is specific to user/keymap-level functionality. + */ +__attribute__((weak)) void housekeeping_task_user(void) {} + +/** \brief housekeeping_task + * + * Invokes hooks for executing code after QMK is done after each loop iteration. + */ +void housekeeping_task(void) { + housekeeping_task_kb(); + housekeeping_task_user(); +} + +/** \brief keyboard_init + * + * FIXME: needs doc + */ +void keyboard_init(void) { + timer_init(); + sync_timer_init(); + matrix_init(); +#if defined(CRC_ENABLE) + crc_init(); +#endif +#ifdef VIA_ENABLE + via_init(); +#endif +#ifdef QWIIC_ENABLE + qwiic_init(); +#endif +#ifdef OLED_DRIVER_ENABLE + oled_init(OLED_ROTATION_0); +#endif +#ifdef ST7565_ENABLE + st7565_init(DISPLAY_ROTATION_0); +#endif +#ifdef PS2_MOUSE_ENABLE + ps2_mouse_init(); +#endif +#ifdef SERIAL_MOUSE_ENABLE + serial_mouse_init(); +#endif +#ifdef ADB_MOUSE_ENABLE + adb_mouse_init(); +#endif +#ifdef BACKLIGHT_ENABLE + backlight_init(); +#endif +#ifdef RGBLIGHT_ENABLE + rgblight_init(); +#endif +#ifdef ENCODER_ENABLE + encoder_init(); +#endif +#ifdef STENO_ENABLE + steno_init(); +#endif +#ifdef POINTING_DEVICE_ENABLE + pointing_device_init(); +#endif +#if defined(NKRO_ENABLE) && defined(FORCE_NKRO) + keymap_config.nkro = 1; + eeconfig_update_keymap(keymap_config.raw); +#endif +#ifdef DIP_SWITCH_ENABLE + dip_switch_init(); +#endif + +#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE) + debug_enable = true; +#endif + + keyboard_post_init_kb(); /* Always keep this last */ +} + +/** \brief key_event_task + * + * This function is responsible for calling into other systems when they need to respond to electrical switch press events. + * This is differnet than keycode events as no layer processing, or filtering occurs. + */ +void switch_events(uint8_t row, uint8_t col, bool pressed) { +#if defined(LED_MATRIX_ENABLE) + process_led_matrix(row, col, pressed); +#endif +#if defined(RGB_MATRIX_ENABLE) + process_rgb_matrix(row, col, pressed); +#endif +} + +/** \brief Keyboard task: Do keyboard routine jobs + * + * Do routine keyboard jobs: + * + * * scan matrix + * * handle mouse movements + * * run visualizer code + * * handle midi commands + * * light LEDs + * + * This is repeatedly called as fast as possible. + */ +void keyboard_task(void) { + static matrix_row_t matrix_prev[MATRIX_ROWS]; + static uint8_t led_status = 0; + matrix_row_t matrix_row = 0; + matrix_row_t matrix_change = 0; +#ifdef QMK_KEYS_PER_SCAN + uint8_t keys_processed = 0; +#endif +#ifdef ENCODER_ENABLE + bool encoders_changed = false; +#endif + + uint8_t matrix_changed = matrix_scan(); + if (matrix_changed) last_matrix_activity_trigger(); + + for (uint8_t r = 0; r < MATRIX_ROWS; r++) { + matrix_row = matrix_get_row(r); + matrix_change = matrix_row ^ matrix_prev[r]; + if (matrix_change) { +#ifdef MATRIX_HAS_GHOST + if (has_ghost_in_row(r, matrix_row)) { + continue; + } +#endif + if (debug_matrix) matrix_print(); + matrix_row_t col_mask = 1; + for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) { + if (matrix_change & col_mask) { + if (should_process_keypress()) { + action_exec((keyevent_t){ + .key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */ + }); + } + // record a processed key + matrix_prev[r] ^= col_mask; + + switch_events(r, c, (matrix_row & col_mask)); + +#ifdef QMK_KEYS_PER_SCAN + // only jump out if we have processed "enough" keys. + if (++keys_processed >= QMK_KEYS_PER_SCAN) +#endif + // process a key per task call + goto MATRIX_LOOP_END; + } + } + } + } + // call with pseudo tick event when no real key event. +#ifdef QMK_KEYS_PER_SCAN + // we can get here with some keys processed now. + if (!keys_processed) +#endif + action_exec(TICK); + +MATRIX_LOOP_END: + +#ifdef DEBUG_MATRIX_SCAN_RATE + matrix_scan_perf_task(); +#endif + +#if defined(RGBLIGHT_ENABLE) + rgblight_task(); +#endif + +#ifdef LED_MATRIX_ENABLE + led_matrix_task(); +#endif +#ifdef RGB_MATRIX_ENABLE + rgb_matrix_task(); +#endif + +#if defined(BACKLIGHT_ENABLE) +# if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS) + backlight_task(); +# endif +#endif + +#ifdef ENCODER_ENABLE + encoders_changed = encoder_read(); + if (encoders_changed) last_encoder_activity_trigger(); +#endif + +#ifdef QWIIC_ENABLE + qwiic_task(); +#endif + +#ifdef OLED_DRIVER_ENABLE + oled_task(); +# ifndef OLED_DISABLE_TIMEOUT + // Wake up oled if user is using those fabulous keys or spinning those encoders! +# ifdef ENCODER_ENABLE + if (matrix_changed || encoders_changed) oled_on(); +# else + if (matrix_changed) oled_on(); +# endif +# endif +#endif + +#ifdef ST7565_ENABLE + st7565_task(); +# ifndef ST7565_DISABLE_TIMEOUT + // Wake up display if user is using those fabulous keys or spinning those encoders! +# ifdef ENCODER_ENABLE + if (matrix_changed || encoders_changed) st7565_on(); +# else + if (matrix_changed) st7565_on(); +# endif +# endif +#endif + +#ifdef MOUSEKEY_ENABLE + // mousekey repeat & acceleration + mousekey_task(); +#endif + +#ifdef PS2_MOUSE_ENABLE + ps2_mouse_task(); +#endif + +#ifdef SERIAL_MOUSE_ENABLE + serial_mouse_task(); +#endif + +#ifdef ADB_MOUSE_ENABLE + adb_mouse_task(); +#endif + +#ifdef SERIAL_LINK_ENABLE + serial_link_update(); +#endif + +#ifdef VISUALIZER_ENABLE + visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds()); +#endif + +#ifdef POINTING_DEVICE_ENABLE + pointing_device_task(); +#endif + +#ifdef MIDI_ENABLE + midi_task(); +#endif + +#ifdef VELOCIKEY_ENABLE + if (velocikey_enabled()) { + velocikey_decelerate(); + } +#endif + +#ifdef JOYSTICK_ENABLE + joystick_task(); +#endif + +#ifdef DIGITIZER_ENABLE + digitizer_task(); +#endif + + // update LED + if (led_status != host_keyboard_leds()) { + led_status = host_keyboard_leds(); + keyboard_set_leds(led_status); + } +} + +/** \brief keyboard set leds + * + * FIXME: needs doc + */ +void keyboard_set_leds(uint8_t leds) { + if (debug_keyboard) { + debug("keyboard_set_led: "); + debug_hex8(leds); + debug("\n"); + } + led_set(leds); +} diff --git a/quantum/keyboard.h b/quantum/keyboard.h new file mode 100644 index 0000000000..08f4e84f94 --- /dev/null +++ b/quantum/keyboard.h @@ -0,0 +1,90 @@ +/* +Copyright 2011,2012,2013 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* key matrix position */ +typedef struct { + uint8_t col; + uint8_t row; +} keypos_t; + +/* key event */ +typedef struct { + keypos_t key; + bool pressed; + uint16_t time; +} keyevent_t; + +/* equivalent test of keypos_t */ +#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col) + +/* Rules for No Event: + * 1) (time == 0) to handle (keyevent_t){} as empty event + * 2) Matrix(255, 255) to make TICK event available + */ +static inline bool IS_NOEVENT(keyevent_t event) { return event.time == 0 || (event.key.row == 255 && event.key.col == 255); } +static inline bool IS_PRESSED(keyevent_t event) { return (!IS_NOEVENT(event) && event.pressed); } +static inline bool IS_RELEASED(keyevent_t event) { return (!IS_NOEVENT(event) && !event.pressed); } + +/* Tick event */ +#define TICK \ + (keyevent_t) { .key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) } + +/* it runs once at early stage of startup before keyboard_init. */ +void keyboard_setup(void); +/* it runs once after initializing host side protocol, debug and MCU peripherals. */ +void keyboard_init(void); +/* it runs repeatedly in main loop */ +void keyboard_task(void); +/* it runs when host LED status is updated */ +void keyboard_set_leds(uint8_t leds); +/* it runs whenever code has to behave differently on a slave */ +bool is_keyboard_master(void); +/* it runs whenever code has to behave differently on left vs right split */ +bool is_keyboard_left(void); + +void keyboard_pre_init_kb(void); +void keyboard_pre_init_user(void); +void keyboard_post_init_kb(void); +void keyboard_post_init_user(void); + +void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol +void housekeeping_task_kb(void); // To be overridden by keyboard-level code +void housekeeping_task_user(void); // To be overridden by user/keymap-level code + +uint32_t last_input_activity_time(void); // Timestamp of the last matrix or encoder activity +uint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder activity + +uint32_t last_matrix_activity_time(void); // Timestamp of the last matrix activity +uint32_t last_matrix_activity_elapsed(void); // Number of milliseconds since the last matrix activity + +uint32_t last_encoder_activity_time(void); // Timestamp of the last encoder activity +uint32_t last_encoder_activity_elapsed(void); // Number of milliseconds since the last encoder activity + +uint32_t get_matrix_scan_rate(void); + +#ifdef __cplusplus +} +#endif diff --git a/quantum/keycode.h b/quantum/keycode.h new file mode 100644 index 0000000000..8facabd818 --- /dev/null +++ b/quantum/keycode.h @@ -0,0 +1,560 @@ +/* +Copyright 2011,2012 Jun Wako + +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 . +*/ + +/* + * Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C) + * + * See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf + * or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older) + */ + +#pragma once + +/* FIXME: Add doxygen comments here */ + +#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED) +#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF) +#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL) +#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI) + +#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF)) +#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE) +#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID) + +#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31) + +#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2) +#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT) +#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN8) +#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT) +#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2) + +#define MOD_BIT(code) (1 << MOD_INDEX(code)) +#define MOD_INDEX(code) ((code)&0x07) + +#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL)) +#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) +#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT)) +#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI)) +#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT) +#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT) +#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI) +#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT) +#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI) +#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT) +#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI) +#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) + +#define FN_BIT(code) (1 << FN_INDEX(code)) +#define FN_INDEX(code) ((code)-KC_FN0) +#define FN_MIN KC_FN0 +#define FN_MAX KC_FN31 + +/* + * Short names for ease of definition of keymap + */ +/* Transparent */ +#define KC_TRANSPARENT 0x01 +#define KC_TRNS KC_TRANSPARENT + +/* Punctuation */ +#define KC_ENT KC_ENTER +#define KC_ESC KC_ESCAPE +#define KC_BSPC KC_BSPACE +#define KC_SPC KC_SPACE +#define KC_MINS KC_MINUS +#define KC_EQL KC_EQUAL +#define KC_LBRC KC_LBRACKET +#define KC_RBRC KC_RBRACKET +#define KC_BSLS KC_BSLASH +#define KC_NUHS KC_NONUS_HASH +#define KC_SCLN KC_SCOLON +#define KC_QUOT KC_QUOTE +#define KC_GRV KC_GRAVE +#define KC_COMM KC_COMMA +#define KC_SLSH KC_SLASH +#define KC_NUBS KC_NONUS_BSLASH + +/* Lock Keys */ +#define KC_CLCK KC_CAPSLOCK +#define KC_CAPS KC_CAPSLOCK +#define KC_SLCK KC_SCROLLLOCK +#define KC_NLCK KC_NUMLOCK +#define KC_LCAP KC_LOCKING_CAPS +#define KC_LNUM KC_LOCKING_NUM +#define KC_LSCR KC_LOCKING_SCROLL + +/* Commands */ +#define KC_PSCR KC_PSCREEN +#define KC_PAUS KC_PAUSE +#define KC_BRK KC_PAUSE +#define KC_INS KC_INSERT +#define KC_DEL KC_DELETE +#define KC_PGDN KC_PGDOWN +#define KC_RGHT KC_RIGHT +#define KC_APP KC_APPLICATION +#define KC_EXEC KC_EXECUTE +#define KC_SLCT KC_SELECT +#define KC_AGIN KC_AGAIN +#define KC_PSTE KC_PASTE +#define KC_ERAS KC_ALT_ERASE +#define KC_CLR KC_CLEAR + +/* Keypad */ +#define KC_PSLS KC_KP_SLASH +#define KC_PAST KC_KP_ASTERISK +#define KC_PMNS KC_KP_MINUS +#define KC_PPLS KC_KP_PLUS +#define KC_PENT KC_KP_ENTER +#define KC_P1 KC_KP_1 +#define KC_P2 KC_KP_2 +#define KC_P3 KC_KP_3 +#define KC_P4 KC_KP_4 +#define KC_P5 KC_KP_5 +#define KC_P6 KC_KP_6 +#define KC_P7 KC_KP_7 +#define KC_P8 KC_KP_8 +#define KC_P9 KC_KP_9 +#define KC_P0 KC_KP_0 +#define KC_PDOT KC_KP_DOT +#define KC_PEQL KC_KP_EQUAL +#define KC_PCMM KC_KP_COMMA + +/* Japanese specific */ +#define KC_ZKHK KC_GRAVE +#define KC_RO KC_INT1 +#define KC_KANA KC_INT2 +#define KC_JYEN KC_INT3 +#define KC_HENK KC_INT4 +#define KC_MHEN KC_INT5 + +/* Korean specific */ +#define KC_HAEN KC_LANG1 +#define KC_HANJ KC_LANG2 + +/* Modifiers */ +#define KC_LCTL KC_LCTRL +#define KC_LSFT KC_LSHIFT +#define KC_LOPT KC_LALT +#define KC_LCMD KC_LGUI +#define KC_LWIN KC_LGUI +#define KC_RCTL KC_RCTRL +#define KC_RSFT KC_RSHIFT +#define KC_ALGR KC_RALT +#define KC_ROPT KC_RALT +#define KC_RCMD KC_RGUI +#define KC_RWIN KC_RGUI + +/* Generic Desktop Page (0x01) */ +#define KC_PWR KC_SYSTEM_POWER +#define KC_SLEP KC_SYSTEM_SLEEP +#define KC_WAKE KC_SYSTEM_WAKE + +/* Consumer Page (0x0C) */ +#define KC_MUTE KC_AUDIO_MUTE +#define KC_VOLU KC_AUDIO_VOL_UP +#define KC_VOLD KC_AUDIO_VOL_DOWN +#define KC_MNXT KC_MEDIA_NEXT_TRACK +#define KC_MPRV KC_MEDIA_PREV_TRACK +#define KC_MSTP KC_MEDIA_STOP +#define KC_MPLY KC_MEDIA_PLAY_PAUSE +#define KC_MSEL KC_MEDIA_SELECT +#define KC_EJCT KC_MEDIA_EJECT +#define KC_CALC KC_CALCULATOR +#define KC_MYCM KC_MY_COMPUTER +#define KC_WSCH KC_WWW_SEARCH +#define KC_WHOM KC_WWW_HOME +#define KC_WBAK KC_WWW_BACK +#define KC_WFWD KC_WWW_FORWARD +#define KC_WSTP KC_WWW_STOP +#define KC_WREF KC_WWW_REFRESH +#define KC_WFAV KC_WWW_FAVORITES +#define KC_MFFD KC_MEDIA_FAST_FORWARD +#define KC_MRWD KC_MEDIA_REWIND +#define KC_BRIU KC_BRIGHTNESS_UP +#define KC_BRID KC_BRIGHTNESS_DOWN + +/* System Specific */ +#define KC_BRMU KC_PAUSE +#define KC_BRMD KC_SCROLLLOCK + +/* Mouse Keys */ +#define KC_MS_U KC_MS_UP +#define KC_MS_D KC_MS_DOWN +#define KC_MS_L KC_MS_LEFT +#define KC_MS_R KC_MS_RIGHT +#define KC_BTN1 KC_MS_BTN1 +#define KC_BTN2 KC_MS_BTN2 +#define KC_BTN3 KC_MS_BTN3 +#define KC_BTN4 KC_MS_BTN4 +#define KC_BTN5 KC_MS_BTN5 +#define KC_BTN6 KC_MS_BTN6 +#define KC_BTN7 KC_MS_BTN7 +#define KC_BTN8 KC_MS_BTN8 +#define KC_WH_U KC_MS_WH_UP +#define KC_WH_D KC_MS_WH_DOWN +#define KC_WH_L KC_MS_WH_LEFT +#define KC_WH_R KC_MS_WH_RIGHT +#define KC_ACL0 KC_MS_ACCEL0 +#define KC_ACL1 KC_MS_ACCEL1 +#define KC_ACL2 KC_MS_ACCEL2 + +/* Keyboard/Keypad Page (0x07) */ +enum hid_keyboard_keypad_usage { + KC_NO = 0x00, + KC_ROLL_OVER, + KC_POST_FAIL, + KC_UNDEFINED, + KC_A, + KC_B, + KC_C, + KC_D, + KC_E, + KC_F, + KC_G, + KC_H, + KC_I, + KC_J, + KC_K, + KC_L, + KC_M, // 0x10 + KC_N, + KC_O, + KC_P, + KC_Q, + KC_R, + KC_S, + KC_T, + KC_U, + KC_V, + KC_W, + KC_X, + KC_Y, + KC_Z, + KC_1, + KC_2, + KC_3, // 0x20 + KC_4, + KC_5, + KC_6, + KC_7, + KC_8, + KC_9, + KC_0, + KC_ENTER, + KC_ESCAPE, + KC_BSPACE, + KC_TAB, + KC_SPACE, + KC_MINUS, + KC_EQUAL, + KC_LBRACKET, + KC_RBRACKET, // 0x30 + KC_BSLASH, + KC_NONUS_HASH, + KC_SCOLON, + KC_QUOTE, + KC_GRAVE, + KC_COMMA, + KC_DOT, + KC_SLASH, + KC_CAPSLOCK, + KC_F1, + KC_F2, + KC_F3, + KC_F4, + KC_F5, + KC_F6, + KC_F7, // 0x40 + KC_F8, + KC_F9, + KC_F10, + KC_F11, + KC_F12, + KC_PSCREEN, + KC_SCROLLLOCK, + KC_PAUSE, + KC_INSERT, + KC_HOME, + KC_PGUP, + KC_DELETE, + KC_END, + KC_PGDOWN, + KC_RIGHT, + KC_LEFT, // 0x50 + KC_DOWN, + KC_UP, + KC_NUMLOCK, + KC_KP_SLASH, + KC_KP_ASTERISK, + KC_KP_MINUS, + KC_KP_PLUS, + KC_KP_ENTER, + KC_KP_1, + KC_KP_2, + KC_KP_3, + KC_KP_4, + KC_KP_5, + KC_KP_6, + KC_KP_7, + KC_KP_8, // 0x60 + KC_KP_9, + KC_KP_0, + KC_KP_DOT, + KC_NONUS_BSLASH, + KC_APPLICATION, + KC_POWER, + KC_KP_EQUAL, + KC_F13, + KC_F14, + KC_F15, + KC_F16, + KC_F17, + KC_F18, + KC_F19, + KC_F20, + KC_F21, // 0x70 + KC_F22, + KC_F23, + KC_F24, + KC_EXECUTE, + KC_HELP, + KC_MENU, + KC_SELECT, + KC_STOP, + KC_AGAIN, + KC_UNDO, + KC_CUT, + KC_COPY, + KC_PASTE, + KC_FIND, + KC__MUTE, + KC__VOLUP, // 0x80 + KC__VOLDOWN, + KC_LOCKING_CAPS, + KC_LOCKING_NUM, + KC_LOCKING_SCROLL, + KC_KP_COMMA, + KC_KP_EQUAL_AS400, + KC_INT1, + KC_INT2, + KC_INT3, + KC_INT4, + KC_INT5, + KC_INT6, + KC_INT7, + KC_INT8, + KC_INT9, + KC_LANG1, // 0x90 + KC_LANG2, + KC_LANG3, + KC_LANG4, + KC_LANG5, + KC_LANG6, + KC_LANG7, + KC_LANG8, + KC_LANG9, + KC_ALT_ERASE, + KC_SYSREQ, + KC_CANCEL, + KC_CLEAR, + KC_PRIOR, + KC_RETURN, + KC_SEPARATOR, + KC_OUT, // 0xA0 + KC_OPER, + KC_CLEAR_AGAIN, + KC_CRSEL, + KC_EXSEL, + +#if 0 + // *************************************************************** + // These keycodes are present in the HID spec, but are * + // nonfunctional on modern OSes. QMK uses this range (0xA5-0xDF) * + // for the media and function keys instead - see below. * + // *************************************************************** + + KC_KP_00 = 0xB0, + KC_KP_000, + KC_THOUSANDS_SEPARATOR, + KC_DECIMAL_SEPARATOR, + KC_CURRENCY_UNIT, + KC_CURRENCY_SUB_UNIT, + KC_KP_LPAREN, + KC_KP_RPAREN, + KC_KP_LCBRACKET, + KC_KP_RCBRACKET, + KC_KP_TAB, + KC_KP_BSPACE, + KC_KP_A, + KC_KP_B, + KC_KP_C, + KC_KP_D, + KC_KP_E, //0xC0 + KC_KP_F, + KC_KP_XOR, + KC_KP_HAT, + KC_KP_PERC, + KC_KP_LT, + KC_KP_GT, + KC_KP_AND, + KC_KP_LAZYAND, + KC_KP_OR, + KC_KP_LAZYOR, + KC_KP_COLON, + KC_KP_HASH, + KC_KP_SPACE, + KC_KP_ATMARK, + KC_KP_EXCLAMATION, + KC_KP_MEM_STORE, //0xD0 + KC_KP_MEM_RECALL, + KC_KP_MEM_CLEAR, + KC_KP_MEM_ADD, + KC_KP_MEM_SUB, + KC_KP_MEM_MUL, + KC_KP_MEM_DIV, + KC_KP_PLUS_MINUS, + KC_KP_CLEAR, + KC_KP_CLEAR_ENTRY, + KC_KP_BINARY, + KC_KP_OCTAL, + KC_KP_DECIMAL, + KC_KP_HEXADECIMAL, +#endif + + /* Modifiers */ + KC_LCTRL = 0xE0, + KC_LSHIFT, + KC_LALT, + KC_LGUI, + KC_RCTRL, + KC_RSHIFT, + KC_RALT, + KC_RGUI + + // ********************************************** + // * 0xF0-0xFF are unallocated in the HID spec. * + // * QMK uses these for Mouse Keys - see below. * + // ********************************************** +}; + +/* Media and Function keys */ +enum internal_special_keycodes { + /* Generic Desktop Page (0x01) */ + KC_SYSTEM_POWER = 0xA5, + KC_SYSTEM_SLEEP, + KC_SYSTEM_WAKE, + + /* Consumer Page (0x0C) */ + KC_AUDIO_MUTE, + KC_AUDIO_VOL_UP, + KC_AUDIO_VOL_DOWN, + KC_MEDIA_NEXT_TRACK, + KC_MEDIA_PREV_TRACK, + KC_MEDIA_STOP, + KC_MEDIA_PLAY_PAUSE, + KC_MEDIA_SELECT, + KC_MEDIA_EJECT, // 0xB0 + KC_MAIL, + KC_CALCULATOR, + KC_MY_COMPUTER, + KC_WWW_SEARCH, + KC_WWW_HOME, + KC_WWW_BACK, + KC_WWW_FORWARD, + KC_WWW_STOP, + KC_WWW_REFRESH, + KC_WWW_FAVORITES, + KC_MEDIA_FAST_FORWARD, + KC_MEDIA_REWIND, + KC_BRIGHTNESS_UP, + KC_BRIGHTNESS_DOWN, + + /* Fn keys */ + KC_FN0 = 0xC0, + KC_FN1, + KC_FN2, + KC_FN3, + KC_FN4, + KC_FN5, + KC_FN6, + KC_FN7, + KC_FN8, + KC_FN9, + KC_FN10, + KC_FN11, + KC_FN12, + KC_FN13, + KC_FN14, + KC_FN15, + KC_FN16, // 0xD0 + KC_FN17, + KC_FN18, + KC_FN19, + KC_FN20, + KC_FN21, + KC_FN22, + KC_FN23, + KC_FN24, + KC_FN25, + KC_FN26, + KC_FN27, + KC_FN28, + KC_FN29, + KC_FN30, + KC_FN31 +}; + +enum mouse_keys { +/* Mouse Buttons */ +#ifdef VIA_ENABLE + KC_MS_UP = 0xF0, +#else + KC_MS_UP = 0xED, +#endif + KC_MS_DOWN, + KC_MS_LEFT, + KC_MS_RIGHT, // 0xF0 + KC_MS_BTN1, + KC_MS_BTN2, + KC_MS_BTN3, + KC_MS_BTN4, + KC_MS_BTN5, +#ifdef VIA_ENABLE + KC_MS_BTN6 = KC_MS_BTN5, + KC_MS_BTN7 = KC_MS_BTN5, + KC_MS_BTN8 = KC_MS_BTN5, +#else + KC_MS_BTN6, + KC_MS_BTN7, + KC_MS_BTN8, +#endif + + /* Mouse Wheel */ + KC_MS_WH_UP, + KC_MS_WH_DOWN, + KC_MS_WH_LEFT, + KC_MS_WH_RIGHT, + + /* Acceleration */ + KC_MS_ACCEL0, + KC_MS_ACCEL1, + KC_MS_ACCEL2 // 0xFF +}; diff --git a/quantum/via.h b/quantum/via.h index d0510fcabd..de0a21da20 100644 --- a/quantum/via.h +++ b/quantum/via.h @@ -16,7 +16,7 @@ #pragma once -#include "tmk_core/common/eeconfig.h" // for EECONFIG_SIZE +#include "eeconfig.h" // for EECONFIG_SIZE // Keyboard level code can change where VIA stores the magic. // The magic is the build date YYMMDD encoded as BCD in 3 bytes, diff --git a/tmk_core/common.mk b/tmk_core/common.mk index 57de8d95e8..69d27e6c0b 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -1,14 +1,8 @@ COMMON_DIR = common PLATFORM_COMMON_DIR = $(COMMON_DIR)/$(PLATFORM_KEY) -TMK_COMMON_SRC += $(COMMON_DIR)/host.c \ - $(COMMON_DIR)/keyboard.c \ - $(COMMON_DIR)/action.c \ - $(COMMON_DIR)/action_tapping.c \ - $(COMMON_DIR)/action_macro.c \ - $(COMMON_DIR)/action_layer.c \ - $(COMMON_DIR)/action_util.c \ - $(COMMON_DIR)/eeconfig.c \ +TMK_COMMON_SRC += \ + $(COMMON_DIR)/host.c \ $(COMMON_DIR)/report.c \ $(COMMON_DIR)/sync_timer.c \ $(COMMON_DIR)/usb_util.c \ diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c deleted file mode 100644 index d19fd2a045..0000000000 --- a/tmk_core/common/action.c +++ /dev/null @@ -1,1126 +0,0 @@ -/* -Copyright 2012,2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "host.h" -#include "keycode.h" -#include "keyboard.h" -#include "mousekey.h" -#include "command.h" -#include "led.h" -#include "action_layer.h" -#include "action_tapping.h" -#include "action_macro.h" -#include "action_util.h" -#include "action.h" -#include "wait.h" - -#ifdef BACKLIGHT_ENABLE -# include "backlight.h" -#endif - -#ifdef DEBUG_ACTION -# include "debug.h" -#else -# include "nodebug.h" -#endif - -#ifdef POINTING_DEVICE_ENABLE -# include "pointing_device.h" -#endif - -int tp_buttons; - -#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) -int retro_tapping_counter = 0; -#endif - -#ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY -__attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; } -#endif - -#ifdef RETRO_TAPPING_PER_KEY -__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; } -#endif - -__attribute__((weak)) bool pre_process_record_quantum(keyrecord_t *record) { return true; } - -#ifndef TAP_CODE_DELAY -# define TAP_CODE_DELAY 0 -#endif -#ifndef TAP_HOLD_CAPS_DELAY -# define TAP_HOLD_CAPS_DELAY 80 -#endif -/** \brief Called to execute an action. - * - * FIXME: Needs documentation. - */ -void action_exec(keyevent_t event) { - if (!IS_NOEVENT(event)) { - dprint("\n---- action_exec: start -----\n"); - dprint("EVENT: "); - debug_event(event); - dprintln(); -#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) - retro_tapping_counter++; -#endif - } - - if (event.pressed) { - // clear the potential weak mods left by previously pressed keys - clear_weak_mods(); - } - -#ifdef SWAP_HANDS_ENABLE - if (!IS_NOEVENT(event)) { - process_hand_swap(&event); - } -#endif - - keyrecord_t record = {.event = event}; - -#ifndef NO_ACTION_ONESHOT -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - if (has_oneshot_layer_timed_out()) { - clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); - } - if (has_oneshot_mods_timed_out()) { - clear_oneshot_mods(); - } -# ifdef SWAP_HANDS_ENABLE - if (has_oneshot_swaphands_timed_out()) { - clear_oneshot_swaphands(); - } -# endif -# endif -#endif - -#ifndef NO_ACTION_TAPPING - if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { - action_tapping_process(record); - } -#else - if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { - process_record(&record); - } - if (!IS_NOEVENT(record.event)) { - dprint("processed: "); - debug_record(record); - dprintln(); - } -#endif -} - -#ifdef SWAP_HANDS_ENABLE -bool swap_hands = false; -bool swap_held = false; - -/** \brief Process Hand Swap - * - * FIXME: Needs documentation. - */ -void process_hand_swap(keyevent_t *event) { - static swap_state_row_t swap_state[MATRIX_ROWS]; - - keypos_t pos = event->key; - swap_state_row_t col_bit = (swap_state_row_t)1 << pos.col; - bool do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit); - - if (do_swap) { - event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row); - event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col); - swap_state[pos.row] |= col_bit; - } else { - swap_state[pos.row] &= ~(col_bit); - } -} -#endif - -#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) -bool disable_action_cache = false; - -void process_record_nocache(keyrecord_t *record) { - disable_action_cache = true; - process_record(record); - disable_action_cache = false; -} -#else -void process_record_nocache(keyrecord_t *record) { process_record(record); } -#endif - -__attribute__((weak)) bool process_record_quantum(keyrecord_t *record) { return true; } - -__attribute__((weak)) void post_process_record_quantum(keyrecord_t *record) {} - -#ifndef NO_ACTION_TAPPING -/** \brief Allows for handling tap-hold actions immediately instead of waiting for TAPPING_TERM or another keypress. - * - * FIXME: Needs documentation. - */ -void process_record_tap_hint(keyrecord_t *record) { - action_t action = layer_switch_get_action(record->event.key); - - switch (action.kind.id) { -# ifdef SWAP_HANDS_ENABLE - case ACT_SWAP_HANDS: - switch (action.swap.code) { - case OP_SH_ONESHOT: - break; - case OP_SH_TAP_TOGGLE: - default: - swap_hands = !swap_hands; - swap_held = true; - } - break; -# endif - } -} -#endif - -/** \brief Take a key event (key press or key release) and processes it. - * - * FIXME: Needs documentation. - */ -void process_record(keyrecord_t *record) { - if (IS_NOEVENT(record->event)) { - return; - } - - if (!process_record_quantum(record)) { -#ifndef NO_ACTION_ONESHOT - if (is_oneshot_layer_active() && record->event.pressed) { - clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); - } -#endif - return; - } - - process_record_handler(record); - post_process_record_quantum(record); -} - -void process_record_handler(keyrecord_t *record) { -#ifdef COMBO_ENABLE - action_t action; - if (record->keycode) { - action = action_for_keycode(record->keycode); - } else { - action = store_or_get_action(record->event.pressed, record->event.key); - } -#else - action_t action = store_or_get_action(record->event.pressed, record->event.key); -#endif - dprint("ACTION: "); - debug_action(action); -#ifndef NO_ACTION_LAYER - dprint(" layer_state: "); - layer_debug(); - dprint(" default_layer_state: "); - default_layer_debug(); -#endif - dprintln(); - - process_action(record, action); -} - -#if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) -void register_button(bool pressed, enum mouse_buttons button) { -# ifdef PS2_MOUSE_ENABLE - tp_buttons = pressed ? tp_buttons | button : tp_buttons & ~button; -# endif -# ifdef POINTING_DEVICE_ENABLE - report_mouse_t currentReport = pointing_device_get_report(); - currentReport.buttons = pressed ? currentReport.buttons | button : currentReport.buttons & ~button; - pointing_device_set_report(currentReport); -# endif -} -#endif - -/** \brief Take an action and processes it. - * - * FIXME: Needs documentation. - */ -void process_action(keyrecord_t *record, action_t action) { - keyevent_t event = record->event; -#ifndef NO_ACTION_TAPPING - uint8_t tap_count = record->tap.count; -#endif - -#ifndef NO_ACTION_ONESHOT - bool do_release_oneshot = false; - // notice we only clear the one shot layer if the pressed key is not a modifier. - if (is_oneshot_layer_active() && event.pressed && (action.kind.id == ACT_USAGE || !IS_MOD(action.key.code)) -# ifdef SWAP_HANDS_ENABLE - && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT) -# endif - ) { - clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); - do_release_oneshot = !is_oneshot_layer_active(); - } -#endif - - switch (action.kind.id) { - /* Key and Mods */ - case ACT_LMODS: - case ACT_RMODS: { - uint8_t mods = (action.kind.id == ACT_LMODS) ? action.key.mods : action.key.mods << 4; - if (event.pressed) { - if (mods) { - if (IS_MOD(action.key.code) || action.key.code == KC_NO) { - // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless. - // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT). - // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO). - add_mods(mods); - } else { - add_weak_mods(mods); - } - send_keyboard_report(); - } - register_code(action.key.code); - } else { - unregister_code(action.key.code); - if (mods) { - if (IS_MOD(action.key.code) || action.key.code == KC_NO) { - del_mods(mods); - } else { - del_weak_mods(mods); - } - send_keyboard_report(); - } - } - } break; -#ifndef NO_ACTION_TAPPING - case ACT_LMODS_TAP: - case ACT_RMODS_TAP: { - uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ? action.key.mods : action.key.mods << 4; - switch (action.layer_tap.code) { -# ifndef NO_ACTION_ONESHOT - case MODS_ONESHOT: - // Oneshot modifier - if (event.pressed) { - if (tap_count == 0) { - dprint("MODS_TAP: Oneshot: 0\n"); - register_mods(mods | get_oneshot_mods()); - } else if (tap_count == 1) { - dprint("MODS_TAP: Oneshot: start\n"); - set_oneshot_mods(mods | get_oneshot_mods()); -# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 - } else if (tap_count == ONESHOT_TAP_TOGGLE) { - dprint("MODS_TAP: Toggling oneshot"); - clear_oneshot_mods(); - set_oneshot_locked_mods(mods); - register_mods(mods); -# endif - } else { - register_mods(mods | get_oneshot_mods()); - } - } else { - if (tap_count == 0) { - clear_oneshot_mods(); - unregister_mods(mods); - } else if (tap_count == 1) { - // Retain Oneshot mods -# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 - if (mods & get_mods()) { - clear_oneshot_locked_mods(); - clear_oneshot_mods(); - unregister_mods(mods); - } - } else if (tap_count == ONESHOT_TAP_TOGGLE) { - // Toggle Oneshot Layer -# endif - } else { - clear_oneshot_mods(); - unregister_mods(mods); - } - } - break; -# endif - case MODS_TAP_TOGGLE: - if (event.pressed) { - if (tap_count <= TAPPING_TOGGLE) { - register_mods(mods); - } - } else { - if (tap_count < TAPPING_TOGGLE) { - unregister_mods(mods); - } - } - break; - default: - if (event.pressed) { - if (tap_count > 0) { -# if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY) - if ( -# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY - !get_ignore_mod_tap_interrupt(get_event_keycode(record->event, false), record) && -# endif - record->tap.interrupted) { - dprint("mods_tap: tap: cancel: add_mods\n"); - // ad hoc: set 0 to cancel tap - record->tap.count = 0; - register_mods(mods); - } else -# endif - { - dprint("MODS_TAP: Tap: register_code\n"); - register_code(action.key.code); - } - } else { - dprint("MODS_TAP: No tap: add_mods\n"); - register_mods(mods); - } - } else { - if (tap_count > 0) { - dprint("MODS_TAP: Tap: unregister_code\n"); - if (action.layer_tap.code == KC_CAPS) { - wait_ms(TAP_HOLD_CAPS_DELAY); - } else { - wait_ms(TAP_CODE_DELAY); - } - unregister_code(action.key.code); - } else { - dprint("MODS_TAP: No tap: add_mods\n"); - unregister_mods(mods); - } - } - break; - } - } break; -#endif -#ifdef EXTRAKEY_ENABLE - /* other HID usage */ - case ACT_USAGE: - switch (action.usage.page) { - case PAGE_SYSTEM: - if (event.pressed) { - host_system_send(action.usage.code); - } else { - host_system_send(0); - } - break; - case PAGE_CONSUMER: - if (event.pressed) { - host_consumer_send(action.usage.code); - } else { - host_consumer_send(0); - } - break; - } - break; -#endif -#ifdef MOUSEKEY_ENABLE - /* Mouse key */ - case ACT_MOUSEKEY: - if (event.pressed) { - mousekey_on(action.key.code); - } else { - mousekey_off(action.key.code); - } - switch (action.key.code) { -# if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) -# ifdef POINTING_DEVICE_ENABLE - case KC_MS_BTN1 ... KC_MS_BTN8: -# else - case KC_MS_BTN1 ... KC_MS_BTN3: -# endif - register_button(event.pressed, MOUSE_BTN_MASK(action.key.code - KC_MS_BTN1)); - break; -# endif - default: - mousekey_send(); - break; - } - break; -#endif -#ifndef NO_ACTION_LAYER - case ACT_LAYER: - if (action.layer_bitop.on == 0) { - /* Default Layer Bitwise Operation */ - if (!event.pressed) { - uint8_t shift = action.layer_bitop.part * 4; - layer_state_t bits = ((layer_state_t)action.layer_bitop.bits) << shift; - layer_state_t mask = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0; - switch (action.layer_bitop.op) { - case OP_BIT_AND: - default_layer_and(bits | mask); - break; - case OP_BIT_OR: - default_layer_or(bits | mask); - break; - case OP_BIT_XOR: - default_layer_xor(bits | mask); - break; - case OP_BIT_SET: - default_layer_set(bits | mask); - break; - } - } - } else { - /* Layer Bitwise Operation */ - if (event.pressed ? (action.layer_bitop.on & ON_PRESS) : (action.layer_bitop.on & ON_RELEASE)) { - uint8_t shift = action.layer_bitop.part * 4; - layer_state_t bits = ((layer_state_t)action.layer_bitop.bits) << shift; - layer_state_t mask = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0; - switch (action.layer_bitop.op) { - case OP_BIT_AND: - layer_and(bits | mask); - break; - case OP_BIT_OR: - layer_or(bits | mask); - break; - case OP_BIT_XOR: - layer_xor(bits | mask); - break; - case OP_BIT_SET: - layer_state_set(bits | mask); - break; - } - } - } - break; - case ACT_LAYER_MODS: - if (event.pressed) { - layer_on(action.layer_mods.layer); - register_mods(action.layer_mods.mods); - } else { - unregister_mods(action.layer_mods.mods); - layer_off(action.layer_mods.layer); - } - break; -# ifndef NO_ACTION_TAPPING - case ACT_LAYER_TAP: - case ACT_LAYER_TAP_EXT: - switch (action.layer_tap.code) { - case OP_TAP_TOGGLE: - /* tap toggle */ - if (event.pressed) { - if (tap_count < TAPPING_TOGGLE) { - layer_invert(action.layer_tap.val); - } - } else { - if (tap_count <= TAPPING_TOGGLE) { - layer_invert(action.layer_tap.val); - } - } - break; - case OP_ON_OFF: - event.pressed ? layer_on(action.layer_tap.val) : layer_off(action.layer_tap.val); - break; - case OP_OFF_ON: - event.pressed ? layer_off(action.layer_tap.val) : layer_on(action.layer_tap.val); - break; - case OP_SET_CLEAR: - event.pressed ? layer_move(action.layer_tap.val) : layer_clear(); - break; -# ifndef NO_ACTION_ONESHOT - case OP_ONESHOT: - // Oneshot modifier -# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 - do_release_oneshot = false; - if (event.pressed) { - del_mods(get_oneshot_locked_mods()); - if (get_oneshot_layer_state() == ONESHOT_TOGGLED) { - reset_oneshot_layer(); - layer_off(action.layer_tap.val); - break; - } else if (tap_count < ONESHOT_TAP_TOGGLE) { - layer_on(action.layer_tap.val); - set_oneshot_layer(action.layer_tap.val, ONESHOT_START); - } - } else { - add_mods(get_oneshot_locked_mods()); - if (tap_count >= ONESHOT_TAP_TOGGLE) { - reset_oneshot_layer(); - clear_oneshot_locked_mods(); - set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED); - } else { - clear_oneshot_layer_state(ONESHOT_PRESSED); - } - } -# else - if (event.pressed) { - layer_on(action.layer_tap.val); - set_oneshot_layer(action.layer_tap.val, ONESHOT_START); - } else { - clear_oneshot_layer_state(ONESHOT_PRESSED); - if (tap_count > 1) { - clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); - } - } -# endif - break; -# endif - default: - /* tap key */ - if (event.pressed) { - if (tap_count > 0) { - dprint("KEYMAP_TAP_KEY: Tap: register_code\n"); - register_code(action.layer_tap.code); - } else { - dprint("KEYMAP_TAP_KEY: No tap: On on press\n"); - layer_on(action.layer_tap.val); - } - } else { - if (tap_count > 0) { - dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n"); - if (action.layer_tap.code == KC_CAPS) { - wait_ms(TAP_HOLD_CAPS_DELAY); - } else { - wait_ms(TAP_CODE_DELAY); - } - unregister_code(action.layer_tap.code); - } else { - dprint("KEYMAP_TAP_KEY: No tap: Off on release\n"); - layer_off(action.layer_tap.val); - } - } - break; - } - break; -# endif -#endif - /* Extentions */ -#ifndef NO_ACTION_MACRO - case ACT_MACRO: - action_macro_play(action_get_macro(record, action.func.id, action.func.opt)); - break; -#endif -#ifdef SWAP_HANDS_ENABLE - case ACT_SWAP_HANDS: - switch (action.swap.code) { - case OP_SH_TOGGLE: - if (event.pressed) { - swap_hands = !swap_hands; - } - break; - case OP_SH_ON_OFF: - swap_hands = event.pressed; - break; - case OP_SH_OFF_ON: - swap_hands = !event.pressed; - break; - case OP_SH_ON: - if (!event.pressed) { - swap_hands = true; - } - break; - case OP_SH_OFF: - if (!event.pressed) { - swap_hands = false; - } - break; -# ifndef NO_ACTION_ONESHOT - case OP_SH_ONESHOT: - if (event.pressed) { - set_oneshot_swaphands(); - } else { - release_oneshot_swaphands(); - } - break; -# endif - -# ifndef NO_ACTION_TAPPING - case OP_SH_TAP_TOGGLE: - /* tap toggle */ - - if (event.pressed) { - if (swap_held) { - swap_held = false; - } else { - swap_hands = !swap_hands; - } - } else { - if (tap_count < TAPPING_TOGGLE) { - swap_hands = !swap_hands; - } - } - break; - default: - /* tap key */ - if (tap_count > 0) { - if (swap_held) { - swap_hands = !swap_hands; // undo hold set up in _tap_hint - swap_held = false; - } - if (event.pressed) { - register_code(action.swap.code); - } else { - wait_ms(TAP_CODE_DELAY); - unregister_code(action.swap.code); - *record = (keyrecord_t){}; // hack: reset tap mode - } - } else { - if (swap_held && !event.pressed) { - swap_hands = !swap_hands; // undo hold set up in _tap_hint - swap_held = false; - } - } -# endif - } -#endif -#ifndef NO_ACTION_FUNCTION - case ACT_FUNCTION: - action_function(record, action.func.id, action.func.opt); - break; -#endif - default: - break; - } - -#ifndef NO_ACTION_LAYER - // if this event is a layer action, update the leds - switch (action.kind.id) { - case ACT_LAYER: - case ACT_LAYER_MODS: -# ifndef NO_ACTION_TAPPING - case ACT_LAYER_TAP: - case ACT_LAYER_TAP_EXT: -# endif - led_set(host_keyboard_leds()); - break; - default: - break; - } -#endif - -#ifndef NO_ACTION_TAPPING -# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) - if (!is_tap_action(action)) { - retro_tapping_counter = 0; - } else { - if (event.pressed) { - if (tap_count > 0) { - retro_tapping_counter = 0; - } - } else { - if (tap_count > 0) { - retro_tapping_counter = 0; - } else { - if ( -# ifdef RETRO_TAPPING_PER_KEY - get_retro_tapping(get_event_keycode(record->event, false), record) && -# endif - retro_tapping_counter == 2) { - tap_code(action.layer_tap.code); - } - retro_tapping_counter = 0; - } - } - } -# endif -#endif - -#ifdef SWAP_HANDS_ENABLE -# ifndef NO_ACTION_ONESHOT - if (event.pressed && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)) { - use_oneshot_swaphands(); - } -# endif -#endif - -#ifndef NO_ACTION_ONESHOT - /* Because we switch layers after a oneshot event, we need to release the - * key before we leave the layer or no key up event will be generated. - */ - if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED)) { - record->event.pressed = false; - layer_on(get_oneshot_layer()); - process_record(record); - layer_off(get_oneshot_layer()); - } -#endif -} - -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -void register_code(uint8_t code) { - if (code == KC_NO) { - return; - } -#ifdef LOCKING_SUPPORT_ENABLE - else if (KC_LOCKING_CAPS == code) { -# ifdef LOCKING_RESYNC_ENABLE - // Resync: ignore if caps lock already is on - if (host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK)) return; -# endif - add_key(KC_CAPSLOCK); - send_keyboard_report(); - wait_ms(100); - del_key(KC_CAPSLOCK); - send_keyboard_report(); - } - - else if (KC_LOCKING_NUM == code) { -# ifdef LOCKING_RESYNC_ENABLE - if (host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)) return; -# endif - add_key(KC_NUMLOCK); - send_keyboard_report(); - wait_ms(100); - del_key(KC_NUMLOCK); - send_keyboard_report(); - } - - else if (KC_LOCKING_SCROLL == code) { -# ifdef LOCKING_RESYNC_ENABLE - if (host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK)) return; -# endif - add_key(KC_SCROLLLOCK); - send_keyboard_report(); - wait_ms(100); - del_key(KC_SCROLLLOCK); - send_keyboard_report(); - } -#endif - - else if IS_KEY (code) { - // TODO: should push command_proc out of this block? - if (command_proc(code)) return; - -#ifndef NO_ACTION_ONESHOT -/* TODO: remove - if (oneshot_state.mods && !oneshot_state.disabled) { - uint8_t tmp_mods = get_mods(); - add_mods(oneshot_state.mods); - - add_key(code); - send_keyboard_report(); - - set_mods(tmp_mods); - send_keyboard_report(); - oneshot_cancel(); - } else -*/ -#endif - { - // Force a new key press if the key is already pressed - // without this, keys with the same keycode, but different - // modifiers will be reported incorrectly, see issue #1708 - if (is_key_pressed(keyboard_report, code)) { - del_key(code); - send_keyboard_report(); - } - add_key(code); - send_keyboard_report(); - } - } else if IS_MOD (code) { - add_mods(MOD_BIT(code)); - send_keyboard_report(); - } -#ifdef EXTRAKEY_ENABLE - else if IS_SYSTEM (code) { - host_system_send(KEYCODE2SYSTEM(code)); - } else if IS_CONSUMER (code) { - host_consumer_send(KEYCODE2CONSUMER(code)); - } -#endif -#ifdef MOUSEKEY_ENABLE - else if IS_MOUSEKEY (code) { - mousekey_on(code); - mousekey_send(); - } -#endif -} - -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -void unregister_code(uint8_t code) { - if (code == KC_NO) { - return; - } -#ifdef LOCKING_SUPPORT_ENABLE - else if (KC_LOCKING_CAPS == code) { -# ifdef LOCKING_RESYNC_ENABLE - // Resync: ignore if caps lock already is off - if (!(host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK))) return; -# endif - add_key(KC_CAPSLOCK); - send_keyboard_report(); - del_key(KC_CAPSLOCK); - send_keyboard_report(); - } - - else if (KC_LOCKING_NUM == code) { -# ifdef LOCKING_RESYNC_ENABLE - if (!(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) return; -# endif - add_key(KC_NUMLOCK); - send_keyboard_report(); - del_key(KC_NUMLOCK); - send_keyboard_report(); - } - - else if (KC_LOCKING_SCROLL == code) { -# ifdef LOCKING_RESYNC_ENABLE - if (!(host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK))) return; -# endif - add_key(KC_SCROLLLOCK); - send_keyboard_report(); - del_key(KC_SCROLLLOCK); - send_keyboard_report(); - } -#endif - - else if IS_KEY (code) { - del_key(code); - send_keyboard_report(); - } else if IS_MOD (code) { - del_mods(MOD_BIT(code)); - send_keyboard_report(); - } else if IS_SYSTEM (code) { - host_system_send(0); - } else if IS_CONSUMER (code) { - host_consumer_send(0); - } -#ifdef MOUSEKEY_ENABLE - else if IS_MOUSEKEY (code) { - mousekey_off(code); - mousekey_send(); - } -#endif -} - -/** \brief Tap a keycode with a delay. - * - * \param code The basic keycode to tap. - * \param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it. - */ -void tap_code_delay(uint8_t code, uint16_t delay) { - register_code(code); - for (uint16_t i = delay; i > 0; i--) { - wait_ms(1); - } - unregister_code(code); -} - -/** \brief Tap a keycode with the default delay. - * - * \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined. - */ -void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); } - -/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately. - * - * \param mods A bitfield of modifiers to register. - */ -void register_mods(uint8_t mods) { - if (mods) { - add_mods(mods); - send_keyboard_report(); - } -} - -/** \brief Removes the given physically pressed modifiers and sends a keyboard report immediately. - * - * \param mods A bitfield of modifiers to unregister. - */ -void unregister_mods(uint8_t mods) { - if (mods) { - del_mods(mods); - send_keyboard_report(); - } -} - -/** \brief Adds the given weak modifiers and sends a keyboard report immediately. - * - * \param mods A bitfield of modifiers to register. - */ -void register_weak_mods(uint8_t mods) { - if (mods) { - add_weak_mods(mods); - send_keyboard_report(); - } -} - -/** \brief Removes the given weak modifiers and sends a keyboard report immediately. - * - * \param mods A bitfield of modifiers to unregister. - */ -void unregister_weak_mods(uint8_t mods) { - if (mods) { - del_weak_mods(mods); - send_keyboard_report(); - } -} - -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -void clear_keyboard(void) { - clear_mods(); - clear_keyboard_but_mods(); -} - -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -void clear_keyboard_but_mods(void) { - clear_keys(); - clear_keyboard_but_mods_and_keys(); -} - -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -void clear_keyboard_but_mods_and_keys() { -#ifdef EXTRAKEY_ENABLE - host_system_send(0); - host_consumer_send(0); -#endif - clear_weak_mods(); - clear_macro_mods(); - send_keyboard_report(); -#ifdef MOUSEKEY_ENABLE - mousekey_clear(); - mousekey_send(); -#endif -} - -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -bool is_tap_key(keypos_t key) { - action_t action = layer_switch_get_action(key); - return is_tap_action(action); -} - -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -bool is_tap_record(keyrecord_t *record) { -#ifdef COMBO_ENABLE - action_t action; - if (record->keycode) { - action = action_for_keycode(record->keycode); - } else { - action = layer_switch_get_action(record->event.key); - } -#else - action_t action = layer_switch_get_action(record->event.key); -#endif - return is_tap_action(action); -} - -/** \brief Utilities for actions. (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -bool is_tap_action(action_t action) { - switch (action.kind.id) { - case ACT_LMODS_TAP: - case ACT_RMODS_TAP: - case ACT_LAYER_TAP: - case ACT_LAYER_TAP_EXT: - switch (action.layer_tap.code) { - case KC_NO ... KC_RGUI: - case OP_TAP_TOGGLE: - case OP_ONESHOT: - return true; - } - return false; - case ACT_SWAP_HANDS: - switch (action.swap.code) { - case KC_NO ... KC_RGUI: - case OP_SH_TAP_TOGGLE: - return true; - } - return false; - case ACT_MACRO: - case ACT_FUNCTION: - if (action.func.opt & FUNC_TAP) { - return true; - } - return false; - } - return false; -} - -/** \brief Debug print (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -void debug_event(keyevent_t event) { dprintf("%04X%c(%u)", (event.key.row << 8 | event.key.col), (event.pressed ? 'd' : 'u'), event.time); } -/** \brief Debug print (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -void debug_record(keyrecord_t record) { - debug_event(record.event); -#ifndef NO_ACTION_TAPPING - dprintf(":%u%c", record.tap.count, (record.tap.interrupted ? '-' : ' ')); -#endif -} - -/** \brief Debug print (FIXME: Needs better description) - * - * FIXME: Needs documentation. - */ -void debug_action(action_t action) { - switch (action.kind.id) { - case ACT_LMODS: - dprint("ACT_LMODS"); - break; - case ACT_RMODS: - dprint("ACT_RMODS"); - break; - case ACT_LMODS_TAP: - dprint("ACT_LMODS_TAP"); - break; - case ACT_RMODS_TAP: - dprint("ACT_RMODS_TAP"); - break; - case ACT_USAGE: - dprint("ACT_USAGE"); - break; - case ACT_MOUSEKEY: - dprint("ACT_MOUSEKEY"); - break; - case ACT_LAYER: - dprint("ACT_LAYER"); - break; - case ACT_LAYER_MODS: - dprint("ACT_LAYER_MODS"); - break; - case ACT_LAYER_TAP: - dprint("ACT_LAYER_TAP"); - break; - case ACT_LAYER_TAP_EXT: - dprint("ACT_LAYER_TAP_EXT"); - break; - case ACT_MACRO: - dprint("ACT_MACRO"); - break; - case ACT_FUNCTION: - dprint("ACT_FUNCTION"); - break; - case ACT_SWAP_HANDS: - dprint("ACT_SWAP_HANDS"); - break; - default: - dprint("UNKNOWN"); - break; - } - dprintf("[%X:%02X]", action.kind.param >> 8, action.kind.param & 0xff); -} diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h deleted file mode 100644 index 3d357b33b8..0000000000 --- a/tmk_core/common/action.h +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright 2012,2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include -#include -#include "keyboard.h" -#include "keycode.h" -#include "action_code.h" -#include "action_macro.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Disable macro and function features when LTO is enabled, since they break */ -#ifdef LTO_ENABLE -# ifndef NO_ACTION_MACRO -# define NO_ACTION_MACRO -# endif -# ifndef NO_ACTION_FUNCTION -# define NO_ACTION_FUNCTION -# endif -#endif - -/* tapping count and state */ -typedef struct { - bool interrupted : 1; - bool reserved2 : 1; - bool reserved1 : 1; - bool reserved0 : 1; - uint8_t count : 4; -} tap_t; - -/* Key event container for recording */ -typedef struct { - keyevent_t event; -#ifndef NO_ACTION_TAPPING - tap_t tap; -#endif -#ifdef COMBO_ENABLE - uint16_t keycode; -#endif -} keyrecord_t; - -/* Execute action per keyevent */ -void action_exec(keyevent_t event); - -/* action for key */ -action_t action_for_key(uint8_t layer, keypos_t key); -action_t action_for_keycode(uint16_t keycode); - -/* macro */ -const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); - -/* user defined special function */ -void action_function(keyrecord_t *record, uint8_t id, uint8_t opt); - -/* keyboard-specific key event (pre)processing */ -bool process_record_quantum(keyrecord_t *record); - -/* Utilities for actions. */ -#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) -extern bool disable_action_cache; -#endif - -/* Code for handling one-handed key modifiers. */ -#ifdef SWAP_HANDS_ENABLE -extern bool swap_hands; -extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS]; -# if (MATRIX_COLS <= 8) -typedef uint8_t swap_state_row_t; -# elif (MATRIX_COLS <= 16) -typedef uint16_t swap_state_row_t; -# elif (MATRIX_COLS <= 32) -typedef uint32_t swap_state_row_t; -# else -# error "MATRIX_COLS: invalid value" -# endif - -void process_hand_swap(keyevent_t *record); -#endif - -void process_record_nocache(keyrecord_t *record); -void process_record(keyrecord_t *record); -void process_record_handler(keyrecord_t *record); -void post_process_record_quantum(keyrecord_t *record); -void process_action(keyrecord_t *record, action_t action); -void register_code(uint8_t code); -void unregister_code(uint8_t code); -void tap_code(uint8_t code); -void tap_code_delay(uint8_t code, uint16_t delay); -void register_mods(uint8_t mods); -void unregister_mods(uint8_t mods); -void register_weak_mods(uint8_t mods); -void unregister_weak_mods(uint8_t mods); -// void set_mods(uint8_t mods); -void clear_keyboard(void); -void clear_keyboard_but_mods(void); -void clear_keyboard_but_mods_and_keys(void); -void layer_switch(uint8_t new_layer); -bool is_tap_key(keypos_t key); -bool is_tap_record(keyrecord_t *record); -bool is_tap_action(action_t action); - -#ifndef NO_ACTION_TAPPING -void process_record_tap_hint(keyrecord_t *record); -#endif - -/* debug */ -void debug_event(keyevent_t event); -void debug_record(keyrecord_t record); -void debug_action(action_t action); - -#ifdef __cplusplus -} -#endif diff --git a/tmk_core/common/action_code.h b/tmk_core/common/action_code.h deleted file mode 100644 index eb18c36ae8..0000000000 --- a/tmk_core/common/action_code.h +++ /dev/null @@ -1,308 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -/** \brief Action codes - * - * 16bit code: action_kind(4bit) + action_parameter(12bit) - * - * Key Actions(00xx) - * ----------------- - * ACT_MODS(000r): - * 000r|0000|0000 0000 No action code - * 000r|0000|0000 0001 Transparent code - * 000r|0000| keycode Key - * 000r|mods|0000 0000 Modifiers - * 000r|mods| keycode Modifiers+Key(Modified key) - * r: Left/Right flag(Left:0, Right:1) - * - * ACT_MODS_TAP(001r): - * 001r|mods|0000 0000 Modifiers with OneShot - * 001r|mods|0000 0001 Modifiers with tap toggle - * 001r|mods|0000 00xx (reserved) - * 001r|mods| keycode Modifiers with Tap Key(Dual role) - * - * Other Keys(01xx) - * ---------------- - * ACT_USAGE(0100): TODO: Not needed? - * 0100|00| usage(10) System control(0x80) - General Desktop page(0x01) - * 0100|01| usage(10) Consumer control(0x01) - Consumer page(0x0C) - * 0100|10| usage(10) (reserved) - * 0100|11| usage(10) (reserved) - * - * ACT_MOUSEKEY(0101): TODO: Merge these two actions to conserve space? - * 0101|xxxx| keycode Mouse key - * - * ACT_SWAP_HANDS(0110): - * 0110|xxxx| keycode Swap hands (keycode on tap, or options) - * - * 0111|xxxx xxxx xxxx (reserved) - * - * Layer Actions(10xx) - * ------------------- - * ACT_LAYER(1000): - * 1000|oo00|pppE BBBB Default Layer Bitwise operation - * oo: operation(00:AND, 01:OR, 10:XOR, 11:SET) - * ppp: 4-bit chunk part(0-7) - * EBBBB: bits and extra bit - * 1000|ooee|pppE BBBB Layer Bitwise Operation - * oo: operation(00:AND, 01:OR, 10:XOR, 11:SET) - * ppp: 4-bit chunk part(0-7) - * EBBBB: bits and extra bit - * ee: on event(01:press, 10:release, 11:both) - * - * ACT_LAYER_MODS(1001): - * 1001|LLLL| mods Layer with modifiers held - * - * ACT_LAYER_TAP(101x): - * 101E|LLLL| keycode On/Off with tap key (0x00-DF)[TAP] - * 101E|LLLL|1110 mods On/Off with modifiers (0xE0-EF)[NOT TAP] - * 101E|LLLL|1111 0000 Invert with tap toggle (0xF0) [TAP] - * 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP] - * 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP] - * 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP] - * 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP] - * 101E|LLLL|1111 xxxx Reserved (0xF5-FF) - * ELLLL: layer 0-31(E: extra bit for layer 16-31) - * - * Extensions(11xx) - * ---------------- - * ACT_MACRO(1100): - * 1100|opt | id(8) Macro play? - * 1100|1111| id(8) Macro record? - * - * 1101|xxxx xxxx xxxx (reserved) - * 1110|xxxx xxxx xxxx (reserved) - * - * ACT_FUNCTION(1111): - * 1111| address(12) Function? - * 1111|opt | id(8) Function? - */ -enum action_kind_id { - /* Key Actions */ - ACT_MODS = 0b0000, - ACT_LMODS = 0b0000, - ACT_RMODS = 0b0001, - ACT_MODS_TAP = 0b0010, - ACT_LMODS_TAP = 0b0010, - ACT_RMODS_TAP = 0b0011, - /* Other Keys */ - ACT_USAGE = 0b0100, - ACT_MOUSEKEY = 0b0101, - /* One-hand Support */ - ACT_SWAP_HANDS = 0b0110, - /* Layer Actions */ - ACT_LAYER = 0b1000, - ACT_LAYER_MODS = 0b1001, - ACT_LAYER_TAP = 0b1010, /* Layer 0-15 */ - ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */ - /* Extensions */ - ACT_MACRO = 0b1100, - ACT_FUNCTION = 0b1111 -}; - -/** \brief Action Code Struct - * - * NOTE: - * In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15). - * AVR looks like a little endian in avr-gcc. - * Not portable across compiler/endianness? - * - * Byte order and bit order of 0x1234: - * Big endian: Little endian: - * -------------------- -------------------- - * FEDC BA98 7654 3210 0123 4567 89AB CDEF - * 0001 0010 0011 0100 0010 1100 0100 1000 - * 0x12 0x34 0x34 0x12 - */ -typedef union { - uint16_t code; - struct action_kind { - uint16_t param : 12; - uint8_t id : 4; - } kind; - struct action_key { - uint8_t code : 8; - uint8_t mods : 4; - uint8_t kind : 4; - } key; - struct action_layer_bitop { - uint8_t bits : 4; - uint8_t xbit : 1; - uint8_t part : 3; - uint8_t on : 2; - uint8_t op : 2; - uint8_t kind : 4; - } layer_bitop; - struct action_layer_mods { - uint8_t mods : 8; - uint8_t layer : 4; - uint8_t kind : 4; - } layer_mods; - struct action_layer_tap { - uint8_t code : 8; - uint8_t val : 5; - uint8_t kind : 3; - } layer_tap; - struct action_usage { - uint16_t code : 10; - uint8_t page : 2; - uint8_t kind : 4; - } usage; - struct action_function { - uint8_t id : 8; - uint8_t opt : 4; - uint8_t kind : 4; - } func; - struct action_swap { - uint8_t code : 8; - uint8_t opt : 4; - uint8_t kind : 4; - } swap; -} action_t; - -/* action utility */ -#define ACTION_NO 0 -#define ACTION_TRANSPARENT 1 -#define ACTION(kind, param) ((kind) << 12 | (param)) - -/** \brief Key Actions - * - * Mod bits: 43210 - * bit 0 ||||+- Control - * bit 1 |||+-- Shift - * bit 2 ||+--- Alt - * bit 3 |+---- Gui - * bit 4 +----- LR flag(Left:0, Right:1) - */ -enum mods_bit { - MOD_LCTL = 0x01, - MOD_LSFT = 0x02, - MOD_LALT = 0x04, - MOD_LGUI = 0x08, - MOD_RCTL = 0x11, - MOD_RSFT = 0x12, - MOD_RALT = 0x14, - MOD_RGUI = 0x18, -}; -enum mods_codes { - MODS_ONESHOT = 0x00, - MODS_TAP_TOGGLE = 0x01, -}; -#define ACTION_KEY(key) ACTION(ACT_MODS, (key)) -#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | 0) -#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | (key)) -#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | (key)) -#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_ONESHOT) -#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_TAP_TOGGLE) - -/** \brief Other Keys - */ -enum usage_pages { PAGE_SYSTEM, PAGE_CONSUMER }; -#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id)) -#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id)) -#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key) - -/** \brief Layer Actions - */ -enum layer_param_on { - ON_PRESS = 1, - ON_RELEASE = 2, - ON_BOTH = 3, -}; - -/** \brief Layer Actions - */ -enum layer_param_bit_op { - OP_BIT_AND = 0, - OP_BIT_OR = 1, - OP_BIT_XOR = 2, - OP_BIT_SET = 3, -}; - -/** \brief Layer Actions - */ -enum layer_param_tap_op { - OP_TAP_TOGGLE = 0xF0, - OP_ON_OFF, - OP_OFF_ON, - OP_SET_CLEAR, - OP_ONESHOT, -}; -#define ACTION_LAYER_BITOP(op, part, bits, on) ACTION(ACT_LAYER, (op) << 10 | (on) << 8 | (part) << 5 | ((bits)&0x1f)) -#define ACTION_LAYER_TAP(layer, key) ACTION(ACT_LAYER_TAP, (layer) << 8 | (key)) -/* Default Layer */ -#define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4)) -/* Layer Operation */ -#define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on)) -#define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer) -#define ACTION_LAYER_TOGGLE(layer) ACTION_LAYER_INVERT(layer, ON_RELEASE) -#define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on)) -#define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on)) -#define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on)) -#define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on)) -#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF) -#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON) -#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR) -#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT) -#define ACTION_LAYER_MODS(layer, mods) ACTION(ACT_LAYER_MODS, (layer) << 8 | (mods)) -/* With Tapping */ -#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key)) -#define ACTION_LAYER_TAP_TOGGLE(layer) ACTION_LAYER_TAP((layer), OP_TAP_TOGGLE) -/* Bitwise Operation */ -#define ACTION_LAYER_BIT_AND(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), (on)) -#define ACTION_LAYER_BIT_OR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), (on)) -#define ACTION_LAYER_BIT_XOR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), (on)) -#define ACTION_LAYER_BIT_SET(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), (on)) -/* Default Layer Bitwise Operation */ -#define ACTION_DEFAULT_LAYER_BIT_AND(part, bits) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), 0) -#define ACTION_DEFAULT_LAYER_BIT_OR(part, bits) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), 0) -#define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0) -#define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0) - -/* Macro */ -#define ACTION_MACRO(id) ACTION(ACT_MACRO, (id)) -#define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP << 8 | (id)) -#define ACTION_MACRO_OPT(id, opt) ACTION(ACT_MACRO, (opt) << 8 | (id)) -/* Function */ -enum function_opts { - FUNC_TAP = 0x8, /* indciates function is tappable */ -}; -#define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id)) -#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP << 8 | (id)) -#define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt) << 8 | (id)) -/* OneHand Support */ -enum swap_hands_param_tap_op { - OP_SH_TOGGLE = 0xF0, - OP_SH_TAP_TOGGLE, - OP_SH_ON_OFF, - OP_SH_OFF_ON, - OP_SH_OFF, - OP_SH_ON, - OP_SH_ONESHOT, -}; - -#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF() -#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE) -#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE) -#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT) -#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key) -#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF) -#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON) -#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON) -#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF) diff --git a/tmk_core/common/action_layer.c b/tmk_core/common/action_layer.c deleted file mode 100644 index ed1a4bd20d..0000000000 --- a/tmk_core/common/action_layer.c +++ /dev/null @@ -1,279 +0,0 @@ -#include -#include "keyboard.h" -#include "action.h" -#include "util.h" -#include "action_layer.h" - -#ifdef DEBUG_ACTION -# include "debug.h" -#else -# include "nodebug.h" -#endif - -/** \brief Default Layer State - */ -layer_state_t default_layer_state = 0; - -/** \brief Default Layer State Set At user Level - * - * Run user code on default layer state change - */ -__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) { return state; } - -/** \brief Default Layer State Set At Keyboard Level - * - * Run keyboard code on default layer state change - */ -__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) { return default_layer_state_set_user(state); } - -/** \brief Default Layer State Set - * - * Static function to set the default layer state, prints debug info and clears keys - */ -static void default_layer_state_set(layer_state_t state) { - state = default_layer_state_set_kb(state); - debug("default_layer_state: "); - default_layer_debug(); - debug(" to "); - default_layer_state = state; - default_layer_debug(); - debug("\n"); -#ifdef STRICT_LAYER_RELEASE - clear_keyboard_but_mods(); // To avoid stuck keys -#else - clear_keyboard_but_mods_and_keys(); // Don't reset held keys -#endif -} - -/** \brief Default Layer Print - * - * Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit. - */ -void default_layer_debug(void) { dprintf("%08lX(%u)", default_layer_state, get_highest_layer(default_layer_state)); } - -/** \brief Default Layer Set - * - * Sets the default layer state. - */ -void default_layer_set(layer_state_t state) { default_layer_state_set(state); } - -#ifndef NO_ACTION_LAYER -/** \brief Default Layer Or - * - * Turns on the default layer based on matching bits between specifed layer and existing layer state - */ -void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); } -/** \brief Default Layer And - * - * Turns on default layer based on matching enabled bits between specifed layer and existing layer state - */ -void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); } -/** \brief Default Layer Xor - * - * Turns on default layer based on non-matching bits between specifed layer and existing layer state - */ -void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); } -#endif - -#ifndef NO_ACTION_LAYER -/** \brief Keymap Layer State - */ -layer_state_t layer_state = 0; - -/** \brief Layer state set user - * - * Runs user code on layer state change - */ -__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) { return state; } - -/** \brief Layer state set keyboard - * - * Runs keyboard code on layer state change - */ -__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { return layer_state_set_user(state); } - -/** \brief Layer state set - * - * Sets the layer to match the specifed state (a bitmask) - */ -void layer_state_set(layer_state_t state) { - state = layer_state_set_kb(state); - dprint("layer_state: "); - layer_debug(); - dprint(" to "); - layer_state = state; - layer_debug(); - dprintln(); -# ifdef STRICT_LAYER_RELEASE - clear_keyboard_but_mods(); // To avoid stuck keys -# else - clear_keyboard_but_mods_and_keys(); // Don't reset held keys -# endif -} - -/** \brief Layer clear - * - * Turn off all layers - */ -void layer_clear(void) { layer_state_set(0); } - -/** \brief Layer state is - * - * Return whether the given state is on (it might still be shadowed by a higher state, though) - */ -bool layer_state_is(uint8_t layer) { return layer_state_cmp(layer_state, layer); } - -/** \brief Layer state compare - * - * Used for comparing layers {mostly used for unit testing} - */ -bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) { - if (!cmp_layer_state) { - return layer == 0; - } - return (cmp_layer_state & ((layer_state_t)1 << layer)) != 0; -} - -/** \brief Layer move - * - * Turns on the given layer and turn off all other layers - */ -void layer_move(uint8_t layer) { layer_state_set((layer_state_t)1 << layer); } - -/** \brief Layer on - * - * Turns on given layer - */ -void layer_on(uint8_t layer) { layer_state_set(layer_state | ((layer_state_t)1 << layer)); } - -/** \brief Layer off - * - * Turns off given layer - */ -void layer_off(uint8_t layer) { layer_state_set(layer_state & ~((layer_state_t)1 << layer)); } - -/** \brief Layer invert - * - * Toggle the given layer (set it if it's unset, or unset it if it's set) - */ -void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ ((layer_state_t)1 << layer)); } - -/** \brief Layer or - * - * Turns on layers based on matching bits between specifed layer and existing layer state - */ -void layer_or(layer_state_t state) { layer_state_set(layer_state | state); } -/** \brief Layer and - * - * Turns on layers based on matching enabled bits between specifed layer and existing layer state - */ -void layer_and(layer_state_t state) { layer_state_set(layer_state & state); } -/** \brief Layer xor - * - * Turns on layers based on non-matching bits between specifed layer and existing layer state - */ -void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); } - -/** \brief Layer debug printing - * - * Print out the hex value of the 32-bit layer state, as well as the value of the highest bit. - */ -void layer_debug(void) { dprintf("%08lX(%u)", layer_state, get_highest_layer(layer_state)); } -#endif - -#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) -/** \brief source layer cache - */ - -uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}}; - -/** \brief update source layers cache - * - * Updates the cached keys when changing layers - */ -void update_source_layers_cache(keypos_t key, uint8_t layer) { - const uint8_t key_number = key.col + (key.row * MATRIX_COLS); - const uint8_t storage_row = key_number / 8; - const uint8_t storage_bit = key_number % 8; - - for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { - source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit); - } -} - -/** \brief read source layers cache - * - * reads the cached keys stored when the layer was changed - */ -uint8_t read_source_layers_cache(keypos_t key) { - const uint8_t key_number = key.col + (key.row * MATRIX_COLS); - const uint8_t storage_row = key_number / 8; - const uint8_t storage_bit = key_number % 8; - uint8_t layer = 0; - - for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { - layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number; - } - - return layer; -} -#endif - -/** \brief Store or get action (FIXME: Needs better summary) - * - * Make sure the action triggered when the key is released is the same - * one as the one triggered on press. It's important for the mod keys - * when the layer is switched after the down event but before the up - * event as they may get stuck otherwise. - */ -action_t store_or_get_action(bool pressed, keypos_t key) { -#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) - if (disable_action_cache) { - return layer_switch_get_action(key); - } - - uint8_t layer; - - if (pressed) { - layer = layer_switch_get_layer(key); - update_source_layers_cache(key, layer); - } else { - layer = read_source_layers_cache(key); - } - return action_for_key(layer, key); -#else - return layer_switch_get_action(key); -#endif -} - -/** \brief Layer switch get layer - * - * Gets the layer based on key info - */ -uint8_t layer_switch_get_layer(keypos_t key) { -#ifndef NO_ACTION_LAYER - action_t action; - action.code = ACTION_TRANSPARENT; - - layer_state_t layers = layer_state | default_layer_state; - /* check top layer first */ - for (int8_t i = MAX_LAYER - 1; i >= 0; i--) { - if (layers & ((layer_state_t)1 << i)) { - action = action_for_key(i, key); - if (action.code != ACTION_TRANSPARENT) { - return i; - } - } - } - /* fall back to layer 0 */ - return 0; -#else - return get_highest_layer(default_layer_state); -#endif -} - -/** \brief Layer switch get layer - * - * Gets action code based on key position - */ -action_t layer_switch_get_action(keypos_t key) { return action_for_key(layer_switch_get_layer(key), key); } diff --git a/tmk_core/common/action_layer.h b/tmk_core/common/action_layer.h deleted file mode 100644 index b87d096eed..0000000000 --- a/tmk_core/common/action_layer.h +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include -#include "keyboard.h" -#include "action.h" - -#ifdef DYNAMIC_KEYMAP_ENABLE -# ifndef DYNAMIC_KEYMAP_LAYER_COUNT -# define DYNAMIC_KEYMAP_LAYER_COUNT 4 -# endif -# if DYNAMIC_KEYMAP_LAYER_COUNT <= 8 -# ifndef LAYER_STATE_8BIT -# define LAYER_STATE_8BIT -# endif -# elif DYNAMIC_KEYMAP_LAYER_COUNT <= 16 -# ifndef LAYER_STATE_16BIT -# define LAYER_STATE_16BIT -# endif -# else -# ifndef LAYER_STATE_32BIT -# define LAYER_STATE_32BIT -# endif -# endif -#endif - -#if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT) -# define LAYER_STATE_32BIT -#endif - -#if defined(LAYER_STATE_8BIT) -typedef uint8_t layer_state_t; -# define MAX_LAYER_BITS 3 -# ifndef MAX_LAYER -# define MAX_LAYER 8 -# endif -# define get_highest_layer(state) biton(state) -#elif defined(LAYER_STATE_16BIT) -typedef uint16_t layer_state_t; -# define MAX_LAYER_BITS 4 -# ifndef MAX_LAYER -# define MAX_LAYER 16 -# endif -# define get_highest_layer(state) biton16(state) -#elif defined(LAYER_STATE_32BIT) -typedef uint32_t layer_state_t; -# define MAX_LAYER_BITS 5 -# ifndef MAX_LAYER -# define MAX_LAYER 32 -# endif -# define get_highest_layer(state) biton32(state) -#else -# error Layer Mask size not specified. HOW?! -#endif - -/* - * Default Layer - */ -extern layer_state_t default_layer_state; -void default_layer_debug(void); -void default_layer_set(layer_state_t state); - -__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state); -__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state); - -#ifndef NO_ACTION_LAYER -/* bitwise operation */ -void default_layer_or(layer_state_t state); -void default_layer_and(layer_state_t state); -void default_layer_xor(layer_state_t state); -#else -# define default_layer_or(state) -# define default_layer_and(state) -# define default_layer_xor(state) -#endif - -/* - * Keymap Layer - */ -#ifndef NO_ACTION_LAYER -extern layer_state_t layer_state; - -void layer_state_set(layer_state_t state); -bool layer_state_is(uint8_t layer); -bool layer_state_cmp(layer_state_t layer1, uint8_t layer2); - -void layer_debug(void); -void layer_clear(void); -void layer_move(uint8_t layer); -void layer_on(uint8_t layer); -void layer_off(uint8_t layer); -void layer_invert(uint8_t layer); -/* bitwise operation */ -void layer_or(layer_state_t state); -void layer_and(layer_state_t state); -void layer_xor(layer_state_t state); -layer_state_t layer_state_set_user(layer_state_t state); -layer_state_t layer_state_set_kb(layer_state_t state); -#else -# define layer_state 0 - -# define layer_state_set(layer) -# define layer_state_is(layer) (layer == 0) -# define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & (layer_state_t)1 << layer) != 0) - -# define layer_debug() -# define layer_clear() -# define layer_move(layer) (void)layer -# define layer_on(layer) (void)layer -# define layer_off(layer) (void)layer -# define layer_invert(layer) (void)layer -# define layer_or(state) (void)state -# define layer_and(state) (void)state -# define layer_xor(state) (void)state -# define layer_state_set_kb(state) (void)state -# define layer_state_set_user(state) (void)state -#endif - -/* pressed actions cache */ -#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) - -void update_source_layers_cache(keypos_t key, uint8_t layer); -uint8_t read_source_layers_cache(keypos_t key); -#endif -action_t store_or_get_action(bool pressed, keypos_t key); - -/* return the topmost non-transparent layer currently associated with key */ -uint8_t layer_switch_get_layer(keypos_t key); - -/* return action depending on current layer status */ -action_t layer_switch_get_action(keypos_t key); diff --git a/tmk_core/common/action_macro.c b/tmk_core/common/action_macro.c deleted file mode 100644 index 92228c0ba8..0000000000 --- a/tmk_core/common/action_macro.c +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "action.h" -#include "action_util.h" -#include "action_macro.h" -#include "wait.h" - -#ifdef DEBUG_ACTION -# include "debug.h" -#else -# include "nodebug.h" -#endif - -#ifndef NO_ACTION_MACRO - -# define MACRO_READ() (macro = MACRO_GET(macro_p++)) -/** \brief Action Macro Play - * - * FIXME: Needs doc - */ -void action_macro_play(const macro_t *macro_p) { - macro_t macro = END; - uint8_t interval = 0; - - if (!macro_p) return; - while (true) { - switch (MACRO_READ()) { - case KEY_DOWN: - MACRO_READ(); - dprintf("KEY_DOWN(%02X)\n", macro); - if (IS_MOD(macro)) { - add_macro_mods(MOD_BIT(macro)); - send_keyboard_report(); - } else { - register_code(macro); - } - break; - case KEY_UP: - MACRO_READ(); - dprintf("KEY_UP(%02X)\n", macro); - if (IS_MOD(macro)) { - del_macro_mods(MOD_BIT(macro)); - send_keyboard_report(); - } else { - unregister_code(macro); - } - break; - case WAIT: - MACRO_READ(); - dprintf("WAIT(%u)\n", macro); - { - uint8_t ms = macro; - while (ms--) wait_ms(1); - } - break; - case INTERVAL: - interval = MACRO_READ(); - dprintf("INTERVAL(%u)\n", interval); - break; - case 0x04 ... 0x73: - dprintf("DOWN(%02X)\n", macro); - register_code(macro); - break; - case 0x84 ... 0xF3: - dprintf("UP(%02X)\n", macro); - unregister_code(macro & 0x7F); - break; - case END: - default: - return; - } - // interval - { - uint8_t ms = interval; - while (ms--) wait_ms(1); - } - } -} -#endif diff --git a/tmk_core/common/action_macro.h b/tmk_core/common/action_macro.h deleted file mode 100644 index 685e2c6ffc..0000000000 --- a/tmk_core/common/action_macro.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include -#include "progmem.h" - -typedef uint8_t macro_t; - -#define MACRO_NONE (macro_t *)0 -#define MACRO(...) \ - ({ \ - static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \ - &__m[0]; \ - }) -#define MACRO_GET(p) pgm_read_byte(p) - -// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped -#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release))) - -// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped -#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro) - -// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #) -#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod) - -// Momentary switch layer when held, sends macro if tapped -#define MACRO_TAP_HOLD_LAYER(record, macro, layer) \ - (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({ \ - layer_on((layer)); \ - MACRO_NONE; \ - }) \ - : MACRO_NONE) \ - : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \ - layer_off((layer)); \ - MACRO_NONE; \ - }))) - -// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #) -#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer) - -#ifndef NO_ACTION_MACRO -void action_macro_play(const macro_t *macro_p); -#else -# define action_macro_play(macro) -#endif - -/* Macro commands - * code(0x04-73) // key down(1byte) - * code(0x04-73) | 0x80 // key up(1byte) - * { KEY_DOWN, code(0x04-0xff) } // key down(2bytes) - * { KEY_UP, code(0x04-0xff) } // key up(2bytes) - * WAIT // wait milli-seconds - * INTERVAL // set interval between macro commands - * END // stop macro execution - * - * Ideas(Not implemented): - * modifiers - * system usage - * consumer usage - * unicode usage - * function call - * conditionals - * loop - */ -enum macro_command_id { - /* 0x00 - 0x03 */ - END = 0x00, - KEY_DOWN, - KEY_UP, - - /* 0x04 - 0x73 (reserved for keycode down) */ - - /* 0x74 - 0x83 */ - WAIT = 0x74, - INTERVAL, - - /* 0x84 - 0xf3 (reserved for keycode up) */ - - /* 0xf4 - 0xff */ -}; - -/* TODO: keycode:0x04-0x73 can be handled by 1byte command else 2bytes are needed - * if keycode between 0x04 and 0x73 - * keycode / (keycode|0x80) - * else - * {KEY_DOWN, keycode} / {KEY_UP, keycode} - */ -#define DOWN(key) KEY_DOWN, (key) -#define UP(key) KEY_UP, (key) -#define TYPE(key) DOWN(key), UP(key) -#define WAIT(ms) WAIT, (ms) -#define INTERVAL(ms) INTERVAL, (ms) - -/* key down */ -#define D(key) DOWN(KC_##key) -/* key up */ -#define U(key) UP(KC_##key) -/* key type */ -#define T(key) TYPE(KC_##key) -/* wait */ -#define W(ms) WAIT(ms) -/* interval */ -#define I(ms) INTERVAL(ms) - -/* for backward comaptibility */ -#define MD(key) DOWN(KC_##key) -#define MU(key) UP(KC_##key) diff --git a/tmk_core/common/action_tapping.c b/tmk_core/common/action_tapping.c deleted file mode 100644 index 36839f9faf..0000000000 --- a/tmk_core/common/action_tapping.c +++ /dev/null @@ -1,456 +0,0 @@ -#include -#include -#include "action.h" -#include "action_layer.h" -#include "action_tapping.h" -#include "keycode.h" -#include "timer.h" - -#ifdef DEBUG_ACTION -# include "debug.h" -#else -# include "nodebug.h" -#endif - -#ifndef NO_ACTION_TAPPING - -# define IS_TAPPING() !IS_NOEVENT(tapping_key.event) -# define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed) -# define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed) -# define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k))) -#ifndef COMBO_ENABLE -# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key))) -#else -# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode) -#endif - -__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; } - -# ifdef TAPPING_TERM_PER_KEY -# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key)) -# else -# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM) -# endif - -# ifdef TAPPING_FORCE_HOLD_PER_KEY -__attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) { return false; } -# endif - -# ifdef PERMISSIVE_HOLD_PER_KEY -__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { return false; } -# endif - -# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY -__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; } -# endif - -static keyrecord_t tapping_key = {}; -static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {}; -static uint8_t waiting_buffer_head = 0; -static uint8_t waiting_buffer_tail = 0; - -static bool process_tapping(keyrecord_t *record); -static bool waiting_buffer_enq(keyrecord_t record); -static void waiting_buffer_clear(void); -static bool waiting_buffer_typed(keyevent_t event); -static bool waiting_buffer_has_anykey_pressed(void); -static void waiting_buffer_scan_tap(void); -static void debug_tapping_key(void); -static void debug_waiting_buffer(void); - -/** \brief Action Tapping Process - * - * FIXME: Needs doc - */ -void action_tapping_process(keyrecord_t record) { - if (process_tapping(&record)) { - if (!IS_NOEVENT(record.event)) { - debug("processed: "); - debug_record(record); - debug("\n"); - } - } else { - if (!waiting_buffer_enq(record)) { - // clear all in case of overflow. - debug("OVERFLOW: CLEAR ALL STATES\n"); - clear_keyboard(); - waiting_buffer_clear(); - tapping_key = (keyrecord_t){}; - } - } - - // process waiting_buffer - if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) { - debug("---- action_exec: process waiting_buffer -----\n"); - } - for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) { - if (process_tapping(&waiting_buffer[waiting_buffer_tail])) { - debug("processed: waiting_buffer["); - debug_dec(waiting_buffer_tail); - debug("] = "); - debug_record(waiting_buffer[waiting_buffer_tail]); - debug("\n\n"); - } else { - break; - } - } - if (!IS_NOEVENT(record.event)) { - debug("\n"); - } -} - -/** \brief Tapping - * - * Rule: Tap key is typed(pressed and released) within TAPPING_TERM. - * (without interfering by typing other key) - */ -/* return true when key event is processed or consumed. */ -bool process_tapping(keyrecord_t *keyp) { - keyevent_t event = keyp->event; - - // if tapping - if (IS_TAPPING_PRESSED()) { - if (WITHIN_TAPPING_TERM(event)) { - if (tapping_key.tap.count == 0) { - if (IS_TAPPING_RECORD(keyp) && !event.pressed) { - // first tap! - debug("Tapping: First tap(0->1).\n"); - tapping_key.tap.count = 1; - debug_tapping_key(); - process_record(&tapping_key); - - // copy tapping state - keyp->tap = tapping_key.tap; - // enqueue - return false; - } - /* Process a key typed within TAPPING_TERM - * This can register the key before settlement of tapping, - * useful for long TAPPING_TERM but may prevent fast typing. - */ -# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) - else if ((( -# ifdef TAPPING_TERM_PER_KEY - get_tapping_term(get_record_keycode(&tapping_key, false), keyp) -# else - TAPPING_TERM -# endif - >= 500) - -# ifdef PERMISSIVE_HOLD_PER_KEY - || get_permissive_hold(get_record_keycode(&tapping_key, false), keyp) -# elif defined(PERMISSIVE_HOLD) - || true -# endif - ) && - IS_RELEASED(event) && waiting_buffer_typed(event)) { - debug("Tapping: End. No tap. Interfered by typing key\n"); - process_record(&tapping_key); - tapping_key = (keyrecord_t){}; - debug_tapping_key(); - // enqueue - return false; - } -# endif - /* Process release event of a key pressed before tapping starts - * Without this unexpected repeating will occur with having fast repeating setting - * https://github.com/tmk/tmk_keyboard/issues/60 - */ - else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) { - // Modifier should be retained till end of this tapping. - action_t action = layer_switch_get_action(event.key); - switch (action.kind.id) { - case ACT_LMODS: - case ACT_RMODS: - if (action.key.mods && !action.key.code) return false; - if (IS_MOD(action.key.code)) return false; - break; - case ACT_LMODS_TAP: - case ACT_RMODS_TAP: - if (action.key.mods && keyp->tap.count == 0) return false; - if (IS_MOD(action.key.code)) return false; - break; - } - // Release of key should be process immediately. - debug("Tapping: release event of a key pressed before tapping\n"); - process_record(keyp); - return true; - } else { - // set interrupted flag when other key preesed during tapping - if (event.pressed) { - tapping_key.tap.interrupted = true; -# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) -# if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) - if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp)) -# endif - { - debug("Tapping: End. No tap. Interfered by pressed key\n"); - process_record(&tapping_key); - tapping_key = (keyrecord_t){}; - debug_tapping_key(); - // enqueue - return false; - } -# endif - } - // enqueue - return false; - } - } - // tap_count > 0 - else { - if (IS_TAPPING_RECORD(keyp) && !event.pressed) { - debug("Tapping: Tap release("); - debug_dec(tapping_key.tap.count); - debug(")\n"); - keyp->tap = tapping_key.tap; - process_record(keyp); - tapping_key = *keyp; - debug_tapping_key(); - return true; - } else if (is_tap_record(keyp) && event.pressed) { - if (tapping_key.tap.count > 1) { - debug("Tapping: Start new tap with releasing last tap(>1).\n"); - // unregister key - process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, -#ifdef COMBO_ENABLE - .keycode = tapping_key.keycode, -#endif - }); - } else { - debug("Tapping: Start while last tap(1).\n"); - } - tapping_key = *keyp; - waiting_buffer_scan_tap(); - debug_tapping_key(); - return true; - } else { - if (!IS_NOEVENT(event)) { - debug("Tapping: key event while last tap(>0).\n"); - } - process_record(keyp); - return true; - } - } - } - // after TAPPING_TERM - else { - if (tapping_key.tap.count == 0) { - debug("Tapping: End. Timeout. Not tap(0): "); - debug_event(event); - debug("\n"); - process_record(&tapping_key); - tapping_key = (keyrecord_t){}; - debug_tapping_key(); - return false; - } else { - if (IS_TAPPING_RECORD(keyp) && !event.pressed) { - debug("Tapping: End. last timeout tap release(>0)."); - keyp->tap = tapping_key.tap; - process_record(keyp); - tapping_key = (keyrecord_t){}; - return true; - } else if (is_tap_record(keyp) && event.pressed) { - if (tapping_key.tap.count > 1) { - debug("Tapping: Start new tap with releasing last timeout tap(>1).\n"); - // unregister key - process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, -#ifdef COMBO_ENABLE - .keycode = tapping_key.keycode, -#endif - }); - } else { - debug("Tapping: Start while last timeout tap(1).\n"); - } - tapping_key = *keyp; - waiting_buffer_scan_tap(); - debug_tapping_key(); - return true; - } else { - if (!IS_NOEVENT(event)) { - debug("Tapping: key event while last timeout tap(>0).\n"); - } - process_record(keyp); - return true; - } - } - } - } else if (IS_TAPPING_RELEASED()) { - if (WITHIN_TAPPING_TERM(event)) { - if (event.pressed) { - if (IS_TAPPING_RECORD(keyp)) { -//# ifndef TAPPING_FORCE_HOLD -# if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY) - if ( -# ifdef TAPPING_FORCE_HOLD_PER_KEY - !get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) && -# endif - !tapping_key.tap.interrupted && tapping_key.tap.count > 0) { - // sequential tap. - keyp->tap = tapping_key.tap; - if (keyp->tap.count < 15) keyp->tap.count += 1; - debug("Tapping: Tap press("); - debug_dec(keyp->tap.count); - debug(")\n"); - process_record(keyp); - tapping_key = *keyp; - debug_tapping_key(); - return true; - } -# endif - // FIX: start new tap again - tapping_key = *keyp; - return true; - } else if (is_tap_record(keyp)) { - // Sequential tap can be interfered with other tap key. - debug("Tapping: Start with interfering other tap.\n"); - tapping_key = *keyp; - waiting_buffer_scan_tap(); - debug_tapping_key(); - return true; - } else { - // should none in buffer - // FIX: interrupted when other key is pressed - tapping_key.tap.interrupted = true; - process_record(keyp); - return true; - } - } else { - if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n"); - process_record(keyp); - return true; - } - } else { - // FIX: process_action here? - // timeout. no sequential tap. - debug("Tapping: End(Timeout after releasing last tap): "); - debug_event(event); - debug("\n"); - tapping_key = (keyrecord_t){}; - debug_tapping_key(); - return false; - } - } - // not tapping state - else { - if (event.pressed && is_tap_record(keyp)) { - debug("Tapping: Start(Press tap key).\n"); - tapping_key = *keyp; - process_record_tap_hint(&tapping_key); - waiting_buffer_scan_tap(); - debug_tapping_key(); - return true; - } else { - process_record(keyp); - return true; - } - } -} - -/** \brief Waiting buffer enq - * - * FIXME: Needs docs - */ -bool waiting_buffer_enq(keyrecord_t record) { - if (IS_NOEVENT(record.event)) { - return true; - } - - if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) { - debug("waiting_buffer_enq: Over flow.\n"); - return false; - } - - waiting_buffer[waiting_buffer_head] = record; - waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE; - - debug("waiting_buffer_enq: "); - debug_waiting_buffer(); - return true; -} - -/** \brief Waiting buffer clear - * - * FIXME: Needs docs - */ -void waiting_buffer_clear(void) { - waiting_buffer_head = 0; - waiting_buffer_tail = 0; -} - -/** \brief Waiting buffer typed - * - * FIXME: Needs docs - */ -bool waiting_buffer_typed(keyevent_t event) { - for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { - if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) { - return true; - } - } - return false; -} - -/** \brief Waiting buffer has anykey pressed - * - * FIXME: Needs docs - */ -__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) { - for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { - if (waiting_buffer[i].event.pressed) return true; - } - return false; -} - -/** \brief Scan buffer for tapping - * - * FIXME: Needs docs - */ -void waiting_buffer_scan_tap(void) { - // tapping already is settled - if (tapping_key.tap.count > 0) return; - // invalid state: tapping_key released && tap.count == 0 - if (!tapping_key.event.pressed) return; - - for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { - if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) { - tapping_key.tap.count = 1; - waiting_buffer[i].tap.count = 1; - process_record(&tapping_key); - - debug("waiting_buffer_scan_tap: found at ["); - debug_dec(i); - debug("]\n"); - debug_waiting_buffer(); - return; - } - } -} - -/** \brief Tapping key debug print - * - * FIXME: Needs docs - */ -static void debug_tapping_key(void) { - debug("TAPPING_KEY="); - debug_record(tapping_key); - debug("\n"); -} - -/** \brief Waiting buffer debug print - * - * FIXME: Needs docs - */ -static void debug_waiting_buffer(void) { - debug("{ "); - for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { - debug("["); - debug_dec(i); - debug("]="); - debug_record(waiting_buffer[i]); - debug(" "); - } - debug("}\n"); -} - -#endif diff --git a/tmk_core/common/action_tapping.h b/tmk_core/common/action_tapping.h deleted file mode 100644 index 7de8049c7f..0000000000 --- a/tmk_core/common/action_tapping.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -/* period of tapping(ms) */ -#ifndef TAPPING_TERM -# define TAPPING_TERM 200 -#endif - -/* tap count needed for toggling a feature */ -#ifndef TAPPING_TOGGLE -# define TAPPING_TOGGLE 5 -#endif - -#define WAITING_BUFFER_SIZE 8 - -#ifndef NO_ACTION_TAPPING -uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache); -uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache); -void action_tapping_process(keyrecord_t record); - -uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record); -bool get_permissive_hold(uint16_t keycode, keyrecord_t *record); -bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record); -bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record); -bool get_retro_tapping(uint16_t keycode, keyrecord_t *record); -#endif diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c deleted file mode 100644 index 2b3c00cba0..0000000000 --- a/tmk_core/common/action_util.c +++ /dev/null @@ -1,455 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#include "host.h" -#include "report.h" -#include "debug.h" -#include "action_util.h" -#include "action_layer.h" -#include "timer.h" -#include "keycode_config.h" - -extern keymap_config_t keymap_config; - -static uint8_t real_mods = 0; -static uint8_t weak_mods = 0; -static uint8_t macro_mods = 0; -#ifdef KEY_OVERRIDE_ENABLE -static uint8_t weak_override_mods = 0; -static uint8_t suppressed_mods = 0; -#endif - -#ifdef USB_6KRO_ENABLE -# define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS) -# define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS) -# define RO_INC(a) RO_ADD(a, 1) -# define RO_DEC(a) RO_SUB(a, 1) -static int8_t cb_head = 0; -static int8_t cb_tail = 0; -static int8_t cb_count = 0; -#endif - -// TODO: pointer variable is not needed -// report_keyboard_t keyboard_report = {}; -report_keyboard_t *keyboard_report = &(report_keyboard_t){}; - -extern inline void add_key(uint8_t key); -extern inline void del_key(uint8_t key); -extern inline void clear_keys(void); - -#ifndef NO_ACTION_ONESHOT -static uint8_t oneshot_mods = 0; -static uint8_t oneshot_locked_mods = 0; -uint8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; } -void set_oneshot_locked_mods(uint8_t mods) { - if (mods != oneshot_locked_mods) { - oneshot_locked_mods = mods; - oneshot_locked_mods_changed_kb(oneshot_locked_mods); - } -} -void clear_oneshot_locked_mods(void) { - if (oneshot_locked_mods) { - oneshot_locked_mods = 0; - oneshot_locked_mods_changed_kb(oneshot_locked_mods); - } -} -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) -static uint16_t oneshot_time = 0; -bool has_oneshot_mods_timed_out(void) { return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT; } -# else -bool has_oneshot_mods_timed_out(void) { return false; } -# endif -#endif - -/* oneshot layer */ -#ifndef NO_ACTION_ONESHOT -/** \brief oneshot_layer_data bits - * LLLL LSSS - * where: - * L => are layer bits - * S => oneshot state bits - */ -static int8_t oneshot_layer_data = 0; - -inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; } -inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; } - -# ifdef SWAP_HANDS_ENABLE -enum { - SHO_OFF, - SHO_ACTIVE, // Swap hands button was pressed, and we didn't send any swapped keys yet - SHO_PRESSED, // Swap hands button is currently pressed - SHO_USED, // Swap hands button is still pressed, and we already sent swapped keys -} swap_hands_oneshot = SHO_OFF; -# endif - -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) -static uint16_t oneshot_layer_time = 0; -inline bool has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); } -# ifdef SWAP_HANDS_ENABLE -static uint16_t oneshot_swaphands_time = 0; -inline bool has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE); } -# endif -# endif - -# ifdef SWAP_HANDS_ENABLE - -void set_oneshot_swaphands(void) { - swap_hands_oneshot = SHO_PRESSED; - swap_hands = true; -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_swaphands_time = timer_read(); - if (oneshot_layer_time != 0) { - oneshot_layer_time = oneshot_swaphands_time; - } -# endif -} - -void release_oneshot_swaphands(void) { - if (swap_hands_oneshot == SHO_PRESSED) { - swap_hands_oneshot = SHO_ACTIVE; - } - if (swap_hands_oneshot == SHO_USED) { - clear_oneshot_swaphands(); - } -} - -void use_oneshot_swaphands(void) { - if (swap_hands_oneshot == SHO_PRESSED) { - swap_hands_oneshot = SHO_USED; - } - if (swap_hands_oneshot == SHO_ACTIVE) { - clear_oneshot_swaphands(); - } -} - -void clear_oneshot_swaphands(void) { - swap_hands_oneshot = SHO_OFF; - swap_hands = false; -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_swaphands_time = 0; -# endif -} - -# endif - -/** \brief Set oneshot layer - * - * FIXME: needs doc - */ -void set_oneshot_layer(uint8_t layer, uint8_t state) { - if (!keymap_config.oneshot_disable) { - oneshot_layer_data = layer << 3 | state; - layer_on(layer); -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_layer_time = timer_read(); -# endif - oneshot_layer_changed_kb(get_oneshot_layer()); - } else { - layer_on(layer); - } -} -/** \brief Reset oneshot layer - * - * FIXME: needs doc - */ -void reset_oneshot_layer(void) { - oneshot_layer_data = 0; -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_layer_time = 0; -# endif - oneshot_layer_changed_kb(get_oneshot_layer()); -} -/** \brief Clear oneshot layer - * - * FIXME: needs doc - */ -void clear_oneshot_layer_state(oneshot_fullfillment_t state) { - uint8_t start_state = oneshot_layer_data; - oneshot_layer_data &= ~state; - if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) { - layer_off(get_oneshot_layer()); - reset_oneshot_layer(); - } -} -/** \brief Is oneshot layer active - * - * FIXME: needs doc - */ -bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); } - -/** \brief set oneshot - * - * FIXME: needs doc - */ -void oneshot_set(bool active) { - if (keymap_config.oneshot_disable != active) { - keymap_config.oneshot_disable = active; - eeconfig_update_keymap(keymap_config.raw); - dprintf("Oneshot: active: %d\n", active); - } -} - -/** \brief toggle oneshot - * - * FIXME: needs doc - */ -void oneshot_toggle(void) { oneshot_set(!keymap_config.oneshot_disable); } - -/** \brief enable oneshot - * - * FIXME: needs doc - */ -void oneshot_enable(void) { oneshot_set(true); } - -/** \brief disable oneshot - * - * FIXME: needs doc - */ -void oneshot_disable(void) { oneshot_set(false); } - -bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; } - -#endif - -/** \brief Send keyboard report - * - * FIXME: needs doc - */ -void send_keyboard_report(void) { - keyboard_report->mods = real_mods; - keyboard_report->mods |= weak_mods; - keyboard_report->mods |= macro_mods; - -#ifndef NO_ACTION_ONESHOT - if (oneshot_mods) { -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - if (has_oneshot_mods_timed_out()) { - dprintf("Oneshot: timeout\n"); - clear_oneshot_mods(); - } -# endif - keyboard_report->mods |= oneshot_mods; - if (has_anykey(keyboard_report)) { - clear_oneshot_mods(); - } - } - -#endif - -#ifdef KEY_OVERRIDE_ENABLE - // These need to be last to be able to properly control key overrides - keyboard_report->mods &= ~suppressed_mods; - keyboard_report->mods |= weak_override_mods; -#endif - - host_keyboard_send(keyboard_report); -} - -/** \brief Get mods - * - * FIXME: needs doc - */ -uint8_t get_mods(void) { return real_mods; } -/** \brief add mods - * - * FIXME: needs doc - */ -void add_mods(uint8_t mods) { real_mods |= mods; } -/** \brief del mods - * - * FIXME: needs doc - */ -void del_mods(uint8_t mods) { real_mods &= ~mods; } -/** \brief set mods - * - * FIXME: needs doc - */ -void set_mods(uint8_t mods) { real_mods = mods; } -/** \brief clear mods - * - * FIXME: needs doc - */ -void clear_mods(void) { real_mods = 0; } - -/** \brief get weak mods - * - * FIXME: needs doc - */ -uint8_t get_weak_mods(void) { return weak_mods; } -/** \brief add weak mods - * - * FIXME: needs doc - */ -void add_weak_mods(uint8_t mods) { weak_mods |= mods; } -/** \brief del weak mods - * - * FIXME: needs doc - */ -void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; } -/** \brief set weak mods - * - * FIXME: needs doc - */ -void set_weak_mods(uint8_t mods) { weak_mods = mods; } -/** \brief clear weak mods - * - * FIXME: needs doc - */ -void clear_weak_mods(void) { weak_mods = 0; } - -#ifdef KEY_OVERRIDE_ENABLE -/** \brief set weak mods used by key overrides. DO not call this manually - */ -void set_weak_override_mods(uint8_t mods) { weak_override_mods = mods; } -/** \brief clear weak mods used by key overrides. DO not call this manually - */ -void clear_weak_override_mods(void) { weak_override_mods = 0; } - -/** \brief set suppressed mods used by key overrides. DO not call this manually - */ -void set_suppressed_override_mods(uint8_t mods) { suppressed_mods = mods; } -/** \brief clear suppressed mods used by key overrides. DO not call this manually - */ -void clear_suppressed_override_mods(void) { suppressed_mods = 0; } -#endif - -/* macro modifier */ -/** \brief get macro mods - * - * FIXME: needs doc - */ -uint8_t get_macro_mods(void) { return macro_mods; } -/** \brief add macro mods - * - * FIXME: needs doc - */ -void add_macro_mods(uint8_t mods) { macro_mods |= mods; } -/** \brief del macro mods - * - * FIXME: needs doc - */ -void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; } -/** \brief set macro mods - * - * FIXME: needs doc - */ -void set_macro_mods(uint8_t mods) { macro_mods = mods; } -/** \brief clear macro mods - * - * FIXME: needs doc - */ -void clear_macro_mods(void) { macro_mods = 0; } - -#ifndef NO_ACTION_ONESHOT -/** \brief get oneshot mods - * - * FIXME: needs doc - */ -uint8_t get_oneshot_mods(void) { return oneshot_mods; } - -void add_oneshot_mods(uint8_t mods) { - if ((oneshot_mods & mods) != mods) { -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_time = timer_read(); -# endif - oneshot_mods |= mods; - oneshot_mods_changed_kb(mods); - } -} - -void del_oneshot_mods(uint8_t mods) { - if (oneshot_mods & mods) { - oneshot_mods &= ~mods; -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_time = oneshot_mods ? timer_read() : 0; -# endif - oneshot_mods_changed_kb(oneshot_mods); - } -} - -/** \brief set oneshot mods - * - * FIXME: needs doc - */ -void set_oneshot_mods(uint8_t mods) { - if (!keymap_config.oneshot_disable) { - if (oneshot_mods != mods) { -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_time = timer_read(); -# endif - oneshot_mods = mods; - oneshot_mods_changed_kb(mods); - } - } -} - -/** \brief clear oneshot mods - * - * FIXME: needs doc - */ -void clear_oneshot_mods(void) { - if (oneshot_mods) { - oneshot_mods = 0; -# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) - oneshot_time = 0; -# endif - oneshot_mods_changed_kb(oneshot_mods); - } -} -#endif - -/** \brief Called when the one shot modifiers have been changed. - * - * \param mods Contains the active modifiers active after the change. - */ -__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {} - -/** \brief Called when the locked one shot modifiers have been changed. - * - * \param mods Contains the active modifiers active after the change. - */ -__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) { oneshot_locked_mods_changed_user(mods); } - -/** \brief Called when the one shot modifiers have been changed. - * - * \param mods Contains the active modifiers active after the change. - */ -__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {} - -/** \brief Called when the one shot modifiers have been changed. - * - * \param mods Contains the active modifiers active after the change. - */ -__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) { oneshot_mods_changed_user(mods); } - -/** \brief Called when the one shot layers have been changed. - * - * \param layer Contains the layer that is toggled on, or zero when toggled off. - */ -__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {} - -/** \brief Called when the one shot layers have been changed. - * - * \param layer Contains the layer that is toggled on, or zero when toggled off. - */ -__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) { oneshot_layer_changed_user(layer); } - -/** \brief inspect keyboard state - * - * FIXME: needs doc - */ -uint8_t has_anymod(void) { return bitpop(real_mods); } diff --git a/tmk_core/common/action_util.h b/tmk_core/common/action_util.h deleted file mode 100644 index f2b3897ae5..0000000000 --- a/tmk_core/common/action_util.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include -#include "report.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern report_keyboard_t *keyboard_report; - -void send_keyboard_report(void); - -/* key */ -inline void add_key(uint8_t key) { add_key_to_report(keyboard_report, key); } - -inline void del_key(uint8_t key) { del_key_from_report(keyboard_report, key); } - -inline void clear_keys(void) { clear_keys_from_report(keyboard_report); } - -/* modifier */ -uint8_t get_mods(void); -void add_mods(uint8_t mods); -void del_mods(uint8_t mods); -void set_mods(uint8_t mods); -void clear_mods(void); - -/* weak modifier */ -uint8_t get_weak_mods(void); -void add_weak_mods(uint8_t mods); -void del_weak_mods(uint8_t mods); -void set_weak_mods(uint8_t mods); -void clear_weak_mods(void); - -/* macro modifier */ -uint8_t get_macro_mods(void); -void add_macro_mods(uint8_t mods); -void del_macro_mods(uint8_t mods); -void set_macro_mods(uint8_t mods); -void clear_macro_mods(void); - -/* oneshot modifier */ -uint8_t get_oneshot_mods(void); -void add_oneshot_mods(uint8_t mods); -void del_oneshot_mods(uint8_t mods); -void set_oneshot_mods(uint8_t mods); -void clear_oneshot_mods(void); -bool has_oneshot_mods_timed_out(void); - -uint8_t get_oneshot_locked_mods(void); -void set_oneshot_locked_mods(uint8_t mods); -void clear_oneshot_locked_mods(void); - -typedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t; -void set_oneshot_layer(uint8_t layer, uint8_t state); -uint8_t get_oneshot_layer(void); -void clear_oneshot_layer_state(oneshot_fullfillment_t state); -void reset_oneshot_layer(void); -bool is_oneshot_layer_active(void); -uint8_t get_oneshot_layer_state(void); -bool has_oneshot_layer_timed_out(void); -bool has_oneshot_swaphands_timed_out(void); - -void oneshot_locked_mods_changed_user(uint8_t mods); -void oneshot_locked_mods_changed_kb(uint8_t mods); -void oneshot_mods_changed_user(uint8_t mods); -void oneshot_mods_changed_kb(uint8_t mods); -void oneshot_layer_changed_user(uint8_t layer); -void oneshot_layer_changed_kb(uint8_t layer); - -void oneshot_toggle(void); -void oneshot_enable(void); -void oneshot_disable(void); -bool is_oneshot_enabled(void); - -/* inspect */ -uint8_t has_anymod(void); - -#ifdef SWAP_HANDS_ENABLE -void set_oneshot_swaphands(void); -void release_oneshot_swaphands(void); -void use_oneshot_swaphands(void); -void clear_oneshot_swaphands(void); -#endif - -#ifdef __cplusplus -} -#endif diff --git a/tmk_core/common/eeconfig.c b/tmk_core/common/eeconfig.c deleted file mode 100644 index ffa56ab56d..0000000000 --- a/tmk_core/common/eeconfig.c +++ /dev/null @@ -1,211 +0,0 @@ -#include -#include -#include "eeprom.h" -#include "eeconfig.h" -#include "action_layer.h" - -#ifdef STM32_EEPROM_ENABLE -# include -# include "eeprom_stm32.h" -#endif - -#if defined(EEPROM_DRIVER) -# include "eeprom_driver.h" -#endif - -#if defined(HAPTIC_ENABLE) -# include "haptic.h" -#endif - -/** \brief eeconfig enable - * - * FIXME: needs doc - */ -__attribute__((weak)) void eeconfig_init_user(void) { - // Reset user EEPROM value to blank, rather than to a set value - eeconfig_update_user(0); -} - -__attribute__((weak)) void eeconfig_init_kb(void) { - // Reset Keyboard EEPROM value to blank, rather than to a set value - eeconfig_update_kb(0); - - eeconfig_init_user(); -} - -/* - * FIXME: needs doc - */ -void eeconfig_init_quantum(void) { -#ifdef STM32_EEPROM_ENABLE - EEPROM_Erase(); -#endif -#if defined(EEPROM_DRIVER) - eeprom_driver_erase(); -#endif - eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); - eeprom_update_byte(EECONFIG_DEBUG, 0); - eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0); - default_layer_state = 0; - eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0); - eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0); - eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0); - eeprom_update_byte(EECONFIG_BACKLIGHT, 0); - eeprom_update_byte(EECONFIG_AUDIO, 0xFF); // On by default - eeprom_update_dword(EECONFIG_RGBLIGHT, 0); - eeprom_update_byte(EECONFIG_STENOMODE, 0); - eeprom_update_dword(EECONFIG_HAPTIC, 0); - eeprom_update_byte(EECONFIG_VELOCIKEY, 0); - eeprom_update_dword(EECONFIG_RGB_MATRIX, 0); - eeprom_update_word(EECONFIG_RGB_MATRIX_EXTENDED, 0); - - // TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS - // within the emulated eeprom via dfu-util or another tool -#if defined INIT_EE_HANDS_LEFT -# pragma message "Faking EE_HANDS for left hand" - eeprom_update_byte(EECONFIG_HANDEDNESS, 1); -#elif defined INIT_EE_HANDS_RIGHT -# pragma message "Faking EE_HANDS for right hand" - eeprom_update_byte(EECONFIG_HANDEDNESS, 0); -#endif - -#if defined(HAPTIC_ENABLE) - haptic_reset(); -#else - // this is used in case haptic is disabled, but we still want sane defaults - // in the haptic configuration eeprom. All zero will trigger a haptic_reset - // when a haptic-enabled firmware is loaded onto the keyboard. - eeprom_update_dword(EECONFIG_HAPTIC, 0); -#endif - - eeconfig_init_kb(); -} - -/** \brief eeconfig initialization - * - * FIXME: needs doc - */ -void eeconfig_init(void) { eeconfig_init_quantum(); } - -/** \brief eeconfig enable - * - * FIXME: needs doc - */ -void eeconfig_enable(void) { eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); } - -/** \brief eeconfig disable - * - * FIXME: needs doc - */ -void eeconfig_disable(void) { -#ifdef STM32_EEPROM_ENABLE - EEPROM_Erase(); -#endif -#if defined(EEPROM_DRIVER) - eeprom_driver_erase(); -#endif - eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF); -} - -/** \brief eeconfig is enabled - * - * FIXME: needs doc - */ -bool eeconfig_is_enabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); } - -/** \brief eeconfig is disabled - * - * FIXME: needs doc - */ -bool eeconfig_is_disabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF); } - -/** \brief eeconfig read debug - * - * FIXME: needs doc - */ -uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); } -/** \brief eeconfig update debug - * - * FIXME: needs doc - */ -void eeconfig_update_debug(uint8_t val) { eeprom_update_byte(EECONFIG_DEBUG, val); } - -/** \brief eeconfig read default layer - * - * FIXME: needs doc - */ -uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); } -/** \brief eeconfig update default layer - * - * FIXME: needs doc - */ -void eeconfig_update_default_layer(uint8_t val) { eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val); } - -/** \brief eeconfig read keymap - * - * FIXME: needs doc - */ -uint16_t eeconfig_read_keymap(void) { return (eeprom_read_byte(EECONFIG_KEYMAP_LOWER_BYTE) | (eeprom_read_byte(EECONFIG_KEYMAP_UPPER_BYTE) << 8)); } -/** \brief eeconfig update keymap - * - * FIXME: needs doc - */ -void eeconfig_update_keymap(uint16_t val) { - eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, val & 0xFF); - eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, (val >> 8) & 0xFF); -} - -/** \brief eeconfig read audio - * - * FIXME: needs doc - */ -uint8_t eeconfig_read_audio(void) { return eeprom_read_byte(EECONFIG_AUDIO); } -/** \brief eeconfig update audio - * - * FIXME: needs doc - */ -void eeconfig_update_audio(uint8_t val) { eeprom_update_byte(EECONFIG_AUDIO, val); } - -/** \brief eeconfig read kb - * - * FIXME: needs doc - */ -uint32_t eeconfig_read_kb(void) { return eeprom_read_dword(EECONFIG_KEYBOARD); } -/** \brief eeconfig update kb - * - * FIXME: needs doc - */ -void eeconfig_update_kb(uint32_t val) { eeprom_update_dword(EECONFIG_KEYBOARD, val); } - -/** \brief eeconfig read user - * - * FIXME: needs doc - */ -uint32_t eeconfig_read_user(void) { return eeprom_read_dword(EECONFIG_USER); } -/** \brief eeconfig update user - * - * FIXME: needs doc - */ -void eeconfig_update_user(uint32_t val) { eeprom_update_dword(EECONFIG_USER, val); } - -/** \brief eeconfig read haptic - * - * FIXME: needs doc - */ -uint32_t eeconfig_read_haptic(void) { return eeprom_read_dword(EECONFIG_HAPTIC); } -/** \brief eeconfig update haptic - * - * FIXME: needs doc - */ -void eeconfig_update_haptic(uint32_t val) { eeprom_update_dword(EECONFIG_HAPTIC, val); } - -/** \brief eeconfig read split handedness - * - * FIXME: needs doc - */ -bool eeconfig_read_handedness(void) { return !!eeprom_read_byte(EECONFIG_HANDEDNESS); } -/** \brief eeconfig update split handedness - * - * FIXME: needs doc - */ -void eeconfig_update_handedness(bool val) { eeprom_update_byte(EECONFIG_HANDEDNESS, !!val); } diff --git a/tmk_core/common/eeconfig.h b/tmk_core/common/eeconfig.h deleted file mode 100644 index a88071729d..0000000000 --- a/tmk_core/common/eeconfig.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include -#include - -#ifndef EECONFIG_MAGIC_NUMBER -# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA // When changing, decrement this value to avoid future re-init issues -#endif -#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF - -/* EEPROM parameter address */ -#define EECONFIG_MAGIC (uint16_t *)0 -#define EECONFIG_DEBUG (uint8_t *)2 -#define EECONFIG_DEFAULT_LAYER (uint8_t *)3 -#define EECONFIG_KEYMAP (uint8_t *)4 -#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5 -#define EECONFIG_BACKLIGHT (uint8_t *)6 -#define EECONFIG_AUDIO (uint8_t *)7 -#define EECONFIG_RGBLIGHT (uint32_t *)8 -#define EECONFIG_UNICODEMODE (uint8_t *)12 -#define EECONFIG_STENOMODE (uint8_t *)13 -// EEHANDS for two handed boards -#define EECONFIG_HANDEDNESS (uint8_t *)14 -#define EECONFIG_KEYBOARD (uint32_t *)15 -#define EECONFIG_USER (uint32_t *)19 -#define EECONFIG_VELOCIKEY (uint8_t *)23 - -#define EECONFIG_HAPTIC (uint32_t *)24 - -// Mutually exclusive -#define EECONFIG_LED_MATRIX (uint32_t *)28 -#define EECONFIG_RGB_MATRIX (uint32_t *)28 -// Speed & Flags -#define EECONFIG_LED_MATRIX_EXTENDED (uint16_t *)32 -#define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32 - -// TODO: Combine these into a single word and single block of EEPROM -#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34 -// Size of EEPROM being used, other code can refer to this for available EEPROM -#define EECONFIG_SIZE 35 -/* debug bit */ -#define EECONFIG_DEBUG_ENABLE (1 << 0) -#define EECONFIG_DEBUG_MATRIX (1 << 1) -#define EECONFIG_DEBUG_KEYBOARD (1 << 2) -#define EECONFIG_DEBUG_MOUSE (1 << 3) - -/* keyconf bit */ -#define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0) -#define EECONFIG_KEYMAP_CAPSLOCK_TO_CONTROL (1 << 1) -#define EECONFIG_KEYMAP_SWAP_LALT_LGUI (1 << 2) -#define EECONFIG_KEYMAP_SWAP_RALT_RGUI (1 << 3) -#define EECONFIG_KEYMAP_NO_GUI (1 << 4) -#define EECONFIG_KEYMAP_SWAP_GRAVE_ESC (1 << 5) -#define EECONFIG_KEYMAP_SWAP_BACKSLASH_BACKSPACE (1 << 6) -#define EECONFIG_KEYMAP_NKRO (1 << 7) - -#define EECONFIG_KEYMAP_LOWER_BYTE EECONFIG_KEYMAP - -bool eeconfig_is_enabled(void); -bool eeconfig_is_disabled(void); - -void eeconfig_init(void); -void eeconfig_init_quantum(void); -void eeconfig_init_kb(void); -void eeconfig_init_user(void); - -void eeconfig_enable(void); - -void eeconfig_disable(void); - -uint8_t eeconfig_read_debug(void); -void eeconfig_update_debug(uint8_t val); - -uint8_t eeconfig_read_default_layer(void); -void eeconfig_update_default_layer(uint8_t val); - -uint16_t eeconfig_read_keymap(void); -void eeconfig_update_keymap(uint16_t val); - -#ifdef AUDIO_ENABLE -uint8_t eeconfig_read_audio(void); -void eeconfig_update_audio(uint8_t val); -#endif - -uint32_t eeconfig_read_kb(void); -void eeconfig_update_kb(uint32_t val); -uint32_t eeconfig_read_user(void); -void eeconfig_update_user(uint32_t val); - -#ifdef HAPTIC_ENABLE -uint32_t eeconfig_read_haptic(void); -void eeconfig_update_haptic(uint32_t val); -#endif - -bool eeconfig_read_handedness(void); -void eeconfig_update_handedness(bool val); diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c deleted file mode 100644 index fcc51973fd..0000000000 --- a/tmk_core/common/keyboard.c +++ /dev/null @@ -1,565 +0,0 @@ -/* -Copyright 2011, 2012, 2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include -#include "keyboard.h" -#include "matrix.h" -#include "keymap.h" -#include "host.h" -#include "led.h" -#include "keycode.h" -#include "timer.h" -#include "sync_timer.h" -#include "print.h" -#include "debug.h" -#include "command.h" -#include "util.h" -#include "sendchar.h" -#include "eeconfig.h" -#include "action_layer.h" -#ifdef BACKLIGHT_ENABLE -# include "backlight.h" -#endif -#ifdef MOUSEKEY_ENABLE -# include "mousekey.h" -#endif -#ifdef PS2_MOUSE_ENABLE -# include "ps2_mouse.h" -#endif -#ifdef SERIAL_MOUSE_ENABLE -# include "serial_mouse.h" -#endif -#ifdef ADB_MOUSE_ENABLE -# include "adb.h" -#endif -#ifdef RGBLIGHT_ENABLE -# include "rgblight.h" -#endif -#ifdef LED_MATRIX_ENABLE -# include "led_matrix.h" -#endif -#ifdef RGB_MATRIX_ENABLE -# include "rgb_matrix.h" -#endif -#ifdef ENCODER_ENABLE -# include "encoder.h" -#endif -#ifdef STENO_ENABLE -# include "process_steno.h" -#endif -#ifdef SERIAL_LINK_ENABLE -# include "serial_link/system/serial_link.h" -#endif -#ifdef VISUALIZER_ENABLE -# include "visualizer/visualizer.h" -#endif -#ifdef POINTING_DEVICE_ENABLE -# include "pointing_device.h" -#endif -#ifdef MIDI_ENABLE -# include "process_midi.h" -#endif -#ifdef JOYSTICK_ENABLE -# include "process_joystick.h" -#endif -#ifdef HD44780_ENABLE -# include "hd44780.h" -#endif -#ifdef QWIIC_ENABLE -# include "qwiic.h" -#endif -#ifdef OLED_DRIVER_ENABLE -# include "oled_driver.h" -#endif -#ifdef ST7565_ENABLE -# include "st7565.h" -#endif -#ifdef VELOCIKEY_ENABLE -# include "velocikey.h" -#endif -#ifdef VIA_ENABLE -# include "via.h" -#endif -#ifdef DIP_SWITCH_ENABLE -# include "dip_switch.h" -#endif -#ifdef STM32_EEPROM_ENABLE -# include "eeprom_stm32.h" -#endif -#ifdef EEPROM_DRIVER -# include "eeprom_driver.h" -#endif -#if defined(CRC_ENABLE) -# include "crc.h" -#endif -#ifdef DIGITIZER_ENABLE -# include "digitizer.h" -#endif - -static uint32_t last_input_modification_time = 0; -uint32_t last_input_activity_time(void) { return last_input_modification_time; } -uint32_t last_input_activity_elapsed(void) { return timer_elapsed32(last_input_modification_time); } - -static uint32_t last_matrix_modification_time = 0; -uint32_t last_matrix_activity_time(void) { return last_matrix_modification_time; } -uint32_t last_matrix_activity_elapsed(void) { return timer_elapsed32(last_matrix_modification_time); } -void last_matrix_activity_trigger(void) { last_matrix_modification_time = last_input_modification_time = timer_read32(); } - -static uint32_t last_encoder_modification_time = 0; -uint32_t last_encoder_activity_time(void) { return last_encoder_modification_time; } -uint32_t last_encoder_activity_elapsed(void) { return timer_elapsed32(last_encoder_modification_time); } -void last_encoder_activity_trigger(void) { last_encoder_modification_time = last_input_modification_time = timer_read32(); } - -// Only enable this if console is enabled to print to -#if defined(DEBUG_MATRIX_SCAN_RATE) -static uint32_t matrix_timer = 0; -static uint32_t matrix_scan_count = 0; -static uint32_t last_matrix_scan_count = 0; - -void matrix_scan_perf_task(void) { - matrix_scan_count++; - - uint32_t timer_now = timer_read32(); - if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) { -# if defined(CONSOLE_ENABLE) - dprintf("matrix scan frequency: %lu\n", matrix_scan_count); -# endif - last_matrix_scan_count = matrix_scan_count; - matrix_timer = timer_now; - matrix_scan_count = 0; - } -} - -uint32_t get_matrix_scan_rate(void) { return last_matrix_scan_count; } -#else -# define matrix_scan_perf_task() -#endif - -#ifdef MATRIX_HAS_GHOST -extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; -static matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) { - matrix_row_t out = 0; - for (uint8_t col = 0; col < MATRIX_COLS; col++) { - // read each key in the row data and check if the keymap defines it as a real key - if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) { - // this creates new row data, if a key is defined in the keymap, it will be set here - out |= 1 << col; - } - } - return out; -} - -static inline bool popcount_more_than_one(matrix_row_t rowdata) { - rowdata &= rowdata - 1; // if there are less than two bits (keys) set, rowdata will become zero - return rowdata; -} - -static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) { - /* No ghost exists when less than 2 keys are down on the row. - If there are "active" blanks in the matrix, the key can't be pressed by the user, - there is no doubt as to which keys are really being pressed. - The ghosts will be ignored, they are KC_NO. */ - rowdata = get_real_keys(row, rowdata); - if ((popcount_more_than_one(rowdata)) == 0) { - return false; - } - /* Ghost occurs when the row shares a column line with other row, - and two columns are read on each row. Blanks in the matrix don't matter, - so they are filtered out. - If there are two or more real keys pressed and they match columns with - at least two of another row's real keys, the row will be ignored. Keep in mind, - we are checking one row at a time, not all of them at once. - */ - for (uint8_t i = 0; i < MATRIX_ROWS; i++) { - if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) { - return true; - } - } - return false; -} - -#endif - -void disable_jtag(void) { -// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles. -#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) - MCUCR |= _BV(JTD); - MCUCR |= _BV(JTD); -#elif defined(__AVR_ATmega32A__) - MCUCSR |= _BV(JTD); - MCUCSR |= _BV(JTD); -#endif -} - -/** \brief matrix_setup - * - * FIXME: needs doc - */ -__attribute__((weak)) void matrix_setup(void) {} - -/** \brief keyboard_pre_init_user - * - * FIXME: needs doc - */ -__attribute__((weak)) void keyboard_pre_init_user(void) {} - -/** \brief keyboard_pre_init_kb - * - * FIXME: needs doc - */ -__attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); } - -/** \brief keyboard_post_init_user - * - * FIXME: needs doc - */ - -__attribute__((weak)) void keyboard_post_init_user() {} - -/** \brief keyboard_post_init_kb - * - * FIXME: needs doc - */ - -__attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); } - -/** \brief keyboard_setup - * - * FIXME: needs doc - */ -void keyboard_setup(void) { -#ifndef NO_JTAG_DISABLE - disable_jtag(); -#endif - print_set_sendchar(sendchar); -#ifdef STM32_EEPROM_ENABLE - EEPROM_Init(); -#endif -#ifdef EEPROM_DRIVER - eeprom_driver_init(); -#endif - matrix_setup(); - keyboard_pre_init_kb(); -} - -/** \brief is_keyboard_master - * - * FIXME: needs doc - */ -__attribute__((weak)) bool is_keyboard_master(void) { return true; } - -/** \brief is_keyboard_left - * - * FIXME: needs doc - */ -__attribute__((weak)) bool is_keyboard_left(void) { return true; } - -/** \brief should_process_keypress - * - * Override this function if you have a condition where keypresses processing should change: - * - splits where the slave side needs to process for rgb/oled functionality - */ -__attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); } - -/** \brief housekeeping_task_kb - * - * Override this function if you have a need to execute code for every keyboard main loop iteration. - * This is specific to keyboard-level functionality. - */ -__attribute__((weak)) void housekeeping_task_kb(void) {} - -/** \brief housekeeping_task_user - * - * Override this function if you have a need to execute code for every keyboard main loop iteration. - * This is specific to user/keymap-level functionality. - */ -__attribute__((weak)) void housekeeping_task_user(void) {} - -/** \brief housekeeping_task - * - * Invokes hooks for executing code after QMK is done after each loop iteration. - */ -void housekeeping_task(void) { - housekeeping_task_kb(); - housekeeping_task_user(); -} - -/** \brief keyboard_init - * - * FIXME: needs doc - */ -void keyboard_init(void) { - timer_init(); - sync_timer_init(); - matrix_init(); -#if defined(CRC_ENABLE) - crc_init(); -#endif -#ifdef VIA_ENABLE - via_init(); -#endif -#ifdef QWIIC_ENABLE - qwiic_init(); -#endif -#ifdef OLED_DRIVER_ENABLE - oled_init(OLED_ROTATION_0); -#endif -#ifdef ST7565_ENABLE - st7565_init(DISPLAY_ROTATION_0); -#endif -#ifdef PS2_MOUSE_ENABLE - ps2_mouse_init(); -#endif -#ifdef SERIAL_MOUSE_ENABLE - serial_mouse_init(); -#endif -#ifdef ADB_MOUSE_ENABLE - adb_mouse_init(); -#endif -#ifdef BACKLIGHT_ENABLE - backlight_init(); -#endif -#ifdef RGBLIGHT_ENABLE - rgblight_init(); -#endif -#ifdef ENCODER_ENABLE - encoder_init(); -#endif -#ifdef STENO_ENABLE - steno_init(); -#endif -#ifdef POINTING_DEVICE_ENABLE - pointing_device_init(); -#endif -#if defined(NKRO_ENABLE) && defined(FORCE_NKRO) - keymap_config.nkro = 1; - eeconfig_update_keymap(keymap_config.raw); -#endif -#ifdef DIP_SWITCH_ENABLE - dip_switch_init(); -#endif - -#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE) - debug_enable = true; -#endif - - keyboard_post_init_kb(); /* Always keep this last */ -} - -/** \brief key_event_task - * - * This function is responsible for calling into other systems when they need to respond to electrical switch press events. - * This is differnet than keycode events as no layer processing, or filtering occurs. - */ -void switch_events(uint8_t row, uint8_t col, bool pressed) { -#if defined(LED_MATRIX_ENABLE) - process_led_matrix(row, col, pressed); -#endif -#if defined(RGB_MATRIX_ENABLE) - process_rgb_matrix(row, col, pressed); -#endif -} - -/** \brief Keyboard task: Do keyboard routine jobs - * - * Do routine keyboard jobs: - * - * * scan matrix - * * handle mouse movements - * * run visualizer code - * * handle midi commands - * * light LEDs - * - * This is repeatedly called as fast as possible. - */ -void keyboard_task(void) { - static matrix_row_t matrix_prev[MATRIX_ROWS]; - static uint8_t led_status = 0; - matrix_row_t matrix_row = 0; - matrix_row_t matrix_change = 0; -#ifdef QMK_KEYS_PER_SCAN - uint8_t keys_processed = 0; -#endif -#ifdef ENCODER_ENABLE - bool encoders_changed = false; -#endif - - uint8_t matrix_changed = matrix_scan(); - if (matrix_changed) last_matrix_activity_trigger(); - - for (uint8_t r = 0; r < MATRIX_ROWS; r++) { - matrix_row = matrix_get_row(r); - matrix_change = matrix_row ^ matrix_prev[r]; - if (matrix_change) { -#ifdef MATRIX_HAS_GHOST - if (has_ghost_in_row(r, matrix_row)) { - continue; - } -#endif - if (debug_matrix) matrix_print(); - matrix_row_t col_mask = 1; - for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) { - if (matrix_change & col_mask) { - if (should_process_keypress()) { - action_exec((keyevent_t){ - .key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */ - }); - } - // record a processed key - matrix_prev[r] ^= col_mask; - - switch_events(r, c, (matrix_row & col_mask)); - -#ifdef QMK_KEYS_PER_SCAN - // only jump out if we have processed "enough" keys. - if (++keys_processed >= QMK_KEYS_PER_SCAN) -#endif - // process a key per task call - goto MATRIX_LOOP_END; - } - } - } - } - // call with pseudo tick event when no real key event. -#ifdef QMK_KEYS_PER_SCAN - // we can get here with some keys processed now. - if (!keys_processed) -#endif - action_exec(TICK); - -MATRIX_LOOP_END: - -#ifdef DEBUG_MATRIX_SCAN_RATE - matrix_scan_perf_task(); -#endif - -#if defined(RGBLIGHT_ENABLE) - rgblight_task(); -#endif - -#ifdef LED_MATRIX_ENABLE - led_matrix_task(); -#endif -#ifdef RGB_MATRIX_ENABLE - rgb_matrix_task(); -#endif - -#if defined(BACKLIGHT_ENABLE) -# if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS) - backlight_task(); -# endif -#endif - -#ifdef ENCODER_ENABLE - encoders_changed = encoder_read(); - if (encoders_changed) last_encoder_activity_trigger(); -#endif - -#ifdef QWIIC_ENABLE - qwiic_task(); -#endif - -#ifdef OLED_DRIVER_ENABLE - oled_task(); -# ifndef OLED_DISABLE_TIMEOUT - // Wake up oled if user is using those fabulous keys or spinning those encoders! -# ifdef ENCODER_ENABLE - if (matrix_changed || encoders_changed) oled_on(); -# else - if (matrix_changed) oled_on(); -# endif -# endif -#endif - -#ifdef ST7565_ENABLE - st7565_task(); -# ifndef ST7565_DISABLE_TIMEOUT - // Wake up display if user is using those fabulous keys or spinning those encoders! -# ifdef ENCODER_ENABLE - if (matrix_changed || encoders_changed) st7565_on(); -# else - if (matrix_changed) st7565_on(); -# endif -# endif -#endif - -#ifdef MOUSEKEY_ENABLE - // mousekey repeat & acceleration - mousekey_task(); -#endif - -#ifdef PS2_MOUSE_ENABLE - ps2_mouse_task(); -#endif - -#ifdef SERIAL_MOUSE_ENABLE - serial_mouse_task(); -#endif - -#ifdef ADB_MOUSE_ENABLE - adb_mouse_task(); -#endif - -#ifdef SERIAL_LINK_ENABLE - serial_link_update(); -#endif - -#ifdef VISUALIZER_ENABLE - visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds()); -#endif - -#ifdef POINTING_DEVICE_ENABLE - pointing_device_task(); -#endif - -#ifdef MIDI_ENABLE - midi_task(); -#endif - -#ifdef VELOCIKEY_ENABLE - if (velocikey_enabled()) { - velocikey_decelerate(); - } -#endif - -#ifdef JOYSTICK_ENABLE - joystick_task(); -#endif - -#ifdef DIGITIZER_ENABLE - digitizer_task(); -#endif - - // update LED - if (led_status != host_keyboard_leds()) { - led_status = host_keyboard_leds(); - keyboard_set_leds(led_status); - } -} - -/** \brief keyboard set leds - * - * FIXME: needs doc - */ -void keyboard_set_leds(uint8_t leds) { - if (debug_keyboard) { - debug("keyboard_set_led: "); - debug_hex8(leds); - debug("\n"); - } - led_set(leds); -} diff --git a/tmk_core/common/keyboard.h b/tmk_core/common/keyboard.h deleted file mode 100644 index 08f4e84f94..0000000000 --- a/tmk_core/common/keyboard.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2011,2012,2013 Jun Wako - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* key matrix position */ -typedef struct { - uint8_t col; - uint8_t row; -} keypos_t; - -/* key event */ -typedef struct { - keypos_t key; - bool pressed; - uint16_t time; -} keyevent_t; - -/* equivalent test of keypos_t */ -#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col) - -/* Rules for No Event: - * 1) (time == 0) to handle (keyevent_t){} as empty event - * 2) Matrix(255, 255) to make TICK event available - */ -static inline bool IS_NOEVENT(keyevent_t event) { return event.time == 0 || (event.key.row == 255 && event.key.col == 255); } -static inline bool IS_PRESSED(keyevent_t event) { return (!IS_NOEVENT(event) && event.pressed); } -static inline bool IS_RELEASED(keyevent_t event) { return (!IS_NOEVENT(event) && !event.pressed); } - -/* Tick event */ -#define TICK \ - (keyevent_t) { .key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) } - -/* it runs once at early stage of startup before keyboard_init. */ -void keyboard_setup(void); -/* it runs once after initializing host side protocol, debug and MCU peripherals. */ -void keyboard_init(void); -/* it runs repeatedly in main loop */ -void keyboard_task(void); -/* it runs when host LED status is updated */ -void keyboard_set_leds(uint8_t leds); -/* it runs whenever code has to behave differently on a slave */ -bool is_keyboard_master(void); -/* it runs whenever code has to behave differently on left vs right split */ -bool is_keyboard_left(void); - -void keyboard_pre_init_kb(void); -void keyboard_pre_init_user(void); -void keyboard_post_init_kb(void); -void keyboard_post_init_user(void); - -void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol -void housekeeping_task_kb(void); // To be overridden by keyboard-level code -void housekeeping_task_user(void); // To be overridden by user/keymap-level code - -uint32_t last_input_activity_time(void); // Timestamp of the last matrix or encoder activity -uint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder activity - -uint32_t last_matrix_activity_time(void); // Timestamp of the last matrix activity -uint32_t last_matrix_activity_elapsed(void); // Number of milliseconds since the last matrix activity - -uint32_t last_encoder_activity_time(void); // Timestamp of the last encoder activity -uint32_t last_encoder_activity_elapsed(void); // Number of milliseconds since the last encoder activity - -uint32_t get_matrix_scan_rate(void); - -#ifdef __cplusplus -} -#endif diff --git a/tmk_core/common/keycode.h b/tmk_core/common/keycode.h deleted file mode 100644 index 8facabd818..0000000000 --- a/tmk_core/common/keycode.h +++ /dev/null @@ -1,560 +0,0 @@ -/* -Copyright 2011,2012 Jun Wako - -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 . -*/ - -/* - * Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C) - * - * See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf - * or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older) - */ - -#pragma once - -/* FIXME: Add doxygen comments here */ - -#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED) -#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF) -#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL) -#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI) - -#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF)) -#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE) -#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID) - -#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31) - -#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2) -#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT) -#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN8) -#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT) -#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2) - -#define MOD_BIT(code) (1 << MOD_INDEX(code)) -#define MOD_INDEX(code) ((code)&0x07) - -#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL)) -#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) -#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT)) -#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI)) -#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT) -#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT) -#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI) -#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT) -#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI) -#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI) -#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT) -#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI) -#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI) -#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) -#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) - -#define FN_BIT(code) (1 << FN_INDEX(code)) -#define FN_INDEX(code) ((code)-KC_FN0) -#define FN_MIN KC_FN0 -#define FN_MAX KC_FN31 - -/* - * Short names for ease of definition of keymap - */ -/* Transparent */ -#define KC_TRANSPARENT 0x01 -#define KC_TRNS KC_TRANSPARENT - -/* Punctuation */ -#define KC_ENT KC_ENTER -#define KC_ESC KC_ESCAPE -#define KC_BSPC KC_BSPACE -#define KC_SPC KC_SPACE -#define KC_MINS KC_MINUS -#define KC_EQL KC_EQUAL -#define KC_LBRC KC_LBRACKET -#define KC_RBRC KC_RBRACKET -#define KC_BSLS KC_BSLASH -#define KC_NUHS KC_NONUS_HASH -#define KC_SCLN KC_SCOLON -#define KC_QUOT KC_QUOTE -#define KC_GRV KC_GRAVE -#define KC_COMM KC_COMMA -#define KC_SLSH KC_SLASH -#define KC_NUBS KC_NONUS_BSLASH - -/* Lock Keys */ -#define KC_CLCK KC_CAPSLOCK -#define KC_CAPS KC_CAPSLOCK -#define KC_SLCK KC_SCROLLLOCK -#define KC_NLCK KC_NUMLOCK -#define KC_LCAP KC_LOCKING_CAPS -#define KC_LNUM KC_LOCKING_NUM -#define KC_LSCR KC_LOCKING_SCROLL - -/* Commands */ -#define KC_PSCR KC_PSCREEN -#define KC_PAUS KC_PAUSE -#define KC_BRK KC_PAUSE -#define KC_INS KC_INSERT -#define KC_DEL KC_DELETE -#define KC_PGDN KC_PGDOWN -#define KC_RGHT KC_RIGHT -#define KC_APP KC_APPLICATION -#define KC_EXEC KC_EXECUTE -#define KC_SLCT KC_SELECT -#define KC_AGIN KC_AGAIN -#define KC_PSTE KC_PASTE -#define KC_ERAS KC_ALT_ERASE -#define KC_CLR KC_CLEAR - -/* Keypad */ -#define KC_PSLS KC_KP_SLASH -#define KC_PAST KC_KP_ASTERISK -#define KC_PMNS KC_KP_MINUS -#define KC_PPLS KC_KP_PLUS -#define KC_PENT KC_KP_ENTER -#define KC_P1 KC_KP_1 -#define KC_P2 KC_KP_2 -#define KC_P3 KC_KP_3 -#define KC_P4 KC_KP_4 -#define KC_P5 KC_KP_5 -#define KC_P6 KC_KP_6 -#define KC_P7 KC_KP_7 -#define KC_P8 KC_KP_8 -#define KC_P9 KC_KP_9 -#define KC_P0 KC_KP_0 -#define KC_PDOT KC_KP_DOT -#define KC_PEQL KC_KP_EQUAL -#define KC_PCMM KC_KP_COMMA - -/* Japanese specific */ -#define KC_ZKHK KC_GRAVE -#define KC_RO KC_INT1 -#define KC_KANA KC_INT2 -#define KC_JYEN KC_INT3 -#define KC_HENK KC_INT4 -#define KC_MHEN KC_INT5 - -/* Korean specific */ -#define KC_HAEN KC_LANG1 -#define KC_HANJ KC_LANG2 - -/* Modifiers */ -#define KC_LCTL KC_LCTRL -#define KC_LSFT KC_LSHIFT -#define KC_LOPT KC_LALT -#define KC_LCMD KC_LGUI -#define KC_LWIN KC_LGUI -#define KC_RCTL KC_RCTRL -#define KC_RSFT KC_RSHIFT -#define KC_ALGR KC_RALT -#define KC_ROPT KC_RALT -#define KC_RCMD KC_RGUI -#define KC_RWIN KC_RGUI - -/* Generic Desktop Page (0x01) */ -#define KC_PWR KC_SYSTEM_POWER -#define KC_SLEP KC_SYSTEM_SLEEP -#define KC_WAKE KC_SYSTEM_WAKE - -/* Consumer Page (0x0C) */ -#define KC_MUTE KC_AUDIO_MUTE -#define KC_VOLU KC_AUDIO_VOL_UP -#define KC_VOLD KC_AUDIO_VOL_DOWN -#define KC_MNXT KC_MEDIA_NEXT_TRACK -#define KC_MPRV KC_MEDIA_PREV_TRACK -#define KC_MSTP KC_MEDIA_STOP -#define KC_MPLY KC_MEDIA_PLAY_PAUSE -#define KC_MSEL KC_MEDIA_SELECT -#define KC_EJCT KC_MEDIA_EJECT -#define KC_CALC KC_CALCULATOR -#define KC_MYCM KC_MY_COMPUTER -#define KC_WSCH KC_WWW_SEARCH -#define KC_WHOM KC_WWW_HOME -#define KC_WBAK KC_WWW_BACK -#define KC_WFWD KC_WWW_FORWARD -#define KC_WSTP KC_WWW_STOP -#define KC_WREF KC_WWW_REFRESH -#define KC_WFAV KC_WWW_FAVORITES -#define KC_MFFD KC_MEDIA_FAST_FORWARD -#define KC_MRWD KC_MEDIA_REWIND -#define KC_BRIU KC_BRIGHTNESS_UP -#define KC_BRID KC_BRIGHTNESS_DOWN - -/* System Specific */ -#define KC_BRMU KC_PAUSE -#define KC_BRMD KC_SCROLLLOCK - -/* Mouse Keys */ -#define KC_MS_U KC_MS_UP -#define KC_MS_D KC_MS_DOWN -#define KC_MS_L KC_MS_LEFT -#define KC_MS_R KC_MS_RIGHT -#define KC_BTN1 KC_MS_BTN1 -#define KC_BTN2 KC_MS_BTN2 -#define KC_BTN3 KC_MS_BTN3 -#define KC_BTN4 KC_MS_BTN4 -#define KC_BTN5 KC_MS_BTN5 -#define KC_BTN6 KC_MS_BTN6 -#define KC_BTN7 KC_MS_BTN7 -#define KC_BTN8 KC_MS_BTN8 -#define KC_WH_U KC_MS_WH_UP -#define KC_WH_D KC_MS_WH_DOWN -#define KC_WH_L KC_MS_WH_LEFT -#define KC_WH_R KC_MS_WH_RIGHT -#define KC_ACL0 KC_MS_ACCEL0 -#define KC_ACL1 KC_MS_ACCEL1 -#define KC_ACL2 KC_MS_ACCEL2 - -/* Keyboard/Keypad Page (0x07) */ -enum hid_keyboard_keypad_usage { - KC_NO = 0x00, - KC_ROLL_OVER, - KC_POST_FAIL, - KC_UNDEFINED, - KC_A, - KC_B, - KC_C, - KC_D, - KC_E, - KC_F, - KC_G, - KC_H, - KC_I, - KC_J, - KC_K, - KC_L, - KC_M, // 0x10 - KC_N, - KC_O, - KC_P, - KC_Q, - KC_R, - KC_S, - KC_T, - KC_U, - KC_V, - KC_W, - KC_X, - KC_Y, - KC_Z, - KC_1, - KC_2, - KC_3, // 0x20 - KC_4, - KC_5, - KC_6, - KC_7, - KC_8, - KC_9, - KC_0, - KC_ENTER, - KC_ESCAPE, - KC_BSPACE, - KC_TAB, - KC_SPACE, - KC_MINUS, - KC_EQUAL, - KC_LBRACKET, - KC_RBRACKET, // 0x30 - KC_BSLASH, - KC_NONUS_HASH, - KC_SCOLON, - KC_QUOTE, - KC_GRAVE, - KC_COMMA, - KC_DOT, - KC_SLASH, - KC_CAPSLOCK, - KC_F1, - KC_F2, - KC_F3, - KC_F4, - KC_F5, - KC_F6, - KC_F7, // 0x40 - KC_F8, - KC_F9, - KC_F10, - KC_F11, - KC_F12, - KC_PSCREEN, - KC_SCROLLLOCK, - KC_PAUSE, - KC_INSERT, - KC_HOME, - KC_PGUP, - KC_DELETE, - KC_END, - KC_PGDOWN, - KC_RIGHT, - KC_LEFT, // 0x50 - KC_DOWN, - KC_UP, - KC_NUMLOCK, - KC_KP_SLASH, - KC_KP_ASTERISK, - KC_KP_MINUS, - KC_KP_PLUS, - KC_KP_ENTER, - KC_KP_1, - KC_KP_2, - KC_KP_3, - KC_KP_4, - KC_KP_5, - KC_KP_6, - KC_KP_7, - KC_KP_8, // 0x60 - KC_KP_9, - KC_KP_0, - KC_KP_DOT, - KC_NONUS_BSLASH, - KC_APPLICATION, - KC_POWER, - KC_KP_EQUAL, - KC_F13, - KC_F14, - KC_F15, - KC_F16, - KC_F17, - KC_F18, - KC_F19, - KC_F20, - KC_F21, // 0x70 - KC_F22, - KC_F23, - KC_F24, - KC_EXECUTE, - KC_HELP, - KC_MENU, - KC_SELECT, - KC_STOP, - KC_AGAIN, - KC_UNDO, - KC_CUT, - KC_COPY, - KC_PASTE, - KC_FIND, - KC__MUTE, - KC__VOLUP, // 0x80 - KC__VOLDOWN, - KC_LOCKING_CAPS, - KC_LOCKING_NUM, - KC_LOCKING_SCROLL, - KC_KP_COMMA, - KC_KP_EQUAL_AS400, - KC_INT1, - KC_INT2, - KC_INT3, - KC_INT4, - KC_INT5, - KC_INT6, - KC_INT7, - KC_INT8, - KC_INT9, - KC_LANG1, // 0x90 - KC_LANG2, - KC_LANG3, - KC_LANG4, - KC_LANG5, - KC_LANG6, - KC_LANG7, - KC_LANG8, - KC_LANG9, - KC_ALT_ERASE, - KC_SYSREQ, - KC_CANCEL, - KC_CLEAR, - KC_PRIOR, - KC_RETURN, - KC_SEPARATOR, - KC_OUT, // 0xA0 - KC_OPER, - KC_CLEAR_AGAIN, - KC_CRSEL, - KC_EXSEL, - -#if 0 - // *************************************************************** - // These keycodes are present in the HID spec, but are * - // nonfunctional on modern OSes. QMK uses this range (0xA5-0xDF) * - // for the media and function keys instead - see below. * - // *************************************************************** - - KC_KP_00 = 0xB0, - KC_KP_000, - KC_THOUSANDS_SEPARATOR, - KC_DECIMAL_SEPARATOR, - KC_CURRENCY_UNIT, - KC_CURRENCY_SUB_UNIT, - KC_KP_LPAREN, - KC_KP_RPAREN, - KC_KP_LCBRACKET, - KC_KP_RCBRACKET, - KC_KP_TAB, - KC_KP_BSPACE, - KC_KP_A, - KC_KP_B, - KC_KP_C, - KC_KP_D, - KC_KP_E, //0xC0 - KC_KP_F, - KC_KP_XOR, - KC_KP_HAT, - KC_KP_PERC, - KC_KP_LT, - KC_KP_GT, - KC_KP_AND, - KC_KP_LAZYAND, - KC_KP_OR, - KC_KP_LAZYOR, - KC_KP_COLON, - KC_KP_HASH, - KC_KP_SPACE, - KC_KP_ATMARK, - KC_KP_EXCLAMATION, - KC_KP_MEM_STORE, //0xD0 - KC_KP_MEM_RECALL, - KC_KP_MEM_CLEAR, - KC_KP_MEM_ADD, - KC_KP_MEM_SUB, - KC_KP_MEM_MUL, - KC_KP_MEM_DIV, - KC_KP_PLUS_MINUS, - KC_KP_CLEAR, - KC_KP_CLEAR_ENTRY, - KC_KP_BINARY, - KC_KP_OCTAL, - KC_KP_DECIMAL, - KC_KP_HEXADECIMAL, -#endif - - /* Modifiers */ - KC_LCTRL = 0xE0, - KC_LSHIFT, - KC_LALT, - KC_LGUI, - KC_RCTRL, - KC_RSHIFT, - KC_RALT, - KC_RGUI - - // ********************************************** - // * 0xF0-0xFF are unallocated in the HID spec. * - // * QMK uses these for Mouse Keys - see below. * - // ********************************************** -}; - -/* Media and Function keys */ -enum internal_special_keycodes { - /* Generic Desktop Page (0x01) */ - KC_SYSTEM_POWER = 0xA5, - KC_SYSTEM_SLEEP, - KC_SYSTEM_WAKE, - - /* Consumer Page (0x0C) */ - KC_AUDIO_MUTE, - KC_AUDIO_VOL_UP, - KC_AUDIO_VOL_DOWN, - KC_MEDIA_NEXT_TRACK, - KC_MEDIA_PREV_TRACK, - KC_MEDIA_STOP, - KC_MEDIA_PLAY_PAUSE, - KC_MEDIA_SELECT, - KC_MEDIA_EJECT, // 0xB0 - KC_MAIL, - KC_CALCULATOR, - KC_MY_COMPUTER, - KC_WWW_SEARCH, - KC_WWW_HOME, - KC_WWW_BACK, - KC_WWW_FORWARD, - KC_WWW_STOP, - KC_WWW_REFRESH, - KC_WWW_FAVORITES, - KC_MEDIA_FAST_FORWARD, - KC_MEDIA_REWIND, - KC_BRIGHTNESS_UP, - KC_BRIGHTNESS_DOWN, - - /* Fn keys */ - KC_FN0 = 0xC0, - KC_FN1, - KC_FN2, - KC_FN3, - KC_FN4, - KC_FN5, - KC_FN6, - KC_FN7, - KC_FN8, - KC_FN9, - KC_FN10, - KC_FN11, - KC_FN12, - KC_FN13, - KC_FN14, - KC_FN15, - KC_FN16, // 0xD0 - KC_FN17, - KC_FN18, - KC_FN19, - KC_FN20, - KC_FN21, - KC_FN22, - KC_FN23, - KC_FN24, - KC_FN25, - KC_FN26, - KC_FN27, - KC_FN28, - KC_FN29, - KC_FN30, - KC_FN31 -}; - -enum mouse_keys { -/* Mouse Buttons */ -#ifdef VIA_ENABLE - KC_MS_UP = 0xF0, -#else - KC_MS_UP = 0xED, -#endif - KC_MS_DOWN, - KC_MS_LEFT, - KC_MS_RIGHT, // 0xF0 - KC_MS_BTN1, - KC_MS_BTN2, - KC_MS_BTN3, - KC_MS_BTN4, - KC_MS_BTN5, -#ifdef VIA_ENABLE - KC_MS_BTN6 = KC_MS_BTN5, - KC_MS_BTN7 = KC_MS_BTN5, - KC_MS_BTN8 = KC_MS_BTN5, -#else - KC_MS_BTN6, - KC_MS_BTN7, - KC_MS_BTN8, -#endif - - /* Mouse Wheel */ - KC_MS_WH_UP, - KC_MS_WH_DOWN, - KC_MS_WH_LEFT, - KC_MS_WH_RIGHT, - - /* Acceleration */ - KC_MS_ACCEL0, - KC_MS_ACCEL1, - KC_MS_ACCEL2 // 0xFF -}; -- cgit v1.2.3 From 0a1bf7f6aa6e44557041e03f6e58df5a180c6d79 Mon Sep 17 00:00:00 2001 From: Simon Arlott <70171+nomis@users.noreply.github.com> Date: Fri, 20 Aug 2021 00:31:23 +0100 Subject: Support using a timer for wait_us() on ChibiOS-based boards (#12211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support using a timer for wait_us() on ChibiOS-based boards (#12198) There are spare GPT timers that can be used to get a more accurate wait_ms() time. This is required for the matrix scan unselect delay (30µs) to be shorter than the system tick rate of 100µs. This is limited to the maximum GPT duration of 65535 so values above that will automatically use the previous implementation based on the system tick. Using a specific timer means it can't be shared by another thread at the same time so when wait_us() is called from anything other than the main thread it will use the system tick implementation too. * Update tmk_core/common/chibios/wait.c * Update tmk_core/common/chibios/wait.c Co-authored-by: Joel Challis --- tmk_core/chibios.mk | 3 +- tmk_core/common/chibios/_wait.c | 89 ++++++++++++++++++++++++++++++++++++++++ tmk_core/common/chibios/_wait.h | 24 ++++++----- tmk_core/common/chibios/wait.c | 90 ++++++++++------------------------------- 4 files changed, 127 insertions(+), 79 deletions(-) create mode 100644 tmk_core/common/chibios/_wait.c (limited to 'tmk_core/common') diff --git a/tmk_core/chibios.mk b/tmk_core/chibios.mk index 7962516a0a..18839710be 100644 --- a/tmk_core/chibios.mk +++ b/tmk_core/chibios.mk @@ -211,7 +211,8 @@ CHIBISRC = $(STARTUPSRC) \ $(BOARDSRC) \ $(STREAMSSRC) \ $(CHIBIOS)/os/various/syscalls.c \ - $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c + $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \ + $(PLATFORM_COMMON_DIR)/wait.c # Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise. QUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM) diff --git a/tmk_core/common/chibios/_wait.c b/tmk_core/common/chibios/_wait.c new file mode 100644 index 0000000000..1fbea2dd5e --- /dev/null +++ b/tmk_core/common/chibios/_wait.c @@ -0,0 +1,89 @@ +/* Copyright 2021 QMK + * + * 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 3 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 . + */ + +#ifndef __OPTIMIZE__ +# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed" +#endif + +#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t" + +__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */ + /* The argument n must be a constant expression. + * That way, compiler optimization will remove unnecessary code. */ + if (n < 1) { + return; + } + if (n > 8) { + unsigned int n8 = n / 8; + n = n - n8 * 8; + switch (n8) { + case 16: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 15: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 14: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 13: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 12: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 11: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 10: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 9: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 8: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 7: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 6: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 5: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 4: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 3: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 2: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 1: + asm volatile(CLOCK_DELAY_NOP8::: "memory"); + case 0: + break; + } + } + switch (n) { + case 8: + asm volatile("nop" ::: "memory"); + case 7: + asm volatile("nop" ::: "memory"); + case 6: + asm volatile("nop" ::: "memory"); + case 5: + asm volatile("nop" ::: "memory"); + case 4: + asm volatile("nop" ::: "memory"); + case 3: + asm volatile("nop" ::: "memory"); + case 2: + asm volatile("nop" ::: "memory"); + case 1: + asm volatile("nop" ::: "memory"); + case 0: + break; + } +} diff --git a/tmk_core/common/chibios/_wait.h b/tmk_core/common/chibios/_wait.h index 5bface53e1..4a5172536b 100644 --- a/tmk_core/common/chibios/_wait.h +++ b/tmk_core/common/chibios/_wait.h @@ -16,6 +16,7 @@ #pragma once #include +#include /* chThdSleepX of zero maps to infinite - so we map to a tiny delay to still yield */ #define wait_ms(ms) \ @@ -26,14 +27,19 @@ chThdSleepMicroseconds(1); \ } \ } while (0) -#define wait_us(us) \ - do { \ - if (us != 0) { \ - chThdSleepMicroseconds(us); \ - } else { \ - chThdSleepMicroseconds(1); \ - } \ - } while (0) + +#ifdef WAIT_US_TIMER +void wait_us(uint16_t duration); +#else +# define wait_us(us) \ + do { \ + if (us != 0) { \ + chThdSleepMicroseconds(us); \ + } else { \ + chThdSleepMicroseconds(1); \ + } \ + } while (0) +#endif /* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus * to which the GPIO is connected. @@ -46,7 +52,7 @@ * (A fairly large value of 0.25 microseconds is set.) */ -#include "wait.c" +#include "_wait.c" #ifndef GPIO_INPUT_PIN_DELAY # define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4) diff --git a/tmk_core/common/chibios/wait.c b/tmk_core/common/chibios/wait.c index c6270fd95e..56fd6ffcec 100644 --- a/tmk_core/common/chibios/wait.c +++ b/tmk_core/common/chibios/wait.c @@ -14,76 +14,28 @@ * along with this program. If not, see . */ -#ifndef __OPTIMIZE__ -# pragma message "Compiler optimizations disabled; wait_cpuclock() won't work as designed" -#endif +#include +#include -#define CLOCK_DELAY_NOP8 "nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t nop\n\t" +#include "_wait.h" -__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */ - /* The argument n must be a constant expression. - * That way, compiler optimization will remove unnecessary code. */ - if (n < 1) { - return; - } - if (n > 8) { - unsigned int n8 = n / 8; - n = n - n8 * 8; - switch (n8) { - case 16: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 15: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 14: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 13: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 12: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 11: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 10: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 9: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 8: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 7: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 6: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 5: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 4: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 3: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 2: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 1: - asm volatile(CLOCK_DELAY_NOP8::: "memory"); - case 0: - break; - } +#ifdef WAIT_US_TIMER +void wait_us(uint16_t duration) { + static const GPTConfig gpt_cfg = {1000000, NULL, 0, 0}; /* 1MHz timer, no callback */ + + if (duration == 0) { + duration = 1; } - switch (n) { - case 8: - asm volatile("nop" ::: "memory"); - case 7: - asm volatile("nop" ::: "memory"); - case 6: - asm volatile("nop" ::: "memory"); - case 5: - asm volatile("nop" ::: "memory"); - case 4: - asm volatile("nop" ::: "memory"); - case 3: - asm volatile("nop" ::: "memory"); - case 2: - asm volatile("nop" ::: "memory"); - case 1: - asm volatile("nop" ::: "memory"); - case 0: - break; + + /* + * Only use this timer on the main thread; + * other threads need to use their own timer. + */ + if (chThdGetSelfX() == &ch.mainthread && duration < (1ULL << (sizeof(gptcnt_t) * 8))) { + gptStart(&WAIT_US_TIMER, &gpt_cfg); + gptPolledDelay(&WAIT_US_TIMER, duration); + } else { + chThdSleepMicroseconds(duration); } -} \ No newline at end of file +} +#endif -- cgit v1.2.3 From e756a21636149ad47c19c659d04be93cf3071dab Mon Sep 17 00:00:00 2001 From: Donald Kjer Date: Mon, 23 Aug 2021 15:15:34 -0700 Subject: eeprom_stm32: implement high density wear leveling (#12567) * eeprom_stm32: implement wear leveling Update EECONFIG_MAGIC_NUMBER eeprom_stm32: check emulated eeprom size is large enough * eeprom_stm32: Increasing simulated EEPROM density on stm32 * Adding utility script to decode emulated eeprom * Adding unit tests * Applying qmk cformat changes * cleaned up flash mocking * Fix for stm32eeprom_parser.py checking via signature with wrong base * Fix for nk65 keyboard Co-authored-by: Ilya Zhuravlev Co-authored-by: zvecr --- build_test.mk | 1 + keyboards/nk65/config.h | 3 + quantum/eeconfig.h | 2 +- testlist.mk | 1 + tmk_core/common/chibios/eeprom_stm32.c | 802 ++++++++++++++++++++++++---- tmk_core/common/chibios/eeprom_stm32.h | 61 +-- tmk_core/common/chibios/flash_stm32.h | 7 +- tmk_core/common/test/eeprom_stm32_tests.cpp | 438 +++++++++++++++ tmk_core/common/test/flash_stm32_mock.c | 50 ++ tmk_core/common/test/rules.mk | 23 + tmk_core/common/test/testlist.mk | 1 + util/stm32eeprom_parser.py | 317 +++++++++++ 12 files changed, 1529 insertions(+), 177 deletions(-) create mode 100644 tmk_core/common/test/eeprom_stm32_tests.cpp create mode 100644 tmk_core/common/test/flash_stm32_mock.c create mode 100644 tmk_core/common/test/rules.mk create mode 100644 tmk_core/common/test/testlist.mk create mode 100755 util/stm32eeprom_parser.py (limited to 'tmk_core/common') diff --git a/build_test.mk b/build_test.mk index b6b8782174..037577bf90 100644 --- a/build_test.mk +++ b/build_test.mk @@ -56,6 +56,7 @@ include $(TMK_PATH)/common.mk include $(QUANTUM_PATH)/debounce/tests/rules.mk include $(QUANTUM_PATH)/sequencer/tests/rules.mk include $(QUANTUM_PATH)/serial_link/tests/rules.mk +include $(TMK_PATH)/common/test/rules.mk ifneq ($(filter $(FULL_TESTS),$(TEST)),) include build_full_test.mk endif diff --git a/keyboards/nk65/config.h b/keyboards/nk65/config.h index fa14e9ffbf..a178307cc0 100755 --- a/keyboards/nk65/config.h +++ b/keyboards/nk65/config.h @@ -148,6 +148,9 @@ along with this program. If not, see . * both 128kb and 256kb versions of F303. * Register 0x1FFFF7CC holds the size of the flash memory. */ +#ifndef FLASHSIZE_BASE +# define FLASHSIZE_BASE ((uint32_t)0x1FFFF7CCU) /*!< FLASH Size register base address */ +#endif #define EEPROM_START_ADDRESS #define FEE_MCU_FLASH_SIZE \ ({ \ diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index a88071729d..bd39971b2c 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -21,7 +21,7 @@ along with this program. If not, see . #include #ifndef EECONFIG_MAGIC_NUMBER -# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA // When changing, decrement this value to avoid future re-init issues +# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE9 // When changing, decrement this value to avoid future re-init issues #endif #define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF diff --git a/testlist.mk b/testlist.mk index d256f4c815..b66b93d295 100644 --- a/testlist.mk +++ b/testlist.mk @@ -4,6 +4,7 @@ FULL_TESTS := $(TEST_LIST) include $(ROOT_DIR)/quantum/debounce/tests/testlist.mk include $(ROOT_DIR)/quantum/sequencer/tests/testlist.mk include $(ROOT_DIR)/quantum/serial_link/tests/testlist.mk +include $(ROOT_DIR)/tmk_core/common/test/testlist.mk define VALIDATE_TEST_LIST ifneq ($1,) diff --git a/tmk_core/common/chibios/eeprom_stm32.c b/tmk_core/common/chibios/eeprom_stm32.c index ea51989728..5bf852fde1 100644 --- a/tmk_core/common/chibios/eeprom_stm32.c +++ b/tmk_core/common/chibios/eeprom_stm32.c @@ -14,185 +14,751 @@ * Artur F. * * Modifications for QMK and STM32F303 by Yiancar + * Modifications to add flash wear leveling by Ilya Zhuravlev + * Modifications to increase flash density by Don Kjer */ #include -#include +#include +#include "debug.h" #include "eeprom_stm32.h" -/***************************************************************************** - * Allows to use the internal flash to store non volatile data. To initialize - * the functionality use the EEPROM_Init() function. Be sure that by reprogramming - * of the controller just affected pages will be deleted. In other case the non - * volatile data will be lost. - ******************************************************************************/ +#include "flash_stm32.h" + +/* + * We emulate eeprom by writing a snapshot compacted view of eeprom contents, + * followed by a write log of any change since that snapshot: + * + * === SIMULATED EEPROM CONTENTS === + * + * ┌─ Compacted ┬ Write Log ─┐ + * │............│[BYTE][BYTE]│ + * │FFFF....FFFF│[WRD0][WRD1]│ + * │FFFFFFFFFFFF│[WORD][NEXT]│ + * │....FFFFFFFF│[BYTE][WRD0]│ + * ├────────────┼────────────┤ + * └──PAGE_BASE │ │ + * PAGE_LAST─┴─WRITE_BASE │ + * WRITE_LAST ┘ + * + * Compacted contents are the 1's complement of the actual EEPROM contents. + * e.g. An 'FFFF' represents a '0000' value. + * + * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom. + * The size of the compacted-area and write log are configurable, and the combined + * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent. + * Simulated Eeprom contents are located at the end of available flash space. + * + * The following configuration defines can be set: + * + * FEE_DENSITY_PAGES # Total number of pages to use for eeprom simulation (Compact + Write log) + * FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_DENSITY_PAGES) + * NOTE: The current implementation does not include page swapping, + * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents. + * + * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals + * FEE_DENSITY_PAGES * FEE_PAGE_SIZE - FEE_DENSITY_BYTES. + * The larger the write log, the less frequently the compacted area needs to be rewritten. + * + * + * *** General Algorithm *** + * + * During initialization: + * The contents of the Compacted-flash area are loaded and the 1's complement value + * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache). + * Write log entries are processed until a 0xFFFF is reached. + * Each log entry updates a byte or word in the cache. + * + * During reads: + * EEPROM contents are given back directly from the cache in memory. + * + * During writes: + * The contents of the cache is updated first. + * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash + * Otherwise: + * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area. + * Otherwise a Write log entry is constructed and appended to the next free position in the Write log. + * + * + * *** Write Log Structure *** + * + * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned. + * + * === WRITE LOG ENTRY FORMATS === + * + * ╔═══ Byte-Entry ══╗ + * ║0XXXXXXX║YYYYYYYY║ + * ║ └──┬──┘║└──┬───┘║ + * ║ Address║ Value ║ + * ╚════════╩════════╝ + * 0 <= Address < 0x80 (128) + * + * ╔ Word-Encoded 0 ╗ + * ║100XXXXXXXXXXXXX║ + * ║ │└─────┬─────┘║ + * ║ │Address >> 1 ║ + * ║ └── Value: 0 ║ + * ╚════════════════╝ + * 0 <= Address <= 0x3FFE (16382) + * + * ╔ Word-Encoded 1 ╗ + * ║101XXXXXXXXXXXXX║ + * ║ │└─────┬─────┘║ + * ║ │Address >> 1 ║ + * ║ └── Value: 1 ║ + * ╚════════════════╝ + * 0 <= Address <= 0x3FFE (16382) + * + * ╔═══ Reserved ═══╗ + * ║110XXXXXXXXXXXXX║ + * ╚════════════════╝ + * + * ╔═══════════ Word-Next ═══════════╗ + * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║ + * ║ └─────┬─────┘║└───────┬──────┘║ + * ║(Address-128)>>1║ ~Value ║ + * ╚════════════════╩════════════════╝ + * ( 0 <= Address < 0x0080 (128): Reserved) + * 0x80 <= Address <= 0x3FFE (16382) + * + * Write Log entry ranges: + * 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF) + * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0 + * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1 + * 0xC000 ... 0xDFFF - Reserved + * 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry) + * 0xFFC0 ... 0xFFFE - Reserved + * 0xFFFF - Unprogrammed + * + */ -/* Private macro -------------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -/* Functions -----------------------------------------------------------------*/ +/* These bits are used for optimizing encoding of bytes, 0 and 1 */ +#define FEE_WORD_ENCODING 0x8000 +#define FEE_VALUE_NEXT 0x6000 +#define FEE_VALUE_RESERVED 0x4000 +#define FEE_VALUE_ENCODED 0x2000 +#define FEE_BYTE_RANGE 0x80 + +// HACK ALERT. This definition may not match your processor +// To Do. Work out correct value for EEPROM_PAGE_SIZE on the STM32F103CT6 etc +#if defined(EEPROM_EMU_STM32F303xC) +# define MCU_STM32F303CC +#elif defined(EEPROM_EMU_STM32F103xB) +# define MCU_STM32F103RB +#elif defined(EEPROM_EMU_STM32F072xB) +# define MCU_STM32F072CB +#elif defined(EEPROM_EMU_STM32F042x6) +# define MCU_STM32F042K6 +#elif !defined(FEE_PAGE_SIZE) || !defined(FEE_DENSITY_PAGES) || !defined(FEE_MCU_FLASH_SIZE) +# error "not implemented." +#endif + +#if !defined(FEE_PAGE_SIZE) || !defined(FEE_DENSITY_PAGES) +# if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6) +# ifndef FEE_PAGE_SIZE +# define FEE_PAGE_SIZE 0x400 // Page size = 1KByte +# endif +# ifndef FEE_DENSITY_PAGES +# define FEE_DENSITY_PAGES 2 // How many pages are used +# endif +# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB) +# ifndef FEE_PAGE_SIZE +# define FEE_PAGE_SIZE 0x800 // Page size = 2KByte +# endif +# ifndef FEE_DENSITY_PAGES +# define FEE_DENSITY_PAGES 4 // How many pages are used +# endif +# else +# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." +# endif +#endif + +#ifndef FEE_MCU_FLASH_SIZE +# if defined(MCU_STM32F103RB) || defined(MCU_STM32F072CB) +# define FEE_MCU_FLASH_SIZE 128 // Size in Kb +# elif defined(MCU_STM32F042K6) +# define FEE_MCU_FLASH_SIZE 32 // Size in Kb +# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) +# define FEE_MCU_FLASH_SIZE 512 // Size in Kb +# elif defined(MCU_STM32F103RD) +# define FEE_MCU_FLASH_SIZE 384 // Size in Kb +# elif defined(MCU_STM32F303CC) +# define FEE_MCU_FLASH_SIZE 256 // Size in Kb +# else +# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." +# endif +#endif + +#define FEE_XSTR(x) FEE_STR(x) +#define FEE_STR(x) #x + +/* Size of combined compacted eeprom and write log pages */ +#define FEE_DENSITY_MAX_SIZE (FEE_DENSITY_PAGES * FEE_PAGE_SIZE) +/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */ +#define FEE_ADDRESS_MAX_SIZE 0x4000 + +#ifndef EEPROM_START_ADDRESS /* *TODO: Get rid of this check */ +# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024) +# pragma message FEE_XSTR(FEE_DENSITY_MAX_SIZE) " > " FEE_XSTR(FEE_MCU_FLASH_SIZE * 1024) +# error emulated eeprom: FEE_DENSITY_PAGES is greater than available flash size +# endif +#endif + +/* Size of emulated eeprom */ +#ifdef FEE_DENSITY_BYTES +# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE) +# pragma message FEE_XSTR(FEE_DENSITY_BYTES) " > " FEE_XSTR(FEE_DENSITY_MAX_SIZE) +# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE +# endif +# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE) +# pragma message FEE_XSTR(FEE_DENSITY_BYTES) " == " FEE_XSTR(FEE_DENSITY_MAX_SIZE) +# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate! +# endif +# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE +# pragma message FEE_XSTR(FEE_DENSITY_BYTES) " > " FEE_XSTR(FEE_ADDRESS_MAX_SIZE) +# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows +# endif +# if ((FEE_DENSITY_BYTES) % 2) == 1 +# error emulated eeprom: FEE_DENSITY_BYTES must be even +# endif +#else +/* Default to half of allocated space used for emulated eeprom, half for write log */ +# define FEE_DENSITY_BYTES (FEE_DENSITY_PAGES * FEE_PAGE_SIZE / 2) +#endif + +/* Size of write log */ +#define FEE_WRITE_LOG_BYTES (FEE_DENSITY_PAGES * FEE_PAGE_SIZE - FEE_DENSITY_BYTES) + +/* Start of the emulated eeprom compacted flash area */ +#ifndef FEE_FLASH_BASE +# define FEE_FLASH_BASE 0x8000000 +#endif +#define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - FEE_WRITE_LOG_BYTES - FEE_DENSITY_BYTES) +/* End of the emulated eeprom compacted flash area */ +#define FEE_PAGE_LAST_ADDRESS (FEE_PAGE_BASE_ADDRESS + FEE_DENSITY_BYTES) +/* Start of the emulated eeprom write log */ +#define FEE_WRITE_LOG_BASE_ADDRESS FEE_PAGE_LAST_ADDRESS +/* End of the emulated eeprom write log */ +#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES) + +/* Flash word value after erase */ +#define FEE_EMPTY_WORD ((uint16_t)0xFFFF) + +#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES) +# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available +#endif + +/* In-memory contents of emulated eeprom for faster access */ +/* *TODO: Implement page swapping */ +static uint16_t WordBuf[FEE_DENSITY_BYTES / 2]; +static uint8_t *DataBuf = (uint8_t *)WordBuf; + +/* Pointer to the first available slot within the write log */ +static uint16_t *empty_slot; + +// #define DEBUG_EEPROM_OUTPUT + +/* + * Debug print utils + */ + +#if defined(DEBUG_EEPROM_OUTPUT) + +# define debug_eeprom debug_enable +# define eeprom_println(s) println(s) +# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__); + +#else /* NO_DEBUG */ + +# define debug_eeprom false +# define eeprom_println(s) +# define eeprom_printf(fmt, ...) + +#endif /* NO_DEBUG */ + +void print_eeprom(void) { +#ifndef NO_DEBUG + int empty_rows = 0; + for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) { + if (i % 16 == 0) { + if (i >= FEE_DENSITY_BYTES - 16) { + /* Make sure we display the last row */ + empty_rows = 0; + } + /* Check if this row is uninitialized */ + ++empty_rows; + for (uint16_t j = 0; j < 16; j++) { + if (DataBuf[i + j]) { + empty_rows = 0; + break; + } + } + if (empty_rows > 1) { + /* Repeat empty row */ + if (empty_rows == 2) { + /* Only display the first repeat empty row */ + println("*"); + } + i += 15; + continue; + } + xprintf("%04x", i); + } + if (i % 8 == 0) print(" "); + + xprintf(" %02x", DataBuf[i]); + if ((i + 1) % 16 == 0) { + println(""); + } + } +#endif +} -uint8_t DataBuf[FEE_PAGE_SIZE]; -/***************************************************************************** - * Delete Flash Space used for user Data, deletes the whole space between - * RW_PAGE_BASE_ADDRESS and the last uC Flash Page - ******************************************************************************/ uint16_t EEPROM_Init(void) { - // unlock flash - FLASH_Unlock(); + /* Load emulated eeprom contents from compacted flash into memory */ + uint16_t *src = (uint16_t *)FEE_PAGE_BASE_ADDRESS; + uint16_t *dest = (uint16_t *)DataBuf; + for (; src < (uint16_t *)FEE_PAGE_LAST_ADDRESS; ++src, ++dest) { + *dest = ~*src; + } + + if (debug_eeprom) { + println("EEPROM_Init Compacted Pages:"); + print_eeprom(); + println("EEPROM_Init Write Log:"); + } + + /* Replay write log */ + uint16_t *log_addr; + for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) { + uint16_t address = *log_addr; + if (address == FEE_EMPTY_WORD) { + break; + } + /* Check for lowest 128-bytes optimization */ + if (!(address & FEE_WORD_ENCODING)) { + uint8_t bvalue = (uint8_t)address; + address >>= 8; + DataBuf[address] = bvalue; + eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue); + } else { + uint16_t wvalue; + /* Check if value is in next word */ + if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) { + /* Read value from next word */ + if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + break; + } + wvalue = ~*log_addr; + if (!wvalue) { + eeprom_printf("Incomplete write at log_addr: 0x%04x;\n", (uint32_t)log_addr); + /* Possibly incomplete write. Ignore and continue */ + continue; + } + address &= 0x1FFF; + address <<= 1; + /* Writes to addresses less than 128 are byte log entries */ + address += FEE_BYTE_RANGE; + } else { + /* Reserved for future use */ + if (address & FEE_VALUE_RESERVED) { + eeprom_printf("Reserved encoded value at log_addr: 0x%04x;\n", (uint32_t)log_addr); + continue; + } + /* Optimization for 0 or 1 values. */ + wvalue = (address & FEE_VALUE_ENCODED) >> 13; + address &= 0x1FFF; + address <<= 1; + } + if (address < FEE_DENSITY_BYTES) { + eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue); + *(uint16_t *)(&DataBuf[address]) = wvalue; + } else { + eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue); + } + } + } - // Clear Flags - // FLASH_ClearFlag(FLASH_SR_EOP|FLASH_SR_PGERR|FLASH_SR_WRPERR); + empty_slot = log_addr; + + if (debug_eeprom) { + println("EEPROM_Init Final DataBuf:"); + print_eeprom(); + } return FEE_DENSITY_BYTES; } -/***************************************************************************** - * Erase the whole reserved Flash Space used for user Data - ******************************************************************************/ -void EEPROM_Erase(void) { - int page_num = 0; - // delete all pages from specified start page to the last page - do { +/* Clear flash contents (doesn't touch in-memory DataBuf) */ +static void eeprom_clear(void) { + FLASH_Unlock(); + + for (uint16_t page_num = 0; page_num < FEE_DENSITY_PAGES; ++page_num) { + eeprom_printf("FLASH_ErasePage(0x%04x)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE))); FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); - page_num++; - } while (page_num < FEE_DENSITY_PAGES); + } + + FLASH_Lock(); + + empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; + eeprom_printf("eeprom_clear empty_slot: 0x%08x\n", (uint32_t)empty_slot); } -/***************************************************************************** - * Writes once data byte to flash on specified address. If a byte is already - * written, the whole page must be copied to a buffer, the byte changed and - * the manipulated buffer written after PageErase. - *******************************************************************************/ -uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { - FLASH_Status FlashStatus = FLASH_COMPLETE; - uint32_t page; - int i; +/* Erase emulated eeprom */ +void EEPROM_Erase(void) { + eeprom_println("EEPROM_Erase"); + /* Erase compacted pages and write log */ + eeprom_clear(); + /* re-initialize to reset DataBuf */ + EEPROM_Init(); +} - // exit if desired address is above the limit (e.G. under 2048 Bytes for 4 pages) - if (Address > FEE_DENSITY_BYTES) { - return 0; +/* Compact write log */ +static uint8_t eeprom_compact(void) { + /* Erase compacted pages and write log */ + eeprom_clear(); + + FLASH_Unlock(); + + FLASH_Status final_status = FLASH_COMPLETE; + + /* Write emulated eeprom contents from memory to compacted flash */ + uint16_t *src = (uint16_t *)DataBuf; + uintptr_t dest = FEE_PAGE_BASE_ADDRESS; + uint16_t value; + for (; dest < FEE_PAGE_LAST_ADDRESS; ++src, dest += 2) { + value = *src; + if (value) { + eeprom_printf("FLASH_ProgramHalfWord(0x%04x, 0x%04x)\n", (uint32_t)dest, ~value); + FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value); + if (status != FLASH_COMPLETE) final_status = status; + } } - // calculate which page is affected (Pagenum1/Pagenum2...PagenumN) - page = FEE_ADDR_OFFSET(Address) / FEE_PAGE_SIZE; + FLASH_Lock(); - // if current data is 0xFF, the byte is empty, just overwrite with the new one - if ((*(__IO uint16_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) == FEE_EMPTY_WORD) { - FlashStatus = FLASH_ProgramHalfWord(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address), (uint16_t)(0x00FF & DataByte)); + if (debug_eeprom) { + println("eeprom_compacted:"); + print_eeprom(); + } + + return final_status; +} + +static uint8_t eeprom_write_direct_entry(uint16_t Address) { + /* Check if we can just write this directly to the compacted flash area */ + uintptr_t directAddress = FEE_PAGE_BASE_ADDRESS + (Address & 0xFFFE); + if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) { + /* Write the value directly to the compacted area without a log entry */ + uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]); + /* Early exit if a write isn't needed */ + if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE; + + FLASH_Unlock(); + + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value); + FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value); + + FLASH_Lock(); + return status; + } + return 0; +} + +static uint8_t eeprom_write_log_word_entry(uint16_t Address) { + FLASH_Status final_status = FLASH_COMPLETE; + + uint16_t value = *(uint16_t *)(&DataBuf[Address]); + eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value); + + /* MSB signifies the lowest 128-byte optimization is not in effect */ + uint16_t encoding = FEE_WORD_ENCODING; + uint8_t entry_size; + if (value <= 1) { + encoding |= value << 13; + entry_size = 2; } else { - // Copy Page to a buffer - memcpy(DataBuf, (uint8_t *)FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE), FEE_PAGE_SIZE); // !!! Calculate base address for the desired page + encoding |= FEE_VALUE_NEXT; + entry_size = 4; + /* Writes to addresses less than 128 are byte log entries */ + Address -= FEE_BYTE_RANGE; + } + + /* if we can't find an empty spot, we must compact emulated eeprom */ + if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + /* Word log writes should be word-aligned. Take back a bit */ + Address >>= 1; + Address |= encoding; + + /* ok we found a place let's write our data */ + FLASH_Unlock(); + + /* address */ + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, Address); + final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address); + + /* value */ + if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) { + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, ~value); + FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value); + if (status != FLASH_COMPLETE) final_status = status; + } + + FLASH_Lock(); - // check if new data is differ to current data, return if not, proceed if yes - if (DataByte == *(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))) { - return 0; + return final_status; +} + +static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { + eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]); + + /* if couldn't find an empty spot, we must compact emulated eeprom */ + if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { + /* compact the write log into the compacted flash area */ + return eeprom_compact(); + } + + /* ok we found a place let's write our data */ + FLASH_Unlock(); + + /* Pack address and value into the same word */ + uint16_t value = (Address << 8) | DataBuf[Address]; + + /* write to flash */ + eeprom_printf("FLASH_ProgramHalfWord(0x%08x, 0x%04x)\n", (uint32_t)empty_slot, value); + FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value); + + FLASH_Lock(); + + return status; +} + +uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= FEE_DENSITY_BYTES) { + eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte); + return FLASH_BAD_ADDRESS; + } + + /* if the value is the same, don't bother writing it */ + if (DataBuf[Address] == DataByte) { + eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte); + return 0; + } + + /* keep DataBuf cache in sync */ + DataBuf[Address] = DataByte; + eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]); + + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + FLASH_Status status = eeprom_write_direct_entry(Address); + if (!status) { + /* Otherwise append to the write log */ + if (Address < FEE_BYTE_RANGE) { + status = eeprom_write_log_byte_entry(Address); + } else { + status = eeprom_write_log_word_entry(Address & 0xFFFE); } + } + if (status != 0 && status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status); + } + return status; +} - // manipulate desired data byte in temp data array if new byte is differ to the current - DataBuf[FEE_ADDR_OFFSET(Address) % FEE_PAGE_SIZE] = DataByte; +uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) { + /* if the address is out-of-bounds, do nothing */ + if (Address >= FEE_DENSITY_BYTES) { + eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord); + return FLASH_BAD_ADDRESS; + } - // Erase Page - FlashStatus = FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)); + /* Check for word alignment */ + FLASH_Status final_status = FLASH_COMPLETE; + if (Address % 2) { + final_status = EEPROM_WriteDataByte(Address, DataWord); + FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8); + if (status != FLASH_COMPLETE) final_status = status; + if (final_status != 0 && final_status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + } + return final_status; + } + + /* if the value is the same, don't bother writing it */ + uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]); + if (oldValue == DataWord) { + eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord); + return 0; + } + + /* keep DataBuf cache in sync */ + *(uint16_t *)(&DataBuf[Address]) = DataWord; + eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address])); - // Write new data (whole page) to flash if data has been changed - for (i = 0; i < (FEE_PAGE_SIZE / 2); i++) { - if ((__IO uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)]) != 0xFFFF) { - FlashStatus = FLASH_ProgramHalfWord((FEE_PAGE_BASE_ADDRESS + (page * FEE_PAGE_SIZE)) + (i * 2), (uint16_t)(0xFF00 | DataBuf[FEE_ADDR_OFFSET(i)])); + /* perform the write into flash memory */ + /* First, attempt to write directly into the compacted flash area */ + final_status = eeprom_write_direct_entry(Address); + if (!final_status) { + /* Otherwise append to the write log */ + /* Check if we need to fall back to byte write */ + if (Address < FEE_BYTE_RANGE) { + final_status = FLASH_COMPLETE; + /* Only write a byte if it has changed */ + if ((uint8_t)oldValue != (uint8_t)DataWord) { + final_status = eeprom_write_log_byte_entry(Address); } + FLASH_Status status = FLASH_COMPLETE; + /* Only write a byte if it has changed */ + if ((oldValue >> 8) != (DataWord >> 8)) { + status = eeprom_write_log_byte_entry(Address + 1); + } + if (status != FLASH_COMPLETE) final_status = status; + } else { + final_status = eeprom_write_log_word_entry(Address); } } - return FlashStatus; + if (final_status != 0 && final_status != FLASH_COMPLETE) { + eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); + } + return final_status; } -/***************************************************************************** - * Read once data byte from a specified address. - *******************************************************************************/ + uint8_t EEPROM_ReadDataByte(uint16_t Address) { uint8_t DataByte = 0xFF; - // Get Byte from specified address - DataByte = (*(__IO uint8_t *)(FEE_PAGE_BASE_ADDRESS + FEE_ADDR_OFFSET(Address))); + if (Address < FEE_DENSITY_BYTES) { + DataByte = DataBuf[Address]; + } + + eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte); return DataByte; } +uint16_t EEPROM_ReadDataWord(uint16_t Address) { + uint16_t DataWord = 0xFFFF; + + if (Address < FEE_DENSITY_BYTES - 1) { + /* Check word alignment */ + if (Address % 2) { + DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8); + } else { + DataWord = *(uint16_t *)(&DataBuf[Address]); + } + } + + eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord); + + return DataWord; +} + /***************************************************************************** * Wrap library in AVR style functions. *******************************************************************************/ -uint8_t eeprom_read_byte(const uint8_t *Address) { - const uint16_t p = (const uint32_t)Address; - return EEPROM_ReadDataByte(p); -} +uint8_t eeprom_read_byte(const uint8_t *Address) { return EEPROM_ReadDataByte((const uintptr_t)Address); } -void eeprom_write_byte(uint8_t *Address, uint8_t Value) { - uint16_t p = (uint32_t)Address; - EEPROM_WriteDataByte(p, Value); -} +void eeprom_write_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); } -void eeprom_update_byte(uint8_t *Address, uint8_t Value) { - uint16_t p = (uint32_t)Address; - EEPROM_WriteDataByte(p, Value); -} +void eeprom_update_byte(uint8_t *Address, uint8_t Value) { EEPROM_WriteDataByte((uintptr_t)Address, Value); } -uint16_t eeprom_read_word(const uint16_t *Address) { - const uint16_t p = (const uint32_t)Address; - return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8); -} +uint16_t eeprom_read_word(const uint16_t *Address) { return EEPROM_ReadDataWord((const uintptr_t)Address); } -void eeprom_write_word(uint16_t *Address, uint16_t Value) { - uint16_t p = (uint32_t)Address; - EEPROM_WriteDataByte(p, (uint8_t)Value); - EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); -} +void eeprom_write_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); } -void eeprom_update_word(uint16_t *Address, uint16_t Value) { - uint16_t p = (uint32_t)Address; - EEPROM_WriteDataByte(p, (uint8_t)Value); - EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); -} +void eeprom_update_word(uint16_t *Address, uint16_t Value) { EEPROM_WriteDataWord((uintptr_t)Address, Value); } uint32_t eeprom_read_dword(const uint32_t *Address) { - const uint16_t p = (const uint32_t)Address; - return EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24); + const uint16_t p = (const uintptr_t)Address; + /* Check word alignment */ + if (p % 2) { + /* Not aligned */ + return (uint32_t)EEPROM_ReadDataByte(p) | (uint32_t)(EEPROM_ReadDataWord(p + 1) << 8) | (uint32_t)(EEPROM_ReadDataByte(p + 3) << 24); + } else { + /* Aligned */ + return EEPROM_ReadDataWord(p) | (EEPROM_ReadDataWord(p + 2) << 16); + } } void eeprom_write_dword(uint32_t *Address, uint32_t Value) { - uint16_t p = (const uint32_t)Address; - EEPROM_WriteDataByte(p, (uint8_t)Value); - EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); - EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16)); - EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24)); -} - -void eeprom_update_dword(uint32_t *Address, uint32_t Value) { - uint16_t p = (const uint32_t)Address; - uint32_t existingValue = EEPROM_ReadDataByte(p) | (EEPROM_ReadDataByte(p + 1) << 8) | (EEPROM_ReadDataByte(p + 2) << 16) | (EEPROM_ReadDataByte(p + 3) << 24); - if (Value != existingValue) { + uint16_t p = (const uintptr_t)Address; + /* Check word alignment */ + if (p % 2) { + /* Not aligned */ EEPROM_WriteDataByte(p, (uint8_t)Value); - EEPROM_WriteDataByte(p + 1, (uint8_t)(Value >> 8)); - EEPROM_WriteDataByte(p + 2, (uint8_t)(Value >> 16)); + EEPROM_WriteDataWord(p + 1, (uint16_t)(Value >> 8)); EEPROM_WriteDataByte(p + 3, (uint8_t)(Value >> 24)); + } else { + /* Aligned */ + EEPROM_WriteDataWord(p, (uint16_t)Value); + EEPROM_WriteDataWord(p + 2, (uint16_t)(Value >> 16)); } } +void eeprom_update_dword(uint32_t *Address, uint32_t Value) { eeprom_write_dword(Address, Value); } + void eeprom_read_block(void *buf, const void *addr, size_t len) { - const uint8_t *p = (const uint8_t *)addr; + const uint8_t *src = (const uint8_t *)addr; uint8_t * dest = (uint8_t *)buf; - while (len--) { - *dest++ = eeprom_read_byte(p++); + + /* Check word alignment */ + if (len && (uintptr_t)src % 2) { + /* Read the unaligned first byte */ + *dest++ = eeprom_read_byte(src++); + --len; + } + + uint16_t value; + bool aligned = ((uintptr_t)dest % 2 == 0); + while (len > 1) { + value = eeprom_read_word((uint16_t *)src); + if (aligned) { + *(uint16_t *)dest = value; + dest += 2; + } else { + *dest++ = value; + *dest++ = value >> 8; + } + src += 2; + len -= 2; + } + if (len) { + *dest = eeprom_read_byte(src); } } void eeprom_write_block(const void *buf, void *addr, size_t len) { - uint8_t * p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); + uint8_t * dest = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + + /* Check word alignment */ + if (len && (uintptr_t)dest % 2) { + /* Write the unaligned first byte */ + eeprom_write_byte(dest++, *src++); + --len; } -} -void eeprom_update_block(const void *buf, void *addr, size_t len) { - uint8_t * p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); + uint16_t value; + bool aligned = ((uintptr_t)src % 2 == 0); + while (len > 1) { + if (aligned) { + value = *(uint16_t *)src; + } else { + value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8); + } + eeprom_write_word((uint16_t *)dest, value); + dest += 2; + src += 2; + len -= 2; + } + + if (len) { + eeprom_write_byte(dest, *src); } } + +void eeprom_update_block(const void *buf, void *addr, size_t len) { eeprom_write_block(buf, addr, len); } diff --git a/tmk_core/common/chibios/eeprom_stm32.h b/tmk_core/common/chibios/eeprom_stm32.h index 4dac7c1b59..8fcfb556b8 100644 --- a/tmk_core/common/chibios/eeprom_stm32.h +++ b/tmk_core/common/chibios/eeprom_stm32.h @@ -23,62 +23,11 @@ #pragma once -#include -#include -#include "flash_stm32.h" - -// HACK ALERT. This definition may not match your processor -// To Do. Work out correct value for EEPROM_PAGE_SIZE on the STM32F103CT6 etc -#if defined(EEPROM_EMU_STM32F303xC) -# define MCU_STM32F303CC -#elif defined(EEPROM_EMU_STM32F103xB) -# define MCU_STM32F103RB -#elif defined(EEPROM_EMU_STM32F072xB) -# define MCU_STM32F072CB -#elif defined(EEPROM_EMU_STM32F042x6) -# define MCU_STM32F042K6 -#else -# error "not implemented." -#endif - -#ifndef EEPROM_PAGE_SIZE -# if defined(MCU_STM32F103RB) || defined(MCU_STM32F042K6) -# define FEE_PAGE_SIZE (uint16_t)0x400 // Page size = 1KByte -# define FEE_DENSITY_PAGES 2 // How many pages are used -# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) || defined(MCU_STM32F103RD) || defined(MCU_STM32F303CC) || defined(MCU_STM32F072CB) -# define FEE_PAGE_SIZE (uint16_t)0x800 // Page size = 2KByte -# define FEE_DENSITY_PAGES 4 // How many pages are used -# else -# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." -# endif -#endif - -#ifndef EEPROM_START_ADDRESS -# if defined(MCU_STM32F103RB) || defined(MCU_STM32F072CB) -# define FEE_MCU_FLASH_SIZE 128 // Size in Kb -# elif defined(MCU_STM32F042K6) -# define FEE_MCU_FLASH_SIZE 32 // Size in Kb -# elif defined(MCU_STM32F103ZE) || defined(MCU_STM32F103RE) -# define FEE_MCU_FLASH_SIZE 512 // Size in Kb -# elif defined(MCU_STM32F103RD) -# define FEE_MCU_FLASH_SIZE 384 // Size in Kb -# elif defined(MCU_STM32F303CC) -# define FEE_MCU_FLASH_SIZE 256 // Size in Kb -# else -# error "No MCU type specified. Add something like -DMCU_STM32F103RB to your compiler arguments (probably in a Makefile)." -# endif -#endif - -// DONT CHANGE -// Choose location for the first EEPROM Page address on the top of flash -#define FEE_PAGE_BASE_ADDRESS ((uint32_t)(0x8000000 + FEE_MCU_FLASH_SIZE * 1024 - FEE_DENSITY_PAGES * FEE_PAGE_SIZE)) -#define FEE_DENSITY_BYTES ((FEE_PAGE_SIZE / 2) * FEE_DENSITY_PAGES - 1) -#define FEE_LAST_PAGE_ADDRESS (FEE_PAGE_BASE_ADDRESS + (FEE_PAGE_SIZE * FEE_DENSITY_PAGES)) -#define FEE_EMPTY_WORD ((uint16_t)0xFFFF) -#define FEE_ADDR_OFFSET(Address) (Address * 2) // 1Byte per Word will be saved to preserve Flash - -// Use this function to initialize the functionality uint16_t EEPROM_Init(void); void EEPROM_Erase(void); -uint16_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); +uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); +uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord); uint8_t EEPROM_ReadDataByte(uint16_t Address); +uint16_t EEPROM_ReadDataWord(uint16_t Address); + +void print_eeprom(void); diff --git a/tmk_core/common/chibios/flash_stm32.h b/tmk_core/common/chibios/flash_stm32.h index 90d5bff47e..9c6a7cc50f 100644 --- a/tmk_core/common/chibios/flash_stm32.h +++ b/tmk_core/common/chibios/flash_stm32.h @@ -22,8 +22,11 @@ extern "C" { #endif -#include -#include +#include + +#ifdef FLASH_STM32_MOCKED +extern uint8_t FlashBuf[MOCK_FLASH_SIZE]; +#endif typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status; diff --git a/tmk_core/common/test/eeprom_stm32_tests.cpp b/tmk_core/common/test/eeprom_stm32_tests.cpp new file mode 100644 index 0000000000..aa84492b87 --- /dev/null +++ b/tmk_core/common/test/eeprom_stm32_tests.cpp @@ -0,0 +1,438 @@ +/* Copyright 2021 by Don Kjer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +extern "C" { +#include "flash_stm32.h" +#include "eeprom_stm32.h" +#include "eeprom.h" +} + +/* Mock Flash Parameters: + * + * === Large Layout === + * flash size: 65536 + * page size: 2048 + * density pages: 16 + * Simulated EEPROM size: 16384 + * + * FlashBuf Layout: + * [Unused | Compact | Write Log ] + * [0......|32768......|49152......65535] + * + * === Tiny Layout === + * flash size: 1024 + * page size: 512 + * density pages: 1 + * Simulated EEPROM size: 256 + * + * FlashBuf Layout: + * [Unused | Compact | Write Log ] + * [0......|512......|768......1023] + * + */ + +#define EEPROM_SIZE (FEE_PAGE_SIZE * FEE_DENSITY_PAGES / 2) +#define LOG_SIZE EEPROM_SIZE +#define LOG_BASE (MOCK_FLASH_SIZE - LOG_SIZE) +#define EEPROM_BASE (LOG_BASE - EEPROM_SIZE) + +/* Log encoding helpers */ +#define BYTE_VALUE(addr, value) (((addr) << 8) | (value)) +#define WORD_ZERO(addr) (0x8000 | ((addr) >> 1)) +#define WORD_ONE(addr) (0xA000 | ((addr) >> 1)) +#define WORD_NEXT(addr) (0xE000 | (((addr)-0x80) >> 1)) + +class EepromStm32Test : public testing::Test { + public: + EepromStm32Test() {} + ~EepromStm32Test() {} + + protected: + void SetUp() override { EEPROM_Erase(); } + + void TearDown() override { +#ifdef EEPROM_DEBUG + dumpEepromDataBuf(); +#endif + } +}; + +TEST_F(EepromStm32Test, TestErase) { + EEPROM_WriteDataByte(0, 0x42); + EEPROM_Erase(); + EXPECT_EQ(EEPROM_ReadDataByte(0), 0); + EXPECT_EQ(EEPROM_ReadDataByte(1), 0); +} + +TEST_F(EepromStm32Test, TestReadGarbage) { + uint8_t garbage = 0x3c; + for (int i = 0; i < MOCK_FLASH_SIZE; ++i) { + garbage ^= 0xa3; + garbage += i; + FlashBuf[i] = garbage; + } + EEPROM_Init(); // Just verify we don't crash +} + +TEST_F(EepromStm32Test, TestWriteBadAddress) { + EXPECT_EQ(EEPROM_WriteDataByte(EEPROM_SIZE, 0x42), FLASH_BAD_ADDRESS); + EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE - 1, 0xbeef), FLASH_BAD_ADDRESS); + EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE, 0xbeef), FLASH_BAD_ADDRESS); +} + +TEST_F(EepromStm32Test, TestReadBadAddress) { + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE), 0xFF); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 1), 0xFFFF); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE), 0xFFFF); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 3)), 0xFF000000); + EXPECT_EQ(eeprom_read_dword((uint32_t*)EEPROM_SIZE), 0xFFFFFFFF); +} + +TEST_F(EepromStm32Test, TestReadByte) { + /* Direct compacted-area baseline: Address < 0x80 */ + FlashBuf[EEPROM_BASE + 2] = ~0xef; + FlashBuf[EEPROM_BASE + 3] = ~0xbe; + /* Direct compacted-area baseline: Address >= 0x80 */ + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); + /* Write Log byte value */ + FlashBuf[LOG_BASE] = 0x65; + FlashBuf[LOG_BASE + 1] = 3; + /* Write Log word value */ + *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_NEXT(EEPROM_SIZE - 2); + *(uint16_t*)&FlashBuf[LOG_BASE + 4] = ~0x9abc; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0x65); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0xbc); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x9a); +} + +TEST_F(EepromStm32Test, TestWriteByte) { + /* Direct compacted-area baseline: Address < 0x80 */ + EEPROM_WriteDataByte(2, 0xef); + EEPROM_WriteDataByte(3, 0xbe); + /* Direct compacted-area baseline: Address >= 0x80 */ + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78); + EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56); + /* Check values */ + /* First write in each aligned word should have been direct */ + EXPECT_EQ(FlashBuf[EEPROM_BASE + 2], (uint8_t)~0xef); + EXPECT_EQ(FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint8_t)~0x78); + + /* Second write per aligned word requires a log entry */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(3, 0xbe)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(EEPROM_SIZE - 1)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0x5678); +} + +TEST_F(EepromStm32Test, TestByteRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); + EEPROM_WriteDataByte(2, 0xef); + EEPROM_WriteDataByte(3, 0xbe); + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78); + EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(0), 0xad); + EXPECT_EQ(EEPROM_ReadDataByte(1), 0xde); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); + /* Write log entries */ + EEPROM_WriteDataByte(2, 0x80); + EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x3c); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataByte(2), 0x80); + EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x3c); + EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56); +} + +TEST_F(EepromStm32Test, TestReadWord) { + /* Direct compacted-area baseline: Address < 0x80 */ + FlashBuf[EEPROM_BASE + 0] = ~0xad; + FlashBuf[EEPROM_BASE + 1] = ~0xde; + /* Direct compacted-area baseline: Address >= 0x80 */ + FlashBuf[EEPROM_BASE + 200] = ~0xcd; + FlashBuf[EEPROM_BASE + 201] = ~0xab; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4] = ~0x34; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 3] = ~0x12; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78; + FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678); + /* Write Log word zero-encoded */ + *(uint16_t*)&FlashBuf[LOG_BASE] = WORD_ZERO(200); + /* Write Log word one-encoded */ + *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_ONE(EEPROM_SIZE - 4); + /* Write Log word value */ + *(uint16_t*)&FlashBuf[LOG_BASE + 4] = WORD_NEXT(EEPROM_SIZE - 2); + *(uint16_t*)&FlashBuf[LOG_BASE + 6] = ~0x9abc; + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 1); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x9abc); +} + +TEST_F(EepromStm32Test, TestWriteWord) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); // Aligned + EEPROM_WriteDataWord(3, 0xbeef); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataWord(200, 0xabcd); // Aligned + EEPROM_WriteDataWord(203, 0x9876); // Unaligned + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234); + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678); + /* Write Log word zero-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0); + /* Write Log word one-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1); + /* Write Log word value aligned */ + EEPROM_WriteDataWord(200, 0x4321); // Aligned + /* Write Log word value unaligned */ + EEPROM_WriteDataByte(202, 0x3c); // Set neighboring byte + EEPROM_WriteDataWord(203, 0xcdef); // Unaligned + /* Check values */ + /* Direct compacted-area */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE], (uint16_t)~0xdead); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 3], (uint16_t)~0xbeef); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 200], (uint16_t)~0xabcd); + EXPECT_EQ(FlashBuf[EEPROM_BASE + 203], (uint8_t)~0x76); + EXPECT_EQ(FlashBuf[EEPROM_BASE + 204], (uint8_t)~0x98); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4], (uint16_t)~0x1234); + EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint16_t)~0x5678); + /* Write Log word zero-encoded */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], WORD_ZERO(EEPROM_SIZE - 4)); + /* Write Log word one-encoded */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_ONE(EEPROM_SIZE - 2)); + /* Write Log word value aligned */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], WORD_NEXT(200)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], (uint16_t)~0x4321); + /* Write Log word value unaligned */ + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], WORD_NEXT(202)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], (uint16_t)~0x763c); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(202)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xef3c); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(204)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0x00cd); +} + +TEST_F(EepromStm32Test, TestWordRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + EEPROM_WriteDataWord(0, 0xdead); // Aligned + EEPROM_WriteDataWord(3, 0xbeef); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + EEPROM_WriteDataWord(200, 0xabcd); // Aligned + EEPROM_WriteDataWord(203, 0x9876); // Unaligned + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234); + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead); + EXPECT_EQ(EEPROM_ReadDataWord(3), 0xbeef); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd); + EXPECT_EQ(EEPROM_ReadDataWord(203), 0x9876); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678); + + /* Write Log word zero-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0); + /* Write Log word one-encoded */ + EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1); + /* Write Log word value aligned */ + EEPROM_WriteDataWord(200, 0x4321); // Aligned + /* Write Log word value unaligned */ + EEPROM_WriteDataByte(202, 0x3c); // Set neighboring byte + EEPROM_WriteDataWord(203, 0xcdef); // Unaligned + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(200), 0x4321); + EXPECT_EQ(EEPROM_ReadDataByte(202), 0x3c); + EXPECT_EQ(EEPROM_ReadDataWord(203), 0xcdef); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0); + EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 1); +} + +TEST_F(EepromStm32Test, TestByteWordBoundary) { + /* Direct compacted-area write */ + EEPROM_WriteDataWord(0x7e, 0xdead); + EEPROM_WriteDataWord(0x80, 0xbeef); + /* Byte log entry */ + EEPROM_WriteDataByte(0x7f, 0x3c); + /* Word log entry */ + EEPROM_WriteDataByte(0x80, 0x18); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0x3cad); + EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xbe18); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(0x7f, 0x3c)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0xbe18); + /* Byte log entries */ + EEPROM_WriteDataWord(0x7e, 0xcafe); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0xcafe); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], BYTE_VALUE(0x7e, 0xfe)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], BYTE_VALUE(0x7f, 0xca)); + /* Byte and Word log entries */ + EEPROM_WriteDataWord(0x7f, 0xba5e); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x7f), 0xba5e); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], BYTE_VALUE(0x7f, 0x5e)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xbeba); + /* Word log entry */ + EEPROM_WriteDataWord(0x80, 0xf00d); + /* Check values */ + EEPROM_Init(); + EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xf00d); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(0x80)); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0xf00d); +} + +TEST_F(EepromStm32Test, TestDWordRoundTrip) { + /* Direct compacted-area: Address < 0x80 */ + eeprom_write_dword((uint32_t*)0, 0xdeadbeef); // Aligned + eeprom_write_dword((uint32_t*)9, 0x12345678); // Unaligned + /* Direct compacted-area: Address >= 0x80 */ + eeprom_write_dword((uint32_t*)200, 0xfacef00d); + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xba5eba11); // Aligned + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0xcafed00d); // Unaligned + /* Check direct values */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x12345678); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 0xfacef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xba5eba11); // Aligned + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0xcafed00d); // Unaligned + /* Write Log byte encoded */ + eeprom_write_dword((uint32_t*)0, 0xdecafbad); + eeprom_write_dword((uint32_t*)9, 0x87654321); + /* Write Log word encoded */ + eeprom_write_dword((uint32_t*)200, 1); + /* Write Log word value aligned */ + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xdeadc0de); // Aligned + eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0x6789abcd); // Unaligned + /* Check log values */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdecafbad); + EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x87654321); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 1); + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xdeadc0de); // Aligned + EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0x6789abcd); // Unaligned +} + +TEST_F(EepromStm32Test, TestBlockRoundTrip) { + char src0[] = "0123456789abcdef"; + void* src1 = (void*)&src0[1]; + /* Various alignments of src & dst, Address < 0x80 */ + eeprom_write_block(src0, (void*)0, sizeof(src0)); + eeprom_write_block(src0, (void*)21, sizeof(src0)); + eeprom_write_block(src1, (void*)40, sizeof(src0) - 1); + eeprom_write_block(src1, (void*)61, sizeof(src0) - 1); + /* Various alignments of src & dst, Address >= 0x80 */ + eeprom_write_block(src0, (void*)140, sizeof(src0)); + eeprom_write_block(src0, (void*)161, sizeof(src0)); + eeprom_write_block(src1, (void*)180, sizeof(src0) - 1); + eeprom_write_block(src1, (void*)201, sizeof(src0) - 1); + + /* Check values */ + EEPROM_Init(); + + char dstBuf[256] = {0}; + char* dst0a = (char*)dstBuf; + char* dst0b = (char*)&dstBuf[20]; + char* dst1a = (char*)&dstBuf[41]; + char* dst1b = (char*)&dstBuf[61]; + char* dst0c = (char*)&dstBuf[80]; + char* dst0d = (char*)&dstBuf[100]; + char* dst1c = (char*)&dstBuf[121]; + char* dst1d = (char*)&dstBuf[141]; + eeprom_read_block((void*)dst0a, (void*)0, sizeof(src0)); + eeprom_read_block((void*)dst0b, (void*)21, sizeof(src0)); + eeprom_read_block((void*)dst1a, (void*)40, sizeof(src0) - 1); + eeprom_read_block((void*)dst1b, (void*)61, sizeof(src0) - 1); + eeprom_read_block((void*)dst0c, (void*)140, sizeof(src0)); + eeprom_read_block((void*)dst0d, (void*)161, sizeof(src0)); + eeprom_read_block((void*)dst1c, (void*)180, sizeof(src0) - 1); + eeprom_read_block((void*)dst1d, (void*)201, sizeof(src0) - 1); + EXPECT_EQ(strcmp((char*)src0, dst0a), 0); + EXPECT_EQ(strcmp((char*)src0, dst0b), 0); + EXPECT_EQ(strcmp((char*)src0, dst0c), 0); + EXPECT_EQ(strcmp((char*)src0, dst0d), 0); + EXPECT_EQ(strcmp((char*)src1, dst1a), 0); + EXPECT_EQ(strcmp((char*)src1, dst1b), 0); + EXPECT_EQ(strcmp((char*)src1, dst1c), 0); + EXPECT_EQ(strcmp((char*)src1, dst1d), 0); +} + +TEST_F(EepromStm32Test, TestCompaction) { + /* Direct writes */ + eeprom_write_dword((uint32_t*)0, 0xdeadbeef); + eeprom_write_byte((uint8_t*)4, 0x3c); + eeprom_write_word((uint16_t*)6, 0xd00d); + eeprom_write_dword((uint32_t*)150, 0xcafef00d); + eeprom_write_dword((uint32_t*)200, 0x12345678); + /* Fill write log entries */ + uint32_t i; + uint32_t val = 0xd8453c6b; + for (i = 0; i < (LOG_SIZE / (sizeof(uint32_t) * 2)); i++) { + val ^= 0x593ca5b3; + val += i; + eeprom_write_dword((uint32_t*)200, val); + } + /* Check values pre-compaction */ + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x3c); + EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val); + EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF); + EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF); + /* Run compaction */ + eeprom_write_byte((uint8_t*)4, 0x1f); + EEPROM_Init(); + EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef); + EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x1f); + EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d); + EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF); + EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF); +} diff --git a/tmk_core/common/test/flash_stm32_mock.c b/tmk_core/common/test/flash_stm32_mock.c new file mode 100644 index 0000000000..1b81d81f9a --- /dev/null +++ b/tmk_core/common/test/flash_stm32_mock.c @@ -0,0 +1,50 @@ +/* Copyright 2021 by Don Kjer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "flash_stm32.h" + +uint8_t FlashBuf[MOCK_FLASH_SIZE] = {0}; + +static bool flash_locked = true; + +FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { + if (flash_locked) return FLASH_ERROR_WRP; + Page_Address -= (uintptr_t)FlashBuf; + Page_Address -= (Page_Address % FEE_PAGE_SIZE); + if (Page_Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS; + memset(&FlashBuf[Page_Address], '\xff', FEE_PAGE_SIZE); + return FLASH_COMPLETE; +} + +FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { + if (flash_locked) return FLASH_ERROR_WRP; + Address -= (uintptr_t)FlashBuf; + if (Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS; + uint16_t oldData = *(uint16_t*)&FlashBuf[Address]; + if (oldData == 0xFFFF || Data == 0) { + *(uint16_t*)&FlashBuf[Address] = Data; + return FLASH_COMPLETE; + } else { + return FLASH_ERROR_PG; + } +} + +FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) { return FLASH_COMPLETE; } +void FLASH_Unlock(void) { flash_locked = false; } +void FLASH_Lock(void) { flash_locked = true; } +void FLASH_ClearFlag(uint32_t FLASH_FLAG) {} diff --git a/tmk_core/common/test/rules.mk b/tmk_core/common/test/rules.mk new file mode 100644 index 0000000000..e47e5880c5 --- /dev/null +++ b/tmk_core/common/test/rules.mk @@ -0,0 +1,23 @@ +eeprom_stm32_DEFS := -DFLASH_STM32_MOCKED -DNO_PRINT -DFEE_FLASH_BASE=FlashBuf +eeprom_stm32_tiny_DEFS := $(eeprom_stm32_DEFS) \ + -DFEE_MCU_FLASH_SIZE=1 \ + -DMOCK_FLASH_SIZE=1024 \ + -DFEE_PAGE_SIZE=512 \ + -DFEE_DENSITY_PAGES=1 +eeprom_stm32_large_DEFS := $(eeprom_stm32_DEFS) \ + -DFEE_MCU_FLASH_SIZE=64 \ + -DMOCK_FLASH_SIZE=65536 \ + -DFEE_PAGE_SIZE=2048 \ + -DFEE_DENSITY_PAGES=16 + +eeprom_stm32_INC := \ + $(TMK_PATH)/common/chibios/ +eeprom_stm32_tiny_INC := $(eeprom_stm32_INC) +eeprom_stm32_large_INC := $(eeprom_stm32_INC) + +eeprom_stm32_SRC := \ + $(TMK_PATH)/common/test/eeprom_stm32_tests.cpp \ + $(TMK_PATH)/common/test/flash_stm32_mock.c \ + $(TMK_PATH)/common/chibios/eeprom_stm32.c +eeprom_stm32_tiny_SRC := $(eeprom_stm32_SRC) +eeprom_stm32_large_SRC := $(eeprom_stm32_SRC) diff --git a/tmk_core/common/test/testlist.mk b/tmk_core/common/test/testlist.mk new file mode 100644 index 0000000000..51a9638bb9 --- /dev/null +++ b/tmk_core/common/test/testlist.mk @@ -0,0 +1 @@ +TEST_LIST += eeprom_stm32_tiny eeprom_stm32_large diff --git a/util/stm32eeprom_parser.py b/util/stm32eeprom_parser.py new file mode 100755 index 0000000000..b124f713d5 --- /dev/null +++ b/util/stm32eeprom_parser.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python +# +# Copyright 2021 Don Kjer +# +# 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 . +# + +from __future__ import print_function + +import argparse +from struct import pack, unpack +import os, sys + +MAGIC_FEEA = '\xea\xff\xfe\xff' + +MAGIC_FEE9 = '\x16\x01' +EMPTY_WORD = '\xff\xff' +WORD_ENCODING = 0x8000 +VALUE_NEXT = 0x6000 +VALUE_RESERVED = 0x4000 +VALUE_ENCODED = 0x2000 +BYTE_RANGE = 0x80 + +CHUNK_SIZE = 1024 + +STRUCT_FMTS = { + 1: 'B', + 2: 'H', + 4: 'I' +} +PRINTABLE='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' + +EECONFIG_V1 = [ + ("MAGIC", 0, 2), + ("DEBUG", 2, 1), + ("DEFAULT_LAYER", 3, 1), + ("KEYMAP", 4, 1), + ("MOUSEKEY_ACCEL", 5, 1), + ("BACKLIGHT", 6, 1), + ("AUDIO", 7, 1), + ("RGBLIGHT", 8, 4), + ("UNICODEMODE", 12, 1), + ("STENOMODE", 13, 1), + ("HANDEDNESS", 14, 1), + ("KEYBOARD", 15, 4), + ("USER", 19, 4), + ("VELOCIKEY", 23, 1), + ("HAPTIC", 24, 4), + ("MATRIX", 28, 4), + ("MATRIX_EXTENDED", 32, 2), + ("KEYMAP_UPPER_BYTE", 34, 1), +] +VIABASE_V1 = 35 + +VERBOSE = False + +def parseArgs(): + parser = argparse.ArgumentParser(description='Decode an STM32 emulated eeprom dump') + parser.add_argument('-s', '--size', type=int, + help='Size of the emulated eeprom (default: input_size / 2)') + parser.add_argument('-o', '--output', help='File to write decoded eeprom to') + parser.add_argument('-y', '--layout-options-size', type=int, + help='VIA layout options size (default: 1)', default=1) + parser.add_argument('-t', '--custom-config-size', type=int, + help='VIA custom config size (default: 0)', default=0) + parser.add_argument('-l', '--layers', type=int, + help='VIA keyboard layers (default: 4)', default=4) + parser.add_argument('-r', '--rows', type=int, help='VIA matrix rows') + parser.add_argument('-c', '--cols', type=int, help='VIA matrix columns') + parser.add_argument('-m', '--macros', type=int, + help='VIA macro count (default: 16)', default=16) + parser.add_argument('-C', '--canonical', action='store_true', + help='Canonical hex+ASCII display.') + parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') + parser.add_argument('input', help='Raw contents of the STM32 flash area used to emulate eeprom') + return parser.parse_args() + + +def decodeEepromFEEA(in_file, size): + decoded=size*[None] + pos = 0 + while True: + chunk = in_file.read(CHUNK_SIZE) + for i in range(0, len(chunk), 2): + decoded[pos] = unpack('B', chunk[i])[0] + pos += 1 + if pos >= size: + break + + if len(chunk) < CHUNK_SIZE or pos >= size: + break + return decoded + +def decodeEepromFEE9(in_file, size): + decoded=size*[None] + pos = 0 + # Read compacted flash + while True: + read_size = min(size - pos, CHUNK_SIZE) + chunk = in_file.read(read_size) + for i in range(len(chunk)): + decoded[pos] = unpack('B', chunk[i])[0] ^ 0xFF + pos += 1 + if pos >= size: + break + + if len(chunk) < read_size or pos >= size: + break + if VERBOSE: + print("COMPACTED EEPROM:") + dumpBinary(decoded, True) + print("WRITE LOG:") + # Read write log + while True: + entry = in_file.read(2) + if len(entry) < 2: + print("Partial log address at position 0x%04x" % pos, file=sys.stderr) + break + pos += 2 + + if entry == EMPTY_WORD: + break + + be_entry = unpack('>H', entry)[0] + entry = unpack('H', entry)[0] + if not (entry & WORD_ENCODING): + address = entry >> 8 + decoded[address] = entry & 0xFF + if VERBOSE: + print("[0x%04x]: BYTE 0x%02x = 0x%02x" % (be_entry, address, decoded[address])) + else: + if (entry & VALUE_NEXT) == VALUE_NEXT: + # Read next word as value + value = in_file.read(2) + if len(value) < 2: + print("Partial log value at position 0x%04x" % pos, file=sys.stderr) + break + pos += 2 + address = entry & 0x1FFF + address <<= 1 + address += BYTE_RANGE + decoded[address] = unpack('B', value[0])[0] ^ 0xFF + decoded[address+1] = unpack('B', value[1])[0] ^ 0xFF + be_value = unpack('>H', value)[0] + if VERBOSE: + print("[0x%04x 0x%04x]: WORD 0x%04x = 0x%02x%02x" % (be_entry, be_value, address, decoded[address+1], decoded[address])) + else: + # Reserved for future use + if entry & VALUE_RESERVED: + if VERBOSE: + print("[0x%04x]: RESERVED 0x%04x" % (be_entry, address)) + continue + address = entry & 0x1FFF + address <<= 1 + decoded[address] = (entry & VALUE_ENCODED) >> 13 + decoded[address+1] = 0 + if VERBOSE: + print("[0x%04x]: ENCODED 0x%04x = 0x%02x%02x" % (be_entry, address, decoded[address+1], decoded[address])) + + return decoded + +def dumpBinary(data, canonical): + def display(pos, row): + print("%04x" % pos, end='') + for i in range(len(row)): + if i % 8 == 0: + print(" ", end='') + char = row[i] + if char is None: + print(" ", end='') + else: + print(" %02x" % row[i], end='') + if canonical: + print(" |", end='') + for i in range(len(row)): + char = row[i] + if char is None: + char = " " + else: + char = chr(char) + if char not in PRINTABLE: + char = "." + print(char, end='') + print("|", end='') + + print("") + + size = len(data) + empty_rows = 0 + prev_row = '' + first_repeat = True + for pos in range(0, size, 16): + row=data[pos:pos+16] + row[len(row):16] = (16-len(row))*[None] + if row == prev_row: + if first_repeat: + print("*") + first_repeat = False + else: + first_repeat = True + display(pos, row) + prev_row = row + print("%04x" % (pos+16)) + +def dumpEeconfig(data, eeconfig): + print("EECONFIG:") + for (name, pos, length) in eeconfig: + fmt = STRUCT_FMTS[length] + value = unpack(fmt, ''.join([chr(x) for x in data[pos:pos+length]]))[0] + print(("%%04x %%s = 0x%%0%dx" % (length * 2)) % (pos, name, value)) + +def dumpVia(data, base, layers, cols, rows, macros, + layout_options_size, custom_config_size): + magicYear = data[base + 0] + magicMonth = data[base + 1] + magicDay = data[base + 2] + # Sanity check + if not 10 <= magicYear <= 0x99 or \ + not 0 <= magicMonth <= 0x12 or \ + not 0 <= magicDay <= 0x31: + print("ERROR: VIA Signature is not valid; Year:%x, Month:%x, Day:%x" % (magicYear, magicMonth, magicDay)) + return + if cols is None or rows is None: + print("ERROR: VIA dump requires specifying --rows and --cols", file=sys.stderr) + return 2 + print("VIA:") + # Decode magic + print("%04x MAGIC = 20%02x-%02x-%02x" % (base, magicYear, magicMonth, magicDay)) + # Decode layout options + options = 0 + pos = base + 3 + for i in range(base+3, base+3+layout_options_size): + options = options << 8 + options |= data[i] + print(("%%04x LAYOUT_OPTIONS = 0x%%0%dx" % (layout_options_size * 2)) % (pos, options)) + pos += layout_options_size + custom_config_size + # Decode keycodes + keymap_size = layers * rows * cols * 2 + if (pos + keymap_size) >= (len(data) - 1): + print("ERROR: VIA keymap requires %d bytes, but only %d available" % (keymap_size, len(data) - pos)) + return 3 + for layer in range(layers): + print("%s LAYER %d %s" % ('-'*int(cols*2.5), layer, '-'*int(cols*2.5))) + for row in range(rows): + print("%04x | " % pos, end='') + for col in range(cols): + keycode = (data[pos] << 8) | (data[pos+1]) + print(" %04x" % keycode, end='') + pos += 2 + print("") + # Decode macros + for macro_num in range(macros): + macro = "" + macro_pos = pos + while pos < len(data): + char = chr(data[pos]) + pos += 1 + if char == '\x00': + print("%04x MACRO[%d] = '%s'" % (macro_pos, macro_num, macro)) + break + else: + macro += char + return 0 + + +def decodeSTM32Eeprom(input, canonical, size=None, output=None, **kwargs): + input_size = os.path.getsize(input) + if size is None: + size = input_size >> 1 + + # Read the first few bytes to check magic signature + with open(input, 'rb') as in_file: + magic=in_file.read(4) + in_file.seek(0) + + if magic == MAGIC_FEEA: + decoded = decodeEepromFEEA(in_file, size) + eeconfig = EECONFIG_V1 + via_base = VIABASE_V1 + elif magic[:2] == MAGIC_FEE9: + decoded = decodeEepromFEE9(in_file, size) + eeconfig = EECONFIG_V1 + via_base = VIABASE_V1 + else: + print("Unknown magic signature: %s" % " ".join(["0x%02x" % ord(x) for x in magic]), file=sys.stderr) + return 1 + + if output is not None: + with open(output, 'wb') as out_file: + out_file.write(pack('%dB' % len(decoded), *decoded)) + print("DECODED EEPROM:") + dumpBinary(decoded, canonical) + dumpEeconfig(decoded, eeconfig) + if kwargs['rows'] is not None and kwargs['cols'] is not None: + return dumpVia(decoded, via_base, **kwargs) + + return 0 + +def main(): + global VERBOSE + kwargs = vars(parseArgs()) + VERBOSE = kwargs.pop('verbose') + return decodeSTM32Eeprom(**kwargs) + +if __name__ == '__main__': + sys.exit(main()) -- cgit v1.2.3 From c9c60d227e38abdf08ca86bcdad3a0cac2579e3f Mon Sep 17 00:00:00 2001 From: Takeshi ISHII <2170248+mtei@users.noreply.github.com> Date: Tue, 24 Aug 2021 22:34:24 +0900 Subject: add wait_cpuclock() macro for AVR and CPU_CLOCK macro (#12755) --- tmk_core/common/avr/_wait.h | 4 +++- tmk_core/common/chibios/_wait.h | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'tmk_core/common') diff --git a/tmk_core/common/avr/_wait.h b/tmk_core/common/avr/_wait.h index ae1a25131a..683db6ae57 100644 --- a/tmk_core/common/avr/_wait.h +++ b/tmk_core/common/avr/_wait.h @@ -37,6 +37,8 @@ } \ } \ } while (0) +#define wait_cpuclock(n) __builtin_avr_delay_cycles(n) +#define CPU_CLOCK F_CPU /* The AVR series GPIOs have a one clock read delay for changes in the digital input signal. * But here's more margin to make it two clocks. */ @@ -44,4 +46,4 @@ # define GPIO_INPUT_PIN_DELAY 2 #endif -#define waitInputPinDelay() __builtin_avr_delay_cycles(GPIO_INPUT_PIN_DELAY) +#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) diff --git a/tmk_core/common/chibios/_wait.h b/tmk_core/common/chibios/_wait.h index 4a5172536b..b740afbd24 100644 --- a/tmk_core/common/chibios/_wait.h +++ b/tmk_core/common/chibios/_wait.h @@ -41,6 +41,10 @@ void wait_us(uint16_t duration); } while (0) #endif +#include "_wait.c" + +#define CPU_CLOCK STM32_SYSCLK + /* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus * to which the GPIO is connected. * The connected buses differ depending on the various series of MCUs. @@ -51,11 +55,8 @@ void wait_us(uint16_t duration); * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used. * (A fairly large value of 0.25 microseconds is set.) */ - -#include "_wait.c" - #ifndef GPIO_INPUT_PIN_DELAY -# define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4) +# define GPIO_INPUT_PIN_DELAY (CPU_CLOCK / 1000000L / 4) #endif #define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) -- cgit v1.2.3