summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--common_features.mk38
-rw-r--r--docs/config_options.md11
-rw-r--r--docs/getting_started_make_guide.md14
-rw-r--r--keyboards/handwired/xealous/debounce.c63
-rw-r--r--keyboards/handwired/xealous/rules.mk10
-rw-r--r--keyboards/lets_split/sockets/config.h9
-rw-r--r--keyboards/lets_split/sockets/rules.mk2
-rw-r--r--keyboards/miniaxe/config.h5
-rw-r--r--keyboards/miniaxe/matrix.c641
-rw-r--r--keyboards/miniaxe/rules.mk3
-rw-r--r--quantum/config_common.h3
-rw-r--r--quantum/debounce.c52
-rw-r--r--quantum/debounce.h11
-rw-r--r--quantum/matrix.c79
-rw-r--r--quantum/split_common/i2c.h5
-rw-r--r--quantum/split_common/matrix.c639
-rw-r--r--quantum/split_common/matrix.h30
-rw-r--r--quantum/split_common/serial.h5
-rw-r--r--quantum/split_common/split_flags.h9
-rw-r--r--quantum/split_common/split_util.c152
-rw-r--r--quantum/split_common/split_util.h15
-rw-r--r--quantum/split_common/transport.c224
-rw-r--r--quantum/split_common/transport.h10
-rw-r--r--tmk_core/common/keyboard.h2
24 files changed, 711 insertions, 1321 deletions
diff --git a/common_features.mk b/common_features.mk
index 572a6db548..8c3361732c 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -254,20 +254,34 @@ QUANTUM_SRC:= \
     $(QUANTUM_DIR)/keymap_common.c \
     $(QUANTUM_DIR)/keycode_config.c
 
-ifeq ($(strip $(SPLIT_KEYBOARD)), yes)
-    ifneq ($(strip $(CUSTOM_MATRIX)), yes)
-       QUANTUM_SRC += $(QUANTUM_DIR)/split_common/matrix.c
-       # Do not use $(QUANTUM_DIR)/matrix.c.
-       CUSTOM_MATRIX=yes
+# Include the standard or split matrix code if needed
+ifneq ($(strip $(CUSTOM_MATRIX)), yes)
+    ifeq ($(strip $(SPLIT_KEYBOARD)), yes)
+        QUANTUM_SRC += $(QUANTUM_DIR)/split_common/matrix.c
+    else
+        QUANTUM_SRC += $(QUANTUM_DIR)/matrix.c
     endif
+endif
+
+# Include the standard debounce code if needed
+ifneq ($(strip $(CUSTOM_DEBOUNCE)), yes)
+    QUANTUM_SRC += $(QUANTUM_DIR)/debounce.c
+endif
+
+ifeq ($(strip $(SPLIT_KEYBOARD)), yes)
     OPT_DEFS += -DSPLIT_KEYBOARD
+
+    # Include files used by all split keyboards
     QUANTUM_SRC += $(QUANTUM_DIR)/split_common/split_flags.c \
-                $(QUANTUM_DIR)/split_common/split_util.c
-    QUANTUM_LIB_SRC += $(QUANTUM_DIR)/split_common/i2c.c
-    QUANTUM_LIB_SRC += $(QUANTUM_DIR)/split_common/serial.c
+                   $(QUANTUM_DIR)/split_common/split_util.c
+
+    # Determine which (if any) transport files are required
+    ifneq ($(strip $(SPLIT_TRANSPORT)), custom)
+        QUANTUM_SRC += $(QUANTUM_DIR)/split_common/transport.c
+        # 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 both drivers here without bloat.
+        QUANTUM_LIB_SRC += $(QUANTUM_DIR)/split_common/i2c.c \
+                           $(QUANTUM_DIR)/split_common/serial.c
+    endif
     COMMON_VPATH += $(QUANTUM_PATH)/split_common
 endif
-
-ifneq ($(strip $(CUSTOM_MATRIX)), yes)
-    QUANTUM_SRC += $(QUANTUM_DIR)/matrix.c
-endif
diff --git a/docs/config_options.md b/docs/config_options.md
index 63bcc41d0a..f5c2e76e7e 100644
--- a/docs/config_options.md
+++ b/docs/config_options.md
@@ -143,7 +143,7 @@ If you define these options you will enable the associated feature, which may in
   * Breaks any Tap Toggle functionality (`TT` or the One Shot Tap Toggle)
 * `#define LEADER_TIMEOUT 300`
   * how long before the leader key times out
-    * If you're having issues finishing the sequence before it times out, you may need to increase the timeout setting. Or you may want to enable the `LEADER_PER_KEY_TIMING` option, which resets the timeout after each key is tapped. 
+    * If you're having issues finishing the sequence before it times out, you may need to increase the timeout setting. Or you may want to enable the `LEADER_PER_KEY_TIMING` option, which resets the timeout after each key is tapped.
 * `#define LEADER_PER_KEY_TIMING`
   * sets the timer for leader key chords to run on each key press rather than overall
 * `#define LEADER_KEY_STRICT_KEY_PROCESSING`
@@ -197,6 +197,9 @@ If you define these options you will enable the associated feature, which may in
 
 Split Keyboard specific options, make sure you have 'SPLIT_KEYBOARD = yes' in your rules.mk
 
+* `SPLIT_TRANSPORT = custom`
+  * Allows replacing the standard split communication routines with a custom one. ARM based split keyboards must use this at present.
+
 ### Setting Handedness
 
 One thing to remember, the side that the USB port is plugged into is always the master half. The side not plugged into USB is the slave.
@@ -208,7 +211,7 @@ There are a few different ways to set handedness for split keyboards (listed in
 3. Set `MASTER_RIGHT`: Half that is plugged into the USB port is determined to be the master and right half (inverse of the default)
 4. Default: The side that is plugged into the USB port is the master half and is assumed to be the left half. The slave side is the right half
 
-* `#define SPLIT_HAND_PIN B7` 
+* `#define SPLIT_HAND_PIN B7`
   * For using high/low pin to determine handedness, low = right hand, high = left hand. Replace `B7` with the pin you are using. This is optional, and if you leave `SPLIT_HAND_PIN` undefined, then you can still use the EE_HANDS method or MASTER_LEFT / MASTER_RIGHT defines like the stock Let's Split uses.
 
 * `#define EE_HANDS` (only works if `SPLIT_HAND_PIN` is not defined)
@@ -302,6 +305,10 @@ Use these to enable or disable building certain features. The more you have enab
   * Current options are AdafruitEzKey, AdafruitBLE, RN42
 * `SPLIT_KEYBOARD`
   * Enables split keyboard support (dual MCU like the let's split and bakingpy's boards) and includes all necessary files located at quantum/split_common
+* `CUSTOM_MATRIX`
+  * Allows replacing the standard matrix scanning routine with a custom one.
+* `CUSTOM_DEBOUNCE`
+  * Allows replacing the standard key debouncing routine with a custom one.
 * `WAIT_FOR_USB`
   * Forces the keyboard to wait for a USB connection to be established before it starts up
 * `NO_USB_STARTUP_CHECK`
diff --git a/docs/getting_started_make_guide.md b/docs/getting_started_make_guide.md
index adc1aed751..bb7e1e7e3b 100644
--- a/docs/getting_started_make_guide.md
+++ b/docs/getting_started_make_guide.md
@@ -97,7 +97,7 @@ This allows you to send Unicode characters using `UC(<code point>)` in your keym
 
 `UNICODEMAP_ENABLE`
 
-This allows you to send Unicode characters using `X(<map index>)` in your keymap. You will need to maintain a mapping table in your keymap file. All possible code points (up to `0x10FFFF`) are supported. 
+This allows you to send Unicode characters using `X(<map index>)` in your keymap. You will need to maintain a mapping table in your keymap file. All possible code points (up to `0x10FFFF`) are supported.
 
 `UCIS_ENABLE`
 
@@ -135,6 +135,18 @@ This enables [key lock](feature_key_lock.md). This consumes an additional 260 by
 
 This enables split keyboard support (dual MCU like the let's split and bakingpy's boards) and includes all necessary files located at quantum/split_common
 
+`SPLIT_TRANSPORT`
+
+As there is no standard split communication driver for ARM-based split keyboards yet, `SPLIT_TRANSPORT = custom` must be used for these. It will prevent the standard split keyboard communication code (which is AVR-specific) from being included, allowing a custom implementation to be used.
+
+`CUSTOM_MATRIX`
+
+Lets you replace the default matrix scanning routine with your own code. You will need to provide your own implementations of matrix_init() and matrix_scan().
+
+`CUSTOM_DEBOUNCE`
+
+Lets you replace the default key debouncing routine with your own code. You will need to provide your own implementation of debounce().
+
 ## Customizing Makefile Options on a Per-Keymap Basis
 
 If your keymap directory has a file called `rules.mk` any options you set in that file will take precedence over other `rules.mk` options for your particular keyboard.
diff --git a/keyboards/handwired/xealous/debounce.c b/keyboards/handwired/xealous/debounce.c
new file mode 100644
index 0000000000..65a99f27f2
--- /dev/null
+++ b/keyboards/handwired/xealous/debounce.c
@@ -0,0 +1,63 @@
+#include <string.h>
+#include "config.h"
+#include "matrix.h"
+#include "timer.h"
+#include "quantum.h"
+
+#ifndef DEBOUNCING_DELAY
+#   define DEBOUNCING_DELAY 5
+#endif
+
+//Debouncing counters
+typedef uint8_t debounce_counter_t;
+#define DEBOUNCE_COUNTER_MODULO 250
+#define DEBOUNCE_COUNTER_INACTIVE 251
+
+static debounce_counter_t *debounce_counters;
+
+void debounce_init(uint8_t num_rows)
+{
+  debounce_counters = malloc(num_rows*MATRIX_COLS);
+  memset(debounce_counters, DEBOUNCE_COUNTER_INACTIVE, num_rows*MATRIX_COLS);
+}
+
+void update_debounce_counters(uint8_t num_rows, uint8_t current_time)
+{
+  for (uint8_t row = 0; row < num_rows; row++)
+  {
+    for (uint8_t col = 0; col < MATRIX_COLS; col++)
+    {
+      if (debounce_counters[row*MATRIX_COLS + col] != DEBOUNCE_COUNTER_INACTIVE)
+      {
+        if (TIMER_DIFF(current_time, debounce_counters[row*MATRIX_COLS + col], DEBOUNCE_COUNTER_MODULO) >= DEBOUNCING_DELAY) {
+          debounce_counters[row*MATRIX_COLS + col] = DEBOUNCE_COUNTER_INACTIVE;
+        }
+      }
+    }
+  }
+}
+
+void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time)
+{
+    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 (debounce_counters[row*MATRIX_COLS + col] == DEBOUNCE_COUNTER_INACTIVE && (delta & (1<<col)))
+          {
+              debounce_counters[row*MATRIX_COLS + col] = current_time;
+              cooked[row] ^= (1 << col);
+          }
+      }
+    }
+}
+
+void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed)
+{
+    uint8_t current_time = timer_read() % DEBOUNCE_COUNTER_MODULO;
+
+    update_debounce_counters(num_rows, current_time);
+    transfer_matrix_values(raw, cooked, num_rows, current_time);
+}
\ No newline at end of file
diff --git a/keyboards/handwired/xealous/rules.mk b/keyboards/handwired/xealous/rules.mk
index eebd11d86e..07e1c875e6 100644
--- a/keyboards/handwired/xealous/rules.mk
+++ b/keyboards/handwired/xealous/rules.mk
@@ -1,4 +1,5 @@
-SRC += matrix_scanrate.c matrix.c 
+#SRC += matrix_scanrate.c matrix.c
+SRC += debounce.c
 
 # MCU name
 MCU = atmega32u4
