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 <algernon@keyboard.io>
pull/695/head
Gergely Nagy 5 years ago
parent cfeff8f2cf
commit d39a8dc46c
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

@ -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 <kaleidoscope/driver/bootloader/avr/Caterina.h>
```
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.

@ -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

@ -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;

@ -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;

@ -18,6 +18,4 @@
#pragma once
#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1
#include "kaleidoscope/device/kbdfans/KBD4x.h"

@ -18,5 +18,4 @@
#pragma once
#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1
#include "kaleidoscope/device/olkb/Planck.h"

@ -18,5 +18,4 @@
#pragma once
#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1
#include "kaleidoscope/device/softhruf/Splitography.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)

@ -18,6 +18,4 @@
#pragma once
#define KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD 1
#include "kaleidoscope/device/technomancy/Atreus2.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

@ -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.

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef __AVR__
#include <Arduino.h>
#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<KeyScannerProps> KeyScanner; \
typedef kaleidoscope::driver::bootloader::avr::BOOTLOADER_ BootLoader; \
}; \
class BOARD_: public kaleidoscope::device::ATMega32U4Keyboard<BOARD_##Props> {};
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<StorageProps> Storage;
};
template <typename _DeviceProps>
class ATMega32U4Keyboard : public kaleidoscope::device::Base<_DeviceProps> {
public:
auto serialPort() -> decltype(Serial) & {
return Serial;
}
};
}
}
#endif

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#if KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD
#include <Arduino.h>
#include <KeyboardioHID.h>
#include "Kaleidoscope-HIDAdaptor-KeyboardioHID.h"
#include "kaleidoscope/MatrixAddr.h"
#include "kaleidoscope/macro_helpers.h"
#include "kaleidoscope/device/avr/pins_and_ports.h"
#include <avr/wdt.h>
#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<NUM_ARGS(ROW_PINS_), NUM_ARGS(COL_PINS_)> 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

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @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<typename _DeviceProps>
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_;
};
}
}

@ -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;

@ -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<ErgoDoxProps> {
public:
ErgoDox(void) {}
static constexpr byte matrix_columns = 6;
static constexpr byte matrix_rows = 14;
static constexpr int8_t led_count = 0;
typedef MatrixAddr<matrix_rows, matrix_columns> 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:

@ -18,39 +18,12 @@
#ifdef ARDUINO_AVR_KBD4X
#include <Kaleidoscope.h>
#include <avr/wdt.h>
#include <avr/boot.h>
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
}
}

@ -19,36 +19,35 @@
#ifdef ARDUINO_AVR_KBD4X
#include <Arduino.h>
#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 <Arduino.h>
#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<KeyScannerProps> KeyScanner;
typedef kaleidoscope::driver::bootloader::avr::FLIP Bootloader;
};
class KBD4x: public kaleidoscope::device::ATMega32U4Keyboard<KBD4xProps> {
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, \

@ -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<uint16_t *>(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);
}
}
}
}

@ -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<Model01LEDDriverProps> {
public:
Model01(void);
static constexpr byte matrix_rows = 4;
static constexpr byte matrix_columns = 16;
static constexpr int8_t led_count = 64;
typedef MatrixAddr<matrix_rows, matrix_columns> 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<Model01KeyScannerProps> {
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<Model01Props> {
public:
static void setup();
static void enableHardwareTestMode();
};
}

@ -18,14 +18,12 @@
#ifdef ARDUINO_AVR_PLANCK
#include <Kaleidoscope.h>
#include <avr/wdt.h>
namespace kaleidoscope {
namespace device {
namespace olkb {
ATMEGA_KEYBOARD_DATA(Planck);
constexpr int8_t Planck::led_count;
AVR_KEYSCANNER_BOILERPLATE
}
}

@ -20,31 +20,20 @@
#ifdef ARDUINO_AVR_PLANCK
#include <Arduino.h>
#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, \

@ -25,14 +25,12 @@
#ifdef ARDUINO_AVR_SPLITOGRAPHY
#include <Kaleidoscope.h>
#include <avr/wdt.h>
namespace kaleidoscope {
namespace device {
namespace softhruf {
ATMEGA_KEYBOARD_DATA(Splitography);
constexpr int8_t Splitography::led_count;
AVR_KEYSCANNER_BOILERPLATE
}
}

@ -26,32 +26,33 @@
#ifdef ARDUINO_AVR_SPLITOGRAPHY
#include <Arduino.h>
#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 <Arduino.h>
#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<KeyScannerProps> KeyScanner;
typedef kaleidoscope::driver::bootloader::avr::FLIP BootLoader;
};
static constexpr int8_t numKeys() {
return matrix_columns * matrix_rows;
class Splitography: public kaleidoscope::device::ATMega32U4Keyboard<SplitographyProps> {
public:
Splitography() {
mcu_.disableJTAG();
}
};

@ -27,50 +27,12 @@
#ifdef ARDUINO_AVR_ATREUS
#include <Kaleidoscope.h>
#include <avr/wdt.h>
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
}
}

@ -27,51 +27,30 @@
#ifdef ARDUINO_AVR_ATREUS
#include <Arduino.h>
#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, \

@ -19,14 +19,12 @@
#ifdef ARDUINO_AVR_ATREUS2
#include <Kaleidoscope.h>
#include <avr/wdt.h>
namespace kaleidoscope {
namespace device {
namespace technomancy {
ATMEGA_KEYBOARD_DATA(Atreus2);
constexpr int8_t Atreus2::led_count;
AVR_KEYSCANNER_BOILERPLATE
}
}

@ -21,35 +21,20 @@
#ifdef ARDUINO_AVR_ATREUS2
#include <Arduino.h>
#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, \

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace kaleidoscope {
namespace driver {
namespace bootloader {
class Base {
public:
Base() {}
void setup() {}
static void rebootBootloader() {}
};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <avr/wdt.h>
#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 {};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <avr/wdt.h>
#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<uint16_t *>(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
}
};
}
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <Kaleidoscope.h>
#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

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <avr/wdt.h>
#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();
};
}
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <avr/wdt.h>
#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");
}
};
}
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Arduino.h>
#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 <avr/wdt.h>
#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 <typename _KeyScannerProps>
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;
}
}
}
};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Arduino.h>
#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<matrix_rows, matrix_columns> 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<matrix_rows, matrix_columns> KeyAddr;
};
template <typename _KeyScannerProps>
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;
}
};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Arduino.h>
#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<BaseProps> {};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 <typename _LEDDriverProps>
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_;
};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<BaseProps> {};
}
}
}

@ -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() {}
};
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#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() {}
};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 {};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope/driver/storage/Base.h"
#include <EEPROM.h>
namespace kaleidoscope {
namespace driver {
namespace storage {
struct AVREEPROMProps : kaleidoscope::driver::storage::BaseProps {
static constexpr uint16_t length = 0;
};
template <typename _StorageProps>
class AVREEPROM : public kaleidoscope::driver::storage::Base<_StorageProps> {
public:
template<typename T>
static T& get(uint16_t offset, T& t) {
return EEPROM.get(offset, t);
}
template<typename T>
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);
}
};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace kaleidoscope {
namespace driver {
namespace storage {
struct BaseProps {
static constexpr uint16_t length = 0;
};
template <typename _StorageProps>
class Base {
public:
template<typename T>
static T& get(uint16_t offset, T& t) {
return t;
}
template<typename T>
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() {}
};
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<BaseProps> {};
}
}
}

@ -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<kaleidoscope::Device::Props::KeyScannerProps>::handleKeyswitchEvent(Key mappedKey, kaleidoscope::Device::Props::KeyScannerProps::KeyAddr key_addr, uint8_t keyState) {
::handleKeyswitchEvent(mappedKey, key_addr, keyState);
}
} // namespace keyscanner
} // namespace driver
} // namespace kaleidoscope

@ -233,6 +233,7 @@ EventHandlerResult DynamicMacros::onFocusEvent(const char *command) {
Kaleidoscope.storage().update(storage_base_ + pos++, b);
}
Kaleidoscope.storage().commit();
updateDynamicMacroCache();
}
}

@ -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;
}

@ -157,6 +157,7 @@ EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) {
updateKey(i, k);
i++;
}
Kaleidoscope.storage().commit();
}
return EventHandlerResult::EVENT_CONSUMED;

@ -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;
}

@ -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;
}

@ -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();
}
}

@ -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();

@ -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;
}

@ -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));

@ -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));
}

@ -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_;
};

@ -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) {

@ -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;
}

@ -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`."

Loading…
Cancel
Save