12 KiB
keybrd Library Developer's Guide
This guide if for maintaining and writing new classes for the keybrd library and its extension libraries. The most common reason for new classes are:
- Port classes for micro controller or I/O expanders
- custom layer schemes for multi-layer keyboards
- experimental features
Who this guide is for
This guide is for the maintainers and developers of the keybrd library and it's extensions. It is assumed the reader is familiar with C++ language including pointers, objects, classes, static class variables, composition, aggregation, inheritance, polymorphism, and enum. Debouncer and I/O expander use bit manipulation.
Custom Row classes
The keybrd library is flexible for designing custom Rows
- RowBase functions can be overridden in a derived class
- choice of Debouncers
- choice of Scanners
this example illustrates the custom Row classes from a fictional keybrd_Ext library the keybrd_Ext library library is for a split keyboard with a matrix on each hand the diagrams show the design decisions made by the developer
Row_Ext overrides RowBase::keyWasPressed() Row_Ext::keyWasPressed() is used to unstick sticky keys
Row_Ext_uC scans the primary matrix Row_Ext_uC is a custom class composed of stock keybrd library classes
Row_Ext_ShiftRegisters scans the secondary matrix Row_Ext_ShiftRegisters is a custom class composed of stock keybrd library classes
Class inheritance diagram
RowBase
|
Row_Ext override RowBase::keyWasPressed()
/ \
Row_Ext_uC Row_Ext_ShiftRegisters inherit Row_Ext::keyWasPressed()
/home/wolfv/Documents/Arduino/keybrd_proj/keybrd/doc/keybrd_library_developer_guide.md
RowScannerInterface
/ \
RowScanner_PinsArray RowScanner_SPIShiftRegisters
Dependency diagram
________ Row_Ext_uC[1] _______________
/ \ \
RowScanner_PinsArray[1] Debouncer_Samples[1] Key[1..*]
/ \ |
strobePin[1] readPins[1..*] Code[1..*]
_____ Row_Ext_ShiftRegisters[1] _____________
/ \ \
RowScanner_SPIShiftRegisters[1] Debouncer_Samples[1] Key[1..*]
/ \ |
strobePin[1] ROW_END[1] Code[1..*]
Class inheritance diagrams
Keybrd library class inheritance diagram
RowBase
/ | \
Row_uC Row_ShiftRegisters Row_IOE
RowScannerInterface
/ \ \
RowScanner_PinsArray RowScanner_PinsBitwise RowScanner_SPIShiftRegisters
IOExpanderPort
_______ RowPort _______
/ | \
RowPort_AVR RowPort_MCP23018 RowPort_PCA9655E (one RowPort class for each type of IC)
_______ ColPort _______
/ | \
ColPort_AVR ColPort_MCP23018 ColPort_PCA9655E (one ColPort class for each type of IC)
_____ LED ______
/ | \
LED_AVR LED_MCP23018 LED_PCA9655E (one LED class for each type of IC)
DebouncerInterface
|
Debouncer_4Samples
LayerStateInterface
|
LayerState
Key __
| \
| Key_LayeredKeysArray
|
Code
|_____________________
| \ \
| Code_LayerLock Code_LayerHold
|
|___________________________
| \ \
| Code_LayeredScScBase Code_LayeredCodeScBase
| | |
| Code_LayeredScSc Code_LayeredCodeSc
|
|__________________________________________
\ \ \ \
Code_Sc Code_Shift Code_AutoShift Code_LockLED
/ | \
Code_ScS Code_ScNS Code_ScNS_00
Dependency diagrams
Example single-layer dependency diagram with LEDs
matrix[1..*]
|
___ row_uC[1..*] ________
/ \ \
RowScanner_PinsArray debouncer keys[1..*]
/ \ |
strobePin[1] readPins[1..*] code[1..*]
|
LED[1]
Example single-layer dependency diagram I/O Expander
________ row_uC[1..*] _________
/ \ \
RowScanner_PinsArray[1] debouncer[1] keys[1..*]
/ \ |
strobePin[1] readPins[1..*] code[1..*]
___ row_IOE[1..*] _________
/ \ \
RowScanner_PinsBitwise[1] debouncer[1] keys[1..*]
/ | \ |
rowPort[1] rowPin[1] colPort[1] code[1..*]
\ / \
\ / colPins[1..*]
\ /
IOExpanderPort[0..*]
Example multi-layer dependency diagram with layer LEDs
layerStates[1..*]
________ row_uC[1..*] _____________________/__ | \
/ \ \ / \ | \
RowScanner_PinsArray[1] debouncer[1] keys[1..*] / code_layer[1..*] LED[0..*]
/ \ | /
strobePin[1] readPins[1..*] code[1..*]
Example multi-layer dependency diagram with shift registers
Row_ShiftRegisters
Example multi-layer dependency diagram with I/O Expander
________ row_uC[1..*] ________________________ _______layerStates[1..*]
/ \ \ \ / |
RowScanner_PinsArray[1] debouncer[1] keys[1..*] code_layer[1..*] |
/ \ | ________________________|
strobePin[1] readPins[1..*] code[1..*] |
|
|
___ row_IOE[1..*] _________ __________|
/ \ \ / |
RowScanner_PinsBitwise[1] debouncer[1] keys[1..*] code_layer[1..*] |
/ | \ | _____________________|
rowPort[1] rowPin[1] colPort[1] code[1..*]
\ / \
\ / colPins[1..*]
\ /
IOExpanderPort[0..*]
Class naming conventions
Class names start with upper case letter. Most derived-class names start with the base class name followed by "_" and a name e.g.
Code
|
Code_LayerLock
This convention leads to class names that convey information about the classes inheritance. Underscore delineates base class name and sub-class name. Capital letters delineate words.
Layer-class naming conventions
Code_Layer class names are concatenations of "Code_", "Layer" or layer name, and persistence. Example persistences are:
- "Lock" - layer remains active after the layer key is released
- "Hold" - layer is active for as long as layer key is held down
Example Code_Layer class names:
- Code_LayerHold
- Code_LayerLock
LayerState class names start with "LayerState" and end with a descriptive name. Example LayerState class names:
- LayerState - basic LayerState class in keybrd library
- LayerState_DH - main LayerState for the keybrd_DH library
- LayerState_MF - LayerState for Mouse Function sub-layers in the keybrd_DH library
Code_Layered class names start with "Code_Layered" and end with a descriptive name. Example Code_Layered class names:
- Code_LayeredScSc
- Key_LayeredKeysArray
Style guide
Following the style guide makes it easier for the next programmer to understand your code.
- For class names, see above section "Class naming conventions"
- For member names, use camelCase starting with lowercase letter.
- Use constants rather than macros, except for header guards.
- For constant names that could be macros, use ALL_CAPS_AND_UNDERSCORE.
- ITEM_COUNT is a constant number of items.
- itemCount is a variable number of items.
- Use header guards CLASS_NAME_H.
- Prefix pointer name with "ptr" e.g. ptrRow = &row;
- Name arrays using the plural of element name e.g. Row* const = ptrsRows { &row0, &row1 };
- Pass arrays using array notation rather than pointer notation. Use
void printArray(char[] array);
not
void printArray( char* array);
-
In constructor's initialization list, use same names for fields and constructor parameters.
-
Do not use new or malloc (making memory leaks impossible).
-
Document class interface in .h file, above the class declaration.
-
Code should be self-documenting. The only comments should be things that may need clarification. A simple function with a good name needs no comment.
-
Code is automatically formated before being pushed to the keybrd repository. The astyle_cpp file specifies the format:
- Allman style indentation
- indent 4 spaces
- replace tabs with spaces
- maximum code width of 100 columns
Trace of keybrd scan
Arduino does not have a debugger. So here is the next best thing; a list of functions in the order that they are called. The trace is of a single-layer keybrd scan (no LEDs and no I/O expander). Refer to it like a table of contents while reading the keybrd library.
Matrix::scan() for each row
RowBase::process()
RowBase::wait() delay time for debounce
RowScanner_PinsArray::scan() strobe row on
for each readPin
set rowState bit
strobe row off
Debouncer_4Samples::debounce() debounce
RowBase::pressRelease() for each key in row
if falling edge
Key_*::release() scanCode->release()
Code_*::release() Keyboard.release(scancode)
if rising edge
Key_*::press() scanCode->press()
Code_*::press() Keyboard.press(scancode)
The Arduino libraries
The keybrd libraries compile on the Arduino IDE and make extensive use of the following Arduino libraries:
#include <Arduino.h>
#include <Wire.h>
#include <Keyboard.h>
#include <Mouse.h>
keybrd guide by Wolfram Volpi is licensed under a Creative Commons Attribution 4.0 International License.
Permissions beyond the scope of this license may be available at https://github.com/wolfv6/keybrd/issues/new.