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 }