matrix.c (6441B)
1 /* Copyright 2021 @ Keychron (https://www.keychron.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 2 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include "matrix.h" 18 #include "atomic_util.h" 19 #include <string.h> 20 21 // Pin connected to DS of 74HC595 22 #define DATA_PIN C15 23 // Pin connected to SH_CP of 74HC595 24 #define CLOCK_PIN A1 25 // Pin connected to ST_CP of 74HC595 26 #define LATCH_PIN A0 27 28 #ifdef MATRIX_ROW_PINS 29 static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; 30 #endif // MATRIX_ROW_PINS 31 #ifdef MATRIX_COL_PINS 32 static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; 33 #endif // MATRIX_COL_PINS 34 35 #define ROWS_PER_HAND (MATRIX_ROWS) 36 37 static inline void gpio_atomic_write_pin_low(pin_t pin) { 38 ATOMIC_BLOCK_FORCEON { 39 gpio_write_pin_low(pin); 40 } 41 } 42 43 static inline void gpio_atomic_write_pin_high(pin_t pin) { 44 ATOMIC_BLOCK_FORCEON { 45 gpio_write_pin_high(pin); 46 } 47 } 48 49 static inline void gpio_atomic_set_pin_output_low(pin_t pin) { 50 ATOMIC_BLOCK_FORCEON { 51 gpio_set_pin_output(pin); 52 gpio_write_pin_low(pin); 53 } 54 } 55 56 static inline void gpio_atomic_set_pin_input_high(pin_t pin) { 57 ATOMIC_BLOCK_FORCEON { 58 gpio_set_pin_input_high(pin); 59 } 60 } 61 62 static inline uint8_t readMatrixPin(pin_t pin) { 63 if (pin != NO_PIN) { 64 return gpio_read_pin(pin); 65 } else { 66 return 1; 67 } 68 } 69 70 // At 3.6V input, three nops (37.5ns) should be enough for all signals 71 #define small_delay() __asm__ __volatile__("nop;nop;nop;\n\t" ::: "memory") 72 #define compiler_barrier() __asm__ __volatile__("" ::: "memory") 73 74 static void shiftOut(uint8_t dataOut) { 75 ATOMIC_BLOCK_FORCEON { 76 for (uint8_t i = 0; i < 8; i++) { 77 compiler_barrier(); 78 if (dataOut & 0x1) { 79 gpio_write_pin_high(DATA_PIN); 80 } else { 81 gpio_write_pin_low(DATA_PIN); 82 } 83 dataOut = dataOut >> 1; 84 compiler_barrier(); 85 gpio_write_pin_high(CLOCK_PIN); 86 small_delay(); 87 gpio_write_pin_low(CLOCK_PIN); 88 } 89 compiler_barrier(); 90 gpio_write_pin_high(LATCH_PIN); 91 small_delay(); 92 gpio_write_pin_low(LATCH_PIN); 93 compiler_barrier(); 94 } 95 } 96 97 static void shiftout_single(uint8_t data) { 98 ATOMIC_BLOCK_FORCEON { 99 compiler_barrier(); 100 if (data & 0x1) { 101 gpio_write_pin_high(DATA_PIN); 102 } else { 103 gpio_write_pin_low(DATA_PIN); 104 } 105 compiler_barrier(); 106 gpio_write_pin_high(CLOCK_PIN); 107 small_delay(); 108 gpio_write_pin_low(CLOCK_PIN); 109 compiler_barrier(); 110 gpio_write_pin_high(LATCH_PIN); 111 small_delay(); 112 gpio_write_pin_low(LATCH_PIN); 113 compiler_barrier(); 114 } 115 } 116 117 static bool select_col(uint8_t col) { 118 pin_t pin = col_pins[col]; 119 120 if (pin != NO_PIN) { 121 #ifdef MATRIX_UNSELECT_DRIVE_HIGH 122 gpio_atomic_write_pin_low(pin); 123 #else 124 gpio_atomic_set_pin_output_low(pin); 125 #endif 126 return true; 127 } else { 128 if (col == 0) { 129 shiftout_single(0x00); 130 } 131 return true; 132 } 133 return false; 134 } 135 136 static void unselect_col(uint8_t col) { 137 pin_t pin = col_pins[col]; 138 139 if (pin != NO_PIN) { 140 #ifdef MATRIX_UNSELECT_DRIVE_HIGH 141 gpio_atomic_write_pin_high(pin); 142 #else 143 gpio_atomic_set_pin_input_high(pin); 144 #endif 145 } else { 146 shiftout_single(0x01); 147 } 148 } 149 150 static void unselect_cols(void) { 151 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 152 pin_t pin = col_pins[x]; 153 if (pin != NO_PIN) { 154 #ifdef MATRIX_UNSELECT_DRIVE_HIGH 155 gpio_atomic_set_pin_output_high(pin); 156 #else 157 gpio_atomic_set_pin_input_high(pin); 158 #endif 159 } else { 160 if (x == 0) 161 // unselect shift Register 162 shiftOut(0xFF); 163 } 164 } 165 } 166 167 static void matrix_init_pins(void) { 168 gpio_set_pin_output(DATA_PIN); 169 gpio_set_pin_output(CLOCK_PIN); 170 gpio_set_pin_output(LATCH_PIN); 171 #ifdef MATRIX_UNSELECT_DRIVE_HIGH 172 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 173 if (col_pins[x] != NO_PIN) { 174 gpio_set_pin_output(col_pins[x]); 175 } 176 } 177 #endif 178 unselect_cols(); 179 for (uint8_t x = 0; x < MATRIX_ROWS; x++) { 180 if (row_pins[x] != NO_PIN) { 181 gpio_atomic_set_pin_input_high(row_pins[x]); 182 } 183 } 184 } 185 186 static void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter) { 187 bool key_pressed = false; 188 189 // Select col 190 if (!select_col(current_col)) { // select col 191 return; // skip NO_PIN col 192 } 193 194 matrix_output_select_delay(); 195 196 // For each row... 197 for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { 198 // Check row pin state 199 if (readMatrixPin(row_pins[row_index]) == 0) { 200 // Pin LO, set col bit 201 current_matrix[row_index] |= row_shifter; 202 key_pressed = true; 203 } else { 204 // Pin HI, clear col bit 205 current_matrix[row_index] &= ~row_shifter; 206 } 207 } 208 209 // Unselect col 210 unselect_col(current_col); 211 matrix_output_unselect_delay(current_col, key_pressed); // wait for all Row signals to go HIGH 212 } 213 214 void matrix_init_custom(void) { 215 // initialize key pins 216 matrix_init_pins(); 217 } 218 219 bool matrix_scan_custom(matrix_row_t current_matrix[]) { 220 matrix_row_t curr_matrix[MATRIX_ROWS] = {0}; 221 222 // Set col, read rows 223 matrix_row_t row_shifter = MATRIX_ROW_SHIFTER; 224 for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) { 225 matrix_read_rows_on_col(curr_matrix, current_col, row_shifter); 226 } 227 228 bool changed = memcmp(current_matrix, curr_matrix, sizeof(curr_matrix)) != 0; 229 if (changed) memcpy(current_matrix, curr_matrix, sizeof(curr_matrix)); 230 231 return changed; 232 }