matrix.c (6016B)
1 /* Copyright 2022 @ 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 #ifndef PIN_USED_74HC595 22 # define PIN_USED_74HC595 8 23 #endif 24 #ifndef PIN_START_74HC595 25 # define PIN_START_74HC595 8 26 #endif 27 28 #ifdef MATRIX_ROW_PINS 29 static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; 30 #endif // MATRIX_ROW_PINS 31 #ifdef MATRIX_COL_PINS 32 static 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_set_pin_output_low(pin_t pin) { 38 ATOMIC_BLOCK_FORCEON { 39 gpio_set_pin_output(pin); 40 gpio_write_pin_low(pin); 41 } 42 } 43 44 static inline void gpio_atomic_set_pin_output_high(pin_t pin) { 45 ATOMIC_BLOCK_FORCEON { 46 gpio_set_pin_output(pin); 47 gpio_write_pin_high(pin); 48 } 49 } 50 51 static inline void gpio_atomic_set_pin_input_high(pin_t pin) { 52 ATOMIC_BLOCK_FORCEON { 53 gpio_set_pin_input_high(pin); 54 } 55 } 56 57 static inline uint8_t readMatrixPin(pin_t pin) { 58 if (pin != NO_PIN) { 59 return gpio_read_pin(pin); 60 } else { 61 return 1; 62 } 63 } 64 65 void small_delay(volatile uint8_t timeout) { 66 while (timeout--); 67 } 68 69 static void shiftOut(uint16_t dataOut) { 70 ATOMIC_BLOCK_FORCEON { 71 for (uint8_t i = 0; i < PIN_USED_74HC595; i++) { 72 if (dataOut & 0x1) { 73 gpio_write_pin_high(DATA_PIN_74HC595); 74 } else { 75 gpio_write_pin_low(DATA_PIN_74HC595); 76 } 77 dataOut = dataOut >> 1; 78 gpio_write_pin_high(CLOCK_PIN_74HC595); 79 small_delay(2); 80 gpio_write_pin_low(CLOCK_PIN_74HC595); 81 } 82 gpio_write_pin_high(LATCH_PIN_74HC595); 83 small_delay(2); 84 gpio_write_pin_low(LATCH_PIN_74HC595); 85 } 86 } 87 88 static void shiftOut_single(uint8_t data) { 89 ATOMIC_BLOCK_FORCEON { 90 if (data & 0x1) { 91 gpio_write_pin_high(DATA_PIN_74HC595); 92 } else { 93 gpio_write_pin_low(DATA_PIN_74HC595); 94 } 95 gpio_write_pin_high(CLOCK_PIN_74HC595); 96 small_delay(2); 97 gpio_write_pin_low(CLOCK_PIN_74HC595); 98 gpio_write_pin_high(LATCH_PIN_74HC595); 99 small_delay(2); 100 gpio_write_pin_low(LATCH_PIN_74HC595); 101 } 102 } 103 104 static bool select_col(uint8_t col) { 105 pin_t pin = col_pins[col]; 106 107 if (pin != NO_PIN) { 108 gpio_atomic_set_pin_output_low(pin); 109 return true; 110 } else { 111 if (col == PIN_START_74HC595) { 112 shiftOut_single(0x00); 113 } 114 return true; 115 } 116 return false; 117 } 118 119 static void unselect_col(uint8_t col) { 120 pin_t pin = col_pins[col]; 121 122 if (pin != NO_PIN) { 123 #ifdef MATRIX_UNSELECT_DRIVE_HIGH 124 gpio_atomic_set_pin_output_high(pin); 125 #else 126 gpio_atomic_set_pin_input_high(pin); 127 #endif 128 } else { 129 shiftOut_single(0x01); 130 } 131 } 132 133 static void unselect_cols(void) { 134 // unselect column pins 135 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 136 pin_t pin = col_pins[x]; 137 138 if (pin != NO_PIN) { 139 #ifdef MATRIX_UNSELECT_DRIVE_HIGH 140 gpio_atomic_set_pin_output_high(pin); 141 #else 142 gpio_atomic_set_pin_input_high(pin); 143 #endif 144 } 145 if (x == PIN_START_74HC595) 146 // unselect Shift Register 147 shiftOut(0xFFFF); 148 } 149 } 150 151 static void matrix_init_pins(void) { 152 gpio_set_pin_output(DATA_PIN_74HC595); 153 gpio_set_pin_output(CLOCK_PIN_74HC595); 154 gpio_set_pin_output(LATCH_PIN_74HC595); 155 #ifdef MATRIX_UNSELECT_DRIVE_HIGH 156 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 157 if (col_pins[x] != NO_PIN) { 158 gpio_set_pin_output(col_pins[x]); 159 } 160 } 161 #endif 162 unselect_cols(); 163 for (uint8_t x = 0; x < MATRIX_ROWS; x++) { 164 if (row_pins[x] != NO_PIN) { 165 gpio_atomic_set_pin_input_high(row_pins[x]); 166 } 167 } 168 } 169 170 static void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter) { 171 bool key_pressed = false; 172 173 // Select col 174 if (!select_col(current_col)) { // select col 175 return; // skip NO_PIN col 176 } 177 178 matrix_output_select_delay(); 179 180 // For each row... 181 for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { 182 // Check row pin state 183 if (readMatrixPin(row_pins[row_index]) == 0) { 184 // Pin LO, set col bit 185 current_matrix[row_index] |= row_shifter; 186 key_pressed = true; 187 } else { 188 // Pin HI, clear col bit 189 current_matrix[row_index] &= ~row_shifter; 190 } 191 } 192 193 // Unselect col 194 unselect_col(current_col); 195 matrix_output_unselect_delay(current_col, key_pressed); // wait for all Row signals to go HIGH 196 } 197 198 void matrix_init_custom(void) { 199 // initialize key pins 200 matrix_init_pins(); 201 } 202 203 bool matrix_scan_custom(matrix_row_t current_matrix[]) { 204 matrix_row_t curr_matrix[MATRIX_ROWS] = {0}; 205 206 // Set col, read rows 207 matrix_row_t row_shifter = MATRIX_ROW_SHIFTER; 208 for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) { 209 matrix_read_rows_on_col(curr_matrix, current_col, row_shifter); 210 } 211 212 bool changed = memcmp(current_matrix, curr_matrix, sizeof(curr_matrix)) != 0; 213 if (changed) memcpy(current_matrix, curr_matrix, sizeof(curr_matrix)); 214 215 return changed; 216 }