diff --git a/doc/plugin/Hardware-EZ-ErgoDox.md b/doc/plugin/Hardware-EZ-ErgoDox.md new file mode 100644 index 00000000..e2ded6bb --- /dev/null +++ b/doc/plugin/Hardware-EZ-ErgoDox.md @@ -0,0 +1,6 @@ +# Kaleidoscope-Hardware-ErgoDox + +This is a plugin for [Kaleidoscope][fw], that adds hardware support for +the ErgoDox. + + [fw]: https://github.com/keyboardio/Kaleidoscope diff --git a/doc/plugin/Hardware-Technomancy-Atreus.md b/doc/plugin/Hardware-Technomancy-Atreus.md new file mode 100644 index 00000000..fb7fcfc1 --- /dev/null +++ b/doc/plugin/Hardware-Technomancy-Atreus.md @@ -0,0 +1,11 @@ +# Kaleidoscope-Hardware-Technomancy-Atreus + +This is a plugin for [Kaleidoscope][fw], that adds hardware support for the +[Atreus][atreus]. Supported models include post-2016 PCB variants with an A* +MCU, and the hand-wired variant from [FalbaTech][falba] with a Teensy2. + +PCBs prior to 2016, and the legacy teensy2 variants are not supported. + + [fw]: https://github.com/keyboardio/Kaleidoscope + [atreus]: https://atreus.technomancy.us/ + [falba]: https://falba.tech/ diff --git a/examples/Atreus/.kaleidoscope-builder.conf b/examples/Atreus/.kaleidoscope-builder.conf new file mode 100644 index 00000000..d80241fb --- /dev/null +++ b/examples/Atreus/.kaleidoscope-builder.conf @@ -0,0 +1,24 @@ +flash_over_usb() { + sleep 1s + + echo "Please reset your keyboard!" + teensy_loader_cli --mcu "${MCU}" -w "${HEX_FILE_PATH}" +} + +flash () { + prepare_to_flash + + # This is defined in the (optional) user config. + # shellcheck disable=SC2154 + ${preFlash_HOOKS} + + flash_over_usb || flash_over_usb + + # This is defined in the (optional) user config. + # shellcheck disable=SC2154 + ${postFlash_HOOKS} +} + +NO_RESET=1 +DEFAULT_SKETCH="Atreus" +BOARD="atreus" diff --git a/examples/Atreus/Atreus.ino b/examples/Atreus/Atreus.ino new file mode 100644 index 00000000..373f09ac --- /dev/null +++ b/examples/Atreus/Atreus.ino @@ -0,0 +1,101 @@ +/* -*- mode: c++ -*- + * Atreus -- A very basic Kaleidoscope example for the Atreus + * Copyright (C) 2018 Keyboard.io, Inc + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "Kaleidoscope.h" +#include "Kaleidoscope-Macros.h" + +#define MO(n) ShiftToLayer(n) +#define TG(n) LockLayer(n) + +enum { + RESET +}; + +#define Key_Exclamation LSHIFT(Key_1) +#define Key_At LSHIFT(Key_2) +#define Key_Hash LSHIFT(Key_3) +#define Key_Dollar LSHIFT(Key_4) +#define Key_And LSHIFT(Key_7) +#define Key_Star LSHIFT(Key_8) +#define Key_Plus LSHIFT(Key_Equals) + +/* *INDENT-OFF* */ +KEYMAPS( + [0] = KEYMAP_STACKED + ( + Key_Q ,Key_W ,Key_E ,Key_R ,Key_T + ,Key_A ,Key_S ,Key_D ,Key_F ,Key_G + ,Key_Z ,Key_X ,Key_C ,Key_V ,Key_B + ,Key_Esc ,Key_Tab ,Key_LeftGui ,Key_LeftShift ,Key_Backspace ,Key_LeftControl + + ,Key_Y ,Key_U ,Key_I ,Key_O ,Key_P + ,Key_H ,Key_J ,Key_K ,Key_L ,Key_Semicolon + ,Key_N ,Key_M ,Key_Comma ,Key_Period ,Key_Slash + ,Key_LeftAlt ,Key_Space ,MO(1) ,Key_Minus ,Key_Quote ,Key_Enter + ), + + [1] = KEYMAP_STACKED + ( + Key_Exclamation ,Key_At ,Key_UpArrow ,Key_LeftCurlyBracket ,Key_RightCurlyBracket + ,Key_Hash ,Key_LeftArrow ,Key_DownArrow ,Key_RightArrow ,Key_Dollar + ,Key_LeftBracket ,Key_RightBracket ,Key_LeftParen ,Key_RightParen ,Key_And + ,TG(2) ,Key_Insert ,Key_LeftGui ,Key_LeftShift ,Key_Backspace ,Key_LeftControl + + ,Key_PageUp ,Key_7 ,Key_8 ,Key_9 ,Key_Star + ,Key_PageDown ,Key_4 ,Key_5 ,Key_6 ,Key_Plus + ,Key_Backtick ,Key_1 ,Key_2 ,Key_3 ,Key_Backslash + ,Key_LeftAlt ,Key_Space ,MO(1) ,Key_Period ,Key_0 ,Key_Equals + ), + + [2] = KEYMAP_STACKED + ( + Key_Insert ,Key_Home ,Key_UpArrow ,Key_End ,Key_PageUp + ,Key_Delete ,Key_LeftArrow ,Key_DownArrow ,Key_RightArrow ,Key_PageDown + ,XXX ,Consumer_VolumeIncrement ,XXX ,XXX ,M(RESET) + ,XXX ,Consumer_VolumeDecrement ,___ ,___ ,___ ,___ + + ,Key_UpArrow ,Key_F7 ,Key_F8 ,Key_F9 ,Key_F10 + ,Key_DownArrow ,Key_F4 ,Key_F5 ,Key_F6 ,Key_F11 + ,XXX ,Key_F1 ,Key_F2 ,Key_F3 ,Key_F12 + ,___ ,___ ,___ ,Key_PrintScreen ,Key_ScrollLock ,Consumer_PlaySlashPause + ) +) +/* *INDENT-ON* */ + +KALEIDOSCOPE_INIT_PLUGINS(Macros); + +const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) { + switch (macroIndex) { + case RESET: + Atreus.resetDevice(); + break; + default: + break; + } + + return MACRO_NONE; +} + +void setup() { + Kaleidoscope.setup(); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/examples/ErgoDox/.kaleidoscope-builder.conf b/examples/ErgoDox/.kaleidoscope-builder.conf new file mode 100644 index 00000000..8ce70f95 --- /dev/null +++ b/examples/ErgoDox/.kaleidoscope-builder.conf @@ -0,0 +1,24 @@ +flash_over_usb() { + sleep 1s + + echo "Please reset your keyboard!" + teensy_loader_cli --mcu "${MCU}" -w "${HEX_FILE_PATH}" +} + +flash () { + prepare_to_flash + + # This is defined in the (optional) user config. + # shellcheck disable=SC2154 + ${preFlash_HOOKS} + + flash_over_usb || flash_over_usb + + # This is defined in the (optional) user config. + # shellcheck disable=SC2154 + ${postFlash_HOOKS} +} + +NO_RESET=1 +DEFAULT_SKETCH="ErgoDox" +BOARD="ergodox" diff --git a/examples/ErgoDox/ErgoDox.ino b/examples/ErgoDox/ErgoDox.ino new file mode 100644 index 00000000..d78779e2 --- /dev/null +++ b/examples/ErgoDox/ErgoDox.ino @@ -0,0 +1,57 @@ +/* -*- mode: c++ -*- + * ErgoDox -- A very basic Kaleidoscope example for the ErgoDox + * Copyright (C) 2018 Keyboard.io, Inc + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "Kaleidoscope.h" + +/* *INDENT-OFF* */ +KEYMAPS( + [0] = KEYMAP_STACKED + ( + // left hand + Key_Equals, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LeftArrow, + Key_Delete, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_NoKey, + Key_Backspace, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_LeftShift, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_LeftControl, + Key_Backtick, Key_Quote, Key_NoKey, Key_LeftArrow, Key_RightArrow, + + Key_NoKey, Key_LeftGui, + Key_Home, + Key_Space, Key_Backspace, Key_End, + + // right hand + Key_RightArrow, Key_6, Key_7, Key_8, Key_9, Key_0, Key_Minus, + Key_NoKey, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Backslash, + Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, + Key_RightControl, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_RightShift, + Key_UpArrow, Key_DownArrow, XXX, XXX, Key_NoKey, + + Key_LeftAlt, Key_Esc, + Key_PageUp, + Key_PageDown, Key_Tab, Key_Enter + ), +) +/* *INDENT-ON* */ + +void setup() { + Kaleidoscope.setup(); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/src/Kaleidoscope-Hardware-EZ-ErgoDox.h b/src/Kaleidoscope-Hardware-EZ-ErgoDox.h new file mode 100644 index 00000000..356e315d --- /dev/null +++ b/src/Kaleidoscope-Hardware-EZ-ErgoDox.h @@ -0,0 +1,20 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope + * Copyright (C) 2018 Keyboard.io, Inc + * + * 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 3 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 . + */ + +#pragma once +#include "kaleidoscope/hardware/ErgoDox.h" diff --git a/src/Kaleidoscope-Hardware-Technomancy-Atreus.h b/src/Kaleidoscope-Hardware-Technomancy-Atreus.h new file mode 100644 index 00000000..f098619a --- /dev/null +++ b/src/Kaleidoscope-Hardware-Technomancy-Atreus.h @@ -0,0 +1,20 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Technomancy-Atreus -- Atreus hardware support for Kaleidoscope + * Copyright (C) 2018 Keyboard.io, Inc + * + * 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 3 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 . + */ + +#pragma once +#include "kaleidoscope/hardware/Atreus.h" diff --git a/src/kaleidoscope/hardware/Atreus.cpp b/src/kaleidoscope/hardware/Atreus.cpp new file mode 100644 index 00000000..db5219cb --- /dev/null +++ b/src/kaleidoscope/hardware/Atreus.cpp @@ -0,0 +1,297 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Technomancy-Atreus -- Atreus hardware support for Kaleidoscope + * Copyright (C) 2018 Keyboard.io, Inc + * + * Based on QMK (commit e9a67f8fd) + * (C) Jack Humbert, Phil Hagelberg, and others + * Original QMK sources: + * - keyboards/atreus/atreus.h + * - keyboards/atreus/config.h + * - quantum/matrix.c + * - tmk_core/common/avr/bootloader.c + * + * 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 3 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 . + */ + +#ifdef ARDUINO_AVR_ATREUS + +#include +#include +#include + +static bool do_scan_ = true; + +ISR(TIMER1_OVF_vect) { + do_scan_ = true; +} + +namespace kaleidoscope { +namespace hardware { + +uint16_t Atreus::previousKeyState_[ROWS]; +uint16_t Atreus::keyState_[ROWS]; +uint16_t Atreus::masks_[ROWS]; + +uint8_t Atreus::debounce_matrix_[ROWS][COLS]; +uint8_t Atreus::debounce = 3; + +void Atreus::setup(void) { + wdt_disable(); + delay(100); + + for (uint8_t i = 0; i < ROWS; i++) { + unselectRow(i); + keyState_[i] = previousKeyState_[i] = 0; + } + + // Initialize columns + DDRB &= ~(_BV(5) | _BV(4) | _BV(6) | _BV(7)); + PORTB |= (_BV(5) | _BV(4) | _BV(6) | _BV(7)); + + DDRD &= ~(_BV(7) | _BV(4) | _BV(6)); + PORTD |= (_BV(7) | _BV(4) | _BV(6)); + + DDRC &= ~(_BV(6)); + PORTC |= (_BV(6)); + + DDRE &= ~(_BV(6)); + PORTE |= (_BV(6)); + + DDRF &= ~(_BV(6) | _BV(7)); + PORTF |= (_BV(6) | _BV(7)); + + /* Set up Timer1 for 500usec */ + TCCR1B = _BV(WGM13); + TCCR1A = 0; + + const uint32_t cycles = (F_CPU / 2000000) * 500; + + ICR1 = cycles; + TCCR1B = _BV(WGM13) | _BV(CS10); + TIMSK1 = _BV(TOIE1); +} + +void Atreus::selectRow(uint8_t row) { + switch (row) { + case 0: + DDRD |= (_BV(0)); + PORTD &= ~(_BV(0)); + break; + case 1: + DDRD |= (_BV(1)); + PORTD &= ~(_BV(1)); + break; + case 2: + DDRD |= (_BV(3)); + PORTD &= ~(_BV(3)); + break; + case 3: + DDRD |= (_BV(2)); + PORTD &= ~(_BV(2)); + break; + default: + break; + } +} + +void Atreus::unselectRow(uint8_t row) { + switch (row) { + case 0: + DDRD &= ~(_BV(0)); + PORTD |= (_BV(0)); + break; + case 1: + DDRD &= ~(_BV(1)); + PORTD |= (_BV(1)); + break; + case 2: + DDRD &= ~(_BV(3)); + PORTD |= (_BV(3)); + break; + case 3: + DDRD &= ~(_BV(2)); + PORTD |= (_BV(2)); + break; + default: + break; + } +} + +uint16_t Atreus::readCols() { + return ((PINB & (_BV(4)) ? 0 : (_BV(0))) | + (PINB & (_BV(5)) ? 0 : (_BV(1))) | + (PINB & (_BV(6)) ? 0 : (_BV(2))) | + (PINB & (_BV(7)) ? 0 : (_BV(3))) | + (PINC & (_BV(6)) ? 0 : (_BV(4))) | + (PIND & (_BV(4)) ? 0 : (_BV(5))) | + (PIND & (_BV(6)) ? 0 : (_BV(6))) | + (PIND & (_BV(7)) ? 0 : (_BV(7))) | + (PINE & (_BV(6)) ? 0 : (_BV(8))) | + (PINF & (_BV(6)) ? 0 : (_BV(9))) | + (PINF & (_BV(7)) ? 0 : (_BV(10)))); +} + +void Atreus::readMatrixRow(uint8_t current_row) { + uint16_t mask, cols; + + previousKeyState_[current_row] = keyState_[current_row]; + + mask = debounceMaskForRow(current_row); + + selectRow(current_row); + cols = (readCols() & mask) | (keyState_[current_row] & ~mask); + unselectRow(current_row); + debounceRow(cols ^ keyState_[current_row], current_row); + keyState_[current_row] = cols; +} + +void Atreus::readMatrix() { + do_scan_ = false; + + for (uint8_t current_row = 0; current_row < ROWS; current_row++) { + readMatrixRow(current_row); + } +} + +void Atreus::actOnMatrixScan() { + for (byte row = 0; row < ROWS; row++) { + for (byte col = 0; col < COLS; col++) { + uint8_t keyState = (bitRead(previousKeyState_[row], col) << 0) | + (bitRead(keyState_[row], col) << 1); + if (keyState) { + handleKeyswitchEvent(Key_NoKey, row, col, keyState); + } + } + previousKeyState_[row] = keyState_[row]; + } +} + +void Atreus::scanMatrix() { + if (!do_scan_) + return; + + readMatrix(); + actOnMatrixScan(); +} + +void Atreus::maskKey(byte row, byte col) { + if (row >= ROWS || col >= COLS) + return; + + bitWrite(masks_[row], col, 1); +} + +void Atreus::unMaskKey(byte row, byte col) { + if (row >= ROWS || col >= COLS) + return; + + bitWrite(masks_[row], col, 0); +} + +bool Atreus::isKeyMasked(byte row, byte col) { + if (row >= ROWS || col >= COLS) + return false; + + return bitRead(masks_[row], col); +} + +// Atreus-specific stuff + +void Atreus::resetDevice() { + cli(); + UDCON = 1; + USBCON = (1 << FRZCLK); + UCSR1B = 0; + _delay_ms(5); + + EIMSK = 0; + PCICR = 0; + SPCR = 0; + ACSR = 0; + EECR = 0; + ADCSRA = 0; + TIMSK0 = 0; + TIMSK1 = 0; + TIMSK3 = 0; + TIMSK4 = 0; + UCSR1B = 0; + TWCR = 0; + DDRB = 0; + DDRC = 0; + DDRD = 0; + DDRE = 0; + DDRF = 0; + TWCR = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + PORTE = 0; + PORTF = 0; + asm volatile("jmp 0x7E00"); +} + +void Atreus::detachFromHost() { + UDCON |= (1 << DETACH); +} + +void Atreus::attachToHost() { + UDCON &= ~(1 << DETACH); +} + +bool Atreus::isKeyswitchPressed(byte row, byte col) { + return (bitRead(keyState_[row], col) != 0); +} + +bool Atreus::isKeyswitchPressed(uint8_t keyIndex) { + keyIndex--; + return isKeyswitchPressed(keyIndex / COLS, keyIndex % COLS); +} + +uint8_t Atreus::pressedKeyswitchCount() { + uint8_t count = 0; + + for (uint8_t r = 0; r < ROWS; r++) { + count += __builtin_popcount(keyState_[r]); + } + return count; +} + +uint16_t Atreus::debounceMaskForRow(uint8_t row) { + uint16_t result = 0; + + for (uint16_t c = 0; c < COLS; ++c) { + if (debounce_matrix_[row][c]) { + --debounce_matrix_[row][c]; + } else { + result |= (1 << c); + } + } + return result; +} + +void Atreus::debounceRow(uint16_t change, uint8_t row) { + for (uint16_t i = 0; i < COLS; ++i) { + if (change & (1 << i)) { + debounce_matrix_[row][i] = debounce; + } + } +} + +} +} + +HARDWARE_IMPLEMENTATION KeyboardHardware; +kaleidoscope::hardware::Atreus &Atreus = KeyboardHardware; + +#endif diff --git a/src/kaleidoscope/hardware/Atreus.h b/src/kaleidoscope/hardware/Atreus.h new file mode 100644 index 00000000..744ada74 --- /dev/null +++ b/src/kaleidoscope/hardware/Atreus.h @@ -0,0 +1,240 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Technomancy-Atreus -- Atreus hardware support for Kaleidoscope + * Copyright (C) 2018 Keyboard.io, Inc + * + * Based on QMK (commit e9a67f8fd) + * (C) Jack Humbert, Jun Wako, Phil Hagelberg, and others + * Original QMK sources: + * - keyboards/atreus/atreus.h + * - keyboards/atreus/config.h + * + * 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 3 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 . + */ + +#pragma once + +#ifdef ARDUINO_AVR_ATREUS + +#include + +#define HARDWARE_IMPLEMENTATION kaleidoscope::hardware::Atreus +#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" + +#include "kaleidoscope/macro_helpers.h" + +struct cRGB { + uint8_t r, g, b; +}; + +#define COLS 11 +#define ROWS 4 +#define LED_COUNT 0 + +#define CRGB(r,g,b) (cRGB){b, g, r} + +namespace kaleidoscope { +namespace hardware { + +class Atreus { + public: + Atreus(void) {} + + void syncLeds(void) {} + void setCrgbAt(byte row, byte col, cRGB color) {} + void setCrgbAt(uint8_t i, cRGB crgb) {} + cRGB getCrgbAt(uint8_t i) { + return CRGB(0, 0, 0); + } + uint8_t getLedIndex(byte row, byte col) { + return 0; + } + + void scanMatrix(void); + void readMatrix(void); + void actOnMatrixScan(void); + void setup(); + + /** Detaching from / attaching to the host. + * + * These two functions should detach the device from (or attach it to) the + * host, preferably without rebooting the device. Their purpose is to allow + * one to do some configuration inbetween, so the re-attach happens with + * different properties. The device remains powered between these operations, + * only the connection to the host gets severed. + */ + void detachFromHost(); + void attachToHost(); + + /* Key masking + * ----------- + * + * There are situations when one wants to ignore key events for a while, and + * mask them out. These functions help do that. In isolation, they do nothing, + * plugins and the core firmware is expected to make use of these. + * + * See `handleKeyswitchEvent` in the Kaleidoscope sources for a use-case. + */ + void maskKey(byte row, byte col); + void unMaskKey(byte row, byte col); + bool isKeyMasked(byte row, byte col); + + /** Key switch states + * + * These methods offer a way to peek at the key switch states, for those cases + * where we need to deal with the state closest to the hardware. Some methods + * offer a way to check if a key is pressed, others return the number of + * pressed keys. + */ + /** + * Check if a key is pressed at a given position. + * + * @param row is the row the key is located at in the matrix. + * @param col is the column the key is located at in the matrix. + * + * @returns true if the key is pressed, false otherwise. + */ + bool isKeyswitchPressed(byte row, byte col); + /** + * Check if a key is pressed at a given position. + * + * @param keyIndex is the key index, as calculated by `keyIndex`. + * + * @note Key indexes start at 1, not 0! + * + * @returns true if the key is pressed, false otherwise. + */ + bool isKeyswitchPressed(uint8_t keyIndex); + /** + * Check the number of key switches currently pressed. + * + * @returns the number of keys pressed. + */ + uint8_t pressedKeyswitchCount(); + + void resetDevice(); + + static uint8_t debounce; + + private: + static uint16_t previousKeyState_[ROWS]; + static uint16_t keyState_[ROWS]; + static uint16_t masks_[ROWS]; + + static void readMatrixRow(uint8_t row); + static uint16_t readCols(); + static void selectRow(uint8_t row); + static void unselectRow(uint8_t row); + + static uint8_t debounce_matrix_[ROWS][COLS]; + static uint16_t debounceMaskForRow(uint8_t row); + static void debounceRow(uint16_t change, uint8_t row); +}; + +#define KEYMAP( \ + R0C0, R0C1, R0C2, R0C3, R0C4, R0C7, R0C8, R0C9, R0C10, R0C11, \ + R1C0, R1C1, R1C2, R1C3, R1C4, R1C7, R1C8, R1C9, R1C10, R1C11, \ + R2C0, R2C1, R2C2, R2C3, R2C4, R2C7, R2C8, R2C9, R2C10, R2C11, \ + R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, R3C6, R3C7, R3C8, R3C9, R3C10, R3C11 \ + ) \ + { \ + { R0C3, R0C2, R0C7, R0C11, R0C1, XXX, R0C10, R0C0, R0C4, R0C8, R0C9 }, \ + { R1C3, R1C2, R1C7, R1C11, R1C1, XXX, R1C10, R1C0, R1C4, R1C8, R1C9 }, \ + { R2C3, R2C2, R2C7, R2C11, R2C1, R3C5, R2C10, R2C0, R2C4, R2C8, R2C9 }, \ + { R3C3, R3C2, R3C7, R3C11, R3C1, R3C6, R3C10, R3C0, R3C4, R3C8, R3C9 } \ + } + +#define KEYMAP_STACKED( \ + R0C0, R0C1, R0C2, R0C3, R0C4, \ + R1C0, R1C1, R1C2, R1C3, R1C4, \ + R2C0, R2C1, R2C2, R2C3, R2C4, \ + R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, \ + \ + R0C7, R0C8, R0C9, R0C10, R0C11, \ + R1C7, R1C8, R1C9, R1C10, R1C11, \ + R2C7, R2C8, R2C9, R2C10, R2C11, \ + R3C6, R3C7, R3C8, R3C9, R3C10, R3C11 \ + ) \ + { \ + { R0C3, R0C2, R0C7, R0C11, R0C1, XXX, R0C10, R0C0, R0C4, R0C8, R0C9 }, \ + { R1C3, R1C2, R1C7, R1C11, R1C1, XXX, R1C10, R1C0, R1C4, R1C8, R1C9 }, \ + { R2C3, R2C2, R2C7, R2C11, R2C1, R3C5, R2C10, R2C0, R2C4, R2C8, R2C9 }, \ + { R3C3, R3C2, R3C7, R3C11, R3C1, R3C6, R3C10, R3C0, R3C4, R3C8, R3C9 } \ + } +} +} + +/* To be used by the hardware implementations, `keyIndex` tells us the index of + * a key, from which we can figure out the row and column as needed. The index + * starts at one, so that plugins that work with a list of key indexes can use + * zero as a sentinel. This is important, because when we initialize arrays with + * fewer elements than the declared array size, the remaining elements will be + * zero. We can use this to avoid having to explicitly add a sentinel in + * user-facing code. + */ +constexpr byte keyIndex(byte row, byte col) { + return row * COLS + col + 1; +} + +constexpr byte R0C0 = keyIndex(0, 0); +constexpr byte R0C1 = keyIndex(0, 1); +constexpr byte R0C2 = keyIndex(0, 2); +constexpr byte R0C3 = keyIndex(0, 3); +constexpr byte R0C4 = keyIndex(0, 4); + +constexpr byte R0C7 = keyIndex(0, 7); +constexpr byte R0C8 = keyIndex(0, 8); +constexpr byte R0C9 = keyIndex(0, 9); +constexpr byte R0C10 = keyIndex(0, 10); +constexpr byte R0C11 = keyIndex(0, 11); + +constexpr byte R1C0 = keyIndex(1, 0); +constexpr byte R1C1 = keyIndex(1, 1); +constexpr byte R1C2 = keyIndex(1, 2); +constexpr byte R1C3 = keyIndex(1, 3); +constexpr byte R1C4 = keyIndex(1, 4); + +constexpr byte R1C7 = keyIndex(1, 7); +constexpr byte R1C8 = keyIndex(1, 8); +constexpr byte R1C9 = keyIndex(1, 9); +constexpr byte R1C10 = keyIndex(1, 10); +constexpr byte R1C11 = keyIndex(1, 11); + +constexpr byte R2C0 = keyIndex(2, 0); +constexpr byte R2C1 = keyIndex(2, 1); +constexpr byte R2C2 = keyIndex(2, 2); +constexpr byte R2C3 = keyIndex(2, 3); +constexpr byte R2C4 = keyIndex(2, 4); + +constexpr byte R2C7 = keyIndex(2, 7); +constexpr byte R2C8 = keyIndex(2, 8); +constexpr byte R2C9 = keyIndex(2, 9); +constexpr byte R2C10 = keyIndex(2, 10); +constexpr byte R2C11 = keyIndex(2, 11); + +constexpr byte R3C0 = keyIndex(3, 0); +constexpr byte R3C1 = keyIndex(3, 1); +constexpr byte R3C2 = keyIndex(3, 2); +constexpr byte R3C3 = keyIndex(3, 3); +constexpr byte R3C4 = keyIndex(3, 4); +constexpr byte R3C5 = keyIndex(3, 5); +constexpr byte R3C6 = keyIndex(3, 6); +constexpr byte R3C7 = keyIndex(3, 7); +constexpr byte R3C8 = keyIndex(3, 8); +constexpr byte R3C9 = keyIndex(3, 9); +constexpr byte R3C10 = keyIndex(3, 10); +constexpr byte R3C11 = keyIndex(3, 11); + +extern kaleidoscope::hardware::Atreus &Atreus; + +#endif diff --git a/src/kaleidoscope/hardware/ErgoDox.cpp b/src/kaleidoscope/hardware/ErgoDox.cpp new file mode 100644 index 00000000..706e3a61 --- /dev/null +++ b/src/kaleidoscope/hardware/ErgoDox.cpp @@ -0,0 +1,254 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope + * Copyright (C) 2018 Keyboard.io, Inc + * + * Based on QMK (commit 8cdb4a915) + * (C) Jack Humbert, Erez Zukerman, Oleg Kostyuk + * Original QMK sources: + * - keyboards/ergodox_ez/ergodox_ez.c + * - keyboards/ergodox_ez/ergodox_ez.h + * - keyboards/ergodox_ez/matrix.c + * - tmk_core/common/avr/bootloader.c + * + * 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 3 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 . + */ + +#ifdef ARDUINO_AVR_ERGODOX + +#include +#include +#include +#include "kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h" + +namespace kaleidoscope { +namespace hardware { + +ErgoDoxScanner ErgoDox::scanner_; +uint8_t ErgoDox::previousKeyState_[ROWS]; +uint8_t ErgoDox::keyState_[ROWS]; +uint8_t ErgoDox::masks_[ROWS]; +uint8_t ErgoDox::debounce_matrix_[ROWS][COLS]; +uint8_t ErgoDox::debounce = 5; + +static bool do_scan_ = 1; + +void ErgoDox::setup(void) { + wdt_disable(); + delay(100); + + TCCR1A = 0b10101001; + TCCR1B = 0b00001001; + + DDRB &= ~(1 << 4); + PORTB &= ~(1 << 4); + + DDRC &= ~(1 << 7); + DDRD &= ~(1 << 5 | 1 << 4); + DDRE &= ~(1 << 6); + PORTC |= (1 << 7); + PORTD |= (1 << 5 | 1 << 4); + PORTE |= (1 << 6); + + scanner_.begin(); + + setStatusLEDBrightness(1, 15); + setStatusLEDBrightness(2, 15); + setStatusLEDBrightness(3, 15); + + /* Set up Timer1 for 500usec */ + TCCR1B = _BV(WGM13); + TCCR1A = 0; + + const uint32_t cycles = (F_CPU / 2000000) * 500; + + ICR1 = cycles; + TCCR1B = _BV(WGM13) | _BV(CS10); + TIMSK1 = _BV(TOIE1); +} + +ISR(TIMER1_OVF_vect) { + do_scan_ = true; +} + +void ErgoDox::readMatrixRow(uint8_t row) { + uint8_t mask, cols; + + previousKeyState_[row] = keyState_[row]; + mask = debounceMaskForRow(row); + cols = (scanner_.readCols(row) & mask) | (keyState_[row] & ~mask); + debounceRow(cols ^ keyState_[row], row); + keyState_[row] = cols; +} + +void ErgoDox::readMatrix() { + do_scan_ = false; + + scanner_.reattachExpanderOnError(); + + for (uint8_t row = 0; row < ROWS / 2; row++) { + scanner_.selectRow(row); + scanner_.selectRow(row + ROWS / 2); + + readMatrixRow(row); + readMatrixRow(row + ROWS / 2); + + scanner_.unselectRows(); + } +} + +void ErgoDox::actOnMatrixScan() { + for (byte row = 0; row < ROWS; row++) { + for (byte col = 0; col < COLS; col++) { + uint8_t keyState = (bitRead(previousKeyState_[row], col) << 0) | + (bitRead(keyState_[row], col) << 1); + if (keyState) + handleKeyswitchEvent(Key_NoKey, row, col, keyState); + } + } +} + +void ErgoDox::scanMatrix() { + if (!do_scan_) + return; + + readMatrix(); + actOnMatrixScan(); +} + +void ErgoDox::maskKey(byte row, byte col) { + if (row >= ROWS || col >= COLS) + return; + + bitWrite(masks_[row], col, 1); +} + +void ErgoDox::unMaskKey(byte row, byte col) { + if (row >= ROWS || col >= COLS) + return; + + bitWrite(masks_[row], col, 0); +} + +bool ErgoDox::isKeyMasked(byte row, byte col) { + if (row >= ROWS || col >= COLS) + return false; + + return bitRead(masks_[row], col); +} + +// ErgoDox-specific stuff +void ErgoDox::setStatusLED(uint8_t led, bool state) { + if (state) { + DDRB |= (1 << (led + 4)); + PORTB |= (1 << (led + 4)); + } else { + DDRB &= ~(1 << (led + 4)); + PORTB &= ~(1 << (led + 4)); + } +} + +void ErgoDox::setStatusLEDBrightness(uint8_t led, uint8_t brightness) { + (led == 1) ? (OCR1A = brightness) : + (led == 2) ? (OCR1B = brightness) : + (OCR1C = brightness); +} + +void ErgoDox::resetDevice() { + cli(); + UDCON = 1; + USBCON = (1 << FRZCLK); + UCSR1B = 0; + _delay_ms(5); + + EIMSK = 0; + PCICR = 0; + SPCR = 0; + ACSR = 0; + EECR = 0; + ADCSRA = 0; + TIMSK0 = 0; + TIMSK1 = 0; + TIMSK3 = 0; + TIMSK4 = 0; + UCSR1B = 0; + TWCR = 0; + DDRB = 0; + DDRC = 0; + DDRD = 0; + DDRE = 0; + DDRF = 0; + TWCR = 0; + PORTB = 0; + PORTC = 0; + PORTD = 0; + PORTE = 0; + PORTF = 0; + asm volatile("jmp 0x7E00"); +} + + +uint8_t ErgoDox::debounceMaskForRow(uint8_t row) { + uint8_t result = 0; + + for (uint8_t c = 0; c < COLS; ++c) { + if (debounce_matrix_[row][c]) { + --debounce_matrix_[row][c]; + } else { + result |= (1 << c); + } + } + return result; +} + +void ErgoDox::debounceRow(uint8_t change, uint8_t row) { + for (uint8_t i = 0; i < COLS; ++i) { + if (change & (1 << i)) { + debounce_matrix_[row][i] = debounce; + } + } +} + +void ErgoDox::detachFromHost() { + UDCON |= (1 << DETACH); +} + +void ErgoDox::attachToHost() { + UDCON &= ~(1 << DETACH); +} + +bool ErgoDox::isKeyswitchPressed(byte row, byte col) { + return (bitRead(keyState_[row], col) != 0); +} + +bool ErgoDox::isKeyswitchPressed(uint8_t keyIndex) { + keyIndex--; + return isKeyswitchPressed(keyIndex / COLS, keyIndex % COLS); +} + +uint8_t ErgoDox::pressedKeyswitchCount() { + uint8_t count = 0; + + for (uint8_t r = 0; r < ROWS; r++) { + count += __builtin_popcount(keyState_[r]); + } + return count; +} + +} +} + +HARDWARE_IMPLEMENTATION KeyboardHardware; +kaleidoscope::hardware::ErgoDox &ErgoDox = KeyboardHardware; + +#endif diff --git a/src/kaleidoscope/hardware/ErgoDox.h b/src/kaleidoscope/hardware/ErgoDox.h new file mode 100644 index 00000000..5fb36ee3 --- /dev/null +++ b/src/kaleidoscope/hardware/ErgoDox.h @@ -0,0 +1,294 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope + * Copyright (C) 2018 Keyboard.io, Inc + * + * Based on QMK (commit 8cdb4a915) + * (C) Jack Humbert, Erez Zukerman, Oleg Kostyuk + * Original QMK sources: + * - keyboards/ergodox_ez/ergodox_ez.c + * - keyboards/ergodox_ez/ergodox_ez.h + * - keyboards/ergodox_ez/matrix.c + * + * 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 3 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 . + */ + +#pragma once + +#ifdef ARDUINO_AVR_ERGODOX + +#include + +#include "kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h" + +#define HARDWARE_IMPLEMENTATION kaleidoscope::hardware::ErgoDox +#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" + +#include "kaleidoscope/macro_helpers.h" + +struct cRGB { + uint8_t r, g, b; +}; + +#define COLS 6 +#define ROWS 14 +#define LED_COUNT 0 + +#define CRGB(r,g,b) (cRGB){b, g, r} + +namespace kaleidoscope { +namespace hardware { + +class ErgoDox { + public: + ErgoDox(void) {} + + void syncLeds(void) {} + void setCrgbAt(byte row, byte col, cRGB color) {} + void setCrgbAt(uint8_t i, cRGB crgb) {} + cRGB getCrgbAt(uint8_t i) { + return CRGB(0, 0, 0); + } + uint8_t getLedIndex(byte row, byte col) { + return 0; + } + + void scanMatrix(void); + void readMatrix(void); + void actOnMatrixScan(void); + void setup(); + + /** Detaching from / attaching to the host. + * + * These two functions should detach the device from (or attach it to) the + * host, preferably without rebooting the device. Their purpose is to allow + * one to do some configuration inbetween, so the re-attach happens with + * different properties. The device remains powered between these operations, + * only the connection to the host gets severed. + */ + void detachFromHost(); + void attachToHost(); + + /* Key masking + * ----------- + * + * There are situations when one wants to ignore key events for a while, and + * mask them out. These functions help do that. In isolation, they do nothing, + * plugins and the core firmware is expected to make use of these. + * + * See `handleKeyswitchEvent` in the Kaleidoscope sources for a use-case. + */ + void maskKey(byte row, byte col); + void unMaskKey(byte row, byte col); + bool isKeyMasked(byte row, byte col); + + /** Key switch states + * + * These methods offer a way to peek at the key switch states, for those cases + * where we need to deal with the state closest to the hardware. Some methods + * offer a way to check if a key is pressed, others return the number of + * pressed keys. + */ + /** + * Check if a key is pressed at a given position. + * + * @param row is the row the key is located at in the matrix. + * @param col is the column the key is located at in the matrix. + * + * @returns true if the key is pressed, false otherwise. + */ + bool isKeyswitchPressed(byte row, byte col); + /** + * Check if a key is pressed at a given position. + * + * @param keyIndex is the key index, as calculated by `keyIndex`. + * + * @note Key indexes start at 1, not 0! + * + * @returns true if the key is pressed, false otherwise. + */ + bool isKeyswitchPressed(uint8_t keyIndex); + /** + * Check the number of key switches currently pressed. + * + * @returns the number of keys pressed. + */ + uint8_t pressedKeyswitchCount(); + + // ErgoDox-specific stuff + void setStatusLED(uint8_t led, bool state = true); + void setStatusLEDBrightness(uint8_t led, uint8_t brightness); + + void resetDevice(); + + static uint8_t debounce; + + private: + static ErgoDoxScanner scanner_; + static uint8_t previousKeyState_[ROWS]; + static uint8_t keyState_[ROWS]; + static uint8_t masks_[ROWS]; + static uint8_t debounce_matrix_[ROWS][COLS]; + + static uint8_t debounceMaskForRow(uint8_t row); + static void debounceRow(uint8_t change, uint8_t row); + static void readMatrixRow(uint8_t row); +}; + +#define KEYMAP_STACKED( \ + /* left hand, spatial positions */ \ + r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, \ + r1c0, r1c1, r1c2, r1c3, r1c4, r1c5, r1c6, \ + r2c0, r2c1, r2c2, r2c3, r2c4, r2c5, \ + r3c0, r3c1, r3c2, r3c3, r3c4, r3c5, r3c6, \ + r4c0, r4c1, r4c2, r4c3, r4c4, \ + r5c5, r5c6, \ + r5c4, \ + r5c3, r5c2, r5c1, \ + \ + /* right hand, spatial positions */ \ + r0c7, r0c8, r0c9, r0c10, r0c11, r0c12, r0c13, \ + r1c7, r1c8, r1c9, r1c10, r1c11, r1c12, r1c13, \ + r2c8, r2c9, r2c10, r2c11, r2c12, r2c13, \ + r3c7, r3c8, r3c9, r3c10, r3c11, r3c12, r3c13, \ + r4c9, r4c10, r4c11, r4c12, r4c13, \ + r5c7, r5c8, \ + r5c9, \ + r5c12, r5c11, r5c10 ) \ + \ + /* matrix positions */ \ + { \ + { r0c0, r1c0, r2c0, r3c0, r4c0, XXX }, \ + { r0c1, r1c1, r2c1, r3c1, r4c1, r5c1 }, \ + { r0c2, r1c2, r2c2, r3c2, r4c2, r5c2 }, \ + { r0c3, r1c3, r2c3, r3c3, r4c3, r5c3 }, \ + { r0c4, r1c4, r2c4, r3c4, r4c4, r5c4 }, \ + { r0c5, r1c5, r2c5, r3c5, XXX, r5c5 }, \ + { r0c6, r1c6, XXX, r3c6, XXX, r5c6 }, \ + \ + { r0c7, r1c7, XXX, r3c7, XXX, r5c7 }, \ + { r0c8, r1c8, r2c8, r3c8, XXX, r5c8 }, \ + { r0c9, r1c9, r2c9, r3c9, r4c9, r5c9 }, \ + { r0c10, r1c10, r2c10, r3c10, r4c10, r5c10 }, \ + { r0c11, r1c11, r2c11, r3c11, r4c11, r5c11 }, \ + { r0c12, r1c12, r2c12, r3c12, r4c12, r5c12 }, \ + { r0c13, r1c13, r2c13, r3c13, r4c13, XXX } \ + } +} +} + +/* To be used by the hardware implementations, `keyIndex` tells us the index of + * a key, from which we can figure out the row and column as needed. The index + * starts at one, so that plugins that work with a list of key indexes can use + * zero as a sentinel. This is important, because when we initialize arrays with + * fewer elements than the declared array size, the remaining elements will be + * zero. We can use this to avoid having to explicitly add a sentinel in + * user-facing code. + */ +constexpr byte keyIndex(byte row, byte col) { + return row * COLS + col + 1; +} + +constexpr byte R0C0 = keyIndex(0, 0); +constexpr byte R0C1 = keyIndex(0, 1); +constexpr byte R0C2 = keyIndex(0, 2); +constexpr byte R0C3 = keyIndex(0, 3); +constexpr byte R0C4 = keyIndex(0, 4); +constexpr byte R0C5 = keyIndex(0, 5); +constexpr byte R0C6 = keyIndex(0, 6); +constexpr byte R0C7 = keyIndex(0, 7); +constexpr byte R0C8 = keyIndex(0, 8); +constexpr byte R0C9 = keyIndex(0, 9); +constexpr byte R0C10 = keyIndex(0, 10); +constexpr byte R0C11 = keyIndex(0, 11); +constexpr byte R0C12 = keyIndex(0, 12); +constexpr byte R0C13 = keyIndex(0, 13); + +constexpr byte R1C0 = keyIndex(1, 0); +constexpr byte R1C1 = keyIndex(1, 1); +constexpr byte R1C2 = keyIndex(1, 2); +constexpr byte R1C3 = keyIndex(1, 3); +constexpr byte R1C4 = keyIndex(1, 4); +constexpr byte R1C5 = keyIndex(1, 5); +constexpr byte R1C6 = keyIndex(1, 6); +constexpr byte R1C7 = keyIndex(1, 7); +constexpr byte R1C8 = keyIndex(1, 8); +constexpr byte R1C9 = keyIndex(1, 9); +constexpr byte R1C10 = keyIndex(1, 10); +constexpr byte R1C11 = keyIndex(1, 11); +constexpr byte R1C12 = keyIndex(1, 12); +constexpr byte R1C13 = keyIndex(1, 13); + +constexpr byte R2C0 = keyIndex(2, 0); +constexpr byte R2C1 = keyIndex(2, 1); +constexpr byte R2C2 = keyIndex(2, 2); +constexpr byte R2C3 = keyIndex(2, 3); +constexpr byte R2C4 = keyIndex(2, 4); +constexpr byte R2C5 = keyIndex(2, 5); +constexpr byte R2C6 = keyIndex(2, 6); +constexpr byte R2C7 = keyIndex(2, 7); +constexpr byte R2C8 = keyIndex(2, 8); +constexpr byte R2C9 = keyIndex(2, 9); +constexpr byte R2C10 = keyIndex(2, 10); +constexpr byte R2C11 = keyIndex(2, 11); +constexpr byte R2C12 = keyIndex(2, 12); +constexpr byte R2C13 = keyIndex(2, 13); + +constexpr byte R3C0 = keyIndex(3, 0); +constexpr byte R3C1 = keyIndex(3, 1); +constexpr byte R3C2 = keyIndex(3, 2); +constexpr byte R3C3 = keyIndex(3, 3); +constexpr byte R3C4 = keyIndex(3, 4); +constexpr byte R3C5 = keyIndex(3, 5); +constexpr byte R3C6 = keyIndex(3, 6); +constexpr byte R3C7 = keyIndex(3, 7); +constexpr byte R3C8 = keyIndex(3, 8); +constexpr byte R3C9 = keyIndex(3, 9); +constexpr byte R3C10 = keyIndex(3, 10); +constexpr byte R3C11 = keyIndex(3, 11); +constexpr byte R3C12 = keyIndex(3, 12); +constexpr byte R3C13 = keyIndex(3, 13); + +constexpr byte R4C0 = keyIndex(4, 0); +constexpr byte R4C1 = keyIndex(4, 1); +constexpr byte R4C2 = keyIndex(4, 2); +constexpr byte R4C3 = keyIndex(4, 3); +constexpr byte R4C4 = keyIndex(4, 4); +constexpr byte R4C5 = keyIndex(4, 5); +constexpr byte R4C6 = keyIndex(4, 6); +constexpr byte R4C7 = keyIndex(4, 7); +constexpr byte R4C8 = keyIndex(4, 8); +constexpr byte R4C9 = keyIndex(4, 9); +constexpr byte R4C10 = keyIndex(4, 10); +constexpr byte R4C11 = keyIndex(4, 11); +constexpr byte R4C12 = keyIndex(4, 12); +constexpr byte R4C13 = keyIndex(4, 13); + +constexpr byte R5C0 = keyIndex(5, 0); +constexpr byte R5C1 = keyIndex(5, 1); +constexpr byte R5C2 = keyIndex(5, 2); +constexpr byte R5C3 = keyIndex(5, 3); +constexpr byte R5C4 = keyIndex(5, 4); +constexpr byte R5C5 = keyIndex(5, 5); +constexpr byte R5C6 = keyIndex(5, 6); +constexpr byte R5C7 = keyIndex(5, 7); +constexpr byte R5C8 = keyIndex(5, 8); +constexpr byte R5C9 = keyIndex(5, 9); +constexpr byte R5C10 = keyIndex(5, 10); +constexpr byte R5C11 = keyIndex(5, 11); +constexpr byte R5C12 = keyIndex(5, 12); +constexpr byte R5C13 = keyIndex(5, 13); + +extern kaleidoscope::hardware::ErgoDox &ErgoDox; + +#endif diff --git a/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.cpp b/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.cpp new file mode 100644 index 00000000..8969e042 --- /dev/null +++ b/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.cpp @@ -0,0 +1,206 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope + * Copyright (C) 2018 Keyboard.io, Inc + * + * Based on QMK (commit 8cdb4a915) + * (C) Jack Humbert, Erez Zukerman, Oleg Kostyuk + * Original QMK sources: + * - keyboards/ergodox_ez/ergodox_ez.c + * - keyboards/ergodox_ez/ergodox_ez.h + * - keyboards/ergodox_ez/matrix.c + * + * 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 3 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 . + */ + +#ifdef ARDUINO_AVR_ERGODOX + +#include "kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h" +#include +#include "kaleidoscope/hardware/ErgoDox/i2cmaster.h" + +#define I2C_ADDR 0b0100000 +#define I2C_ADDR_WRITE ( (I2C_ADDR<<1) | I2C_WRITE ) +#define I2C_ADDR_READ ( (I2C_ADDR<<1) | I2C_READ ) +#define IODIRA 0x00 +#define IODIRB 0x01 +#define GPPUA 0x0C +#define GPPUB 0x0D +#define GPIOA 0x12 +#define GPIOB 0x13 +#define OLATA 0x14 +#define OLATB 0x15 + +namespace kaleidoscope { +namespace hardware { + +uint8_t ErgoDoxScanner::initExpander() { + uint8_t status = 0x20; + + i2c_init(); + delay(1000); + + status = i2c_start(I2C_ADDR_WRITE); + if (status) + goto out; + status = i2c_write(IODIRA); + if (status) + goto out; + status = i2c_write(0b00000000); + if (status) + goto out; + status = i2c_write(0b00111111); + if (status) + goto out; + + i2c_stop(); + + status = i2c_start(I2C_ADDR_WRITE); + if (status) + goto out; + status = i2c_write(GPPUA); + if (status) + goto out; + status = i2c_write(0b00000000); + if (status) + goto out; + status = i2c_write(0b00111111); + if (status) + goto out; + +out: + i2c_stop(); + + return status; +} + +void +ErgoDoxScanner::initCols() { + DDRF &= ~(1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 1 | 1 << 0); + PORTF |= (1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 1 | 1 << 0); +} + +void +ErgoDoxScanner::begin() { + expander_error_ = initExpander(); + + unselectRows(); + initCols(); +} + +void +ErgoDoxScanner::selectRow(int row) { + if (row < 7) { + if (!expander_error_) { + expander_error_ = i2c_start(I2C_ADDR_WRITE); + if (expander_error_) + goto out; + expander_error_ = i2c_write(GPIOA); + if (expander_error_) + goto out; + expander_error_ = i2c_write(0xFF & ~(1 << row)); + if (expander_error_) + goto out; +out: + i2c_stop(); + } + } else { + switch (row) { + case 7: + DDRB |= (1 << 0); + PORTB &= ~(1 << 0); + break; + case 8: + DDRB |= (1 << 1); + PORTB &= ~(1 << 1); + break; + case 9: + DDRB |= (1 << 2); + PORTB &= ~(1 << 2); + break; + case 10: + DDRB |= (1 << 3); + PORTB &= ~(1 << 3); + break; + case 11: + DDRD |= (1 << 2); + PORTD &= ~(1 << 3); + break; + case 12: + DDRD |= (1 << 3); + PORTD &= ~(1 << 3); + break; + case 13: + DDRC |= (1 << 6); + PORTC &= ~(1 << 6); + break; + } + } +} + +void +ErgoDoxScanner::unselectRows() { + DDRB &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3); + PORTB &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3); + DDRD &= ~(1 << 2 | 1 << 3); + PORTD &= ~(1 << 2 | 1 << 3); + DDRC &= ~(1 << 6); + PORTC &= ~(1 << 6); +} + +uint8_t +ErgoDoxScanner::readCols(int row) { + if (row < 7) { + if (expander_error_) { + return 0; + } + uint8_t data = 0; + + expander_error_ = i2c_start(I2C_ADDR_WRITE); + if (expander_error_) + goto out; + expander_error_ = i2c_write(GPIOB); + if (expander_error_) + goto out; + expander_error_ = i2c_start(I2C_ADDR_READ); + if (expander_error_) + goto out; + + data = i2c_readNak(); + data = ~data; +out: + i2c_stop(); + return data; + } else { + return (~((PINF & 0x03) | ((PINF & 0xF0) >> 2))) & ~0b11000000; + } +} + +void +ErgoDoxScanner::reattachExpanderOnError() { + static uint32_t start_time = millis(); + + if (!expander_error_) + return; + + if (millis() - start_time < 5000) + return; + + expander_error_ = initExpander(); + start_time = millis(); +} + +} +} + +#endif diff --git a/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h b/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h new file mode 100644 index 00000000..a3c63f06 --- /dev/null +++ b/src/kaleidoscope/hardware/ErgoDox/ErgoDoxScanner.h @@ -0,0 +1,56 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope + * Copyright (C) 2018 Keyboard.io, Inc + * + * Based on QMK (commit 8cdb4a915) + * (C) Jack Humbert, Erez Zukerman, Oleg Kostyuk + * Original QMK sources: + * - keyboards/ergodox_ez/ergodox_ez.c + * - keyboards/ergodox_ez/ergodox_ez.h + * - keyboards/ergodox_ez/matrix.c + * + * 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 3 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 . + */ + +#pragma once + +#ifdef ARDUINO_AVR_ERGODOX + +#include + +namespace kaleidoscope { +namespace hardware { + +class ErgoDoxScanner { + public: + ErgoDoxScanner() {} + + void begin(); + void initCols(); + void selectRow(int row); + void unselectRows(); + uint8_t readCols(int row); + + void reattachExpanderOnError(); + + private: + uint8_t expander_error_ = 0x20; + + uint8_t initExpander(); +}; + +} +} + +#endif diff --git a/src/kaleidoscope/hardware/ErgoDox/i2cmaster.cpp b/src/kaleidoscope/hardware/ErgoDox/i2cmaster.cpp new file mode 100644 index 00000000..74018792 --- /dev/null +++ b/src/kaleidoscope/hardware/ErgoDox/i2cmaster.cpp @@ -0,0 +1,203 @@ +/************************************************************************* +* Title: I2C master library using hardware TWI interface +* Author: Peter Fleury http://jump.to/fleury +* File: $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $ +* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3 +* Target: any AVR device with hardware TWI +* Usage: API compatible with I2C Software Library i2cmaster.h +**************************************************************************/ + +#ifdef ARDUINO_AVR_ERGODOX + +#include +#include + +#include "kaleidoscope/hardware/ErgoDox/i2cmaster.h" + + +/* define CPU frequency in Mhz here if not defined in Makefile */ +#ifndef F_CPU +#define F_CPU 16000000UL +#endif + +/* I2C clock in Hz */ +#define SCL_CLOCK 400000L + + +/************************************************************************* + Initialization of the I2C bus interface. Need to be called only once +*************************************************************************/ +void i2c_init(void) { + /* initialize TWI clock + * minimal values in Bit Rate Register (TWBR) and minimal Prescaler + * bits in the TWI Status Register should give us maximal possible + * I2C bus speed - about 444 kHz + * + * for more details, see 20.5.2 in ATmega16/32 secification + */ + + TWSR = 0; /* no prescaler */ + TWBR = 10; /* must be >= 10 for stable operation */ + +}/* i2c_init */ + + +/************************************************************************* + Issues a start condition and sends address and transfer direction. + return 0 = device accessible, 1= failed to access device +*************************************************************************/ +unsigned char i2c_start(unsigned char address) { + uint8_t twst; + + // send START condition + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); + + // wait until transmission completed + while (!(TWCR & (1 << TWINT))); + + // check value of TWI Status Register. Mask prescaler bits. + twst = TW_STATUS & 0xF8; + if ((twst != TW_START) && (twst != TW_REP_START)) return 1; + + // send device address + TWDR = address; + TWCR = (1 << TWINT) | (1 << TWEN); + + // wail until transmission completed and ACK/NACK has been received + while (!(TWCR & (1 << TWINT))); + + // check value of TWI Status Register. Mask prescaler bits. + twst = TW_STATUS & 0xF8; + if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) return 1; + + return 0; + +}/* i2c_start */ + + +/************************************************************************* + Issues a start condition and sends address and transfer direction. + If device is busy, use ack polling to wait until device is ready + + Input: address and transfer direction of I2C device +*************************************************************************/ +void i2c_start_wait(unsigned char address) { + uint8_t twst; + + + while (1) { + // send START condition + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); + + // wait until transmission completed + while (!(TWCR & (1 << TWINT))); + + // check value of TWI Status Register. Mask prescaler bits. + twst = TW_STATUS & 0xF8; + if ((twst != TW_START) && (twst != TW_REP_START)) continue; + + // send device address + TWDR = address; + TWCR = (1 << TWINT) | (1 << TWEN); + + // wail until transmission completed + while (!(TWCR & (1 << TWINT))); + + // check value of TWI Status Register. Mask prescaler bits. + twst = TW_STATUS & 0xF8; + if ((twst == TW_MT_SLA_NACK) || (twst == TW_MR_DATA_NACK)) { + /* device busy, send stop condition to terminate write operation */ + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); + + // wait until stop condition is executed and bus released + while (TWCR & (1 << TWSTO)); + + continue; + } + //if( twst != TW_MT_SLA_ACK) return 1; + break; + } + +}/* i2c_start_wait */ + + +/************************************************************************* + Issues a repeated start condition and sends address and transfer direction + + Input: address and transfer direction of I2C device + + Return: 0 device accessible + 1 failed to access device +*************************************************************************/ +unsigned char i2c_rep_start(unsigned char address) { + return i2c_start(address); + +}/* i2c_rep_start */ + + +/************************************************************************* + Terminates the data transfer and releases the I2C bus +*************************************************************************/ +void i2c_stop(void) { + /* send stop condition */ + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); + + // wait until stop condition is executed and bus released + while (TWCR & (1 << TWSTO)); + +}/* i2c_stop */ + + +/************************************************************************* + Send one byte to I2C device + + Input: byte to be transfered + Return: 0 write successful + 1 write failed +*************************************************************************/ +unsigned char i2c_write(unsigned char data) { + uint8_t twst; + + // send data to the previously addressed device + TWDR = data; + TWCR = (1 << TWINT) | (1 << TWEN); + + // wait until transmission completed + while (!(TWCR & (1 << TWINT))); + + // check value of TWI Status Register. Mask prescaler bits + twst = TW_STATUS & 0xF8; + if (twst != TW_MT_DATA_ACK) return 1; + return 0; + +}/* i2c_write */ + + +/************************************************************************* + Read one byte from the I2C device, request more data from device + + Return: byte read from I2C device +*************************************************************************/ +unsigned char i2c_readAck(void) { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); + while (!(TWCR & (1 << TWINT))); + + return TWDR; + +}/* i2c_readAck */ + + +/************************************************************************* + Read one byte from the I2C device, read is followed by a stop condition + + Return: byte read from I2C device +*************************************************************************/ +unsigned char i2c_readNak(void) { + TWCR = (1 << TWINT) | (1 << TWEN); + while (!(TWCR & (1 << TWINT))); + + return TWDR; + +}/* i2c_readNak */ + +#endif diff --git a/src/kaleidoscope/hardware/ErgoDox/i2cmaster.h b/src/kaleidoscope/hardware/ErgoDox/i2cmaster.h new file mode 100644 index 00000000..65533c59 --- /dev/null +++ b/src/kaleidoscope/hardware/ErgoDox/i2cmaster.h @@ -0,0 +1,180 @@ +#pragma once +/************************************************************************* +* Title: C include file for the I2C master interface +* (i2cmaster.S or twimaster.c) +* Author: Peter Fleury http://jump.to/fleury +* File: $Id: i2cmaster.h,v 1.10 2005/03/06 22:39:57 Peter Exp $ +* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3 +* Target: any AVR device +* Usage: see Doxygen manual +**************************************************************************/ + +#ifdef ARDUINO_AVR_ERGODOX + +#ifdef DOXYGEN +/** + @defgroup pfleury_ic2master I2C Master library + @code #include @endcode + + @brief I2C (TWI) Master Software Library + + Basic routines for communicating with I2C slave devices. This single master + implementation is limited to one bus master on the I2C bus. + + This I2c library is implemented as a compact assembler software implementation of the I2C protocol + which runs on any AVR (i2cmaster.S) and as a TWI hardware interface for all AVR with built-in TWI hardware (twimaster.c). + Since the API for these two implementations is exactly the same, an application can be linked either against the + software I2C implementation or the hardware I2C implementation. + + Use 4.7k pull-up resistor on the SDA and SCL pin. + + Adapt the SCL and SDA port and pin definitions and eventually the delay routine in the module + i2cmaster.S to your target when using the software I2C implementation ! + + Adjust the CPU clock frequence F_CPU in twimaster.c or in the Makfile when using the TWI hardware implementaion. + + @note + The module i2cmaster.S is based on the Atmel Application Note AVR300, corrected and adapted + to GNU assembler and AVR-GCC C call interface. + Replaced the incorrect quarter period delays found in AVR300 with + half period delays. + + @author Peter Fleury pfleury@gmx.ch http://jump.to/fleury + + @par API Usage Example + The following code shows typical usage of this library, see example test_i2cmaster.c + + @code + + #include + + + #define Dev24C02 0xA2 // device address of EEPROM 24C02, see datasheet + + int main(void) + { + unsigned char ret; + + i2c_init(); // initialize I2C library + + // write 0x75 to EEPROM address 5 (Byte Write) + i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode + i2c_write(0x05); // write address = 5 + i2c_write(0x75); // write value 0x75 to EEPROM + i2c_stop(); // set stop conditon = release bus + + + // read previously written value back from EEPROM address 5 + i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode + + i2c_write(0x05); // write address = 5 + i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode + + ret = i2c_readNak(); // read one byte from EEPROM + i2c_stop(); + + for(;;); + } + @endcode + +*/ +#endif /* DOXYGEN */ + +/**@{*/ + +#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304 +#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !" +#endif + +#include + +/** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */ +#define I2C_READ 1 + +/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */ +#define I2C_WRITE 0 + + +/** + @brief initialize the I2C master interace. Need to be called only once + @param void + @return none + */ +extern void i2c_init(void); + + +/** + @brief Terminates the data transfer and releases the I2C bus + @param void + @return none + */ +extern void i2c_stop(void); + + +/** + @brief Issues a start condition and sends address and transfer direction + + @param addr address and transfer direction of I2C device + @retval 0 device accessible + @retval 1 failed to access device + */ +extern unsigned char i2c_start(unsigned char addr); + + +/** + @brief Issues a repeated start condition and sends address and transfer direction + + @param addr address and transfer direction of I2C device + @retval 0 device accessible + @retval 1 failed to access device + */ +extern unsigned char i2c_rep_start(unsigned char addr); + + +/** + @brief Issues a start condition and sends address and transfer direction + + If device is busy, use ack polling to wait until device ready + @param addr address and transfer direction of I2C device + @return none + */ +extern void i2c_start_wait(unsigned char addr); + + +/** + @brief Send one byte to I2C device + @param data byte to be transfered + @retval 0 write successful + @retval 1 write failed + */ +extern unsigned char i2c_write(unsigned char data); + + +/** + @brief read one byte from the I2C device, request more data from device + @return byte read from I2C device + */ +extern unsigned char i2c_readAck(void); + +/** + @brief read one byte from the I2C device, read is followed by a stop condition + @return byte read from I2C device + */ +extern unsigned char i2c_readNak(void); + +/** + @brief read one byte from the I2C device + + Implemented as a macro, which calls either i2c_readAck or i2c_readNak + + @param ack 1 send ack, request more data from device
+ 0 send nak, read is followed by a stop condition + @return byte read from I2C device + */ +extern unsigned char i2c_read(unsigned char ack); +#define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNak(); + + +/**@}*/ + +#endif