While not complete yet, this is a stab at documenting the new device APIs. There are parts still missing, but pointers are there to the headers which show the API. `UPGRADING.md` and `NEWS.md` have also been updated to mention these. Signed-off-by: Gergely Nagy <algernon@keyboard.io>pull/695/head
parent
3601272478
commit
7557c6d395
@ -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.
|
Loading…
Reference in new issue