matrix.c (6574B)
1 // Copyright 2023 ZSA Technology Labs, Inc <@zsa> 2 // Copyright 2023 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 #include <stdint.h> 6 #include "voyager.h" 7 #include "mcp23018.h" 8 9 #pragma GCC push_options 10 #pragma GCC optimize("-O3") 11 12 extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values 13 extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values 14 static matrix_row_t raw_matrix_right[MATRIX_COLS]; 15 16 #define MCP_ROWS_PER_HAND (MATRIX_ROWS / 2) 17 #ifndef VOYAGER_I2C_TIMEOUT 18 # define VOYAGER_I2C_TIMEOUT 100 19 #endif 20 // Delay between each i2c io expander ops (in MCU cycles) 21 #ifndef IO_EXPANDER_OP_DELAY 22 # define IO_EXPANDER_OP_DELAY 500 23 #endif 24 25 extern bool mcp23018_leds[2]; 26 extern bool is_launching; 27 28 static uint16_t mcp23018_reset_loop; 29 uint8_t mcp23018_errors; 30 31 bool io_expander_ready(void) { 32 uint8_t tx; 33 return mcp23018_readPins(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTA, &tx); 34 } 35 36 void matrix_init_custom(void) { 37 // outputs 38 gpio_set_pin_output(B10); 39 gpio_set_pin_output(B11); 40 gpio_set_pin_output(B12); 41 gpio_set_pin_output(B13); 42 gpio_set_pin_output(B14); 43 gpio_set_pin_output(B15); 44 45 // inputs 46 gpio_set_pin_input_low(A0); 47 gpio_set_pin_input_low(A1); 48 gpio_set_pin_input_low(A2); 49 gpio_set_pin_input_low(A3); 50 gpio_set_pin_input_low(A6); 51 gpio_set_pin_input_low(A7); 52 gpio_set_pin_input_low(B0); 53 54 mcp23018_init(MCP23018_DEFAULT_ADDRESS); 55 mcp23018_errors += !mcp23018_set_config(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTA, 0b00000000); 56 mcp23018_errors += !mcp23018_set_config(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTB, 0b00111111); 57 58 if (!mcp23018_errors) { 59 is_launching = true; 60 } 61 } 62 63 bool matrix_scan_custom(matrix_row_t current_matrix[]) { 64 bool changed = false; 65 // Attempt to reset the mcp23018 if it's not initialized 66 if (mcp23018_errors) { 67 if (++mcp23018_reset_loop > 0x1FFF) { 68 if (io_expander_ready()) { 69 // If we managed to initialize the mcp23018 - we need to reinitialize the matrix / layer state. During an electric discharge the i2c peripherals might be in a weird state. Giving a delay and resetting the MCU allows to recover from this. 70 wait_ms(200); 71 mcu_reset(); 72 } 73 } 74 } 75 76 // Scanning left and right side of the keyboard for key presses. 77 // Left side is scanned by reading the gpio pins directly, right side is scanned by reading the mcp23018 registers. 78 79 matrix_row_t data = 0; 80 for (uint8_t row = 0; row <= MCP_ROWS_PER_HAND; row++) { 81 // strobe row 82 switch (row) { 83 case 0: 84 gpio_write_pin_high(B10); 85 break; 86 case 1: 87 gpio_write_pin_high(B11); 88 break; 89 case 2: 90 gpio_write_pin_high(B12); 91 break; 92 case 3: 93 gpio_write_pin_high(B13); 94 break; 95 case 4: 96 gpio_write_pin_high(B14); 97 break; 98 case 5: 99 gpio_write_pin_high(B15); 100 break; 101 case 6: 102 break; // Left hand has 6 rows 103 } 104 105 // Selecting the row on the right side of the keyboard. 106 if (!mcp23018_errors) { 107 // select row 108 mcp23018_errors += !mcp23018_set_output(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTA, 0b01111111 & ~(1 << (row))); 109 mcp23018_errors += !mcp23018_set_output(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTB, ((uint8_t)!mcp23018_leds[1] << 6) | ((uint8_t)!mcp23018_leds[0] << 7)); 110 } 111 // Reading the left side of the keyboard. 112 if (row < MCP_ROWS_PER_HAND) { 113 // i2c comm incur enough wait time 114 if (mcp23018_errors) { 115 // need wait to settle pin state 116 matrix_io_delay(); 117 } 118 // read col data 119 data = ((gpio_read_pin(A0) << 0) | (gpio_read_pin(A1) << 1) | (gpio_read_pin(A2) << 2) | (gpio_read_pin(A3) << 3) | (gpio_read_pin(A6) << 4) | (gpio_read_pin(A7) << 5) | (gpio_read_pin(B0) << 6)); 120 // unstrobe row 121 switch (row) { 122 case 0: 123 gpio_write_pin_low(B10); 124 break; 125 case 1: 126 gpio_write_pin_low(B11); 127 break; 128 case 2: 129 gpio_write_pin_low(B12); 130 break; 131 case 3: 132 gpio_write_pin_low(B13); 133 break; 134 case 4: 135 gpio_write_pin_low(B14); 136 break; 137 case 5: 138 gpio_write_pin_low(B15); 139 break; 140 case 6: 141 break; 142 } 143 144 if (current_matrix[row] != data) { 145 current_matrix[row] = data; 146 changed = true; 147 } 148 } 149 150 // Reading the right side of the keyboard. 151 if (!mcp23018_errors) { 152 for (uint16_t i = 0; i < IO_EXPANDER_OP_DELAY; i++) { 153 __asm__("nop"); 154 } 155 uint8_t rx; 156 mcp23018_errors += !mcp23018_readPins(MCP23018_DEFAULT_ADDRESS, mcp23018_PORTB, &rx); 157 data = ~(rx & 0b00111111); 158 for (uint16_t i = 0; i < IO_EXPANDER_OP_DELAY; i++) { 159 __asm__("nop"); 160 } 161 } else { 162 data = 0; 163 } 164 165 if (raw_matrix_right[row] != data) { 166 raw_matrix_right[row] = data; 167 changed = true; 168 } 169 } 170 171 for (uint8_t row = 0; row < MCP_ROWS_PER_HAND; row++) { 172 current_matrix[11 - row] = 0; 173 for (uint8_t col = 0; col < MATRIX_COLS; col++) { 174 current_matrix[11 - row] |= ((raw_matrix_right[6 - col] & (1 << row) ? 1 : 0) << col); 175 } 176 } 177 return changed; 178 } 179 180 // DO NOT REMOVE 181 // Needed for proper wake/sleep 182 void matrix_power_up(void) { 183 bool temp_launching = is_launching; 184 185 matrix_init_custom(); 186 187 is_launching = temp_launching; 188 if (!temp_launching) { 189 STATUS_LED_1(false); 190 STATUS_LED_2(false); 191 STATUS_LED_3(false); 192 STATUS_LED_4(false); 193 } 194 195 // initialize matrix state: all keys off 196 for (uint8_t i = 0; i < MATRIX_ROWS; i++) { 197 matrix[i] = 0; 198 } 199 } 200 201 bool is_transport_connected(void) { 202 return (bool)(mcp23018_errors == 0); 203 } 204 #pragma GCC pop_options