From 7557c6d395f4bb26c2a844749f162eb032e45d37 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Thu, 7 Nov 2019 16:41:01 +0100 Subject: [PATCH] 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 --- NEWS.md | 9 ++ UPGRADING.md | 13 +++ doc/device-apis.md | 208 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 doc/device-apis.md diff --git a/NEWS.md b/NEWS.md index 500ccd15..74c6aafb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -24,6 +24,15 @@ The API version will remain the same, even if we introduce breaking changes - until a stable release is made from the v2 branch. From that point onwards, the API version will change with further breaking changes. +### New device API + +A new hardware device API was introduced in November 2019, replacing the old +system. It was designed to be more composable, more future proof than the old +system. All hardware plugins under Keyboardio control have been updated to use +the new APIs. + +See [UPGRADING.md](UPGRADING.md#new-device-api) for more information. + ### New plugin API A new plugin API was introduced in May 2018, which replaces the old system. The new system is hopefully easier to use and develop for: diff --git a/UPGRADING.md b/UPGRADING.md index 984afbab..7acfda77 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -7,6 +7,7 @@ If any of this does not make sense to you, or you have trouble updating your .in * [Upgrade notes](#upgrade-notes) + [New features](#new-features) + - [New device API](#new-device-api) - [New plugin API](#new-plugin-api) - [Bidirectional communication for plugins](#bidirectional-communication-for-plugins) - [Consistent timing](#consistent-timing) @@ -28,6 +29,18 @@ If any of this does not make sense to you, or you have trouble updating your .in ## New features +### New device API + +We are introducing - or rather, replacing - the older hardware plugins, with a system that's much more composable, more extensible, and will allow us to better support new devices, different MCUs, and so on. + +### For end-users + +For end users, this doesn't come with any breaking changes. A few things have been deprecated (`ROWS`, `COLS`, `LED_COUNT`, `KeyboardHardware`), but they still function for the time being. + +### For developers + +For those wishing to port Kaleidoscope to devices it doesn't support yet, the new API should make most things considerably easier. Please see the (work in progress) documentation in [doc/device-apis.md](doc/device-apis.md). + ### New plugin API #### For end-users diff --git a/doc/device-apis.md b/doc/device-apis.md new file mode 100644 index 00000000..1ed9c076 --- /dev/null +++ b/doc/device-apis.md @@ -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 {}; +``` + +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 +#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 + +namespace kaleidoscope { +namespace device { +namespace imaginary { + +AVR_KEYSCANNER_BOILERPLATE + +} +} +} +#endif +``` + +That's it.