summaryrefslogtreecommitdiff
path: root/keyboards/snes_macropad/matrix.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/snes_macropad/matrix.c')
-rw-r--r--keyboards/snes_macropad/matrix.c146
1 files changed, 146 insertions, 0 deletions
diff --git a/keyboards/snes_macropad/matrix.c b/keyboards/snes_macropad/matrix.c
new file mode 100644
index 0000000000..28d036aca9
--- /dev/null
+++ b/keyboards/snes_macropad/matrix.c
@@ -0,0 +1,146 @@
+// Copyright 2023 John Barbero Unenge (@jbarberu)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "matrix.h"
+#include "gpio.h"
+#include "wait.h"
+#include "string.h"
+
+#define SNES_CLOCK GP0
+#define SNES_LATCH GP1
+#define SNES_D0 GP2
+#define SNES_D1 GP3
+#define SNES_IO GP4
+
+#define KBD_ROW0 GP24
+#define KBD_ROW1 GP23
+#define KBD_ROW2 GP22
+#define KBD_NUM_ROWS 3
+
+#define KBD_COL0 GP18
+#define KBD_COL1 GP19
+#define KBD_COL2 GP20
+#define KBD_COL3 GP21
+#define KBD_ROW_SETUP_DELAY_US 5
+
+// The real snes will clock 16 bits out of the controller, but only really has 12 bits of data
+#define SNES_DATA_BITS 16
+#define SNES_DATA_SETUP_DELAY_US 10
+#define SNES_CLOCK_PULSE_DURATION 10
+
+static const int kbd_pin_map[] = {
+ KBD_ROW0,
+ KBD_ROW1,
+ KBD_ROW2
+};
+
+void matrix_init_custom(void) {
+ // init snes controller
+ setPinInputHigh(SNES_D0);
+ // todo: look into protocol for other strange snes controllers that use D1 and IO
+ // setPinInputHigh(SNES_D1);
+ // setPinInputHigh(SNES_IO);
+ setPinOutput(SNES_CLOCK);
+ setPinOutput(SNES_LATCH);
+ writePinLow(SNES_CLOCK);
+ writePinLow(SNES_LATCH);
+
+ // init rows
+ setPinOutput(KBD_ROW0);
+ setPinOutput(KBD_ROW1);
+ setPinOutput(KBD_ROW2);
+ writePinHigh(KBD_ROW0);
+ writePinHigh(KBD_ROW1);
+ writePinHigh(KBD_ROW2);
+
+ // init columns
+ setPinInputHigh(KBD_COL0);
+ setPinInputHigh(KBD_COL1);
+ setPinInputHigh(KBD_COL2);
+ setPinInputHigh(KBD_COL3);
+}
+
+static matrix_row_t readRow(size_t row, int setupDelay) {
+ const int pin = kbd_pin_map[row];
+
+ // select the row
+ setPinOutput(pin);
+ writePinLow(pin);
+ wait_us(setupDelay);
+
+ // read the column data
+ const matrix_row_t ret =
+ (readPin(KBD_COL0) ? 0 : 1 << 0)
+ | (readPin(KBD_COL1) ? 0 : 1 << 1)
+ | (readPin(KBD_COL2) ? 0 : 1 << 2)
+ | (readPin(KBD_COL3) ? 0 : 1 << 3);
+
+ // deselect the row
+ setPinOutput(pin);
+ writePinHigh(pin);
+
+ return ret;
+}
+
+static void readKeyboard(matrix_row_t current_matrix[]) {
+ for (size_t row = 0; row < KBD_NUM_ROWS; ++row) {
+ current_matrix[row] = readRow(row, KBD_ROW_SETUP_DELAY_US);
+ }
+}
+
+static matrix_row_t getBits(uint16_t value, size_t bit0, size_t bit1, size_t bit2, size_t bit3) {
+ matrix_row_t ret = 0;
+ ret |= (value >> bit3) & 1;
+ ret <<= 1;
+ ret |= (value >> bit2) & 1;
+ ret <<= 1;
+ ret |= (value >> bit1) & 1;
+ ret <<= 1;
+ ret |= (value >> bit0) & 1;
+ return ret;
+}
+
+static void readSnesController(matrix_row_t current_matrix[]) {
+ uint16_t controller = 0;
+
+ writePinHigh(SNES_LATCH);
+
+ for (size_t bit = 0; bit < SNES_DATA_BITS; ++bit) {
+ // Wait for shift register to setup the data line
+ wait_us(SNES_DATA_SETUP_DELAY_US);
+
+ // Shift accumulated data and read data pin
+ controller <<= 1;
+ controller |= readPin(SNES_D0) ? 0 : 1;
+ // todo: maybe read D1 and IO here too
+
+ // Shift next bit in
+ writePinHigh(SNES_CLOCK);
+ wait_us(SNES_CLOCK_PULSE_DURATION);
+ writePinLow(SNES_CLOCK);
+ }
+
+ writePinLow(SNES_LATCH);
+
+ controller >>= 4;
+
+ // SNES button order is pretty random, and we'd like them to be a bit tidier
+ current_matrix[3] = getBits(controller, 1, 0, 8, 9);
+ current_matrix[4] = getBits(controller, 7, 6, 5, 4);
+ current_matrix[5] = getBits(controller, 3, 11, 2, 10);
+}
+
+bool matrix_scan_custom(matrix_row_t current_matrix[]) {
+ const size_t MATRIX_ARRAY_SIZE = MATRIX_ROWS * sizeof(matrix_row_t);
+
+ // create a copy of the current_matrix, before we read hardware state
+ matrix_row_t last_value[MATRIX_ROWS];
+ memcpy(last_value, current_matrix, MATRIX_ARRAY_SIZE);
+
+ // read hardware state into current_matrix
+ readKeyboard(current_matrix);
+ readSnesController(current_matrix);
+
+ // check if anything changed
+ return memcmp(last_value, current_matrix, MATRIX_ARRAY_SIZE) != 0;
+}