@@ -37,7 +38,7 @@ F_USB = $(F_CPU)
 
 # Bootloader
 #     This definition is optional, and if your keyboard supports multiple bootloaders of
-#     different sizes, comment this out, and the correct address will be loaded 
+#     different sizes, comment this out, and the correct address will be loaded
 #     automatically (+60). See bootloader.mk for all options.
 BOOTLOADER = caterina
 
@@ -59,14 +60,15 @@ MIDI_ENABLE = no            # MIDI controls
 AUDIO_ENABLE = yes          # 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.
 SPLIT_KEYBOARD = yes        # Use shared split_common code
 SUBPROJECT_rev1 = yes
 
 # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
 SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend
 
-CUSTOM_MATRIX = yes
+CUSTOM_MATRIX = no
+CUSTOM_DEBOUNCE = yes
 
 LAYOUTS = split60
 
diff --git a/keyboards/lets_split/sockets/config.h b/keyboards/lets_split/sockets/config.h
index 6939d37dc5..e73c45722d 100644
--- a/keyboards/lets_split/sockets/config.h
+++ b/keyboards/lets_split/sockets/config.h
@@ -85,3 +85,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //#define NO_ACTION_ONESHOT
 //#define NO_ACTION_MACRO
 //#define NO_ACTION_FUNCTION
+
+#ifdef USE_Link_Time_Optimization
+  // LTO has issues with macros (action_get_macro) and "functions" (fn_actions),
+  //  so just disable them
+  #define NO_ACTION_MACRO
+  #define NO_ACTION_FUNCTION
+
+  #define DISABLE_LEADER
+#endif // USE_Link_Time_Optimization
\ No newline at end of file
diff --git a/keyboards/lets_split/sockets/rules.mk b/keyboards/lets_split/sockets/rules.mk
index e14d18d8de..da04decf40 100644
--- a/keyboards/lets_split/sockets/rules.mk
+++ b/keyboards/lets_split/sockets/rules.mk
@@ -1,3 +1,5 @@
 BACKLIGHT_ENABLE = no
 AUDIO_ENABLE = yes
 RGBLIGHT_ENABLE = yes #Don't enable this along with I2C
+
+EXTRAFLAGS += -flto -DUSE_Link_Time_Optimization
diff --git a/keyboards/miniaxe/config.h b/keyboards/miniaxe/config.h
index 2b732ca16f..7a68476a54 100644
--- a/keyboards/miniaxe/config.h
+++ b/keyboards/miniaxe/config.h
@@ -44,8 +44,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 // #define MATRIX_ROW_PINS { D0, D5 }
 // #define MATRIX_COL_PINS { F1, F0, B0 }
-#define NO_PIN 0xFF
-#define MATRIX_ROW_COL_PINS { \
+#define DIRECT_PINS { \
     { F1, E6, B0, B2, B3 }, \
     { F5, F0, B1, B7, D2 }, \
     { F6, F7, C7, D5, D3 }, \
@@ -54,7 +53,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define UNUSED_PINS
 
 /* COL2ROW, ROW2COL, or CUSTOM_MATRIX */
-#define DIODE_DIRECTION CUSTOM_MATRIX
+//#define DIODE_DIRECTION CUSTOM_MATRIX
 
 // #define BACKLIGHT_PIN B7
 // #define BACKLIGHT_BREATHING
