qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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