summaryrefslogtreecommitdiffstats
path: root/keyboards/fc660c/matrix.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/fc660c/matrix.c')
-rw-r--r--keyboards/fc660c/matrix.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/keyboards/fc660c/matrix.c b/keyboards/fc660c/matrix.c
new file mode 100644
index 000000000..211506fdf
--- /dev/null
+++ b/keyboards/fc660c/matrix.c
@@ -0,0 +1,226 @@
+/*
+Copyright 2017 Balz Guenat
+based on work by Jun Wako <wakojun@gmail.com>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * scan matrix
+ */
+#include <stdint.h>
+#include <stdbool.h>
+#include <util/delay.h>
+#include "print.h"
+#include "debug.h"
+#include "util.h"
+#include "timer.h"
+#include "matrix.h"
+#include "led.h"
+
+// Timer resolution check
+#if (1000000/TIMER_RAW_FREQ > 20)
+# error "Timer resolution(>20us) is not enough for HHKB matrix scan tweak on V-USB."
+#endif
+
+
+/*
+ * Pin configuration for ATMega32U4
+ *
+ * Row: PD4-6, 7(~EN)
+ * Col: PB0-2, 3(Z5 ~EN), 4(Z4 ~EN)
+ * Key: PC6(pull-uped)
+ * Hys: PC7
+ */
+static inline void KEY_ENABLE(void) { (PORTD &= ~(1<<7)); }
+static inline void KEY_UNABLE(void) { (PORTD |= (1<<7)); }
+static inline bool KEY_STATE(void) { return (PINC & (1<<6)); }
+static inline void KEY_HYS_ON(void) { (PORTC |= (1<<7)); }
+static inline void KEY_HYS_OFF(void) { (PORTC &= ~(1<<7)); }
+static inline void KEY_INIT(void)
+{
+ /* Col */
+ DDRB |= 0x1F;
+ /* Key: input with pull-up */
+ DDRC &= ~(1<<6);
+ PORTC |= (1<<6);
+ /* Hys */
+ DDRC |= (1<<7);
+ /* Row */
+ DDRD |= 0xF0;
+
+ KEY_UNABLE();
+ KEY_HYS_OFF();
+}
+static inline void SET_ROW(uint8_t ROW)
+{
+ // set row with unabling key
+ PORTD = (PORTD & 0x0F) | (1<<7) | ((ROW & 0x07) << 4);
+}
+static inline void SET_COL(uint8_t COL)
+{
+ // |PB3(Z5 ~EN)|PB4(Z4 ~EN)
+ // --------|-----------|-----------
+ // Col:0-7 |high |low
+ // Col:8-F |low |high
+ PORTB = (PORTB & 0xE0) | ((COL & 0x08) ? 1<<4 : 1<<3) | (COL & 0x07);
+}
+
+static uint32_t matrix_last_modified = 0;
+
+// matrix state buffer(1:on, 0:off)
+static matrix_row_t *matrix;
+static matrix_row_t *matrix_prev;
+static matrix_row_t _matrix0[MATRIX_ROWS];
+static matrix_row_t _matrix1[MATRIX_ROWS];
+
+
+__attribute__ ((weak))
+void matrix_init_quantum(void) {
+ matrix_init_kb();
+}
+
+__attribute__ ((weak))
+void matrix_scan_quantum(void) {
+ matrix_scan_kb();
+}
+
+__attribute__ ((weak))
+void matrix_init_kb(void) {
+ matrix_init_user();
+}
+
+__attribute__ ((weak))
+void matrix_scan_kb(void) {
+ matrix_scan_user();
+}
+
+__attribute__ ((weak))
+void matrix_init_user(void) {
+}
+
+__attribute__ ((weak))
+void matrix_scan_user(void) {
+}
+
+
+void matrix_init(void)
+{
+ KEY_INIT();
+
+ // LEDs on CapsLock and Insert
+ DDRB |= (1<<5) | (1<<6);
+ PORTB |= (1<<5) | (1<<6);
+
+ // initialize matrix state: all keys off
+ for (uint8_t i=0; i < MATRIX_ROWS; i++) _matrix0[i] = 0x00;
+ for (uint8_t i=0; i < MATRIX_ROWS; i++) _matrix1[i] = 0x00;
+ matrix = _matrix0;
+ matrix_prev = _matrix1;
+ matrix_init_quantum();
+}
+
+uint8_t matrix_scan(void)
+{
+ matrix_row_t *tmp;
+
+ tmp = matrix_prev;
+ matrix_prev = matrix;
+ matrix = tmp;
+
+ uint8_t row, col;
+ for (col = 0; col < MATRIX_COLS; col++) {
+ SET_COL(col);
+ for (row = 0; row < MATRIX_ROWS; row++) {
+ //KEY_SELECT(row, col);
+ SET_ROW(row);
+ _delay_us(2);
+
+ // Not sure this is needed. This just emulates HHKB controller's behaviour.
+ if (matrix_prev[row] & (1<<col)) {
+ KEY_HYS_ON();
+ }
+ _delay_us(10);
+
+ // NOTE: KEY_STATE is valid only in 20us after KEY_ENABLE.
+ // If V-USB interrupts in this section we could lose 40us or so
+ // and would read invalid value from KEY_STATE.
+ uint8_t last = TIMER_RAW;
+
+ KEY_ENABLE();
+
+ // Wait for KEY_STATE outputs its value.
+ _delay_us(2);
+
+ if (KEY_STATE()) {
+ matrix[row] &= ~(1<<col);
+ } else {
+ matrix[row] |= (1<<col);
+ }
+
+ // Ignore if this code region execution time elapses more than 20us.
+ // MEMO: 20[us] * (TIMER_RAW_FREQ / 1000000)[count per us]
+ // MEMO: then change above using this rule: a/(b/c) = a*1/(b/c) = a*(c/b)
+ if (TIMER_DIFF_RAW(TIMER_RAW, last) > 20/(1000000/TIMER_RAW_FREQ)) {
+ matrix[row] = matrix_prev[row];
+ }
+
+ _delay_us(5);
+ KEY_HYS_OFF();
+ KEY_UNABLE();
+
+ // NOTE: KEY_STATE keep its state in 20us after KEY_ENABLE.
+ // This takes 25us or more to make sure KEY_STATE returns to idle state.
+ _delay_us(75);
+ }
+ if (matrix[row] ^ matrix_prev[row]) {
+ matrix_last_modified = timer_read32();
+ }
+ }
+ matrix_scan_quantum();
+ return 1;
+}
+
+inline
+matrix_row_t matrix_get_row(uint8_t row) {
+ return matrix[row];
+}
+
+void matrix_print(void)
+{
+#if (MATRIX_COLS <= 8)
+ print("r/c 01234567\n");
+#elif (MATRIX_COLS <= 16)
+ print("r/c 0123456789ABCDEF\n");
+#elif (MATRIX_COLS <= 32)
+ print("r/c 0123456789ABCDEF0123456789ABCDEF\n");
+#endif
+
+ for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
+
+#if (MATRIX_COLS <= 8)
+ xprintf("%02X: %08b%s\n", row, bitrev(matrix_get_row(row)),
+#elif (MATRIX_COLS <= 16)
+ xprintf("%02X: %016b%s\n", row, bitrev16(matrix_get_row(row)),
+#elif (MATRIX_COLS <= 32)
+ xprintf("%02X: %032b%s\n", row, bitrev32(matrix_get_row(row)),
+#endif
+#ifdef MATRIX_HAS_GHOST
+ matrix_has_ghost_in_row(row) ? " <ghost" : ""
+#else
+ ""
+#endif
+ );
+ }
+}