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