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 }