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