/* Copyright 2011 Jun Wako Copyright 2020 Kan-Ru Chen This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "matrix.h" #include "debug.h" #include "timer.h" #include "wait.h" #include "suspend.h" #include #ifdef BLUETOOTH_ENABLE # include "adafruit_ble.h" #endif #define RELAX_TIME_US 5 #define ADC_READ_TIME_US 5 uint8_t power_save_level; static uint32_t matrix_last_modified = 0; static inline void key_strobe_high(void) { writePinLow(B6); } static inline void key_strobe_low(void) { writePinHigh(B6); } static inline bool key_state(void) { return readPin(D7); } static inline void key_prev_on(void) { writePinHigh(B7); } static inline void key_prev_off(void) { writePinLow(B7); } static inline bool key_power_state(void) { return !readPin(D6); } static inline void suspend_power_down_longer(void) { uint8_t times = 60; while (--times) suspend_power_down(); } void matrix_power_up(void) { dprint("[matrix_on]\n"); // change pins output DDRB = 0xFF; PORTB = 0x40; // switch MOS FET on setPinOutput(D6); writePinLow(D6); } void matrix_power_down(void) { dprint("[matrix_off]\n"); // input with pull-up consumes less than without it when pin is open DDRB = 0x00; PORTB = 0xFF; // switch MOS FET off setPinOutput(D6); writePinHigh(D6); } static inline void key_select_row(uint8_t row) { PORTB = (PORTB & 0b11111000) | ((row)&0b111); } static inline void key_select_col(uint8_t col) { PORTB = (PORTB & 0b11000111) | (((col)&0b111) << 3); } static inline bool key_prev_was_on(matrix_row_t matrix[], uint8_t row, uint8_t col) { return matrix[row] & (1 << col); } void matrix_init_custom(void) { power_save_level = 0; } bool matrix_scan_custom(matrix_row_t current_matrix[]) { bool matrix_has_changed = false; // power on if (!key_power_state()) { matrix_power_up(); } for (uint8_t row = 0; row < MATRIX_ROWS; row++) { matrix_row_t last_row_value = current_matrix[row]; key_select_row(row); wait_us(RELAX_TIME_US); for (uint8_t col = 0; col < MATRIX_COLS; col++) { // Hysteresis control: assert(1) when previous key state is on if (key_prev_was_on(current_matrix, row, col)) { key_prev_on(); } else { key_prev_off(); } // Disable interrupts to encure the ADC timing is correct cli(); // strobe key_select_col(col); key_strobe_high(); // Wait for ADC to outputs its value. // 1us was ok on one HHKB, but not worked on another. // no wait doesn't work on Teensy++ with pro(1us works) // no wait does work on tmk PCB(8MHz) with pro2 // 1us wait does work on both of above // 1us wait doesn't work on tmk(16MHz) // 5us wait does work on tmk(16MHz) // 5us wait does work on tmk(16MHz/2) // 5us wait does work on tmk(8MHz) // 10us wait does work on Teensy++ with pro // 10us wait does work on 328p+iwrap with pro // 10us wait doesn't work on tmk PCB(8MHz) with pro2(very lagged scan) wait_us(ADC_READ_TIME_US); if (key_state()) { current_matrix[row] &= ~(1 << col); } else { current_matrix[row] |= (1 << col); } key_strobe_low(); sei(); // Make sure enough time has elapsed since the last call // This is to ensure the matrix voltages have relaxed wait_us(RELAX_TIME_US); } if (current_matrix[row] ^ last_row_value) { matrix_has_changed = true; matrix_last_modified = timer_read32(); } } // Power saving uint32_t time_diff = timer_elapsed32(matrix_last_modified); if (time_diff > MATRIX_POWER_SAVE_TIMEOUT_L3_MS) { power_save_level = 3; suspend_power_down_longer(); } else if (time_diff > MATRIX_POWER_SAVE_TIMEOUT_L2_MS) { power_save_level = 2; #ifdef BLUETOOTH_ENABLE if (!adafruit_ble_is_connected()) { power_save_level = 3; } #endif suspend_power_down_longer(); } else if (time_diff > MATRIX_POWER_SAVE_TIMEOUT_MS) { power_save_level = 1; suspend_power_down(); } else { if (power_save_level != 0) { power_save_level = 0; suspend_wakeup_init(); } } return matrix_has_changed; } bool adafruit_ble_delbonds(void); bool adafruit_ble_reconnect(void); bool command_extra(uint8_t code) { switch (code) { #ifdef BLUETOOTH_ENABLE case KC_R: adafruit_ble_delbonds(); return true; case KC_S: adafruit_ble_reconnect(); return true; #endif default: return false; } }