Merge pull request #695 from keyboardio/hardware/new-api

New hardware API
pull/713/head
Jesse Vincent 5 years ago committed by GitHub
commit 5545715ab8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -24,6 +24,15 @@ The API version will remain the same, even if we introduce breaking changes -
until a stable release is made from the v2 branch. From that point onwards, the
API version will change with further breaking changes.
### New device API
A new hardware device API was introduced in November 2019, replacing the old
system. It was designed to be more composable, more future proof than the old
system. All hardware plugins under Keyboardio control have been updated to use
the new APIs.
See [UPGRADING.md](UPGRADING.md#new-device-api) for more information.
### New plugin API
A new plugin API was introduced in May 2018, which replaces the old system. The new system is hopefully easier to use and develop for:

@ -7,6 +7,7 @@ If any of this does not make sense to you, or you have trouble updating your .in
* [Upgrade notes](#upgrade-notes)
+ [New features](#new-features)
- [New device API](#new-device-api)
- [New plugin API](#new-plugin-api)
- [Bidirectional communication for plugins](#bidirectional-communication-for-plugins)
- [Consistent timing](#consistent-timing)
@ -28,6 +29,18 @@ If any of this does not make sense to you, or you have trouble updating your .in
## New features
### New device API
We are introducing - or rather, replacing - the older hardware plugins, with a system that's much more composable, more extensible, and will allow us to better support new devices, different MCUs, and so on.
### For end-users
For end users, this doesn't come with any breaking changes. A few things have been deprecated (`ROWS`, `COLS`, `LED_COUNT`, `KeyboardHardware`), but they still function for the time being.
### For developers
For those wishing to port Kaleidoscope to devices it doesn't support yet, the new API should make most things considerably easier. Please see the (work in progress) documentation in [doc/device-apis.md](doc/device-apis.md).
### New plugin API
#### For end-users

@ -0,0 +1,208 @@
# Kaleidoscope Device API internals
This document is aimed at people interested in working on adding new devices -
or improving support for existing ones - to Kaleidoscope. The APIs detailed here
are a little bit more complex than most of the APIs our plugins provide.
Nevertheless, we hope they're still reasonably easy to use, and this document is
an attempt to explain some of the more intricate parts of it.
## Overview
The core idea of the APIs is that to build up a device, we compose various
components together, by describing their _properties_, and using fairly generic,
templated helper classes with the properties as template parameters.
This way, we can assemble together a device with a given _MCU_, which uses a
particular _Bootloader_, some kind of _Storage_, perhaps some _LEDs_, and it
will more than likely have a _key scanner_ component too.
The base and helper classes provide a lot of the functionality themselves, so
for a device built up from components already supported by Kaleidoscope, the
amount of custom code one has to write will be minimal.
## Component details
### Device
A `Device` is the topmost level component, it is the interface the rest of
Kaleidoscope will work with. The [`kaleidoscope::device::Base`][k:d:Base] class
is the ancestor of _all_ devices, everything derives from this. Devices that use
an `ATMega32U4` MCU we also have the
[`kaleidoscope::device::ATMega32U4Keyboard`][k:d:a32u4] class, which sets up
some of the components that is common to all `ATMega32U4`-based devices (such as
the _MCU_ and the _Storage_).
[k:d:Base]:../src/kaleidoscope/device/Base.h
[k:d:a32u4]: ../src/kaleidoscope/device/ATMega32U4.h
As hinted at above, a device - or rather, it's `Props` - describe the components
used for the device, such as the MCU, the Bootloader, the Storage driver, LEDs,
and the key scanner. If any of that is unneeded, there's no need to specify them
in `Props` - the defaults are all no-ops.
All devices must also come with a `Props` struct, deriving from [`kaleidoscope::device::BaseProps`][k:d:BaseProps].
[k:d:BaseProps]: ../src/kaleidoscope/device/Base.h
As an example, the most basic device we can have, that does nothing, would look
like this:
```c++
class ExampleDevice : public kaleidoscope::device::Base<kaleidoscope::device::BaseProps> {};
```
That's not very useful, though. More often than not, we want to override at
least some of the properties. In some cases, even override some of the
pre-defined methods of the device. See the [base class][k:d:Base] for an
up-to-date list of methods and interfaces it provides. The most often changed
methods are likely to be `setup()` and the constructor, and
`enableHardwareTestMode()` if the device implements a hardware test mode. The
rest are wrappers around the various components described by the `Props`.
In other words, the majority of customisation is in the `Props`, and in what
components the device ends up using.
### MCU
The heart of any device will be the main controller unit, or _MCU_ for short.
The [`kaleidoscope::driver::mcu::Base`][k:d:m:Base] class is the ancestor of our
MCU drivers, including [`mcu::ATMega32U4`][k:d:m:a32u4].
[k:d:m:Base]: ../src/kaleidoscope/driver/mcu/Base.h
[k:d:m:a32u4]: ../src/kaleidoscope/driver/mcu/ATMega32U4.h
The core firmware will use the `detachFromHost()` and `attachToHost()` methods
of the MCU driver, along with `setup()`, but the driver - like any other
driver - is free to have other methods, to be used by individual devices.
For example, the [`ATMega32U4`][k:d:m:a32u4] driver implements a `disableJTAG()`
and a `disableClockDivision()` method, which some of our devices use in their
constructors.
Unlike some other components, the `MCU` component has no properties.
### Bootloader
Another important component of a device is a bootloader. The bootloader is the
thing that allows us to re-program the keyboard without additional hardware
(aptly called a programmer). As such, the [`base class`][k:d:b:Base] has a
single method, `rebootBootloader()`, which our bootloader components implement.
[k:d:b:Base]: ../src/kaleidoscope/bootloader/Base.h
Kaleidoscope currently supports [`Catalina`][k:d:b:Catalina],
[`HalfKay`][k:d:b:HalfKay], and [`FLIP`][k:d:b:FLIP] bootloaders. Please consult
them for more information. In many cases, setting up the bootloader in the
device props is all one needs to do.
[k:d:b:Catalina]: ../src/kaleidoscope/driver/bootloader/avr/Catalina.h
[k:d:b:HalfKay]: ../src/kaleidoscope/driver/bootloader/avr/HalfKay.h
[k:d:b:FLIP]: ../src/kaleidoscope/driver/bootloader/avr/FLIP.h
Like the _MCU_ component, the _bootloader_ does not use Props, either.
### Storage
Not nearly as essential for a device is the [`Storage`][k:d:s:Base] component.
Storage is for persistent storage of configuration data, such as key maps,
colormaps, feature toggles, and so on. It's not a required component, but a
recommended one nevertheless. This storage component is what allows apps like
[Chrysalis][chrysalis] to configure some aspects of the keyboard without having
to flash new firmware.
The Storage API resembles the Arduino EEPROM API very closely. In fact, our
[`AVREEPROM`][k:d:s:AVREEPROM] class is but a thin wrapper around that!
[k:d:s:Base]: ../src/kaleidoscope/driver/storage/Base.h
[chrysalis]: https://github.com/keyboardio/Chrysalis
[k:d:s:AVREEPROM]: ../src/kaleidoscope/driver/storage/AVREEPROM.h
The `Storage` component does use Props, one that describes the length - or
size - of it. We provide an [`ATMega32U4EEPROMProps`][k:d:s:a32u4props] helper,
which is preconfigured for the 1k EEPROM size of the ATMega32U4.
[k:d:s:a32u4props]: ../src/kaleidoscope/driver/storage/ATMega32U4EEPROMProps.h
### LEDs
[`kaleidoscope::driver::led::Base`][k:d:l:Base]
[k:d:l:Base]: ../src/kaleidoscope/driver/led/Base.h
### Keyscanner
[`kaleidoscope::driver::keyscanner::Base`][k:d:ks:Base]
[k:d:ks:Base]: ../src/kaleidoscope/driver/keyscanner/Base.h
## Helpers
[`kaleidoscope::device::ATMega32U4Keyboard`][k:d:a32u4k]
[`kaleidoscope::driver::keyscanner::AVR`][k:d:ks:avr]
[k:d:a32u4k]: ../src/kaleidoscope/device/ATMega32U4Keyboard.h
[k:d:ks:avr]: ../src/kaleidoscope/driver/keyscanner/AVR.h
## Putting it all together
To put things into perspective, and show a simple example, we'll build an
imaginary mini keypad: `ATMega32U4` with `Caterina` as bootloader, no LEDs, and
four keys only.
### `ImaginaryKeypad.h`
```c++
#pragma once
#ifdef ARDUINO_AVR_IMAGINARY_KEYPAD
#include <Arduino.h>
#include "kaleidoscope/driver/keyscanner/AVR.h"
#include "kaleidoscope/driver/bootloader/avr/Caterina.h"
#include "kaleidoscope/device/ATMega32U4Keyboard.h"
namespace kaleidoscope {
namespace device {
namespace imaginary {
ATMEGA32U4_KEYBOARD(
Keypad, Caterina,
ROW_PIN_LIST({ PIN_D0, PIN_D1 }),
COL_PIN_LIST({ PIN_C0, PIN_C1 })
);
#define PER_KEY_DATA(dflt, \
R0C0, R0C1, \
R1C0, R1C1 \
) \
R0C0, R0C1, R1C0, R1C1
}
}
typedef kaleidoscope::device::imaginary::Keypad Device;
}
#endif
```
### `ImaginaryKeypad.cpp`
```c++
#ifdef ARDUINO_AVR_IMAGINARY_KEYPAD
#include <Kaleidoscope.h>
namespace kaleidoscope {
namespace device {
namespace imaginary {
AVR_KEYSCANNER_BOILERPLATE
}
}
}
#endif
```
That's it.

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

@ -0,0 +1,2 @@
BOARD="keyboardio_imago"
SKETCH="Imago"

@ -0,0 +1,192 @@
/* -*- mode: c++ -*-
* Imago.ino -- Example sketch for the Keyboardio Imago
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTabILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "Kaleidoscope.h"
#include "Kaleidoscope-Macros.h"
// Support for controlling the keyboard's LEDs
#include "Kaleidoscope-LEDControl.h"
// Support for the "Boot greeting" effect, which pulses the 'LED' button for 10s
// when the keyboard is connected to a computer (or that computer is powered on)
#include "Kaleidoscope-LEDEffect-BootGreeting.h"
// Support for LED modes that set all LEDs to a single color
#include "Kaleidoscope-LEDEffect-SolidColor.h"
// Support for an LED mode that makes all the LEDs 'breathe'
#include "Kaleidoscope-LEDEffect-Breathe.h"
#include "Kaleidoscope-LEDEffect-Chase.h"
// Support for LED modes that pulse the keyboard's LED in a rainbow pattern
#include "Kaleidoscope-LEDEffect-Rainbow.h"
// Support for host power management (suspend & wakeup)
#include "Kaleidoscope-HostPowerManagement.h"
// Support for magic combos (key chords that trigger an action)
#include "Kaleidoscope-MagicCombo.h"
// Support for USB quirks, like changing the key state report protocol
#include "Kaleidoscope-USB-Quirks.h"
enum { _QWERTY,
};
/* *INDENT-OFF* */
KEYMAPS(
[_QWERTY] = KEYMAP(
Key_F1, Key_Escape, Key_Backtick, Key_1, Key_2, Key_3, Key_4, Key_5, Key_6, Key_7, Key_8, Key_9, Key_0, Key_Minus, Key_Equals, Key_Backspace,
Key_F2, Key_Tab, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_LeftBracket, Key_RightBracket, Key_Backslash,
Key_F3, Key_Escape, Key_A, Key_S, Key_D, Key_F, Key_G, Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, Key_Enter,
Key_F4, Key_LeftShift, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_UpArrow, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_RightShift, Key_LEDEffectNext,
Key_F5, Key_LeftControl,Key_LeftAlt, Key_LeftGui, Key_Backspace, Key_LeftArrow, Key_DownArrow, Key_RightArrow, Key_Space, Key_RightAlt, Key_Menu, Key_RightControl, Key_LEDEffectNext
));
/* *INDENT-ON* */
// These 'solid' color effect definitions define a rainbow of
// LED color modes calibrated to draw 500mA or less on the
// Keyboardio Model 01.
static kaleidoscope::plugin::LEDSolidColor solidRed(160, 0, 0);
static kaleidoscope::plugin::LEDSolidColor solidOrange(140, 70, 0);
static kaleidoscope::plugin::LEDSolidColor solidYellow(130, 100, 0);
static kaleidoscope::plugin::LEDSolidColor solidGreen(0, 160, 0);
static kaleidoscope::plugin::LEDSolidColor solidBlue(0, 70, 130);
static kaleidoscope::plugin::LEDSolidColor solidIndigo(0, 0, 170);
static kaleidoscope::plugin::LEDSolidColor solidViolet(130, 0, 120);
/** toggleLedsOnSuspendResume toggles the LEDs off when the host goes to sleep,
* and turns them back on when it wakes up.
*/
void toggleLedsOnSuspendResume(kaleidoscope::plugin::HostPowerManagement::Event event) {
switch (event) {
case kaleidoscope::plugin::HostPowerManagement::Suspend:
LEDControl.set_all_leds_to({0, 0, 0});
LEDControl.syncLeds();
LEDControl.paused = true;
break;
case kaleidoscope::plugin::HostPowerManagement::Resume:
LEDControl.paused = false;
LEDControl.refreshAll();
break;
case kaleidoscope::plugin::HostPowerManagement::Sleep:
break;
}
}
/** hostPowerManagementEventHandler dispatches power management events (suspend,
* resume, and sleep) to other functions that perform action based on these
* events.
*/
void hostPowerManagementEventHandler(kaleidoscope::plugin::HostPowerManagement::Event event) {
toggleLedsOnSuspendResume(event);
}
/** This 'enum' is a list of all the magic combos used by the Model 01's
* firmware The names aren't particularly important. What is important is that
* each is unique.
*
* These are the names of your magic combos. They will be used by the
* `USE_MAGIC_COMBOS` call below.
*/
enum {
// Toggle between Boot (6-key rollover; for BIOSes and early boot) and NKRO
// mode.
COMBO_TOGGLE_NKRO_MODE
};
/** A tiny wrapper, to be used by MagicCombo.
* This simply toggles the keyboard protocol via USBQuirks, and wraps it within
* a function with an unused argument, to match what MagicCombo expects.
*/
static void toggleKeyboardProtocol(uint8_t combo_index) {
USBQuirks.toggleKeyboardProtocol();
}
/** Magic combo list, a list of key combo and action pairs the firmware should
* recognise.
*/
USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
// Left Fn + Esc + Shift
.keys = { R3C6, R2C6, R3C7 }
});
KALEIDOSCOPE_INIT_PLUGINS(Macros,
// LEDControl provides support for other LED modes
LEDControl,
// The rainbow effect changes the color of all of the keyboard's keys at the same time
// running through all the colors of the rainbow.
LEDRainbowEffect,
// The rainbow wave effect lights up your keyboard with all the colors of a rainbow
// and slowly moves the rainbow across your keyboard
LEDRainbowWaveEffect,
// The chase effect follows the adventure of a blue pixel which chases a red pixel across
// your keyboard. Spoiler: the blue pixel never catches the red pixel
LEDChaseEffect,
// These static effects turn your keyboard's LEDs a variety of colors
solidRed, solidOrange, solidYellow, solidGreen, solidBlue, solidIndigo, solidViolet,
// The breathe effect slowly pulses all of the LEDs on your keyboard
LEDBreatheEffect,
// The HostPowerManagement plugin allows us to turn LEDs off when then host
// goes to sleep, and resume them when it wakes up.
HostPowerManagement,
// The MagicCombo plugin lets you use key combinations to trigger custom
// actions - a bit like Macros, but triggered by pressing multiple keys at the
// same time.
MagicCombo,
// The USBQuirks plugin lets you do some things with USB that we aren't
// comfortable - or able - to do automatically, but can be useful
// nevertheless. Such as toggling the key report protocol between Boot (used
// by BIOSes) and Report (NKRO).
USBQuirks
);
void setup() {
Kaleidoscope.setup();
Serial.begin(9600);
}
void loop() {
Kaleidoscope.loop();
}

@ -0,0 +1,55 @@
# This stub makefile for a Kaleidoscope example pulls in all the targets
# required to build the example
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
SKETCHBOOK_DIR ?= $(HOME)/Documents/Arduino
PACKAGE_DIR ?= $(HOME)/Library/Arduino15
else
SKETCHBOOK_DIR ?= $(HOME)/Arduino
PACKAGE_DIR ?= $(HOME)/.arduino15
endif
ARDUINO_INSTALLED_ENV=$(shell ls -dt $(PACKAGE_DIR)/packages/keyboardio/hardware/avr 2>/dev/null |head -n 1)
MANUALLY_INSTALLED_ENV=$(shell ls -dt $(SKETCHBOOK_DIR)/hardware/keyboardio/avr 2>/dev/null |head -n 1)
ifneq ("$(wildcard $(ARDUINO_INSTALLED_ENV)/boards.txt)","")
ifneq ("$(wildcard $(MANUALLY_INSTALLED_ENV)/boards.txt)","")
$(info ***************************************************************************)
$(info It appears that you have installed two copies of Kaleidoscope. One copy was)
$(info installed using Arduino's "Board Manager", while the other was installed by)
$(info hand, probably using "git".)
$(info )
$(info This will likely cause some trouble as you try to build keyboard firmware)
$(info using Kaleidoscope. You may want to remove either: )
$(info )
$(info $(PACKAGE_DIR)/packages/keyboardio/ which was installed using Arduino)
$(info )
$(info or)
$(info )
$(info $(SKETCHBOOK_DIR)/hardware/keyboardio/ which was installed by hand.)
$(info )
$(info ***************************************************************************)
$(info )
endif
BOARD_HARDWARE_PATH = $(ARDUINO_INSTALLED_ENV)
KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR ?= build-tools/makefiles/
KALEIDOSCOPE_BUILDER_DIR ?= $(ARDUINO_INSTALLED_ENV)/libraries/Kaleidoscope/bin/
endif
BOARD_HARDWARE_PATH ?= $(SKETCHBOOK_DIR)/hardware
KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR ?= keyboardio/avr/build-tools/makefiles/
include $(BOARD_HARDWARE_PATH)/$(KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR)/rules.mk

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

@ -0,0 +1,21 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Hardware-Keyboardio-Imago -- Keyboardio Imago hardware support for Kaleidoscope
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope/device/keyboardio/Imago.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, \

@ -0,0 +1,191 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Hardware-Keyboardio-Imago -- Imago hardware support for Kaleidoscope
* Copyright (C) 2018, 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/>.
*/
#ifdef ARDUINO_AVR_KEYBOARDIO_IMAGO
#include <Kaleidoscope.h>
extern "C" {
#include "twi.h"
}
#define ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0]))
#define LED_DRIVER_ADDR 0x30
namespace kaleidoscope {
namespace device {
namespace keyboardio {
static constexpr uint8_t CMD_SET_REGISTER = 0xFD;
static constexpr uint8_t CMD_WRITE_ENABLE = 0xFE;
static constexpr uint8_t WRITE_ENABLE_ONCE = 0b11000101;
static constexpr uint8_t LED_REGISTER_PWM0 = 0x00;
static constexpr uint8_t LED_REGISTER_PWM1 = 0x01;
static constexpr uint8_t LED_REGISTER_DATA0 = 0x02;
static constexpr uint8_t LED_REGISTER_DATA1 = 0x03;
static constexpr uint8_t LED_REGISTER_CONTROL = 0x04;
static constexpr uint8_t LED_REGISTER_PWM0_SIZE = 0xB4;
static constexpr uint8_t LED_REGISTER_PWM1_SIZE = 0xAB;
static constexpr uint8_t LED_REGISTER_DATA0_SIZE = 0xB4;
static constexpr uint8_t LED_REGISTER_DATA1_SIZE = 0xAB;
static constexpr uint8_t LED_REGISTER_DATA_LARGEST = LED_REGISTER_DATA0_SIZE;
AVR_KEYSCANNER_BOILERPLATE
bool ImagoLEDDriver::isLEDChanged = true;
cRGB ImagoLEDDriver::led_data[];
#define NOLED 254
static constexpr uint8_t key_led_map[5][16] PROGMEM = {
{ 104, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 115, 12, 116},
{ 91, 13, NOLED, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 102, 15, 103},
{ 78, 26, 27, 28, 29, 30, 31, NOLED, 33, 34, 35, 36, 37, 89, 38, NOLED},
{ 65, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, NOLED, 90},
{ 52, 66, 53, 54, NOLED, 56, 57, 71, 59, NOLED, 61, 62, 63, 64, NOLED, 77}
};
void ImagoLEDDriver::setup() {
setAllPwmTo(0xFF);
selectRegister(LED_REGISTER_CONTROL);
twiSend(LED_DRIVER_ADDR, 0x01, 0xFF); //global current
twiSend(LED_DRIVER_ADDR, 0x00, 0x01); //normal operation
}
void ImagoLEDDriver::twiSend(uint8_t addr, uint8_t Reg_Add, uint8_t Reg_Dat) {
uint8_t data[] = {Reg_Add, Reg_Dat };
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), 1, 0);
}
void ImagoLEDDriver::unlockRegister(void) {
twiSend(LED_DRIVER_ADDR, CMD_WRITE_ENABLE, WRITE_ENABLE_ONCE); //unlock
}
void ImagoLEDDriver::selectRegister(uint8_t page) {
// Registers automatically get locked at startup and after a given write
// It'd be nice to disable that.
unlockRegister();
twiSend(LED_DRIVER_ADDR, CMD_SET_REGISTER, page);
}
void ImagoLEDDriver::setCrgbAt(uint8_t i, cRGB crgb) {
if (!Kaleidoscope.device().LEDs().isValid(i))
return;
cRGB oldColor = getCrgbAt(i);
isLEDChanged |= !(oldColor.r == crgb.r && oldColor.g == crgb.g && oldColor.b == crgb.b);
led_data[i] = crgb;
}
uint8_t ImagoLEDDriver::getLedIndex(uint8_t key_offset) {
return pgm_read_byte(key_led_map + key_offset);
}
cRGB ImagoLEDDriver::getCrgbAt(uint8_t i) {
if (!Kaleidoscope.device().LEDs().isValid(i))
return {0, 0, 0};
return led_data[i];
}
void ImagoLEDDriver::syncLeds() {
// if (!isLEDChanged)
// return;
uint8_t data[LED_REGISTER_DATA_LARGEST + 1];
data[0] = 0;// the address of the first byte to copy in
uint8_t last_led = 0;
// Write the first LED bank
selectRegister(LED_REGISTER_DATA0);
for (auto i = 1; i < LED_REGISTER_DATA0_SIZE; i += 3) {
data[i] = led_data[last_led].b;
data[i + 1] = led_data[last_led].g;
data[i + 2] = led_data[last_led].r;
last_led++;
}
twi_writeTo(LED_DRIVER_ADDR, data, LED_REGISTER_DATA0_SIZE + 1, 1, 0);
// Don't reset "Last LED", because this is just us picking up from the last bank
// TODO - we don't use all 117 LEDs on the Imago, so we can probably stop writing earlier
// Write the second LED bank
// For space efficiency, we reuse the LED sending buffer
// The twi library should never send more than the number of elements
// we say to send it.
// The page 2 version has 180 elements. The page 3 version has only 171.
selectRegister(LED_REGISTER_DATA1);
for (auto i = 1; i < LED_REGISTER_DATA1_SIZE; i += 3) {
data[i] = led_data[last_led].b;
data[i + 1] = led_data[last_led].g;
data[i + 2] = led_data[last_led].r;
last_led++;
}
twi_writeTo(LED_DRIVER_ADDR, data, LED_REGISTER_DATA1_SIZE + 1, 1, 0);
isLEDChanged = false;
}
void ImagoLEDDriver::setAllPwmTo(uint8_t step) {
selectRegister(LED_REGISTER_PWM0);
uint8_t data[0xB5] = {};
data[0] = 0;
// PWM Register 0 is 0x00 to 0xB3
for (auto i = 1; i <= 0xB4; i++) {
data[i] = step;
}
twi_writeTo(LED_DRIVER_ADDR, data, 0xB5, 1, 0);
selectRegister(LED_REGISTER_PWM1);
// PWM Register 1 is 0x00 to 0xAA
for (auto i = 1; i <= LED_REGISTER_PWM1_SIZE; i++) {
data[i] = step;
}
twi_writeTo(LED_DRIVER_ADDR, data, 0xAC, 1, 0);
}
void Imago::setup() {
digitalWrite(MOSI, HIGH);
digitalWrite(SS, HIGH);
uint8_t twi_uninitialized = 1;
if (twi_uninitialized--) {
twi_init();
}
TWBR = 10;
kaleidoscope::device::ATMega32U4Keyboard<ImagoProps>::setup();
}
}
}
}
#endif

@ -0,0 +1,102 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Hardware-Keyboardio-Imago -- Imago hardware support for Kaleidoscope
* Copyright (C) 2018, 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 ARDUINO_AVR_KEYBOARDIO_IMAGO
#include <Arduino.h>
struct cRGB {
uint8_t b;
uint8_t g;
uint8_t r;
};
#define CRGB(r,g,b) (cRGB){b, g, r}
#include "kaleidoscope/driver/keyscanner/AVR.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 {
struct ImagoLEDDriverProps: public kaleidoscope::driver::led::BaseProps {
static constexpr uint8_t led_count = 78;
};
class ImagoLEDDriver : public kaleidoscope::driver::led::Base<ImagoLEDDriverProps> {
public:
static void setup();
static void syncLeds();
static void setCrgbAt(uint8_t i, cRGB crgb);
static cRGB getCrgbAt(uint8_t i);
static uint8_t getLedIndex(uint8_t key_offset);
static cRGB led_data[117]; // 117 is the number of LEDs the chip drives
// until we clean stuff up a bit, it's easiest to just have the whole struct around
private:
static bool isLEDChanged;
static void selectRegister(uint8_t);
static void unlockRegister();
static void setAllPwmTo(uint8_t);
static void twiSend(uint8_t addr, uint8_t Reg_Add, uint8_t Reg_Dat);
};
struct ImagoProps : kaleidoscope::device::ATMega32U4KeyboardProps {
struct KeyScannerProps : public kaleidoscope::driver::keyscanner::AVRProps {
AVR_KEYSCANNER_PROPS(
ROW_PIN_LIST({ PIN_F6, PIN_F5, PIN_F4, PIN_F1, PIN_F0}),
COL_PIN_LIST({ PIN_B2, PIN_B7, PIN_E2, PIN_C7, PIN_C6, PIN_B6, PIN_B5, PIN_B4, PIN_D7, PIN_D6, PIN_D4, PIN_D5, PIN_D3, PIN_D2, PIN_E6, PIN_F7})
);
};
typedef kaleidoscope::driver::keyscanner::AVR<KeyScannerProps> KeyScanner;
typedef ImagoLEDDriverProps LEDDriverProps;
typedef ImagoLEDDriver LEDDriver;
typedef kaleidoscope::driver::bootloader::avr::Caterina BootLoader;
};
class Imago: public kaleidoscope::device::ATMega32U4Keyboard<ImagoProps> {
public:
void setup();
};
#define PER_KEY_DATA(dflt, \
R0C0, R0C1, R0C2, R0C3, R0C4, R0C5, R0C6, R0C7, R0C8, R0C9, R0C10, R0C11, R0C12, R0C13, R0C14, R0C15, \
R1C0, R1C1, R1C3, R1C4, R1C5, R1C6, R1C7, R1C8, R1C9, R1C10, R1C11, R1C12, R1C13, R1C14, R1C15, \
R2C0, R2C1, R2C2, R2C3, R2C4, R2C5, R2C6, R2C8, R2C9, R2C10, R2C11, R2C12, R2C13, R2C14, \
R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, R3C6, R3C7, R3C8, R3C9, R3C10, R3C11, R3C12, R3C13, R3C15, \
R4C0, R4C1, R4C2, R4C3, R4C5, R4C6, R4C7, R4C8, R4C10, R4C11, R4C12, R4C13, R4C15 \
,... ) \
R0C0, R0C1, R0C2, R0C3, R0C4, R0C5, R0C6, R0C7, R0C8, R0C9, R0C10, R0C11, R0C12, R0C13, R0C14, R0C15, \
R1C0, R1C1, XXX, R1C3, R1C4, R1C5, R1C6, R1C7, R1C8, R1C9, R1C10, R1C11, R1C12, R1C13, R1C14, R1C15, \
R2C0, R2C1, R2C2, R2C3, R2C4, R2C5, R2C6, XXX, R2C8, R2C9, R2C10, R2C11, R2C12, R2C13, R2C14, XXX , \
R3C0, R3C1, R3C2, R3C3, R3C4, R3C5, R3C6, R3C7, R3C8, R3C9, R3C10, R3C11, R3C12, R3C13, XXX, R3C15, \
R4C0, R4C1, R4C2, R4C3, XXX, R4C5, R4C6, R4C7, R4C8, XXX, R4C10, R4C11, R4C12, R4C13, XXX, R4C15
}
}
typedef kaleidoscope::device::keyboardio::Imago Device;
}
#endif

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

