diff options
author | John Barbero <john@lsrkttn.com> | 2023-11-09 18:52:47 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-09 09:52:47 -0800 |
commit | 39d0a14258cbd1dd640405cdbc806dadb01521a8 (patch) | |
tree | 6fb7338b4873513e5a998cc63e14773d37228f30 /keyboards/snes_macropad/matrix.c | |
parent | daabe2d8c5eab9d9d605f8e079dfae82d2b06a8d (diff) |
Add SNES Macropad keyboard (#22377)
Co-authored-by: jack <0x6a73@protonmail.com>
Diffstat (limited to 'keyboards/snes_macropad/matrix.c')
-rw-r--r-- | keyboards/snes_macropad/matrix.c | 146 |
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; +} |