diff --git a/keyboards/miniaxe/matrix.c b/keyboards/miniaxe/matrix.c
deleted file mode 100644
index 5fec1281df..0000000000
--- a/keyboards/miniaxe/matrix.c
+++ /dev/null
@@ -1,641 +0,0 @@
-/*
-Copyright 2012 Jun Wako <wakojun@gmail.com>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/*
- * scan matrix
- */
-#include <stdint.h>
-#include <stdbool.h>
-#include <avr/io.h>
-#include "wait.h"
-#include "print.h"
-#include "debug.h"
-#include "util.h"
-#include "matrix.h"
-#include "split_util.h"
-#include "pro_micro.h"
-#include "config.h"
-#include "timer.h"
-#include "split_flags.h"
-
-#ifdef BACKLIGHT_ENABLE
-#   include "backlight.h"
-    extern backlight_config_t backlight_config;
-#endif
-
-#if defined(USE_I2C) || defined(EH)
-#  include "i2c.h"
-#else // USE_SERIAL
-#  include "serial.h"
-#endif
-
-#ifndef DEBOUNCING_DELAY
-#   define DEBOUNCING_DELAY 5
-#endif
-
-#if (DEBOUNCING_DELAY > 0)
-    static uint16_t debouncing_time;
-    static bool debouncing = false;
-#endif
-
-#if defined(USE_I2C) || defined(EH)
-
-#if (MATRIX_COLS <= 8)
-#    define print_matrix_header()  print("\nr/c 01234567\n")
-#    define print_matrix_row(row)  print_bin_reverse8(matrix_get_row(row))
-#    define matrix_bitpop(i)       bitpop(matrix[i])
-#    define ROW_SHIFTER ((uint8_t)1)
-#else
-#    error "Currently only supports 8 COLS"
-#endif
-
-#else // USE_SERIAL
-
-#if (MATRIX_COLS <= 8)
-#    define print_matrix_header()  print("\nr/c 01234567\n")
-#    define print_matrix_row(row)  print_bin_reverse8(matrix_get_row(row))
-#    define matrix_bitpop(i)       bitpop(matrix[i])
-#    define ROW_SHIFTER ((uint8_t)1)
-#elif (MATRIX_COLS <= 16)
-#    define print_matrix_header()  print("\nr/c 0123456789ABCDEF\n")
-#    define print_matrix_row(row)  print_bin_reverse16(matrix_get_row(row))
-#    define matrix_bitpop(i)       bitpop16(matrix[i])
-#    define ROW_SHIFTER ((uint16_t)1)
-#elif (MATRIX_COLS <= 32)
-#    define print_matrix_header()  print("\nr/c 0123456789ABCDEF0123456789ABCDEF\n")
-#    define print_matrix_row(row)  print_bin_reverse32(matrix_get_row(row))
-#    define matrix_bitpop(i)       bitpop32(matrix[i])
-#    define ROW_SHIFTER  ((uint32_t)1)
-#endif
-
-#endif
-static matrix_row_t matrix_debouncing[MATRIX_ROWS];
-
-#define ERROR_DISCONNECT_COUNT 5
-
-#define ROWS_PER_HAND (MATRIX_ROWS/2)
-
-static uint8_t error_count = 0;
-
-#if ((DIODE_DIRECTION == COL2ROW) || (DIODE_DIRECTION == ROW2COL))
-static uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
-static uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
-#elif (DIODE_DIRECTION == CUSTOM_MATRIX)
-static uint8_t row_col_pins[MATRIX_ROWS][MATRIX_COLS] = MATRIX_ROW_COL_PINS;
-#endif
-
-/* matrix state(1:on, 0:off) */
-static matrix_row_t matrix[MATRIX_ROWS];
-static matrix_row_t matrix_debouncing[MATRIX_ROWS];
-
-#if (DIODE_DIRECTION == COL2ROW)
-    static void init_cols(void);
-    static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
-    static void unselect_rows(void);
-    static void select_row(uint8_t row);
-    static void unselect_row(uint8_t row);
-#elif (DIODE_DIRECTION == ROW2COL)
-    static void init_rows(void);
-    static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
-    static void unselect_cols(void);
-    static void unselect_col(uint8_t col);
-    static void select_col(uint8_t col);
-#elif (DIODE_DIRECTION == CUSTOM_MATRIX)
-    static void init_cols_rows(void);
-    static bool read_cols(matrix_row_t current_matrix[], uint8_t current_row);
-#endif
-
-__attribute__ ((weak))
-void matrix_init_kb(void) {
-    matrix_init_user();
-}
-
-__attribute__ ((weak))
-void matrix_scan_kb(void) {
-    matrix_scan_user();
-}
-
-__attribute__ ((weak))
-void matrix_init_user(void) {
-}
-
-__attribute__ ((weak))
-void matrix_scan_user(void) {
-}
-
-__attribute__ ((weak))
-void matrix_slave_scan_user(void) {
-}
-
-inline
-uint8_t matrix_rows(void)
-{
-    return MATRIX_ROWS;
-}
-
-inline
-uint8_t matrix_cols(void)
-{
-    return MATRIX_COLS;
-}
-
-void matrix_init(void)
-{
-#ifdef DISABLE_JTAG
-  // JTAG disable for PORT F. write JTD bit twice within four cycles.
-  MCUCR |= (1<<JTD);
-  MCUCR |= (1<<JTD);
-#endif
-
-    debug_enable = true;
-    debug_matrix = true;
-    debug_mouse = true;
-
-    // Set pinout for right half if pinout for that half is defined
-    if (!isLeftHand) {
-#ifdef MATRIX_ROW_PINS_RIGHT
-        const uint8_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT;
-        for (uint8_t i = 0; i < MATRIX_ROWS; i++)
-            row_pins[i] = row_pins_right[i];
-#endif
-#ifdef MATRIX_COL_PINS_RIGHT
-        const uint8_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT;
-        for (uint8_t i = 0; i < MATRIX_COLS; i++)
-            col_pins[i] = col_pins_right[i];
-#endif
-    }
-
-    // initialize row and col
-#if (DIODE_DIRECTION == COL2ROW)
-    unselect_rows();
-    init_cols();
-#elif (DIODE_DIRECTION == ROW2COL)
-    unselect_cols();
-    init_rows();
-#elif (DIODE_DIRECTION == CUSTOM_MATRIX)
-    init_cols_rows();
-#endif
-
-    // initialize matrix state: all keys off
-    for (uint8_t i=0; i < MATRIX_ROWS; i++) {
-        matrix[i] = 0;
-        matrix_debouncing[i] = 0;
-    }
-    
-    matrix_init_quantum();
-    
-}
-
-uint8_t _matrix_scan(void)
-{
-    int offset = isLeftHand ? 0 : (ROWS_PER_HAND);
-#if (DIODE_DIRECTION == COL2ROW)
-    // Set row, read cols
-    for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
-#       if (DEBOUNCING_DELAY > 0)
-            bool matrix_changed = read_cols_on_row(matrix_debouncing+offset, current_row);
-
-            if (matrix_changed) {
-                debouncing = true;
-                debouncing_time = timer_read();
-            }
-
-#       else
-            read_cols_on_row(matrix+offset, current_row);
-#       endif
-
-    }
-
-#elif (DIODE_DIRECTION == ROW2COL)
-    // Set col, read rows
-    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
-#       if (DEBOUNCING_DELAY > 0)
-            bool matrix_changed = read_rows_on_col(matrix_debouncing+offset, current_col);
-            if (matrix_changed) {
-                debouncing = true;
-                debouncing_time = timer_read();
-            }
-#       else
-             read_rows_on_col(matrix+offset, current_col);
-#       endif
-
-    }
-
-#elif (DIODE_DIRECTION == CUSTOM_MATRIX)
-    // Set row, read cols
-    for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
-#       if (DEBOUNCING_DELAY > 0)
-            bool matrix_changed = read_cols(matrix_debouncing+offset, current_row);
-            if (matrix_changed) {
-                debouncing = true;
-                debouncing_time = timer_read();
-            }
-#       else
-            read_cols(matrix+offset, current_row);
-#       endif
-    }
-#endif
-
-#   if (DEBOUNCING_DELAY > 0)
-        if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) {
-            for (uint8_t i = 0; i < ROWS_PER_HAND; i++) {
-                matrix[i+offset] = matrix_debouncing[i+offset];
-            }
-            debouncing = false;
-        }
-#   endif
-
-    return 1;
-}
-
-#if defined(USE_I2C) || defined(EH)
-
-// Get rows from other half over i2c
-int i2c_transaction(void) {
-    int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;
-    int err = 0;
-    
-    // write backlight info
-    #ifdef BACKLIGHT_ENABLE
-        if (BACKLIT_DIRTY) {
-            err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
-            if (err) goto i2c_error;
-            
-            // Backlight location
-            err = i2c_master_write(I2C_BACKLIT_START);
-            if (err) goto i2c_error;
-            
-            // Write backlight 
-            i2c_master_write(get_backlight_level());
-            
-            BACKLIT_DIRTY = false;
-        }
-    #endif
-
-    err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
-    if (err) goto i2c_error;
-
-    // start of matrix stored at I2C_KEYMAP_START
-    err = i2c_master_write(I2C_KEYMAP_START);
-    if (err) goto i2c_error;
-
-    // Start read
-    err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_READ);
-    if (err) goto i2c_error;
-
-    if (!err) {
-        int i;
-        for (i = 0; i < ROWS_PER_HAND-1; ++i) {
-            matrix[slaveOffset+i] = i2c_master_read(I2C_ACK);
-        }
-        matrix[slaveOffset+i] = i2c_master_read(I2C_NACK);
-        i2c_master_stop();
-    } else {
-i2c_error: // the cable is disconnceted, or something else went wrong
-        i2c_reset_state();
-        return err;
-    }
-    
-    #ifdef RGBLIGHT_ENABLE
-        if (RGB_DIRTY) {
-            err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
-            if (err) goto i2c_error;
-            
-            // RGB Location
-            err = i2c_master_write(I2C_RGB_START);
-            if (err) goto i2c_error;
-            
-            uint32_t dword = eeconfig_read_rgblight();
-            
-            // Write RGB
-            err = i2c_master_write_data(&dword, 4);
-            if (err) goto i2c_error;
-            
-            RGB_DIRTY = false;
-            i2c_master_stop();
-        }
-    #endif
-
-    return 0;
-}
-
-#else // USE_SERIAL
-
-
-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];
-} Serial_s2m_buffer_t;
-
-volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
-volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
-uint8_t volatile status0 = 0;
-
-SSTD_t transactions[] = {
-    { (uint8_t *)&status0,
-      sizeof(serial_m2s_buffer), (uint8_t *)&serial_m2s_buffer,
-      sizeof(serial_s2m_buffer), (uint8_t *)&serial_s2m_buffer
-  }
-};
-
-void serial_master_init(void)
-{ soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
-
-void serial_slave_init(void)
-{ soft_serial_target_init(transactions, TID_LIMIT(transactions)); }
-
-int serial_transaction(void) {
-    int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;
-
-    if (soft_serial_transaction()) {
-        return 1;
-    }
-
-    // TODO:  if MATRIX_COLS > 8 change to unpack()
-    for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        matrix[slaveOffset+i] = serial_s2m_buffer.smatrix[i];
-    }
-    
-    #ifdef RGBLIGHT_ENABLE
-        // Code to send RGB over serial goes here (not implemented yet)
-    #endif
-    
-    #ifdef BACKLIGHT_ENABLE
-        // Write backlight level for slave to read
-        serial_m2s_buffer.backlight_level = backlight_config.enable ? backlight_config.level : 0;
-    #endif
-
-    return 0;
-}
-#endif
-
-uint8_t matrix_scan(void)
-{
-    uint8_t ret = _matrix_scan();
-
-#if defined(USE_I2C) || defined(EH)
-    if( i2c_transaction() ) {
-#else // USE_SERIAL
-    if( serial_transaction() ) {
-#endif
-
-        error_count++;
-
-        if (error_count > ERROR_DISCONNECT_COUNT) {
-            // reset other half if disconnected
-            int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;
-            for (int i = 0; i < ROWS_PER_HAND; ++i) {
-                matrix[slaveOffset+i] = 0;
-            }
-        }
-    } else {
-        error_count = 0;
-    }
-    matrix_scan_quantum();
-    return ret;
-}
-
-void matrix_slave_scan(void) {
-    _matrix_scan();
-
-    int offset = (isLeftHand) ? 0 : ROWS_PER_HAND;
-
-#if defined(USE_I2C) || defined(EH)
-    for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        i2c_slave_buffer[I2C_KEYMAP_START+i] = matrix[offset+i];
-    }   
-#else // USE_SERIAL
-    // TODO: if MATRIX_COLS > 8 change to pack()
-    for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        serial_s2m_buffer.smatrix[i] = matrix[offset+i];
-    }
-#endif
-    matrix_slave_scan_user();
-}
-
-bool matrix_is_modified(void)
-{
-    if (debouncing) return false;
-    return true;
-}
-
-inline
-bool matrix_is_on(uint8_t row, uint8_t col)
-{
-    return (matrix[row] & ((matrix_row_t)1<<col));
-}
-
-inline
-matrix_row_t matrix_get_row(uint8_t row)
-{
-    return matrix[row];
-}
-
-void matrix_print(void)
-{
-    print("\nr/c 0123456789ABCDEF\n");
-    for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
-        phex(row); print(": ");
-        pbin_reverse16(matrix_get_row(row));
-        print("\n");
-    }
-}
-
-uint8_t matrix_key_count(void)
-{
-    uint8_t count = 0;
-    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
-        count += bitpop16(matrix[i]);
-    }
-    return count;
-}
-
-#if (DIODE_DIRECTION == COL2ROW)
-
-static void init_cols(void)
-{
-    for(uint8_t x = 0; x < MATRIX_COLS; x++) {
-        uint8_t pin = col_pins[x];
-        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
-        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
-    }
-}
-
-static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
-{
-    // Store last value of row prior to reading
-    matrix_row_t last_row_value = current_matrix[current_row];
-
-    // Clear data in matrix row
-    current_matrix[current_row] = 0;
-
-    // Select row and wait for row selecton to stabilize
-    select_row(current_row);
-    wait_us(30);
-
-    // For each col...
-    for(uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
-
-        // Select the col pin to read (active low)
-        uint8_t pin = col_pins[col_index];
-        uint8_t pin_state = (_SFR_IO8(pin >> 4) & _BV(pin & 0xF));
-
-        // Populate the matrix row with the state of the col pin
-        current_matrix[current_row] |=  pin_state ? 0 : (ROW_SHIFTER << col_index);
-    }
-
-    // Unselect row
-    unselect_row(current_row);
-
-    return (last_row_value != current_matrix[current_row]);
-}
-
-static void select_row(uint8_t row)
-{
-    uint8_t pin = row_pins[row];
-    _SFR_IO8((pin >> 4) + 1) |=  _BV(pin & 0xF); // OUT
-    _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF); // LOW
-}
-
-static void unselect_row(uint8_t row)
-{
-    uint8_t pin = row_pins[row];
-    _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
-    _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
-}
-
-static void unselect_rows(void)
-{
-    for(uint8_t x = 0; x < ROWS_PER_HAND; x++) {
-        uint8_t pin = row_pins[x];
-        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
-        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
-    }
-}
-
-#elif (DIODE_DIRECTION == ROW2COL)
-
-static void init_rows(void)
-{
-    for(uint8_t x = 0; x < ROWS_PER_HAND; x++) {
-        uint8_t pin = row_pins[x];
-        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
-        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
-    }
-}
-
-static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
-{
-    bool matrix_changed = false;
-
-    // Select col and wait for col selecton to stabilize
-    select_col(current_col);
-    wait_us(30);
-
-    // For each row...
-    for(uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++)
-    {
-
-        // Store last value of row prior to reading
-        matrix_row_t last_row_value = current_matrix[row_index];
-
-        // Check row pin state
-        if ((_SFR_IO8(row_pins[row_index] >> 4) & _BV(row_pins[row_index] & 0xF)) == 0)
-        {
-            // Pin LO, set col bit
-            current_matrix[row_index] |= (ROW_SHIFTER << current_col);
-        }
-        else
-        {
-            // Pin HI, clear col bit
-            current_matrix[row_index] &= ~(ROW_SHIFTER << current_col);
-        }
-
-        // Determine if the matrix changed state
-        if ((last_row_value != current_matrix[row_index]) && !(matrix_changed))
-        {
-            matrix_changed = true;
-        }
-    }
-
-    // Unselect col
-    unselect_col(current_col);
-
-    return matrix_changed;
-}
-
-static void select_col(uint8_t col)
-{
-    uint8_t pin = col_pins[col];
-    _SFR_IO8((pin >> 4) + 1) |=  _BV(pin & 0xF); // OUT
-    _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF); // LOW
-}
-
-static void unselect_col(uint8_t col)
-{
-    uint8_t pin = col_pins[col];
-    _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
-    _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
-}
-
-static void unselect_cols(void)
-{
-    for(uint8_t x = 0; x < MATRIX_COLS; x++) {
-        uint8_t pin = col_pins[x];
-        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
-        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
-    }
-}
-
-#elif (DIODE_DIRECTION == CUSTOM_MATRIX)
-
-static void init_cols_rows(void)
-{
-    for(int row = 0; row < MATRIX_ROWS; row++) {
-        for(int col = 0; col < MATRIX_COLS; col++) {
-            uint8_t pin = row_col_pins[row][col];
-            if(pin == NO_PIN) {
-                continue;
-            }
-            // DDxn set 0 for input
-            _SFR_IO8((pin >> 4) + 1) &=  ~_BV(pin & 0xF);
-            // PORTxn set 1 for input/pullup
-            _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF);
-        }
-    }
-}
-
-static bool read_cols(matrix_row_t current_matrix[], uint8_t current_row)
-{
-    matrix_row_t last_row_value = current_matrix[current_row];
-    current_matrix[current_row] = 0;
-
-    for(uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
-        uint8_t pin = row_col_pins[current_row][col_index];
-        if(pin == NO_PIN) {
-            current_matrix[current_row] |= 0;
-        }
-        else {
-            uint8_t pin_state = (_SFR_IO8(pin >> 4) & _BV(pin & 0xF));
-            current_matrix[current_row] |= pin_state ? 0 : (ROW_SHIFTER << col_index);
-        }
-    }
-
-    return (last_row_value != current_matrix[current_row]);
-}
-
-#endif
diff --git a/keyboards/miniaxe/rules.mk b/keyboards/miniaxe/rules.mk
index 96e27686b8..2f56a907ba 100644
--- a/keyboards/miniaxe/rules.mk
+++ b/keyboards/miniaxe/rules.mk
@@ -1,4 +1,3 @@
-SRC += matrix.c
 
 # MCU name
 #MCU = at90usb1286
