summaryrefslogtreecommitdiff
path: root/keyboards/sp111/matrix.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/sp111/matrix.c')
-rw-r--r--keyboards/sp111/matrix.c178
1 files changed, 178 insertions, 0 deletions
diff --git a/keyboards/sp111/matrix.c b/keyboards/sp111/matrix.c
new file mode 100644
index 0000000000..33b232dca7
--- /dev/null
+++ b/keyboards/sp111/matrix.c
@@ -0,0 +1,178 @@
+/* Copyright 2020 zvecr<git@zvecr.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "mcp23018.h"
+#include "quantum.h"
+
+// Optimize scanning code for speed as a slight mitigation for the port expander
+#pragma GCC push_options
+#pragma GCC optimize("-O3")
+
+#define I2C_ADDR 0x20
+
+static uint16_t mcp23018_reset_loop = 0;
+static uint8_t mcp23018_errors = 0;
+
+static const pin_t row_pins[MATRIX_ROWS / 2] = {B1, D5, D4, D6, D7, B4};
+static const pin_t col_pins[MATRIX_COLS] = {F5, F6, F7, C7, C6, B6, B5, D3, D2, B3, B2};
+
+//_____REGULAR funcs____________________________________________________________
+
+static void select_row(uint8_t row) {
+ setPinOutput(row_pins[row]);
+ writePinLow(row_pins[row]);
+}
+
+static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); }
+
+static void unselect_rows(void) {
+ for (uint8_t x = 0; x < MATRIX_ROWS / 2; x++) {
+ setPinInputHigh(row_pins[x]);
+ }
+}
+
+static void init_pins(void) {
+ unselect_rows();
+ for (uint8_t x = 0; x < MATRIX_COLS; x++) {
+ setPinInputHigh(col_pins[x]);
+ }
+}
+
+static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
+ // Store last value of row prior to reading
+ matrix_row_t last_row_value = current_matrix[current_row];
+
+ // Clear data in matrix row
+ matrix_row_t current_row_value = 0;
+
+ // Select row and wait for row selection to stabilize
+ select_row(current_row);
+ wait_us(5);
+
+ // For each col...
+ for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
+ // Select the col pin to read (active low)
+ uint8_t pin_state = readPin(col_pins[col_index]);
+
+ // Populate the matrix row with the state of the col pin
+ current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index);
+ }
+
+ // Unselect row
+ unselect_row(current_row);
+
+ if (last_row_value == current_row_value) {
+ return false;
+ }
+
+ current_matrix[current_row] = current_row_value;
+ return true;
+}
+
+//_____MCP23018 funcs___________________________________________________________
+
+static void init_pins_MCP23018(void) {
+ mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTA, 0b11111111);
+ mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, 0b01100000);
+}
+
+static void select_row_MCP23018(uint8_t row) {
+ uint8_t mask = 0;
+
+ switch (row) {
+ case 6:
+ mask = 0b10000000;
+ break;
+ case 7:
+ mask = 0b00000001;
+ break;
+ case 8:
+ mask = 0b00000010;
+ break;
+ case 9:
+ mask = 0b00000100;
+ break;
+ case 10:
+ mask = 0b00001000;
+ break;
+ case 11:
+ mask = 0b00010000;
+ break;
+ }
+
+ mcp23018_errors += !mcp23018_set_output(I2C_ADDR, mcp23018_PORTB, ~mask);
+}
+
+static uint16_t read_cols_MCP23018(void) {
+ uint16_t tmp = 0xFFFF;
+ mcp23018_errors += !mcp23018_readPins_all(I2C_ADDR, &tmp);
+
+ uint16_t state = ((tmp & 0b11111111) << 2) | ((tmp & 0b0110000000000000) >> 13);
+ return (~state) & 0b1111111111;
+}
+
+static bool read_cols_on_row_MCP23018(matrix_row_t current_matrix[], uint8_t current_row) {
+ // Store last value of row prior to reading
+ matrix_row_t last_row_value = current_matrix[current_row];
+
+ // No need to Clear data in matrix row as we just replace in one go
+
+ // Select row and wait for row selection to stabilize
+ select_row_MCP23018(current_row);
+
+ matrix_row_t current_row_value = read_cols_MCP23018();
+
+ // No need to Unselect row as the next `select_row` will blank everything
+
+ if (last_row_value == current_row_value) {
+ return false;
+ }
+
+ current_matrix[current_row] = current_row_value;
+ return true;
+}
+
+//_____CUSTOM MATRIX IMPLEMENTATION____________________________________________________
+
+void matrix_init_custom(void) {
+ mcp23018_init(I2C_ADDR);
+
+ init_pins();
+ init_pins_MCP23018();
+}
+
+bool matrix_scan_custom(matrix_row_t current_matrix[]) {
+ bool changed = false;
+ for (uint8_t current_row = 0; current_row < MATRIX_ROWS / 2; current_row++) {
+ changed |= read_cols_on_row(current_matrix, current_row);
+ }
+
+ if (mcp23018_errors) {
+ if (++mcp23018_reset_loop > 0x7FFF) {
+ // tuned to about 5s given the current scan rate
+ print("trying to reset mcp23018\n");
+ mcp23018_reset_loop = 0;
+ mcp23018_errors = 0;
+ init_pins_MCP23018();
+ }
+ return changed;
+ }
+
+ for (uint8_t current_row = MATRIX_ROWS / 2; current_row < MATRIX_ROWS; current_row++) {
+ changed |= read_cols_on_row_MCP23018(current_matrix, current_row);
+ }
+ return changed;
+}
+#pragma GCC pop_options