qmk_firmware

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

ec_switch_matrix.c (13748B)


      1 /* Copyright 2023 Cipulot
      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 3 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 
     17 #include "ec_switch_matrix.h"
     18 #include "analog.h"
     19 #include "atomic_util.h"
     20 #include "math.h"
     21 #include "print.h"
     22 #include "wait.h"
     23 
     24 #if defined(__AVR__)
     25 #    error "AVR platforms not supported due to a variety of reasons. Among them there are limited memory, limited number of pins and ADC not being able to give satisfactory results."
     26 #endif
     27 
     28 #define OPEN_DRAIN_SUPPORT defined(PAL_MODE_OUTPUT_OPENDRAIN)
     29 
     30 eeprom_ec_config_t eeprom_ec_config;
     31 ec_config_t        ec_config;
     32 
     33 // Pin and port array
     34 const pin_t row_pins[]                                 = MATRIX_ROW_PINS;
     35 const pin_t amux_sel_pins[]                            = AMUX_SEL_PINS;
     36 const pin_t amux_en_pins[]                             = AMUX_EN_PINS;
     37 const pin_t amux_n_col_sizes[]                         = AMUX_COL_CHANNELS_SIZES;
     38 const pin_t amux_n_col_channels[][AMUX_MAX_COLS_COUNT] = {AMUX_COL_CHANNELS};
     39 
     40 #ifdef UNUSED_POSITIONS_LIST
     41 const uint8_t UNUSED_POSITIONS[][2] = UNUSED_POSITIONS_LIST;
     42 #    define UNUSED_POSITIONS_COUNT (sizeof(UNUSED_POSITIONS) / sizeof(UNUSED_POSITIONS[0]))
     43 #endif
     44 
     45 #define AMUX_SEL_PINS_COUNT ARRAY_SIZE(amux_sel_pins)
     46 #define EXPECTED_AMUX_SEL_PINS_COUNT ceil(log2(AMUX_MAX_COLS_COUNT)
     47 // Checks for the correctness of the configuration
     48 _Static_assert(ARRAY_SIZE(amux_en_pins) == AMUX_COUNT, "AMUX_EN_PINS doesn't have the minimum number of bits required to enable all the multiplexers available");
     49 // Check that number of select pins is enough to select all the channels
     50 _Static_assert(AMUX_SEL_PINS_COUNT == EXPECTED_AMUX_SEL_PINS_COUNT), "AMUX_SEL_PINS doesn't have the minimum number of bits required address all the channels");
     51 // Check that number of elements in AMUX_COL_CHANNELS_SIZES is enough to specify the number of channels for all the multiplexers available
     52 _Static_assert(ARRAY_SIZE(amux_n_col_sizes) == AMUX_COUNT, "AMUX_COL_CHANNELS_SIZES doesn't have the minimum number of elements required to specify the number of channels for all the multiplexers available");
     53 
     54 static uint16_t sw_value[MATRIX_ROWS][MATRIX_COLS];
     55 
     56 static adc_mux adcMux;
     57 
     58 // Initialize the row pins
     59 void init_row(void) {
     60     // Set all row pins as output and low
     61     for (uint8_t idx = 0; idx < MATRIX_ROWS; idx++) {
     62         gpio_set_pin_output(row_pins[idx]);
     63         gpio_write_pin_low(row_pins[idx]);
     64     }
     65 }
     66 
     67 // Initialize the multiplexers
     68 void init_amux(void) {
     69     for (uint8_t idx = 0; idx < AMUX_COUNT; idx++) {
     70         gpio_set_pin_output(amux_en_pins[idx]);
     71         gpio_write_pin_low(amux_en_pins[idx]);
     72     }
     73     for (uint8_t idx = 0; idx < AMUX_SEL_PINS_COUNT; idx++) {
     74         gpio_set_pin_output(amux_sel_pins[idx]);
     75     }
     76 }
     77 
     78 // Disable all the unused rows
     79 void disable_unused_row(uint8_t row) {
     80     // disable all the other rows apart from the current selected one
     81     for (uint8_t idx = 0; idx < MATRIX_ROWS; idx++) {
     82         if (idx != row) {
     83             gpio_write_pin_low(row_pins[idx]);
     84         }
     85     }
     86 }
     87 
     88 // Select the multiplexer channel of the specified multiplexer
     89 void select_amux_channel(uint8_t channel, uint8_t col) {
     90     // Get the channel for the specified multiplexer
     91     uint8_t ch = amux_n_col_channels[channel][col];
     92     // momentarily disable specified multiplexer
     93     gpio_write_pin_high(amux_en_pins[channel]);
     94     // Select the multiplexer channel
     95     for (uint8_t i = 0; i < AMUX_SEL_PINS_COUNT; i++) {
     96         gpio_write_pin(amux_sel_pins[i], ch & (1 << i));
     97     }
     98     // re enable specified multiplexer
     99     gpio_write_pin_low(amux_en_pins[channel]);
    100 }
    101 
    102 // Disable all the unused multiplexers
    103 void disable_unused_amux(uint8_t channel) {
    104     // disable all the other multiplexers apart from the current selected one
    105     for (uint8_t idx = 0; idx < AMUX_COUNT; idx++) {
    106         if (idx != channel) {
    107             gpio_write_pin_high(amux_en_pins[idx]);
    108         }
    109     }
    110 }
    111 // Discharge the peak hold capacitor
    112 void discharge_capacitor(void) {
    113 #ifdef OPEN_DRAIN_SUPPORT
    114     gpio_write_pin_low(DISCHARGE_PIN);
    115 #else
    116     gpio_write_pin_low(DISCHARGE_PIN);
    117     gpio_set_pin_output(DISCHARGE_PIN);
    118 #endif
    119 }
    120 
    121 // Charge the peak hold capacitor
    122 void charge_capacitor(uint8_t row) {
    123 #ifdef OPEN_DRAIN_SUPPORT
    124     gpio_write_pin_high(DISCHARGE_PIN);
    125 #else
    126     gpio_set_pin_input(DISCHARGE_PIN);
    127 #endif
    128     gpio_write_pin_high(row_pins[row]);
    129 }
    130 
    131 // Initialize the peripherals pins
    132 int ec_init(void) {
    133     // Initialize ADC
    134     palSetLineMode(ANALOG_PORT, PAL_MODE_INPUT_ANALOG);
    135     adcMux = pinToMux(ANALOG_PORT);
    136 
    137     // Dummy call to make sure that adcStart() has been called in the appropriate state
    138     adc_read(adcMux);
    139 
    140     // Initialize discharge pin as discharge mode
    141     gpio_write_pin_low(DISCHARGE_PIN);
    142 #ifdef OPEN_DRAIN_SUPPORT
    143     gpio_set_pin_output_open_drain(DISCHARGE_PIN);
    144 #else
    145     gpio_set_pin_output(DISCHARGE_PIN);
    146 #endif
    147 
    148     // Initialize drive lines
    149     init_row();
    150 
    151     // Initialize AMUXs
    152     init_amux();
    153 
    154     return 0;
    155 }
    156 
    157 // Get the noise floor
    158 void ec_noise_floor(void) {
    159     // Initialize the noise floor
    160     for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
    161         for (uint8_t col = 0; col < MATRIX_COLS; col++) {
    162             ec_config.noise_floor[row][col] = 0;
    163         }
    164     }
    165 
    166     // Sample the noise floor
    167     for (uint8_t i = 0; i < DEFAULT_NOISE_FLOOR_SAMPLING_COUNT; i++) {
    168         for (uint8_t amux = 0; amux < AMUX_COUNT; amux++) {
    169             disable_unused_amux(amux);
    170             for (uint8_t col = 0; col < amux_n_col_sizes[amux]; col++) {
    171                 uint8_t sum = 0;
    172                 for (uint8_t i = 0; i < (amux > 0 ? amux : 0); i++)
    173                     sum += amux_n_col_sizes[i];
    174                 uint8_t adjusted_col = col + sum;
    175                 for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
    176 #ifdef UNUSED_POSITIONS_LIST
    177                     if (is_unused_position(row, adjusted_col)) continue;
    178 #endif
    179                     disable_unused_row(row);
    180                     ec_config.noise_floor[row][adjusted_col] += ec_readkey_raw(amux, row, col);
    181                 }
    182             }
    183         }
    184         wait_ms(5);
    185     }
    186 
    187     // Average the noise floor
    188     for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
    189         for (uint8_t col = 0; col < MATRIX_COLS; col++) {
    190             ec_config.noise_floor[row][col] /= DEFAULT_NOISE_FLOOR_SAMPLING_COUNT;
    191         }
    192     }
    193 }
    194 
    195 // Scan key values and update matrix state
    196 bool ec_matrix_scan(matrix_row_t current_matrix[]) {
    197     bool updated = false;
    198 
    199     for (uint8_t amux = 0; amux < AMUX_COUNT; amux++) {
    200         disable_unused_amux(amux);
    201         for (uint8_t col = 0; col < amux_n_col_sizes[amux]; col++) {
    202             uint8_t sum = 0;
    203             for (uint8_t i = 0; i < (amux > 0 ? amux : 0); i++)
    204                 sum += amux_n_col_sizes[i];
    205             uint8_t adjusted_col = col + sum;
    206             for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
    207 #ifdef UNUSED_POSITIONS_LIST
    208                 if (is_unused_position(row, adjusted_col)) continue;
    209 #endif
    210                 disable_unused_row(row);
    211                 sw_value[row][adjusted_col] = ec_readkey_raw(amux, row, col);
    212 
    213                 if (ec_config.bottoming_calibration) {
    214                     if (ec_config.bottoming_calibration_starter[row][adjusted_col]) {
    215                         ec_config.bottoming_reading[row][adjusted_col]             = sw_value[row][adjusted_col];
    216                         ec_config.bottoming_calibration_starter[row][adjusted_col] = false;
    217                     } else if (sw_value[row][adjusted_col] > ec_config.bottoming_reading[row][adjusted_col]) {
    218                         ec_config.bottoming_reading[row][adjusted_col] = sw_value[row][adjusted_col];
    219                     }
    220                 } else {
    221                     updated |= ec_update_key(&current_matrix[row], row, adjusted_col, sw_value[row][adjusted_col]);
    222                 }
    223             }
    224         }
    225     }
    226 
    227     return ec_config.bottoming_calibration ? false : updated;
    228 }
    229 
    230 // Read the capacitive sensor value
    231 uint16_t ec_readkey_raw(uint8_t channel, uint8_t row, uint8_t col) {
    232     uint16_t sw_value = 0;
    233 
    234     // Select the multiplexer
    235     select_amux_channel(channel, col);
    236 
    237     // Set the row pin to low state to avoid ghosting
    238     gpio_write_pin_low(row_pins[row]);
    239 
    240     ATOMIC_BLOCK_FORCEON {
    241         // Set the row pin to high state and have capacitor charge
    242         charge_capacitor(row);
    243         // Read the ADC value
    244         sw_value = adc_read(adcMux);
    245     }
    246     // Discharge peak hold capacitor
    247     discharge_capacitor();
    248     // Waiting for the ghost capacitor to discharge fully
    249     wait_us(DISCHARGE_TIME);
    250 
    251     return sw_value;
    252 }
    253 
    254 // Update press/release state of key
    255 bool ec_update_key(matrix_row_t *current_row, uint8_t row, uint8_t col, uint16_t sw_value) {
    256     bool current_state = (*current_row >> col) & 1;
    257 
    258     // Real Time Noise Floor Calibration
    259     if (sw_value < (ec_config.noise_floor[row][col] - NOISE_FLOOR_THRESHOLD)) {
    260         uprintf("Noise Floor Change: %d, %d, %d\n", row, col, sw_value);
    261         ec_config.noise_floor[row][col]                             = sw_value;
    262         ec_config.rescaled_mode_0_actuation_threshold[row][col]     = rescale(ec_config.mode_0_actuation_threshold, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
    263         ec_config.rescaled_mode_0_release_threshold[row][col]       = rescale(ec_config.mode_0_release_threshold, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
    264         ec_config.rescaled_mode_1_initial_deadzone_offset[row][col] = rescale(ec_config.mode_1_initial_deadzone_offset, 0, 1023, ec_config.noise_floor[row][col], eeprom_ec_config.bottoming_reading[row][col]);
    265     }
    266 
    267     // Normal board-wide APC
    268     if (ec_config.actuation_mode == 0) {
    269         if (current_state && sw_value < ec_config.rescaled_mode_0_release_threshold[row][col]) {
    270             *current_row &= ~(1 << col);
    271             uprintf("Key released: %d, %d, %d\n", row, col, sw_value);
    272             return true;
    273         }
    274         if ((!current_state) && sw_value > ec_config.rescaled_mode_0_actuation_threshold[row][col]) {
    275             *current_row |= (1 << col);
    276             uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
    277             return true;
    278         }
    279     }
    280     // Rapid Trigger
    281     else if (ec_config.actuation_mode == 1) {
    282         // Is key in active zone?
    283         if (sw_value > ec_config.rescaled_mode_1_initial_deadzone_offset[row][col]) {
    284             // Is key pressed while in active zone?
    285             if (current_state) {
    286                 // Is the key still moving down?
    287                 if (sw_value > ec_config.extremum[row][col]) {
    288                     ec_config.extremum[row][col] = sw_value;
    289                     uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
    290                 }
    291                 // Has key moved up enough to be released?
    292                 else if (sw_value < ec_config.extremum[row][col] - ec_config.rescaled_mode_1_release_offset[row][col]) {
    293                     ec_config.extremum[row][col] = sw_value;
    294                     *current_row &= ~(1 << col);
    295                     uprintf("Key released: %d, %d, %d\n", row, col, sw_value);
    296                     return true;
    297                 }
    298             }
    299             // Key is not pressed while in active zone
    300             else {
    301                 // Is the key still moving up?
    302                 if (sw_value < ec_config.extremum[row][col]) {
    303                     ec_config.extremum[row][col] = sw_value;
    304                 }
    305                 // Has key moved down enough to be pressed?
    306                 else if (sw_value > ec_config.extremum[row][col] + ec_config.rescaled_mode_1_actuation_offset[row][col]) {
    307                     ec_config.extremum[row][col] = sw_value;
    308                     *current_row |= (1 << col);
    309                     uprintf("Key pressed: %d, %d, %d\n", row, col, sw_value);
    310                     return true;
    311                 }
    312             }
    313         }
    314         // Key is not in active zone
    315         else {
    316             // Check to avoid key being stuck in pressed state near the active zone threshold
    317             if (sw_value < ec_config.extremum[row][col]) {
    318                 ec_config.extremum[row][col] = sw_value;
    319                 *current_row &= ~(1 << col);
    320                 return true;
    321             }
    322         }
    323     }
    324     return false;
    325 }
    326 
    327 // Print the matrix values
    328 void ec_print_matrix(void) {
    329     for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
    330         for (uint8_t col = 0; col < MATRIX_COLS - 1; col++) {
    331             uprintf("%4d,", sw_value[row][col]);
    332         }
    333         uprintf("%4d\n", sw_value[row][MATRIX_COLS - 1]);
    334     }
    335     print("\n");
    336 }
    337 
    338 // Check if the position is unused
    339 #ifdef UNUSED_POSITIONS_LIST
    340 bool is_unused_position(uint8_t row, uint8_t col) {
    341     for (uint8_t i = 0; i < UNUSED_POSITIONS_COUNT; i++) {
    342         if (UNUSED_POSITIONS[i][0] == row && UNUSED_POSITIONS[i][1] == col) {
    343             return true;
    344         }
    345     }
    346     return false;
    347 }
    348 #endif
    349 
    350 // Rescale the value to a different range
    351 uint16_t rescale(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) {
    352     return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    353 }