@@ -83,6 +82,6 @@ FAUXCLICKY_ENABLE = no      # Use buzzer to emulate clicky switches
 HD44780_ENABLE = no 		# Enable support for HD44780 based LCDs (+400)
 
 DEBUG_ENABLE = no
-CUSTOM_MATRIX = yes         # Use custom matrix code 
+CUSTOM_MATRIX = no         # Use custom matrix code
 SPLIT_KEYBOARD = yes        # Use shared split_common code
 
diff --git a/quantum/config_common.h b/quantum/config_common.h
index 606cd9381a..0b2e408a43 100644
--- a/quantum/config_common.h
+++ b/quantum/config_common.h
@@ -21,6 +21,9 @@
 #define ROW2COL       1
 #define CUSTOM_MATRIX 2 /* Disables built-in matrix scanning code */
 
+// useful for direct pin mapping
+#define NO_PIN (~0)
+
 #ifdef __AVR__
     #ifndef __ASSEMBLER__
       #include <avr/io.h>
diff --git a/quantum/debounce.c b/quantum/debounce.c
new file mode 100644
index 0000000000..929023ab2d
--- /dev/null
+++ b/quantum/debounce.c
@@ -0,0 +1,52 @@
+
+#include "matrix.h"
+#include "timer.h"
+#include "quantum.h"
+
+#ifndef DEBOUNCING_DELAY
+#  define DEBOUNCING_DELAY 5
+#endif
+
+void debounce_init(uint8_t num_rows) {
+}
+
+#if DEBOUNCING_DELAY > 0
+
+static bool debouncing = false;
+
+void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
+  static uint16_t debouncing_time;
+
+  if (changed) {
+    debouncing = true;
+    debouncing_time = timer_read();
+  }
+
+  if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) {
+    for (uint8_t i = 0; i < num_rows; i++) {
+      cooked[i] = raw[i];
+    }
+    debouncing = false;
+  }
+}
+
+bool debounce_active(void) {
+  return debouncing;
+}
+
+#else
+
+// no debounce
+void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
+  if (changed)
+  {
+  for (uint8_t i = 0; i < num_rows; i++) {
+      cooked[i] = raw[i];
+    }
+  }
+}
+
+bool debounce_active(void) {
+  return false;
+}
+#endif
diff --git a/quantum/debounce.h b/quantum/debounce.h
new file mode 100644
index 0000000000..360af77e78
--- /dev/null
+++ b/quantum/debounce.h
@@ -0,0 +1,11 @@
+#pragma once
+
+// raw is the current key state
+// on entry cooked is the previous debounced state
+// on exit cooked is the current debounced state
+// changed is true if raw has changed since the last call
+void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);
+
+bool debounce_active(void);
+
+void debounce_init(uint8_t num_rows);
\ No newline at end of file
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 9b5ce33d23..49a1845696 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -21,21 +21,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "debug.h"
 #include "util.h"
 #include "matrix.h"
-#include "timer.h"
+#include "debounce.h"
 #include "quantum.h"
 
-
-/* Set 0 if debouncing isn't needed */
-
-#ifndef DEBOUNCING_DELAY
-#   define DEBOUNCING_DELAY 5
-#endif
-
-#if (DEBOUNCING_DELAY > 0)
-    static uint16_t debouncing_time;
-    static bool debouncing = false;
-#endif
-
 #if (MATRIX_COLS <= 8)
 #    define print_matrix_header()  print("\nr/c 01234567\n")
 #    define print_matrix_row(row)  print_bin_reverse8(matrix_get_row(row))
@@ -63,9 +51,9 @@ static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
 #endif
 
 /* matrix state(1:on, 0:off) */
-static matrix_row_t matrix[MATRIX_ROWS];
+static matrix_row_t raw_matrix[MATRIX_ROWS];
 
-static matrix_row_t matrix_debouncing[MATRIX_ROWS];
+static matrix_row_t matrix[MATRIX_ROWS];
 
 
 #if (DIODE_DIRECTION == COL2ROW)
@@ -157,70 +145,39 @@ void matrix_init(void) {
 
     // initialize matrix state: all keys off
     for (uint8_t i=0; i < MATRIX_ROWS; i++) {
+        raw_matrix[i] = 0;
         matrix[i] = 0;
-        matrix_debouncing[i] = 0;
     }
+    debounce_init(MATRIX_ROWS);
 
     matrix_init_quantum();
 }
 
 uint8_t matrix_scan(void)
 {
+  bool changed = false;
 
 #if (DIODE_DIRECTION == COL2ROW)
-
-    // Set row, read cols
-    for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
-#       if (DEBOUNCING_DELAY > 0)
-            bool matrix_changed = read_cols_on_row(matrix_debouncing, current_row);
-
-            if (matrix_changed) {
-                debouncing = true;
-                debouncing_time = timer_read();
-            }
-
-#       else
-            read_cols_on_row(matrix, current_row);
-#       endif
-
-    }
-
+  // Set row, read cols
+  for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
+    changed |= read_cols_on_row(raw_matrix, current_row);
+  }
 #elif (DIODE_DIRECTION == ROW2COL)
-
-    // Set col, read rows
-    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
-#       if (DEBOUNCING_DELAY > 0)
-            bool matrix_changed = read_rows_on_col(matrix_debouncing, current_col);
-            if (matrix_changed) {
-                debouncing = true;
-                debouncing_time = timer_read();
-            }
-#       else
-             read_rows_on_col(matrix, current_col);
-#       endif
-
-    }
-
+  // Set col, read rows
+  for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
+    changed |= read_rows_on_col(raw_matrix, current_col);
+  }
 #endif
 
-#   if (DEBOUNCING_DELAY > 0)
-        if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) {
-            for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
-                matrix[i] = matrix_debouncing[i];
-            }
-            debouncing = false;
-        }
-#   endif
+  debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
 
-    matrix_scan_quantum();
-    return 1;
+  matrix_scan_quantum();
+  return 1;
 }
 
 bool matrix_is_modified(void)
 {
-#if (DEBOUNCING_DELAY > 0)
-    if (debouncing) return false;
-#endif
+    if (debounce_active()) return false;
     return true;
 }
 
