keybrd library is an open source library for creating custom-keyboard firmware.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

keybrd_library_developer_guide.md 11KB

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 adding new classes are:

  • I/O expander classes
  • 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 the C++ language including pointers, objects, classes, static class variables, composition, aggregation, inheritance, polymorphism, and enum. Row, Scanner, and Debouncer classes use bit manipulation.

Custom Row classes

Row classes are central to the keybrd library. Row is an abstract base class that allows flexibility in designing derived Row classes:

  • Row functions can be overridden in a derived class
  • choice of Debouncers
  • choice of Scanners

This example illustrates the custom Row classes for a fictional keybrd_Ext extension library. The keybrd_Ext library is for a split keyboard with a matrix on each hand and sticky keys.

Row_Ext::keyWasPressed() overrides Row::keyWasPressed() which is used to unstick sticky keys.

Row_Ext_uC and Row_Ext_ShiftRegisters are a custom classes composed of stock keybrd library classes.
Row_Ext_uC uses Scanner_uC to scan the primary matrix.
Row_Ext_ShiftRegisters uses Scanner_ShiftRegs74HC165 to scan the peripheral matrix.

Class inheritance diagram


            Row
             |
           Row_Ext                                (override Row::keyWasPressed() )
          /      \
    Row_Ext_uC  Row_Ext_ShiftRegisters            (inherit Row_Ext::keyWasPressed() )


    Scanner_uC  Scanner_ShiftRegs74HC165

Dependency diagram


             ________ Row_Ext_uC[1] ______________
            /            |                        \
    Scanner_uC[1]  Debouncer_Samples[1]           Key[1..*]
         /                                         |
    strobePin[1]                                  Code[1..*]


            _________ Row_Ext_ShiftRegisters[1] ________
           /                            \               \
    Scanner_ShiftRegs74HC165[1]  Debouncer_Samples[1]  Key[1..*]
          |                                             |
    strobePin[1]                                       Code[1..*]

Class inheritance diagrams

Keybrd library class inheritance diagram

         ________ Row ___________
        /          |             \
    Row_uC  Row_ShiftRegisters  Row_IOE (todo to be added)


    Scanner_uC  Scanner_Port  Scanner_ShiftRegs74HC165


        PortIOE

        PortWrite
           |
    PortWrite_PCA9655E                          (one PortWrite class for each IOE type)
 
        PortRead
           |
    PortRead_PCA9655E                           (one PortRead class for each IOE type)
 
        _ LED _
       /       \
    LED_uC    LED_PCA9655E


    DebouncerInterface
          |
    Debouncer_Samples

    ScanDelay

    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_LEDLock  Code_Null
                                 /      \
                            Code_ScS  Code_ScNS

Dependency diagrams

Dependency diagram of example single-layer keyboard with LEDs

            _ Row_uC[1..*] _
           /      |         \
    Scanner_uC  Debouncer  Keys[1..*] __
                             |          \
                           Code[1..*]  Code_LEDLock[1..*]
                                         |
                                       LED_PinNumber[1]

Dependency diagram of example multi-layer keyboard with layer LEDs

                                              LayerStates[1..*]
             ________ Row_uC[1..*] ___________/__    |         \
            /           |         \          /   \   |          \
    Scanner_uC[1]  Debouncer[1]  Keys[1..*] / Code_Layer[1..*]  LED_PinNumber[0..*]
                                   |       /
                                 Code[1..*]

Dependency diagram of example peripheral matrix with shift registers

                  Row_ShiftRegisters[1..*]
                 /              \         \
    RowScanner_ShiftRegisters  Debouncer  Keys[1..*]
                                           |
                                          Code[1..*]

Dependency diagram of example peripheral matrix with I/O Expander and LEDs

                     _____ Row_IOE[1..*] _________
                    /               \             \
            __ Scanner_Port[1] __   Debouncer[1]  Keys[1..*] __
           /          |          \                  |          \
    PortWrite[1]  strobePin[1]  PortRead[1]      Code[1..*]  Code_LEDLock[1..*]
           \                   /   \                            |
            \                 /   ColPins[1..*]              LED[1]
             \               /
               PortIOE[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

Layer classes are explained in tutorial_3a_multi-layer_keyboard.md.

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”.
  • Member names use camelCase starting with lowercase letter.
  • Use constants rather than macros, except for header guards.
  • Global const names and static const names use ALL_CAPS_WITH_UNDERSCORE.
  • Macros use ALL_CAPS_WITH_UNDERSCORE and have _MACRO suffix e.g. SAMPLE_COUNT_MACRO
  • Header guards have _H suffix e.g. #ifndef FILE_NAME_H
  • Pointer names are prefixed with “ptr” e.g. ptrRow = &row;
  • Arrays names use the plural of the element name e.g. Row* const = ptrsRows { &row0, &row1 };
  • Pass arrays using array notation rather than pointer notation: 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 (make memory leaks impossible).
  • Document class interface in .h file, above the class declaration.
  • Code should be self-documenting. A simple function with a good name needs no comment.
  • Code is automatically formatted 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 a list of functions in the order that they are called. The trace is of a one-row single-layer keybrd scan. Refer to it like a table of contents while reading the keybrd library.

    loop()                                      for each row
        Row::process()
            Scanner_uC::scan()                      strobe row on
                                                        for each readPin
                                                            set readState bit
                                                    strobe row off
            Debouncer_Samples::debounce()           debounce
            Row::send()                         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)
        scanDelay.delay();
         

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>

Creative Commons License
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.