@ -0,0 +1,561 @@
/*
twi.c - TWI/I2C library for Wiring & Arduino
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
This library 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 Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
*/
#define ENABLE_TWI_SLAVE_MODE 0
#include <math.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <compat/twi.h>
// #include "Arduino.h" // for digitalWrite
#define true 1
#define false 0
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#include "pins_arduino.h"
#include "twi.h"
static volatile uint8_t twi_state;
static volatile uint8_t twi_slarw;
static volatile uint8_t twi_sendStop; // should the transaction end with a stop
static volatile uint8_t twi_inRepStart; // in the middle of a repeated start
static void (*twi_onSlaveTransmit)(void);
static void (*twi_onSlaveReceive)(uint8_t*, int);
static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_masterBufferIndex;
static volatile uint8_t twi_masterBufferLength;
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_txBufferIndex;
static volatile uint8_t twi_txBufferLength;
#if ENABLE_TWI_SLAVE_MODE
static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_rxBufferIndex;
#endif
static volatile uint8_t twi_error;
/*
* Function twi_init
* Desc readys twi pins and sets twi bitrate
* Input none
* Output none
*/
void twi_init(void) {
// initialize state
twi_state = TWI_READY;
twi_sendStop = true; // default value
twi_inRepStart = false;
// activate internal pullups for twi.
// digitalWrite(SDA, 1);
PORTD |= _BV(0);
// digitalWrite(SCL, 1);
PORTD |= _BV(1);
// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
note: TWBR should be 10 or higher for master mode
It is 72 for a 16mhz Wiring board with 100kHz TWI */
// enable twi module, acks, and twi interrupt
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}
/*
* Function twi_disable
* Desc disables twi pins
* Input none
* Output none
*/
void twi_disable(void) {
// disable twi module, acks, and twi interrupt
TWCR &= ~(_BV(TWEN) | _BV(TWIE) | _BV(TWEA));
// deactivate internal pullups for twi.
// digitalWrite(SDA, 0);
PORTD &= ~_BV(0);
// digitalWrite(SCL, 0);
PORTD &= ~_BV(1);
}
/*
* Function twi_slaveInit
* Desc sets slave address and enables interrupt
* Input none
* Output none
*/
void twi_setAddress(uint8_t address) {
// set twi slave address (skip over TWGCE bit)
TWAR = address << 1;
}
/*
* Function twi_setClock
* Desc sets twi bit rate
* Input Clock Frequency
* Output none
*/
void twi_setFrequency(uint32_t frequency) {
TWBR = ((F_CPU / frequency) - 16) / 2;
/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
note: TWBR should be 10 or higher for master mode
It is 72 for a 16mhz Wiring board with 100kHz TWI */
}
/*
* Function twi_readFrom
* Desc attempts to become twi bus master and read a
* series of bytes from a device on the bus
* Input address: 7bit i2c device address
* data: pointer to byte array
* length: number of bytes to read into array
* sendStop: Boolean indicating whether to send a stop at the end
* Output number of bytes read
*/
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop) {
uint8_t i;
// ensure data will fit into buffer
if (TWI_BUFFER_LENGTH < length) {
return 0;
}
// wait until twi is ready, become master receiver
while (TWI_READY != twi_state) {
continue;
}
twi_state = TWI_MRX;
twi_sendStop = sendStop;
// reset error state (0xFF.. no error occured)
twi_error = 0xFF;
// initialize buffer iteration vars
twi_masterBufferIndex = 0;
twi_masterBufferLength = length - 1; // This is not intuitive, read on...
// On receive, the previously configured ACK/NACK setting is transmitted in
// response to the received byte before the interrupt is signalled.
// Therefor we must actually set NACK when the _next_ to last byte is
// received, causing that NACK to be sent in response to receiving the last
// expected byte of data.
// build sla+w, slave device address + w bit
twi_slarw = TW_READ;
twi_slarw |= address << 1;
if (true == twi_inRepStart) {
// if we're in the repeated start state, then we've already sent the start,
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent ourselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
do {
TWDR = twi_slarw;
} while (TWCR & _BV(TWWC));
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
} else
// send start condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
// wait for read operation to complete
while (TWI_MRX == twi_state) {
continue;
}
if (twi_masterBufferIndex < length)
length = twi_masterBufferIndex;
// copy twi buffer to data
for (i = 0; i < length; ++i) {
data[i] = twi_masterBuffer[i];
}
return length;
}
/*
* Function twi_writeTo
* Desc attempts to become twi bus master and write a
* series of bytes to a device on the bus
* Input address: 7bit i2c device address
* data: pointer to byte array
* length: number of bytes in array
* wait: boolean indicating to wait for write or not
* sendStop: boolean indicating whether or not to send a stop at the end
* Output 0 .. success
* 1 .. length to long for buffer
* 2 .. address send, NACK received
* 3 .. data send, NACK received
* 4 .. other twi error (lost bus arbitration, bus error, ..)
*/
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop) {
uint8_t i;
// ensure data will fit into buffer
if (TWI_BUFFER_LENGTH < length) {
return 1;
}
// wait until twi is ready, become master transmitter
while (TWI_READY != twi_state) {
continue;
}
twi_state = TWI_MTX;
twi_sendStop = sendStop;
// reset error state (0xFF.. no error occured)
twi_error = 0xFF;
// initialize buffer iteration vars
twi_masterBufferIndex = 0;
twi_masterBufferLength = length;
// copy data to twi buffer
for (i = 0; i < length; ++i) {
twi_masterBuffer[i] = data[i];
}
// build sla+w, slave device address + w bit
twi_slarw = TW_WRITE;
twi_slarw |= address << 1;
// if we're in a repeated start, then we've already sent the START
// in the ISR. Don't do it again.
//
if (true == twi_inRepStart) {
// if we're in the repeated start state, then we've already sent the start,
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent outselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
do {
TWDR = twi_slarw;
} while (TWCR & _BV(TWWC));
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
} else
// send start condition
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs
// wait for write operation to complete
while (wait && (TWI_MTX == twi_state)) {
continue;
}
switch (twi_error) {
case 0xFF:
return 0; // success
case TW_MT_SLA_NACK:
return 2; // error: address send, nack received
case TW_MT_DATA_NACK:
return 3; // error: data send, nack received
default:
return 4; // other twi error
}
}
/*
* Function twi_transmit
* Desc fills slave tx buffer with data
* must be called in slave tx event callback
* Input data: pointer to byte array
* length: number of bytes in array
* Output 1 length too long for buffer
* 2 not slave transmitter
* 0 ok
*/
uint8_t twi_transmit(const uint8_t* data, uint8_t length) {
uint8_t i;
// ensure data will fit into buffer
if (TWI_BUFFER_LENGTH < (twi_txBufferLength + length)) {
return 1;
}
// ensure we are currently a slave transmitter
if (TWI_STX != twi_state) {
return 2;
}
// set length and copy data into tx buffer
for (i = 0; i < length; ++i) {
twi_txBuffer[twi_txBufferLength + i] = data[i];
}
twi_txBufferLength += length;
return 0;
}
/*
* Function twi_attachSlaveRxEvent
* Desc sets function called before a slave read operation
* Input function: callback function to use
* Output none
*/
void twi_attachSlaveRxEvent(void (*function)(uint8_t*, int)) {
twi_onSlaveReceive = function;
}
/*
* Function twi_attachSlaveTxEvent
* Desc sets function called before a slave write operation
* Input function: callback function to use
* Output none
*/
void twi_attachSlaveTxEvent(void (*function)(void)) {
twi_onSlaveTransmit = function;
}
/*
* Function twi_reply
* Desc sends byte or readys receive line
* Input ack: byte indicating to ack or to nack
* Output none
*/
void twi_reply(uint8_t ack) {
// transmit master read ready signal, with or without ack
if (ack) {
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
} else {
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
}
}
/*
* Function twi_stop
* Desc relinquishes bus master status
* Input none
* Output none
*/
void twi_stop(void) {
// send stop condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);
// wait for stop condition to be exectued on bus
// TWINT is not set after a stop condition!
while (TWCR & _BV(TWSTO)) {
continue;
}
// update twi state
twi_state = TWI_READY;
}
/*
* Function twi_releaseBus
* Desc releases bus control
* Input none
* Output none
*/
void twi_releaseBus(void) {
// release bus
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
// update twi state
twi_state = TWI_READY;
}
ISR(TWI_vect) {
switch (TW_STATUS) {
// All Master
case TW_START: // sent start condition
case TW_REP_START: // sent repeated start condition
// copy device address and r/w bit to output register and ack
TWDR = twi_slarw;
twi_reply(1);
break;
// Master Transmitter
case TW_MT_SLA_ACK: // slave receiver acked address
case TW_MT_DATA_ACK: // slave receiver acked data
// if there is data to send, send it, otherwise stop
if (twi_masterBufferIndex < twi_masterBufferLength) {
// copy data to output register and ack
TWDR = twi_masterBuffer[twi_masterBufferIndex++];
twi_reply(1);
} else {
if (twi_sendStop)
twi_stop();
else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN) ;
twi_state = TWI_READY;
}
}
break;
case TW_MT_SLA_NACK: // address sent, nack received
twi_error = TW_MT_SLA_NACK;
twi_stop();
break;
case TW_MT_DATA_NACK: // data sent, nack received
twi_error = TW_MT_DATA_NACK;
twi_stop();
break;
case TW_MT_ARB_LOST: // lost bus arbitration
twi_error = TW_MT_ARB_LOST;
twi_releaseBus();
break;
// Master Receiver
case TW_MR_DATA_ACK: // data received, ack sent
// put byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
/* intentionally fall through */
case TW_MR_SLA_ACK: // address sent, ack received
// ack if more bytes are expected, otherwise nack
if (twi_masterBufferIndex < twi_masterBufferLength) {
twi_reply(1);
} else {
twi_reply(0);
}
break;
case TW_MR_DATA_NACK: // data received, nack sent
// put final byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
if (twi_sendStop)
twi_stop();
else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN) ;
twi_state = TWI_READY;
}
break;
case TW_MR_SLA_NACK: // address sent, nack received
twi_stop();
break;
// TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case
#if ENABLE_TWI_SLAVE_MODE
// Slave Receiver
case TW_SR_SLA_ACK: // addressed, returned ack
case TW_SR_GCALL_ACK: // addressed generally, returned ack
case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack
case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack
// enter slave receiver mode
twi_state = TWI_SRX;
// indicate that rx buffer can be overwritten and ack
twi_rxBufferIndex = 0;
twi_reply(1);
break;
case TW_SR_DATA_ACK: // data received, returned ack
case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack
// if there is still room in the rx buffer
if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) {
// put byte in buffer and ack
twi_rxBuffer[twi_rxBufferIndex++] = TWDR;
twi_reply(1);
} else {
// otherwise nack
twi_reply(0);
}
break;
case TW_SR_STOP: // stop or repeated start condition received
// ack future responses and leave slave receiver state
twi_releaseBus();
// put a null char after data if there's room
if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) {
twi_rxBuffer[twi_rxBufferIndex] = '\0';
}
// callback to user defined callback
twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
// since we submit rx buffer to "wire" library, we can reset it
twi_rxBufferIndex = 0;
break;
case TW_SR_DATA_NACK: // data received, returned nack
case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack
// nack back at master
twi_reply(0);
break;
// Slave Transmitter
case TW_ST_SLA_ACK: // addressed, returned ack
case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
// enter slave transmitter mode
twi_state = TWI_STX;
// ready the tx buffer index for iteration
twi_txBufferIndex = 0;
// set tx buffer length to be zero, to verify if user changes it
twi_txBufferLength = 0;
// request for txBuffer to be filled and length to be set
// note: user must call twi_transmit(bytes, length) to do this
twi_onSlaveTransmit();
// if they didn't change buffer & length, initialize it
if (0 == twi_txBufferLength) {
twi_txBufferLength = 1;
twi_txBuffer[0] = 0x00;
}
// transmit first byte from buffer, fall
case TW_ST_DATA_ACK: // byte sent, ack returned
// copy data to output register
TWDR = twi_txBuffer[twi_txBufferIndex++];
// if there is more to send, ack, otherwise nack
if (twi_txBufferIndex < twi_txBufferLength) {
twi_reply(1);
} else {
twi_reply(0);
}
break;
case TW_ST_DATA_NACK: // received nack, we are done
case TW_ST_LAST_DATA: // received ack, but we are done already!
// ack future responses
twi_reply(1);
// leave slave receiver state
twi_state = TWI_READY;
break;
#endif
// All
case TW_NO_INFO: // no state information
break;
case TW_BUS_ERROR: // bus error, illegal stop/start
twi_error = TW_BUS_ERROR;
twi_stop();
break;
}
}

@ -0,0 +1,53 @@
/*
twi.h - TWI/I2C library for Wiring & Arduino
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
This library 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 Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef twi_h
#define twi_h
#include <inttypes.h>
//#define ATMEGA8
#ifndef TWI_FREQ
#define TWI_FREQ 100000L
#endif
#ifndef TWI_BUFFER_LENGTH
#define TWI_BUFFER_LENGTH 192
#endif
#define TWI_READY 0
#define TWI_MRX 1
#define TWI_MTX 2
#define TWI_SRX 3
#define TWI_STX 4
void twi_init(void);
void twi_disable(void);
void twi_setAddress(uint8_t);
void twi_setFrequency(uint32_t);
uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t);
uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t);
uint8_t twi_transmit(const uint8_t*, uint8_t);
void twi_attachSlaveRxEvent(void (*)(uint8_t*, int));
void twi_attachSlaveTxEvent(void (*)(void));
void twi_reply(uint8_t);
void twi_stop(void);
void twi_releaseBus(void);
#endif

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