diff --git a/quantum/split_common/i2c.h b/quantum/split_common/i2c.h
index b3cbe8c826..91e8e96f47 100644
--- a/quantum/split_common/i2c.h
+++ b/quantum/split_common/i2c.h
@@ -1,5 +1,4 @@
-#ifndef I2C_H
-#define I2C_H
+#pragma once
 
 #include <stdint.h>
 
@@ -58,5 +57,3 @@ extern unsigned char i2c_readNak(void);
 extern unsigned char i2c_read(unsigned char ack);
 
 #define i2c_read(ack)  (ack) ? i2c_readAck() : i2c_readNak();
-
-#endif
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
index 2c37053f88..c3d2857ed5 100644
--- a/quantum/split_common/matrix.c
+++ b/quantum/split_common/matrix.c
@@ -25,529 +25,304 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "matrix.h"
 #include "split_util.h"
 #include "config.h"
-#include "timer.h"
 #include "split_flags.h"
 #include "quantum.h"
-
-#ifdef BACKLIGHT_ENABLE
-#   include "backlight.h"
-    extern backlight_config_t backlight_config;
-#endif
-
-#if defined(USE_I2C) || defined(EH)
-#  include "i2c.h"
-#else // USE_SERIAL
-#  include "serial.h"
-#endif
-
-#ifndef DEBOUNCING_DELAY
-#   define DEBOUNCING_DELAY 5
-#endif
-
-#if (DEBOUNCING_DELAY > 0)
-    static uint16_t debouncing_time;
-    static bool debouncing = false;
-#endif
-
-#if defined(USE_I2C) || defined(EH)
-
-#if (MATRIX_COLS <= 8)
-#    define print_matrix_header()  print("\nr/c 01234567\n")
-#    define print_matrix_row(row)  print_bin_reverse8(matrix_get_row(row))
-#    define matrix_bitpop(i)       bitpop(matrix[i])
-#    define ROW_SHIFTER ((uint8_t)1)
-#else
-#    error "Currently only supports 8 COLS"
-#endif
-
-#else // USE_SERIAL
+#include "debounce.h"
+#include "transport.h"
 
 #if (MATRIX_COLS <= 8)
-#    define print_matrix_header()  print("\nr/c 01234567\n")
-#    define print_matrix_row(row)  print_bin_reverse8(matrix_get_row(row))
-#    define matrix_bitpop(i)       bitpop(matrix[i])
-#    define ROW_SHIFTER ((uint8_t)1)
+#  define print_matrix_header() print("\nr/c 01234567\n")
+#  define print_matrix_row(row) print_bin_reverse8(matrix_get_row(row))
+#  define matrix_bitpop(i) bitpop(matrix[i])
+#  define ROW_SHIFTER ((uint8_t)1)
 #elif (MATRIX_COLS <= 16)
-#    define print_matrix_header()  print("\nr/c 0123456789ABCDEF\n")
-#    define print_matrix_row(row)  print_bin_reverse16(matrix_get_row(row))
-#    define matrix_bitpop(i)       bitpop16(matrix[i])
-#    define ROW_SHIFTER ((uint16_t)1)
+#  define print_matrix_header() print("\nr/c 0123456789ABCDEF\n")
+#  define print_matrix_row(row) print_bin_reverse16(matrix_get_row(row))
+#  define matrix_bitpop(i) bitpop16(matrix[i])
+#  define ROW_SHIFTER ((uint16_t)1)
 #elif (MATRIX_COLS <= 32)
-#    define print_matrix_header()  print("\nr/c 0123456789ABCDEF0123456789ABCDEF\n")
-#    define print_matrix_row(row)  print_bin_reverse32(matrix_get_row(row))
-#    define matrix_bitpop(i)       bitpop32(matrix[i])
-#    define ROW_SHIFTER  ((uint32_t)1)
-#endif
-
+#  define print_matrix_header() print("\nr/c 0123456789ABCDEF0123456789ABCDEF\n")
+#  define print_matrix_row(row) print_bin_reverse32(matrix_get_row(row))
+#  define matrix_bitpop(i) bitpop32(matrix[i])
+#  define ROW_SHIFTER ((uint32_t)1)
 #endif
-static matrix_row_t matrix_debouncing[MATRIX_ROWS];
 
 #define ERROR_DISCONNECT_COUNT 5
 
-#define ROWS_PER_HAND (MATRIX_ROWS/2)
-
-static uint8_t error_count = 0;
+#define ROWS_PER_HAND (MATRIX_ROWS / 2)
 
+#ifdef DIRECT_PINS
+static pin_t direct_pins[MATRIX_ROWS][MATRIX_COLS] = DIRECT_PINS;
+#else
 static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
 static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+#endif
 
 /* matrix state(1:on, 0:off) */
 static matrix_row_t matrix[MATRIX_ROWS];
-static matrix_row_t matrix_debouncing[MATRIX_ROWS];
-
-#if (DIODE_DIRECTION == COL2ROW)
-    static void init_cols(void);
-    static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
-    static void unselect_rows(void);
-    static void select_row(uint8_t row);
-    static void unselect_row(uint8_t row);
-#elif (DIODE_DIRECTION == ROW2COL)
-    static void init_rows(void);
-    static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
-    static void unselect_cols(void);
-    static void unselect_col(uint8_t col);
-    static void select_col(uint8_t col);
-#endif
+static matrix_row_t raw_matrix[ROWS_PER_HAND];
 
-__attribute__ ((weak))
-void matrix_init_kb(void) {
-    matrix_init_user();
-}
+// row offsets for each hand
+uint8_t thisHand, thatHand;
 
-__attribute__ ((weak))
-void matrix_scan_kb(void) {
-    matrix_scan_user();
-}
+// user-defined overridable functions
 
-__attribute__ ((weak))
-void matrix_init_user(void) {
-}
+__attribute__((weak)) void matrix_init_kb(void) { matrix_init_user(); }
 
-__attribute__ ((weak))
-void matrix_scan_user(void) {
-}
+__attribute__((weak)) void matrix_scan_kb(void) { matrix_scan_user(); }
 
-__attribute__ ((weak))
-void matrix_slave_scan_user(void) {
-}
+__attribute__((weak)) void matrix_init_user(void) {}
 
-inline
-uint8_t matrix_rows(void)
-{
-    return MATRIX_ROWS;
-}
+__attribute__((weak)) void matrix_scan_user(void) {}
 
-inline
-uint8_t matrix_cols(void)
-{
-    return MATRIX_COLS;
-}
+__attribute__((weak)) void matrix_slave_scan_user(void) {}
 
-void matrix_init(void)
-{
-    debug_enable = true;
-    debug_matrix = true;
-    debug_mouse = true;
+// helper functions
 
-    // Set pinout for right half if pinout for that half is defined
-    if (!isLeftHand) {
-#ifdef MATRIX_ROW_PINS_RIGHT
-        const uint8_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT;
-        for (uint8_t i = 0; i < MATRIX_ROWS; i++)
-            row_pins[i] = row_pins_right[i];
-#endif
-#ifdef MATRIX_COL_PINS_RIGHT
-        const uint8_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT;
-        for (uint8_t i = 0; i < MATRIX_COLS; i++)
-            col_pins[i] = col_pins_right[i];
-#endif
-    }
+inline uint8_t matrix_rows(void) { return MATRIX_ROWS; }
 
-    // initialize row and col
-#if (DIODE_DIRECTION == COL2ROW)
-    unselect_rows();
-    init_cols();
-#elif (DIODE_DIRECTION == ROW2COL)
-    unselect_cols();
-    init_rows();
-#endif
+inline uint8_t matrix_cols(void) { return MATRIX_COLS; }
 
-    // initialize matrix state: all keys off
-    for (uint8_t i=0; i < MATRIX_ROWS; i++) {
-        matrix[i] = 0;
-        matrix_debouncing[i] = 0;
-    }
-    
-    matrix_init_quantum();
-    
+bool matrix_is_modified(void) {
+  if (debounce_active()) return false;
+  return true;
 }
 
-uint8_t _matrix_scan(void)
-{
-    int offset = isLeftHand ? 0 : (ROWS_PER_HAND);
-#if (DIODE_DIRECTION == COL2ROW)
-    // Set row, read cols
-    for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
-#       if (DEBOUNCING_DELAY > 0)
-            bool matrix_changed = read_cols_on_row(matrix_debouncing+offset, current_row);
-
-            if (matrix_changed) {
-                debouncing = true;
-                debouncing_time = timer_read();
-            }
-
-#       else
-            read_cols_on_row(matrix+offset, current_row);
-#       endif
-
-    }
+inline bool matrix_is_on(uint8_t row, uint8_t col) { return (matrix[row] & ((matrix_row_t)1 << col)); }
 
-#elif (DIODE_DIRECTION == ROW2COL)
-    // Set col, read rows
-    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
-#       if (DEBOUNCING_DELAY > 0)
-            bool matrix_changed = read_rows_on_col(matrix_debouncing+offset, current_col);
-            if (matrix_changed) {
-                debouncing = true;
-                debouncing_time = timer_read();
-            }
-#       else
-             read_rows_on_col(matrix+offset, current_col);
-#       endif
+inline matrix_row_t matrix_get_row(uint8_t row) { return matrix[row]; }
 
-    }
-#endif
+void matrix_print(void) {
+  print_matrix_header();
 
-#   if (DEBOUNCING_DELAY > 0)
-        if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) {
-            for (uint8_t i = 0; i < ROWS_PER_HAND; i++) {
-                matrix[i+offset] = matrix_debouncing[i+offset];
-            }
-            debouncing = false;
-        }
-#   endif
+  for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
+    phex(row);
+    print(": ");
+    print_matrix_row(row);
+    print("\n");
+  }
+}
 
-    return 1;
+uint8_t matrix_key_count(void) {
+  uint8_t count = 0;
+  for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+    count += matrix_bitpop(i);
+  }
+  return count;
 }
 
-#if defined(USE_I2C) || defined(EH)
-
-// Get rows from other half over i2c
-int i2c_transaction(void) {
-    int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;
-    int err = 0;
-    
-    // write backlight info
-    #ifdef BACKLIGHT_ENABLE
-        if (BACKLIT_DIRTY) {
-            err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
-            if (err) goto i2c_error;
-            
-            // Backlight location
-            err = i2c_master_write(I2C_BACKLIT_START);
-            if (err) goto i2c_error;
-            
-            // Write backlight 
-            i2c_master_write(get_backlight_level());
-            
-            BACKLIT_DIRTY = false;
-        }
-    #endif
+// matrix code
 
-    err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
-    if (err) goto i2c_error;
+#ifdef DIRECT_PINS
 
-    // start of matrix stored at I2C_KEYMAP_START
-    err = i2c_master_write(I2C_KEYMAP_START);
-    if (err) goto i2c_error;
+static void init_pins(void) {
+  for (int row = 0; row < MATRIX_ROWS; row++) {
+    for (int col = 0; col < MATRIX_COLS; col++) {
+      pin_t pin = direct_pins[row][col];
+      if (pin != NO_PIN) {
+        setPinInputHigh(pin);
+      }
+    }
+  }
+}
 
-    // Start read
-    err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_READ);
-    if (err) goto i2c_error;
+static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
+  matrix_row_t last_row_value = current_matrix[current_row];
+  current_matrix[current_row] = 0;
 
-    if (!err) {
-        int i;
-        for (i = 0; i < ROWS_PER_HAND-1; ++i) {
-            matrix[slaveOffset+i] = i2c_master_read(I2C_ACK);
-        }
-        matrix[slaveOffset+i] = i2c_master_read(I2C_NACK);
-        i2c_master_stop();
-    } else {
-i2c_error: // the cable is disconnceted, or something else went wrong
-        i2c_reset_state();
-        return err;
+  for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
+    pin_t pin = direct_pins[current_row][col_index];
+    if (pin != NO_PIN) {
+      current_matrix[current_row] |= readPin(pin) ? 0 : (ROW_SHIFTER << col_index);
     }
-    
-    #ifdef RGBLIGHT_ENABLE
-        if (RGB_DIRTY) {
-            err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
-            if (err) goto i2c_error;
-            
-            // RGB Location
-            err = i2c_master_write(I2C_RGB_START);
-            if (err) goto i2c_error;
-            
-            uint32_t dword = eeconfig_read_rgblight();
-            
-            // Write RGB
-            err = i2c_master_write_data(&dword, 4);
-            if (err) goto i2c_error;
-            
-            RGB_DIRTY = false;
-            i2c_master_stop();
-        }
-    #endif
+  }
 
-    return 0;
+  return (last_row_value != current_matrix[current_row]);
 }
 
-#else // USE_SERIAL
-
+#elif (DIODE_DIRECTION == COL2ROW)
 
-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];
-} Serial_s2m_buffer_t;
+static void select_row(uint8_t row) {
+  writePinLow(row_pins[row]);
+  setPinOutput(row_pins[row]);
+}
 
-volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
-volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
-uint8_t volatile status0 = 0;
+static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); }
 
-SSTD_t transactions[] = {
-    { (uint8_t *)&status0,
-      sizeof(serial_m2s_buffer), (uint8_t *)&serial_m2s_buffer,
-      sizeof(serial_s2m_buffer), (uint8_t *)&serial_s2m_buffer
+static void unselect_rows(void) {
+  for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
+    setPinInputHigh(row_pins[x]);
   }
-};
+}
 
-void serial_master_init(void)
-{ soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
+static void init_pins(void) {
+  unselect_rows();
+  for (uint8_t x = 0; x < MATRIX_COLS; x++) {
+    setPinInputHigh(col_pins[x]);
+  }
+}
 
-void serial_slave_init(void)
-{ soft_serial_target_init(transactions, TID_LIMIT(transactions)); }
+static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
+  // Store last value of row prior to reading
+  matrix_row_t last_row_value = current_matrix[current_row];
 
-int serial_transaction(void) {
-    int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;
+  // Clear data in matrix row
+  current_matrix[current_row] = 0;
 
-    if (soft_serial_transaction()) {
-        return 1;
-    }
+  // Select row and wait for row selecton to stabilize
+  select_row(current_row);
+  wait_us(30);
 
-    // TODO:  if MATRIX_COLS > 8 change to unpack()
-    for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        matrix[slaveOffset+i] = serial_s2m_buffer.smatrix[i];
-    }
-    
-    #if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
-        // Code to send RGB over serial goes here (not implemented yet)
-    #endif
-    
-    #ifdef BACKLIGHT_ENABLE
-        // Write backlight level for slave to read
-        serial_m2s_buffer.backlight_level = backlight_config.enable ? backlight_config.level : 0;
-    #endif
-
-    return 0;
-}
-#endif
+  // For each col...
+  for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
+    // Populate the matrix row with the state of the col pin
+    current_matrix[current_row] |= readPin(col_pins[col_index]) ? 0 : (ROW_SHIFTER << col_index);
+  }
 
-uint8_t matrix_scan(void)
-{
-    uint8_t ret = _matrix_scan();
+  // Unselect row
+  unselect_row(current_row);
 
-#if defined(USE_I2C) || defined(EH)
-    if( i2c_transaction() ) {
-#else // USE_SERIAL
-    if( serial_transaction() ) {
-#endif
+  return (last_row_value != current_matrix[current_row]);
+}
 
-        error_count++;
+#elif (DIODE_DIRECTION == ROW2COL)
 
-        if (error_count > ERROR_DISCONNECT_COUNT) {
-            // reset other half if disconnected
-            int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;
-            for (int i = 0; i < ROWS_PER_HAND; ++i) {
-                matrix[slaveOffset+i] = 0;
-            }
-        }
-    } else {
-        error_count = 0;
-    }
-    matrix_scan_quantum();
-    return ret;
+static void select_col(uint8_t col) {
+  writePinLow(col_pins[col]);
+  setPinOutput(col_pins[col]);
 }
 
-void matrix_slave_scan(void) {
-    _matrix_scan();
-
-    int offset = (isLeftHand) ? 0 : ROWS_PER_HAND;
+static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); }
 
-#if defined(USE_I2C) || defined(EH)
-    for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        i2c_slave_buffer[I2C_KEYMAP_START+i] = matrix[offset+i];
-    }   
-#else // USE_SERIAL
-    // TODO: if MATRIX_COLS > 8 change to pack()
-    for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        serial_s2m_buffer.smatrix[i] = matrix[offset+i];
-    }
-#endif
-    matrix_slave_scan_user();
+static void unselect_cols(void) {
+  for (uint8_t x = 0; x < MATRIX_COLS; x++) {
+    setPinInputHigh(col_pins[x]);
+  }
 }
 
-bool matrix_is_modified(void)
-{
-    if (debouncing) return false;
-    return true;
+static void init_pins(void) {
+  unselect_cols();
+  for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
+    setPinInputHigh(row_pins[x]);
+  }
 }
 
-inline
-bool matrix_is_on(uint8_t row, uint8_t col)
-{
-    return (matrix[row] & ((matrix_row_t)1<<col));
-}
+static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
+  bool matrix_changed = false;
 
-inline
-matrix_row_t matrix_get_row(uint8_t row)
-{
-    return matrix[row];
-}
+  // Select col and wait for col selecton to stabilize
+  select_col(current_col);
+  wait_us(30);
+
+  // For each row...
+  for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) {
+    // Store last value of row prior to reading
+    matrix_row_t last_row_value = current_matrix[row_index];
 
-void matrix_print(void)
-{
-    print("\nr/c 0123456789ABCDEF\n");
-    for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
-        phex(row); print(": ");
-        pbin_reverse16(matrix_get_row(row));
-        print("\n");
+    // Check row pin state
+    if (readPin(row_pins[row_index])) {
+      // Pin HI, clear col bit
+      current_matrix[row_index] &= ~(ROW_SHIFTER << current_col);
+    } else {
+      // Pin LO, set col bit
+      current_matrix[row_index] |= (ROW_SHIFTER << current_col);
     }
-}
 
-uint8_t matrix_key_count(void)
-{
-    uint8_t count = 0;
-    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
-        count += bitpop16(matrix[i]);
+    // Determine if the matrix changed state
+    if ((last_row_value != current_matrix[row_index]) && !(matrix_changed)) {
+      matrix_changed = true;
     }
-    return count;
-}
+  }
 
-#if (DIODE_DIRECTION == COL2ROW)
+  // Unselect col
+  unselect_col(current_col);
 
-static void init_cols(void)
-{
-    for(uint8_t x = 0; x < MATRIX_COLS; x++) {
-        setPinInputHigh(col_pins[x]);
-    }
+  return matrix_changed;
 }
 
-static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
-{
-    // Store last value of row prior to reading
-    matrix_row_t last_row_value = current_matrix[current_row];
-
-    // Clear data in matrix row
-    current_matrix[current_row] = 0;
+#endif
 
-    // Select row and wait for row selecton to stabilize
-    select_row(current_row);
-    wait_us(30);
+void matrix_init(void) {
+  debug_enable = true;
+  debug_matrix = true;
+  debug_mouse  = true;
 
-    // For each col...
-    for(uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
-        // Populate the matrix row with the state of the col pin
-        current_matrix[current_row] |=  readPin(col_pins[col_index]) ? 0 : (ROW_SHIFTER << col_index);
+  // Set pinout for right half if pinout for that half is defined
+  if (!isLeftHand) {
+#ifdef MATRIX_ROW_PINS_RIGHT
+    const uint8_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT;
+    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+      row_pins[i] = row_pins_right[i];
     }
+#endif
+#ifdef MATRIX_COL_PINS_RIGHT
+    const uint8_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT;
+    for (uint8_t i = 0; i < MATRIX_COLS; i++) {
+      col_pins[i] = col_pins_right[i];
+    }
+#endif
+  }
 
-    // Unselect row
-    unselect_row(current_row);
+  thisHand = isLeftHand ? 0 : (ROWS_PER_HAND);
+  thatHand = ROWS_PER_HAND - thisHand;
 
-    return (last_row_value != current_matrix[current_row]);
-}
+  // initialize key pins
+  init_pins();
 
-static void select_row(uint8_t row)
-{
-    writePinLow(row_pins[row]);
-    setPinOutput(row_pins[row]);
-}
+  // initialize matrix state: all keys off
+  for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+    matrix[i] = 0;
+  }
 
-static void unselect_row(uint8_t row)
-{
-    setPinInputHigh(row_pins[row]);
-}
+  debounce_init(ROWS_PER_HAND);
 
-static void unselect_rows(void)
-{
-    for(uint8_t x = 0; x < ROWS_PER_HAND; x++) {
-        setPinInputHigh(row_pins[x]);
-    }
+  matrix_init_quantum();
 }
 
+uint8_t _matrix_scan(void) {
+  bool changed = false;
+
+#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
+  // Set row, read cols
+  for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
+    changed |= read_cols_on_row(raw_matrix, current_row);
+  }
 #elif (DIODE_DIRECTION == ROW2COL)
+  // Set col, read rows
+  for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
+    changed |= read_rows_on_col(raw_matrix, current_col);
+  }
+#endif
 
-static void init_rows(void)
-{
-    for(uint8_t x = 0; x < ROWS_PER_HAND; x++) {
-        setPinInputHigh(row_pins[x]);
-    }
-}
+  debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed);
 
-static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
-{
-    bool matrix_changed = false;
+  return 1;
+}
 
-    // Select col and wait for col selecton to stabilize
-    select_col(current_col);
-    wait_us(30);
+uint8_t matrix_scan(void) {
+  uint8_t ret = _matrix_scan();
 
-    // For each row...
-    for(uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++)
-    {
+  if (is_keyboard_master()) {
+    static uint8_t error_count;
 
-        // Store last value of row prior to reading
-        matrix_row_t last_row_value = current_matrix[row_index];
+    if (!transport_master(matrix + thatHand)) {
+      error_count++;
 
-        // Check row pin state
-        if (readPin(row_pins[row_index]))
-        {
-            // Pin HI, clear col bit
-            current_matrix[row_index] &= ~(ROW_SHIFTER << current_col);
-        }
-        else
-        {
-            // Pin LO, set col bit
-            current_matrix[row_index] |= (ROW_SHIFTER << current_col);
-        }
-
-        // Determine if the matrix changed state
-        if ((last_row_value != current_matrix[row_index]) && !(matrix_changed))
-        {
-            matrix_changed = true;
+      if (error_count > ERROR_DISCONNECT_COUNT) {
+        // reset other half if disconnected
+        for (int i = 0; i < ROWS_PER_HAND; ++i) {
+          matrix[thatHand + i] = 0;
         }
+      }
+    } else {
+      error_count = 0;
     }
 
-    // Unselect col
-    unselect_col(current_col);
-
-    return matrix_changed;
-}
-
-static void select_col(uint8_t col)
-{
-    writePinLow(col_pins[col]);
-    setPinOutput(col_pins[col]);
-}
-
-static void unselect_col(uint8_t col)
-{
-    setPinInputHigh(col_pins[col]);
-}
+    matrix_scan_quantum();
+  } else {
+    transport_slave(matrix + thisHand);
+    matrix_slave_scan_user();
+  }
 
-static void unselect_cols(void)
-{
-    for(uint8_t x = 0; x < MATRIX_COLS; x++) {
-        setPinInputHigh(col_pins[x]);
-    }
+  return ret;
 }
-
-#endif
diff --git a/quantum/split_common/matrix.h b/quantum/split_common/matrix.h
index b5cb45baed..c2bdd3098c 100644
--- a/quantum/split_common/matrix.h
+++ b/quantum/split_common/matrix.h
@@ -1,31 +1,3 @@
-#ifndef SPLIT_COMMON_MATRIX_H
-#define SPLIT_COMMON_MATRIX_H
+#pragma once
 
 #include <common/matrix.h>
-
-#ifdef RGBLIGHT_ENABLE
-#   include "rgblight.h"
-#endif
-
-typedef struct _Serial_m2s_buffer_t {
-#ifdef BACKLIGHT_ENABLE
-    uint8_t backlight_level;
-#endif
-#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
-    rgblight_config_t rgblight_config; //not yet use
-    //
-    // 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 the RGBLIGHT_SPLIT macro.
-    //
-    // Otherwise, if the master side MCU drives both sides RGB LED chains,
-    // there is no need to communicate.
-#endif
-} Serial_m2s_buffer_t;
-
-extern volatile Serial_m2s_buffer_t serial_m2s_buffer;
-
-void serial_master_init(void);
-void serial_slave_init(void);
-
-#endif
diff --git a/quantum/split_common/serial.h b/quantum/split_common/serial.h
index b6638b3bde..1c1e640069 100644
--- a/quantum/split_common/serial.h
+++ b/quantum/split_common/serial.h
@@ -1,5 +1,4 @@
-#ifndef SOFT_SERIAL_H
-#define SOFT_SERIAL_H
+#pragma once
 
 #include <stdbool.h>
 
@@ -61,5 +60,3 @@ int  soft_serial_transaction(int sstd_index);
 #ifdef SERIAL_USE_MULTI_TRANSACTION
 int  soft_serial_get_and_clean_status(int sstd_index);
 #endif
-
-#endif /* SOFT_SERIAL_H */
diff --git a/quantum/split_common/split_flags.h b/quantum/split_common/split_flags.h
index f101fff5b5..aaac474a7d 100644
--- a/quantum/split_common/split_flags.h
+++ b/quantum/split_common/split_flags.h
@@ -1,10 +1,9 @@
-#ifndef SPLIT_FLAGS_H
-#define SPLIT_FLAGS_H
+#pragma once
 
 #include <stdbool.h>
 #include <stdint.h>
 
-/** 
+/**
 * Global Flags
 **/
 
@@ -14,7 +13,3 @@ extern volatile bool RGB_DIRTY;
 
 //Backlight Stuff
 extern volatile bool BACKLIT_DIRTY;
-
-
-
-#endif
\ No newline at end of file
diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c
index e41b6f6386..5095cb8fdc 100644
--- a/quantum/split_common/split_util.c
+++ b/quantum/split_common/split_util.c
@@ -4,142 +4,84 @@
 #include "config.h"
 #include "timer.h"
 #include "split_flags.h"
+#include "transport.h"
 #include "quantum.h"
 
 #ifdef EE_HANDS
 #   include "tmk_core/common/eeprom.h"
-#endif
-
-#ifdef BACKLIGHT_ENABLE
-#   include "backlight.h"
-#endif
-
-#if defined(USE_I2C) || defined(EH)
-#  include "i2c.h"
+#   include "eeconfig.h"
 #endif
 
 volatile bool isLeftHand = true;
 
-volatile uint8_t setTries = 0;
-
-static void setup_handedness(void) {
+__attribute__((weak))
+bool is_keyboard_left(void) {
   #ifdef SPLIT_HAND_PIN
     // Test pin SPLIT_HAND_PIN for High/Low, if low it's right hand
     setPinInput(SPLIT_HAND_PIN);
-    isLeftHand = readPin(SPLIT_HAND_PIN);
+    return readPin(SPLIT_HAND_PIN);
   #else
     #ifdef EE_HANDS
-      isLeftHand = eeprom_read_byte(EECONFIG_HANDEDNESS);
+      return eeprom_read_byte(EECONFIG_HANDEDNESS);
     #else
       #ifdef MASTER_RIGHT
-        isLeftHand = !has_usb();
+        return !is_keyboard_master();
       #else
-        isLeftHand = has_usb();
+        return is_keyboard_master();
       #endif
     #endif
   #endif
 }
 
-static void keyboard_master_setup(void) {
-#if defined(USE_I2C) || defined(EH)
-  i2c_master_init();
-  #ifdef SSD1306OLED
-    matrix_master_OLED_init ();
-  #endif
-#else
-  serial_master_init();
-#endif
+bool is_keyboard_master(void)
+{
+#ifdef __AVR__
+  static enum { UNKNOWN, MASTER, SLAVE } usbstate = UNKNOWN;
 
-    // For master the Backlight info needs to be sent on startup
-    // Otherwise the salve won't start with the proper info until an update
-    BACKLIT_DIRTY = true;
-}
+  // only check once, as this is called often
+  if (usbstate == UNKNOWN)
+  {
+    USBCON |= (1 << OTGPADE);  // enables VBUS pad
+    wait_us(5);
 
-static void keyboard_slave_setup(void) {
-  timer_init();
-#if defined(USE_I2C) || defined(EH)
-    i2c_slave_init(SLAVE_I2C_ADDRESS);
+    usbstate = (USBSTA & (1 << VBUS)) ? MASTER : SLAVE;  // checks state of VBUS
+  }
+
+  return (usbstate == MASTER);
 #else
-    serial_slave_init();
+  return true;
 #endif
 }
 
-bool has_usb(void) {
-   USBCON |= (1 << OTGPADE); //enables VBUS pad
-   _delay_us(5);
-   return (USBSTA & (1<<VBUS));  //checks state of VBUS
-}
-
-void split_keyboard_setup(void) {
-   setup_handedness();
+static void keyboard_master_setup(void) {
+#if defined(USE_I2C) || defined(EH)
+  #ifdef SSD1306OLED
+    matrix_master_OLED_init ();
+  #endif
+#endif
+  transport_master_init();
 
-   if (has_usb()) {
-      keyboard_master_setup();
-   } else {
-      keyboard_slave_setup();
-   }
-   sei();
+  // For master the Backlight info needs to be sent on startup
+  // Otherwise the salve won't start with the proper info until an update
+  BACKLIT_DIRTY = true;
 }
 
-void keyboard_slave_loop(void) {
-   matrix_init();
-
-   //Init RGB
-   #ifdef RGBLIGHT_ENABLE
-      rgblight_init();
-   #endif
-
-   while (1) {
-    // Matrix Slave Scan
-    matrix_slave_scan();
-
-    // Read Backlight Info
-    #ifdef BACKLIGHT_ENABLE
-        #ifdef USE_I2C
-            if (BACKLIT_DIRTY) {
-                backlight_set(i2c_slave_buffer[I2C_BACKLIT_START]);
-                BACKLIT_DIRTY = false;
-            }
-        #else // USE_SERIAL
-            backlight_set(serial_m2s_buffer.backlight_level);
-        #endif
-    #endif
-    // Read RGB Info
-    #ifdef RGBLIGHT_ENABLE
-        #ifdef USE_I2C
-            if (RGB_DIRTY) {
-                // Disable interupts (RGB data is big)
-                cli();
-                // Create new DWORD for RGB data
-                uint32_t dword;
-
-                // Fill the new DWORD with the data that was sent over
-                uint8_t *dword_dat = (uint8_t *)(&dword);
-                for (int i = 0; i < 4; i++) {
-                    dword_dat[i] = i2c_slave_buffer[I2C_RGB_START+i];
-                }
-
-                // Update the RGB now with the new data and set RGB_DIRTY to false
-                rgblight_update_dword(dword);
-                RGB_DIRTY = false;
-                // Re-enable interupts now that RGB is set
-                sei();
-            }
-        #else // USE_SERIAL
-          #ifdef RGBLIGHT_SPLIT
-            // Add serial implementation for RGB here
-          #endif
-        #endif
-    #endif
-   }
+static void keyboard_slave_setup(void)
+{
+  transport_slave_init();
 }
 
 // this code runs before the usb and keyboard is initialized
-void matrix_setup(void) {
-    split_keyboard_setup();
-
-    if (!has_usb()) {
-        //rgblight_init();
-        keyboard_slave_loop();
-    }
+void matrix_setup(void)
+{
+  isLeftHand = is_keyboard_left();
+
+  if (is_keyboard_master())
+  {
+    keyboard_master_setup();
+  }
+  else
+  {
+    keyboard_slave_setup();
+  }
 }
diff --git a/quantum/split_common/split_util.h b/quantum/split_common/split_util.h
index d6cf3e72a9..20f7535bf4 100644
--- a/quantum/split_common/split_util.h
+++ b/quantum/split_common/split_util.h
@@ -1,23 +1,10 @@
-#ifndef SPLIT_KEYBOARD_UTIL_H
-#define SPLIT_KEYBOARD_UTIL_H
+#pragma once
 
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include "eeconfig.h"
-
-#define SLAVE_I2C_ADDRESS           0x32
 
 extern volatile bool isLeftHand;
 
-// slave version of matix scan, defined in matrix.c
-void matrix_slave_scan(void);
-
-void split_keyboard_setup(void);
-bool has_usb(void);
-void keyboard_slave_loop(void);
-
 void matrix_master_OLED_init (void);
-
-#endif
diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c
new file mode 100644
index 0000000000..95738530ec
--- /dev/null
+++ b/quantum/split_common/transport.c
@@ -0,0 +1,224 @@
+
+#include "config.h"
+#include "matrix.h"
+#include "quantum.h"
+
+#define ROWS_PER_HAND (MATRIX_ROWS/2)
+
+#ifdef RGBLIGHT_ENABLE
+#   include "rgblight.h"
+#endif
+
+#ifdef BACKLIGHT_ENABLE
+# include "backlight.h"
+  extern backlight_config_t backlight_config;
+#endif
+
+#if defined(USE_I2C) || defined(EH)
+
+#include "i2c.h"
+
+#ifndef SLAVE_I2C_ADDRESS
+#  define SLAVE_I2C_ADDRESS           0x32
+#endif
+
+#if (MATRIX_COLS > 8)
+#  error "Currently only supports 8 COLS"
+#endif
+
+// Get rows from other half over i2c
+bool transport_master(matrix_row_t matrix[]) {
+  int err = 0;
+
+  // write backlight info
+#ifdef BACKLIGHT_ENABLE
+  if (BACKLIT_DIRTY) {
+    err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
+    if (err) { goto i2c_error; }
+
+    // Backlight location
+    err = i2c_master_write(I2C_BACKLIT_START);
+    if (err) { goto i2c_error; }
+
+    // Write backlight
+    i2c_master_write(get_backlight_level());
+
+    BACKLIT_DIRTY = false;
+  }
+#endif
+
+  err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
+  if (err) { goto i2c_error; }
+
+  // start of matrix stored at I2C_KEYMAP_START
+  err = i2c_master_write(I2C_KEYMAP_START);
+  if (err) { goto i2c_error; }
+
+  // Start read
+  err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_READ);
+  if (err) { goto i2c_error; }
+
+  if (!err) {
+    int i;
+    for (i = 0; i < ROWS_PER_HAND-1; ++i) {
+      matrix[i] = i2c_master_read(I2C_ACK);
+    }
+    matrix[i] = i2c_master_read(I2C_NACK);
+    i2c_master_stop();
+  } else {
+i2c_error: // the cable is disconnceted, or something else went wrong
+    i2c_reset_state();
+    return false;
+  }
+
+#ifdef RGBLIGHT_ENABLE
+  if (RGB_DIRTY) {
+    err = i2c_master_start(SLAVE_I2C_ADDRESS + I2C_WRITE);
+    if (err) { goto i2c_error; }
+
+    // RGB Location
+    err = i2c_master_write(I2C_RGB_START);
+    if (err) { goto i2c_error; }
+
+    uint32_t dword = eeconfig_read_rgblight();
+
+    // Write RGB
+    err = i2c_master_write_data(&dword, 4);
+    if (err) { goto i2c_error; }
+
+    RGB_DIRTY = false;
+    i2c_master_stop();
+  }
+#endif
+
+  return true;
+}
+
+void transport_slave(matrix_row_t matrix[]) {
+
+  for (int i = 0; i < ROWS_PER_HAND; ++i)
+  {
+    i2c_slave_buffer[I2C_KEYMAP_START + i] = matrix[i];
+  }
+  // Read Backlight Info
+  #ifdef BACKLIGHT_ENABLE
+  if (BACKLIT_DIRTY)
+  {
+    backlight_set(i2c_slave_buffer[I2C_BACKLIT_START]);
+    BACKLIT_DIRTY = false;
+  }
+  #endif
+  #ifdef RGBLIGHT_ENABLE
+  if (RGB_DIRTY)
+  {
+    // Disable interupts (RGB data is big)
+    cli();
+    // Create new DWORD for RGB data
+    uint32_t dword;
+
+    // Fill the new DWORD with the data that was sent over
+    uint8_t * dword_dat = (uint8_t *)(&dword);
+    for (int i = 0; i < 4; i++)
+    {
+      dword_dat[i] = i2c_slave_buffer[I2C_RGB_START + i];
+    }
+
+    // Update the RGB now with the new data and set RGB_DIRTY to false
+    rgblight_update_dword(dword);
+    RGB_DIRTY = false;
+    // Re-enable interupts now that RGB is set
+    sei();
+  }
+  #endif
+}
+
+void transport_master_init(void) {
+  i2c_master_init();
+}
+
+void transport_slave_init(void) {
+  i2c_slave_init(SLAVE_I2C_ADDRESS);
+}
+
+#else // USE_SERIAL
+
+#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];
+} Serial_s2m_buffer_t;
+
+typedef struct _Serial_m2s_buffer_t {
+#ifdef BACKLIGHT_ENABLE
+    uint8_t backlight_level;
+#endif
+#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+    rgblight_config_t rgblight_config; //not yet use
+    //
+    // 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 the RGBLIGHT_SPLIT macro.
+    //
+    // Otherwise, if the master side MCU drives both sides RGB LED chains,
+    // there is no need to communicate.
+#endif
+} Serial_m2s_buffer_t;
+
+volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
+volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
+uint8_t volatile status0 = 0;
+
+SSTD_t transactions[] = {
+  { (uint8_t *)&status0,
+    sizeof(serial_m2s_buffer), (uint8_t *)&serial_m2s_buffer,
+    sizeof(serial_s2m_buffer), (uint8_t *)&serial_s2m_buffer
+  }
+};
+
+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)); }
+
+bool transport_master(matrix_row_t matrix[]) {
+
+  if (soft_serial_transaction()) {
+    return false;
+  }
+
+  // TODO:  if MATRIX_COLS > 8 change to unpack()
+  for (int i = 0; i < ROWS_PER_HAND; ++i) {
+    matrix[i] = serial_s2m_buffer.smatrix[i];
+  }
+
+  #if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+    // Code to send RGB over serial goes here (not implemented yet)
+  #endif
+
+  #ifdef BACKLIGHT_ENABLE
+    // Write backlight level for slave to read
+    serial_m2s_buffer.backlight_level = backlight_config.enable ? backlight_config.level : 0;
+  #endif
+
+  return true;
+}
+
+void transport_slave(matrix_row_t matrix[]) {
+
+  // TODO: if MATRIX_COLS > 8 change to pack()
+  for (int i = 0; i < ROWS_PER_HAND; ++i)
+  {
+    serial_s2m_buffer.smatrix[i] = matrix[i];
+  }
+  #ifdef BACKLIGHT_ENABLE
+    backlight_set(serial_m2s_buffer.backlight_level);
+  #endif
+  #if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+  // Add serial implementation for RGB here
+  #endif
+
+}
+
+#endif
diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h
new file mode 100644
index 0000000000..ccce57e444
--- /dev/null
+++ b/quantum/split_common/transport.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <common/matrix.h>
+
+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 matrix[]);
+void transport_slave(matrix_row_t matrix[]);
diff --git a/tmk_core/common/keyboard.h b/tmk_core/common/keyboard.h
index 71e594a890..ea2f336e9d 100644
--- a/tmk_core/common/keyboard.h
+++ b/tmk_core/common/keyboard.h
@@ -67,6 +67,8 @@ void keyboard_init(void);
 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);
 
 #ifdef __cplusplus
 }