matrix.c (4138B)
1 // Copyright 2023 John Barbero Unenge (@jbarberu) 2 // SPDX-License-Identifier: GPL-2.0-or-later 3 4 #include "matrix.h" 5 #include "gpio.h" 6 #include "wait.h" 7 #include "string.h" 8 9 #define SNES_CLOCK GP0 10 #define SNES_LATCH GP1 11 #define SNES_D0 GP2 12 #define SNES_D1 GP3 13 #define SNES_IO GP4 14 15 #define KBD_ROW0 GP24 16 #define KBD_ROW1 GP23 17 #define KBD_ROW2 GP22 18 #define KBD_NUM_ROWS 3 19 20 #define KBD_COL0 GP18 21 #define KBD_COL1 GP19 22 #define KBD_COL2 GP20 23 #define KBD_COL3 GP21 24 #define KBD_ROW_SETUP_DELAY_US 5 25 26 // The real snes will clock 16 bits out of the controller, but only really has 12 bits of data 27 #define SNES_DATA_BITS 16 28 #define SNES_DATA_SETUP_DELAY_US 10 29 #define SNES_CLOCK_PULSE_DURATION 10 30 31 static const int kbd_pin_map[] = { 32 KBD_ROW0, 33 KBD_ROW1, 34 KBD_ROW2 35 }; 36 37 void matrix_init_custom(void) { 38 // init snes controller 39 gpio_set_pin_input_high(SNES_D0); 40 // todo: look into protocol for other strange snes controllers that use D1 and IO 41 // gpio_set_pin_input_high(SNES_D1); 42 // gpio_set_pin_input_high(SNES_IO); 43 gpio_set_pin_output(SNES_CLOCK); 44 gpio_set_pin_output(SNES_LATCH); 45 gpio_write_pin_low(SNES_CLOCK); 46 gpio_write_pin_low(SNES_LATCH); 47 48 // init rows 49 gpio_set_pin_output(KBD_ROW0); 50 gpio_set_pin_output(KBD_ROW1); 51 gpio_set_pin_output(KBD_ROW2); 52 gpio_write_pin_high(KBD_ROW0); 53 gpio_write_pin_high(KBD_ROW1); 54 gpio_write_pin_high(KBD_ROW2); 55 56 // init columns 57 gpio_set_pin_input_high(KBD_COL0); 58 gpio_set_pin_input_high(KBD_COL1); 59 gpio_set_pin_input_high(KBD_COL2); 60 gpio_set_pin_input_high(KBD_COL3); 61 } 62 63 static matrix_row_t readRow(size_t row, int setupDelay) { 64 const int pin = kbd_pin_map[row]; 65 66 // select the row 67 gpio_set_pin_output(pin); 68 gpio_write_pin_low(pin); 69 wait_us(setupDelay); 70 71 // read the column data 72 const matrix_row_t ret = 73 (gpio_read_pin(KBD_COL0) ? 0 : 1 << 0) 74 | (gpio_read_pin(KBD_COL1) ? 0 : 1 << 1) 75 | (gpio_read_pin(KBD_COL2) ? 0 : 1 << 2) 76 | (gpio_read_pin(KBD_COL3) ? 0 : 1 << 3); 77 78 // deselect the row 79 gpio_set_pin_output(pin); 80 gpio_write_pin_high(pin); 81 82 return ret; 83 } 84 85 static void readKeyboard(matrix_row_t current_matrix[]) { 86 for (size_t row = 0; row < KBD_NUM_ROWS; ++row) { 87 current_matrix[row] = readRow(row, KBD_ROW_SETUP_DELAY_US); 88 } 89 } 90 91 static matrix_row_t getBits(uint16_t value, size_t bit0, size_t bit1, size_t bit2, size_t bit3) { 92 matrix_row_t ret = 0; 93 ret |= (value >> bit3) & 1; 94 ret <<= 1; 95 ret |= (value >> bit2) & 1; 96 ret <<= 1; 97 ret |= (value >> bit1) & 1; 98 ret <<= 1; 99 ret |= (value >> bit0) & 1; 100 return ret; 101 } 102 103 static void readSnesController(matrix_row_t current_matrix[]) { 104 uint16_t controller = 0; 105 106 gpio_write_pin_high(SNES_LATCH); 107 108 for (size_t bit = 0; bit < SNES_DATA_BITS; ++bit) { 109 // Wait for shift register to setup the data line 110 wait_us(SNES_DATA_SETUP_DELAY_US); 111 112 // Shift accumulated data and read data pin 113 controller <<= 1; 114 controller |= gpio_read_pin(SNES_D0) ? 0 : 1; 115 // todo: maybe read D1 and IO here too 116 117 // Shift next bit in 118 gpio_write_pin_high(SNES_CLOCK); 119 wait_us(SNES_CLOCK_PULSE_DURATION); 120 gpio_write_pin_low(SNES_CLOCK); 121 } 122 123 gpio_write_pin_low(SNES_LATCH); 124 125 controller >>= 4; 126 127 // SNES button order is pretty random, and we'd like them to be a bit tidier 128 current_matrix[3] = getBits(controller, 1, 0, 8, 9); 129 current_matrix[4] = getBits(controller, 7, 6, 5, 4); 130 current_matrix[5] = getBits(controller, 3, 11, 2, 10); 131 } 132 133 bool matrix_scan_custom(matrix_row_t current_matrix[]) { 134 const size_t MATRIX_ARRAY_SIZE = MATRIX_ROWS * sizeof(matrix_row_t); 135 136 // create a copy of the current_matrix, before we read hardware state 137 matrix_row_t last_value[MATRIX_ROWS]; 138 memcpy(last_value, current_matrix, MATRIX_ARRAY_SIZE); 139 140 // read hardware state into current_matrix 141 readKeyboard(current_matrix); 142 readSnesController(current_matrix); 143 144 // check if anything changed 145 return memcmp(last_value, current_matrix, MATRIX_ARRAY_SIZE) != 0; 146 }