initial commit keybrd version 0.3.0
This commit is contained in:
commit
98b6060e7c
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal 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
44
README.md
Normal 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
33
doc/CHANGELOG.md
Normal 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
|
||||
|
37
doc/Teensy2_microcontroller.txt
Normal file
37
doc/Teensy2_microcontroller.txt
Normal 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
11
doc/astyle_cpp
Normal 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
|
189
doc/keybrd_library_developer_guide.md
Normal file
189
doc/keybrd_library_developer_guide.md
Normal 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>
|
220
doc/keybrd_library_user_guide.md
Normal file
220
doc/keybrd_library_user_guide.md
Normal 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
22
doc/planned_features.md
Normal 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
|
112
examples/keybrd_mapping_bb/keybrd_mapping_bb.ino
Normal file
112
examples/keybrd_mapping_bb/keybrd_mapping_bb.ino
Normal 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
9
library.properties
Normal 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
11
src/Code.h
Normal 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
30
src/Code_AutoShift.cpp
Normal 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
38
src/Code_AutoShift.h
Normal 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
68
src/Code_LEDLock.cpp
Normal 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
32
src/Code_LEDLock.h
Normal 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
11
src/Code_LayerHold.cpp
Normal 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
21
src/Code_LayerHold.h
Normal 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
10
src/Code_LayerLock.cpp
Normal 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
21
src/Code_LayerLock.h
Normal 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
|
7
src/Code_LayeredCodeSc.cpp
Normal file
7
src/Code_LayeredCodeSc.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
#include "Code_LayeredCodeSc.h"
|
||||
|
||||
void Code_LayeredCodeSc::press()
|
||||
{
|
||||
layer = refStateLayers.getActiveLayer();
|
||||
pressCode();
|
||||
}
|
23
src/Code_LayeredCodeSc.h
Normal file
23
src/Code_LayeredCodeSc.h
Normal 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
|
25
src/Code_LayeredCodeScBase.cpp
Normal file
25
src/Code_LayeredCodeScBase.cpp
Normal 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();
|
||||
}
|
||||
}
|
27
src/Code_LayeredCodeScBase.h
Normal file
27
src/Code_LayeredCodeScBase.h
Normal 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
7
src/Code_LayeredScSc.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
#include "Code_LayeredScSc.h"
|
||||
|
||||
void Code_LayeredScSc::press()
|
||||
{
|
||||
layer = refStateLayers.getActiveLayer();
|
||||
pressScancode();
|
||||
}
|
22
src/Code_LayeredScSc.h
Normal file
22
src/Code_LayeredScSc.h
Normal 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
|
20
src/Code_LayeredScScBase.cpp
Normal file
20
src/Code_LayeredScScBase.cpp
Normal 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);
|
||||
}
|
26
src/Code_LayeredScScBase.h
Normal file
26
src/Code_LayeredScScBase.h
Normal 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
14
src/Code_Null.h
Normal 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
11
src/Code_Sc.cpp
Normal 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
19
src/Code_Sc.h
Normal 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
13
src/Code_ScNS.cpp
Normal 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
23
src/Code_ScNS.h
Normal 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
20
src/Code_ScS.cpp
Normal 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
22
src/Code_ScS.h
Normal 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
34
src/Code_Shift.cpp
Normal 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
23
src/Code_Shift.h
Normal 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
11
src/ColPort.cpp
Normal 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
23
src/ColPort.h
Normal 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
17
src/ColPort_AVR.cpp
Normal 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
41
src/ColPort_AVR.h
Normal 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
41
src/ColPort_MCP23018.cpp
Normal 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
46
src/ColPort_MCP23018.h
Normal 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)
|
||||