matrix.c (9195B)
1 /* 2 Copyright 2013 Oleg Kostyuk <cub.uanic@gmail.com> 3 2020 Pierre Chevalier <pierrechevalier83@gmail.com> 4 2021 weteor 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /* 21 * This code was heavily inspired by the ergodox_ez keymap, and modernized 22 * to take advantage of the quantum.h microcontroller agnostics gpio control 23 * abstractions and use the macros defined in config.h for the wiring as opposed 24 * to repeating that information all over the place. 25 */ 26 27 #include "matrix.h" 28 #include "debug.h" 29 #include "wait.h" 30 #include "i2c_master.h" 31 32 extern i2c_status_t tca9555_status; 33 #define I2C_TIMEOUT 1000 34 35 // I2C address: 36 // All address pins of the tca9555 are connected to the ground 37 // | 0 | 1 | 0 | 0 | A2 | A1 | A0 | 38 // | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 39 #define I2C_ADDR (0b0100000 << 1) 40 41 // Register addresses 42 #define IODIRA 0x06 // i/o direction register 43 #define IODIRB 0x07 44 #define IREGP0 0x00 // GPIO pull-up resistor register 45 #define IREGP1 0x01 46 #define OREGP0 0x02 // general purpose i/o port register (write modifies OLAT) 47 #define OREGP1 0x03 48 49 bool i2c_initialized = 0; 50 i2c_status_t tca9555_status = I2C_ADDR; 51 52 uint8_t init_tca9555(void) { 53 print("starting init"); 54 tca9555_status = I2C_ADDR; 55 56 // I2C subsystem 57 if (i2c_initialized == 0) { 58 i2c_init(); // on pins D(1,0) 59 i2c_initialized = true; 60 wait_ms(I2C_TIMEOUT); 61 } 62 63 // set pin direction 64 // - unused : input : 1 65 // - input : input : 1 66 // - driving : output : 0 67 uint8_t conf[2] = { 68 // This means: write on pin 5 of port 0, read on rest 69 0b11011111, 70 // This means: we will write on pins 0 to 2 on port 1. read rest 71 0b11111000, 72 }; 73 tca9555_status = i2c_write_register(I2C_ADDR, IODIRA, conf, 2, I2C_TIMEOUT); 74 75 return tca9555_status; 76 } 77 78 /* matrix state(1:on, 0:off) */ 79 static matrix_row_t matrix[MATRIX_ROWS]; // debounced values 80 81 static matrix_row_t read_cols(uint8_t row); 82 static void init_cols(void); 83 static void unselect_rows(void); 84 static void select_row(uint8_t row); 85 86 static uint8_t tca9555_reset_loop; 87 88 void matrix_init_custom(void) { 89 // initialize row and col 90 91 tca9555_status = init_tca9555(); 92 93 unselect_rows(); 94 init_cols(); 95 96 // initialize matrix state: all keys off 97 for (uint8_t i = 0; i < MATRIX_ROWS; i++) { 98 matrix[i] = 0; 99 } 100 } 101 102 void matrix_power_up(void) { 103 tca9555_status = init_tca9555(); 104 105 unselect_rows(); 106 init_cols(); 107 108 // initialize matrix state: all keys off 109 for (uint8_t i = 0; i < MATRIX_ROWS; i++) { 110 matrix[i] = 0; 111 } 112 } 113 114 // Reads and stores a row, returning 115 // whether a change occurred. 116 static inline bool store_matrix_row(matrix_row_t current_matrix[], uint8_t index) { 117 matrix_row_t temp = read_cols(index); 118 if (current_matrix[index] != temp) { 119 current_matrix[index] = temp; 120 return true; 121 } 122 return false; 123 } 124 125 bool matrix_scan_custom(matrix_row_t current_matrix[]) { 126 if (tca9555_status) { // if there was an error 127 if (++tca9555_reset_loop == 0) { 128 // since tca9555_reset_loop is 8 bit - we'll try to reset once in 255 matrix scans 129 // this will be approx bit more frequent than once per second 130 dprint("trying to reset tca9555\n"); 131 tca9555_status = init_tca9555(); 132 if (tca9555_status) { 133 dprint("right side not responding\n"); 134 } else { 135 dprint("right side attached\n"); 136 } 137 } 138 } 139 140 bool changed = false; 141 for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) { 142 // select rows from left and right hands 143 uint8_t left_index = i; 144 uint8_t right_index = i + MATRIX_ROWS_PER_SIDE; 145 select_row(left_index); 146 select_row(right_index); 147 148 // we don't need a 30us delay anymore, because selecting a 149 // left-hand row requires more than 30us for i2c. 150 151 changed |= store_matrix_row(current_matrix, left_index); 152 changed |= store_matrix_row(current_matrix, right_index); 153 154 unselect_rows(); 155 } 156 157 return changed; 158 } 159 160 static void init_cols(void) { 161 // init on tca9555 162 // not needed, already done as part of init_tca9555() 163 164 // init on mcu 165 pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_L; 166 for (int pin_index = 0; pin_index < MATRIX_COLS_PER_SIDE; pin_index++) { 167 pin_t pin = matrix_col_pins_mcu[pin_index]; 168 gpio_set_pin_input(pin); 169 gpio_write_pin_high(pin); 170 } 171 } 172 173 static matrix_row_t read_cols(uint8_t row) { 174 if (row < MATRIX_ROWS_PER_SIDE) { 175 pin_t matrix_col_pins_mcu[MATRIX_COLS_PER_SIDE] = MATRIX_COL_PINS_L; 176 matrix_row_t current_row_value = 0; 177 // For each col... 178 for (uint8_t col_index = 0; col_index < MATRIX_COLS_PER_SIDE; col_index++) { 179 // Select the col pin to read (active low) 180 uint8_t pin_state = gpio_read_pin(matrix_col_pins_mcu[col_index]); 181 182 // Populate the matrix row with the state of the col pin 183 current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); 184 } 185 return current_row_value; 186 } else { 187 if (tca9555_status) { // if there was an error 188 return 0; 189 } else { 190 uint8_t data = 0; 191 uint8_t ports[2] = {0}; 192 tca9555_status = i2c_read_register(I2C_ADDR, IREGP0, ports, 2, I2C_TIMEOUT); 193 if (tca9555_status) { // if there was an error 194 // do nothing 195 return 0; 196 } else { 197 uint8_t port0 = ports[0]; 198 uint8_t port1 = ports[1]; 199 200 // The initial state was all ones and any depressed key at a given column for the currently selected row will have its bit flipped to zero. 201 // The return value is a row as represented in the generic matrix code were the rightmost bits represent the lower columns and zeroes represent non-depressed keys while ones represent depressed keys. 202 // Since the pins are not ordered sequentially, we have to build the correct dataset from the two ports. Refer to the schematic to see where every pin is connected. 203 data |= ( port0 & 0x01 ); 204 data |= ( port0 & 0x02 ); 205 data |= ( port1 & 0x10 ) >> 2; 206 data |= ( port1 & 0x08 ); 207 data |= ( port0 & 0x40 ) >> 2; 208 data = ~(data); 209 210 tca9555_status = I2C_STATUS_SUCCESS; 211 return data; 212 } 213 } 214 } 215 } 216 217 static void unselect_rows(void) { 218 // no need to unselect on tca9555, because the select step sets all 219 // the other row bits high, and it's not changing to a different 220 // direction 221 222 // unselect rows on microcontroller 223 pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L; 224 for (int pin_index = 0; pin_index < MATRIX_ROWS_PER_SIDE; pin_index++) { 225 pin_t pin = matrix_row_pins_mcu[pin_index]; 226 gpio_set_pin_input(pin); 227 gpio_write_pin_low(pin); 228 } 229 } 230 231 static void select_row(uint8_t row) { 232 uint8_t port0 = 0xff; 233 uint8_t port1 = 0xff; 234 235 if (row < MATRIX_ROWS_PER_SIDE) { 236 // select on atmega32u4 237 pin_t matrix_row_pins_mcu[MATRIX_ROWS_PER_SIDE] = MATRIX_ROW_PINS_L; 238 pin_t pin = matrix_row_pins_mcu[row]; 239 gpio_set_pin_output(pin); 240 gpio_write_pin_low(pin); 241 } else { 242 // select on tca9555 243 if (tca9555_status) { // if there was an error 244 // do nothing 245 } else { 246 switch(row) { 247 case 4: port1 &= ~(1 << 0); break; 248 case 5: port1 &= ~(1 << 1); break; 249 case 6: port1 &= ~(1 << 2); break; 250 case 7: port0 &= ~(1 << 5); break; 251 default: break; 252 } 253 254 uint8_t ports[2] = {port0, port1}; 255 tca9555_status = i2c_write_register(I2C_ADDR, OREGP0, ports, 2, I2C_TIMEOUT); 256 // Select the desired row by writing a byte for the entire GPIOB bus where only the bit representing the row we want to select is a zero (write instruction) and every other bit is a one. 257 // Note that the row - MATRIX_ROWS_PER_SIDE reflects the fact that being on the right hand, the columns are numbered from MATRIX_ROWS_PER_SIDE to MATRIX_ROWS, but the pins we want to write to are indexed from zero up on the GPIOB bus. 258 } 259 } 260 }