Archived
1
0

initial commit keybrd version 0.3.0

This commit is contained in:
wolfv6 2016-05-09 08:05:08 -06:00
commit 98b6060e7c
92 changed files with 3846 additions and 0 deletions

21
LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Wolfram Volpi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

44
README.md Normal file
View File

@ -0,0 +1,44 @@
keybrd library for creating custom-keyboard firmware
====================================================
keybrd library is an open source library for creating custom-keyboard firmware.
The resulting keyboard firmware is compatible with standard USB keyboard drivers.
keybrd library can support any keyboard configuration:
* one-piece
* split with I/O expander
* single-layer
* multiple-layer
Multiple-layer keyboards can write symbols without using the shift key:
~ ! @ # $ % ^ & * () _ {} | < > : ?
keybrd library leverages the Arduino environment to create keyboard firmware.
The Arduino development environment is free, and easy for novice programmers to setup and learn.
The keybrd library has been tested on the Teensy 2.0 microcontroller, MCP23018 I/O expander, and PCA9655E I/O expander.
> The public API should not be considered stable.
> Currently the keybrd library is limited to 8x8 matrices, which is enough for compact split keyboards.
Example minimal keybrd sketch
-----------------------------
Here is a [minimal keybrd sketch](keybrd_single-layer_2/keybrd_single-layer_2.ino).
todo after teensy LC bb, copy and remove annotations from keybrd_single-layer_2_annotated.ino
The sketch has about 50 lines of code and runs a 4-key keyboard.
It runs on a breadboard with rows, columns, and diodes just like the big keyboards.
The sketch is small because the keybrd library takes care of the low-level details.
The keybrd tutorial 1 shows how to make a breadboard keyboard.
The keybrd tutorials 2 and 3 show how to create custom keybrd firmware.
Example complex keybrd sketch
-----------------------------
The DodoHand keybrd emulates the DataHand keyboard.
It has 72 keys, 4 layers, 2 sub-layers, 2 matrices, and is loaded with features.
The [DodoHand sketch](todo /sketch.cpp), and its instantiation files, contain about 800 lines of code.
Support
-------
The [doc](doc) folder contains guides and tutorials.
Please ask a questions in [issues](https://github.com/wolfv6/Keybrd/issues) if something is not clear.

33
doc/CHANGELOG.md Normal file
View File

@ -0,0 +1,33 @@
# Change Log
All notable changes to the keybrd project will be documented in this file.
This project adheres to [Semantic Versioning 2.0.0](http://semver.org/).
Version 0.x.x is for initial development. The public API should not be considered stable.
Version 1.0.0 will be released when the public API is stable.
## [Unreleased][unreleased]
## [0.3.0] - 2016-05-09
### Changed
* Restructured the project directory to conform to Arduino library manager specifications
* Moved keybrd_DH library extension (DodoHand) to its own repository
* Moved sketches to examples directory
* Replaced Key_Layered dependency on LayerManager with StateLayers class
### Added
* tutorials
## [0.2.0] - 2016-02-25
### Added
* Port classes for micro-controllers and I/O expanders
* DH_2565 sketch with DataHand layout
* Sticky mouse button (SMB) for DataHand layout
* Supporting documentation
## [0.1.0] - 2015-02-10
### Added
* The library runs on Teensy 2.0 microcontroller and MCP23018 I/O expander
* Limited to 8x8 matrix, which is enough for compact or split keyboards
* First draft of supporting documentation
* Example keybrd sketches for single-layer, multi-layer, and DataHand layer schemes

View File

@ -0,0 +1,37 @@
Teensy 2.0 Pinout Diagram
-------------------------
USB is on top in the diagram.
Inner columns are pin numbers, outer columns are port+bit pin name.
```
ground GND USB VCC +5v power
B0 0 21 F0
B1 1 20 F1
B2 2 19 F4
B3 3 18 F5
B7 4 17 F6
SCL D0 5 16 F7
SDA D1 6 15 B6
D2 7 14 B5
D3 8 13 B4
C6 9 12 D7
C7 10 11 D6 Do not use pin D6 for scanning keyboard matrix
LED on pin D6 pulls voltage down and will always return low
BOTTOM (USB on top, left to right)
PIN# port+bit function
23 D5
VCC 5v power
GND ground
RST reset
22 D4
MID (below USB, left to right)
PIN# port+bit function
24 E6
Ref
```
Teensy pin out with port names is on http://www.pjrc.com/teensy/td_digital.html
Identifying and naming ports is useful when instantiating RowPorts and ColPorts.
Keybrd library was tested on Teensy 2.0

11
doc/astyle_cpp Normal file
View File

@ -0,0 +1,11 @@
# this file specifies style for keybrd C++ and Arduino sketch .ino files
# Artistic Style is a console application for formatting C++ and Java source code
# http://sourceforge.net/projects/astyle/files/ download
# http://astyle.sourceforge.net/astyle.html manual
--style=allman
--indent-classes
--keep-one-line-blocks
--indent=spaces=4
--convert-tabs
--max-code-length=100

View File

@ -0,0 +1,189 @@
keybrd Library Developer's Guide
================================
This guide contains diagrams, naming conventions, and a style guide.
This guide will help you design custom classes to interface with the keybrd library.
The most common need for custom 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, developers, and anyone that wants to extend the keybrd library.
It is assumed the reader is familiar with C++ language including pointers, objects, classes, static class variables, composition, inheritance, polymorphism, and enum.
Some classes use bit manipulation.
## Custom keybrd classes
Please refer to tutorial_7a_using_someone_else's_keybrd_extension_library.md
## Class diagrams
Keybrd library class inheritance diagram
```
Matrix
Row
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)
StateLayersInterface
|
StateLayers
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
```
## Association diagrams
single-layer Keybrd association diagram with LEDs
```
keybrd[1]
|
matrix[1..*]
|
row[1..*]_____________________________
| \ \ \
rowPort[1] rowPin[1] colPort[1] keys[1]
| |
colPins[1..*] code[1..*]
|
LED[1]
```
multi-layer Keybrd association diagram with LEDs and I/O Expander
```
keybrd[1]
|
matrix[1..*]
| stateLayers[1..*]
row[1..*]_________________________________________/__ | \
| \ \ \ / \ | \
rowPort[1] rowPin[1] colPort[1] keys[1] / code_layer[1..*] LED[0..*]
\ / \ | / /
\ / colPins[1..*] key[1..*] /
\ / | /
\ / code[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_Sc
```
This convention leads to class names that convey information about the classes inheritance.
Underscore delineates base class name and sub name. Capital letters delineate words.
## 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 elements 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 (to make memory leaks impossible).
* If class has any non-[POD](http://en.wikipedia.org/wiki/Plain_old_data_structure) data members, [do not inline constructors and destructors](http://www.chromium.org/developers/coding-style/cpp-dos-and-donts).
* 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.
http://stackoverflow.com/questions/2198241/best-practice-for-c-function-commenting
* Code is automatically formated before being pushed to the keybrd repository
The options file doc/astyle_cpp specifies the format:
* Allman style indentation
* indent 4 spaces
* replace tabs with spaces
* maximum code width of 100 columns
## keybrd sketches
keybrd sketch naming convention is described in the keybrd_library_user_guide "Sample keybrd sketches".
The head of sketch should contain latest version of keybrd lib that sketch was test on:
tested on keybrd v1.1 by wolfv6
## trace of keybrd scan
Arduino does not have a debugger; so here is a list of functions in the order that they are called.
Refer to it like a table of contents while reading the keybrd library.
```
Keybrd::scan() for each matrix
Matrix::scan() for each row
Row::process()
Row::scan()
RowPort_*::setActivePin*() strobe row on
for each col port
ColPort_*::read() read col port
RowPort_*::setActivePin*() strobe row off
Row::getRowState() for each col port
for each connected col pin
if key is pressed
set rowState bit
Row::debounce() debounce
Row::detectEdge() detect edge
Row::pressRelease() for each key in row
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](https://www.arduino.cc/en/Reference/Libraries):
#include <Arduino.h>
#include <Wire.h>
#include <Keyboard.h>
#include <Mouse.h>

View File

@ -0,0 +1,220 @@
keybrd Library User's Guide
===========================
keybrd is an open source library for creating custom-keyboard firmware.
The resulting keyboard firmware is compatible with standard USB keyboard drivers.
This guide shows how to
* set up the Arduino development environment
* install the keybrd library
* compile and load keybrd firmware
The Arduino development environment is free and simple as possible.
Its easy for novice programmers to setup and learn.
## Who this guide is for
This guide is for anyone who wants to use the keybrd library to develop custom-keyboard firmware.
A reader with programming experience, but no C++ experience, would understand the tutorials well enough to modify existing keybrd sketches.
An experienced C++ programmer would be able to write original sketches and classes from scratch.
The library is written in the C++ language and uses pointers, objects, classes, static class variables, composition, inheritance, and enum.
## Microcontroller board requirements
The keybrd library works with Teensy and Arduino boards.
[Teensy LC](https://www.pjrc.com/teensy/teensyLC.html) has 8K RAM, which is more than enough memory for any keyboard.
keybrd has been tested on the DodoHand keyboard with Teensy 2.0 and PCA9655E I/O expander using the DH_2565 sketch.
Teensy LC is preferred over the older Teensy 2.0 for it's larger memory capacity and lower price.
## Getting started with Teensy, Arduino IDE, and keybrd
The Arduino IDE is used to edit and compile sketches, and then load them on to the microcontroller.
Teensyduino is a software add-on for the Arduino IDE that allows it to compile to Teensy.
[Teensy Getting Started](http://www.pjrc.com/teensy/first_use.html) is a good way to familiarize yourself with Teensy.
[Arduino Development Environment](http://arduino.cc/en/guide/Environment) is a brief description.
The following steps create an Arduino development environment for keybrd sketches.
### Install Arduino IDE and Teensyduino
Follow the install steps are modified from the [Teensyduino download page](https://www.pjrc.com/teensy/td_download.html)
For Linux:
1. Download and extract the Arduino software.
Move the extracted directory to /opt:
$ sudo mv ~/Downloads/arduino-1.6.7 /opt/arduino-1.6.7
2. The "Linux udev rules" link is at top right of page.
Save the teensy.rules file in /etc/udev/rules.d/
3. "Teensyduino Files" installer links are at top of page.
Download the installer to your Downloads directory.
Make the installer executable:
$ chmod 755 teensyduino.64bit
Run the teensyduino installer and fill the form fields:
Arduino location to install Teensyduino: /usr/local/bin/arduino-1.x.x
Libraries to Install: None
4. Launch Arduino IDE from /opt/arduino-1.x.x/arduino
### Setup Arduino IDE for compiling keybrd firmware
From the Arduino IDE tool bar, select:
* Tools > Board > Teensy LC (or whatever board you are using)
* Tools > USB Type > Keyboard + Mouse + Joystick
optional:
* File > Preferences > Compiler warnings: All
* File > Preferences > check: Use external editor
A Sketchbook is a folder that the Arduino IDE uses to store sketches and libraries.
The default location for [Arduino libraries](https://www.arduino.cc/en/Guide/Libraries) is in
~/Documents/Arduino/libraries/
### Download and unpack keybrd-master.zip into your Arduino directory
todo update after testing Arduino library manager
link from tutorial 7 ## Publishing
https://www.arduino.cc/en/Guide/Libraries
> Installing Additional Arduino Libraries
> Using the Library Manager
Down load keybrd-master.zip from the [Download ZIP](https://github.com/wolfv6/keybrd) button.
Unpack keybrd-master.zip into your Arduino directory on your system (default location is ~/Documents/Arduino/).
### keybrd library and keybrd extension libraries
todo update after testing Arduino library manager
The keybrd library contains the foundation classes for creating a keyboard firmware.
For emphasis, it is sometimes referred to as the "core keybrd library".
keybrd extension libraries contain additional classes that extend the keyboard library.
keybrd extension library names are prefixed by "keybrd_".
The Arduino IDE looks for libraries in Arduino/libraries/.
For example, the DodoHand keyboard requires that the core keybrd library and the keybrd_DH extension library be installed:
* Arduino/libraries/keybrd/
* Arduino/libraries/keybrd_DH/
A keybrd extension library allows classes to be shared by multiple sketches without polluting the core keybrd library with classes that few other keyboards can use.
### Compile and load keybrd sketch
If it isn't already plugged in, plug the USB cable into the computer and controller.
> CAUTION: It is possible to loose control of your keyboard when running a keybrd sketch.
> If the keybrd sketch has a mouse object, it is also possible to loose control of your mouse.
> USB keyboard protocol is capable of spewing characters and mouse commands at up to 500 per second.
> Take the following precautions before uploading an untested keybrd sketch to a controller:
> * Save all files and close dangerous applications.
> * Park the cursor in an editor opened to a test file.
> That way you can immediately see if the controller starts spewing characters.
> * Be prepared to turn off the controller:
> turn off Teensy Loader's green "Auto" button and push Teensy's reset button
> if that fails, unplug Teensy USB
Compile and load workflow:
1. Open a keybrd sketch in the Arduino IDE (for example Arduino/keybrds/firmware/keybrd_single-layer/keybrd_single-layer_1221_bb/keybrd_single-layer_1221_bb.ino)
2. Prepare for loosing control of keyboard and mouse.
3. On the Arduino IDE, click the Upload button.
4. The Teensy boot loader window opens, you might need to press and release the tiny pushbutton on the Teensy circuit board.
## Example keybrd sketches
Example keybrd sketches are in the keybrd_proj/keybrd/examples/ directory.
Extension libraries have their example sketches similarly located.
The example sketch names use the following conventions.
keybrd_extension_feature_version.ino
where
* **keybrd** indicates a keybrd sketch
* **extension** is the extension library name e.g. DH, DualMode
* **feature** is distinguishing feature of keybrd sketch e.g. breadboard, LED, sound, Dvorak
* **version** is version number
The first two fields are mandatory, the remaining fields are optional.
## Diode orientation
The physical martix rows and columns on a keyboard can be in any direction or shape.
[diode](https://en.wikipedia.org/wiki/Diode) orientation is specified in [Matrix.h](todo)
![Diode](images/120px-Diode_pinout_en_fr.svg.png)
Diagram is of typical through-the-hole diode in same alignment as diode symbol.
Cross bar and band depict the cathode.
## Troubleshooting check list
The following is a listing of items to check when a new keybrd sketch or keyboard is having trouble.
Development-environment items to check:
* If the keyboard has an I/O expander, power cycle (replug the USB) after loading the HEX file.
* If the keybrd extension library directory name or location was changed, see section
[Populate Arduino/libraries with keybrd library symlinks](todo link)
* If compile error: 'KEY_A' was not declared in this scope
From the Arduino IDE tool bar, select: Tools > USB Type > Keyboard + Mouse + Joystick
Sketch items to check:
* For each row, number of keys in Row should equal number of colPort pins.
In this example, row_0 has six colPort pins in ptrsColPorts, and six keys in ptrsKeys_0:
```
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<0 | 1<<1 | 1<<2 | 1<<3 );
ColPort_AVR colPortD(DDRD, PORTD, PIND, 1<<2 | 1<<3 );
ColPort* const ptrsColPorts[] = { &colPortB, &colPortD };
const uint8_t COL_PORT_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts);
const Key* const ptrsKeys_0[] = { &k_00, &k_01, &k_02, &k_03, &k_04, &k_05 };
Row row_0(ptrsKeys_0, &rowPortF, 1<<0, ptrsColPorts, COL_PORT_COUNT);
```
* Some of the constructors take array-element-count arguments, make sure that the correct counts are passed to the constructors. Or use sizeof() like this example:
```
Row* const ptrsRows[] = { &row0, &row1, &row2, &row3 };
const uint8_t ROW_COUNT = sizeof(ptrsRows)/sizeof(*ptrsRows);
Matrix matrix(ptrsRows, ROW_COUNT, 1);
```
* For multi-layered keyboards, the number of codes in each Key_Layered should equal the number of layers.
Hardware items to check:
* Connections
* Diode orientation
* 5 volts across power and ground
* To validate keyboard hardware, modify the simple single-layer keybrd sketch from the tutorial.
## Keybrd nomenclature
**[scancode](http://en.wikipedia.org/wiki/Scancode)** -
Is a 16-bit integer assigned to a key position on a keyboard.
The keyboard sends a scancode to the computer for every key press and release.
**[Layers](http://deskthority.net/wiki/Layer)** -
are key bindings provided by the keyboard firmware.
The standard [IBM PC keyboard](http://en.wikipedia.org/wiki/IBM_PC_keyboard) has one layer.
Many compact keyboards have an additional [Fn layer](http://en.wikipedia.org/wiki/Fn_key).
The [Neo layout](http://neo-layout.org/index_en.html) has 6 layers.
**Layer code** - is an integer assigned to a layer.
**Layer scheme** - is a system for changing layers while typing.
A single-layer scheme does not change layers.
**Layout** - is a grid of keys. Key caps are often labeled to show a keyboard's layout.
**[Matrix](http://pcbheaven.com/wikipages/How_Key_Matrices_Works/)** - is a collection of switches connected by rows and columns.
**[bounce](http://en.wikipedia.org/wiki/Switch#Contact_bounce)** -
Keyboard switches are made of moving contacts.
When the contacts close, they bounce apart one or more times before making steady contact.
A debouncer removes the bounce so that a key press is sent to the computer only once.
**[Modifier key](http://en.wikipedia.org/wiki/Modifier_key)** - is a special key on a computer keyboard that temporarily modifies the normal action of another key when pressed together. By themselves, modifier keys usually do nothing; that is, pressing any of the Shift, Alt, or Ctrl keys alone does not trigger any action from the computer.
**Sketch** - is the name that Arduino uses for a program
**keybrd sketch** - is an Arduino sketch that uses the keybrd library to define a keyboard firmware.

22
doc/planned_features.md Normal file
View File

@ -0,0 +1,22 @@
PLANNED FEATURES is a view of where the keybrd project is headed.
Top priority
============
Add interrupts for I2C
Med priority
============
Add support for Teensy LC micro controller
Add 16x16 matrix capability (currently limited to 8x8 matrices)
Low priority
============
Add matrix-to-layout transform array (to decouple matrix from layout)
Change tutorial sketches from teensy 2.0 and PCA9655E-D IOE to Teensy LC and MCP23018 IOE
Add more tutorials:
tutorial_5_LEDs.md
tutorial_6_mapping_matrix_to_layout.md
tutorial_9_active_high.md

View File

@ -0,0 +1,112 @@
/* keybrd_mapping_bb.ino
Runs on DodoHand hardware or breadboard, using the left matrix, first two rows and columns.
Uses the same variable naming convention as DH_2565.
| Layout | **0** | **1** |
|:------:|-------|-------|
| **0** | a ! | b @ |
| **1** | fn | shift |
*/
// ################# GLOBAL ####################
// ================ INCLUDES ===================
//Arudino library files
#include <Wire.h>
//keybrd library files
//#include <objects_scancode.h>
#include <Code_Sc.h>
#include <Code_ScS.h>
#include <Code_Shift.h>
#include <StateLayers.h>
//#include <Code_LayerLock.h>
#include <Code_LayerHold.h>
#include <Key_LayeredKeysArray.h>
#include <RowPort_AVR_Optic.h>
#include <ColPort_AVR.h>
#include <Row.h>
#include <Matrix.h>
#include <Debug.h>
// ================= DEBUG =====================
Debug debug;
// =========== SPEED CONFUGURATIONS ============
const unsigned int Row::DELAY_MICROSECONDS = 1000;
// =============== LEFT PORTS ==================
RowPort_AVR_Optic rowPortF_L(DDRF, PORTF);
ColPort_AVR colPortB_L(DDRB, PORTB, PINB, 1<<0 | 1<<1 );
ColPort* const ptrsColPorts_L[] = { &colPortB_L };
const uint8_t COL_PORT_L_COUNT = sizeof(ptrsColPorts_L)/sizeof(*ptrsColPorts_L);
// ================= CODES =====================
// -------------- LAYER CODES ------------------
StateLayers stateLayer;
//Code_LayerLock l_alpha(0, stateLayer);
Code_LayerHold l_fn(1, stateLayer);
// --------------- SHIFT CODE ------------------
Code_Shift s_shift(MODIFIERKEY_LEFT_SHIFT);
Code_Shift *const ptrsShift[] = { &s_shift };
Code_Shift *const *const Code_AutoShift::ptrsShifts = ptrsShift;
const uint8_t Code_AutoShift::shiftCount = sizeof(ptrsShifts)/sizeof(*ptrsShifts);
// --------------- SCAN CODES ------------------
Code_Sc s_a(KEY_A);
Code_Sc s_b(KEY_B);
Code_ScS s_exclamation(KEY_1);
Code_ScS s_at(KEY_2);
StateLayersInterface& Key_LayeredKeysArray::refStateLayers = stateLayer;
// ============== LEFT MATRIX ==================
// --------------- LEFT KEYS -------------------
Key* const ptrsCodes_L00[] = { &s_a, &s_exclamation };
Key_LayeredKeysArray k_L00(ptrsCodes_L00);
Key* const ptrsCodes_L01[] = { &s_b, &s_at };
Key_LayeredKeysArray k_L01(ptrsCodes_L01);
// -------------- LEFT MAPPING -----------------
// the mapping layout array consumes no additional SRAM
/*
Key* const ptrsLayout[2][2] = { { &k_L00, &k_L01 },
{ &l_fn, &s_shift } };
*/
Key* const ptrsLayout[2][2] = { { &k_L01, &k_L00 }, //swapped keys a-b
{ &l_fn, &s_shift } };
// --------------- LEFT ROWS -------------------
Key* const ptrsKeys_L0[] = { ptrsLayout[0][0], ptrsLayout[0][1] };
Row row_L0(rowPortF_L, 1<<0, ptrsColPorts_L, COL_PORT_L_COUNT, ptrsKeys_L0);
Key* const ptrsKeys_L1[] = { ptrsLayout[1][0], ptrsLayout[1][1] };
Row row_L1(rowPortF_L, 1<<1, ptrsColPorts_L, COL_PORT_L_COUNT, ptrsKeys_L1);
// -------------- LEFT MATRIX ------------------
Row* const ptrsRows_L[] = { &row_L0, &row_L1 };
const uint8_t ROW_L_COUNT = sizeof(ptrsRows_L)/sizeof(*ptrsRows_L);
Matrix matrix_L(ptrsRows_L, ROW_L_COUNT, 1);
// ################## MAIN #####################
void setup()
{
Keyboard.begin();
delay(1000); //time for OS to detect USB before printing
Keyboard.print(F("keybrd_mapping_bb.ino, "));
debug.print_free_RAM();
}
void loop()
{
matrix_L.scan();
}

9
library.properties Normal file
View File

@ -0,0 +1,9 @@
name=keybrd
version=0.3.0
author=Wolfram Volpi
maintainer=Wolfram Volpi
sentence=A library for creating custom-keyboard firmware.
paragraph=<br>Create keyboards with any configuration:<br>one-piece, split with I/O expander, single-layer, multiple-layer
category=Device Control
url=https://github.com/wolfv6/keybrd
architectures=avr

11
src/Code.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef CODE_H
#define CODE_H
#include "Key.h"
/* Code is an interface class
It's derived concrete classes send press and release USB scancodes to the computer.
*/
class Code : public Key
{
};
#endif

30
src/Code_AutoShift.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "Code_AutoShift.h"
bool Code_AutoShift::isShifted() const
{
//if a shift key is pressed return true else return false
for (uint8_t i = 0; i < shiftCount; i++)
{
if (ptrsShifts[i]->isPressed())
{
return true;
}
}
return false;
}
void Code_AutoShift::releaseAllShifts() const
{
for (uint8_t i = 0; i < shiftCount; i++)
{
ptrsShifts[i]->unpress();
}
}
void Code_AutoShift::restoreAllShifts() const
{
for (uint8_t i = 0; i < shiftCount; i++)
{
ptrsShifts[i]->restorePressed();
}
}

38
src/Code_AutoShift.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef CODE_AUTOSHIFT_H
#define CODE_AUTOSHIFT_H
#include "Code.h"
#include "Code_Shift.h"
/* Code_AutoShift is an abstract base class for Codes that depend on automatic shifting.
Code_AutoShift can manage one or more shift keys.
Example initialization:
const Code_Shift s_shift(MODIFIERKEY_LEFT_SHIFT);
const Code_Shift *const ptrsS[] = { &s_shift };
const Code_Shift *const *const Code_AutoShift::ptrsShifts = ptrsS;
const uint8_t Code_AutoShift::shiftCount = sizeof(ptrsShifts)/sizeof(*ptrsShifts);
The two Code_Shift pointer arrays (ptrsShifts and ptrsS) must have distinct names.
Automatic shifting is usful on multi-layered keyboards.
The shift state for Code_ScS and Code_ScNS are changed and restored:
Code_ScS object is a scancode shifted e.g. '%' in symbols layer
Code_ScNS object is a scancode not shifted e.g. '5' in numbers layer
keyboards without Code_ScS and Code_ScNS can omit ptrsShifts[] array and
and place scancode MODIFIERKEY_LEFT_SHIFT directly in Code_Sc:
Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT);
*/
class Code_AutoShift : public Code
{
private:
static Code_Shift *const *const ptrsShifts; //array of Code_Shift pointers
static const uint8_t shiftCount;
protected:
bool isShifted() const;
void releaseAllShifts() const;
void restoreAllShifts() const;
public:
virtual void press()=0;
virtual void release()=0;
};
#endif

68
src/Code_LEDLock.cpp Normal file
View File

@ -0,0 +1,68 @@
#include "Code_LEDLock.h"
/* USB_LED_bit are codes from http://www.usb.org/developers/hidpage/HID1_11.pdf keyboard output report
*/
Code_LEDLock::Code_LEDLock(const uint16_t scancode, LED& refLED)
: scancode(scancode), refLED(refLED)
{
switch (scancode) //initilize USB_LED_bit for given scancode
{
case KEY_NUM_LOCK:
USB_LED_bit = 1<<0;
break;
case KEY_CAPS_LOCK:
USB_LED_bit = 1<<1;
break;
case KEY_SCROLL_LOCK:
USB_LED_bit = 1<<2;
break;
/* guessing at these case names:
case KEY_COMPOSE: //for separate accent keys
USB_LED_bit = 1<<3; break;
break;
case KEY_KANA: //for Japanese keyboards
USB_LED_bit = 1<<4; break;
break;
*/
}
}
void Code_LEDLock::press()
{
Keyboard.press(scancode);
updateLED();
}
void Code_LEDLock::release()
{
Keyboard.release(scancode);
}
/* updateLED() is a separate function from press() because Arduino boards may need a different implementation.
updateLED() has been tested on teensy 2.0.
The variable "keyboard_leds" is in /opt/arduino-1.6.7/hardware/teensy/avr/cores/usb_hid/usb.c
// 1=num lock, 2=caps lock, 4=scroll lock, 8=compose, 16=kana
https://forum.pjrc.com/threads/25368-How-do-I-receive-a-numlock-capslock-LED-signal-from-the-PC
updateLED() has NOT been tested on an Arduino board.
The word "keyboard_leds does not appear in "Arduino\hardware\arduino\cores\
This shows how to hack KeyReport in Arduino: https://www.sparkfun.com/tutorials/337
TMK firmware uses variable "usb_led" instead of "keyboard_leds"
http://deskthority.net/workshop-f7/how-to-build-your-very-own-keyboard-firmware-t7177.html >usb_led
*/
void Code_LEDLock::updateLED() const
{
/* KEY_SCROLL_LOCK is not working on Teensy2.0, it prints keyboard_leds=0, maybe Linux doesn't have it.
Here is the debug code:
Keyboard.print(F(" keyboard_leds="));
Keyboard.print(keyboard_leds);//KEY_NUM_LOCK:1, KEY_CAPS_LOCK:2, KEY_SCROLL_LOCK:0
Keyboard.print(" ");
*/
if (keyboard_leds & USB_LED_bit) //if LED status bit is set
{
refLED.off(); //LED on/off seem inverted, but it works
}
else
{
refLED.on();
}
}

32
src/Code_LEDLock.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef CODE_LEDLOCK_H
#define CODE_LEDLOCK_H
#include <Arduino.h>
#include <inttypes.h>
#include <Code.h>
#include <LED.h>
extern volatile uint8_t keyboard_leds;
/* Class Code_LEDLock turns LED on and off
scancode is KEY_CAPS_LOCK, KEY_SCROLL_LOCK, or KEY_NUM_LOCK
In keybrd sketch, ports should be instantiated before Code_LEDLock is instantiated
because LED.off() needs ports to be configured by port constructor.
If a key does not have an LED indictor light, use Code_S instead e.g.:
Code_S CapsLck(KEY_CAPS_LOCK);
*/
class Code_LEDLock : public Code
{
private:
const uint16_t scancode;
uint8_t USB_LED_bit; //codes used by keyboard output report
LED& refLED;
void updateLED() const;
public:
Code_LEDLock(const uint16_t scancode, LED& refLED);
virtual void press();
virtual void release();
};
#endif

11
src/Code_LayerHold.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "Code_LayerHold.h"
void Code_LayerHold::press()
{
refStateLayers.hold(layer);
}
void Code_LayerHold::release()
{
refStateLayers.unhold(layer);
}

21
src/Code_LayerHold.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef CODE_LAYERHOLD_H
#define CODE_LAYERHOLD_H
#include <inttypes.h>
#include <Code.h>
#include "StateLayers.h"
/* Code_LayerHold calls StateLayers when pressed to change activeLayer.
*/
class Code_LayerHold : public Code
{
private:
const uint8_t layer;
StateLayers& refStateLayers;
public:
Code_LayerHold(const uint8_t layer, StateLayers& refStateLayers)
: layer(layer), refStateLayers(refStateLayers) {}
virtual void press();
virtual void release();
};
#endif

10
src/Code_LayerLock.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "Code_LayerLock.h"
void Code_LayerLock::press()
{
refStateLayers.lock(layer);
}
void Code_LayerLock::release()
{
}

21
src/Code_LayerLock.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef CODE_LAYERLOCK_H
#define CODE_LAYERLOCK_H
#include <inttypes.h>
#include <Code.h>
#include "StateLayers.h"
/* Code_LayerLock calls StateLayers when pressed to change activeLayer.
*/
class Code_LayerLock : public Code
{
private:
const uint8_t layer;
StateLayers& refStateLayers;
public:
Code_LayerLock(const uint8_t layer, StateLayers& refStateLayers)
: layer(layer), refStateLayers(refStateLayers) {}
virtual void press();
virtual void release();
};
#endif

View File

@ -0,0 +1,7 @@
#include "Code_LayeredCodeSc.h"
void Code_LayeredCodeSc::press()
{
layer = refStateLayers.getActiveLayer();
pressCode();
}

23
src/Code_LayeredCodeSc.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef CODE_LAYEREDCODESC_H
#define CODE_LAYEREDCODESC_H
#include <Arduino.h>
#include <inttypes.h>
#include <Code_LayeredCodeScBase.h>
#include <StateLayersInterface.h>
/* Class Code_LayeredCodeSc is a 2-layer code, one object for each layer e.g.
layer0: ms_up //mouse up
layer1: KEY_UP //up arrow
When the key is pressed, the active layer is retrieved from refStateLayers,
and the object for the active layer is sent to USB.
*/
class Code_LayeredCodeSc : public Code_LayeredCodeScBase
{
private:
static StateLayersInterface& refStateLayers;
public:
Code_LayeredCodeSc(Code& refCode0, const uint16_t scancode1)
: Code_LayeredCodeScBase(refCode0, scancode1, 0) { }
virtual void press();
};
#endif

View File

@ -0,0 +1,25 @@
#include "Code_LayeredCodeScBase.h"
void Code_LayeredCodeScBase::pressCode()
{
if (layer)
{
Keyboard.press(scancode1);
}
else
{
refCode0.press();
}
}
void Code_LayeredCodeScBase::release()
{
if (layer)
{
Keyboard.release(scancode1);
}
else
{
refCode0.release();
}
}

View File

@ -0,0 +1,27 @@
#ifndef CODE_LAYEREDCODESCBASE_H
#define CODE_LAYEREDCODESCBASE_H
#include <Arduino.h>
#include <inttypes.h>
#include "Code.h"
/* Class Code_LayeredCodeScBase is a 2-layer code, one object for each layer e.g.
layer0: ms_up //mouse up
layer1: KEY_UP //up arrow
When the key is pressed, the active layer is retrieved from refStateLayers,
and the object for the active layer is sent to USB.
*/
class Code_LayeredCodeScBase : public Code
{
private:
Code& refCode0;
const uint16_t scancode1;
protected:
bool layer;
public:
Code_LayeredCodeScBase(Code& refCode0, const uint16_t scancode1, uint8_t layer):
refCode0(refCode0), scancode1(scancode1), layer(layer) { }
virtual void press()=0;
virtual void release();
virtual void pressCode();
};
#endif

7
src/Code_LayeredScSc.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "Code_LayeredScSc.h"
void Code_LayeredScSc::press()
{
layer = refStateLayers.getActiveLayer();
pressScancode();
}

22
src/Code_LayeredScSc.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef CODE_LAYEREDSCSC_H
#define CODE_LAYEREDSCSC_H
#include <Arduino.h>
#include <inttypes.h>
#include <StateLayersInterface.h>
#include <Code_LayeredScScBase.h>
/* Class Code_LayeredScSc is composed of two scancodes; "S" stands for Scancode.
layer is retreived from refStateLayers.
when layer=0, press sends scancode0
when layer=1, press sends scancode1
*/
class Code_LayeredScSc : public Code_LayeredScScBase
{
private:
static StateLayersInterface& refStateLayers;
public:
Code_LayeredScSc(const uint16_t scancode0, const uint16_t scancode1)
: Code_LayeredScScBase(scancode0, scancode1) { }
virtual void press();
};
#endif

View File

@ -0,0 +1,20 @@
#include "Code_LayeredScScBase.h"
void Code_LayeredScScBase::pressScancode()
{
if (layer)
{
scancode = scancode1;
}
else
{
scancode = scancode0;
}
Keyboard.press(scancode);
}
void Code_LayeredScScBase::release()
{
Keyboard.release(scancode);
}

View File

@ -0,0 +1,26 @@
#ifndef CODE_LAYERED2SCANCODES_H
#define CODE_LAYERED2SCANCODES_H
#include <Arduino.h>
#include <inttypes.h>
#include "Code.h"
/* Class Code_LayeredScScBase is an abstract base class. It is composed of two scancodes:
if layer=0, send scancode0
if layer=1, send scancode1
*/
class Code_LayeredScScBase : public Code
{
private:
const uint16_t scancode0;
const uint16_t scancode1;
uint16_t scancode;
protected:
bool layer; //0 or 1
public:
Code_LayeredScScBase(const uint16_t scancode0, const uint16_t scancode1):
scancode0(scancode0), scancode1(scancode1), layer(0) { }
virtual void press()=0;
virtual void release();
void pressScancode();
};
#endif

14
src/Code_Null.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef CODE_NULL_H
#define CODE_NULL_H
#include <Arduino.h>
#include <Code.h>
/* Class Code_Null doesn't do anything. It is usefull for blank codes.
*/
class Code_Null: public Code
{
public:
virtual void press() {};
virtual void release() {};
};
#endif

11
src/Code_Sc.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "Code_Sc.h"
void Code_Sc::press()
{
Keyboard.press(scancode);
}
void Code_Sc::release()
{
Keyboard.release(scancode);
}

19
src/Code_Sc.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef CODE_SC_H
#define CODE_SC_H
#include <Arduino.h>
#include <inttypes.h>
#include <Code.h>
/* Class Code_Sc is composed of one scancode, which it sends when press() or release() is called.
"S" stands for Scancode.
*/
class Code_Sc : public Code
{
private:
const uint16_t scancode;
public:
Code_Sc(const uint16_t scancode): scancode(scancode) { }
virtual void press();
virtual void release();
};
#endif

13
src/Code_ScNS.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "Code_ScNS.h"
void Code_ScNS::press()
{
releaseAllShifts();
Keyboard.press(scancode);
restoreAllShifts();
}
void Code_ScNS::release()
{
Keyboard.release(scancode);
}

23
src/Code_ScNS.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef CODE_SCNS_H
#define CODE_SCNS_H
#include <Arduino.h>
#include <inttypes.h>
#include <Code_AutoShift.h>
/* Class Code_ScNS is composed of one scancode, which it sends when press() or release() is called.
autoShift insures that all MODIFIERKEY_SHIFTs are released.
Letters will still print as capital if CapsLck is on.
"ScNS" stands for Scancode Not Shifted.
If scancode is a letter, CapsLck will invert the case.
Normally this is not a problem because most layer schemes control letter case by shift.
*/
class Code_ScNS: public Code_AutoShift
{
private:
const uint16_t scancode;
public:
Code_ScNS(const uint16_t scancode): scancode(scancode) { }
virtual void press();
virtual void release();
};
#endif

20
src/Code_ScS.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "Code_ScS.h"
void Code_ScS::press()
{
if (isShifted())
{
Keyboard.press(scancode);
}
else
{
Keyboard.press(MODIFIERKEY_LEFT_SHIFT); //temporarily shift
Keyboard.press(scancode);
Keyboard.release(MODIFIERKEY_LEFT_SHIFT); //restore
}
}
void Code_ScS::release()
{
Keyboard.release(scancode);
}

22
src/Code_ScS.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef CODE_ScS_H
#define CODE_ScS_H
#include <Arduino.h>
#include <inttypes.h>
#include <Code_AutoShift.h>
/* Class Code_ScS contains one scancode, which it sends when press() or release() is called.
autoShift insures that MODIFIERKEY_LEFT_SHIFT is pressed.
"SS" stands for Scancode Shifted.
If scancode is a letter, CapsLck will invert the case.
Normally this is not a problem because most layer schemes control letter case by shift.
*/
class Code_ScS: public Code_AutoShift
{
private:
const uint16_t scancode;
public:
Code_ScS(const uint16_t scancode): scancode(scancode) { }
virtual void press();
virtual void release();
};
#endif

34
src/Code_Shift.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "Code_Shift.h"
void Code_Shift::press()
{
Keyboard.press(scancode);
pressed = true;
}
void Code_Shift::release()
{
Keyboard.release(scancode);
pressed = false;
}
bool Code_Shift::isPressed()
{
return pressed;
}
void Code_Shift::unpress()
{
if (pressed)
{
Keyboard.release(scancode);
}
}
void Code_Shift::restorePressed()
{
if (pressed)
{
Keyboard.press(scancode);
}
}

23
src/Code_Shift.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef CODE_SHIFT_H
#define CODE_SHIFT_H
#include <Arduino.h>
#include <inttypes.h>
#include <Code.h>
/* Class Code_Shift sends shift scancode for multi-layered keybrds.
Explanation in Code_AutoShift.h
*/
class Code_Shift : public Code
{
private:
bool pressed; //state of physical shift key, false means released
const uint16_t scancode; //MODIFIERKEY_LEFT_SHIFT or MODIFIERKEY_RIGHT_SHIFT
public:
Code_Shift(const uint16_t scancode) : pressed(false), scancode(scancode) {}
virtual void press();
virtual void release();
bool isPressed();
void unpress();
void restorePressed();
};
#endif

11
src/ColPort.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "ColPort.h"
uint8_t ColPort::getColPins()
{
return colPins;
}
uint8_t ColPort::getPortState()
{
return portState;
}

23
src/ColPort.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef COLPORT_H
#define COLPORT_H
#include <Arduino.h>
#include <inttypes.h>
/*
ColPort is an abstract base class.
Port classes are the keybrd library's interface to microcontoller ports or I/O expander ports.
*/
class ColPort
{
protected:
const uint8_t colPins; //bitwise pin configuration, 1 means read column
uint8_t portState; //bitwise pin values, which is set in read()
public:
ColPort(const uint8_t colPins): colPins(colPins), portState(0) {}
//read port and store it's pins values in portState
virtual void read()=0;
uint8_t getColPins();
uint8_t getPortState();
};
#endif

17
src/ColPort_AVR.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "ColPort_AVR.h"
/*
configures column port's DDRx and PORTx.
*/
ColPort_AVR::ColPort_AVR(volatile unsigned char& DDRx, volatile unsigned char& PORTx,
volatile unsigned char& PINx, const uint8_t colPins)
: ColPort(colPins), DDR(DDRx = ~colPins), PORT(PORTx = colPins), PIN(PINx)
{}
/*
Saves all port-pin values to portState.
*/
void ColPort_AVR::read()
{
portState = PIN;
}

41
src/ColPort_AVR.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef COLPORT_AVR_H
#define COLPORT_AVR_H
#include <Arduino.h>
#include <inttypes.h>
#include <ColPort.h>
/* One AVR microcontroller port connected to matrix columns.
Instantiation
------------
The constructor configures column's DDRx and PORTx.
The 'x' in parameters DDRx, PORTx, and PINx should all be the same letter.
colPins is port's bitwise pin configuration
1=configure as input (for pins connected to column)
0=configure as output (for LED or not connected to a column)
Example instantiation on column port B, with pins 2 and 3 connected to columns:
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<2 | 1<<3 );
colPins are read from pin 0 on up.
Diode orientation
----------------
Rows, columns, and diode orientation are explained in Matrix.h
*/
class ColPort_AVR : public ColPort
{
private:
const volatile unsigned char& DDR; //Data Direction Register, Direction: 0=Input
const volatile unsigned char& PORT; //PORT register
const volatile unsigned char& PIN; //PIN read register
public:
//The constructor initialization list is in .cpp
ColPort_AVR(volatile unsigned char& DDRx, volatile unsigned char& PORTx,
volatile unsigned char& PINx, const uint8_t colPins);
//read port and store result in portState
virtual void read();
};
#endif

41
src/ColPort_MCP23018.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "ColPort_MCP23018.h"
/*
configures column port's IODIR, GPIO, and GPPU.
*/
ColPort_MCP23018::ColPort_MCP23018(IOExpanderPort& port, const uint8_t colPins)
: ColPort(colPins), port(port), IODIR(port.num), GPIO(port.num + 0x12), GPPU(port.num + 0x0C)
{}
void ColPort_MCP23018::begin()
{
//Wire.begin() should only be called once https://www.arduino.cc/en/Reference/WireBegin
#ifndef WIRE_BEGIN
#define WIRE_BEGIN
Wire.begin();
#endif
Wire.beginTransmission(port.ADDR);
Wire.write(IODIR);
Wire.write(colPins); //0=configure as output (for LED), 1=configure as input (for read)
Wire.endTransmission();
Wire.beginTransmission(port.ADDR);
Wire.write(GPPU);
Wire.write(colPins); //0=pull-up disabled (for LED), 1=pull-up enabled (for read)
Wire.endTransmission();
}
/*
Saves all port-pin values to portState.
*/
void ColPort_MCP23018::read()
{
Wire.beginTransmission(port.ADDR);
Wire.write(GPIO); //GPIO immediately before requestFrom
Wire.endTransmission();
Wire.requestFrom(port.ADDR, 1u); //request one byte from input port
portState = Wire.read();
}

46
src/ColPort_MCP23018.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef COLPORT_MCP23018_H
#define COLPORT_MCP23018_H
#include <Arduino.h>
#include <inttypes.h>
#include <Wire.h>
#include <ColPort.h>
#include "IOExpanderPort.h"
/* One MCP23018 I/O expander port connected to matrix columns.
Instantiation
------------
colPins parameter is port's bitwise pin configuration
1=configure as input (for read)