From e9cb9f42a511afd24a47812b5cf83895e8936744 Mon Sep 17 00:00:00 2001
From: Drashna Jaelre <drashna@live.com>
Date: Sun, 18 Aug 2024 17:13:38 -0700
Subject: [PATCH] [Keyboard] Update ZSA Moonlander (#23911)

---
 keyboards/zsa/moonlander/config.h          |  45 ----
 keyboards/zsa/moonlander/keyboard.json     |  84 +++++++
 keyboards/zsa/moonlander/ld/STM32F303xB.ld |  85 ++++++++
 keyboards/zsa/moonlander/matrix.c          | 226 +++++++++----------
 keyboards/zsa/moonlander/mcuconf.h         |   8 -
 keyboards/zsa/moonlander/moonlander.c      | 242 ++++++++-------------
 keyboards/zsa/moonlander/rules.mk          |   6 +-
 7 files changed, 365 insertions(+), 331 deletions(-)
 create mode 100644 keyboards/zsa/moonlander/ld/STM32F303xB.ld

diff --git a/keyboards/zsa/moonlander/config.h b/keyboards/zsa/moonlander/config.h
index 08870fba9d..b846d89f45 100644
--- a/keyboards/zsa/moonlander/config.h
+++ b/keyboards/zsa/moonlander/config.h
@@ -18,60 +18,15 @@
 
 #pragma once
 
-
-#define WEBUSB_LANDING_PAGE_URL u8"configure.ergodox-ez.com"
-
 /* key matrix size */
 #define MATRIX_ROWS 12
 #define MATRIX_COLS 7
 
-/*  PCB default pin-out */
-// #define MATRIX_ROW_PINS { B10, B11, B12, B13, B14, B15 }
-// #define MATRIX_COL_PINS { A0, A1, A2, A3, A6, A7, B0 }
-
-// #define MCP23_ROW_PINS { GPB5, GBP4, GBP3, GBP2, GBP1, GBP0 }
-// #define MCP23_COL_PINS { GPA0, GBA1, GBA2, GBA3, GBA4, GBA5, GBA6 }
-
-// #define MCP23_LED_R GPB7
-// #define MCP23_LED_G GPB6
-// #define MCP23_LED_B GPA7
-
 #define EEPROM_I2C_24LC128
 
-// Not needed, is default address:
-// #define EXTERNAL_EEPROM_I2C_BASE_ADDRESS 0b10100000
-
-/* COL2ROW or ROW2COL */
-#define DIODE_DIRECTION ROW2COL
-
-/*
- * Feature disable options
- *  These options are also useful to firmware size reduction.
- */
-
-/* disable debug print */
-//#define NO_DEBUG
-
-/* disable print */
-//#define NO_PRINT
-
-/* disable action features */
-//#define NO_ACTION_LAYER
-//#define NO_ACTION_TAPPING
-//#define NO_ACTION_ONESHOT
-
 #define IS31FL3731_I2C_ADDRESS_1 IS31FL3731_I2C_ADDRESS_GND
 #define IS31FL3731_I2C_ADDRESS_2 IS31FL3731_I2C_ADDRESS_VCC
 
-#define MOUSEKEY_INTERVAL           20
-#define MOUSEKEY_DELAY              0
-#define MOUSEKEY_TIME_TO_MAX        60
-#define MOUSEKEY_MAX_SPEED          7
-#define MOUSEKEY_WHEEL_DELAY        400
-#define MOUSEKEY_WHEEL_INTERVAL     MOUSEKEY_INTERVAL
-#define MOUSEKEY_WHEEL_MAX_SPEED    MOUSEKEY_MAX_SPEED
-#define MOUSEKEY_WHEEL_TIME_TO_MAX  MOUSEKEY_TIME_TO_MAX
-
 #define MUSIC_MAP
 
 #define FIRMWARE_VERSION_SIZE 17
diff --git a/keyboards/zsa/moonlander/keyboard.json b/keyboards/zsa/moonlander/keyboard.json
index 571674fe1c..2ef6657e91 100644
--- a/keyboards/zsa/moonlander/keyboard.json
+++ b/keyboards/zsa/moonlander/keyboard.json
@@ -16,6 +16,7 @@
         "bootmagic": true,
         "command": true,
         "console": true,
+        "deferred_exec": true,
         "extrakey": true,
         "mousekey": true,
         "nkro": true,
@@ -37,6 +38,13 @@
     "eeprom": {
         "driver": "i2c"
     },
+    "mousekey": {
+        "delay": 0,
+        "interval": 20,
+        "max_speed": 7,
+        "time_to_max": 60,
+        "wheel_delay": 400
+    },
     "rgb_matrix": {
         "animations": {
             "alphas_mods": true,
@@ -85,6 +93,82 @@
         },
         "center_point": [120, 36],
         "driver": "is31fl3731",
+        "layout": [
+            {"matrix": [0, 0], "x": 0, "y": 4, "flags": 1},
+            {"matrix": [1, 0], "x": 0, "y": 20, "flags": 1},
+            {"matrix": [2, 0], "x": 0, "y": 36, "flags": 1},
+            {"matrix": [3, 0], "x": 0, "y": 52, "flags": 1},
+            {"matrix": [4, 0], "x": 0, "y": 68, "flags": 1},
+            {"matrix": [0, 1], "x": 16, "y": 3, "flags": 4},
+            {"matrix": [1, 1], "x": 16, "y": 19, "flags": 4},
+            {"matrix": [2, 1], "x": 16, "y": 35, "flags": 4},
+            {"matrix": [3, 1], "x": 16, "y": 51, "flags": 4},
+            {"matrix": [4, 1], "x": 16, "y": 67, "flags": 1},
+            {"matrix": [0, 2], "x": 32, "y": 1, "flags": 4},
+            {"matrix": [1, 2], "x": 32, "y": 17, "flags": 4},
+            {"matrix": [2, 2], "x": 32, "y": 33, "flags": 4},
+            {"matrix": [3, 2], "x": 32, "y": 49, "flags": 4},
+            {"matrix": [4, 2], "x": 32, "y": 65, "flags": 1},
+            {"matrix": [0, 3], "x": 48, "y": 0, "flags": 4},
+            {"matrix": [1, 3], "x": 48, "y": 16, "flags": 4},
+            {"matrix": [2, 3], "x": 48, "y": 32, "flags": 4},
+            {"matrix": [3, 3], "x": 48, "y": 48, "flags": 4},
+            {"matrix": [4, 3], "x": 48, "y": 64, "flags": 1},
+            {"matrix": [0, 4], "x": 64, "y": 1, "flags": 4},
+            {"matrix": [1, 4], "x": 64, "y": 17, "flags": 4},
+            {"matrix": [2, 4], "x": 64, "y": 33, "flags": 4},
+            {"matrix": [3, 4], "x": 64, "y": 49, "flags": 4},
+            {"matrix": [4, 4], "x": 64, "y": 65, "flags": 1},
+            {"matrix": [0, 5], "x": 80, "y": 3, "flags": 4},
+            {"matrix": [1, 5], "x": 80, "y": 19, "flags": 4},
+            {"matrix": [2, 5], "x": 80, "y": 35, "flags": 4},
+            {"matrix": [3, 5], "x": 80, "y": 51, "flags": 4},
+            {"matrix": [0, 6], "x": 96, "y": 4, "flags": 1},
+            {"matrix": [1, 6], "x": 96, "y": 20, "flags": 1},
+            {"matrix": [2, 6], "x": 96, "y": 36, "flags": 1},
+            {"matrix": [5, 0], "x": 88, "y": 69, "flags": 1},
+            {"matrix": [5, 1], "x": 100, "y": 80, "flags": 1},
+            {"matrix": [5, 2], "x": 112, "y": 91, "flags": 1},
+            {"matrix": [5, 3], "x": 108, "y": 69, "flags": 1},
+            {"matrix": [6, 6], "x": 240, "y": 4, "flags": 1},
+            {"matrix": [7, 6], "x": 240, "y": 20, "flags": 1},
+            {"matrix": [8, 6], "x": 240, "y": 36, "flags": 1},
+            {"matrix": [9, 6], "x": 240, "y": 52, "flags": 1},
+            {"matrix": [10, 6], "x": 240, "y": 68, "flags": 1},
+            {"matrix": [6, 5], "x": 224, "y": 3, "flags": 4},
+            {"matrix": [7, 5], "x": 224, "y": 19, "flags": 4},
+            {"matrix": [8, 5], "x": 224, "y": 35, "flags": 4},
+            {"matrix": [9, 5], "x": 224, "y": 51, "flags": 4},
+            {"matrix": [10, 5], "x": 224, "y": 67, "flags": 1},
+            {"matrix": [6, 4], "x": 208, "y": 1, "flags": 4},
+            {"matrix": [7, 4], "x": 208, "y": 17, "flags": 4},
+            {"matrix": [8, 4], "x": 208, "y": 33, "flags": 4},
+            {"matrix": [9, 4], "x": 208, "y": 49, "flags": 4},
+            {"matrix": [10, 4], "x": 208, "y": 65, "flags": 1},
+            {"matrix": [6, 3], "x": 192, "y": 0, "flags": 4},
+            {"matrix": [7, 3], "x": 192, "y": 16, "flags": 4},
+            {"matrix": [8, 3], "x": 192, "y": 32, "flags": 4},
+            {"matrix": [9, 3], "x": 192, "y": 48, "flags": 4},
+            {"matrix": [10, 3], "x": 192, "y": 64, "flags": 1},
+            {"matrix": [6, 2], "x": 176, "y": 1, "flags": 4},
+            {"matrix": [7, 2], "x": 176, "y": 17, "flags": 4},
+            {"matrix": [8, 2], "x": 176, "y": 33, "flags": 4},
+            {"matrix": [9, 2], "x": 176, "y": 49, "flags": 4},
+            {"matrix": [10, 2], "x": 176, "y": 65, "flags": 1},
+            {"matrix": [6, 1], "x": 160, "y": 3, "flags": 4},
+            {"matrix": [7, 1], "x": 160, "y": 19, "flags": 4},
+            {"matrix": [8, 1], "x": 160, "y": 35, "flags": 4},
+            {"matrix": [9, 1], "x": 160, "y": 51, "flags": 4},
+            {"matrix": [6, 0], "x": 144, "y": 4, "flags": 1},
+            {"matrix": [7, 0], "x": 144, "y": 20, "flags": 1},
+            {"matrix": [8, 0], "x": 144, "y": 36, "flags": 1},
+            {"matrix": [11, 6], "x": 152, "y": 69, "flags": 1},
+            {"matrix": [11, 5], "x": 140, "y": 80, "flags": 1},
+            {"matrix": [11, 4], "x": 128, "y": 91, "flags": 1},
+            {"matrix": [11, 3], "x": 132, "y": 69, "flags": 1}
+        ],
+        "led_flush_limit": 26,
+        "led_process_limit": 5,
         "max_brightness": 175,
         "sleep": true
     },
diff --git a/keyboards/zsa/moonlander/ld/STM32F303xB.ld b/keyboards/zsa/moonlander/ld/STM32F303xB.ld
new file mode 100644
index 0000000000..c3a81461f9
--- /dev/null
+++ b/keyboards/zsa/moonlander/ld/STM32F303xB.ld
@@ -0,0 +1,85 @@
+/*
+    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.
+*/
+
+/*
+ * STM32F303xB memory setup.
+ */
+MEMORY
+{
+    flash0 (rx) : org = 0x08000000, len = 128k
+    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 = 32k
+    ram1   (wx) : org = 0x00000000, len = 0
+    ram2   (wx) : org = 0x00000000, len = 0
+    ram3   (wx) : org = 0x00000000, len = 0
+    ram4   (wx) : org = 0x10000000, len = 8k
+    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
diff --git a/keyboards/zsa/moonlander/matrix.c b/keyboards/zsa/moonlander/matrix.c
index 2c9edd417c..867fa85a66 100644
--- a/keyboards/zsa/moonlander/matrix.c
+++ b/keyboards/zsa/moonlander/matrix.c
@@ -14,62 +14,39 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
+ */
 
 #include "moonlander.h"
-#include "i2c_master.h"
+#include "mcp23018.h"
 
+#pragma GCC push_options
+#pragma GCC optimize("-O3")
 /*
 #define MATRIX_ROW_PINS { B10, B11, B12, B13, B14, B15 } outputs
 #define MATRIX_COL_PINS { A0, A1, A2, A3, A6, A7, B0 }   inputs
+#define MCP23_ROW_PINS { GPB5, GBP4, GBP3, GBP2, GBP1, GBP0 }       outputs
+#define MCP23_COL_PINS { GPA0, GBA1, GBA2, GBA3, GBA4, GBA5, GBA6 } inputs
+
  */
 /* matrix state(1:on, 0:off) */
-extern matrix_row_t matrix[MATRIX_ROWS];      // debounced values
-extern matrix_row_t raw_matrix[MATRIX_ROWS];  // raw values
-static matrix_row_t raw_matrix_right[MATRIX_COLS];
+extern matrix_row_t matrix[MATRIX_ROWS];     // debounced values
+extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
+static matrix_row_t raw_matrix_right[MATRIX_ROWS];
 
-#define ROWS_PER_HAND (MATRIX_ROWS / 2)
-#ifndef MOONLANDER_I2C_TIMEOUT
-#    define MOONLANDER_I2C_TIMEOUT 100
-#endif
+#define MCP_ROWS_PER_HAND (MATRIX_ROWS / 2)
 
 extern bool mcp23018_leds[3];
 extern bool is_launching;
 
-bool           mcp23018_initd = false;
-static uint8_t mcp23018_reset_loop;
+static uint16_t mcp23018_reset_loop;
+uint8_t         mcp23018_errors;
 
-uint8_t mcp23018_tx[3];
-uint8_t mcp23018_rx[1];
-
-void mcp23018_init(void) {
-    i2c_init();
-
-    // #define MCP23_ROW_PINS { GPB5, GBP4, GBP3, GBP2, GBP1, GBP0 }       outputs
-    // #define MCP23_COL_PINS { GPA0, GBA1, GBA2, GBA3, GBA4, GBA5, GBA6 } inputs
-
-    mcp23018_tx[0] = 0x00;        // IODIRA
-    mcp23018_tx[1] = 0b00000000;  // A is output
-    mcp23018_tx[2] = 0b00111111;  // B is inputs
-
-    if (MSG_OK != i2c_transmit(MCP23018_DEFAULT_ADDRESS << 1, mcp23018_tx, 3, MOONLANDER_I2C_TIMEOUT)) {
-        dprintf("error hori\n");
-    } else {
-        mcp23018_tx[0] = 0x0C;        // GPPUA
-        mcp23018_tx[1] = 0b10000000;  // A is not pulled-up
-        mcp23018_tx[2] = 0b11111111;  // B is pulled-up
-
-        if (MSG_OK != i2c_transmit(MCP23018_DEFAULT_ADDRESS << 1, mcp23018_tx, 3, MOONLANDER_I2C_TIMEOUT)) {
-            dprintf("error hori\n");
-        } else {
-            mcp23018_initd = is_launching = true;
-        }
-    }
+bool io_expander_ready(void) {
+    uint8_t tx;
+    return mcp23018_read_pins(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTA, &tx);
 }
 
 void matrix_init_custom(void) {
-    dprintf("matrix init\n");
-    // debug_matrix = true;
     // outputs
     gpio_set_pin_output(B10);
     gpio_set_pin_output(B11);
@@ -87,115 +64,116 @@ void matrix_init_custom(void) {
     gpio_set_pin_input_low(A7);
     gpio_set_pin_input_low(B0);
 
-    mcp23018_init();
+    mcp23018_init(MCP23018_DEFAULT_ADDRESS);
+    mcp23018_errors += !mcp23018_set_config(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTA, 0b00000000);
+    mcp23018_errors += !mcp23018_set_config(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTB, 0b00111111);
+
+    if (!mcp23018_errors) {
+        is_launching = true;
+    }
 }
 
 bool matrix_scan_custom(matrix_row_t current_matrix[]) {
     bool changed = false;
-
-    // Try to re-init right side
-    if (!mcp23018_initd) {
-        if (++mcp23018_reset_loop == 0) {
-            // if (++mcp23018_reset_loop >= 1300) {
-            // since mcp23018_reset_loop is 8 bit - we'll try to reset once in 255 matrix scans
-            // this will be approx bit more frequent than once per second
-            print("trying to reset mcp23018\n");
-            mcp23018_init();
-            if (!mcp23018_initd) {
-                print("right side not responding\n");
-            } else {
-                print("right side attached\n");
-#ifdef RGB_MATRIX_ENABLE
-                rgb_matrix_init();
-#endif
+    // Attempt to reset the mcp23018 if it's not initialized
+    if (mcp23018_errors) {
+        if (++mcp23018_reset_loop > 0x1FFF) {
+            if (io_expander_ready()) {
+                // If we managed to initialize the mcp23018 - we need to reinitialize the matrix / layer state. During an electric discharge the i2c peripherals might be in a weird state. Giving a delay and resetting the MCU allows to recover from this.
+                wait_ms(200);
+                mcu_reset();
             }
         }
     }
 
     matrix_row_t data = 0;
     // actual matrix
-    for (uint8_t row = 0; row <= ROWS_PER_HAND; row++) {
+    for (uint8_t row = 0; row <= MCP_ROWS_PER_HAND; row++) {
         // strobe row
         switch (row) {
-            case 0: gpio_write_pin_high(B10); break;
-            case 1: gpio_write_pin_high(B11); break;
-            case 2: gpio_write_pin_high(B12); break;
-            case 3: gpio_write_pin_high(B13); break;
-            case 4: gpio_write_pin_high(B14); break;
-            case 5: gpio_write_pin_high(B15); break;
-            case 6: break; // Left hand has 6 rows
+            case 0:
+                gpio_write_pin_high(B10);
+                break;
+            case 1:
+                gpio_write_pin_high(B11);
+                break;
+            case 2:
+                gpio_write_pin_high(B12);
+                break;
+            case 3:
+                gpio_write_pin_high(B13);
+                break;
+            case 4:
+                gpio_write_pin_high(B14);
+                break;
+            case 5:
+                gpio_write_pin_high(B15);
+                break;
+            case 6:
+                break; // Left hand has 6 rows
         }
 
-        // right side
-        if (mcp23018_initd) {
-            // #define MCP23_ROW_PINS { GPB5, GBP4, GBP3, GBP2, GBP1, GBP0 }       outputs
-            // #define MCP23_COL_PINS { GPA0, GBA1, GBA2, GBA3, GBA4, GBA5, GBA6 } inputs
-
+        // Selecting the row on the right side of the keyboard.
+        if (!mcp23018_errors) {
             // select row
-            mcp23018_tx[0] = 0x12;                                                                   // GPIOA
-            mcp23018_tx[1] = (0b01111111 & ~(1 << (row))) | ((uint8_t)!mcp23018_leds[2] << 7);       // activate row
-            mcp23018_tx[2] = ((uint8_t)!mcp23018_leds[1] << 6) | ((uint8_t)!mcp23018_leds[0] << 7);  // activate row
+            mcp23018_errors += !mcp23018_set_output_all(MCP23018_DEFAULT_ADDRESS, (0b01111111 & ~(1 << (row))) | ((uint8_t)!mcp23018_leds[2] << 7), ((uint8_t)!mcp23018_leds[1] << 6) | ((uint8_t)!mcp23018_leds[0] << 7));
+        }
 
-            if (MSG_OK != i2c_transmit(MCP23018_DEFAULT_ADDRESS << 1, mcp23018_tx, 3, MOONLANDER_I2C_TIMEOUT)) {
-                dprintf("error hori\n");
-                mcp23018_initd = false;
+        // Reading the left side of the keyboard.
+        if (row < MCP_ROWS_PER_HAND) {
+            // i2c comm incur enough wait time
+            if (mcp23018_errors) {
+                // need wait to settle pin state
+                matrix_io_delay();
+            }
+            // read col data
+            data = ((readPin(A0) << 0) | (readPin(A1) << 1) | (readPin(A2) << 2) | (readPin(A3) << 3) | (readPin(A6) << 4) | (readPin(A7) << 5) | (readPin(B0) << 6));
+            // unstrobe  row
+            switch (row) {
+                case 0:
+                    gpio_write_pin_low(B10);
+                    break;
+                case 1:
+                    gpio_write_pin_low(B11);
+                    break;
+                case 2:
+                    gpio_write_pin_low(B12);
+                    break;
+                case 3:
+                    gpio_write_pin_low(B13);
+                    break;
+                case 4:
+                    gpio_write_pin_low(B14);
+                    break;
+                case 5:
+                    gpio_write_pin_low(B15);
+                    break;
+                case 6:
+                    break;
             }
 
-            // read col
-
-            mcp23018_tx[0] = 0x13;  // GPIOB
-            if (MSG_OK != i2c_read_register(MCP23018_DEFAULT_ADDRESS << 1, mcp23018_tx[0], &mcp23018_rx[0], 1, MOONLANDER_I2C_TIMEOUT)) {
-                dprintf("error vert\n");
-                mcp23018_initd = false;
+            if (current_matrix[row] != data) {
+                current_matrix[row] = data;
+                changed             = true;
             }
+        }
 
-            data = ~(mcp23018_rx[0] & 0b00111111);
-            // data = 0x01;
+        // Reading the right side of the keyboard.
+        if (!mcp23018_errors) {
+            uint8_t rx;
+            mcp23018_errors += !mcp23018_read_pins(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTB, &rx);
+            data = ~(rx & 0b00111111);
         } else {
             data = 0;
         }
 
         if (raw_matrix_right[row] != data) {
             raw_matrix_right[row] = data;
-            changed         = true;
-        }
-
-
-        // left side
-        if (row < ROWS_PER_HAND) {
-            // i2c comm incur enough wait time
-            if (!mcp23018_initd) {
-                // need wait to settle pin state
-                matrix_io_delay();
-            }
-            // read col data
-            data = (
-                (gpio_read_pin(A0) << 0 ) |
-                (gpio_read_pin(A1) << 1 ) |
-                (gpio_read_pin(A2) << 2 ) |
-                (gpio_read_pin(A3) << 3 ) |
-                (gpio_read_pin(A6) << 4 ) |
-                (gpio_read_pin(A7) << 5 ) |
-                (gpio_read_pin(B0) << 6 )
-            );
-            // unstrobe  row
-            switch (row) {
-                case 0: gpio_write_pin_low(B10); break;
-                case 1: gpio_write_pin_low(B11); break;
-                case 2: gpio_write_pin_low(B12); break;
-                case 3: gpio_write_pin_low(B13); break;
-                case 4: gpio_write_pin_low(B14); break;
-                case 5: gpio_write_pin_low(B15); break;
-                case 6: break;
-            }
-
-            if (current_matrix[row] != data) {
-                current_matrix[row]    = data;
-                changed                = true;
-            }
+            changed               = true;
         }
     }
-    for (uint8_t row = 0; row < ROWS_PER_HAND; row++) {
+
+    for (uint8_t row = 0; row < MCP_ROWS_PER_HAND; row++) {
         current_matrix[11 - row] = 0;
         for (uint8_t col = 0; col < MATRIX_COLS; col++) {
             current_matrix[11 - row] |= ((raw_matrix_right[6 - col] & (1 << row) ? 1 : 0) << col);
@@ -222,12 +200,12 @@ void matrix_power_up(void) {
     }
 
     // initialize matrix state: all keys off
-    for (uint8_t i=0; i < MATRIX_ROWS; i++) {
+    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
         matrix[i] = 0;
     }
-
 }
 
 bool is_transport_connected(void) {
-    return mcp23018_initd;
-}
\ No newline at end of file
+    return (bool)(mcp23018_errors == 0);
+}
+#pragma GCC pop_options
diff --git a/keyboards/zsa/moonlander/mcuconf.h b/keyboards/zsa/moonlander/mcuconf.h
index 032c853f4e..43d777453b 100644
--- a/keyboards/zsa/moonlander/mcuconf.h
+++ b/keyboards/zsa/moonlander/mcuconf.h
@@ -22,10 +22,6 @@
 #undef STM32_I2C_USE_I2C1
 #define STM32_I2C_USE_I2C1 TRUE
 
-// for future hardwar
-#undef STM32_I2C_USE_I2C2
-#define STM32_I2C_USE_I2C2 TRUE
-
 // for audio
 #undef STM32_DAC_USE_DAC1_CH1
 #define STM32_DAC_USE_DAC1_CH1 TRUE
@@ -33,7 +29,3 @@
 #define STM32_DAC_USE_DAC1_CH2 TRUE
 #undef STM32_GPT_USE_TIM6
 #define STM32_GPT_USE_TIM6 TRUE
-#undef STM32_GPT_USE_TIM7
-#define STM32_GPT_USE_TIM7 TRUE
-#undef STM32_GPT_USE_TIM8
-#define STM32_GPT_USE_TIM8 TRUE
diff --git a/keyboards/zsa/moonlander/moonlander.c b/keyboards/zsa/moonlander/moonlander.c
index 0584e8cd31..b95966c414 100644
--- a/keyboards/zsa/moonlander/moonlander.c
+++ b/keyboards/zsa/moonlander/moonlander.c
@@ -23,87 +23,96 @@ keyboard_config_t keyboard_config;
 bool mcp23018_leds[3] = {0, 0, 0};
 bool is_launching     = false;
 
-#ifdef DYNAMIC_MACRO_ENABLE
-static bool is_dynamic_recording = false;
+#if defined(DEFERRED_EXEC_ENABLE)
+#    if defined(DYNAMIC_MACRO_ENABLE)
+deferred_token dynamic_macro_token = INVALID_DEFERRED_TOKEN;
+
+static uint32_t dynamic_macro_led(uint32_t trigger_time, void *cb_arg) {
+    static bool led_state = true;
+    if (!is_launching) {
+        led_state = !led_state;
+        ML_LED_3(led_state);
+    }
+    return 100;
+}
 
 bool dynamic_macro_record_start_kb(int8_t direction) {
     if (!dynamic_macro_record_start_user(direction)) {
         return false;
     }
-    is_dynamic_recording = true;
-    return true;
+    if (dynamic_macro_token == INVALID_DEFERRED_TOKEN) {
+        ML_LED_3(true);
+        dynamic_macro_token = defer_exec(100, dynamic_macro_led, NULL);
+    }
+    return true
 }
 
 bool dynamic_macro_record_end_kb(int8_t direction) {
     if (!dynamic_macro_record_end_user(direction)) {
         return false;
     }
-    is_dynamic_recording = false;
-    ML_LED_3(false);
-    return false;
+    if (cancel_deferred_exec(dynamic_macro_token)) {
+        dynamic_macro_token = INVALID_DEFERRED_TOKEN;
+        ML_LED_3(false);
+    }
+    return false
+}
+#    endif
+
+static uint32_t startup_exec(uint32_t trigger_time, void *cb_arg) {
+    static uint8_t startup_loop = 0;
+
+    switch (startup_loop++) {
+        case 0:
+            ML_LED_1(true);
+            ML_LED_2(false);
+            ML_LED_3(false);
+            ML_LED_4(false);
+            ML_LED_5(false);
+            ML_LED_6(false);
+            break;
+        case 1:
+            ML_LED_2(true);
+            break;
+        case 2:
+            ML_LED_3(true);
+            break;
+        case 3:
+            ML_LED_4(true);
+            break;
+        case 4:
+            ML_LED_5(true);
+            break;
+        case 5:
+            ML_LED_6(true);
+            break;
+        case 6:
+            ML_LED_1(false);
+            break;
+        case 7:
+            ML_LED_2(false);
+            break;
+        case 8:
+            ML_LED_3(false);
+            break;
+        case 9:
+            ML_LED_4(false);
+            break;
+        case 10:
+            ML_LED_5(false);
+            break;
+        case 11:
+            ML_LED_6(false);
+            break;
+        case 12:
+            is_launching = false;
+            layer_state_set_kb(layer_state);
+            return 0;
+    }
+    return 250;
 }
 #endif
 
-void moonlander_led_task(void) {
-    if (is_launching) {
-        ML_LED_1(false);
-        ML_LED_2(false);
-        ML_LED_3(false);
-        ML_LED_4(false);
-        ML_LED_5(false);
-        ML_LED_6(false);
-
-        ML_LED_1(true);
-        wait_ms(250);
-        ML_LED_2(true);
-        wait_ms(250);
-        ML_LED_3(true);
-        wait_ms(250);
-        ML_LED_4(true);
-        wait_ms(250);
-        ML_LED_5(true);
-        wait_ms(250);
-        ML_LED_6(true);
-        wait_ms(250);
-        ML_LED_1(false);
-        wait_ms(250);
-        ML_LED_2(false);
-        wait_ms(250);
-        ML_LED_3(false);
-        wait_ms(250);
-        ML_LED_4(false);
-        wait_ms(250);
-        ML_LED_5(false);
-        wait_ms(250);
-        ML_LED_6(false);
-        wait_ms(250);
-        is_launching = false;
-        layer_state_set_kb(layer_state);
-    }
-#ifdef DYNAMIC_MACRO_ENABLE
-    else if (is_dynamic_recording) {
-        ML_LED_3(true);
-        wait_ms(100);
-        ML_LED_3(false);
-        wait_ms(155);
-    }
-#endif
-#if !defined(MOONLANDER_USER_LEDS)
-    else {
-        layer_state_set_kb(layer_state);
-    }
-#endif
-}
-
-static THD_WORKING_AREA(waLEDThread, 128);
-static THD_FUNCTION(LEDThread, arg) {
-    (void)arg;
-    chRegSetThreadName("LEDThread");
-    while (true) {
-        moonlander_led_task();
-    }
-}
-
 void keyboard_pre_init_kb(void) {
     gpio_set_pin_output(B5);
     gpio_set_pin_output(B4);
@@ -113,13 +122,6 @@ void keyboard_pre_init_kb(void) {
     gpio_write_pin_low(B4);
     gpio_write_pin_low(B3);
 
-    chThdCreateStatic(waLEDThread, sizeof(waLEDThread), NORMALPRIO - 16, LEDThread, NULL);
-
-    /* the array is initialized to 0, no need to re-set it here */
-    // mcp23018_leds[0] = 0;  // blue
-    // mcp23018_leds[1] = 0;  // green
-    // mcp23018_leds[2] = 0;  // red
-
     keyboard_pre_init_user();
 }
 
@@ -183,13 +185,7 @@ layer_state_t layer_state_set_kb(layer_state_t state) {
 #ifdef RGB_MATRIX_ENABLE
 // clang-format off
 const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
-/* Refer to IS31 manual for these locations
- *   driver
- *   |  R location
- *   |  |      G location
- *   |  |      |      B location
- *   |  |      |      | */
-    {0, C3_2,  C1_1,  C4_2}, // 1
+    {0, C3_2,  C1_1,  C4_2},
     {0, C2_2,  C1_2,  C4_3},
     {0, C2_3,  C1_3,  C3_3},
     {0, C2_4,  C1_4,  C3_4},
@@ -199,7 +195,7 @@ const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
     {0, C2_8,  C1_8,  C3_8},
     {0, C3_1,  C2_1,  C4_1},
 
-    {0, C7_8,  C6_8,  C8_8}, // 10
+    {0, C7_8,  C6_8,  C8_8},
     {0, C7_7,  C6_7,  C9_8},
     {0, C8_7,  C6_6,  C9_7},
     {0, C8_6,  C7_6,  C9_6},
@@ -209,7 +205,7 @@ const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
     {0, C8_2,  C7_2,  C9_2},
     {0, C8_1,  C7_1,  C9_1},
 
-    {0, C3_10,  C1_9,   C4_10}, // 19
+    {0, C3_10,  C1_9,   C4_10},
     {0, C2_10,  C1_10,  C4_11},
     {0, C2_11,  C1_11,  C3_11},
     {0, C2_12,  C1_12,  C3_12},
@@ -219,7 +215,7 @@ const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
     {0, C2_16,  C1_16,  C3_16},
     {0, C3_9,   C2_9,   C4_9},
 
-    {0, C7_16,  C6_16,  C8_16}, // 28
+    {0, C7_16,  C6_16,  C8_16},
     {0, C7_15,  C6_15,  C9_16},
     {0, C8_15,  C6_14,  C9_15},
     {0, C8_10,  C7_10,  C9_10},
@@ -229,7 +225,7 @@ const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
     {0, C8_13,  C7_13,  C9_13},
     {0, C8_14,  C7_14,  C9_14},
 
-    {1, C3_2,  C1_1,  C4_2}, // 1
+    {1, C3_2,  C1_1,  C4_2},
     {1, C2_2,  C1_2,  C4_3},
     {1, C2_3,  C1_3,  C3_3},
     {1, C2_4,  C1_4,  C3_4},
@@ -239,7 +235,7 @@ const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
     {1, C2_8,  C1_8,  C3_8},
     {1, C3_1,  C2_1,  C4_1},
 
-    {1, C7_8,  C6_8,  C8_8}, // 10
+    {1, C7_8,  C6_8,  C8_8},
     {1, C7_7,  C6_7,  C9_8},
     {1, C8_7,  C6_6,  C9_7},
     {1, C8_6,  C7_6,  C9_6},
@@ -249,7 +245,7 @@ const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
     {1, C8_2,  C7_2,  C9_2},
     {1, C8_1,  C7_1,  C9_1},
 
-    {1, C3_10,  C1_9,   C4_10}, // 19
+    {1, C3_10,  C1_9,   C4_10},
     {1, C2_10,  C1_10,  C4_11},
     {1, C2_11,  C1_11,  C3_11},
     {1, C2_12,  C1_12,  C3_12},
@@ -259,7 +255,7 @@ const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
     {1, C2_16,  C1_16,  C3_16},
     {1, C3_9,   C2_9,   C4_9},
 
-    {1, C7_16,  C6_16,  C8_16}, // 28
+    {1, C7_16,  C6_16,  C8_16},
     {1, C7_15,  C6_15,  C9_16},
     {1, C8_15,  C6_14,  C9_15},
     {1, C8_10,  C7_10,  C9_10},
@@ -268,61 +264,8 @@ const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = {
     {1, C8_12,  C7_12,  C9_12},
     {1, C8_13,  C7_13,  C9_13},
     {1, C8_14,  C7_14,  C9_14},
-
 };
-
-led_config_t g_led_config = { {
-    {  0,  5, 10, 15, 20, 25, 29 },
-    {  1,  6, 11, 16, 21, 26, 30 },
-    {  2,  7, 12, 17, 22, 27, 31 },
-    {  3,  8, 13, 18, 23, 28, NO_LED },
-    {  4,  9, 14, 19, 24, NO_LED, NO_LED },
-    { 32, 33, 34, 35, NO_LED, NO_LED, NO_LED },
-    { 65, 61, 56, 51, 46, 41, 36 },
-    { 66, 62, 57, 52, 47, 42, 37 },
-    { 67, 63, 58, 53, 48, 43, 38 },
-    { NO_LED, 64, 59, 54, 49, 44, 39 },
-    { NO_LED, NO_LED, 60, 55, 50, 45, 40 },
-    { NO_LED, NO_LED, NO_LED, 71, 70, 69, 68 }
-}, {
-    {  0,   4}, {  0,  20}, {  0,  36}, {  0, 52}, {  0,  68},
-    { 16,   3}, { 16,  19}, { 16,  35}, { 16, 51}, { 16,  67},
-    { 32,   1}, { 32,  17}, { 32,  33}, { 32, 49}, { 32,  65},
-    { 48,   0}, { 48,  16}, { 48,  32}, { 48, 48}, { 48,  64},
-    { 64,   1}, { 64,  17}, { 64,  33}, { 64, 49}, { 64,  65},
-    { 80,   3}, { 80,  19}, { 80,  35}, { 80, 51},
-    { 96,   4}, { 96,  20}, { 96,  36},
-    { 88,  69}, {100,  80}, {112,  91}, {108, 69},
-
-    {240,   4}, {240,  20}, {240,  36}, {240,  52}, {240,  68},
-    {224,   3}, {224,  19}, {224,  35}, {224,  51}, {224,  67},
-    {208,   1}, {208,  17}, {208,  33}, {208,  49}, {208,  65},
-    {192,   0}, {192,  16}, {192,  32}, {192,  48}, {192,  64},
-    {176,   1}, {176,  17}, {176,  33}, {176,  49}, {176,  65},
-    {160,   3}, {160,  19}, {160,  35}, {160,  51},
-    {144,   4}, {144,  20}, {144,  36},
-    {152,  69}, {140,  80}, {128,  91}, {132,  69}
-}, {
-    1, 1, 1, 1, 1,
-    4, 4, 4, 4, 1,
-    4, 4, 4, 4, 1,
-    4, 4, 4, 4, 1,
-    4, 4, 4, 4, 1,
-    4, 4, 4, 4,
-    1, 1, 1,
-    1, 1, 1, 1,
-
-    1, 1, 1, 1, 1,
-    4, 4, 4, 4, 1,
-    4, 4, 4, 4, 1,
-    4, 4, 4, 4, 1,
-    4, 4, 4, 4, 1,
-    4, 4, 4, 4,
-    1, 1, 1,
-    1, 1, 1, 1
-} };
 // clang-format on
-
 #endif
 
 #ifdef AUDIO_ENABLE
@@ -368,11 +311,6 @@ const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = {
     {{6,5}, {5,5}, {4,5}, {3,5}, {2,5}, {1,5},{0,5}},
 };
 // clang-format on
-
-void keyboard_post_init_kb(void) {
-    rgb_matrix_enable_noeeprom();
-    keyboard_post_init_user();
-}
 #endif
 
 #if defined(AUDIO_ENABLE) && defined(MUSIC_MAP)
@@ -451,7 +389,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
     return true;
 }
 
-void matrix_init_kb(void) {
+void keyboard_post_init_kb(void) {
     keyboard_config.raw = eeconfig_read_kb();
 
     if (!keyboard_config.led_level && !keyboard_config.led_level_res) {
@@ -460,11 +398,11 @@ void matrix_init_kb(void) {
         eeconfig_update_kb(keyboard_config.raw);
     }
 #ifdef RGB_MATRIX_ENABLE
-    if (keyboard_config.rgb_matrix_enable) {
-        rgb_matrix_set_flags(LED_FLAG_ALL);
-    } else {
-        rgb_matrix_set_flags(LED_FLAG_NONE);
-    }
+    rgb_matrix_enable_noeeprom();
+#endif
+#if defined(DEFERRED_EXEC_ENABLE)
+    is_launching = true;
+    defer_exec(500, startup_exec, NULL);
 #endif
     matrix_init_user();
 }
diff --git a/keyboards/zsa/moonlander/rules.mk b/keyboards/zsa/moonlander/rules.mk
index 10928ea061..d2018cfff0 100644
--- a/keyboards/zsa/moonlander/rules.mk
+++ b/keyboards/zsa/moonlander/rules.mk
@@ -1,5 +1,7 @@
 CUSTOM_MATRIX = lite
 
-# project specific files
-SRC += matrix.c
+VPATH += drivers/gpio
+SRC += matrix.c mcp23018.c
 I2C_DRIVER_REQUIRED = yes
+
+MCU_LDSCRIPT = STM32F303xB