qmk_firmware

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

matrix.c (5600B)


      1 /*
      2 Copyright 2017 Balz Guenat
      3 based on work by Jun Wako <wakojun@gmail.com>
      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 /*
     20  * scan matrix
     21  */
     22 #include <stdint.h>
     23 #include <stdbool.h>
     24 #include "wait.h"
     25 #include "print.h"
     26 #include "debug.h"
     27 #include "util.h"
     28 #include "timer.h"
     29 #include "matrix.h"
     30 #include "led.h"
     31 #include "avr/timer_avr.h"
     32 
     33 // Timer resolution check
     34 #if (1000000/TIMER_RAW_FREQ > 20)
     35 #   error "Timer resolution(>20us) is not enough for HHKB matrix scan tweak on V-USB."
     36 #endif
     37 
     38 
     39 /*
     40  * Pin configuration for ATMega32U4
     41  *
     42  * Row:     PD4-6, PD7(~EN)
     43  * Col:     PB0-3
     44  * Key:     PC6(pull-uped)
     45  * Hys:     PC7
     46  */
     47 static inline void KEY_ENABLE(void) { (PORTD &= ~(1<<7)); }
     48 static inline void KEY_UNABLE(void) { (PORTD |=  (1<<7)); }
     49 static inline bool KEY_STATE(void) { return (PINC & (1<<6)); }
     50 static inline void KEY_HYS_ON(void) { (PORTC |=  (1<<7)); }
     51 static inline void KEY_HYS_OFF(void) { (PORTC &= ~(1<<7)); }
     52 static inline void KEY_INIT(void)
     53 {
     54     /* Col */
     55     DDRB  |=  0x0F;
     56     /* Key: input with pull-up */
     57     DDRC  &= ~(1<<6);
     58     PORTC |=  (1<<6);
     59     /* Hys */
     60     DDRC  |=  (1<<7);
     61     /* Row */
     62     DDRD  |=  0xF0;
     63 
     64     KEY_UNABLE();
     65     KEY_HYS_OFF();
     66 }
     67 static inline void SET_ROW(uint8_t ROW)
     68 {
     69     // PD4-6
     70     PORTD = (PORTD & 0x8F) | ((ROW & 0x07) << 4);
     71 }
     72 static inline void SET_COL(uint8_t COL)
     73 {
     74     // PB0-3
     75     PORTB = (PORTB & 0xF0) | (COL & 0x0F);
     76 }
     77 
     78 static uint32_t matrix_last_modified = 0;
     79 
     80 // matrix state buffer(1:on, 0:off)
     81 static matrix_row_t *matrix;
     82 static matrix_row_t *matrix_prev;
     83 static matrix_row_t _matrix0[MATRIX_ROWS];
     84 static matrix_row_t _matrix1[MATRIX_ROWS];
     85 
     86 
     87 
     88 __attribute__ ((weak))
     89 void matrix_init_kb(void) {
     90     matrix_init_user();
     91 }
     92 
     93 __attribute__ ((weak))
     94 void matrix_scan_kb(void) {
     95     matrix_scan_user();
     96 }
     97 
     98 __attribute__ ((weak))
     99 void matrix_init_user(void) {
    100 }
    101 
    102 __attribute__ ((weak))
    103 void matrix_scan_user(void) {
    104 }
    105 
    106 
    107 void matrix_init(void)
    108 {
    109     debug_enable = true;
    110     debug_matrix = true;
    111 
    112     KEY_INIT();
    113 
    114     // LEDs on NumLock, CapsLock and ScrollLock(PB4, PB5, PB6)
    115     DDRB  |= (1<<4) | (1<<5) | (1<<6);
    116     PORTB |= (1<<4) | (1<<5) | (1<<6);
    117 
    118     // initialize matrix state: all keys off
    119     for (uint8_t i=0; i < MATRIX_ROWS; i++) _matrix0[i] = 0x00;
    120     for (uint8_t i=0; i < MATRIX_ROWS; i++) _matrix1[i] = 0x00;
    121     matrix = _matrix0;
    122     matrix_prev = _matrix1;
    123     matrix_init_kb();
    124 }
    125 
    126 uint8_t matrix_scan(void)
    127 {
    128     matrix_row_t *tmp;
    129 
    130     tmp = matrix_prev;
    131     matrix_prev = matrix;
    132     matrix = tmp;
    133 
    134     uint8_t row, col;
    135     for (col = 0; col < MATRIX_COLS; col++) {
    136         SET_COL(col);
    137         for (row = 0; row < MATRIX_ROWS; row++) {
    138             //KEY_SELECT(row, col);
    139             SET_ROW(row);
    140             _delay_us(2);
    141 
    142             // Not sure this is needed. This just emulates HHKB controller's behaviour.
    143             if (matrix_prev[row] & (1<<col)) {
    144                 KEY_HYS_ON();
    145             }
    146             _delay_us(10);
    147 
    148             // NOTE: KEY_STATE is valid only in 20us after KEY_ENABLE.
    149             // If V-USB interrupts in this section we could lose 40us or so
    150             // and would read invalid value from KEY_STATE.
    151             uint8_t last = TIMER_RAW;
    152 
    153             KEY_ENABLE();
    154 
    155             // Wait for KEY_STATE outputs its value.
    156             _delay_us(2);
    157 
    158             if (KEY_STATE()) {
    159                 matrix[row] &= ~(1<<col);
    160             } else {
    161                 matrix[row] |= (1<<col);
    162             }
    163 
    164             // Ignore if this code region execution time elapses more than 20us.
    165             // MEMO: 20[us] * (TIMER_RAW_FREQ / 1000000)[count per us]
    166             // MEMO: then change above using this rule: a/(b/c) = a*1/(b/c) = a*(c/b)
    167             if (TIMER_DIFF_RAW(TIMER_RAW, last) > 20/(1000000/TIMER_RAW_FREQ)) {
    168                 matrix[row] = matrix_prev[row];
    169             }
    170 
    171             _delay_us(5);
    172             KEY_HYS_OFF();
    173             KEY_UNABLE();
    174 
    175             // NOTE: KEY_STATE keep its state in 20us after KEY_ENABLE.
    176             // This takes 25us or more to make sure KEY_STATE returns to idle state.
    177             _delay_us(75);
    178         }
    179         if (matrix[row] ^ matrix_prev[row]) {
    180             matrix_last_modified = timer_read32();
    181         }
    182     }
    183     matrix_scan_kb();
    184     return 1;
    185 }
    186 
    187 inline
    188 matrix_row_t matrix_get_row(uint8_t row) {
    189     return matrix[row];
    190 }
    191 
    192 void matrix_print(void)
    193 {
    194 #if (MATRIX_COLS <= 8)
    195     print("r/c 01234567\n");
    196 #elif (MATRIX_COLS <= 16)
    197     print("r/c 0123456789ABCDEF\n");
    198 #elif (MATRIX_COLS <= 32)
    199     print("r/c 0123456789ABCDEF0123456789ABCDEF\n");
    200 #endif
    201 
    202     for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
    203 
    204 #if (MATRIX_COLS <= 8)
    205         xprintf("%02X: %08b%s\n", row, bitrev(matrix_get_row(row)),
    206 #elif (MATRIX_COLS <= 16)
    207         xprintf("%02X: %016b%s\n", row, bitrev16(matrix_get_row(row)),
    208 #elif (MATRIX_COLS <= 32)
    209         xprintf("%02X: %032b%s\n", row, bitrev32(matrix_get_row(row)),
    210 #endif
    211 #ifdef MATRIX_HAS_GHOST
    212         matrix_has_ghost_in_row(row) ?  " <ghost" : ""
    213 #else
    214         ""
    215 #endif
    216         );
    217     }
    218 }