qmk_firmware

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

matrix.c (5644B)


      1 /*
      2 Copyright 2011 Jun Wako <wakojun@gmail.com>
      3 Copyright 2020 Kan-Ru Chen <kanru@kanru.info>
      4 
      5 This program is free software: you can redistribute it and/or modify
      6 it under the terms of the GNU General Public License as published by
      7 the Free Software Foundation, either version 2 of the License, or
      8 (at your option) any later version.
      9 
     10 This program is distributed in the hope that it will be useful,
     11 but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 */
     18 
     19 #include "matrix.h"
     20 #include "debug.h"
     21 #include "timer.h"
     22 #include "wait.h"
     23 #include "suspend.h"
     24 #include <avr/interrupt.h>
     25 
     26 #ifdef BLUETOOTH_ENABLE
     27 #    include "adafruit_ble.h"
     28 #endif
     29 
     30 #define RELAX_TIME_US 5
     31 #define ADC_READ_TIME_US 5
     32 
     33 uint8_t power_save_level;
     34 
     35 static uint32_t matrix_last_modified = 0;
     36 
     37 static inline void key_strobe_high(void) { gpio_write_pin_low(B6); }
     38 static inline void key_strobe_low(void) { gpio_write_pin_high(B6); }
     39 static inline bool key_state(void) { return gpio_read_pin(D7); }
     40 static inline void key_prev_on(void) { gpio_write_pin_high(B7); }
     41 static inline void key_prev_off(void) { gpio_write_pin_low(B7); }
     42 static inline bool key_power_state(void) { return !gpio_read_pin(D6); }
     43 
     44 static inline void suspend_power_down_longer(void) {
     45     uint8_t times = 60;
     46     while (--times) suspend_power_down();
     47 }
     48 
     49 void matrix_power_up(void) {
     50     dprint("[matrix_on]\n");
     51     // change pins output
     52     DDRB  = 0xFF;
     53     PORTB = 0x40;
     54     // switch MOS FET on
     55     gpio_set_pin_output(D6);
     56     gpio_write_pin_low(D6);
     57 }
     58 
     59 void matrix_power_down(void) {
     60     dprint("[matrix_off]\n");
     61     // input with pull-up consumes less than without it when pin is open
     62     DDRB  = 0x00;
     63     PORTB = 0xFF;
     64     // switch MOS FET off
     65     gpio_set_pin_output(D6);
     66     gpio_write_pin_high(D6);
     67 }
     68 
     69 static inline void key_select_row(uint8_t row) { PORTB = (PORTB & 0b11111000) | ((row)&0b111); }
     70 static inline void key_select_col(uint8_t col) { PORTB = (PORTB & 0b11000111) | (((col)&0b111) << 3); }
     71 static inline bool key_prev_was_on(matrix_row_t matrix[], uint8_t row, uint8_t col) { return matrix[row] & (1 << col); }
     72 
     73 void matrix_init_custom(void) { power_save_level = 0; }
     74 
     75 bool matrix_scan_custom(matrix_row_t current_matrix[]) {
     76     bool matrix_has_changed = false;
     77 
     78     // power on
     79     if (!key_power_state()) {
     80         matrix_power_up();
     81     }
     82     for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
     83         matrix_row_t last_row_value = current_matrix[row];
     84 
     85         key_select_row(row);
     86         wait_us(RELAX_TIME_US);
     87 
     88         for (uint8_t col = 0; col < MATRIX_COLS; col++) {
     89             // Hysteresis control: assert(1) when previous key state is on
     90             if (key_prev_was_on(current_matrix, row, col)) {
     91                 key_prev_on();
     92             } else {
     93                 key_prev_off();
     94             }
     95 
     96             // Disable interrupts to encure the ADC timing is correct
     97             cli();
     98 
     99             // strobe
    100             key_select_col(col);
    101             key_strobe_high();
    102 
    103             // Wait for ADC to outputs its value.
    104             // 1us was ok on one HHKB, but not worked on another.
    105             // no   wait doesn't work on Teensy++ with pro(1us works)
    106             // no   wait does    work on tmk PCB(8MHz) with pro2
    107             // 1us  wait does    work on both of above
    108             // 1us  wait doesn't work on tmk(16MHz)
    109             // 5us  wait does    work on tmk(16MHz)
    110             // 5us  wait does    work on tmk(16MHz/2)
    111             // 5us  wait does    work on tmk(8MHz)
    112             // 10us wait does    work on Teensy++ with pro
    113             // 10us wait does    work on 328p+iwrap with pro
    114             // 10us wait doesn't work on tmk PCB(8MHz) with pro2(very lagged scan)
    115             wait_us(ADC_READ_TIME_US);
    116 
    117             if (key_state()) {
    118                 current_matrix[row] &= ~(1 << col);
    119             } else {
    120                 current_matrix[row] |= (1 << col);
    121             }
    122 
    123             key_strobe_low();
    124             sei();
    125 
    126             // Make sure enough time has elapsed since the last call
    127             // This is to ensure the matrix voltages have relaxed
    128             wait_us(RELAX_TIME_US);
    129         }
    130         if (current_matrix[row] ^ last_row_value) {
    131             matrix_has_changed   = true;
    132             matrix_last_modified = timer_read32();
    133         }
    134     }
    135 
    136     // Power saving
    137     uint32_t time_diff = timer_elapsed32(matrix_last_modified);
    138     if (time_diff > MATRIX_POWER_SAVE_TIMEOUT_L3_MS) {
    139         power_save_level = 3;
    140         suspend_power_down_longer();
    141     } else if (time_diff > MATRIX_POWER_SAVE_TIMEOUT_L2_MS) {
    142         power_save_level = 2;
    143 #ifdef BLUETOOTH_ENABLE
    144         if (!adafruit_ble_is_connected()) {
    145             power_save_level = 3;
    146         }
    147 #endif
    148         suspend_power_down_longer();
    149     } else if (time_diff > MATRIX_POWER_SAVE_TIMEOUT_MS) {
    150         power_save_level = 1;
    151         suspend_power_down();
    152     } else {
    153         if (power_save_level != 0) {
    154             power_save_level = 0;
    155             suspend_wakeup_init();
    156         }
    157     }
    158 
    159     return matrix_has_changed;
    160 }
    161 
    162 bool adafruit_ble_delbonds(void);
    163 bool adafruit_ble_reconnect(void);
    164 
    165 bool command_extra(uint8_t code) {
    166     switch (code) {
    167 #ifdef BLUETOOTH_ENABLE
    168         case KC_R:
    169             adafruit_ble_delbonds();
    170             return true;
    171         case KC_S:
    172             adafruit_ble_reconnect();
    173             return true;
    174 #endif
    175         default:
    176             return false;
    177     }
    178 }