matrix.c (5468B)
1 /* Copyright 2020 zvecr<git@zvecr.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 #include "matrix.h" 17 #include "mcp23018.h" 18 #include "print.h" 19 #include "wait.h" 20 21 // Optimize scanning code for speed as a slight mitigation for the port expander 22 #pragma GCC push_options 23 #pragma GCC optimize("-O3") 24 25 #define I2C_ADDR 0x20 26 27 static uint16_t mcp23018_reset_loop = 0; 28 static uint8_t mcp23018_errors = 0; 29 30 static const pin_t row_pins[MATRIX_ROWS / 2] = {B1, D5, D4, D6, D7, B4}; 31 static const pin_t col_pins[MATRIX_COLS] = {F5, F6, F7, C7, C6, B6, B5, D3, D2, B3, B2}; 32 33 //_____REGULAR funcs____________________________________________________________ 34 35 static void select_row(uint8_t row) { 36 gpio_set_pin_output(row_pins[row]); 37 gpio_write_pin_low(row_pins[row]); 38 } 39 40 static void unselect_row(uint8_t row) { gpio_set_pin_input_high(row_pins[row]); } 41 42 static void unselect_rows(void) { 43 for (uint8_t x = 0; x < MATRIX_ROWS / 2; x++) { 44 gpio_set_pin_input_high(row_pins[x]); 45 } 46 } 47 48 static void init_pins(void) { 49 unselect_rows(); 50 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 51 gpio_set_pin_input_high(col_pins[x]); 52 } 53 } 54 55 static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { 56 // Store last value of row prior to reading 57 matrix_row_t last_row_value = current_matrix[current_row]; 58 59 // Clear data in matrix row 60 matrix_row_t current_row_value = 0; 61 62 // Select row and wait for row selection to stabilize 63 select_row(current_row); 64 wait_us(5); 65 66 // For each col... 67 for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { 68 // Select the col pin to read (active low) 69 uint8_t pin_state = gpio_read_pin(col_pins[col_index]); 70 71 // Populate the matrix row with the state of the col pin 72 current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index); 73 } 74 75 // Unselect row 76 unselect_row(current_row); 77 78 if (last_row_value == current_row_value) { 79 return false; 80 } 81 82 current_matrix[current_row] = current_row_value; 83 return true; 84 } 85 86 //_____MCP23018 funcs___________________________________________________________ 87 88 static void init_pins_MCP23018(void) { 89 mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTA, 0b11111111); 90 mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, 0b01100000); 91 } 92 93 static void select_row_MCP23018(uint8_t row) { 94 uint8_t mask = 0; 95 96 switch (row) { 97 case 6: 98 mask = 0b10000000; 99 break; 100 case 7: 101 mask = 0b00000001; 102 break; 103 case 8: 104 mask = 0b00000010; 105 break; 106 case 9: 107 mask = 0b00000100; 108 break; 109 case 10: 110 mask = 0b00001000; 111 break; 112 case 11: 113 mask = 0b00010000; 114 break; 115 } 116 117 mcp23018_errors += !mcp23018_set_output(I2C_ADDR, mcp23018_PORTB, ~mask); 118 } 119 120 static uint16_t read_cols_MCP23018(void) { 121 uint16_t tmp = 0xFFFF; 122 mcp23018_errors += !mcp23018_read_pins_all(I2C_ADDR, &tmp); 123 124 uint16_t state = ((tmp & 0b11111111) << 2) | ((tmp & 0b0110000000000000) >> 13); 125 return (~state) & 0b1111111111; 126 } 127 128 static bool read_cols_on_row_MCP23018(matrix_row_t current_matrix[], uint8_t current_row) { 129 // Store last value of row prior to reading 130 matrix_row_t last_row_value = current_matrix[current_row]; 131 132 // No need to Clear data in matrix row as we just replace in one go 133 134 // Select row and wait for row selection to stabilize 135 select_row_MCP23018(current_row); 136 137 matrix_row_t current_row_value = read_cols_MCP23018(); 138 139 // No need to Unselect row as the next `select_row` will blank everything 140 141 if (last_row_value == current_row_value) { 142 return false; 143 } 144 145 current_matrix[current_row] = current_row_value; 146 return true; 147 } 148 149 //_____CUSTOM MATRIX IMPLEMENTATION____________________________________________________ 150 151 void matrix_init_custom(void) { 152 mcp23018_init(I2C_ADDR); 153 154 init_pins(); 155 init_pins_MCP23018(); 156 } 157 158 bool matrix_scan_custom(matrix_row_t current_matrix[]) { 159 bool changed = false; 160 for (uint8_t current_row = 0; current_row < MATRIX_ROWS / 2; current_row++) { 161 changed |= read_cols_on_row(current_matrix, current_row); 162 } 163 164 if (mcp23018_errors) { 165 if (++mcp23018_reset_loop > 0x7FFF) { 166 // tuned to about 5s given the current scan rate 167 print("trying to reset mcp23018\n"); 168 mcp23018_reset_loop = 0; 169 mcp23018_errors = 0; 170 init_pins_MCP23018(); 171 } 172 return changed; 173 } 174 175 for (uint8_t current_row = MATRIX_ROWS / 2; current_row < MATRIX_ROWS; current_row++) { 176 changed |= read_cols_on_row_MCP23018(current_matrix, current_row); 177 } 178 return changed; 179 } 180 #pragma GCC pop_options