From d39a8dc46c4003c0532eb805bfcd919f29b31821 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 5 Nov 2019 14:08:06 +0100 Subject: [PATCH] Redesign of the device plugin APIs The new device APIs were built on top of composition (instead of inheritance, like the former one). At the highest level, we have `kaleidoscope::device::Base` and `kaleidoscope::device::BaseProps`. The latter is a set of overrideable properties, components that make up the device: the key scanner, LEDs, MCU, and so on. Many components - like the key scanner and LEDs - also come in a similar setup: the base class and properties, because this allows us to make them fairly efficient templates. All of the existing devices have been ported to the new APIs. While the old `Hardware` base class remains - for now, and deprecated - it is not guaranteed to work. Signed-off-by: Gergely Nagy --- doc/driver/Bootloader.md | 58 ++ doc/plugin/ATMegaKeyboard.md | 82 --- examples/Devices/KBDFans/KBD4x/KBD4x.ino | 2 +- .../Devices/Technomancy/Atreus/Atreus.ino | 2 +- src/Kaleidoscope-Hardware-KBDFans-KBD4x.h | 2 - src/Kaleidoscope-Hardware-OLKB-Planck.h | 1 - ...eidoscope-Hardware-SOFTHRUF-Splitography.h | 1 - ...Kaleidoscope-Hardware-Technomancy-Atreus.h | 2 - ...aleidoscope-Hardware-Technomancy-Atreus2.h | 2 - src/kaleidoscope/Hardware.h | 6 +- src/kaleidoscope/Kaleidoscope.h | 7 + src/kaleidoscope/device/ATMega32U4Keyboard.h | 59 ++ src/kaleidoscope/device/ATMegaKeyboard.cpp | 150 ------ src/kaleidoscope/device/ATMegaKeyboard.h | 166 ------ src/kaleidoscope/device/Base.h | 504 ++++++++++++++++++ src/kaleidoscope/device/ez/ErgoDox.cpp | 34 -- src/kaleidoscope/device/ez/ErgoDox.h | 43 +- src/kaleidoscope/device/kbdfans/KBD4x.cpp | 29 +- src/kaleidoscope/device/kbdfans/KBD4x.h | 39 +- .../device/keyboardio/Model01.cpp | 221 ++++---- src/kaleidoscope/device/keyboardio/Model01.h | 148 +++-- src/kaleidoscope/device/olkb/Planck.cpp | 4 +- src/kaleidoscope/device/olkb/Planck.h | 27 +- .../device/softhruf/Splitography.cpp | 4 +- .../device/softhruf/Splitography.h | 37 +- .../device/technomancy/Atreus.cpp | 40 +- src/kaleidoscope/device/technomancy/Atreus.h | 45 +- .../device/technomancy/Atreus2.cpp | 4 +- src/kaleidoscope/device/technomancy/Atreus2.h | 31 +- src/kaleidoscope/driver/bootloader/Base.h | 34 ++ src/kaleidoscope/driver/bootloader/None.h | 37 ++ .../driver/bootloader/avr/Caterina.h | 56 ++ .../driver/bootloader/avr/FLIP.cpp | 76 +++ src/kaleidoscope/driver/bootloader/avr/FLIP.h | 41 ++ .../driver/bootloader/avr/HalfKay.h | 72 +++ src/kaleidoscope/driver/keyscanner/AVR.h | 239 +++++++++ src/kaleidoscope/driver/keyscanner/Base.h | 82 +++ src/kaleidoscope/driver/keyscanner/None.h | 38 ++ src/kaleidoscope/driver/led/Base.h | 120 +++++ src/kaleidoscope/driver/led/None.h | 46 ++ .../driver/{MCU.h => mcu/ATMega32U4.h} | 19 +- src/kaleidoscope/driver/mcu/Base.h | 47 ++ src/kaleidoscope/driver/mcu/None.h | 36 ++ .../driver/storage/ATMega32U4EEPROMProps.h | 32 ++ src/kaleidoscope/driver/storage/AVREEPROM.h | 59 ++ src/kaleidoscope/driver/storage/Base.h | 59 ++ src/kaleidoscope/driver/storage/None.h | 36 ++ src/kaleidoscope/key_events.h | 15 +- src/kaleidoscope/plugin/DynamicMacros.cpp | 1 + .../plugin/EEPROM-Keymap-Programmer.cpp | 1 + src/kaleidoscope/plugin/EEPROM-Keymap.cpp | 1 + src/kaleidoscope/plugin/EEPROM-Settings.cpp | 2 + src/kaleidoscope/plugin/FingerPainter.cpp | 1 + src/kaleidoscope/plugin/HostOS.cpp | 1 + src/kaleidoscope/plugin/LED-Palette-Theme.cpp | 3 + src/kaleidoscope/plugin/LEDControl.cpp | 16 +- src/kaleidoscope/plugin/LEDControl.h | 4 +- src/kaleidoscope/plugin/LEDEffect-Chase.cpp | 12 +- src/kaleidoscope/plugin/LEDEffect-Chase.h | 2 +- src/kaleidoscope/plugin/LEDEffect-Rainbow.cpp | 14 +- src/kaleidoscope/plugin/TypingBreaks.cpp | 2 + src/kaleidoscope_internal/deprecations.h | 7 + 62 files changed, 2060 insertions(+), 901 deletions(-) create mode 100644 doc/driver/Bootloader.md delete mode 100644 doc/plugin/ATMegaKeyboard.md create mode 100644 src/kaleidoscope/device/ATMega32U4Keyboard.h delete mode 100644 src/kaleidoscope/device/ATMegaKeyboard.cpp delete mode 100644 src/kaleidoscope/device/ATMegaKeyboard.h create mode 100644 src/kaleidoscope/device/Base.h create mode 100644 src/kaleidoscope/driver/bootloader/Base.h create mode 100644 src/kaleidoscope/driver/bootloader/None.h create mode 100644 src/kaleidoscope/driver/bootloader/avr/Caterina.h create mode 100644 src/kaleidoscope/driver/bootloader/avr/FLIP.cpp create mode 100644 src/kaleidoscope/driver/bootloader/avr/FLIP.h create mode 100644 src/kaleidoscope/driver/bootloader/avr/HalfKay.h create mode 100644 src/kaleidoscope/driver/keyscanner/AVR.h create mode 100644 src/kaleidoscope/driver/keyscanner/Base.h create mode 100644 src/kaleidoscope/driver/keyscanner/None.h create mode 100644 src/kaleidoscope/driver/led/Base.h create mode 100644 src/kaleidoscope/driver/led/None.h rename src/kaleidoscope/driver/{MCU.h => mcu/ATMega32U4.h} (83%) create mode 100644 src/kaleidoscope/driver/mcu/Base.h create mode 100644 src/kaleidoscope/driver/mcu/None.h create mode 100644 src/kaleidoscope/driver/storage/ATMega32U4EEPROMProps.h create mode 100644 src/kaleidoscope/driver/storage/AVREEPROM.h create mode 100644 src/kaleidoscope/driver/storage/Base.h create mode 100644 src/kaleidoscope/driver/storage/None.h diff --git a/doc/driver/Bootloader.md b/doc/driver/Bootloader.md new file mode 100644 index 00000000..70bbeb56 --- /dev/null +++ b/doc/driver/Bootloader.md @@ -0,0 +1,58 @@ +# kaleidoscope::driver::bootloader + +We rarely have to work with or care about the bootloader from the user firmware, +but there's one scenario when we do: if we want to reset the device, and put it +into bootloader (programmable) mode, we need to do that in a bootloader-specific +manner. + +This driver provides a number of helpers that implement the reset functionality +for various bootloaders. + +## Using the driver + +To use the driver, we need to include the appropriate header, from the hardware plugin of +our keyboard: + +```c++ +#include +``` + +Next, we set up the device `Properties` so that it includes the override for our +bootloader: + +```c++ +struct OurBoardProps : kaleidoscope::device::BaseProps { + typedef kaleidoscope::driver::bootloader::avr::Caterina Bootloader; +}; +``` + +The base classes will do all the rest. + +## Methods provided by all bootloader drivers + +### `.rebootBootloader()` + +> Resets the device, and forces it into bootloader (programmable) mode. + +## List of bootloaders + +All of the drivers below live below the `kaleidoscope::driver::bootloader` +namespace. + +## `avr::Caterina`: + +Used by many (most?) arduino MCUs. Provided by +`kaleidoscope/driver/bootloader/avr/Caterina.h`. + +### `avr::HalfKay` + +Used by the Teensy2. Provided by `kaleidoscope/driver/bootloader/avr/HalfKay.h`. + +### `avr::FLIP` + +Used by the ATMega32U4 MCUs by default, unless another bootloader has been +flashed on them. Provided by `kaleidoscope/driver/bootloader/avr/FLIP.h`. + +For this driver to work, one also needs to define the +`KALEIDOSCOPE_BOOTLOADER_FLIP_WORKAROUND` macro before including the driver +header, for technical reasons. diff --git a/doc/plugin/ATMegaKeyboard.md b/doc/plugin/ATMegaKeyboard.md deleted file mode 100644 index e0697338..00000000 --- a/doc/plugin/ATMegaKeyboard.md +++ /dev/null @@ -1,82 +0,0 @@ -# ATMegaKeyboard - -A lot of custom keyboards are built upon the ATMega MCU (most often an -`atmega32u4`), and the vast majority of them follow a similar architecture, so -much so that the vast majority of code can be lifted out into a base class, -making porting to these keyboards trivial. All we have to do is tell it our -pinout, and we're all done. - -## Porting hardware using the ATMegaKeyboard class - -The plugin assumes a simple matrix layout, which we can tell it by using the -`ATMEGA_KEYBOARD_CONFIG` macro, which takes two arguments: a list of row and -column pins. In the `.cpp`, simply use `ATMEGA_KEYBOARD_DATA`, with the hardware -object as its argument: - -```c++ -// Kaleidoscope-Vendor-ExampleHardware.h -#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1 -#include "kaleidoscope/hardware/vendor/ExampleHardware.h" - -// kaleidoscope/hardware/vendor/ExampleHardware.h -namespace kaleidoscope { -namespace hardware { -namespace vendor { -class ExampleKeyboard: public ATMegaKeyboard { - public: - ExampleKeyboard() {} - - ATMEGA_KEYBOARD_CONFIG( - ROW_PIN_LIST({PIN_D1, PIN_D2}), - COL_PIN_LIST({PIN_F6, PIN_F7}) - ); - - static constexpr int8_t led_count = 0; -}; - -#define KEYMAP( \ - r0c0 ,r0c1 \ - ,r1c0 ,r1c1 \ -) \ - { r0c0 ,r0c1 }, \ - { r1c0 ,r1c1 } - -} -} -} - -// kaleidoscope/hardware/vendor/ExampleHardware.cpp -namespace kaleidoscope { -namespace hardware { -namespace vendor { - -ATMEGA_KEYBOARD_DATA(ExampleHardware); -constexpr int8_t ExampleHardware::led_count; - -} -} -} - -HARDWARE_IMPLEMENTATION KeyboardHardware; -kaleidoscope::hardware::vendor::ExampleHardware &ExampleHardware = KeyboardHardware; -``` - -## Overriding methods - -For performance and space-saving reasons, the base class does not use virtual -methods. Instead, whenever it calls a method of its own, it will call it through -the `KeyboardHardware` singleton object, which is always an instance of the -exact hardware plugin. Thus, implementing a function in the subclass will shadow -the one in the base class. - -This can be used to implement more efficient methods, would it be needed. The -[Atreus][hardware:atreus] port does this, for example, by overriding the generic -`readCols` with a more specialised, faster implementation. - -## Further reading - -See the [Planck][hardware:planck] and [Atreus][hardware:atreus] ports for an -example of how this class can be used in practice. - - [hardware:planck]: ../../src/kaleidoscope/hardware/olkb/Planck.h - [hardware:atreus]: ../../src/kaleidoscope/hardware/technomancy/Atreus.h diff --git a/examples/Devices/KBDFans/KBD4x/KBD4x.ino b/examples/Devices/KBDFans/KBD4x/KBD4x.ino index bb7d433d..91af5f75 100644 --- a/examples/Devices/KBDFans/KBD4x/KBD4x.ino +++ b/examples/Devices/KBDFans/KBD4x/KBD4x.ino @@ -78,7 +78,7 @@ KALEIDOSCOPE_INIT_PLUGINS(Macros); const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) { switch (macroIndex) { case RESET: - Kaleidoscope.device().resetDevice(); + Kaleidoscope.rebootBootloader(); break; default: break; diff --git a/examples/Devices/Technomancy/Atreus/Atreus.ino b/examples/Devices/Technomancy/Atreus/Atreus.ino index 72fda7f8..fcb3b3e4 100644 --- a/examples/Devices/Technomancy/Atreus/Atreus.ino +++ b/examples/Devices/Technomancy/Atreus/Atreus.ino @@ -83,7 +83,7 @@ KALEIDOSCOPE_INIT_PLUGINS(Macros); const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) { switch (macroIndex) { case RESET: - Kaleidoscope.device().resetDevice(); + Kaleidoscope.rebootBootloader(); break; default: break; diff --git a/src/Kaleidoscope-Hardware-KBDFans-KBD4x.h b/src/Kaleidoscope-Hardware-KBDFans-KBD4x.h index 2fbc0fce..cc4a0c59 100644 --- a/src/Kaleidoscope-Hardware-KBDFans-KBD4x.h +++ b/src/Kaleidoscope-Hardware-KBDFans-KBD4x.h @@ -18,6 +18,4 @@ #pragma once -#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1 - #include "kaleidoscope/device/kbdfans/KBD4x.h" diff --git a/src/Kaleidoscope-Hardware-OLKB-Planck.h b/src/Kaleidoscope-Hardware-OLKB-Planck.h index f18a1811..1ebfc961 100644 --- a/src/Kaleidoscope-Hardware-OLKB-Planck.h +++ b/src/Kaleidoscope-Hardware-OLKB-Planck.h @@ -18,5 +18,4 @@ #pragma once -#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1 #include "kaleidoscope/device/olkb/Planck.h" diff --git a/src/Kaleidoscope-Hardware-SOFTHRUF-Splitography.h b/src/Kaleidoscope-Hardware-SOFTHRUF-Splitography.h index cd02b5f9..94e79bba 100644 --- a/src/Kaleidoscope-Hardware-SOFTHRUF-Splitography.h +++ b/src/Kaleidoscope-Hardware-SOFTHRUF-Splitography.h @@ -18,5 +18,4 @@ #pragma once -#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1 #include "kaleidoscope/device/softhruf/Splitography.h" diff --git a/src/Kaleidoscope-Hardware-Technomancy-Atreus.h b/src/Kaleidoscope-Hardware-Technomancy-Atreus.h index c4958a7d..ae7340d8 100644 --- a/src/Kaleidoscope-Hardware-Technomancy-Atreus.h +++ b/src/Kaleidoscope-Hardware-Technomancy-Atreus.h @@ -18,8 +18,6 @@ #pragma once -#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1 - #if !defined(KALEIDOSCOPE_HARDWARE_ATREUS_PINOUT_ASTAR) && \ !defined(KALEIDOSCOPE_HARDWARE_ATREUS_PINOUT_ASTAR_DOWN) && \ !defined(KALEIDOSCOPE_HARDWARE_ATREUS_PINOUT_LEGACY_TEENSY2) diff --git a/src/Kaleidoscope-Hardware-Technomancy-Atreus2.h b/src/Kaleidoscope-Hardware-Technomancy-Atreus2.h index b6b67b67..1416d6d5 100644 --- a/src/Kaleidoscope-Hardware-Technomancy-Atreus2.h +++ b/src/Kaleidoscope-Hardware-Technomancy-Atreus2.h @@ -18,6 +18,4 @@ #pragma once -#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1 - #include "kaleidoscope/device/technomancy/Atreus2.h" diff --git a/src/kaleidoscope/Hardware.h b/src/kaleidoscope/Hardware.h index 6fcbdee0..7d846954 100644 --- a/src/kaleidoscope/Hardware.h +++ b/src/kaleidoscope/Hardware.h @@ -31,8 +31,6 @@ #endif /* All hardware libraries must define the following macros: - * HARDWARE_IMPLEMENTATION - the name of your public object conforming to - * the 'class Hardware' interface below. * CRGB(r,g,b) - explained below * cRGB, a structure with at least three members: r, g, and b - * compilation will fail otherwise. @@ -263,7 +261,7 @@ class Hardware { * * Must restore the link detachFromHost severed. */ - void attachToHost() { + DEPRECATED(HARDWARE_BASE_CLASS) void attachToHost() { UDCON &= ~_BV(DETACH); } /** @} */ @@ -376,7 +374,7 @@ class Hardware { * Called once when the device boots, this should initialize the device, and * bring it up into a useful state. */ - void setup() {} + DEPRECATED(HARDWARE_BASE_CLASS) void setup() {} /** * Method to configure the device for a hardware test mode diff --git a/src/kaleidoscope/Kaleidoscope.h b/src/kaleidoscope/Kaleidoscope.h index eca0f9ae..4237c65e 100644 --- a/src/kaleidoscope/Kaleidoscope.h +++ b/src/kaleidoscope/Kaleidoscope.h @@ -139,6 +139,9 @@ class Kaleidoscope_ { * * The next three functions wrap methods of the device plugin, to make using * them in user sketches easier, and require less typing. + * + * For what they do, please see the documentation in the + * `kaleidoscope::device::Base` class. */ auto serialPort() -> decltype(device().serialPort()) & { return device().serialPort(); @@ -148,6 +151,10 @@ class Kaleidoscope_ { return device().storage(); } + void rebootBootloader() { + device().rebootBootloader(); + } + /** Returns the timer as it was at the start of the cycle. * The goal of this method is two-fold: * - To reduce the amount of calls to millis(), providing something cheaper. diff --git a/src/kaleidoscope/device/ATMega32U4Keyboard.h b/src/kaleidoscope/device/ATMega32U4Keyboard.h new file mode 100644 index 00000000..f3cc8882 --- /dev/null +++ b/src/kaleidoscope/device/ATMega32U4Keyboard.h @@ -0,0 +1,59 @@ +/* -*- mode: c++ -*- + * device::ATMega32U4Keyboard -- Generic ATMega32U4 keyboard base class + * Copyright (C) 2019 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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 __AVR__ + +#include +#include "kaleidoscope/device/Base.h" + +#include "kaleidoscope/driver/mcu/ATMega32U4.h" +#include "kaleidoscope/driver/storage/ATMega32U4EEPROMProps.h" +#include "kaleidoscope/driver/storage/AVREEPROM.h" + +#define ATMEGA32U4_KEYBOARD(BOARD_, BOOTLOADER_, ROW_PINS_, COL_PINS_) \ + struct BOARD_##Props : kaleidoscope::device::ATMega32U4KeyboardProps { \ + struct KeyScannerProps : public kaleidoscope::driver::keyscanner::AVRProps { \ + AVR_KEYSCANNER_PROPS(ROW_PIN_LIST(ROW_PINS_), COL_PIN_LIST(COL_PINS_)); \ + }; \ + typedef kaleidoscope::driver::keyscanner::AVR KeyScanner; \ + typedef kaleidoscope::driver::bootloader::avr::BOOTLOADER_ BootLoader; \ + }; \ + class BOARD_: public kaleidoscope::device::ATMega32U4Keyboard {}; + +namespace kaleidoscope { +namespace device { + +struct ATMega32U4KeyboardProps : kaleidoscope::device::BaseProps { + typedef kaleidoscope::driver::mcu::ATMega32U4 MCU; + typedef kaleidoscope::driver::storage::ATMega32U4EEPROMProps StorageProps; + typedef kaleidoscope::driver::storage::AVREEPROM Storage; +}; + +template +class ATMega32U4Keyboard : public kaleidoscope::device::Base<_DeviceProps> { + public: + auto serialPort() -> decltype(Serial) & { + return Serial; + } +}; + +} +} + +#endif diff --git a/src/kaleidoscope/device/ATMegaKeyboard.cpp b/src/kaleidoscope/device/ATMegaKeyboard.cpp deleted file mode 100644 index 655a5f6a..00000000 --- a/src/kaleidoscope/device/ATMegaKeyboard.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- mode: c++ -*- - * Kaleidoscope-Hardware-ATMegaKeyboard -- Base class for some ATMega-based boards - * 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, version 3. - * - * 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 "Kaleidoscope.h" - -#if KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD - -#include "kaleidoscope/device/ATMegaKeyboard.h" - -namespace kaleidoscope { -namespace device { - -uint8_t ATMegaKeyboard::debounce = 3; - -void ATMegaKeyboard::setup(void) { - wdt_disable(); - - for (uint8_t i = 0; i < Kaleidoscope.device().matrix_columns; i++) { - DDR_INPUT(Kaleidoscope.device().matrix_col_pins[i]); - ENABLE_PULLUP(Kaleidoscope.device().matrix_col_pins[i]); - } - - for (uint8_t i = 0; i < Kaleidoscope.device().matrix_rows; i++) { - DDR_OUTPUT(Kaleidoscope.device().matrix_row_pins[i]); - OUTPUT_HIGH(Kaleidoscope.device().matrix_row_pins[i]); - } - - /* Set up Timer1 for 1700usec */ - TCCR1B = _BV(WGM13); - TCCR1A = 0; - - const uint32_t cycles = (F_CPU / 2000000) * 1700; - - ICR1 = cycles; - TCCR1B = _BV(WGM13) | _BV(CS10); - TIMSK1 = _BV(TOIE1); -} - -void __attribute__((optimize(3))) ATMegaKeyboard::readMatrix(void) { - for (uint8_t current_row = 0; current_row < Kaleidoscope.device().matrix_rows; current_row++) { - uint16_t mask, cols; - - mask = Kaleidoscope.device().debounceMaskForRow(current_row); - - OUTPUT_TOGGLE(Kaleidoscope.device().matrix_row_pins[current_row]); - cols = (Kaleidoscope.device().readCols() & mask) | (Kaleidoscope.device().keyState_[current_row] & ~mask); - OUTPUT_TOGGLE(Kaleidoscope.device().matrix_row_pins[current_row]); - Kaleidoscope.device().debounceRow(cols ^ Kaleidoscope.device().keyState_[current_row], current_row); - Kaleidoscope.device().keyState_[current_row] = cols; - } -} - -uint8_t ATMegaKeyboard::pressedKeyswitchCount() { - uint8_t count = 0; - - for (int8_t r = 0; r < Kaleidoscope.device().matrix_rows; r++) { - count += __builtin_popcount(Kaleidoscope.device().keyState_[r]); - } - return count; -} - -uint8_t ATMegaKeyboard::previousPressedKeyswitchCount() { - uint8_t count = 0; - - for (int8_t r = 0; r < Kaleidoscope.device().matrix_rows; r++) { - count += __builtin_popcount(Kaleidoscope.device().previousKeyState_[r]); - } - return count; -} - -void __attribute__((optimize(3))) ATMegaKeyboard::actOnMatrixScan() { - for (byte row = 0; row < Kaleidoscope.device().matrix_rows; row++) { - for (byte col = 0; col < Kaleidoscope.device().matrix_columns; col++) { - uint8_t keyState = (bitRead(Kaleidoscope.device().previousKeyState_[row], col) << 0) | - (bitRead(Kaleidoscope.device().keyState_[row], col) << 1); - if (keyState) { - handleKeyswitchEvent(Key_NoKey, ::KeyAddr(row, col), keyState); - } - } - Kaleidoscope.device().previousKeyState_[row] = Kaleidoscope.device().keyState_[row]; - } -} - -void ATMegaKeyboard::scanMatrix() { - // We ALWAYS want to tell Kaleidoscope about the state of the matrix - Kaleidoscope.device().actOnMatrixScan(); -} - -/* - * This function has loop unrolling disabled on purpose: we want to give the - * hardware enough time to produce stable PIN reads for us. If we unroll the - * loop, we will not have that, because even with the NOP, the codepath is too - * fast. If we don't have stable reads, then entire rows or columns will behave - * erratically. - * - * For this reason, we ask the compiler to not unroll our loop, which in turn, - * gives hardware enough time to produce stable reads, at the cost of a little - * bit of speed. - * - * Do not remove the attribute! - */ -__attribute__((optimize("no-unroll-loops"))) -uint16_t ATMegaKeyboard::readCols() { - uint16_t results = 0x00 ; - for (uint8_t i = 0; i < Kaleidoscope.device().matrix_columns; i++) { - asm("NOP"); // We need to pause a beat before reading or we may read before the pin is hot - results |= (!READ_PIN(Kaleidoscope.device().matrix_col_pins[i]) << i); - } - return results; -} - -uint16_t ATMegaKeyboard::debounceMaskForRow(uint8_t row) { - uint16_t result = 0; - - for (uint16_t c = 0; c < Kaleidoscope.device().matrix_columns; ++c) { - if (Kaleidoscope.device().debounce_matrix_[row][c]) { - --Kaleidoscope.device().debounce_matrix_[row][c]; - } else { - result |= _BV(c); - } - } - return result; -} - -void ATMegaKeyboard::debounceRow(uint16_t change, uint8_t row) { - for (uint16_t i = 0; i < Kaleidoscope.device().matrix_columns; ++i) { - if (change & _BV(i)) { - Kaleidoscope.device().debounce_matrix_[row][i] = debounce; - } - } -} - -} -} - -#endif diff --git a/src/kaleidoscope/device/ATMegaKeyboard.h b/src/kaleidoscope/device/ATMegaKeyboard.h deleted file mode 100644 index 8846b72c..00000000 --- a/src/kaleidoscope/device/ATMegaKeyboard.h +++ /dev/null @@ -1,166 +0,0 @@ -/* -*- mode: c++ -*- - * Kaleidoscope-Hardware-ATMegaKeyboard -- Base class for some ATMega-based boards - * 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, version 3. - * - * 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 - -#if KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD - -#include -#include -#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" -#include "kaleidoscope/MatrixAddr.h" - -#include "kaleidoscope/macro_helpers.h" -#include "kaleidoscope/device/avr/pins_and_ports.h" - -#include - -#ifndef CRGB - -struct cRGB { - uint8_t r, g, b; -}; - -#define CRGB(r,g,b) (cRGB){b, g, r} - -#endif - -#include "kaleidoscope/Hardware.h" -#include "kaleidoscope/driver/MCU.h" - -#define ROW_PIN_LIST(...) __VA_ARGS__ -#define COL_PIN_LIST(...) __VA_ARGS__ - -// By implementing all KeyAddr based access methods via macros in -// the derived hardware classes, we deal with the problem that -// keyboard matrix dimension (matrix_rows/matrix_columns) -// and thus type KeyAddr is only known to the derived hardware classes -// but not to ATMegaKeyboard. -// -#define ATMEGA_KEYBOARD_MATRIX_ACCESS_METHODS \ - bool isKeyswitchPressed(KeyAddr key_addr) { \ - return (bitRead(keyState_[key_addr.row()], \ - key_addr.col()) != 0); \ - } \ - DEPRECATED(ROW_COL_FUNC) bool isKeyswitchPressed(uint8_t row, byte col)\ - { \ - return isKeyswitchPressed(KeyAddr(row, col)); \ - } \ - bool isKeyswitchPressed(uint8_t keyIndex) { \ - keyIndex--; \ - return isKeyswitchPressed(KeyAddr(keyIndex)); \ - } \ - bool wasKeyswitchPressed(KeyAddr key_addr) { \ - return (bitRead(previousKeyState_[key_addr.row()], \ - key_addr.col()) != 0); \ - } \ - DEPRECATED(ROW_COL_FUNC) bool wasKeyswitchPressed(uint8_t row, byte col)\ - { \ - return wasKeyswitchPressed(KeyAddr(row, col)); \ - } \ - bool wasKeyswitchPressed(uint8_t keyIndex) { \ - keyIndex--; \ - return wasKeyswitchPressed(KeyAddr(keyIndex)); \ - } \ - void maskKey(KeyAddr key_addr) { \ - if (!key_addr.isValid()) \ - return; \ - \ - bitWrite(masks_[key_addr.row()], key_addr.col(), 1); \ - } \ - DEPRECATED(ROW_COL_FUNC) void maskKey(byte row, byte col) { \ - maskKey(KeyAddr(row, col)); \ - } \ - void unMaskKey(KeyAddr key_addr) { \ - if (!key_addr.isValid()) \ - return; \ - \ - bitWrite(masks_[key_addr.row()], key_addr.col(), 0); \ - } \ - DEPRECATED(ROW_COL_FUNC) void unMaskKey(byte row, byte col) { \ - unMaskKey(KeyAddr(row, col)); \ - } \ - bool isKeyMasked(KeyAddr key_addr) { \ - if (!key_addr.isValid()) \ - return false; \ - \ - return bitRead(masks_[key_addr.row()], \ - key_addr.col()); \ - } \ - DEPRECATED(ROW_COL_FUNC) bool isKeyMasked(byte row, byte col) { \ - return isKeyMasked(KeyAddr(row, col)); \ - } - -#define ATMEGA_KEYBOARD_CONFIG(ROW_PINS_, COL_PINS_) \ - static const int8_t matrix_rows = NUM_ARGS(ROW_PINS_); \ - static const int8_t matrix_columns = NUM_ARGS(COL_PINS_); \ - static constexpr uint8_t matrix_row_pins[matrix_rows] = ROW_PINS_; \ - static constexpr uint8_t matrix_col_pins[matrix_columns] = COL_PINS_; \ - typedef MatrixAddr KeyAddr; \ - \ - static uint16_t previousKeyState_[matrix_rows]; \ - static volatile uint16_t keyState_[matrix_rows]; \ - static uint16_t masks_[matrix_rows]; \ - static volatile uint8_t debounce_matrix_[matrix_rows][matrix_columns]; \ - \ - ATMEGA_KEYBOARD_MATRIX_ACCESS_METHODS - -#define ATMEGA_KEYBOARD_DATA(BOARD) \ - const int8_t BOARD::matrix_rows; \ - const int8_t BOARD::matrix_columns; \ - constexpr uint8_t BOARD::matrix_row_pins[matrix_rows]; \ - constexpr uint8_t BOARD::matrix_col_pins[matrix_columns]; \ - uint16_t BOARD::previousKeyState_[matrix_rows]; \ - volatile uint16_t BOARD::keyState_[matrix_rows]; \ - uint16_t BOARD::masks_[matrix_rows]; \ - volatile uint8_t BOARD::debounce_matrix_[matrix_rows][matrix_columns]; \ - \ - ISR(TIMER1_OVF_vect) { \ - Kaleidoscope.device().readMatrix(); \ - } - -namespace kaleidoscope { -namespace device { -class ATMegaKeyboard : public kaleidoscope::Hardware { - public: - ATMegaKeyboard(void) {} - - static uint8_t debounce; - - void setup(void); - void readMatrix(void); - void actOnMatrixScan(); - void scanMatrix(); - - uint8_t pressedKeyswitchCount(); - - uint8_t previousPressedKeyswitchCount(); - - - protected: - kaleidoscope::driver::mcu::ATMega32U4 mcu_; - - private: - - uint16_t readCols(); - uint16_t debounceMaskForRow(uint8_t row); - void debounceRow(uint16_t change, uint8_t row); -}; -} -} - -#endif diff --git a/src/kaleidoscope/device/Base.h b/src/kaleidoscope/device/Base.h new file mode 100644 index 00000000..c965510a --- /dev/null +++ b/src/kaleidoscope/device/Base.h @@ -0,0 +1,504 @@ +/* -*- mode: c++ -*- + * kaleidoscope::device::Base -- Kaleidoscope device Base class + * Copyright (C) 2017, 2018, 2019 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, version 3. + * + * 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 . + */ + +/** @file kaleidoscope/device/Base.h + * Base class for Kaleidoscope device libraries. + */ + +#pragma once + +#include "kaleidoscope/MatrixAddr.h" +#include "kaleidoscope_internal/deprecations.h" +#include "kaleidoscope/macro_helpers.h" + +#include "kaleidoscope/driver/keyscanner/None.h" +#include "kaleidoscope/driver/led/None.h" +#include "kaleidoscope/driver/mcu/None.h" +#include "kaleidoscope/driver/bootloader/None.h" +#include "kaleidoscope/driver/storage/None.h" + +#ifndef CRGB +#error cRGB and CRGB *must* be defined before including this header! +#endif + +/* All hardware libraries must define the following types and macros: + * kaleidoscope::Device - a typedef to your device's class. + * CRGB(r,g,b) - explained below + * cRGB, a structure with at least three members: r, g, and b - + * compilation will fail otherwise. + * + * Despite its name, the members do not need to be in the order r g b -- most + * likely they will be in an order that is convenient for the hardware. So + * initializing a cRGB with a struct literal will give surprising results for any + * colors where r, g, and b do not have the same value. Each Hardware library + * defines a CRGB(r,g,b) macro which returns a literal cRGB with the given values. + */ + +namespace kaleidoscope { +namespace device { + +struct BaseProps { + typedef kaleidoscope::driver::keyscanner::BaseProps KeyScannerProps; + typedef kaleidoscope::driver::keyscanner::None KeyScanner; + typedef kaleidoscope::driver::led::BaseProps LEDDriverProps; + typedef kaleidoscope::driver::led::None LEDDriver; + typedef kaleidoscope::driver::mcu::None MCU; + typedef kaleidoscope::driver::bootloader::None Bootloader; + typedef kaleidoscope::driver::storage::BaseProps StorageProps; + typedef kaleidoscope::driver::storage::None Storage; +}; + +template +class Base { + private: + class NoOpSerial { + public: + NoOpSerial() {} + }; + + static NoOpSerial noop_serial_; + + public: + Base() {} + + typedef _DeviceProps Props; + + typedef typename _DeviceProps::KeyScanner KeyScanner; + typedef typename _DeviceProps::KeyScannerProps KeyScannerProps; + typedef typename _DeviceProps::KeyScannerProps::KeyAddr KeyAddr; + typedef typename _DeviceProps::LEDDriverProps LEDDriverProps; + typedef typename _DeviceProps::LEDDriver LEDDriver; + typedef typename _DeviceProps::MCU MCU; + typedef typename _DeviceProps::Bootloader Bootloader; + typedef typename _DeviceProps::StorageProps StorageProps; + typedef typename _DeviceProps::Storage Storage; + + static constexpr uint8_t matrix_rows = KeyScannerProps::matrix_rows; + static constexpr uint8_t matrix_columns = KeyScannerProps::matrix_columns; + static constexpr uint8_t led_count = LEDDriverProps::led_count; + static constexpr typename LEDDriver::LEDs &LEDs() { + return LEDDriver::LEDs; + } + + /** + * @returns the number of keys on the keyboard. + */ + static constexpr int8_t numKeys() { + return matrix_columns * matrix_rows; + } + + /** + * Returns the storage driver used by the keyboard. + */ + Storage &storage() { + return storage_; + } + + /** + * Returns the serial port driver used by the keyboard. + */ + NoOpSerial &serialPort() { + return noop_serial_; + } + + /** + * @defgroup kaleidoscope_hardware_leds Kaleidoscope::Hardware/LEDs + * @{ + */ + /** + * Sync the LEDs with the underlying hardware. This should make sure that + * changes made before this call are reflected on the device. + */ + void syncLeds(void) { + led_driver_.syncLeds(); + } + /** + * Set the color of a per-key LED at a given row and column. + * + * Setting the color does not need to take effect immediately, it can be + * delayed until @ref syncLeds is called. + * + * @param key_addr is the matrix address of the LED. + * @param color is the color to set the LED to. + */ + void setCrgbAt(KeyAddr key_addr, cRGB color) { + setCrgbAt(getLedIndex(key_addr), color); + } + /** + * Set the color of a per-key LED at a given row and column. + * + * Setting the color does not need to take effect immediately, it can be + * delayed until @ref syncLeds is called. + * + * @param row is the logical row position of the key. + * @param col is the logical column position of the key. + * @param color is the color to set the LED to. + */ + DEPRECATED(ROW_COL_FUNC) void setCrgbAt(byte row, byte col, cRGB color) { + setCrgbAt(KeyAddr(row, col), color); + } + /** + * Set the color of a per-key LED at a given LED index. + * + * Setting the color does not need to take effect immediately, it can be + * delayed until @ref syncLeds is called. + * + * @param i is the LED index to change the color of. + * @param color is the color to set it to. + */ + void setCrgbAt(uint8_t i, cRGB color) { + led_driver_.setCrgbAt(i, color); + } + /** + * Returns the color of the LED at a given index. + * + * @param i is the index of the LED to return the color of. + * + * @returns The color at the given position. + */ + cRGB getCrgbAt(uint8_t i) { + return led_driver_.getCrgbAt(i); + } + /** + * Returns the color of the LED at a given index. + * + * @param key_addr is the key address of the LED to return the color of. + * + * @returns The color at the given position. + */ + cRGB getCrgbAt(KeyAddr key_addr) { + return getCrgbAt(getLedIndex(key_addr)); + } + /** + * Returns the index of the LED at a given row & column. + * + * @param key_addr is the matrix address of the LED. + * + * @returns The index of the LED at the given position, or -1 if there are no + * LEDs there. + */ + int8_t getLedIndex(KeyAddr key_addr) { + return led_driver_.getLedIndex(key_addr.toInt()); + } + /** + * Returns the index of the LED at a given row & column. + * + * @param row is the logical row position of the key. + * @param col is the logical column position of the key. + * + * @returns The index of the LED at the given position, or -1 if there are no + * LEDs there. + */ + DEPRECATED(ROW_COL_FUNC) int8_t getLedIndex(uint8_t row, byte col) { + return led_driver_.getLedIndex(KeyAddr(row, col)); + } + /** @} */ + + /** @defgroup kaleidoscope_hardware_matrix Kaleidoscope::Hardware/Matrix + * @{ + */ + /** + * Scan the keyboard matrix, and act on it. + */ + void scanMatrix(void) { + key_scanner_.scanMatrix(); + } + /** + * Read the state of the keyboard matrix. + * + * Do whatever is necessary to read the current keyboard state - but without + * acting on it. + * + * This is primarily used by @ref scanMatrix, but may have other uses too. + */ + void readMatrix(void) { + key_scanner_.readMatrix(); + } + /** + * Act on the scanned keyboard matrix. + * + * Iterate through the scanned state (@see readMatrix), and act on any events. + */ + void actOnMatrixScan(void) { + key_scanner_.actOnMatrixScan(); + } + /** @} */ + + /** @defgroup kaleidoscope_hardware_masking Kaleidoscope::Hardware/Key masking + * + * Sometimes there are situations when one wants to ignore key events for a + * while, to mask them out. Masked keys will be ignored until they are + * released. + * + * This is implemented in the Hardware library because that knows best how + * to mask efficiently, as this requires a deeper knowledge of the hardware, + * which is all but hidden from the rest of the plugins. + * @{ + */ + /** + * Mask out a key. + * + * Masking a key out means that any other event than a release will be + * ignored until said release. + * + * @param key_addr is the matrix address of the key. + */ + void maskKey(KeyAddr key_addr) { + key_scanner_.maskKey(key_addr); + } + /** + * Mask out a key. + * + * Masking a key out means that any other event than a release will be + * ignored until said release. + * + * @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. + */ + DEPRECATED(ROW_COL_FUNC) void maskKey(byte row, byte col) { + key_scanner_.maskKey(KeyAddr(row, col)); + } + /** + * Unmask a key. + * + * Remove the mask - if any - for a given key. To be used when the mask + * needs to be removed without the key being released. + * + * @param key_addr is the matrix address of the key. + */ + void unMaskKey(KeyAddr key_addr) { + key_scanner_.unMaskKey(key_addr); + } + /** + * Unmask a key. + * + * Remove the mask - if any - for a given key. To be used when the mask + * needs to be removed without the key being released. + * + * @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. + */ + DEPRECATED(ROW_COL_FUNC) void unMaskKey(byte row, byte col) { + key_scanner_.unMaskKey(KeyAddr(row, col)); + } + /** + * Check whether a key is masked or not. + * + * @param key_addr is the matrix address of the key. + * + * @returns true if the key is masked, false otherwise. + */ + bool isKeyMasked(KeyAddr key_addr) { + return key_scanner_.isKeyMasked(key_addr); + } + /** + * Check whether a key is masked or not. + * + * @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 masked, false otherwise. + */ + DEPRECATED(ROW_COL_FUNC) bool isKeyMasked(byte row, byte col) { + return key_scanner_.isKeyMasked(KeyAddr(row, col)); + } + /** @} */ + + /** @defgroup kaleidoscope_hardware_reattach Kaleidoscope::Hardware/Attach & Detach + * + * In situations where one wants to re-initialize the devices, perhaps to + * change settings inbetween, detaching from and then attaching back to the + * host is a desirable feature to have. Especially if this does not cut power, + * nor reboot the device. + * + * Because different hardware has different ways to accomplish this, the + * hardware plugin must provide these functions. Kaleidoscope will wrap them, + * so user code does not have to deal with them directly. + * @{ + */ + /** + * Detach the device from the host. + * + * Must detach the device, without rebooting or cutting power. Only the end + * points should get detached, the device must remain powered on. + */ + void detachFromHost() { + mcu_.detachFromHost(); + } + /** + * Attack the device to the host. + * + * Must restore the link detachFromHost severed. + */ + void attachToHost() { + mcu_.attachToHost(); + } + /** @} */ + + /** + * @defgroup kaleidoscope_hardware_keyswitch_state Kaleidoscope::Hardware/Key-switch state + * + * 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 key_addr is the matrix address of the key. + * + * @returns true if the key is pressed, false otherwise. + */ + bool isKeyswitchPressed(KeyAddr key_addr) { + return key_scanner_.isKeyswitchPressed(key_addr); + } + /** + * 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. + */ + DEPRECATED(ROW_COL_FUNC) bool isKeyswitchPressed(byte row, byte col) { + return key_scanner_.isKeyswitchPressed(KeyAddr(row, 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) { + return key_scanner_.isKeyswitchPressed(KeyAddr(--keyIndex)); + } + /** + * Check the number of key switches currently pressed. + * + * @returns the number of keys pressed. + */ + uint8_t pressedKeyswitchCount() { + return key_scanner_.pressedKeyswitchCount(); + } + + /** + * Check if a key was pressed at a given position on the previous scan + * + * @param key_addr is the matrix address of the key. + * + * @returns true if the key was pressed, false otherwise. + */ + bool wasKeyswitchPressed(KeyAddr key_addr) { + return key_scanner_.wasKeyswitchPressed(key_addr); + } + /** + * Check if a key was pressed at a given position on the previous scan + * + * @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 was pressed, false otherwise. + */ + DEPRECATED(ROW_COL_FUNC) bool wasKeyswitchPressed(byte row, byte col) { + return key_scanner_.wasKeyswitchPressed(KeyAddr(row, col)); + } + /** + * Check if a key was pressed at a given position on the previous scan. + * + * @param keyIndex is the key index, as calculated by `keyIndex`. + * + * @note Key indexes start at 1, not 0! + * + * @returns true if the key was pressed, false otherwise. + */ + bool wasKeyswitchPressed(uint8_t keyIndex) { + return key_scanner_.wasKeyswitchPressed(KeyAddr(--keyIndex)); + } + + /** + * Check the number of key switches pressed in the previous scan. + * + * @returns the number of keys pressed. + */ + uint8_t previousPressedKeyswitchCount() { + return key_scanner_.previousPressedKeyswitchCount(); + } + + + /** @} */ + + /** + * @defgroup kaleidoscope_hardware_misc Kaleidoscope::Hardware/Miscellaneous methods + * @{ + */ + /** + * Method to do any hardware-specific initialization. + * + * Called once when the device boots, this should initialize the device, and + * bring it up into a useful state. + */ + void setup() { + bootloader_.setup(); + mcu_.setup(); + storage_.setup(); + key_scanner_.setup(); + led_driver_.setup(); + } + + /** + * Method to configure the device for a hardware test mode + * + * Called by the Kaleidoscope Hardware test plugin, this method should + * do any device-specific initialization needed for factory hardware testing + * + */ + void enableHardwareTestMode() {} + + /** + * Method to put the device into programmable/bootloader mode. + * + * This is the old, legacy name of the method. + */ + DEPRECATED(HARDWARE_RESETDEVICE) void resetDevice() { + bootloader_.rebootBootloader(); + } + + /** + * Method to put the device into programmable/bootloader mode. + */ + void rebootBootloader() { + bootloader_.rebootBootloader(); + } + + /** @} */ + + protected: + KeyScanner key_scanner_; + LEDDriver led_driver_; + MCU mcu_; + Bootloader bootloader_; + Storage storage_; +}; + +} +} diff --git a/src/kaleidoscope/device/ez/ErgoDox.cpp b/src/kaleidoscope/device/ez/ErgoDox.cpp index 6bc45b6c..1d6c528c 100644 --- a/src/kaleidoscope/device/ez/ErgoDox.cpp +++ b/src/kaleidoscope/device/ez/ErgoDox.cpp @@ -168,40 +168,6 @@ void ErgoDox::setStatusLEDBrightness(uint8_t led, uint8_t 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; diff --git a/src/kaleidoscope/device/ez/ErgoDox.h b/src/kaleidoscope/device/ez/ErgoDox.h index 424d6a39..fe2aaee5 100644 --- a/src/kaleidoscope/device/ez/ErgoDox.h +++ b/src/kaleidoscope/device/ez/ErgoDox.h @@ -33,63 +33,46 @@ #include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" -#include "kaleidoscope/macro_helpers.h" - struct cRGB { uint8_t r, g, b; }; #define CRGB(r,g,b) (cRGB){b, g, r} -#include "kaleidoscope/Hardware.h" -#include "kaleidoscope/MatrixAddr.h" +#include "kaleidoscope/driver/keyscanner/Base.h" +#include "kaleidoscope/driver/bootloader/avr/HalfKay.h" +#include "kaleidoscope/device/ATMega32U4Keyboard.h" namespace kaleidoscope { namespace device { namespace ez { -class ErgoDox : public kaleidoscope::Hardware { +struct ErgoDoxProps : public kaleidoscope::device::ATMega32U4KeyboardProps { + struct KeyScannerProps : kaleidoscope::driver::keyscanner::BaseProps { + KEYSCANNER_PROPS(14, 6); + }; + typedef kaleidoscope::driver::bootloader::avr::HalfKay Bootloader; +}; + +class ErgoDox : public kaleidoscope::device::ATMega32U4Keyboard { public: ErgoDox(void) {} - static constexpr byte matrix_columns = 6; - static constexpr byte matrix_rows = 14; - static constexpr int8_t led_count = 0; - - typedef MatrixAddr KeyAddr; - static constexpr int8_t numKeys() { - return matrix_columns * matrix_rows; - } - void scanMatrix(void); void readMatrix(void); void actOnMatrixScan(void); void setup(); void maskKey(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) void maskKey(byte row, byte col) { - maskKey(KeyAddr(row, col)); - } void unMaskKey(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) void unMaskKey(byte row, byte col) { - unMaskKey(KeyAddr(row, col)); - } bool isKeyMasked(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) bool isKeyMasked(byte row, byte col) { - return isKeyMasked(KeyAddr(row, col)); - } bool isKeyswitchPressed(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) bool isKeyswitchPressed(byte row, byte col) { - return isKeyswitchPressed(KeyAddr(row, col)); - } bool isKeyswitchPressed(uint8_t keyIndex); uint8_t pressedKeyswitchCount(); bool wasKeyswitchPressed(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) bool wasKeyswitchPressed(byte row, byte col) { - return wasKeyswitchPressed(KeyAddr(row, col)); - } + bool wasKeyswitchPressed(uint8_t keyIndex); uint8_t previousPressedKeyswitchCount(); @@ -97,8 +80,6 @@ class ErgoDox : public kaleidoscope::Hardware { void setStatusLED(uint8_t led, bool state = true); void setStatusLEDBrightness(uint8_t led, uint8_t brightness); - void resetDevice(); - static uint8_t debounce; private: diff --git a/src/kaleidoscope/device/kbdfans/KBD4x.cpp b/src/kaleidoscope/device/kbdfans/KBD4x.cpp index 3cbc7268..7664dd68 100644 --- a/src/kaleidoscope/device/kbdfans/KBD4x.cpp +++ b/src/kaleidoscope/device/kbdfans/KBD4x.cpp @@ -18,39 +18,12 @@ #ifdef ARDUINO_AVR_KBD4X #include -#include -#include - namespace kaleidoscope { namespace device { namespace kbdfans { -ATMEGA_KEYBOARD_DATA(KBD4x); -constexpr int8_t KBD4x::led_count; - -#define BOOTLOADER_RESET_KEY 0xB007B007 -uint32_t reset_key __attribute__((section(".noinit"))); - -/* - * This function runs before main(), and jumps to the bootloader after a reset - * initiated by .resetDevice(). - */ -void _bootloader_jump_after_watchdog_reset() -__attribute__((used, naked, section(".init3"))); -void _bootloader_jump_after_watchdog_reset() { - if ((MCUSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) { - reset_key = 0; - - ((void (*)(void))(((FLASHEND + 1L) - 4096) >> 1))(); - } -} - -void KBD4x::resetDevice() { - reset_key = BOOTLOADER_RESET_KEY; - wdt_enable(WDTO_250MS); - for (;;); -} +AVR_KEYSCANNER_BOILERPLATE } } diff --git a/src/kaleidoscope/device/kbdfans/KBD4x.h b/src/kaleidoscope/device/kbdfans/KBD4x.h index faedf0cd..b18b8bd1 100644 --- a/src/kaleidoscope/device/kbdfans/KBD4x.h +++ b/src/kaleidoscope/device/kbdfans/KBD4x.h @@ -19,36 +19,35 @@ #ifdef ARDUINO_AVR_KBD4X -#include -#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" +#define KALEIDOSCOPE_BOOTLOADER_FLIP_WORKAROUND 1 -#include "kaleidoscope/macro_helpers.h" -#include "kaleidoscope/device/avr/pins_and_ports.h" +#include -#include "kaleidoscope/device/ATMegaKeyboard.h" +#include "kaleidoscope/driver/keyscanner/AVR.h" +#include "kaleidoscope/driver/bootloader/avr/FLIP.h" +#include "kaleidoscope/device/ATMega32U4Keyboard.h" namespace kaleidoscope { namespace device { namespace kbdfans { -class KBD4x: public kaleidoscope::device::ATMegaKeyboard { + +struct KBD4xProps : kaleidoscope::device::ATMega32U4KeyboardProps { + struct KeyScannerProps : public kaleidoscope::driver::keyscanner::AVRProps { + AVR_KEYSCANNER_PROPS( + ROW_PIN_LIST({ PIN_D0, PIN_D1, PIN_D2, PIN_D3 }), + COL_PIN_LIST({ PIN_F0, PIN_F1, PIN_F4, PIN_F5, PIN_F6, PIN_F7, PIN_B3, PIN_B1, PIN_B0, PIN_D5, PIN_B7, PIN_C7 }) + ); + }; + typedef kaleidoscope::driver::keyscanner::AVR KeyScanner; + typedef kaleidoscope::driver::bootloader::avr::FLIP Bootloader; +}; + +class KBD4x: public kaleidoscope::device::ATMega32U4Keyboard { public: - KBD4x(void) { + KBD4x() { mcu_.disableJTAG(); mcu_.disableClockDivision(); } - - ATMEGA_KEYBOARD_CONFIG( - ROW_PIN_LIST({ PIN_D0, PIN_D1, PIN_D2, PIN_D3 }), - COL_PIN_LIST({ PIN_F0, PIN_F1, PIN_F4, PIN_F5, PIN_F6, PIN_F7, PIN_B3, PIN_B1, PIN_B0, PIN_D5, PIN_B7, PIN_C7 }) - ); - - static constexpr int8_t led_count = 0; - - static constexpr int8_t numKeys() { - return matrix_columns * matrix_rows; - } - - void resetDevice(); }; #define PER_KEY_DATA(dflt, \ diff --git a/src/kaleidoscope/device/keyboardio/Model01.cpp b/src/kaleidoscope/device/keyboardio/Model01.cpp index a3507d5c..49d407db 100644 --- a/src/kaleidoscope/device/keyboardio/Model01.cpp +++ b/src/kaleidoscope/device/keyboardio/Model01.cpp @@ -26,32 +26,22 @@ namespace kaleidoscope { namespace device { namespace keyboardio { -KeyboardioScanner Model01::leftHand(0); -KeyboardioScanner Model01::rightHand(3); +/********* Model01Hands *********/ -bool Model01::isLEDChanged = true; +struct Model01Hands { + static KeyboardioScanner leftHand; + static KeyboardioScanner rightHand; -keydata_t Model01::leftHandMask; -keydata_t Model01::rightHandMask; - -static constexpr int8_t key_led_map[Model01::led_count] PROGMEM = { - 3, 4, 11, 12, 19, 20, 26, 27, 36, 37, 43, 44, 51, 52, 59, 60, - 2, 5, 10, 13, 18, 21, 25, 28, 35, 38, 42, 45, 50, 53, 58, 61, - 1, 6, 9, 14, 17, 22, 24, 29, 34, 39, 41, 46, 49, 54, 57, 62, - 0, 7, 8, 15, 16, 23, 31, 30, 33, 32, 40, 47, 48, 55, 56, 63, + static void setup(); }; -Model01::Model01(void) { +KeyboardioScanner Model01Hands::leftHand(0); +KeyboardioScanner Model01Hands::rightHand(3); -} - -void Model01::enableScannerPower(void) { - // Turn on power to the LED net - DDRC |= _BV(7); - PORTC |= _BV(7); -} +void Model01Hands::setup(void) { + // TODO: Consider not doing this until 30s after keyboard + // boot up, to make it easier to rescue things in case of power draw issues. -void Model01::enableHighPowerLeds(void) { // This lets the keyboard pull up to 1.6 amps from the host. // That violates the USB spec. But it sure is pretty looking DDRE |= _BV(6); @@ -60,51 +50,29 @@ void Model01::enableHighPowerLeds(void) { // Set B4, the overcurrent check to an input with an internal pull-up DDRB &= ~_BV(4); // set bit, input PORTB &= ~_BV(4); // set bit, enable pull-up resistor - - - -} - -void Model01::setup(void) { - wdt_disable(); - delay(100); - enableScannerPower(); - - // TODO: Consider not doing this until 30s after keyboard - // boot up, to make it easier to rescue things in case of power draw issues. - enableHighPowerLeds(); - leftHandState.all = 0; - rightHandState.all = 0; - - TWBR = 12; // This is 400mhz, which is the fastest we can drive the ATTiny -} - -void Model01::enableHardwareTestMode() { - - // Toggle the programming LEDS on - PORTD |= (1 << 5); - PORTB |= (1 << 0); - - // Disable the debouncer on the ATTinys - Kaleidoscope.device().setKeyscanInterval(2); } +/********* LED Driver *********/ +bool Model01LEDDriver::isLEDChanged = true; +static constexpr int8_t key_led_map[Model01::led_count] PROGMEM = { + 3, 4, 11, 12, 19, 20, 26, 27, 36, 37, 43, 44, 51, 52, 59, 60, + 2, 5, 10, 13, 18, 21, 25, 28, 35, 38, 42, 45, 50, 53, 58, 61, + 1, 6, 9, 14, 17, 22, 24, 29, 34, 39, 41, 46, 49, 54, 57, 62, + 0, 7, 8, 15, 16, 23, 31, 30, 33, 32, 40, 47, 48, 55, 56, 63, +}; -void Model01::setCrgbAt(int8_t i, cRGB crgb) { - if (i < 0) { - return; - } +void Model01LEDDriver::setCrgbAt(uint8_t i, cRGB crgb) { if (i < 32) { cRGB oldColor = getCrgbAt(i); isLEDChanged |= !(oldColor.r == crgb.r && oldColor.g == crgb.g && oldColor.b == crgb.b); - leftHand.ledData.leds[i] = crgb; + Model01Hands::leftHand.ledData.leds[i] = crgb; } else if (i < 64) { cRGB oldColor = getCrgbAt(i); isLEDChanged |= !(oldColor.r == crgb.r && oldColor.g == crgb.g && oldColor.b == crgb.b); - rightHand.ledData.leds[i - 32] = crgb; + Model01Hands::rightHand.ledData.leds[i - 32] = crgb; } else { // TODO(anyone): // how do we want to handle debugging assertions about crazy user @@ -112,26 +80,22 @@ void Model01::setCrgbAt(int8_t i, cRGB crgb) { } } -void Model01::setCrgbAt(KeyAddr key_addr, cRGB color) { - setCrgbAt(getLedIndex(key_addr), color); +uint8_t Model01LEDDriver::getLedIndex(uint8_t key_offset) { + return pgm_read_byte(&(key_led_map[key_offset])); } -int8_t Model01::getLedIndex(KeyAddr key_addr) { - return pgm_read_byte(&(key_led_map[key_addr.toInt()])); -} - -cRGB Model01::getCrgbAt(int8_t i) { - if (i < 0 || i >= 64) +cRGB Model01LEDDriver::getCrgbAt(uint8_t i) { + if (i >= 64) return {0, 0, 0}; if (i < 32) { - return leftHand.ledData.leds[i]; + return Model01Hands::leftHand.ledData.leds[i]; } else { - return rightHand.ledData.leds[i - 32]; + return Model01Hands::rightHand.ledData.leds[i - 32]; } } -void Model01::syncLeds() { +void Model01LEDDriver::syncLeds() { if (!isLEDChanged) return; @@ -141,22 +105,22 @@ void Model01::syncLeds() { // we run into a race condition with updating the next bank // on an ATTiny before it's done writing the previous one to memory - leftHand.sendLEDData(); - rightHand.sendLEDData(); + Model01Hands::leftHand.sendLEDData(); + Model01Hands::rightHand.sendLEDData(); - leftHand.sendLEDData(); - rightHand.sendLEDData(); + Model01Hands::leftHand.sendLEDData(); + Model01Hands::rightHand.sendLEDData(); - leftHand.sendLEDData(); - rightHand.sendLEDData(); + Model01Hands::leftHand.sendLEDData(); + Model01Hands::rightHand.sendLEDData(); - leftHand.sendLEDData(); - rightHand.sendLEDData(); + Model01Hands::leftHand.sendLEDData(); + Model01Hands::rightHand.sendLEDData(); isLEDChanged = false; } -boolean Model01::ledPowerFault() { +boolean Model01LEDDriver::ledPowerFault() { if (PINB & _BV(4)) { return true; } else { @@ -164,28 +128,49 @@ boolean Model01::ledPowerFault() { } } -void Model01::readMatrix() { +/********* Key scanner *********/ + +keydata_t Model01KeyScanner::leftHandState; +keydata_t Model01KeyScanner::rightHandState; +keydata_t Model01KeyScanner::previousLeftHandState; +keydata_t Model01KeyScanner::previousRightHandState; +keydata_t Model01KeyScanner::leftHandMask; +keydata_t Model01KeyScanner::rightHandMask; + +void Model01KeyScanner::enableScannerPower(void) { + // Turn on power to the LED net + DDRC |= _BV(7); + PORTC |= _BV(7); +} + +void Model01KeyScanner::setup() { + wdt_disable(); + delay(100); + enableScannerPower(); +} + +void Model01KeyScanner::readMatrix() { //scan the Keyboard matrix looking for connections previousLeftHandState = leftHandState; previousRightHandState = rightHandState; - if (leftHand.readKeys()) { - leftHandState = leftHand.getKeyData(); + if (Model01Hands::leftHand.readKeys()) { + leftHandState = Model01Hands::leftHand.getKeyData(); } - if (rightHand.readKeys()) { - rightHandState = rightHand.getKeyData(); + if (Model01Hands::rightHand.readKeys()) { + rightHandState = Model01Hands::rightHand.getKeyData(); } } -void Model01::actOnHalfRow(byte row, byte colState, byte colPrevState, byte startPos) { +void Model01KeyScanner::actOnHalfRow(byte row, byte colState, byte colPrevState, byte startPos) { if ((colState != colPrevState) || (colState != 0)) { for (byte col = 0; col < 8; col++) { // Build up the key state for row, col uint8_t keyState = ((bitRead(colPrevState, 0) << 0) | (bitRead(colState, 0) << 1)); if (keyState) - handleKeyswitchEvent(Key_NoKey, KeyAddr(row, startPos - col), keyState); + ThisType::handleKeyswitchEvent(Key_NoKey, KeyAddr(row, startPos - col), keyState); // Throw away the data we've just used, so we can read the next column colState = colState >> 1; @@ -194,7 +179,7 @@ void Model01::actOnHalfRow(byte row, byte colState, byte colPrevState, byte star } } -void Model01::actOnMatrixScan() { +void Model01KeyScanner::actOnMatrixScan() { for (byte row = 0; row < 4; row++) { actOnHalfRow(row, leftHandState.rows[row], previousLeftHandState.rows[row], 7); actOnHalfRow(row, rightHandState.rows[row], previousRightHandState.rows[row], 15); @@ -202,32 +187,11 @@ void Model01::actOnMatrixScan() { } -void Model01::scanMatrix() { +void Model01KeyScanner::scanMatrix() { readMatrix(); actOnMatrixScan(); } -void Model01::rebootBootloader() { - // Set the magic bits to get a Caterina-based device - // to reboot into the bootloader and stay there, rather - // than run move onward - // - // These values are the same as those defined in - // Caterina.c - - uint16_t bootKey = 0x7777; - uint16_t *const bootKeyPtr = reinterpret_cast(0x0800); - - // Stash the magic key - *bootKeyPtr = bootKey; - - // Set a watchdog timer - wdt_enable(WDTO_120MS); - - while (1) {} // This infinite loop ensures nothing else - // happens before the watchdog reboots us -} - // In the maskKey(), unMaskKey(), and isKeyMasked() functions, we read and write bits in // two bitfields -- one for each half of the keyboard. The fourth bit of the column number // tells us which bitfield (right or left) to access, thus the "8" (B00001000). The row @@ -242,7 +206,7 @@ constexpr byte HAND_BIT = B00001000; constexpr byte ROW_BITS = B00110000; constexpr byte COL_BITS = B00000111; -void Model01::maskKey(KeyAddr key_addr) { +void Model01KeyScanner::maskKey(KeyAddr key_addr) { if (!key_addr.isValid()) return; @@ -255,7 +219,7 @@ void Model01::maskKey(KeyAddr key_addr) { } } -void Model01::unMaskKey(KeyAddr key_addr) { +void Model01KeyScanner::unMaskKey(KeyAddr key_addr) { if (!key_addr.isValid()) return; @@ -268,7 +232,7 @@ void Model01::unMaskKey(KeyAddr key_addr) { } } -bool Model01::isKeyMasked(KeyAddr key_addr) { +bool Model01KeyScanner::isKeyMasked(KeyAddr key_addr) { if (!key_addr.isValid()) return false; @@ -281,18 +245,18 @@ bool Model01::isKeyMasked(KeyAddr key_addr) { } } -void Model01::maskHeldKeys(void) { +void Model01KeyScanner::maskHeldKeys() { memcpy(leftHandMask.rows, leftHandState.rows, sizeof(leftHandMask)); memcpy(rightHandMask.rows, rightHandState.rows, sizeof(rightHandMask)); } -void Model01::setKeyscanInterval(uint8_t interval) { - leftHand.setKeyscanInterval(interval); - rightHand.setKeyscanInterval(interval); +void Model01KeyScanner::setKeyscanInterval(uint8_t interval) { + Model01Hands::leftHand.setKeyscanInterval(interval); + Model01Hands::rightHand.setKeyscanInterval(interval); } -bool Model01::isKeyswitchPressed(KeyAddr key_addr) { +bool Model01KeyScanner::isKeyswitchPressed(KeyAddr key_addr) { auto row = key_addr.row(); auto col = key_addr.col(); if (col <= 7) { @@ -302,13 +266,7 @@ bool Model01::isKeyswitchPressed(KeyAddr key_addr) { } } -bool Model01::isKeyswitchPressed(uint8_t keyIndex) { - keyIndex--; - return isKeyswitchPressed(KeyAddr(keyIndex)); -} - - -bool Model01::wasKeyswitchPressed(KeyAddr key_addr) { +bool Model01KeyScanner::wasKeyswitchPressed(KeyAddr key_addr) { auto row = key_addr.row(); auto col = key_addr.col(); if (col <= 7) { @@ -318,19 +276,32 @@ bool Model01::wasKeyswitchPressed(KeyAddr key_addr) { } } -bool Model01::wasKeyswitchPressed(uint8_t keyIndex) { - keyIndex--; - return wasKeyswitchPressed(KeyAddr(keyIndex)); -} - -uint8_t Model01::pressedKeyswitchCount() { +uint8_t Model01KeyScanner::pressedKeyswitchCount() { return __builtin_popcountl(leftHandState.all) + __builtin_popcountl(rightHandState.all); } -uint8_t Model01::previousPressedKeyswitchCount() { +uint8_t Model01KeyScanner::previousPressedKeyswitchCount() { return __builtin_popcountl(previousLeftHandState.all) + __builtin_popcountl(previousRightHandState.all); } +/********* Hardware plugin *********/ + +void Model01::setup() { + KeyScanner::setup(); + Model01Hands::setup(); + + TWBR = 12; // This is 400mhz, which is the fastest we can drive the ATTiny +} + +void Model01::enableHardwareTestMode() { + // Toggle the programming LEDS on + PORTD |= (1 << 5); + PORTB |= (1 << 0); + + // Disable the debouncer on the ATTinys + KeyScanner::setKeyscanInterval(2); +} + } } } diff --git a/src/kaleidoscope/device/keyboardio/Model01.h b/src/kaleidoscope/device/keyboardio/Model01.h index f8e9cbc9..889891ed 100644 --- a/src/kaleidoscope/device/keyboardio/Model01.h +++ b/src/kaleidoscope/device/keyboardio/Model01.h @@ -24,100 +24,88 @@ #include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" #include "KeyboardioScanner.h" -#include "kaleidoscope/macro_helpers.h" - #define CRGB(r,g,b) (cRGB){b, g, r} -#include "kaleidoscope/Hardware.h" -#include "kaleidoscope/MatrixAddr.h" +#include "kaleidoscope/driver/keyscanner/Base.h" +#include "kaleidoscope/driver/led/Base.h" +#include "kaleidoscope/driver/bootloader/avr/Caterina.h" +#include "kaleidoscope/device/ATMega32U4Keyboard.h" namespace kaleidoscope { namespace device { namespace keyboardio { -class Model01 : public kaleidoscope::Hardware { +struct Model01LEDDriverProps : public kaleidoscope::driver::led::BaseProps { + static constexpr uint8_t led_count = 64; +}; + +class Model01LEDDriver : public kaleidoscope::driver::led::Base { public: - Model01(void); - - static constexpr byte matrix_rows = 4; - static constexpr byte matrix_columns = 16; - static constexpr int8_t led_count = 64; - - typedef MatrixAddr KeyAddr; - static constexpr int8_t numKeys() { - return matrix_columns * matrix_rows; - } - - void syncLeds(void); - void setCrgbAt(KeyAddr key_addr, cRGB color); - DEPRECATED(ROW_COL_FUNC) void setCrgbAt(byte row, byte col, cRGB color) { - setCrgbAt(KeyAddr(row, col), color); - } - void setCrgbAt(int8_t i, cRGB crgb); - cRGB getCrgbAt(int8_t i); - int8_t getLedIndex(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) int8_t getLedIndex(byte row, byte col) { - return getLedIndex(KeyAddr(row, col)); - } - - void scanMatrix(void); - void readMatrix(void); - void actOnMatrixScan(void); - void setup(); - void rebootBootloader(); - - /* These public functions are things supported by the Model 01, but - * aren't necessarily part of the Kaleidoscope API - */ - void enableHighPowerLeds(void); - void enableScannerPower(void); - void setKeyscanInterval(uint8_t interval); - boolean ledPowerFault(void); - - void maskKey(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) void maskKey(byte row, byte col) { - maskKey(KeyAddr(row, col)); - } - void unMaskKey(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) void unMaskKey(byte row, byte col) { - unMaskKey(KeyAddr(row, col)); - } - bool isKeyMasked(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) bool isKeyMasked(byte row, byte col) { - return isKeyMasked(KeyAddr(row, col)); - } - void maskHeldKeys(void); - - bool isKeyswitchPressed(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) bool isKeyswitchPressed(byte row, byte col) { - return isKeyswitchPressed(KeyAddr(row, col)); - } - bool isKeyswitchPressed(uint8_t keyIndex); - uint8_t pressedKeyswitchCount(); - - bool wasKeyswitchPressed(KeyAddr key_addr); - DEPRECATED(ROW_COL_FUNC) bool wasKeyswitchPressed(byte row, byte col) { - return wasKeyswitchPressed(KeyAddr(row, col)); - } - bool wasKeyswitchPressed(uint8_t keyIndex); - uint8_t previousPressedKeyswitchCount(); - - void enableHardwareTestMode(); - - keydata_t leftHandState; - keydata_t rightHandState; - keydata_t previousLeftHandState; - keydata_t previousRightHandState; + static void syncLeds(); + static void setCrgbAt(uint8_t i, cRGB crgb); + static cRGB getCrgbAt(uint8_t i); - private: - static void actOnHalfRow(byte row, byte colState, byte colPrevState, byte startPos); + static uint8_t getLedIndex(uint8_t key_offset); + + static void enableHighPowerLeds(); + static boolean ledPowerFault(); + private: static bool isLEDChanged; - static KeyboardioScanner leftHand; - static KeyboardioScanner rightHand; +}; + +struct Model01KeyScannerProps : public kaleidoscope::driver::keyscanner::BaseProps { + KEYSCANNER_PROPS(4, 16); +}; + +class Model01KeyScanner : public kaleidoscope::driver::keyscanner::Base { + private: + typedef Model01KeyScanner ThisType; + public: + static void setup(); + static void scanMatrix(); + static void readMatrix(); + static void actOnMatrixScan(); + + static void maskKey(KeyAddr key_addr); + static void unMaskKey(KeyAddr key_addr); + static bool isKeyMasked(KeyAddr key_addr); + static void maskHeldKeys(); + + static bool isKeyswitchPressed(KeyAddr key_addr); + static uint8_t pressedKeyswitchCount(); + + static bool wasKeyswitchPressed(KeyAddr key_addr); + static uint8_t previousPressedKeyswitchCount(); + + static void setKeyscanInterval(uint8_t interval); + + protected: + static keydata_t leftHandState; + static keydata_t rightHandState; + static keydata_t previousLeftHandState; + static keydata_t previousRightHandState; static keydata_t leftHandMask; static keydata_t rightHandMask; + + static void actOnHalfRow(byte row, byte colState, byte colPrevState, byte startPos); + static void enableScannerPower(); +}; + +struct Model01Props : kaleidoscope::device::ATMega32U4KeyboardProps { + typedef Model01LEDDriverProps LEDDriverProps; + typedef Model01LEDDriver LEDDriver; + typedef Model01KeyScannerProps KeyScannerProps; + typedef Model01KeyScanner KeyScanner; + typedef kaleidoscope::driver::bootloader::avr::Caterina BootLoader; +}; + +class Model01 : public kaleidoscope::device::ATMega32U4Keyboard { + public: + static void setup(); + + static void enableHardwareTestMode(); }; } diff --git a/src/kaleidoscope/device/olkb/Planck.cpp b/src/kaleidoscope/device/olkb/Planck.cpp index ab9fa631..eb4d9106 100644 --- a/src/kaleidoscope/device/olkb/Planck.cpp +++ b/src/kaleidoscope/device/olkb/Planck.cpp @@ -18,14 +18,12 @@ #ifdef ARDUINO_AVR_PLANCK #include -#include namespace kaleidoscope { namespace device { namespace olkb { -ATMEGA_KEYBOARD_DATA(Planck); -constexpr int8_t Planck::led_count; +AVR_KEYSCANNER_BOILERPLATE } } diff --git a/src/kaleidoscope/device/olkb/Planck.h b/src/kaleidoscope/device/olkb/Planck.h index 39bac427..2fdb7311 100644 --- a/src/kaleidoscope/device/olkb/Planck.h +++ b/src/kaleidoscope/device/olkb/Planck.h @@ -20,31 +20,20 @@ #ifdef ARDUINO_AVR_PLANCK #include -#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" -#include "kaleidoscope/macro_helpers.h" -#include "kaleidoscope/device/avr/pins_and_ports.h" - -#include "kaleidoscope/device/ATMegaKeyboard.h" +#include "kaleidoscope/driver/keyscanner/AVR.h" +#include "kaleidoscope/driver/bootloader/avr/HalfKay.h" +#include "kaleidoscope/device/ATMega32U4Keyboard.h" namespace kaleidoscope { namespace device { namespace olkb { -class Planck: public kaleidoscope::device::ATMegaKeyboard { - public: - Planck(void) {} - - ATMEGA_KEYBOARD_CONFIG( - ROW_PIN_LIST({ PIN_D0, PIN_D5, PIN_B5, PIN_B6 }), - COL_PIN_LIST({ PIN_F1, PIN_F0, PIN_B0, PIN_C7, PIN_F4, PIN_F5, PIN_F6, PIN_F7, PIN_D4, PIN_D6, PIN_B4, PIN_D7 }) - ); - - static constexpr int8_t led_count = 0; - static constexpr int8_t numKeys() { - return matrix_columns * matrix_rows; - } -}; +ATMEGA32U4_KEYBOARD( + Planck, HalfKay, + ROW_PIN_LIST({ PIN_D0, PIN_D5, PIN_B5, PIN_B6 }), + COL_PIN_LIST({ PIN_F1, PIN_F0, PIN_B0, PIN_C7, PIN_F4, PIN_F5, PIN_F6, PIN_F7, PIN_D4, PIN_D6, PIN_B4, PIN_D7 }) +); #define PER_KEY_DATA(dflt, \ R0C0, R0C1, R0C2, R0C3, R0C4, R0C5, R0C6, R0C7, R0C8, R0C9, R0C10, R0C11, \ diff --git a/src/kaleidoscope/device/softhruf/Splitography.cpp b/src/kaleidoscope/device/softhruf/Splitography.cpp index 148b5680..edf78906 100644 --- a/src/kaleidoscope/device/softhruf/Splitography.cpp +++ b/src/kaleidoscope/device/softhruf/Splitography.cpp @@ -25,14 +25,12 @@ #ifdef ARDUINO_AVR_SPLITOGRAPHY #include -#include namespace kaleidoscope { namespace device { namespace softhruf { -ATMEGA_KEYBOARD_DATA(Splitography); -constexpr int8_t Splitography::led_count; +AVR_KEYSCANNER_BOILERPLATE } } diff --git a/src/kaleidoscope/device/softhruf/Splitography.h b/src/kaleidoscope/device/softhruf/Splitography.h index 6441e015..ec20aab2 100644 --- a/src/kaleidoscope/device/softhruf/Splitography.h +++ b/src/kaleidoscope/device/softhruf/Splitography.h @@ -26,32 +26,33 @@ #ifdef ARDUINO_AVR_SPLITOGRAPHY -#include -#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" +#define KALEIDOSCOPE_BOOTLOADER_FLIP_WORKAROUND 1 -#include "kaleidoscope/macro_helpers.h" -#include "kaleidoscope/device/avr/pins_and_ports.h" +#include -#include "kaleidoscope/device/ATMegaKeyboard.h" +#include "kaleidoscope/driver/keyscanner/AVR.h" +#include "kaleidoscope/driver/bootloader/avr/FLIP.h" +#include "kaleidoscope/device/ATMega32U4Keyboard.h" namespace kaleidoscope { namespace device { namespace softhruf { -class Splitography: public kaleidoscope::device::ATMegaKeyboard { - public: - Splitography(void) { - mcu_.disableJTAG(); - } - ATMEGA_KEYBOARD_CONFIG( - ROW_PIN_LIST({ PIN_D0, PIN_D1, PIN_D2, PIN_D3 }), - COL_PIN_LIST({ PIN_F0, PIN_F1, PIN_F4, PIN_F5, PIN_F6, PIN_F7, PIN_C7, PIN_C6, PIN_B6, PIN_B5, PIN_B4, PIN_D7 }) - ); - - static constexpr int8_t led_count = 0; +struct SplitographyProps : kaleidoscope::device::ATMega32U4KeyboardProps { + struct KeyScannerProps : public kaleidoscope::driver::keyscanner::AVRProps { + AVR_KEYSCANNER_PROPS( + ROW_PIN_LIST({ PIN_D0, PIN_D1, PIN_D2, PIN_D3 }), + COL_PIN_LIST({ PIN_F0, PIN_F1, PIN_F4, PIN_F5, PIN_F6, PIN_F7, PIN_C7, PIN_C6, PIN_B6, PIN_B5, PIN_B4, PIN_D7 }) + ); + }; + typedef kaleidoscope::driver::keyscanner::AVR KeyScanner; + typedef kaleidoscope::driver::bootloader::avr::FLIP BootLoader; +}; - static constexpr int8_t numKeys() { - return matrix_columns * matrix_rows; +class Splitography: public kaleidoscope::device::ATMega32U4Keyboard { + public: + Splitography() { + mcu_.disableJTAG(); } }; diff --git a/src/kaleidoscope/device/technomancy/Atreus.cpp b/src/kaleidoscope/device/technomancy/Atreus.cpp index 971e3d8a..51e595d4 100644 --- a/src/kaleidoscope/device/technomancy/Atreus.cpp +++ b/src/kaleidoscope/device/technomancy/Atreus.cpp @@ -27,50 +27,12 @@ #ifdef ARDUINO_AVR_ATREUS #include -#include namespace kaleidoscope { namespace device { namespace technomancy { -ATMEGA_KEYBOARD_DATA(Atreus); -constexpr int8_t Atreus::led_count; - -// 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"); -} - +AVR_KEYSCANNER_BOILERPLATE } } diff --git a/src/kaleidoscope/device/technomancy/Atreus.h b/src/kaleidoscope/device/technomancy/Atreus.h index e0accf57..09242ccc 100644 --- a/src/kaleidoscope/device/technomancy/Atreus.h +++ b/src/kaleidoscope/device/technomancy/Atreus.h @@ -27,51 +27,30 @@ #ifdef ARDUINO_AVR_ATREUS #include -#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" -#include "kaleidoscope/macro_helpers.h" - -#include "kaleidoscope/device/ATMegaKeyboard.h" +#include "kaleidoscope/driver/keyscanner/AVR.h" +#include "kaleidoscope/driver/bootloader/avr/HalfKay.h" +#include "kaleidoscope/device/ATMega32U4Keyboard.h" namespace kaleidoscope { namespace device { namespace technomancy { -class Atreus: public kaleidoscope::device::ATMegaKeyboard { - friend class ATMegaKeyboard; - public: - Atreus(void) {} +ATMEGA32U4_KEYBOARD( + Atreus, HalfKay, #ifdef KALEIDOSCOPE_HARDWARE_ATREUS_PINOUT_ASTAR - ATMEGA_KEYBOARD_CONFIG( - ROW_PIN_LIST({PIN_D0, PIN_D1, PIN_D3, PIN_D2}), - COL_PIN_LIST({PIN_D7, PIN_C6, PIN_B5, PIN_B4, PIN_E6, PIN_D4, PIN_B6, PIN_F6, PIN_F7, PIN_D6, PIN_B7}) - ); + ROW_PIN_LIST({PIN_D0, PIN_D1, PIN_D3, PIN_D2}), + COL_PIN_LIST({PIN_D7, PIN_C6, PIN_B5, PIN_B4, PIN_E6, PIN_D4, PIN_B6, PIN_F6, PIN_F7, PIN_D6, PIN_B7}) #endif - #ifdef KALEIDOSCOPE_HARDWARE_ATREUS_PINOUT_ASTAR_DOWN - ATMEGA_KEYBOARD_CONFIG( - ROW_PIN_LIST({PIN_D0, PIN_D1, PIN_D3, PIN_D2}), - COL_PIN_LIST({PIN_B7, PIN_D6, PIN_F7, PIN_F6, PIN_B6, PIN_D4, PIN_E6, PIN_B4, PIN_B5, PIN_C6, PIN_D7}) - ); + ROW_PIN_LIST({PIN_D0, PIN_D1, PIN_D3, PIN_D2}), + COL_PIN_LIST({PIN_B7, PIN_D6, PIN_F7, PIN_F6, PIN_B6, PIN_D4, PIN_E6, PIN_B4, PIN_B5, PIN_C6, PIN_D7}) #endif - #ifdef KALEIDOSCOPE_HARDWARE_ATREUS_PINOUT_LEGACY_TEENSY2 - ATMEGA_KEYBOARD_CONFIG( - ROW_PIN_LIST({PIN_D0, PIN_D1, PIN_D2, PIN_D3}), - COL_PIN_LIST({PIN_F6, PIN_F5, PIN_F4, PIN_B7, PIN_B6, PIN_B5, PIN_B4, PIN_B3, PIN_B2, PIN_B1, PIN_B0}) - ); + ROW_PIN_LIST({PIN_D0, PIN_D1, PIN_D2, PIN_D3}), + COL_PIN_LIST({PIN_F6, PIN_F5, PIN_F4, PIN_B7, PIN_B6, PIN_B5, PIN_B4, PIN_B3, PIN_B2, PIN_B1, PIN_B0}) #endif - - static constexpr int8_t led_count = 0; - - static constexpr int8_t numKeys() { - return matrix_columns * matrix_rows; - } - - void resetDevice(); - - protected: -}; +); #define PER_KEY_DATA(dflt, \ R0C0, R0C1, R0C2, R0C3, R0C4, R0C7, R0C8, R0C9, R0C10, R0C11, \ diff --git a/src/kaleidoscope/device/technomancy/Atreus2.cpp b/src/kaleidoscope/device/technomancy/Atreus2.cpp index 13ba5daa..5bfb0fc3 100644 --- a/src/kaleidoscope/device/technomancy/Atreus2.cpp +++ b/src/kaleidoscope/device/technomancy/Atreus2.cpp @@ -19,14 +19,12 @@ #ifdef ARDUINO_AVR_ATREUS2 #include -#include namespace kaleidoscope { namespace device { namespace technomancy { -ATMEGA_KEYBOARD_DATA(Atreus2); -constexpr int8_t Atreus2::led_count; +AVR_KEYSCANNER_BOILERPLATE } } diff --git a/src/kaleidoscope/device/technomancy/Atreus2.h b/src/kaleidoscope/device/technomancy/Atreus2.h index f94f9e39..aac16e1c 100644 --- a/src/kaleidoscope/device/technomancy/Atreus2.h +++ b/src/kaleidoscope/device/technomancy/Atreus2.h @@ -21,35 +21,20 @@ #ifdef ARDUINO_AVR_ATREUS2 #include -#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" -#include "kaleidoscope/macro_helpers.h" - -#include "kaleidoscope/device/ATMegaKeyboard.h" +#include "kaleidoscope/driver/keyscanner/AVR.h" +#include "kaleidoscope/driver/bootloader/avr/Caterina.h" +#include "kaleidoscope/device/ATMega32U4Keyboard.h" namespace kaleidoscope { namespace device { namespace technomancy { -class Atreus2: public kaleidoscope::device::ATMegaKeyboard { - friend class ATMegaKeyboard; - public: - Atreus2(void) {} - - ATMEGA_KEYBOARD_CONFIG( - ROW_PIN_LIST({PIN_F6, PIN_F5, PIN_F4, PIN_F1}), - COL_PIN_LIST({PIN_F7, PIN_E2, PIN_C7, PIN_C6, PIN_B6, PIN_B5, PIN_D7, PIN_D6, PIN_D4, PIN_D5, PIN_D3, PIN_D2}) - - ) - static constexpr int8_t led_count = 0; - - static constexpr int8_t numKeys() { - return matrix_columns * matrix_rows; - } - - void resetDevice(); - protected: -}; +ATMEGA32U4_KEYBOARD( + Atreus2, Caterina, + ROW_PIN_LIST({PIN_F6, PIN_F5, PIN_F4, PIN_F1}), + COL_PIN_LIST({PIN_F7, PIN_E2, PIN_C7, PIN_C6, PIN_B6, PIN_B5, PIN_D7, PIN_D6, PIN_D4, PIN_D5, PIN_D3, PIN_D2}) +); #define PER_KEY_DATA(dflt, \ R0C0, R0C1, R0C2, R0C3, R0C4, R0C7, R0C8, R0C9, R0C10, R0C11, \ diff --git a/src/kaleidoscope/driver/bootloader/Base.h b/src/kaleidoscope/driver/bootloader/Base.h new file mode 100644 index 00000000..532f46ce --- /dev/null +++ b/src/kaleidoscope/driver/bootloader/Base.h @@ -0,0 +1,34 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::bootloader::base -- Base Bootloader Driver + * Copyright (C) 2019 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, version 3. + * + * 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 + +namespace kaleidoscope { +namespace driver { +namespace bootloader { + +class Base { + public: + Base() {} + + void setup() {} + static void rebootBootloader() {} +}; + +} +} +} diff --git a/src/kaleidoscope/driver/bootloader/None.h b/src/kaleidoscope/driver/bootloader/None.h new file mode 100644 index 00000000..d91fe105 --- /dev/null +++ b/src/kaleidoscope/driver/bootloader/None.h @@ -0,0 +1,37 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::bootloader::None -- Dummy Bootloader driver + * Copyright (C) 2019 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, version 3. + * + * 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 +#include "kaleidoscope/driver/bootloader/Base.h" + +namespace kaleidoscope { +namespace driver { +namespace bootloader { + +/* + * The purpose of this class is to serve as a default inside the base + * `kaleidoscope::device::Base` class, with a name more descriptive than + * `bootloader::Base`. In practice, one shouldn't use it, and should override + * the bootloader in the device description. + */ +class None : public kaleidoscope::driver::bootloader::Base {}; + +} +} +} diff --git a/src/kaleidoscope/driver/bootloader/avr/Caterina.h b/src/kaleidoscope/driver/bootloader/avr/Caterina.h new file mode 100644 index 00000000..7006c512 --- /dev/null +++ b/src/kaleidoscope/driver/bootloader/avr/Caterina.h @@ -0,0 +1,56 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::bootloader::avr::Caterina -- Driver for the Caterina bootloader + * Copyright (C) 2019 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, version 3. + * + * 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 +#include "kaleidoscope/driver/bootloader/Base.h" + +namespace kaleidoscope { +namespace driver { +namespace bootloader { +namespace avr { + +class Caterina : public kaleidoscope::driver::bootloader::Base { + public: + static void rebootBootloader() { + // Set the magic bits to get a Caterina-based device + // to reboot into the bootloader and stay there, rather + // than run move onward + // + // These values are the same as those defined in + // Caterina.c: + // https://github.com/arduino/ArduinoCore-avr/blob/5755ddea49fa69d6c505c772ebee5af5078e2ebf/bootloaders/caterina/Caterina.c#L130-L133 + + uint16_t bootKey = 0x7777; + uint16_t *const bootKeyPtr = reinterpret_cast(0x0800); + + // Stash the magic key + *bootKeyPtr = bootKey; + + // Set a watchdog timer + wdt_enable(WDTO_120MS); + + while (true) {} // This infinite loop ensures nothing else + // happens before the watchdog reboots us + } +}; + +} +} +} +} diff --git a/src/kaleidoscope/driver/bootloader/avr/FLIP.cpp b/src/kaleidoscope/driver/bootloader/avr/FLIP.cpp new file mode 100644 index 00000000..4ab4485f --- /dev/null +++ b/src/kaleidoscope/driver/bootloader/avr/FLIP.cpp @@ -0,0 +1,76 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::bootloader::avr::FLIP -- Driver for the Atmel FLIP bootloader + * Copyright (C) 2019 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, version 3. + * + * 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 + +#ifdef KALEIDOSCOPE_BOOTLOADER_FLIP_WORKAROUND +#include "kaleidoscope/driver/bootloader/avr/FLIP.h" + +namespace kaleidoscope { +namespace driver { +namespace bootloader { +namespace avr { + +// The FLIP bootloader does not provide a way to boot into bootloader mode from +// within the firmware: it will always start the user firmware if available, and +// if the reset wasn't triggered by the physical reset button. To enter the +// bootloader mode, we need to jump there from the user firmware ourselves. +// +// Since we do not want to unconditionally jump into bootloader, we set a static +// memory address to a magic value, to signal our intention, then reboot via the +// watchdog timer. We need to reboot to clear registers and have a clean slate +// for the bootloader. +// +// For this to work, we need to run our signal checking code before `main()`, so +// we put the checker function into a special section that does that. +// +// Since this code cannot be optimized out on keyboards that use another +// bootloader, we need to guard this implementation with an ifdef (see above). +// +// For more information, see: +// http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html + +#define BOOTLOADER_RESET_KEY 0xB007B007 +static uint32_t reset_key __attribute__((section(".noinit"))); + +/* + * This function runs before main(), and jumps to the bootloader after a reset + * initiated by .resetDevice(). + */ +static void _bootloader_jump_after_watchdog_reset() +__attribute__((used, naked, section(".init3"))); +static void _bootloader_jump_after_watchdog_reset() { + if ((MCUSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) { + reset_key = 0; + + ((void (*)(void))(((FLASHEND + 1L) - 4096) >> 1))(); + } +} + +void FLIP::rebootBootloader() { + reset_key = BOOTLOADER_RESET_KEY; + wdt_enable(WDTO_250MS); + while (true) {} // This infinite loop ensures nothing else + // happens before the watchdog reboots us +} + +} +} +} +} + +#endif diff --git a/src/kaleidoscope/driver/bootloader/avr/FLIP.h b/src/kaleidoscope/driver/bootloader/avr/FLIP.h new file mode 100644 index 00000000..bc79d59f --- /dev/null +++ b/src/kaleidoscope/driver/bootloader/avr/FLIP.h @@ -0,0 +1,41 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::bootloader::avr::FLIP -- Driver for the Atmel FLIP bootloader + * Copyright (C) 2019 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, version 3. + * + * 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 + +#ifndef KALEIDOSCOPE_BOOTLOADER_FLIP_WORKAROUND +#error To use the FLIP bootloader driver, KALEIDOSCOPE_BOOTLOADER_FLIP_WORKAROUND *must* be defined prior to including this header! +#endif + +#include "kaleidoscope/driver/bootloader/Base.h" + +namespace kaleidoscope { +namespace driver { +namespace bootloader { +namespace avr { + +class FLIP : public kaleidoscope::driver::bootloader::Base { + public: + static void rebootBootloader(); +}; + +} +} +} +} diff --git a/src/kaleidoscope/driver/bootloader/avr/HalfKay.h b/src/kaleidoscope/driver/bootloader/avr/HalfKay.h new file mode 100644 index 00000000..651db242 --- /dev/null +++ b/src/kaleidoscope/driver/bootloader/avr/HalfKay.h @@ -0,0 +1,72 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::bootloader::avr::HalfKay -- Driver for the HalfKay bootloader + * Copyright (C) 2019 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, version 3. + * + * 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 +#include "kaleidoscope/driver/bootloader/Base.h" + +namespace kaleidoscope { +namespace driver { +namespace bootloader { +namespace avr { + +class HalfKay : public kaleidoscope::driver::bootloader::Base { + public: + // To reset a Teensy with the HalfKay bootloader, we need to disable all + // interrupts, all peripherals we have attached, USB, the watchdog timer, etc. + // Once done, we can jump to the bootloader address. + // + // Documentation: https://www.pjrc.com/teensy/jump_to_bootloader.html + static void rebootBootloader() { + 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"); + } +}; + +} +} +} +} diff --git a/src/kaleidoscope/driver/keyscanner/AVR.h b/src/kaleidoscope/driver/keyscanner/AVR.h new file mode 100644 index 00000000..1481fc34 --- /dev/null +++ b/src/kaleidoscope/driver/keyscanner/AVR.h @@ -0,0 +1,239 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::keyscanner::AVR -- AVR-based keyscanner component + * Copyright (C) 2018-2019 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, version 3. + * + * 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 +#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h" + +#include "kaleidoscope/macro_helpers.h" +#include "kaleidoscope/driver/keyscanner/Base.h" +#include "kaleidoscope/device/avr/pins_and_ports.h" + +#include + +#define ROW_PIN_LIST(...) __VA_ARGS__ +#define COL_PIN_LIST(...) __VA_ARGS__ + +#define AVR_KEYSCANNER_PROPS(ROW_PINS_, COL_PINS_) \ + KEYSCANNER_PROPS(NUM_ARGS(ROW_PINS_), NUM_ARGS(COL_PINS_)); \ + static constexpr uint8_t matrix_row_pins[matrix_rows] = ROW_PINS_; \ + static constexpr uint8_t matrix_col_pins[matrix_columns] = COL_PINS_; + +#define AVR_KEYSCANNER_BOILERPLATE \ + KEYSCANNER_PROPS_BOILERPLATE(kaleidoscope::Device::KeyScannerProps); \ + constexpr uint8_t kaleidoscope::Device::KeyScannerProps::matrix_row_pins[matrix_rows]; \ + constexpr uint8_t kaleidoscope::Device::KeyScannerProps::matrix_col_pins[matrix_columns]; \ + template<> \ + volatile uint16_t kaleidoscope::Device::KeyScanner::previousKeyState_[kaleidoscope::Device::KeyScannerProps::matrix_rows] = {}; \ + template<> \ + volatile uint16_t kaleidoscope::Device::KeyScanner::keyState_[kaleidoscope::Device::KeyScannerProps::matrix_rows] = {}; \ + template<> \ + uint16_t kaleidoscope::Device::KeyScanner::masks_[kaleidoscope::Device::KeyScannerProps::matrix_rows] = {}; \ + template<> \ + uint8_t kaleidoscope::Device::KeyScanner::debounce_matrix_[kaleidoscope::Device::KeyScannerProps::matrix_rows][kaleidoscope::Device::KeyScannerProps::matrix_columns] = {}; \ + \ + ISR(TIMER1_OVF_vect) { \ + Kaleidoscope.device().readMatrix(); \ + } + +namespace kaleidoscope { +namespace driver { +namespace keyscanner { + +struct AVRProps: kaleidoscope::driver::keyscanner::BaseProps { + static const uint8_t debounce = 3; + + /* + * The following two lines declare an empty array. Both of these must be + * shadowed by the descendant keyscanner description class. + */ + static constexpr uint8_t matrix_row_pins[] = {}; + static constexpr uint8_t matrix_col_pins[] = {}; +}; + +template +class AVR : public kaleidoscope::driver::keyscanner::Base<_KeyScannerProps> { + private: + typedef AVR<_KeyScannerProps> ThisType; + + public: + void setup() { + static_assert( + sizeof(_KeyScannerProps::matrix_row_pins) > 0, + "The key scanner description has an empty array of matrix row pins." + ); + static_assert( + sizeof(_KeyScannerProps::matrix_col_pins) > 0, + "The key scanner description has an empty array of matrix column pins." + ); + + wdt_disable(); + + for (uint8_t i = 0; i < _KeyScannerProps::matrix_columns; i++) { + DDR_INPUT(_KeyScannerProps::matrix_col_pins[i]); + ENABLE_PULLUP(_KeyScannerProps::matrix_col_pins[i]); + } + + for (uint8_t i = 0; i < _KeyScannerProps::matrix_rows; i++) { + DDR_OUTPUT(_KeyScannerProps::matrix_row_pins[i]); + OUTPUT_HIGH(_KeyScannerProps::matrix_row_pins[i]); + } + + /* Set up Timer1 for 1700usec */ + TCCR1B = _BV(WGM13); + TCCR1A = 0; + + const uint32_t cycles = (F_CPU / 2000000) * 1700; + + ICR1 = cycles; + TCCR1B = _BV(WGM13) | _BV(CS10); + TIMSK1 = _BV(TOIE1); + } + + void __attribute__((optimize(3))) readMatrix(void) { + for (uint8_t current_row = 0; current_row < _KeyScannerProps::matrix_rows; current_row++) { + uint16_t mask, cols; + + mask = debounceMaskForRow(current_row); + + OUTPUT_TOGGLE(_KeyScannerProps::matrix_row_pins[current_row]); + cols = (readCols() & mask) | (keyState_[current_row] & ~mask); + OUTPUT_TOGGLE(_KeyScannerProps::matrix_row_pins[current_row]); + debounceRow(cols ^ keyState_[current_row], current_row); + keyState_[current_row] = cols; + } + } + + void scanMatrix() { + actOnMatrixScan(); + } + + void __attribute__((optimize(3))) actOnMatrixScan() { + for (byte row = 0; row < _KeyScannerProps::matrix_rows; row++) { + for (byte col = 0; col < _KeyScannerProps::matrix_columns; col++) { + uint8_t keyState = (bitRead(previousKeyState_[row], col) << 0) | + (bitRead(keyState_[row], col) << 1); + if (keyState) { + ThisType::handleKeyswitchEvent(Key_NoKey, typename _KeyScannerProps::KeyAddr(row, col), keyState); + } + } + previousKeyState_[row] = keyState_[row]; + } + } + + uint8_t pressedKeyswitchCount() { + uint8_t count = 0; + + for (int8_t r = 0; r < _KeyScannerProps::matrix_rows; r++) { + count += __builtin_popcount(keyState_[r]); + } + return count; + } + bool isKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) { + return (bitRead(keyState_[key_addr.row()], + key_addr.col()) != 0); + } + + uint8_t previousPressedKeyswitchCount() { + uint8_t count = 0; + + for (int8_t r = 0; r < _KeyScannerProps::matrix_rows; r++) { + count += __builtin_popcount(previousKeyState_[r]); + } + return count; + } + bool wasKeyswitchPressed(typename _KeyScannerProps::KeyAddr key_addr) { + return (bitRead(previousKeyState_[key_addr.row()], + key_addr.col()) != 0); + } + + void maskKey(typename _KeyScannerProps::KeyAddr key_addr) { + if (!key_addr.isValid()) + return; + + bitWrite(masks_[key_addr.row()], key_addr.col(), 1); + } + void unMaskKey(typename _KeyScannerProps::KeyAddr key_addr) { + if (!key_addr.isValid()) + return; + + bitWrite(masks_[key_addr.row()], key_addr.col(), 0); + } + bool isKeyMasked(typename _KeyScannerProps::KeyAddr key_addr) { + if (!key_addr.isValid()) + return false; + + return bitRead(masks_[key_addr.row()], + key_addr.col()); + } + + private: + typedef _KeyScannerProps KeyScannerProps_; + static volatile uint16_t previousKeyState_[_KeyScannerProps::matrix_rows]; + static volatile uint16_t keyState_[_KeyScannerProps::matrix_rows]; + static uint16_t masks_[_KeyScannerProps::matrix_rows]; + static uint8_t debounce_matrix_[_KeyScannerProps::matrix_rows][_KeyScannerProps::matrix_columns]; + + /* + * This function has loop unrolling disabled on purpose: we want to give the + * hardware enough time to produce stable PIN reads for us. If we unroll the + * loop, we will not have that, because even with the NOP, the codepath is too + * fast. If we don't have stable reads, then entire rows or columns will behave + * erratically. + * + * For this reason, we ask the compiler to not unroll our loop, which in turn, + * gives hardware enough time to produce stable reads, at the cost of a little + * bit of speed. + * + * Do not remove the attribute! + */ + __attribute__((optimize("no-unroll-loops"))) + uint16_t readCols() { + uint16_t results = 0x00 ; + for (uint8_t i = 0; i < _KeyScannerProps::matrix_columns; i++) { + asm("NOP"); // We need to pause a beat before reading or we may read before the pin is hot + results |= (!READ_PIN(_KeyScannerProps::matrix_col_pins[i]) << i); + } + return results; + } + + uint16_t debounceMaskForRow(uint8_t row) { + uint16_t result = 0; + + for (uint16_t c = 0; c < _KeyScannerProps::matrix_columns; ++c) { + if (debounce_matrix_[row][c]) { + --debounce_matrix_[row][c]; + } else { + result |= _BV(c); + } + } + return result; + } + + void debounceRow(uint16_t change, uint8_t row) { + for (uint16_t i = 0; i < _KeyScannerProps::matrix_columns; ++i) { + if (change & _BV(i)) { + debounce_matrix_[row][i] = _KeyScannerProps::debounce; + } + } + } +}; + +} +} +} diff --git a/src/kaleidoscope/driver/keyscanner/Base.h b/src/kaleidoscope/driver/keyscanner/Base.h new file mode 100644 index 00000000..f9094ffb --- /dev/null +++ b/src/kaleidoscope/driver/keyscanner/Base.h @@ -0,0 +1,82 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::keyscanner::base -- Keyscanner base class + * Copyright (C) 2019 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, version 3. + * + * 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 + +#include "kaleidoscope/key_defs.h" +#include "kaleidoscope/MatrixAddr.h" + +#define KEYSCANNER_PROPS(ROWS_, COLS_) \ + static constexpr uint8_t matrix_rows = ROWS_; \ + static constexpr uint8_t matrix_columns = COLS_; \ + typedef MatrixAddr KeyAddr; + +#define KEYSCANNER_PROPS_BOILERPLATE(BOARD) \ + const uint8_t BOARD::matrix_rows; \ + const uint8_t BOARD::matrix_columns; + +namespace kaleidoscope { +namespace driver { +namespace keyscanner { + +struct BaseProps { + static constexpr uint8_t matrix_rows = 0; + static constexpr uint8_t matrix_columns = 0; + + typedef MatrixAddr KeyAddr; +}; + +template +class Base { + public: + Base() {} + + typedef typename _KeyScannerProps::KeyAddr KeyAddr; + + static void handleKeyswitchEvent(Key mappedKey, KeyAddr key_addr, uint8_t keyState); + + void setup() {} + void readMatrix() {} + void scanMatrix() {} + void actOnMatrixScan() {} + + uint8_t pressedKeyswitchCount() { + return 0; + } + bool isKeyswitchPressed(KeyAddr key_addr) { + return false; + } + + uint8_t previousPressedKeyswitchCount() { + return 0; + } + bool wasKeyswitchPressed(KeyAddr key_addr) { + return false; + } + + void maskKey(KeyAddr key_addr) {} + void unMaskKey(KeyAddr key_addr) {} + bool isKeyMasked(KeyAddr key_addr) { + return false; + } +}; + +} +} +} diff --git a/src/kaleidoscope/driver/keyscanner/None.h b/src/kaleidoscope/driver/keyscanner/None.h new file mode 100644 index 00000000..8ad6b155 --- /dev/null +++ b/src/kaleidoscope/driver/keyscanner/None.h @@ -0,0 +1,38 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::keyscanner::None -- Dummy keyscanner component + * Copyright (C) 2019 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, version 3. + * + * 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 + +#include "kaleidoscope/driver/keyscanner/Base.h" + +namespace kaleidoscope { +namespace driver { +namespace keyscanner { + +/* + * The purpose of this class is to serve as a default inside the base + * `kaleidoscope::device::Base` class, with a name more descriptive than + * `keyscanner::Base`. In practice, one shouldn't use it, and should override + * the bootloader in the device description. + */ +class None : public kaleidoscope::driver::keyscanner::Base {}; + +} +} +} diff --git a/src/kaleidoscope/driver/led/Base.h b/src/kaleidoscope/driver/led/Base.h new file mode 100644 index 00000000..db82a747 --- /dev/null +++ b/src/kaleidoscope/driver/led/Base.h @@ -0,0 +1,120 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::led::Base -- LED hardware driver base class for Kaleidoscope + * Copyright (C) 2019 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, version 3. + * + * 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 + +#ifndef CRGB +#error cRGB and CRGB *must* be defined before including this header! +#endif + +namespace kaleidoscope { +namespace driver { +namespace led { + +struct BaseProps { + static constexpr uint8_t led_count = 0; +}; + +template +class Base { + public: + Base() {} + + void setup() {} + void syncLeds(void) {} + void setCrgbAt(uint8_t i, cRGB color) {} + cRGB getCrgbAt(uint8_t i) { + cRGB c = { + 0, 0, 0 + }; + return c; + } + uint8_t getLedIndex(uint8_t key_offset) { + return 0; + } + + static class LEDs { + private: + uint8_t offset_; + public: + LEDs() : offset_(0) {} + LEDs(uint8_t offset) : offset_(offset) {} + + typedef LEDs ThisType; + + constexpr uint8_t offset() { + return offset_; + } + + ThisType operator++() { + ++offset_; + return *this; + } + + ThisType operator++(int) { // postfix ++ + ThisType copy(*this); + ++*this; // call the prefix increment + return copy; + } + + ThisType operator--() { + --offset_; + return *this; + } + + ThisType operator--(int) { // postfix ++ + ThisType copy(*this); + --*this; // call the prefix increment + return copy; + } + + bool operator==(const ThisType &other) const { + return offset_ == other.offset_; + } + + bool operator!=(const ThisType &other) const { + return offset_ != other.offset_; + } + + struct Range { + typedef ThisType Iterator; + static constexpr ThisType begin() { + return ThisType(uint8_t(0)); + } + static constexpr ThisType end() { + return ThisType(_LEDDriverProps::led_count); + } + }; + + static constexpr Range all() { + return Range{}; + } + constexpr const ThisType &operator*() const { + return *this; + } + constexpr bool isValid(uint8_t index) { + return (_LEDDriverProps::led_count > 0 && index < _LEDDriverProps::led_count); + } + } LEDs; + + protected: + typedef _LEDDriverProps Props_; +}; + +} +} +} diff --git a/src/kaleidoscope/driver/led/None.h b/src/kaleidoscope/driver/led/None.h new file mode 100644 index 00000000..19328573 --- /dev/null +++ b/src/kaleidoscope/driver/led/None.h @@ -0,0 +1,46 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::led::None -- Dummy LED hardware driver for Kaleidoscope + * Copyright (C) 2019 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, version 3. + * + * 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 + +#ifndef CRGB + +struct cRGB { + uint8_t r, g, b; +}; + +#define CRGB(r,g,b) (cRGB){b, g, r} + +#endif + +#include "kaleidoscope/driver/led/Base.h" + +namespace kaleidoscope { +namespace driver { +namespace led { + +/* + * The purpose of this class is to serve as a default inside the base + * `kaleidoscope::device::Base` class, with a name more descriptive than + * `leddriver::Base`. In practice, one shouldn't use it, and should override the + * bootloader in the device description. + */ +class None : public kaleidoscope::driver::led::Base {}; + +} +} +} diff --git a/src/kaleidoscope/driver/MCU.h b/src/kaleidoscope/driver/mcu/ATMega32U4.h similarity index 83% rename from src/kaleidoscope/driver/MCU.h rename to src/kaleidoscope/driver/mcu/ATMega32U4.h index e04d76e1..8b52a2d9 100644 --- a/src/kaleidoscope/driver/MCU.h +++ b/src/kaleidoscope/driver/mcu/ATMega32U4.h @@ -1,5 +1,5 @@ /* -*- mode: c++ -*- - * kaleidoscope::driver::mcu -- Various MCU drivers + * driver::MCU::ATMega32U4 -- ATMega32U4 MCU driver for Kaleidoscope * Copyright (C) 2019 Keyboard.io, Inc * * This program is free software: you can redistribute it and/or modify it under @@ -17,15 +17,22 @@ #pragma once +#include "kaleidoscope/driver/mcu/Base.h" + namespace kaleidoscope { namespace driver { namespace mcu { -class ATMega32U4 { +class ATMega32U4 : public kaleidoscope::driver::mcu::Base { public: - ATMega32U4() {} + void detachFromHost() { + UDCON |= _BV(DETACH); + } + void attachToHost() { + UDCON &= ~_BV(DETACH); + } - static inline void disableJTAG() { + static void disableJTAG() { /* These two lines here are the result of many hours spent chasing ghosts. * These are great lines, and we love them dearly, for they make a set of * pins that would otherwise be reserved for JTAG accessible from the @@ -48,10 +55,12 @@ class ATMega32U4 { MCUCR |= (1 << JTD); } - static inline void disableClockDivision() { + static void disableClockDivision() { CLKPR = (1 << CLKPCE); CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); } + + void setup() {} }; } diff --git a/src/kaleidoscope/driver/mcu/Base.h b/src/kaleidoscope/driver/mcu/Base.h new file mode 100644 index 00000000..671738ab --- /dev/null +++ b/src/kaleidoscope/driver/mcu/Base.h @@ -0,0 +1,47 @@ +/* -*- mode: c++ -*- + * driver::mcu::Base -- MCU driver base class for Kaleidoscope + * Copyright (C) 2019 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, version 3. + * + * 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 + +namespace kaleidoscope { +namespace driver { +namespace mcu { + +class Base { + public: + Base() {} + + void setup() {} + + /** + * Detach the device from the host. + * + * Must detach the device, without rebooting or cutting power. Only the end + * points should get detached, the device must remain powered on. + */ + void detachFromHost() {} + /** + * Attack the device to the host. + * + * Must restore the link detachFromHost severed. + */ + void attachToHost() {} +}; + +} +} +} diff --git a/src/kaleidoscope/driver/mcu/None.h b/src/kaleidoscope/driver/mcu/None.h new file mode 100644 index 00000000..2a1a397e --- /dev/null +++ b/src/kaleidoscope/driver/mcu/None.h @@ -0,0 +1,36 @@ +/* -*- mode: c++ -*- + * driver::MCU::None -- Dummy MCU driver for Kaleidoscope + * Copyright (C) 2019 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, version 3. + * + * 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/driver/mcu/Base.h" + +namespace kaleidoscope { +namespace driver { +namespace mcu { + +/* + * The purpose of this class is to serve as a default inside the base + * `kaleidoscope::device::Base` class, with a name more descriptive than + * `mcu::Base`. In practice, one shouldn't use it, and should override the + * bootloader in the device description. + */ +class None : public kaleidoscope::driver::mcu::Base {}; + +} +} +} diff --git a/src/kaleidoscope/driver/storage/ATMega32U4EEPROMProps.h b/src/kaleidoscope/driver/storage/ATMega32U4EEPROMProps.h new file mode 100644 index 00000000..2db9085a --- /dev/null +++ b/src/kaleidoscope/driver/storage/ATMega32U4EEPROMProps.h @@ -0,0 +1,32 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::storage::ATMega32U4StorageProps -- Storage driver props for ATMega32U4 + * Copyright (C) 2019 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, version 3. + * + * 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/driver/storage/AVREEPROM.h" + +namespace kaleidoscope { +namespace driver { +namespace storage { + +struct ATMega32U4EEPROMProps : kaleidoscope::driver::storage::AVREEPROMProps { + static constexpr uint16_t length = 1024; +}; + +} +} +} diff --git a/src/kaleidoscope/driver/storage/AVREEPROM.h b/src/kaleidoscope/driver/storage/AVREEPROM.h new file mode 100644 index 00000000..6a39f2b3 --- /dev/null +++ b/src/kaleidoscope/driver/storage/AVREEPROM.h @@ -0,0 +1,59 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::storage::AVREEPROM -- Storage driver with AVR EEPROM backend + * Copyright (C) 2019 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, version 3. + * + * 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/driver/storage/Base.h" +#include + +namespace kaleidoscope { +namespace driver { +namespace storage { + +struct AVREEPROMProps : kaleidoscope::driver::storage::BaseProps { + static constexpr uint16_t length = 0; +}; + +template +class AVREEPROM : public kaleidoscope::driver::storage::Base<_StorageProps> { + public: + template + static T& get(uint16_t offset, T& t) { + return EEPROM.get(offset, t); + } + + template + static const T& put(uint16_t offset, T& t) { + return EEPROM.put(offset, t); + } + + uint8_t read(int idx) { + return EEPROM.read(idx); + } + + void write(int idx, uint8_t val) { + EEPROM.write(idx, val); + } + + void update(int idx, uint8_t val) { + EEPROM.update(idx, val); + } +}; + +} +} +} diff --git a/src/kaleidoscope/driver/storage/Base.h b/src/kaleidoscope/driver/storage/Base.h new file mode 100644 index 00000000..97ae7f4c --- /dev/null +++ b/src/kaleidoscope/driver/storage/Base.h @@ -0,0 +1,59 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::storage::Base -- Base Storage driver class + * Copyright (C) 2019 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, version 3. + * + * 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 + +namespace kaleidoscope { +namespace driver { +namespace storage { + +struct BaseProps { + static constexpr uint16_t length = 0; +}; + +template +class Base { + public: + template + static T& get(uint16_t offset, T& t) { + return t; + } + + template + static const T& put(uint16_t offset, T& t) { + return t; + } + + uint8_t read(int idx) { + return 0; + } + + void write(int idx, uint8_t val) {} + + void update(int idx, uint8_t val) {} + + const uint16_t length() { + return _StorageProps::length; + } + + void setup() {} + void commit() {} +}; + +} +} +} diff --git a/src/kaleidoscope/driver/storage/None.h b/src/kaleidoscope/driver/storage/None.h new file mode 100644 index 00000000..f45b141a --- /dev/null +++ b/src/kaleidoscope/driver/storage/None.h @@ -0,0 +1,36 @@ +/* -*- mode: c++ -*- + * kaleidoscope::driver::storage::None -- Dummy Storage hardware driver for Kaleidoscope + * Copyright (C) 2019 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, version 3. + * + * 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/driver/storage/Base.h" + +namespace kaleidoscope { +namespace driver { +namespace storage { + +/* + * The purpose of this class is to serve as a default inside the base + * `kaleidoscope::device::Base` class, with a name more descriptive than + * `storage::Base`. In practice, one shouldn't use it, and should override the + * bootloader in the device description. + */ +class None : public kaleidoscope::driver::storage::Base {}; + +} +} +} diff --git a/src/kaleidoscope/key_events.h b/src/kaleidoscope/key_events.h index 41e2accf..84945703 100644 --- a/src/kaleidoscope/key_events.h +++ b/src/kaleidoscope/key_events.h @@ -72,7 +72,20 @@ * currentState may be flagged INJECTED, which signals that the event was * injected, and is not a direct result of a keypress, coming from the scanner. */ -void handleKeyswitchEvent(Key mappedKey, KeyAddr key_addr, uint8_t keyState); +void handleKeyswitchEvent(Key mappedKey, kaleidoscope::Device::Props::KeyScannerProps::KeyAddr key_addr, uint8_t keyState); DEPRECATED(ROW_COL_FUNC) inline void handleKeyswitchEvent(Key mappedKey, byte row, byte col, uint8_t keyState) { handleKeyswitchEvent(mappedKey, KeyAddr(row, col), keyState); } + +namespace kaleidoscope { +namespace driver { +namespace keyscanner { + +template<> +inline +void Base::handleKeyswitchEvent(Key mappedKey, kaleidoscope::Device::Props::KeyScannerProps::KeyAddr key_addr, uint8_t keyState) { + ::handleKeyswitchEvent(mappedKey, key_addr, keyState); +} +} // namespace keyscanner +} // namespace driver +} // namespace kaleidoscope diff --git a/src/kaleidoscope/plugin/DynamicMacros.cpp b/src/kaleidoscope/plugin/DynamicMacros.cpp index bf0bdc67..ff07cdef 100644 --- a/src/kaleidoscope/plugin/DynamicMacros.cpp +++ b/src/kaleidoscope/plugin/DynamicMacros.cpp @@ -233,6 +233,7 @@ EventHandlerResult DynamicMacros::onFocusEvent(const char *command) { Kaleidoscope.storage().update(storage_base_ + pos++, b); } + Kaleidoscope.storage().commit(); updateDynamicMacroCache(); } } diff --git a/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp b/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp index 512e8ce9..b1cdb0f2 100644 --- a/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp +++ b/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp @@ -39,6 +39,7 @@ void EEPROMKeymapProgrammer::nextState(void) { case WAIT_FOR_CODE: case WAIT_FOR_SOURCE_KEY: ::EEPROMKeymap.updateKey(update_position_, new_key_); + Kaleidoscope.storage().commit(); cancel(); break; } diff --git a/src/kaleidoscope/plugin/EEPROM-Keymap.cpp b/src/kaleidoscope/plugin/EEPROM-Keymap.cpp index 7b56da83..aa7cf0cd 100644 --- a/src/kaleidoscope/plugin/EEPROM-Keymap.cpp +++ b/src/kaleidoscope/plugin/EEPROM-Keymap.cpp @@ -157,6 +157,7 @@ EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) { updateKey(i, k); i++; } + Kaleidoscope.storage().commit(); } return EventHandlerResult::EVENT_CONSUMED; diff --git a/src/kaleidoscope/plugin/EEPROM-Settings.cpp b/src/kaleidoscope/plugin/EEPROM-Settings.cpp index f987fb2b..2789e4cc 100644 --- a/src/kaleidoscope/plugin/EEPROM-Settings.cpp +++ b/src/kaleidoscope/plugin/EEPROM-Settings.cpp @@ -50,6 +50,7 @@ EventHandlerResult EEPROMSettings::onSetup() { * encounter a firmware with no version defined, we'll set sensible * defaults. */ Kaleidoscope.storage().put(0, settings_); + Kaleidoscope.storage().commit(); } return EventHandlerResult::OK; } @@ -147,6 +148,7 @@ uint16_t EEPROMSettings::used(void) { void EEPROMSettings::update(void) { Kaleidoscope.storage().put(0, settings_); + Kaleidoscope.storage().commit(); is_valid_ = true; } diff --git a/src/kaleidoscope/plugin/FingerPainter.cpp b/src/kaleidoscope/plugin/FingerPainter.cpp index 661a4bc5..1e5691c2 100644 --- a/src/kaleidoscope/plugin/FingerPainter.cpp +++ b/src/kaleidoscope/plugin/FingerPainter.cpp @@ -103,6 +103,7 @@ EventHandlerResult FingerPainter::onFocusEvent(const char *command) { for (uint16_t i = 0; i < Kaleidoscope.device().numKeys() / 2; i++) { Kaleidoscope.storage().update(color_base_ + i, 0); } + Kaleidoscope.storage().commit(); return EventHandlerResult::OK; } diff --git a/src/kaleidoscope/plugin/HostOS.cpp b/src/kaleidoscope/plugin/HostOS.cpp index cb1a1e9b..d38fc638 100644 --- a/src/kaleidoscope/plugin/HostOS.cpp +++ b/src/kaleidoscope/plugin/HostOS.cpp @@ -41,6 +41,7 @@ EventHandlerResult HostOS::onSetup(void) { void HostOS::os(hostos::Type new_os) { os_ = new_os; Kaleidoscope.storage().update(eeprom_slice_, os_); + Kaleidoscope.storage().commit(); } } diff --git a/src/kaleidoscope/plugin/LED-Palette-Theme.cpp b/src/kaleidoscope/plugin/LED-Palette-Theme.cpp index 3d780a2c..e733e83d 100644 --- a/src/kaleidoscope/plugin/LED-Palette-Theme.cpp +++ b/src/kaleidoscope/plugin/LED-Palette-Theme.cpp @@ -96,6 +96,7 @@ void LEDPaletteTheme::updateColorIndexAtPosition(uint16_t map_base, uint16_t pos indexes = (color_index << 4) + other; } Kaleidoscope.storage().update(map_base + position / 2, indexes); + Kaleidoscope.storage().commit(); } EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *command) { @@ -132,6 +133,7 @@ EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *command) { Kaleidoscope.storage().put(palette_base_ + i * sizeof(color), color); i++; } + Kaleidoscope.storage().commit(); ::LEDControl.refreshAll(); @@ -174,6 +176,7 @@ EventHandlerResult LEDPaletteTheme::themeFocusEvent(const char *command, Kaleidoscope.storage().update(theme_base + pos, indexes); pos++; } + Kaleidoscope.storage().commit(); ::LEDControl.refreshAll(); diff --git a/src/kaleidoscope/plugin/LEDControl.cpp b/src/kaleidoscope/plugin/LEDControl.cpp index 52788754..96306104 100644 --- a/src/kaleidoscope/plugin/LEDControl.cpp +++ b/src/kaleidoscope/plugin/LEDControl.cpp @@ -96,12 +96,12 @@ void LEDControl::set_all_leds_to(uint8_t r, uint8_t g, uint8_t b) { } void LEDControl::set_all_leds_to(cRGB color) { - for (int8_t i = 0; i < Kaleidoscope.device().led_count; i++) { - setCrgbAt(i, color); + for (auto led_index : Kaleidoscope.device().LEDs().all()) { + setCrgbAt(led_index.offset(), color); } } -void LEDControl::setCrgbAt(int8_t led_index, cRGB crgb) { +void LEDControl::setCrgbAt(uint8_t led_index, cRGB crgb) { Kaleidoscope.device().setCrgbAt(led_index, crgb); } @@ -109,7 +109,7 @@ void LEDControl::setCrgbAt(KeyAddr key_addr, cRGB color) { Kaleidoscope.device().setCrgbAt(key_addr, color); } -cRGB LEDControl::getCrgbAt(int8_t led_index) { +cRGB LEDControl::getCrgbAt(uint8_t led_index) { return Kaleidoscope.device().getCrgbAt(led_index); } cRGB LEDControl::getCrgbAt(KeyAddr key_addr) { @@ -239,15 +239,15 @@ EventHandlerResult FocusLEDCommand::onFocusEvent(const char *command) { } case THEME: { if (::Focus.isEOL()) { - for (auto key_addr : KeyAddr::all()) { - cRGB c = ::LEDControl.getCrgbAt(key_addr); + for (auto led_index : Kaleidoscope.device().LEDs().all()) { + cRGB c = ::LEDControl.getCrgbAt(led_index.offset()); ::Focus.send(c); } break; } - for (auto key_addr : KeyAddr::all()) { + for (auto led_index : Kaleidoscope.device().LEDs().all()) { if (::Focus.isEOL()) { break; } @@ -256,7 +256,7 @@ EventHandlerResult FocusLEDCommand::onFocusEvent(const char *command) { ::Focus.read(color); - ::LEDControl.setCrgbAt(key_addr, color); + ::LEDControl.setCrgbAt(led_index.offset(), color); } break; } diff --git a/src/kaleidoscope/plugin/LEDControl.h b/src/kaleidoscope/plugin/LEDControl.h index 8001bacf..72fb8bf7 100644 --- a/src/kaleidoscope/plugin/LEDControl.h +++ b/src/kaleidoscope/plugin/LEDControl.h @@ -85,12 +85,12 @@ class LEDControl : public kaleidoscope::Plugin { return 0; } - static void setCrgbAt(int8_t led_index, cRGB crgb); + static void setCrgbAt(uint8_t led_index, cRGB crgb); static void setCrgbAt(KeyAddr key_addr, cRGB color); DEPRECATED(ROW_COL_FUNC) static void setCrgbAt(byte row, byte col, cRGB color) { setCrgbAt(KeyAddr(row, col), color); } - static cRGB getCrgbAt(int8_t led_index); + static cRGB getCrgbAt(uint8_t led_index); static cRGB getCrgbAt(KeyAddr key_addr); DEPRECATED(ROW_COL_FUNC) static cRGB getCrgbAt(byte row, byte col) { return getCrgbAt(KeyAddr(row, col)); diff --git a/src/kaleidoscope/plugin/LEDEffect-Chase.cpp b/src/kaleidoscope/plugin/LEDEffect-Chase.cpp index 15afceab..5f74fe9e 100644 --- a/src/kaleidoscope/plugin/LEDEffect-Chase.cpp +++ b/src/kaleidoscope/plugin/LEDEffect-Chase.cpp @@ -30,15 +30,15 @@ void LEDChaseEffect::TransientLEDMode::update(void) { // The red LED is at `pos_`; the blue one follows behind. `direction_` is // either +1 or -1; `distance_` is the gap between them. - int8_t pos2 = pos_ - (direction_ * parent_->distance_); + uint8_t pos2 = pos_ - (direction_ * parent_->distance_); // First, we turn off the LEDs that were turned on in the previous update. - // `pos_` is always in the valid range (0 <= pos_ < Kaleidoscope.device().led_count), but after it + // `pos_` is always in the valid range (0 <= pos_ < LED_COUNT), but after it // changes direction, for the first few updates, `pos2` will be out of bounds. // Since it's an unsigned integer, even when it would have a value below zero, // it underflows and so one test is good for both ends of the range. ::LEDControl.setCrgbAt(pos_, CRGB(0, 0, 0)); - if (pos2 < Kaleidoscope.device().led_count) + if (Kaleidoscope.device().LEDs().isValid(pos2)) ::LEDControl.setCrgbAt(pos2, CRGB(0, 0, 0)); // Next, we adjust the red light's position. If the direction hasn't changed (the red @@ -46,9 +46,9 @@ void LEDChaseEffect::TransientLEDMode::update(void) { // one. If the new position puts it out of bounds, we reverse the direction, and bring // it back in bounds. When this happens, the blue light "jumps" behind the red one, and // will be out of bounds. The simplest way to do this is to assign it a value that is - // known to be invalid (Kaleidoscope.device().led_count). + // known to be invalid (LED_COUNT). pos_ += direction_; - if (pos_ < Kaleidoscope.device().led_count && pos_ > 0) { + if (Kaleidoscope.device().LEDs().isValid(pos_)) { pos2 += direction_; } else { direction_ = -direction_; @@ -59,7 +59,7 @@ void LEDChaseEffect::TransientLEDMode::update(void) { // Last, we turn on the LEDs at their new positions. As before, the blue light (pos2) is // only set if it's in the valid LED range. ::LEDControl.setCrgbAt(pos_, CRGB(255, 0, 0)); - if (pos2 < Kaleidoscope.device().led_count) + if (Kaleidoscope.device().LEDs().isValid(pos2)) ::LEDControl.setCrgbAt(pos2, CRGB(0, 0, 255)); } diff --git a/src/kaleidoscope/plugin/LEDEffect-Chase.h b/src/kaleidoscope/plugin/LEDEffect-Chase.h index 31a93764..9d09f094 100644 --- a/src/kaleidoscope/plugin/LEDEffect-Chase.h +++ b/src/kaleidoscope/plugin/LEDEffect-Chase.h @@ -58,7 +58,7 @@ class LEDChaseEffect : public Plugin, const LEDChaseEffect *parent_; - int8_t pos_ = uint8_t(0); + uint8_t pos_ = uint8_t(0); int8_t direction_ = 1; uint16_t last_update_; }; diff --git a/src/kaleidoscope/plugin/LEDEffect-Rainbow.cpp b/src/kaleidoscope/plugin/LEDEffect-Rainbow.cpp index 8ccd4dc6..76fd05f0 100644 --- a/src/kaleidoscope/plugin/LEDEffect-Rainbow.cpp +++ b/src/kaleidoscope/plugin/LEDEffect-Rainbow.cpp @@ -61,18 +61,18 @@ void LEDRainbowWaveEffect::TransientLEDMode::update(void) { rainbow_last_update += parent_->rainbow_update_delay; } - for (auto key_addr : KeyAddr::all()) { - uint16_t key_hue = rainbow_hue + 16 * (key_addr.toInt() / 4); - // We want key_hue to be capped at 255, but we do not want to clip it to + for (auto led_index : Kaleidoscope.device().LEDs().all()) { + uint16_t led_hue = rainbow_hue + 16 * (led_index.offset() / 4); + // We want led_hue to be capped at 255, but we do not want to clip it to // that, because that does not result in a nice animation. Instead, when it // is higher than 255, we simply substract 255, and repeat that until we're // within cap. This lays out the rainbow in a kind of wave. - while (key_hue >= 255) { - key_hue -= 255; + while (led_hue >= 255) { + led_hue -= 255; } - cRGB rainbow = hsvToRgb(key_hue, rainbow_saturation, parent_->rainbow_value); - ::LEDControl.setCrgbAt(key_addr.toInt(), rainbow); + cRGB rainbow = hsvToRgb(led_hue, rainbow_saturation, parent_->rainbow_value); + ::LEDControl.setCrgbAt(led_index.offset(), rainbow); } rainbow_hue += rainbow_wave_steps; if (rainbow_hue >= 255) { diff --git a/src/kaleidoscope/plugin/TypingBreaks.cpp b/src/kaleidoscope/plugin/TypingBreaks.cpp index 3aef63a3..f150cea2 100644 --- a/src/kaleidoscope/plugin/TypingBreaks.cpp +++ b/src/kaleidoscope/plugin/TypingBreaks.cpp @@ -110,6 +110,7 @@ EventHandlerResult TypingBreaks::onSetup() { Kaleidoscope.storage().get(settings_base_, idle_time); if (idle_time == 0xffffffff) { Kaleidoscope.storage().put(settings_base_, settings); + Kaleidoscope.storage().commit(); } Kaleidoscope.storage().get(settings_base_, settings); @@ -193,6 +194,7 @@ EventHandlerResult TypingBreaks::onFocusEvent(const char *command) { } Kaleidoscope.storage().put(settings_base_, settings); + Kaleidoscope.storage().commit(); return EventHandlerResult::EVENT_CONSUMED; } diff --git a/src/kaleidoscope_internal/deprecations.h b/src/kaleidoscope_internal/deprecations.h index 835a20f2..d92e9fd5 100644 --- a/src/kaleidoscope_internal/deprecations.h +++ b/src/kaleidoscope_internal/deprecations.h @@ -34,6 +34,9 @@ #define _DEPRECATED_MESSAGE_KEYBOARDHARDWARE \ "`KeyboardHardware` is deprecated, please use\n" __NL__ \ "`Kaleidoscope.device()` instead" +#define _DEPRECATED_MESSAGE_HARDWARE_RESETDEVICE \ + "`KeyboardHardware.resetDevice()` is deprecated, please use " __NL__ \ + "`Kaleidoscope.rebootBootloader()` instead." #define _DEPRECATED_MESSAGE_ROWS \ "The `ROWS` macro is deprecated, please use\n" __NL__ \ @@ -46,3 +49,7 @@ #define _DEPRECATED_MESSAGE_LED_COUNT \ "The `LED_COUNT` macro is deprecated, please use \n" __NL__ \ "`Kaleidoscope.device().led_count` instead." + +#define _DEPRECATED_MESSAGE_HARDWARE_BASE_CLASS \ + "The `Hardware` base class is deprecated. Please use\n" __NL_ \ + "the new APIs based on `kaleidoscope::device::Base`."