qmk_firmware

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

matrix.c (5823B)


      1 // Copyright 2022 QMK
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 
      4 #include "gpio.h"
      5 #include "matrix.h"
      6 #include "mcp23018.h"
      7 #include "util.h"
      8 #include "wait.h"
      9 #include "debug.h"
     10 
     11 #define I2C_ADDR 0x20
     12 
     13 static uint8_t mcp23018_errors = 0;
     14 
     15 static void expander_init(void) {
     16     mcp23018_init(I2C_ADDR);
     17 }
     18 
     19 static void expander_init_cols(void) {
     20     mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTA, ALL_INPUT);
     21     mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ALL_INPUT);
     22 }
     23 
     24 static void expander_select_row(uint8_t row) {
     25     if (mcp23018_errors) {
     26         // wait to mimic i2c interactions
     27         wait_us(100);
     28         return;
     29     }
     30 
     31     mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ~(1 << (row + 1)));
     32 }
     33 
     34 static void expander_unselect_row(uint8_t row) {
     35     // No need to unselect row as the next `select_row` will blank everything anyway
     36 }
     37 
     38 static void expander_unselect_rows(void) {
     39     if (mcp23018_errors) {
     40         return;
     41     }
     42 
     43     mcp23018_errors += !mcp23018_set_config(I2C_ADDR, mcp23018_PORTB, ALL_INPUT);
     44 }
     45 
     46 static matrix_row_t expander_read_row(void) {
     47     if (mcp23018_errors) {
     48         return 0;
     49     }
     50 
     51     uint8_t ret = 0xFF;
     52     mcp23018_errors += !mcp23018_read_pins(I2C_ADDR, mcp23018_PORTA, &ret);
     53 
     54     ret = bitrev(~ret);
     55     ret = ((ret & 0b11111000) >> 1) | (ret & 0b00000011);
     56 
     57     return ((uint16_t)ret) << 7;
     58 }
     59 
     60 static void expander_scan(void) {
     61     if (!mcp23018_errors) {
     62         return;
     63     }
     64 
     65     static uint16_t mcp23018_reset_loop = 0;
     66     if (++mcp23018_reset_loop > 0x1FFF) {
     67         // tuned to about 5s given the current scan rate
     68         dprintf("trying to reset mcp23018\n");
     69         mcp23018_reset_loop = 0;
     70         mcp23018_errors     = 0;
     71         expander_unselect_rows();
     72         expander_init_cols();
     73     }
     74 }
     75 
     76 /* Column pin configuration
     77  *
     78  * Pro Micro: 6    5    4    3    2    1    0
     79  *            PD3  PD2  PD4  PC6  PD7  PE6  PB4
     80  *
     81  * Expander:  13   12   11   10   9    8    7
     82  */
     83 static void init_cols(void) {
     84     // Pro Micro
     85     gpio_set_pin_input_high(E6);
     86     gpio_set_pin_input_high(D2);
     87     gpio_set_pin_input_high(D3);
     88     gpio_set_pin_input_high(D4);
     89     gpio_set_pin_input_high(D7);
     90     gpio_set_pin_input_high(C6);
     91     gpio_set_pin_input_high(B4);
     92 
     93     // Expander
     94     expander_init_cols();
     95 }
     96 
     97 static matrix_row_t read_cols(void) {
     98     // clang-format off
     99     return expander_read_row() |
    100         (gpio_read_pin(D3) ? 0 : (1<<6)) |
    101         (gpio_read_pin(D2) ? 0 : (1<<5)) |
    102         (gpio_read_pin(D4) ? 0 : (1<<4)) |
    103         (gpio_read_pin(C6) ? 0 : (1<<3)) |
    104         (gpio_read_pin(D7) ? 0 : (1<<2)) |
    105         (gpio_read_pin(E6) ? 0 : (1<<1)) |
    106         (gpio_read_pin(B4) ? 0 : (1<<0)) ;
    107     // clang-format on
    108 }
    109 
    110 /* Row pin configuration
    111  *
    112  * Pro Micro: 0   1   2   3   4   5
    113  *            F4  F5  F6  F7  B1  B2
    114  *
    115  * Expander:  0   1   2   3   4   5
    116  */
    117 static void unselect_rows(void) {
    118     // Pro Micro
    119     gpio_set_pin_input(B1);
    120     gpio_set_pin_input(B2);
    121     gpio_set_pin_input(F4);
    122     gpio_set_pin_input(F5);
    123     gpio_set_pin_input(F6);
    124     gpio_set_pin_input(F7);
    125     gpio_write_pin_low(B1);
    126     gpio_write_pin_low(B2);
    127     gpio_write_pin_low(F4);
    128     gpio_write_pin_low(F5);
    129     gpio_write_pin_low(F6);
    130     gpio_write_pin_low(F7);
    131 
    132     // Expander
    133     expander_unselect_rows();
    134 }
    135 
    136 static void unselect_row(uint8_t row) {
    137     // Pro Micro
    138     switch (row) {
    139         case 0:
    140             gpio_set_pin_input(F4);
    141             gpio_write_pin_low(F4);
    142             break;
    143         case 1:
    144             gpio_set_pin_input(F5);
    145             gpio_write_pin_low(F5);
    146             break;
    147         case 2:
    148             gpio_set_pin_input(F6);
    149             gpio_write_pin_low(F6);
    150             break;
    151         case 3:
    152             gpio_set_pin_input(F7);
    153             gpio_write_pin_low(F7);
    154             break;
    155         case 4:
    156             gpio_set_pin_input(B1);
    157             gpio_write_pin_low(B1);
    158             break;
    159         case 5:
    160             gpio_set_pin_input(B2);
    161             gpio_write_pin_low(B2);
    162             break;
    163     }
    164 
    165     // Expander
    166     expander_unselect_row(row);
    167 }
    168 
    169 static void select_row(uint8_t row) {
    170     // Pro Micro
    171     switch (row) {
    172         case 0:
    173             gpio_set_pin_output(F4);
    174             gpio_write_pin_low(F4);
    175             break;
    176         case 1:
    177             gpio_set_pin_output(F5);
    178             gpio_write_pin_low(F5);
    179             break;
    180         case 2:
    181             gpio_set_pin_output(F6);
    182             gpio_write_pin_low(F6);
    183             break;
    184         case 3:
    185             gpio_set_pin_output(F7);
    186             gpio_write_pin_low(F7);
    187             break;
    188         case 4:
    189             gpio_set_pin_output(B1);
    190             gpio_write_pin_low(B1);
    191             break;
    192         case 5:
    193             gpio_set_pin_output(B2);
    194             gpio_write_pin_low(B2);
    195             break;
    196     }
    197 
    198     // Expander
    199     expander_select_row(row);
    200 }
    201 
    202 static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {
    203     // Store last value of row prior to reading
    204     matrix_row_t last_row_value = current_matrix[current_row];
    205 
    206     // Clear data in matrix row
    207     current_matrix[current_row] = 0;
    208 
    209     // Select row and wait for row selection to stabilize
    210     select_row(current_row);
    211     // Skip the wait_us(30); as i2c is slow enough to debounce the io changes
    212 
    213     current_matrix[current_row] = read_cols();
    214 
    215     unselect_row(current_row);
    216 
    217     return (last_row_value != current_matrix[current_row]);
    218 }
    219 
    220 void matrix_init_custom(void) {
    221     expander_init();
    222 
    223     unselect_rows();
    224     init_cols();
    225 }
    226 
    227 bool matrix_scan_custom(matrix_row_t current_matrix[]) {
    228     expander_scan();
    229 
    230     bool changed = false;
    231     for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
    232         changed |= read_cols_on_row(current_matrix, current_row);
    233     }
    234     return changed;
    235 }