matrix.c (6040B)
1 /* 2 Copyright 2018 listofoptions <listofoptions@gmail.com> 3 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation, either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <stdint.h> 19 #include <stdbool.h> 20 #include <string.h> 21 #if defined(__AVR__) 22 #include <avr/io.h> 23 #endif 24 #include <util/delay.h> 25 26 #include "wait.h" 27 #include "print.h" 28 #include "debug.h" 29 #include "util.h" 30 #include "matrix.h" 31 #include "timer.h" 32 #include <LUFA/Drivers/Peripheral/SPI.h> 33 34 #ifndef DEBOUNCE 35 # define DEBOUNCE 5 36 #endif 37 38 #if ( DEBOUNCE > 0 ) 39 static uint16_t debouncing_time ; 40 static bool debouncing = false ; 41 #endif 42 43 static uint8_t matrix [MATRIX_ROWS] = {0}; 44 45 #if ( DEBOUNCE > 0 ) 46 static uint8_t matrix_debounce_old [MATRIX_ROWS] = {0}; 47 static uint8_t matrix_debounce_new [MATRIX_ROWS] = {0}; 48 #endif 49 50 __attribute__ ((weak)) 51 void matrix_init_kb(void) { 52 matrix_init_user(); 53 } 54 55 __attribute__ ((weak)) 56 void matrix_scan_kb(void) { 57 matrix_scan_user(); 58 } 59 60 __attribute__ ((weak)) 61 void matrix_init_user(void) { 62 } 63 64 __attribute__ ((weak)) 65 void matrix_scan_user(void) { 66 } 67 68 // the keyboard's internal wiring is such that the inputs to the logic are 69 // a clock signal, and a reset line. 70 // the output is a single output pin. im bitbanging here, but the SPI controller 71 // would work normally 72 // 73 // the device functions, by using the clock signal to count 128 bits, the lower 74 // 3 bits of this 7 bit counter are tied to a 1-of-8 multiplexer, this forms 75 // the columns. 76 // the upper 4 bits form the rows, and are decoded using bcd to decimal 77 // decoders, so that 14 out of 16 of the outputs are wired to the rows of the 78 // matrix. each switch has a diode, such that the row signal feeds into the 79 // switch, and then into the diode, then into one of the columns into the 80 // matrix. the reset pin can be used to reset the entire counter. 81 82 #define HP_46010A_RESET_PIN B0 83 #define HP_46010A_SCLK_PIN B1 84 #define HP_46010A_SDATA_PIN B3 85 #define HP_46010A_LED_PIN D6 86 87 inline 88 static 89 void SCLK_increment(void) { 90 gpio_write_pin_low(HP_46010A_SCLK_PIN); 91 _delay_us( 4 ) ; // make sure the line is stable 92 gpio_write_pin_high(HP_46010A_SCLK_PIN); 93 _delay_us( 4 ) ; 94 95 return ; 96 } 97 98 inline 99 static 100 void Matrix_Reset(void) { 101 gpio_write_pin_high(HP_46010A_RESET_PIN); 102 _delay_us( 4 ) ; // make sure the line is stable 103 gpio_write_pin_low(HP_46010A_RESET_PIN); 104 105 return ; 106 } 107 108 inline 109 static 110 uint8_t Matrix_ReceiveByte (void) { 111 uint8_t received = 0 ; 112 uint8_t temp = 0 ; 113 for ( uint8_t bit = 0; bit < MATRIX_COLS; ++bit ) { 114 // toggle the clock 115 SCLK_increment(); 116 temp = gpio_read_pin(HP_46010A_SDATA_PIN) << 4 ; 117 received |= temp >> bit ; 118 } 119 120 return received ; 121 } 122 123 inline 124 static 125 void Matrix_ThrowByte(void) { 126 // we use MATRIX_COLS - 1 here because that would put us at 7 clocks 127 for ( uint8_t bit = 0; bit < MATRIX_COLS - 1; ++bit ) { 128 // toggle the clock 129 SCLK_increment(); 130 } 131 132 return ; 133 } 134 135 void matrix_init (void) { 136 // debug_matrix = 1; 137 // PB0 (SS) and PB1 (SCLK) set to outputs 138 gpio_set_pin_output(HP_46010A_RESET_PIN); 139 gpio_set_pin_output(HP_46010A_SCLK_PIN); 140 // PB2, is unused, and PB3 is our serial input 141 gpio_set_pin_input(HP_46010A_SDATA_PIN); 142 143 // SS is reset for this board, and is active High 144 // SCLK is the serial clock and is active High 145 gpio_write_pin_low(HP_46010A_RESET_PIN); 146 gpio_write_pin_high(HP_46010A_SCLK_PIN); 147 148 // led pin 149 gpio_set_pin_output(HP_46010A_LED_PIN); 150 gpio_write_pin_low(HP_46010A_LED_PIN); 151 152 matrix_init_kb(); 153 154 //toggle reset, to put the keyboard logic into a known state 155 Matrix_Reset() ; 156 } 157 158 uint8_t matrix_scan(void) { 159 160 // the first byte of the keyboard's output data can be ignored 161 Matrix_ThrowByte(); 162 163 #if ( DEBOUNCE > 0 ) 164 165 for ( uint8_t row = 0 ; row < MATRIX_ROWS ; ++row ) { 166 //transfer old debouncing values 167 matrix_debounce_old[row] = matrix_debounce_new[row] ; 168 // read new key-states in 169 matrix_debounce_new[row] = Matrix_ReceiveByte() ; 170 171 if ( matrix_debounce_new[row] != matrix_debounce_old[row] ) { 172 debouncing = true ; 173 debouncing_time = timer_read() ; 174 } 175 } 176 177 #else 178 // without debouncing we simply just read in the raw matrix 179 for ( uint8_t row = 0 ; row < MATRIX_ROWS ; ++row ) { 180 matrix[row] = Matrix_ReceiveByte ; 181 } 182 #endif 183 184 185 #if ( DEBOUNCE > 0 ) 186 if ( debouncing && ( timer_elapsed( debouncing_time ) > DEBOUNCE ) ) { 187 188 for ( uint8_t row = 0 ; row < MATRIX_ROWS ; ++row ) { 189 matrix[row] = matrix_debounce_new[row] ; 190 } 191 192 debouncing = false ; 193 } 194 #endif 195 Matrix_Reset() ; 196 197 matrix_scan_kb() ; 198 return 1; 199 } 200 201 inline 202 uint8_t matrix_get_row( uint8_t row ) { 203 return matrix[row]; 204 } 205 206 void matrix_print(void) 207 { 208 print("\nr/c 01234567\n"); 209 210 for (uint8_t row = 0; row < MATRIX_ROWS; row++) { 211 print_hex8(row); print(": "); 212 print_bin_reverse8(matrix_get_row(row)); 213 print("\n"); 214 } 215 } 216 217 inline 218 uint8_t matrix_rows(void) { 219 return MATRIX_ROWS; 220 } 221 222 inline 223 uint8_t matrix_cols(void) { 224 return MATRIX_COLS; 225 } 226 227 // as an aside, I used the M0110 converter: 228 // tmk_core/common/keyboard.c, quantum/matrix.c, and the project layout of the planck 229 // the online ducmentation starting from : 230 // https://docs.qmk.fm/#/config_options 231 // https://docs.qmk.fm/#/understanding_qmk 232 // and probably a few i forgot....