matrix.c (5823B)
1 // Copyright 2022 QMK 2 // SPDX-License-Identifier: GPL-2.0-or-later 3 4 #include "gpio.h" 5 #include "matrix.h" 6 #include "mcp23018.h" 7 #include "util.h" 8 #include "wait.h" 9 #include "debug.h" 10 11 #define I2C_ADDR 0x20 12 13 static uint8_t mcp23018_errors = 0; 14 15 static void expander_init(void) { 16 mcp23018_init(I2C_ADDR); 17 } 18 19 static void expander_init_cols(void) { 20 mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTA, ALL_INPUT); 21 mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ALL_INPUT); 22 } 23 24 static void expander_select_row(uint8_t row) { 25 if (mcp23018_errors) { 26 // wait to mimic i2c interactions 27 wait_us(100); 28 return; 29 } 30 31 mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ~(1 << (row + 1))); 32 } 33 34 static void expander_unselect_row(uint8_t row) { 35 // No need to unselect row as the next `select_row` will blank everything anyway 36 } 37 38 static void expander_unselect_rows(void) { 39 if (mcp23018_errors) { 40 return; 41 } 42 43 mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ALL_INPUT); 44 } 45 46 static matrix_row_t expander_read_row(void) { 47 if (mcp23018_errors) { 48 return 0; 49 } 50 51 uint8_t ret = 0xFF; 52 mcp23018_errors += !mcp23018_read_pins(I2C_ADDR, mcp23018_PORTA, &ret); 53 54 ret = bitrev(~ret); 55 ret = ((ret & 0b11111000) >> 1) | (ret & 0b00000011); 56 57 return ((uint16_t)ret) << 7; 58 } 59 60 static void expander_scan(void) { 61 if (!mcp23018_errors) { 62 return; 63 } 64 65 static uint16_t mcp23018_reset_loop = 0; 66 if (++mcp23018_reset_loop > 0x1FFF) { 67 // tuned to about 5s given the current scan rate 68 dprintf("trying to reset mcp23018\n"); 69 mcp23018_reset_loop = 0; 70 mcp23018_errors = 0; 71 expander_unselect_rows(); 72 expander_init_cols(); 73 } 74 } 75 76 /* Column pin configuration 77 * 78 * Pro Micro: 6 5 4 3 2 1 0 79 * PD3 PD2 PD4 PC6 PD7 PE6 PB4 80 * 81 * Expander: 13 12 11 10 9 8 7 82 */ 83 static void init_cols(void) { 84 // Pro Micro 85 gpio_set_pin_input_high(E6); 86 gpio_set_pin_input_high(D2); 87 gpio_set_pin_input_high(D3); 88 gpio_set_pin_input_high(D4); 89 gpio_set_pin_input_high(D7); 90 gpio_set_pin_input_high(C6); 91 gpio_set_pin_input_high(B4); 92 93 // Expander 94 expander_init_cols(); 95 } 96 97 static matrix_row_t read_cols(void) { 98 // clang-format off 99 return expander_read_row() | 100 (gpio_read_pin(D3) ? 0 : (1<<6)) | 101 (gpio_read_pin(D2) ? 0 : (1<<5)) | 102 (gpio_read_pin(D4) ? 0 : (1<<4)) | 103 (gpio_read_pin(C6) ? 0 : (1<<3)) | 104 (gpio_read_pin(D7) ? 0 : (1<<2)) | 105 (gpio_read_pin(E6) ? 0 : (1<<1)) | 106 (gpio_read_pin(B4) ? 0 : (1<<0)) ; 107 // clang-format on 108 } 109 110 /* Row pin configuration 111 * 112 * Pro Micro: 0 1 2 3 4 5 113 * F4 F5 F6 F7 B1 B2 114 * 115 * Expander: 0 1 2 3 4 5 116 */ 117 static void unselect_rows(void) { 118 // Pro Micro 119 gpio_set_pin_input(B1); 120 gpio_set_pin_input(B2); 121 gpio_set_pin_input(F4); 122 gpio_set_pin_input(F5); 123 gpio_set_pin_input(F6); 124 gpio_set_pin_input(F7); 125 gpio_write_pin_low(B1); 126 gpio_write_pin_low(B2); 127 gpio_write_pin_low(F4); 128 gpio_write_pin_low(F5); 129 gpio_write_pin_low(F6); 130 gpio_write_pin_low(F7); 131 132 // Expander 133 expander_unselect_rows(); 134 } 135 136 static void unselect_row(uint8_t row) { 137 // Pro Micro 138 switch (row) { 139 case 0: 140 gpio_set_pin_input(F4); 141 gpio_write_pin_low(F4); 142 break; 143 case 1: 144 gpio_set_pin_input(F5); 145 gpio_write_pin_low(F5); 146 break; 147 case 2: 148 gpio_set_pin_input(F6); 149 gpio_write_pin_low(F6); 150 break; 151 case 3: 152 gpio_set_pin_input(F7); 153 gpio_write_pin_low(F7); 154 break; 155 case 4: 156 gpio_set_pin_input(B1); 157 gpio_write_pin_low(B1); 158 break; 159 case 5: 160 gpio_set_pin_input(B2); 161 gpio_write_pin_low(B2); 162 break; 163 } 164 165 // Expander 166 expander_unselect_row(row); 167 } 168 169 static void select_row(uint8_t row) { 170 // Pro Micro 171 switch (row) { 172 case 0: 173 gpio_set_pin_output(F4); 174 gpio_write_pin_low(F4); 175 break; 176 case 1: 177 gpio_set_pin_output(F5); 178 gpio_write_pin_low(F5); 179 break; 180 case 2: 181 gpio_set_pin_output(F6); 182 gpio_write_pin_low(F6); 183 break; 184 case 3: 185 gpio_set_pin_output(F7); 186 gpio_write_pin_low(F7); 187 break; 188 case 4: 189 gpio_set_pin_output(B1); 190 gpio_write_pin_low(B1); 191 break; 192 case 5: 193 gpio_set_pin_output(B2); 194 gpio_write_pin_low(B2); 195 break; 196 } 197 198 // Expander 199 expander_select_row(row); 200 } 201 202 static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { 203 // Store last value of row prior to reading 204 matrix_row_t last_row_value = current_matrix[current_row]; 205 206 // Clear data in matrix row 207 current_matrix[current_row] = 0; 208 209 // Select row and wait for row selection to stabilize 210 select_row(current_row); 211 // Skip the wait_us(30); as i2c is slow enough to debounce the io changes 212 213 current_matrix[current_row] = read_cols(); 214 215 unselect_row(current_row); 216 217 return (last_row_value != current_matrix[current_row]); 218 } 219 220 void matrix_init_custom(void) { 221 expander_init(); 222 223 unselect_rows(); 224 init_cols(); 225 } 226 227 bool matrix_scan_custom(matrix_row_t current_matrix[]) { 228 expander_scan(); 229 230 bool changed = false; 231 for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) { 232 changed |= read_cols_on_row(current_matrix, current_row); 233 } 234 return changed; 235 }