Start documenting the new device APIs

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
Gergely Nagy 5 years ago
parent 3601272478
commit 7557c6d395
No known key found for this signature in database
GPG Key ID: AC1E90BAC433F68F

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

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

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