From a8d0ec0749046b0ab89c18b1b7083b1e8674de2a Mon Sep 17 00:00:00 2001 From: XScorpion2 Date: Tue, 1 Dec 2020 12:04:42 -0600 Subject: [Split] Sync Timer feature (#10997) A timer that is kept in sync between the halves of a split keyboard --- quantum/quantum.h | 1 + quantum/rgb_matrix.c | 6 ++--- quantum/rgblight.c | 21 +++++++-------- quantum/split_common/transport.c | 26 +++++++++++++++++- tmk_core/common.mk | 1 + tmk_core/common/keyboard.c | 2 ++ tmk_core/common/sync_timer.c | 58 ++++++++++++++++++++++++++++++++++++++++ tmk_core/common/sync_timer.h | 54 +++++++++++++++++++++++++++++++++++++ 8 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 tmk_core/common/sync_timer.c create mode 100644 tmk_core/common/sync_timer.h diff --git a/quantum/quantum.h b/quantum/quantum.h index cb0af306ac..59c3dd4904 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -53,6 +53,7 @@ #include "eeconfig.h" #include "bootloader.h" #include "timer.h" +#include "sync_timer.h" #include "config_common.h" #include "led.h" #include "action_util.h" diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index f239bd582f..a3dd51f7b3 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c @@ -266,9 +266,9 @@ static bool rgb_matrix_none(effect_params_t *params) { static void rgb_task_timers(void) { #if defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0 - uint32_t deltaTime = timer_elapsed32(rgb_timer_buffer); + uint32_t deltaTime = sync_timer_elapsed32(rgb_timer_buffer); #endif // defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0 - rgb_timer_buffer = timer_read32(); + rgb_timer_buffer = sync_timer_read32(); // Update double buffer timers #if RGB_DISABLE_TIMEOUT > 0 @@ -296,7 +296,7 @@ static void rgb_task_timers(void) { static void rgb_task_sync(void) { // next task - if (timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING; + if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING; } static void rgb_task_start(void) { diff --git a/quantum/rgblight.c b/quantum/rgblight.c index 7f9e330d37..65308572db 100644 --- a/quantum/rgblight.c +++ b/quantum/rgblight.c @@ -29,7 +29,7 @@ #endif #include "wait.h" #include "progmem.h" -#include "timer.h" +#include "sync_timer.h" #include "rgblight.h" #include "color.h" #include "debug.h" @@ -684,18 +684,16 @@ static void rgblight_layers_write(void) { # ifdef RGBLIGHT_LAYER_BLINK rgblight_layer_mask_t _blinked_layer_mask = 0; -uint16_t _blink_duration = 0; static uint16_t _blink_timer; void rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) { rgblight_set_layer_state(layer, true); _blinked_layer_mask |= 1 << layer; - _blink_timer = timer_read(); - _blink_duration = duration_ms; + _blink_timer = sync_timer_read() + duration_ms; } void rgblight_unblink_layers(void) { - if (_blinked_layer_mask != 0 && timer_elapsed(_blink_timer) > _blink_duration) { + if (_blinked_layer_mask != 0 && timer_expired(sync_timer_read(), _blink_timer)) { for (uint8_t layer = 0; layer < RGBLIGHT_MAX_LAYERS; layer++) { if ((_blinked_layer_mask & 1 << layer) != 0) { rgblight_set_layer_state(layer, false); @@ -799,7 +797,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 */ @@ -832,7 +830,7 @@ void rgblight_timer_enable(void) { if (!is_static_effect(rgblight_config.mode)) { rgblight_status.timer_enabled = true; } - animation_status.last_timer = timer_read(); + animation_status.last_timer = sync_timer_read(); RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE; dprintf("rgblight timer enabled.\n"); } @@ -941,18 +939,19 @@ void rgblight_task(void) { # endif if (animation_status.restart) { animation_status.restart = false; - animation_status.last_timer = timer_read() - interval_time - 1; + animation_status.last_timer = sync_timer_read(); animation_status.pos16 = 0; // restart signal to local each effect } - if (timer_elapsed(animation_status.last_timer) >= interval_time) { + uint16_t now = sync_timer_read(); + if (timer_expired(now, animation_status.last_timer)) { # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) static uint16_t report_last_timer = 0; static bool tick_flag = false; uint16_t oldpos16; if (tick_flag) { tick_flag = false; - if (timer_elapsed(report_last_timer) >= 30000) { - report_last_timer = timer_read(); + if (timer_expired(now, report_last_timer)) { + report_last_timer += 30000; dprintf("rgblight animation tick report to slave\n"); RGBLIGHT_SPLIT_ANIMATION_TICK; } diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index 467ff81a97..6856b60558 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c @@ -6,6 +6,7 @@ #include "quantum.h" #define ROWS_PER_HAND (MATRIX_ROWS / 2) +#define SYNC_TIMER_OFFSET 2 #ifdef RGBLIGHT_ENABLE # include "rgblight.h" @@ -27,6 +28,9 @@ static pin_t encoders_pad[] = ENCODERS_PAD_A; # include "i2c_slave.h" typedef struct _I2C_slave_buffer_t { +# ifndef DISABLE_SYNC_TIMER + uint32_t sync_timer; +# endif matrix_row_t smatrix[ROWS_PER_HAND]; uint8_t backlight_level; # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) @@ -44,6 +48,7 @@ static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_re # define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level) # define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync) +# define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer) # define I2C_KEYMAP_START offsetof(I2C_slave_buffer_t, smatrix) # define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state) # define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm) @@ -91,10 +96,18 @@ bool transport_master(matrix_row_t matrix[]) { } } # 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 matrix[]) { +# ifndef DISABLE_SYNC_TIMER + sync_timer_update(i2c_buffer->sync_timer); +# endif // Copy matrix to I2C buffer memcpy((void *)i2c_buffer->smatrix, (void *)matrix, sizeof(i2c_buffer->smatrix)); @@ -133,12 +146,15 @@ 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 { +# ifndef DISABLE_SYNC_TIMER + uint32_t sync_timer; +# endif # ifdef BACKLIGHT_ENABLE uint8_t backlight_level; # endif @@ -251,11 +267,19 @@ bool transport_master(matrix_row_t matrix[]) { // Write wpm to slave serial_m2s_buffer.current_wpm = get_current_wpm(); # 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 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] = matrix[i]; diff --git a/tmk_core/common.mk b/tmk_core/common.mk index fdf2aa0972..05839824c0 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -18,6 +18,7 @@ TMK_COMMON_SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/report.c \ $(PLATFORM_COMMON_DIR)/suspend.c \ $(PLATFORM_COMMON_DIR)/timer.c \ + $(COMMON_DIR)/sync_timer.c \ $(PLATFORM_COMMON_DIR)/bootloader.c \ ifeq ($(PLATFORM),AVR) diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 8c7bdc8b55..a1fbc01da6 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -23,6 +23,7 @@ along with this program. If not, see . #include "led.h" #include "keycode.h" #include "timer.h" +#include "sync_timer.h" #include "print.h" #include "debug.h" #include "command.h" @@ -255,6 +256,7 @@ __attribute__((weak)) void housekeeping_task_user(void) {} */ void keyboard_init(void) { timer_init(); + sync_timer_init(); matrix_init(); #ifdef VIA_ENABLE via_init(); diff --git a/tmk_core/common/sync_timer.c b/tmk_core/common/sync_timer.c new file mode 100644 index 0000000000..de24b463b6 --- /dev/null +++ b/tmk_core/common/sync_timer.c @@ -0,0 +1,58 @@ +/* +Copyright (C) 2020 Ryan Caltabiano + +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. + +If you happen to meet one of the copyright holders in a bar you are obligated +to buy them one pint of beer. + +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. +*/ + +#include "sync_timer.h" +#include "keyboard.h" + +#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER) +volatile int32_t sync_timer_ms; + +void sync_timer_init(void) { sync_timer_ms = 0; } + +void sync_timer_update(uint32_t time) { + if (is_keyboard_master()) return; + sync_timer_ms = time - timer_read32(); +} + +uint16_t sync_timer_read(void) { + if (is_keyboard_master()) return timer_read(); + return sync_timer_read32(); +} + +uint32_t sync_timer_read32(void) { + if (is_keyboard_master()) return timer_read32(); + return sync_timer_ms + timer_read32(); +} + +uint16_t sync_timer_elapsed(uint16_t last) { + if (is_keyboard_master()) return timer_elapsed(last); + return TIMER_DIFF_16(sync_timer_read(), last); +} + +uint32_t sync_timer_elapsed32(uint32_t last) { + if (is_keyboard_master()) return timer_elapsed32(last); + return TIMER_DIFF_32(sync_timer_read32(), last); +} +#endif diff --git a/tmk_core/common/sync_timer.h b/tmk_core/common/sync_timer.h new file mode 100644 index 0000000000..9ddef45bb2 --- /dev/null +++ b/tmk_core/common/sync_timer.h @@ -0,0 +1,54 @@ +/* +Copyright (C) 2020 Ryan Caltabiano + +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. + +If you happen to meet one of the copyright holders in a bar you are obligated +to buy them one pint of beer. + +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 "timer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER) +void sync_timer_init(void); +void sync_timer_update(uint32_t time); +uint16_t sync_timer_read(void); +uint32_t sync_timer_read32(void); +uint16_t sync_timer_elapsed(uint16_t last); +uint32_t sync_timer_elapsed32(uint32_t last); +#else +# define sync_timer_init() +# define sync_timer_clear() +# define sync_timer_update(t) +# define sync_timer_read() timer_read() +# define sync_timer_read32() timer_read32() +# define sync_timer_elapsed(t) timer_elapsed(t) +# define sync_timer_elapsed32(t) timer_elapsed32(t) +#endif + +#ifdef __cplusplus +} +#endif -- cgit v1.2.1