summaryrefslogtreecommitdiffstats
path: root/keyboards/handwired/symmetric70_proto/matrix_fast
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/handwired/symmetric70_proto/matrix_fast')
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/cpp_map.h53
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/gpio_extr.h28
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/matrix.c234
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/matrix_config_expand.c234
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/matrix_extension_74hc15x.c72
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/matrix_extr.h36
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/readme.md168
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/test_config.h21
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/test_config_74hc157.h28
-rw-r--r--keyboards/handwired/symmetric70_proto/matrix_fast/test_config_direct.h34
10 files changed, 908 insertions, 0 deletions
diff --git a/keyboards/handwired/symmetric70_proto/matrix_fast/cpp_map.h b/keyboards/handwired/symmetric70_proto/matrix_fast/cpp_map.h
new file mode 100644
index 0000000000..d197be6d9f
--- /dev/null
+++ b/keyboards/handwired/symmetric70_proto/matrix_fast/cpp_map.h
@@ -0,0 +1,53 @@
+/* Copyright 2021 mtei
+ *
+ * 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/>.
+ */
+#pragma once
+// clang-format off
+
+#define _MAP1(E, _1) E(_1)
+#define _MAP2(E, _1,_2) E(_1) E(_2)
+#define _MAP3(E, _1,_2,_3) E(_1) E(_2) E(_3)
+#define _MAP4(E, _1,_2,_3,_4) E(_1) E(_2) E(_3) E(_4)
+#define _MAP5(E, _1,_2,_3,_4,_5) E(_1) E(_2) E(_3) E(_4) E(_5)
+#define _MAP6(E, _1,_2,_3,_4,_5,_6) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6)
+#define _MAP7(E, _1,_2,_3,_4,_5,_6,_7) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7)
+#define _MAP8(E, _1,_2,_3,_4,_5,_6,_7,_8) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8)
+#define _MAP9(E, _1,_2,_3,_4,_5,_6,_7,_8,_9) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9)
+#define _MAP10(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10)
+#define _MAP11(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11)
+#define _MAP12(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12)
+#define _MAP13(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13)
+#define _MAP14(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14)
+#define _MAP15(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15)
+#define _MAP16(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16)
+#define _MAP17(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17)
+#define _MAP18(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18)
+#define _MAP19(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19)
+#define _MAP20(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20)
+#define _MAP21(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21)
+#define _MAP22(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22)
+#define _MAP23(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23)
+#define _MAP24(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24)
+#define _MAP25(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24) E(_25)
+#define _MAP26(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24) E(_25) E(_26)
+#define _MAP27(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24) E(_25) E(_26) E(_27)
+#define _MAP28(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24) E(_25) E(_26) E(_27) E(_28)
+#define _MAP29(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24) E(_25) E(_26) E(_27) E(_28) E(_29)
+#define _MAP30(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24) E(_25) E(_26) E(_27) E(_28) E(_29) E(_30)
+#define _MAP31(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24) E(_25) E(_26) E(_27) E(_28) E(_29) E(_30) E(_31)
+#define _MAP32(E, _1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32) E(_1) E(_2) E(_3) E(_4) E(_5) E(_6) E(_7) E(_8) E(_9) E(_10) E(_11) E(_12) E(_13) E(_14) E(_15) E(_16) E(_17) E(_18) E(_19) E(_20) E(_21) E(_22) E(_23) E(_24) E(_25) E(_26) E(_27) E(_28) E(_29) E(_30) E(_31) E(_32)
+
+#define SELECT_MAP(e,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,NAME,...) NAME
+#define MAP(E,...) SELECT_MAP(E,__VA_ARGS__,_MAP32,_MAP31,_MAP30,_MAP29,_MAP28,_MAP27,_MAP26,_MAP25,_MAP24,_MAP23,_MAP22,_MAP21,_MAP20,_MAP19,_MAP18,_MAP17,_MAP16,_MAP15,_MAP14,_MAP13,_MAP12,_MAP11,_MAP10,_MAP9,_MAP8,_MAP7,_MAP6,_MAP5,_MAP4,_MAP3,_MAP2,_MAP1)(E,__VA_ARGS__)
diff --git a/keyboards/handwired/symmetric70_proto/matrix_fast/gpio_extr.h b/keyboards/handwired/symmetric70_proto/matrix_fast/gpio_extr.h
new file mode 100644
index 0000000000..e31cb5f3a5
--- /dev/null
+++ b/keyboards/handwired/symmetric70_proto/matrix_fast/gpio_extr.h
@@ -0,0 +1,28 @@
+#pragma once
+// clang-format off
+
+#if defined(__AVR__)
+typedef uint8_t port_data_t;
+
+#define readPort(port) PINx_ADDRESS(port)
+
+#define setPortBitInput(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
+#define setPortBitInputHigh(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) |= _BV((bit)&0xF))
+#define setPortBitOutput(port, bit) (DDRx_ADDRESS(port) |= _BV((bit)&0xF))
+
+#define writePortBitLow(port, bit) (PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
+#define writePortBitHigh(port, bit) (PORTx_ADDRESS(port) |= _BV((bit)&0xF))
+
+#else
+typedef uint16_t port_data_t;
+
+#define readPort(qmk_pin) palReadPort(PAL_PORT(qmk_pin))
+
+#define setPortBitInput(qmk_pin, bit) palSetPadMode(PAL_PORT(qmk_pin), bit, PAL_MODE_INPUT)
+#define setPortBitInputHigh(qmk_pin, bit) palSetPadMode(PAL_PORT(qmk_pin), bit, PAL_MODE_INPUT_PULLUP)
+#define setPortBitInputLow(qmk_pin, bit) palSetPadMode(PAL_PORT(qmk_pin), bit, PAL_MODE_INPUT_PULLDOWN)
+#define setPortBitOutput(qmk_pin, bit) palSetPadMode(PAL_PORT(qmk_pin), bit, PAL_MODE_OUTPUT_PUSHPULL)
+
+#define writePortBitLow(qmk_pin, bit) palClearLine(PAL_LINE(PAL_PORT(qmk_pin), bit))
+#define writePortBitHigh(qmk_pin, bit) palSetLine(PAL_LINE(PAL_PORT(qmk_pin), bit))
+#endif
diff --git a/keyboards/handwired/symmetric70_proto/matrix_fast/matrix.c b/keyboards/handwired/symmetric70_proto/matrix_fast/matrix.c
new file mode 100644
index 0000000000..cb21bfcf8d
--- /dev/null
+++ b/keyboards/handwired/symmetric70_proto/matrix_fast/matrix.c
@@ -0,0 +1,234 @@
+/*
+Copyright 2021 mtei
+
+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/>.
+*/
+// clang-format off
+#include <stdint.h>
+#include <stdbool.h>
+#include <gpio.h>
+#ifndef readPort
+# include "gpio_extr.h"
+#endif
+#include "util.h"
+#include "matrix.h"
+#include "matrix_extr.h"
+#include "debounce.h"
+#include "quantum.h"
+
+#define ALWAYS_INLINE inline __attribute__((always_inline))
+#define NO_INLINE __attribute__((noinline))
+#define LOCAL_FUNC static
+#define LOCAL_DATA static
+
+#ifndef _BV
+# define _BV(bit) (1 << (bit))
+#endif
+
+#ifndef MATRIX_DEBUG_PIN
+# define MATRIX_DEBUG_PIN_INIT()
+# define MATRIX_DEBUG_SCAN_START()
+# define MATRIX_DEBUG_SCAN_END()
+# define MATRIX_DEBUG_DELAY_START()
+# define MATRIX_DEBUG_DELAY_END()
+# define MATRIX_DEBUG_GAP()
+#else
+# define MATRIX_DEBUG_GAP() asm volatile("nop \n nop":::"memory")
+#endif
+
+typedef uint16_t port_width_t;
+#if MATRIX_TYPE == DIRECT_SWITCH || MATRIX_TYPE == DIODE_COL2ROW
+# define MATRIX_LINES MATRIX_ROWS
+typedef matrix_row_t matrix_line_t;
+#endif
+#if MATRIX_TYPE == DIODE_ROW2COL
+# define MATRIX_LINES MATRIX_COLS
+typedef matrix_col_t matrix_line_t;
+#endif
+typedef struct _port_descriptor {
+ int device;
+ pin_t port;
+} port_descriptor;
+
+/* matrix state(1:on, 0:off) */
+extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
+extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
+
+#define setPortBitOutput_writeLow(port, bit) \
+ do { setPortBitOutput(port, bit); writePortBitLow(port, bit); } while(0)
+#define setPortBitOutput_writeLow_atomic(port, bit) \
+ do { ATOMIC_BLOCK_FORCEON { setPortBitOutput_writeLow(port, bit); } } while(0)
+#define setPortBitInputHigh_atomic(port, bit) \
+ do { ATOMIC_BLOCK_FORCEON { setPortBitInputHigh(port, bit); } } while(0)
+
+#if defined(MATRIX_IN_PORTS) && defined(MATRIX_IN_PINS)
+# include "matrix_config_expand.c"
+#else
+# error matrix.c need defined MATRIX_IN_PORTS and MATRIX_IN_PINS
+#endif
+
+LOCAL_FUNC
+void unselect_output(uint8_t out_index) {
+ unselect_output_inline(out_index);
+}
+
+LOCAL_FUNC
+void init_output_ports(void) {
+ for (int i = 0; i < END_outpin_index; i++) {
+ unselect_output(i);
+ }
+}
+
+LOCAL_FUNC
+void init_all_ports(void) {
+ init_input_ports();
+ init_output_ports();
+ init_inport_mask();
+ init_extension();
+}
+
+LOCAL_FUNC ALWAYS_INLINE void select_line_and_read_input_ports(uint8_t current_line, port_width_t port_buffer[NUM_OF_INPUT_PORTS]);
+LOCAL_FUNC void select_line_and_read_input_ports(uint8_t current_line, port_width_t port_buffer[NUM_OF_INPUT_PORTS]) {
+ // Select row (or col)
+ select_output(current_line);
+ matrix_output_select_delay();
+
+ // Read ports
+ read_all_input_ports(port_buffer, false);
+
+ // Unselect row (or col)
+ unselect_output_inline(current_line);
+}
+
+LOCAL_FUNC ALWAYS_INLINE void read_matrix_line(matrix_line_t phy_matrix[], uint8_t current_line);
+
+#if MATRIX_TYPE == DIODE_ROW2COL || MATRIX_TYPE == DIODE_COL2ROW
+LOCAL_FUNC void read_matrix_line(matrix_line_t phy_matrix[], uint8_t current_line) {
+ // Start with a clear matrix row
+ matrix_line_t current_line_value = 0;
+ port_width_t port_buffer[NUM_OF_INPUT_PORTS];
+
+#ifdef MATRIX_GPIO_NEED_SEPARATE_ATOMIC
+ select_line_and_read_input_ports(current_line, port_buffer);
+#else
+ ATOMIC_BLOCK_FORCEON {
+ select_line_and_read_input_ports(current_line, port_buffer);
+ }
+#endif
+
+ // Build row (or col)
+ current_line_value = build_matrix_line(port_buffer);
+
+ // Wait signal raise up
+ if (current_line_value) {
+ MATRIX_DEBUG_DELAY_START();
+ wait_unselect_done();
+ MATRIX_DEBUG_DELAY_END();
+ }
+ phy_matrix[current_line] = current_line_value;
+}
+#endif // MATRIX_TYPE == DIODE_ROW2COL || MATRIX_TYPE == DIODE_COL2ROW
+
+#if MATRIX_TYPE == DIRECT_SWITCH
+LOCAL_FUNC void read_matrix_line(matrix_line_t phy_matrix[], uint8_t current_line) {
+ port_width_t port_buffer[NUM_OF_INPUT_PORTS];
+
+ if (current_line != 0) {
+ return;
+ }
+
+ for (uint8_t i = 0; i < MATRIX_LINES; i++) {
+ phy_matrix[i] = 0;
+ }
+
+ read_all_input_ports(port_buffer, false);
+
+ // Build matrix
+ build_matrix_direct(port_buffer, phy_matrix);
+}
+#endif // MATRIX_TYPE == DIRECT_SWITCH
+
+void matrix_init(void) {
+ // initialize key pins
+ init_all_ports();
+
+ // initialize matrix state: all keys off
+ for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+ raw_matrix[i] = 0;
+ matrix[i] = 0;
+ }
+
+ debounce_init(MATRIX_ROWS);
+
+ matrix_init_quantum();
+}
+
+uint8_t matrix_scan(void) {
+ matrix_line_t phy_matrix[MATRIX_LINES];
+
+ MATRIX_DEBUG_PIN_INIT();
+
+ MATRIX_DEBUG_SCAN_START();
+
+ // read I/O port to phy_matrix[] (physical matrix)
+ //select line, read inputs
+ for (uint8_t current_line = 0; current_line < MATRIX_LINES; current_line++) {
+ read_matrix_line(phy_matrix, current_line);
+ }
+ MATRIX_DEBUG_SCAN_END(); MATRIX_DEBUG_GAP(); MATRIX_DEBUG_SCAN_START();
+
+ bool changed = false;
+#if MATRIX_TYPE == DIRECT_SWITCH || MATRIX_TYPE == DIODE_COL2ROW
+ // copy phy_matrix[] to raw_matrix[]
+ for (uint8_t current_line = 0; current_line < MATRIX_ROWS; current_line++) {
+ if (raw_matrix[current_line] != phy_matrix[current_line]) {
+ changed = true;
+ raw_matrix[current_line] = phy_matrix[current_line];
+ }
+ }
+#endif
+#if MATRIX_TYPE == DIODE_ROW2COL
+ // transpose phy_matrix[] to raw_matrix[]
+ matrix_row_t trans_matrix[MATRIX_ROWS];
+ for (uint8_t i = 0; i < MATRIX_ROWS; i++ ) {
+ trans_matrix[i] = 0;
+ }
+ for (uint8_t src_line = 0; src_line < MATRIX_LINES; src_line++) {
+ matrix_line_t src_line_data = phy_matrix[src_line];
+ matrix_row_t dist_bit = MATRIX_ROW_SHIFTER << src_line;
+ for (uint8_t dist_rows = 0; dist_rows < MATRIX_ROWS; dist_rows++) {
+ if ((src_line_data & 1) == 1) {
+ trans_matrix[dist_rows] |= dist_bit;
+ }
+ src_line_data >>= 1;
+ }
+ }
+ for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
+ if (raw_matrix[current_row] != trans_matrix[current_row]) {
+ changed = true;
+ raw_matrix[current_row] = trans_matrix[current_row];
+ }
+ }
+#endif
+ MATRIX_DEBUG_SCAN_END(); MATRIX_DEBUG_GAP(); MATRIX_DEBUG_SCAN_START();
+
+ // debounce raw_matrix[] to matrix[]
+ debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
+ MATRIX_DEBUG_SCAN_END(); MATRIX_DEBUG_GAP();
+
+ MATRIX_DEBUG_SCAN_START();
+ matrix_scan_quantum();
+ MATRIX_DEBUG_SCAN_END();
+ return (uint8_t)changed;
+}
diff --git a/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_config_expand.c b/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_config_expand.c
new file mode 100644
index 0000000000..0df605db47
--- /dev/null
+++ b/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_config_expand.c
@@ -0,0 +1,234 @@
+/*
+Copyright 2021 mtei
+
+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/>.
+*/
+// clang-format off
+
+#include "matrix_extr.h"
+
+#ifdef DEBUG_MATRIX_CONFIG
+// config expand debug
+// avr-gcc -DDEBUG_MATRIX_CONFIG=\"test_config.h\" -E -C matrix_config_expand.c
+# include DEBUG_MATRIX_CONFIG
+#endif
+
+#undef NO_PIN /* cancel NO_PIN define in tmk_core/common/pin_defs.h */
+#define NO_PIN NO_PORT, 0
+#if MATRIX_TYPE == DIRECT_SWITCH
+# undef MATRIX_OUT_PORTS
+# define MATRIX_OUT_PINS (0, NO_PIN)
+#endif
+
+#include "cpp_map.h"
+
+#if defined(MATRIX_EXTENSION_74HC157) || defined(MATRIX_EXTENSION_74HC153)
+# define MATRIX_EXTENSION "matrix_extension_74hc15x.c"
+#endif
+
+#ifdef MATRIX_EXTENSION
+# include MATRIX_EXTENSION
+#endif
+
+#ifdef MATRIX_GPIO_NEED_SEPARATE_ATOMIC
+# ifndef setMatrixInputHigh
+# define setMatrixInputHigh(dev, port, bit) do { if ((dev) == MCU_GPIO) { setPortBitInputHigh_atomic(port, bit); }} while(0)
+# endif
+# ifndef setMatrixOutput_writeHighZ
+# define setMatrixOutput_writeHighZ(dev, port, bit) do { if ((dev) == MCU_GPIO) { setPortBitInputHigh_atomic(port, bit); }} while(0)
+# endif
+# ifndef setMatrixOutput_writeLow
+# define setMatrixOutput_writeLow(dev, port, bit) do { if ((dev) == MCU_GPIO) { setPortBitOutput_writeLow_atomic(port, bit); }} while(0)
+# endif
+#else
+# ifndef setMatrixInputHigh
+# define setMatrixInputHigh(dev, port, bit) do { if ((dev) == MCU_GPIO) { setPortBitInputHigh(port, bit); }} while(0)
+# endif
+# ifndef setMatrixOutput_writeHighZ
+# define setMatrixOutput_writeHighZ(dev, port, bit) do { if ((dev) == MCU_GPIO) { setPortBitInputHigh(port, bit); }} while(0)
+# endif
+# ifndef setMatrixOutput_writeLow
+# define setMatrixOutput_writeLow(dev, port, bit) do { if ((dev) == MCU_GPIO) { setPortBitOutput_writeLow(port, bit); }} while(0)
+# endif
+#endif
+
+#ifndef readMatrixPort
+# define readMatrixPort(dev, port) (((dev) == MCU_GPIO) ? readPort(port) : 0)
+#endif
+#ifndef getMatrixInputMaskBit
+# define getMatrixInputMaskBit(dev, bit) (((dev) != NO_DEVICE) ? _BV((bit)&0xF) : 0)
+#endif
+
+#ifndef init_extension
+# define init_extension()
+#endif
+
+enum DEVICE_NAME {
+ MCU_GPIO,
+ NO_DEVICE,
+#ifdef MATRIX_DEVICES
+ MATRIX_DEVICES
+#endif
+};
+
+#define _INPUT_PORTS_ENUM_ELEMENT(name, dev, port) inport_index_##name,
+#define INPUT_PORTS_ENUM_ELEMENT(x) _INPUT_PORTS_ENUM_ELEMENT x
+enum INPUT_PORTS {
+ INPUT_PORTS_ENUM_ELEMENT((NO_PORT, NO_DEVICE, 0))
+ MAP(INPUT_PORTS_ENUM_ELEMENT, MATRIX_IN_PORTS)
+ NUM_OF_INPUT_PORTS
+};
+
+#define _INPUT_PINS_ENUM_ELEMENT(index, port, bit) inpin_index_##index,
+#define INPUT_PINS_ENUM_ELEMENT(x) _INPUT_PINS_ENUM_ELEMENT x
+enum INPUT_PINS {
+ MAP(INPUT_PINS_ENUM_ELEMENT, MATRIX_IN_PINS)
+ END_inpin_index
+};
+
+#define _OUTPUT_PORTS_ENUM_ELEMENT(name, dev, port) outport_index_##name,
+#define OUTPUT_PORTS_ENUM_ELEMENT(x) _OUTPUT_PORTS_ENUM_ELEMENT x
+enum OUTPUT_PORTS {
+ OUTPUT_PORTS_ENUM_ELEMENT((NO_PORT, NO_DEVICE, 0))
+#ifdef MATRIX_OUT_PORTS
+ MAP(OUTPUT_PORTS_ENUM_ELEMENT, MATRIX_OUT_PORTS)
+#endif
+ NUM_OF_OUTPUT_PORTS
+};
+
+#define _OUTPUT_PINS_ENUM_ELEMENT(index, port, bit) outpin_index_##index,
+#define OUTPUT_PINS_ENUM_ELEMENT(x) _OUTPUT_PINS_ENUM_ELEMENT x
+enum OUTPUT_PINS {
+ MAP(OUTPUT_PINS_ENUM_ELEMENT, MATRIX_OUT_PINS)
+ END_outpin_index
+};
+
+port_width_t iport_mask[NUM_OF_INPUT_PORTS];
+
+#define _INPUT_PORTS_LIST_ELEMENT(name, dev, port) \
+ [inport_index_##name] = { dev, port },
+#define INPUT_PORTS_LIST_ELEMENT(x) _INPUT_PORTS_LIST_ELEMENT x
+LOCAL_DATA
+const port_descriptor inport_list[NUM_OF_INPUT_PORTS] = {
+ INPUT_PORTS_LIST_ELEMENT((NO_PORT, NO_DEVICE, 0))
+ MAP(INPUT_PORTS_LIST_ELEMENT, MATRIX_IN_PORTS)
+};
+
+#define _OUTPUT_PORTS_LIST_ELEMENT(name, dev, port) \
+ [outport_index_##name] = { dev, port },
+#define OUTPUT_PORTS_LIST_ELEMENT(x) _OUTPUT_PORTS_LIST_ELEMENT x
+LOCAL_DATA
+const port_descriptor outport_list[NUM_OF_OUTPUT_PORTS] = {
+ OUTPUT_PORTS_LIST_ELEMENT((NO_PORT, NO_DEVICE, 0))
+#ifdef MATRIX_OUT_PORTS
+ MAP(OUTPUT_PORTS_LIST_ELEMENT, MATRIX_OUT_PORTS)
+#endif
+};
+
+#define _SELECT_OUTPUT_PIN(index, pname, bit) \
+ case outpin_index_##index: \
+ setMatrixOutput_writeLow(outport_list[outport_index_##pname].device, \
+ outport_list[outport_index_##pname].port, bit); \
+ break;
+#define SELECT_OUTPUT_PIN(x) _SELECT_OUTPUT_PIN x
+LOCAL_FUNC ALWAYS_INLINE void select_output(uint8_t out_index);
+LOCAL_FUNC
+void select_output(uint8_t out_index) {
+ switch (out_index) {
+ MAP(SELECT_OUTPUT_PIN, MATRIX_OUT_PINS)
+ }
+}
+
+#define _UNSELECT_OUTPUT_PIN(index, pname, bit) \
+ case outpin_index_##index: \
+ setMatrixOutput_writeHighZ(outport_list[outport_index_##pname].device, \
+ outport_list[outport_index_##pname].port, bit); \
+ break;
+#define UNSELECT_OUTPUT_PIN(x) _UNSELECT_OUTPUT_PIN x
+LOCAL_FUNC ALWAYS_INLINE void unselect_output_inline(uint8_t out_index);
+LOCAL_FUNC
+void unselect_output_inline(uint8_t out_index) {
+ switch (out_index) {
+ MAP(UNSELECT_OUTPUT_PIN, MATRIX_OUT_PINS)
+ }
+}
+
+#define _INIT_INPUT_PIN(index, pname, bit) \
+ setMatrixInputHigh(inport_list[inport_index_##pname].device, \
+ inport_list[inport_index_##pname].port, bit);
+#define INIT_INPUT_PIN(x) _INIT_INPUT_PIN x
+LOCAL_FUNC
+void init_input_ports(void) {
+ MAP(INIT_INPUT_PIN, MATRIX_IN_PINS)
+}
+
+#define _INIT_INPORT_MASK(index, pname, bit) \
+ iport_mask[inport_index_##pname] |= getMatrixInputMaskBit(inport_list[inport_index_##pname].device, bit);
+#define INIT_INPORT_MASK(x) _INIT_INPORT_MASK x
+LOCAL_FUNC
+void init_inport_mask(void) {
+ for (int i = 0; i < NUM_OF_INPUT_PORTS; i++ ) {
+ iport_mask[i] = 0;
+ }
+ MAP(INIT_INPORT_MASK, MATRIX_IN_PINS)
+}
+
+#define _READ_INPUT_PORT(name, dev, port) \
+ buffer[inport_index_##name] = readMatrixPort(dev, port);
+#define READ_INPUT_PORT(x) _READ_INPUT_PORT x
+LOCAL_FUNC
+ALWAYS_INLINE void read_all_input_ports(port_width_t buffer[NUM_OF_INPUT_PORTS], bool wait_unselect);
+LOCAL_FUNC
+void read_all_input_ports(port_width_t buffer[NUM_OF_INPUT_PORTS], bool wait_unselect) {
+ READ_INPUT_PORT((NO_PORT, NO_DEVICE, 0))
+ MAP(READ_INPUT_PORT, MATRIX_IN_PORTS)
+}
+
+#define _MASK_INPUT(name, dev, port) \
+ mask |= ((~buffer[inport_index_##name]) & iport_mask[inport_index_##name]);
+#define MASK_INPUT(x) _MASK_INPUT x
+LOCAL_FUNC ALWAYS_INLINE void wait_unselect_done(void);
+LOCAL_FUNC
+void wait_unselect_done(void) {
+ port_width_t mask;
+ port_width_t buffer[NUM_OF_INPUT_PORTS];
+ do {
+ read_all_input_ports(buffer, true);
+ MATRIX_DEBUG_DELAY_END();
+ mask = 0;
+ MAP(MASK_INPUT, MATRIX_IN_PORTS);
+ MATRIX_DEBUG_DELAY_START();
+ } while (mask != 0);
+}
+
+#define _BUILD_INPUT_PORT(index, pname, bit) \
+ result |= (buffer[inport_index_##pname] & _BV(bit)) ? 0 : _BV(inpin_index_##index);
+#define BUILD_INPUT_PORT(x) _BUILD_INPUT_PORT x
+LOCAL_FUNC ALWAYS_INLINE matrix_line_t build_matrix_line(port_width_t buffer[NUM_OF_INPUT_PORTS]);
+LOCAL_FUNC
+matrix_line_t build_matrix_line(port_width_t buffer[NUM_OF_INPUT_PORTS]) {
+ matrix_line_t result = 0;
+ MAP(BUILD_INPUT_PORT, MATRIX_IN_PINS);
+ return result;
+}
+
+#define _BUILD_INPUT_PORT_DIRECT(index, pname, bit) \
+ matrix[(inpin_index_##index)/MATRIX_COLS] \
+ |= (buffer[inport_index_##pname] & _BV(bit)) ? 0 : _BV((inpin_index_##index)%MATRIX_COLS);
+#define BUILD_INPUT_PORT_DIRECT(x) _BUILD_INPUT_PORT_DIRECT x
+LOCAL_FUNC ALWAYS_INLINE void build_matrix_direct(port_width_t buffer[NUM_OF_INPUT_PORTS], matrix_line_t matrix[]);
+LOCAL_FUNC
+void build_matrix_direct(port_width_t buffer[NUM_OF_INPUT_PORTS], matrix_line_t matrix[]) {
+ MAP(BUILD_INPUT_PORT_DIRECT, MATRIX_IN_PINS);
+}
diff --git a/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_extension_74hc15x.c b/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_extension_74hc15x.c
new file mode 100644
index 0000000000..bca53da24c
--- /dev/null
+++ b/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_extension_74hc15x.c
@@ -0,0 +1,72 @@
+/*
+Copyright 2021 mtei
+
+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/>.
+*/
+// clang-format off
+
+#if defined(MATRIX_EXTENSION_74HC157)
+# define MATRIX_DEVICES MCU_GPIOa, MCU_GPIOb
+# define IS_74HC15x(dev) ((dev)==MCU_GPIOa || (dev)==MCU_GPIOb)
+# define MATRIX_EXT_74HC15x MATRIX_EXTENSION_74HC157
+#elif defined(MATRIX_EXTENSION_74HC153)
+# define MATRIX_DEVICES MCU_GPIOa, MCU_GPIOb, MCU_GPIOc, MCU_GPIOd
+# define IS_74HC15x(dev) ((dev)==MCU_GPIOa || (dev)==MCU_GPIOb || (dev)==MCU_GPIOc || (dev)==MCU_GPIOd)
+# define MATRIX_EXT_74HC15x MATRIX_EXTENSION_74HC153
+#endif
+
+static const pin_t sel_pins[] = { MATRIX_EXT_74HC15x };
+
+#ifdef MATRIX_GPIO_NEED_SEPARATE_ATOMIC
+# define setMatrixInputHigh(dev, port, bit) \
+ do { \
+ if ((dev) == MCU_GPIO || IS_74HC15x(dev)) { \
+ setPortBitInputHigh_atomic(port, bit); \
+ }
+ } while(0)
+#else
+# define setMatrixInputHigh(dev, port, bit) \
+ do { \
+ if ((dev) == MCU_GPIO || IS_74HC15x(dev)) { \
+ setPortBitInputHigh(port, bit); \
+ } \
+ } while(0)
+#endif
+
+LOCAL_FUNC ALWAYS_INLINE void select74HC15x(uint8_t devid);
+LOCAL_FUNC
+void select74HC15x(uint8_t devid) {
+ writePin(sel_pins[0], devid&1);
+#if defined(MATRIX_EXTENSION_74HC153)
+ writePin(sel_pins[1], devid&2);
+#endif
+}
+
+LOCAL_FUNC ALWAYS_INLINE port_width_t readPortMultiplexer(uint8_t devid, pin_t port);
+LOCAL_FUNC port_width_t readPortMultiplexer(uint8_t devid, pin_t port) {
+ select74HC15x(devid);
+ waitInputPinDelay();
+ return readPort(port);
+}
+
+#define readMatrixPort(dev, port) \
+ ((dev) == MCU_GPIO)? readPort(port): (IS_74HC15x(dev))? readPortMultiplexer((dev)-MCU_GPIOa, port):0
+
+#define INIT_74HC15X(x) setPinOutput(x); writePinLow(x);
+LOCAL_FUNC
+void init_74hc15x(void) {
+ MAP(INIT_74HC15X, MATRIX_EXT_74HC15x)
+}
+#define init_extension() init_74hc15x()
+
diff --git a/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_extr.h b/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_extr.h
new file mode 100644
index 0000000000..e0cf528919
--- /dev/null
+++ b/keyboards/handwired/symmetric70_proto/matrix_fast/matrix_extr.h
@@ -0,0 +1,36 @@
+#pragma once
+// clang-format off
+
+#define DIRECT_SWITCH 1
+#define DIODE_ROW2COL 2
+#define DIODE_COL2ROW 3
+
+#ifndef ROW2COL
+# define COL2ROW 0
+# define ROW2COL 1
+#endif
+
+#ifdef DIRECT_PINS
+# define MATRIX_TYPE DIRECT_SWITCH
+#else
+# if DIODE_DIRECTION == ROW2COL
+# define MATRIX_TYPE DIODE_ROW2COL
+# endif
+# if DIODE_DIRECTION == COL2ROW
+# define MATRIX_TYPE DIODE_COL2ROW
+# endif
+#endif
+
+#ifndef MATRIX_TYPE
+# error "MATRIX_TYPE could not be determined."
+#endif
+
+#if (MATRIX_ROWS <= 8)
+typedef uint8_t matrix_col_t;
+#elif (MATRIX_ROWS <= 16)
+typedef uint16_t matrix_col_t;
+#elif (MATRIX_ROWS <= 32)
+typedef uint32_t matrix_col_t;
+#else
+# error "MATRIX_ROWS: invalid value"
+#endif
diff --git a/keyboards/handwired/symmetric70_proto/matrix_fast/readme.md b/keyboards/handwired/symmetric70_proto/matrix_fast/readme.md
new file mode 100644
index 0000000000..dc744a4c46
--- /dev/null
+++ b/keyboards/handwired/symmetric70_proto/matrix_fast/readme.md
@@ -0,0 +1,168 @@
+# Fast and extensible matrix.c
+
+This matrix.c is faster and more extensible than the standard quantum/matrix.c.
+
+* The execution speed of the `matrix_scan()` function is several times faster than quantum/matrix.c.
+* In addition to handling MCU GPIOs, it can be extended to handle I/O extenders.
+
+## ToDo list
+- [x] support Pro Micro
+- [x] support Proton-C
+- [x] support DIRECT_PINS
+- [x] support DIODE_DIRECTION == ROW2COL
+- [x] support 74HC157: quadruple 2-line to 1-line data selectors / multiplexers
+- [x] support 74HC153: dual 4-line to 1-line data selectors / multiplexers
+- [ ] support I/O expander (MCP23018)
+- [ ] support MCU & I/O expander (MCP23018) mixture like ErgoDox
+
+## Configuration
+
+This matrix.c requires a different configuration than quantum/matrix.c.
+
+### Output pins configuration
+
+The output pins is the Row pins if `DIODE_DIRECTION == COL2ROW`, and the Col pins if `DIODE_DIRECTION == ROW2COL`. When DIRECT_PINS is defined, the output pins do not need to be set.
+
+Example:
+```c
+// list of OUTPUT(row) ports
+#define MATRIX_OUT_PORTS \
+ (Port_D, MCU_GPIO, D0), \
+ (Port_C, MCU_GPIO, C0), \
+ (Port_E, MCU_GPIO, E0), \
+ (Port_B, MCU_GPIO, B0)
+// list of OUTPUT pins
+#define MATRIX_OUT_PINS \
+ (0, Port_D, 4), \
+ (1, Port_C, 6), \
+ (2, Port_D, 7), \
+ (3, Port_E, 6), \
+ (4, Port_B, 4), \
+ (5, Port_B, 5)
+```
+
+### Input pins configuration
+
+The input pins is the Col pins if `DIODE_DIRECTION == COL2ROW`, and the Row pins if `DIODE_DIRECTION == ROW2COL`. When DIRECT_PINS is defined, the input pin settings will enumerate the connection pins of all switches.
+
+Example:
+```c
+// list of INPUT ports
+#define MATRIX_IN_PORTS (Port_F, MCU_GPIO, F0), (Port_B, MCU_GPIO, B0)
+// list of INPUT pins
+#define MATRIX_IN_PINS \
+ (0, Port_F, 4), \
+ (1, Port_F, 5), \
+ (2, Port_F, 6), \
+ (3, Port_F, 7), \
+ (4, Port_B, 1), \
+ (5, Port_B, 3)
+```
+
+### Multiplexer Extension
+
+By defining the `MATRIX_EXTENSION_74HC157` macro or `MATRIX_EXTENSION_74HC153` macro, you can connect a multiplexer to the GPIO to extend the input pins.
+
+Example:
+```c
+#define MATRIX_EXTENSION_74HC157 B2 /* or #define MATRIX_EXTENSION_74HC153 B2, B6 */
+
+// list of OUTPUT ports
+#define MATRIX_OUT_PORTS (Port_D, MCU_GPIO, D0), (Port_C, MCU_GPIO, C0), (Port_E, MCU_GPIO, E0), (Port_B, MCU_GPIO, B0)
+// list of OUTPUT pins
+#define MATRIX_OUT_PINS (0, Port_D, 4), (1, Port_C, 6), (2, Port_D, 7), (3, Port_E, 6), (4, Port_B, 4), (5, Port_B, 5)
+
+// list of INPUT ports
+#define MATRIX_IN_PORTS \
+ (Port_Fa, MCU_GPIOa, F0), \
+ (Port_Ba, MCU_GPIOa, B0), \
+ (Port_Fb, MCU_GPIOb, F0), \
+ (Port_Bb, MCU_GPIOb, B0)
+// list of INPUT pins
+#define MATRIX_IN_PINS \
+ (0, Port_Fa, 4), \
+ (1, Port_Fb, 5), \
+ (2, Port_Fb, 6), \
+ (3, Port_Fa, 7), \
+ (4, Port_Ba, 1), \
+ (5, Port_Bb, 3)
+```
+
+### I/O expander Extension
+
+I plan to provide extensions to support I/O expanders such as MCP23018 and PCA9555.
+
+## Compile
+
+* Measure the execution time of matrix_scan()
+ * `make MTEST=matrix_debug_scan[,<other options>..] handwired/symmetric70_proto/promicro/fast:default:flash`
+* Measure delay time.
+ * `make MTEST=matrix_debug_delay[,<other options>..] handwired/symmetric70_proto/promicro/fast:default:flash`
+
+## Measurement result
+### Pro Micro (ATmega32u4 16Mhz)
+#### Default setting (show `matrix_scan()` time)
+ - `make MTEST=matrix_debug_scan handwired/symmetric70_proto/promicro/fast:default:flash`
+ - CH1: Row 0
+ - CH2: Row 1
+ - CH3: Row 4
+ - CH4: matrix_scan()
+ - Execution time of matrix_scan() 75.6us
+ - Frequency of matrix scan 8.09kHz (123.6us)
+ ![DS1Z_QuickPrint7](https://user-images.githubusercontent.com/2170248/116003927-538d9100-a63b-11eb-9b36-7db47d9b1541.png)
+
+#### Default setting (show delay time)
+ - `make MTEST=matrix_debug_delay handwired/symmetric70_proto/promicro/fast:default:flash`
+##### Press R0C0 key
+ - CH1: Row 0
+ - CH2: Row 1
+ - CH3: Row 4
+ - CH4: delay time
+ - Frequency of matrix scan 7.84kHz (127.6us)
+
+![DS1Z_QuickPrint9](https://user-images.githubusercontent.com/2170248/116003974-99e2f000-a63b-11eb-9c9e-3b3b1025db66.png)
+![DS1Z_QuickPrint10](https://user-images.githubusercontent.com/2170248/116003978-a1a29480-a63b-11eb-97d8-5a6e11c0db2f.png)
+
+### Proton C
+#### Default setting (show `matrix_scan()` time)
+ - `make MTEST=matrix_debug_scan handwired/symmetric70_proto/proton_c/fast:default:flash`
+ - CH1: Row 0
+ - CH2: Row 1
+ - CH3: Row 4
+ - CH4: matrix_scan()
+ - Execution time of matrix_scan() 49.8us
+ - Frequency of matrix scan 15.1kHz (66.2.6us)
+
+![DS1Z_QuickPrint11](https://user-images.githubusercontent.com/2170248/116088141-8cca0d80-a6dc-11eb-8782-1d29c57690b8.png)
+
+#### Default setting (show delay time)
+ - `make MTEST=matrix_debug_delay handwired/symmetric70_proto/proton_c/fast:default:flash`
+##### Press R0C0 key
+ - CH1: Row 0
+ - CH2: Row 1
+ - CH3: Row 4
+ - CH4: delay time
+ - Frequency of matrix scan 13.9kHz (71.8us)
+
+![DS1Z_QuickPrint12](https://user-images.githubusercontent.com/2170248/116088247-a8cdaf00-a6dc-11eb-8a47-104694a40117.png)
+![DS1Z_QuickPrint13](https://use