qmk_firmware

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

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