commit
5545715ab8
@ -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
|
@ -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
|
@ -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"
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
@ -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> {};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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> {};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue