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)
|
||||||
|
0=configure as output (for LED or not connected to a column)
|
||||||
|
|
||||||
|
example instantiation for column port A, with pins 2 and 3 connected to columnss:
|
||||||
|
IOExpanderPort portA(0, ~0);
|
||||||
|
ColPort_MCP23018 colPortA(portA, 1<<2 | 1<<3 );
|
||||||
|
example instantiation for column port B, with pins 2 and 3 connected to columns:
|
||||||
|
IOExpanderPort portB(1, ~0);
|
||||||
|
ColPort_MCP23018 colPortB(portB, 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_MCP23018 : public ColPort
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IOExpanderPort& port;
|
||||||
|
const uint8_t IODIR; //Input/Ouput Direction register
|
||||||
|
const uint8_t GPIO; //General Purpose Input/Ouput register
|
||||||
|
const uint8_t GPPU; //General Purpose Pullup register
|
||||||
|
public:
|
||||||
|
//The constructor initialization list is in .cpp
|
||||||
|
ColPort_MCP23018(IOExpanderPort& port, const uint8_t colPins);
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
//read port and store result in portState
|
||||||
|
virtual void read();
|
||||||
|
};
|
||||||
|
#endif
|
37
src/ColPort_PCA9655E.cpp
Normal file
37
src/ColPort_PCA9655E.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include "ColPort_PCA9655E.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
configures column port's configuration, input, and pins.
|
||||||
|
*/
|
||||||
|
ColPort_PCA9655E::ColPort_PCA9655E
|
||||||
|
(IOExpanderPort& port, const uint8_t colPins)
|
||||||
|
: ColPort(colPins), port(port), configurationByteCommand(port.num + 6), inputByteCommand(port.num)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void ColPort_PCA9655E::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(configurationByteCommand);
|
||||||
|
Wire.write(colPins); //0=configure as output (for LED), 1=configure as input (for read)
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Saves all port-pin values to portState.
|
||||||
|
*/
|
||||||
|
void ColPort_PCA9655E::read()
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(inputByteCommand); //input immediately before requestFrom
|
||||||
|
Wire.endTransmission(false); //PCA9655E needs false to send a restart
|
||||||
|
|
||||||
|
Wire.requestFrom(port.ADDR, 1u); //request one byte from input port
|
||||||
|
|
||||||
|
portState = Wire.read();
|
||||||
|
}
|
44
src/ColPort_PCA9655E.h
Normal file
44
src/ColPort_PCA9655E.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef COLPORT_PCA9655E_H
|
||||||
|
#define COLPORT_PCA9655E_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <ColPort.h>
|
||||||
|
#include "IOExpanderPort.h"
|
||||||
|
|
||||||
|
/* One PCA9655E I/O expander port connected to matrix columns.
|
||||||
|
|
||||||
|
Instantiation
|
||||||
|
------------
|
||||||
|
colPins parameter 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 for column port 0, with pins 2 and 3 connected to columns:
|
||||||
|
IOExpanderPort port0(0, 0);
|
||||||
|
ColPort_PCA9655E colPort0(port0, 2<<0 | 1<<3 );
|
||||||
|
Example instantiation for column port 1, with pins 2 and 3 connected to columns:
|
||||||
|
IOExpanderPort port1(1, 0);
|
||||||
|
ColPort_PCA9655E colPort1(port1, 2<<0 | 1<<3 );
|
||||||
|
|
||||||
|
colPins are read from pin 0 on up.
|
||||||
|
|
||||||
|
Diode orientation
|
||||||
|
----------------
|
||||||
|
Rows, columns, and diode orientation are explained in Matrix.h
|
||||||
|
*/
|
||||||
|
class ColPort_PCA9655E : public ColPort
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IOExpanderPort& port;
|
||||||
|
const uint8_t configurationByteCommand;
|
||||||
|
const uint8_t inputByteCommand;
|
||||||
|
public:
|
||||||
|
//The constructor initialization list is in .cpp
|
||||||
|
ColPort_PCA9655E(IOExpanderPort& port, const uint8_t colPins);
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
//read port and store result in portState
|
||||||
|
virtual void read();
|
||||||
|
};
|
||||||
|
#endif
|
32
src/Debug.cpp
Normal file
32
src/Debug.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "Debug.h"
|
||||||
|
#include "getFreeSRAM.h"
|
||||||
|
|
||||||
|
void Debug::print_free_RAM()
|
||||||
|
{
|
||||||
|
delay(1000); //give OS time to find USB
|
||||||
|
Keyboard.print(F("Free SRAM = "));
|
||||||
|
Keyboard.println( getFreeSRAM() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug::print_microseconds_per_scan()
|
||||||
|
{
|
||||||
|
if (millis() >= nextTime)
|
||||||
|
{
|
||||||
|
Keyboard.print(1000000/scanCount); //print microseconds per scan
|
||||||
|
Keyboard.write(',');
|
||||||
|
scanCount = 0;
|
||||||
|
nextTime = millis() + 1000; //print every second
|
||||||
|
}
|
||||||
|
scanCount++;
|
||||||
|
}
|
||||||
|
void Debug::print_scans_per_second()
|
||||||
|
{
|
||||||
|
if (millis() >= nextTime)
|
||||||
|
{
|
||||||
|
Keyboard.print(scanCount); //print scans per second
|
||||||
|
Keyboard.write(',');
|
||||||
|
scanCount = 0;
|
||||||
|
nextTime = millis() + 1000; //print every second
|
||||||
|
}
|
||||||
|
scanCount++;
|
||||||
|
}
|
16
src/Debug.h
Normal file
16
src/Debug.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef DEBUG_H
|
||||||
|
#define DEBUG_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class Debug
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
unsigned long nextTime = 0;
|
||||||
|
unsigned int scanCount = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void print_free_RAM(); //print free SRAM, call this from setup()
|
||||||
|
void print_microseconds_per_scan(); //print microseconds per scan every second
|
||||||
|
void print_scans_per_second(); //print scans per second every second
|
||||||
|
};
|
||||||
|
#endif
|
49
src/IOExpanderPort.h
Normal file
49
src/IOExpanderPort.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef IOEXPANDERPORT_H
|
||||||
|
#define IOEXPANDERPORT_H
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/* The pins of an IC's port can be split between RowPort, ColPort, and LED.
|
||||||
|
|
||||||
|
IOExpanderPort contains outputVal, the value of a port's output register.
|
||||||
|
outputVal is used for port manipulation by classes RowPort and LED.
|
||||||
|
One port's outputVal can be shared by one RowPort object and multiple LED objects.
|
||||||
|
|
||||||
|
IOExpanderPort is only used by I/O expander port classes.
|
||||||
|
AVR port classes do not need a similar class because PORTx is global in the Arduino library.
|
||||||
|
|
||||||
|
Instantiation
|
||||||
|
------------
|
||||||
|
Example IOExpanderPort::ADDR initilization:
|
||||||
|
const uint8_t IOExpanderPort::ADDR = 0x18;
|
||||||
|
Be careful with the ADDR.
|
||||||
|
Table 6 in PCA9655E datasheet lists 8-bit versions of I2C addresses.
|
||||||
|
The Arduino Wire library uses 7-bit addresses throughout, so drop the low bit.
|
||||||
|
For example, I2C address with AD2=GND AD1=SCL AD0=SCL,
|
||||||
|
Table 6 lists 8-bit ADDR = 0x30 (b 00110000)
|
||||||
|
while Arduino uses 7-bit ADDR = 0x18 (b 00011000)
|
||||||
|
http://playground.arduino.cc/Main/WireLibraryDetailedReference
|
||||||
|
The PCA9655E data sheet is on http://www.onsemi.com/pub_link/Collateral/PCA9655E-D.PDF
|
||||||
|
|
||||||
|
portNumber: If the I/O expander uses port letters, use 0 instead of A, use 1 instead of B.
|
||||||
|
outputVal: For pins that are connected to active low rows, set outputVal bit to 1.
|
||||||
|
Set all other outputVal bits to 0.
|
||||||
|
|
||||||
|
Example instantiation for port0 with active low rows on all pins:
|
||||||
|
IOExpanderPort port0(0, ~0);
|
||||||
|
|
||||||
|
Example instantiation for portA with active low rows on pins 0,1,2:
|
||||||
|
IOExpanderPort portA(0, 1<<0 | 1<<1 | 1<<2 );
|
||||||
|
|
||||||
|
Example instantiation for portB with active high rows on pins 0,1,2:
|
||||||
|
IOExpanderPort portB(1, 0);
|
||||||
|
*/
|
||||||
|
struct IOExpanderPort
|
||||||
|
{
|
||||||
|
static const uint8_t ADDR; //I2C address
|
||||||
|
const uint8_t num; //port number
|
||||||
|
uint8_t outputVal; //bitwise value of output register
|
||||||
|
|
||||||
|
IOExpanderPort(const uint8_t portNumber, uint8_t outputVal)
|
||||||
|
: num(portNumber), outputVal(outputVal) {}
|
||||||
|
};
|
||||||
|
#endif
|
18
src/Key.h
Normal file
18
src/Key.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//header-guard name needs to be uinique
|
||||||
|
//arduino\hardware\teensy\avr\cores\teensy\keylayouts.h has #define KEY_H
|
||||||
|
//Keypad library has a header file Key.h
|
||||||
|
#ifndef KEY_H_keybrd
|
||||||
|
#define KEY_H_keybrd
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/* Key is an interface class
|
||||||
|
*/
|
||||||
|
class Key
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void press()=0; //send scancode to USB for press
|
||||||
|
virtual void release()=0; //send scancode to USB for release
|
||||||
|
};
|
||||||
|
#endif
|
13
src/Key_LayeredKeysArray.cpp
Normal file
13
src/Key_LayeredKeysArray.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "Key_LayeredKeysArray.h"
|
||||||
|
|
||||||
|
void Key_LayeredKeysArray::press()
|
||||||
|
{
|
||||||
|
layer = refStateLayers.getActiveLayer();
|
||||||
|
|
||||||
|
ptrsKeys[layer]->press();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Key_LayeredKeysArray::release()
|
||||||
|
{
|
||||||
|
ptrsKeys[layer]->release();
|
||||||
|
}
|
25
src/Key_LayeredKeysArray.h
Normal file
25
src/Key_LayeredKeysArray.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef KEY_LAYEREDKEYSARRAY_H
|
||||||
|
#define KEY_LAYEREDKEYSARRAY_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <StateLayersInterface.h>
|
||||||
|
#include <Key.h>
|
||||||
|
|
||||||
|
/* Class Key_LayeredKeysArray contains an array of Key pointers, one pointer per layer.
|
||||||
|
Codes are a kind of Key, so the Key pointers can point to Codes as well.
|
||||||
|
|
||||||
|
When the key is pressed, active layer is retreived from refStateLayers and
|
||||||
|
the Key object of the active layer is called.
|
||||||
|
*/
|
||||||
|
class Key_LayeredKeysArray : public Key
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Key *const *const ptrsKeys; //array of Key pointers, one Key per layer
|
||||||
|
uint8_t layer; //active layer when key was pressed
|
||||||
|
static StateLayersInterface& refStateLayers;
|
||||||
|
public:
|
||||||
|
Key_LayeredKeysArray(Key *const ptrsKeys[]): ptrsKeys(ptrsKeys) {}
|
||||||
|
virtual void press();
|
||||||
|
virtual void release();
|
||||||
|
};
|
||||||
|
#endif
|
21
src/LED.h
Normal file
21
src/LED.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef LED_H
|
||||||
|
#define LED_H
|
||||||
|
|
||||||
|
/* LED is an abstract base class
|
||||||
|
Each LED object is an IC pin that is used to power an LED on and off.
|
||||||
|
|
||||||
|
Connect the LED in series with the resistor:
|
||||||
|
Calculate current-limiting-resistor value (100 Ohms to 10k Ohms will work with 5 volts)
|
||||||
|
R = (Vs - Vf) / If
|
||||||
|
http://www.digikey.com/en/resources/conversion-calculators/conversion-calculator-led-series-resistor
|
||||||
|
Connect the LED's anode (the longer lead) to the AVR output pin (+)
|
||||||
|
Connect the LED's cathode to ground (-)
|
||||||
|
Never connect a LED directly from ground to power. Doing so would destroy the LED.
|
||||||
|
*/
|
||||||
|
class LED
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on()=0;
|
||||||
|
virtual void off()=0;
|
||||||
|
};
|
||||||
|
#endif
|
11
src/LED_AVR.cpp
Normal file
11
src/LED_AVR.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "LED_AVR.h"
|
||||||
|
|
||||||
|
void LED_AVR::on()
|
||||||
|
{
|
||||||
|
PORT |= pin; //set pin high
|
||||||
|
}
|
||||||
|
|
||||||
|
void LED_AVR::off()
|
||||||
|
{
|
||||||
|
PORT &= ~pin; //set pin low
|
||||||
|
}
|
23
src/LED_AVR.h
Normal file
23
src/LED_AVR.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef LED_AVR_H
|
||||||
|
#define LED_AVR_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <LED.h>
|
||||||
|
|
||||||
|
/* A LED_AVR object is an AVR pin that is used to power an LED on and off.
|
||||||
|
DDRx Data Direction Register is configured as output in RowPort_AVR constructor.
|
||||||
|
*/
|
||||||
|
class LED_AVR: public LED
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
volatile unsigned char& PORT;
|
||||||
|
const uint8_t pin; //bitwise pin to LED
|
||||||
|
|
||||||
|
public:
|
||||||
|
LED_AVR(volatile unsigned char& PORTx, const uint8_t pin): PORT(PORTx), pin(pin) {}
|
||||||
|
|
||||||
|
virtual void on();
|
||||||
|
|
||||||
|
virtual void off();
|
||||||
|
};
|
||||||
|
#endif
|
17
src/LED_MCP23018.cpp
Normal file
17
src/LED_MCP23018.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "LED_MCP23018.h"
|
||||||
|
|
||||||
|
void LED_MCP23018::on()
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(GPIO);
|
||||||
|
Wire.write(port.outputVal &= ~pin); //set pin low (sink)
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LED_MCP23018::off()
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(GPIO);
|
||||||
|
Wire.write(port.outputVal |= pin); //set pin high (sink off)
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
35
src/LED_MCP23018.h
Normal file
35
src/LED_MCP23018.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef LED_MCP23018_H
|
||||||
|
#define LED_MCP23018_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <LED.h>
|
||||||
|
#include "IOExpanderPort.h"
|
||||||
|
|
||||||
|
/* Class LED_MCP23018 uses a MCP23018 I/O expander pin to turn a LED on and off.
|
||||||
|
|
||||||
|
Connect the LED in series with the resistor:
|
||||||
|
determin resistor value needed (Internet search: LED resistor value)
|
||||||
|
Connect the LED's (-) ground to the AVR output pin
|
||||||
|
connect LED's (+) to power
|
||||||
|
Never connect a LED directly from ground to power. Doing so would destroy the LED.
|
||||||
|
|
||||||
|
MCP23018 ouput is open drain. The output acts like a switch to ground.
|
||||||
|
It cannot produce a high signal by itself.
|
||||||
|
*/
|
||||||
|
class LED_MCP23018: public LED
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IOExpanderPort& port;
|
||||||
|
const uint8_t GPIO; //General Purpose Input/Ouput register address
|
||||||
|
const uint8_t pin; //bitwise pin to LED
|
||||||
|
|
||||||
|
public:
|
||||||
|
LED_MCP23018(IOExpanderPort& port, const uint8_t pin)
|
||||||
|
: port(port), GPIO(port.num + 0x12), pin(pin) {}
|
||||||
|
|
||||||
|
virtual void on();
|
||||||
|
|
||||||
|
virtual void off();
|
||||||
|
};
|
||||||
|
#endif
|
17
src/LED_PCA9655E.cpp
Normal file
17
src/LED_PCA9655E.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "LED_PCA9655E.h"
|
||||||
|
|
||||||
|
void LED_PCA9655E::on()
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(outputByteCommand);
|
||||||
|
Wire.write(port.outputVal |= pin); //set pin high
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LED_PCA9655E::off()
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(outputByteCommand);
|
||||||
|
Wire.write(port.outputVal &= ~pin); //set pin low
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
26
src/LED_PCA9655E.h
Normal file
26
src/LED_PCA9655E.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef LED_PCA9655E_H
|
||||||
|
#define LED_PCA9655E_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <LED.h>
|
||||||
|
#include "IOExpanderPort.h"
|
||||||
|
|
||||||
|
/* A LED_PCA9655E object is an PCA9655E pin that is connected to an LED indicator light.
|
||||||
|
Input/Ouput Direction configuration is set to ouput in row_Port_PCA9655E.begin() and col_Port_PCA9655E.begin().
|
||||||
|
*/
|
||||||
|
class LED_PCA9655E: public LED
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IOExpanderPort& port;
|
||||||
|
const uint8_t outputByteCommand; //General Purpose Input/Ouput register address
|
||||||
|
const uint8_t pin; //bitwise pin to LED
|
||||||
|
|
||||||
|
public:
|
||||||
|
LED_PCA9655E(IOExpanderPort& port, const uint8_t pin)
|
||||||
|
: port(port), outputByteCommand(port.num + 2), pin(pin) {}
|
||||||
|
|
||||||
|
virtual void on();
|
||||||
|
virtual void off();
|
||||||
|
};
|
||||||
|
#endif
|
12
src/Matrix.cpp
Normal file
12
src/Matrix.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "Matrix.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
scan every row of matrix one time
|
||||||
|
*/
|
||||||
|
void Matrix::scan()
|
||||||
|
{
|
||||||
|
for (uint8_t i=0; i < rowCount; i++)
|
||||||
|
{
|
||||||
|
ptrsRows[i]->process(activeHigh);
|
||||||
|
}
|
||||||
|
}
|
41
src/Matrix.h
Normal file
41
src/Matrix.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef MATRIX_H
|
||||||
|
#define MATRIX_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "Row.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Diode orientation
|
||||||
|
----------------
|
||||||
|
A keyboard's physically matrix is composed of rows and columns.
|
||||||
|
The rows and columns are physically connected to the keys.
|
||||||
|
The rows and columns are distinguishable by diode orientation (not horizontal/vertical).
|
||||||
|
|
||||||
|
For active low diode orientation is:
|
||||||
|
cathodes on rows
|
||||||
|
anodes on columns
|
||||||
|
|
||||||
|
For active high diode orientation is reversed:
|
||||||
|
anodes on rows
|
||||||
|
cathodes on columns
|
||||||
|
|
||||||
|
Pull-down resistors
|
||||||
|
-------------------
|
||||||
|
If Matrix uses active low, IC requires one pull-up resistor on each ColPort::colPins.
|
||||||
|
If Matrix uses active high, IC requires one pull-down resistor on each ColPort::colPins.
|
||||||
|
External pull-down resistors should have a value between 10k Ohms and 2.2k Ohms.
|
||||||
|
*/
|
||||||
|
class Matrix
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Row *const *const ptrsRows; //array of row pointers
|
||||||
|
const uint8_t rowCount;
|
||||||
|
const bool activeHigh; //logic level of strobe pin: 0=activeLow, 1=activeHigh
|
||||||
|
|
||||||
|
public:
|
||||||
|
Matrix( Row *const ptrsRows[], const uint8_t rowCount, const bool activeHigh)
|
||||||
|
: ptrsRows(ptrsRows), rowCount(rowCount), activeHigh(activeHigh) {}
|
||||||
|
|
||||||
|
void scan();
|
||||||
|
};
|
||||||
|
#endif
|
204
src/Row.cpp
Normal file
204
src/Row.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include "Row.h"
|
||||||
|
/*
|
||||||
|
scans the row and calls any newly pressed or released keys.
|
||||||
|
*/
|
||||||
|
void Row::process(const bool activeHigh)
|
||||||
|
{
|
||||||
|
//these variables are all bitwise, one bit per key
|
||||||
|
uint8_t rowState; //1 means pressed, 0 means released
|
||||||
|
uint16_t rowEnd; //1 bit marks positioned after last key of row
|
||||||
|
uint8_t newDebounced; //1 means pressed, 0 means released
|
||||||
|
uint8_t isFallingEdge; //1 means falling edge
|
||||||
|
uint8_t isRisingEdge; //1 means rising edge
|
||||||
|
|
||||||
|
scan(activeHigh); //save column-port-pin values to portState
|
||||||
|
rowState = getRowState(rowEnd, activeHigh);
|
||||||
|
newDebounced = debounce(rowState);
|
||||||
|
detectEdge(newDebounced, isFallingEdge, isRisingEdge);
|
||||||
|
pressRelease(rowEnd, isFallingEdge, isRisingEdge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Strobes the row and reads the columns.
|
||||||
|
Strobe is on for shortest possible time to preserve IR LED on DodoHand's optic switch.
|
||||||
|
*/
|
||||||
|
void Row::scan(const bool activeHigh)
|
||||||
|
{
|
||||||
|
//strobe row on
|
||||||
|
if (activeHigh)
|
||||||
|
{
|
||||||
|
refRowPort.setActivePinHigh(rowPin);
|
||||||
|
}
|
||||||
|
else //activeLow
|
||||||
|
{
|
||||||
|
refRowPort.setActivePinLow(rowPin);
|
||||||
|
}
|
||||||
|
|
||||||
|
//read all the column ports
|
||||||
|
for (uint8_t i=0; i < colPortCount; i++)
|
||||||
|
{
|
||||||
|
ptrsColPorts[i]->read();
|
||||||
|
}
|
||||||
|
|
||||||
|
//strobe row off
|
||||||
|
if (activeHigh)
|
||||||
|
{
|
||||||
|
refRowPort.setActivePinLow(rowPin);
|
||||||
|
}
|
||||||
|
else //activeLow
|
||||||
|
{
|
||||||
|
refRowPort.setActivePinHigh(rowPin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copies column pins to rowState. Unused column pins are not copied.
|
||||||
|
Sets rowEnd and returns rowState.
|
||||||
|
rowEnd is bitwise, where 1 bit corrsiponds to place immediatly after last key of row.
|
||||||
|
rowEnd and rowMask are larger type than portMask so that they can not overflow.
|
||||||
|
*/
|
||||||
|
uint8_t Row::getRowState(uint16_t& rowEnd, const bool activeHigh)
|
||||||
|
{
|
||||||
|
uint16_t rowMask = 1; //bitwise, one col per bit, active col bit is 1
|
||||||
|
uint8_t rowState = 0; //bitwise, one key per bit, 1 means key is pressed
|
||||||
|
|
||||||
|
for (uint8_t i=0; i < colPortCount; i++) //for each col port
|
||||||
|
{
|
||||||
|
//bitwise colPins, 1 means pin is connected to column
|
||||||
|
uint8_t colPins = ptrsColPorts[i]->getColPins();
|
||||||
|
|
||||||
|
//bitwise colPortState, pin values where set in ColPort::read(), get them now
|
||||||
|
uint8_t colPortState = ptrsColPorts[i]->getPortState();
|
||||||
|
|
||||||
|
if (activeHigh)
|
||||||
|
{
|
||||||
|
colPortState = ~colPortState;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( uint8_t portMask = 1; portMask > 0; portMask <<= 1 ) //shift portMask until overflow
|
||||||
|
{ //for each pin of col port
|
||||||
|
if (portMask & colPins) //if pin is connected to column
|
||||||
|
{
|
||||||
|
if (portMask & ~colPortState) //if pin detected a key press
|
||||||
|
{
|
||||||
|
rowState |= rowMask; //set rowState bit for that key
|
||||||
|
}
|
||||||
|
|
||||||
|
rowMask <<= 1; //shift rowMask to next key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rowEnd = rowMask;
|
||||||
|
|
||||||
|
return rowState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parameter rowState is bitwise, 1 means pressed, 0 means released.
|
||||||
|
Returns debounced rowState.
|
||||||
|
|
||||||
|
Debounce uses multiple samples to debounces switch states,
|
||||||
|
where each sample contains the switch states for a row of keys, one bit per switch.
|
||||||
|
|
||||||
|
Debounce uses Marty's debounce algorithm from
|
||||||
|
http://drmarty.blogspot.com.br/2009/05/best-switch-debounce-routine-ever.html
|
||||||
|
I2C and TWI protocals do not include any Packet Error Checking (PEC).
|
||||||
|
The goal of Marty's debounce routine is to reject spurious signals,
|
||||||
|
which is useful when conecting split keyboards with a cable using I2C or TWI.
|
||||||
|
Was tested on split keyboard with 3-meter long telephone wire to I/O expander
|
||||||
|
|
||||||
|
Marty's debounce algorithm:
|
||||||
|
Periodically read samples and update the state when a number consecutive sample bits are equal.
|
||||||
|
|
||||||
|
samples[SAMPLE_COUNT] is a ring buffer and samplesIndex is it's current write index.
|
||||||
|
SAMPLE_COUNT is #defined in Row.h
|
||||||
|
SAMPLE_COUNT is the number of consecutive equal samples needed to debounce.
|
||||||
|
It is a macro because it is used to define array size of samples[SAMPLE_COUNT] in Row.
|
||||||
|
SAMPLE_COUNT should be at lease 2.
|
||||||
|
|
||||||
|
Multiple samples are for error correction on I2C I/O expander and shorten response time.
|
||||||
|
On keyboards without I/O expander, multiple samples only shorten response time.
|
||||||
|
Larger SAMPLE_COUNTs are more reliable but consume more memory, where
|
||||||
|
SAMPLE_COUNT*ROW_COUNT = bytes of memory consumed by keyboard
|
||||||
|
So don't make SAMPLE_COUNT too large, SAMPLE_COUNT = 4 is very reliable for I2C error correction.
|
||||||
|
|
||||||
|
big SAMPLE_COUNT for fast response, small SAMPLE_COUNT to save memory
|
||||||
|
there is a way to define SAMPLE_COUNT in the sketch, but it's ugly
|
||||||
|
see http://forum.arduino.cc/index.php?topic=364843.0 > rely #2
|
||||||
|
*/
|
||||||
|
uint8_t Row::debounce(const uint8_t rowState)
|
||||||
|
{
|
||||||
|
uint8_t all_1 = ~0; //bitwise
|
||||||
|
uint8_t all_0 = 0; //bitwise
|
||||||
|
|
||||||
|
delayMicroseconds(DELAY_MICROSECONDS); //delay between Row scans to debounce key
|
||||||
|
|
||||||
|
samples[samplesIndex] = rowState; //insert rowState into samples[] ring buffer
|
||||||
|
|
||||||
|
if (++samplesIndex >= SAMPLE_COUNT)
|
||||||
|
{
|
||||||
|
samplesIndex = 0; //wrap samplesIndex to beginning of ring buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t j = 0; j < SAMPLE_COUNT; j++) //traverse the sample[] ring buffer
|
||||||
|
{
|
||||||
|
all_1 &= samples[j]; //1 if all samples are 1
|
||||||
|
all_0 |= samples[j]; //0 if all samples are 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// update newDebounce if all the samples agree with one another
|
||||||
|
// if all samples=1 then newDebounced=1
|
||||||
|
// elseif all samples=0 then newDebounced=0
|
||||||
|
// else newDebounced=debounced i.e. no change
|
||||||
|
return all_1 | (all_0 & debounced);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Computes isFallingEdge and isRisingEdge.
|
||||||
|
All 3 parameters are bitwise.
|
||||||
|
*/
|
||||||
|
void Row::detectEdge(uint8_t newDebounced, uint8_t& isFallingEdge, uint8_t& isRisingEdge)
|
||||||
|
{
|
||||||
|
uint8_t debouncedChanged; //bitwise
|
||||||
|
|
||||||
|
debouncedChanged = newDebounced xor debounced;
|
||||||
|
debounced = newDebounced;
|
||||||
|
|
||||||
|
//bit=1 if last debounced changed from 1 to 0, else bit=0
|
||||||
|
isFallingEdge = debouncedChanged & ~debounced;
|
||||||
|
|
||||||
|
//bit=1 if last debounced changed from 0 to 1, else bit=0
|
||||||
|
isRisingEdge = debouncedChanged & debounced;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
calls key's press() or release() function if it was pressed or released.
|
||||||
|
All 3 parameters are bitwise.
|
||||||
|
*/
|
||||||
|
void Row::pressRelease(const uint16_t rowEnd, const uint8_t isFallingEdge,
|
||||||
|
const uint8_t isRisingEdge)
|
||||||
|
{
|
||||||
|
uint8_t rowMask; //bitwise, active col bit is 1
|
||||||
|
uint8_t col; //index for ptrsKeys[col] array
|
||||||
|
|
||||||
|
for (rowMask=1, col=0; rowMask<rowEnd; rowMask<<=1, col++) //for each key in row
|
||||||
|
{
|
||||||
|
//release before press avoids impossible key sequence
|
||||||
|
if (rowMask & isFallingEdge) //if key was released
|
||||||
|
{
|
||||||
|
ptrsKeys[col]->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowMask & isRisingEdge) //if key was pressed
|
||||||
|
{
|
||||||
|
ptrsKeys[col]->press();
|
||||||
|
keyWasPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Row::keyWasPressed()
|
||||||
|
{
|
||||||
|
//empty in Row class. To unstick sticky keys, override keyWasPressed() in derived class.
|
||||||
|
}
|
81
src/Row.h
Normal file
81
src/Row.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#ifndef ROW_H
|
||||||
|
#define ROW_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Key.h>
|
||||||
|
#include <RowPort.h>
|
||||||
|
#include <ColPort.h>
|
||||||
|
|
||||||
|
#define SAMPLE_COUNT 4 //number of consecutive equal bits needed to change a debounced bit
|
||||||
|
|
||||||
|
/*
|
||||||
|
Instantiation
|
||||||
|
------------
|
||||||
|
Example instantiation of a row:
|
||||||
|
RowPort_AVR rowPortF(DDRF, PORTF);
|
||||||
|
|
||||||
|
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_PORTS_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts);
|
||||||
|
|
||||||
|
const PROGMEM 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_PORTS_COUNT);
|
||||||
|
|
||||||
|
Number of ColPort::colPins should equal number of keys in Row::ptrsKeys array
|
||||||
|
if a pin is missing, a key will be unresposive
|
||||||
|
if a Key pointer is missing, the keyboard will fail in an unprdictable way
|
||||||
|
|
||||||
|
A keyboard with a faster scan rate is more resposive.
|
||||||
|
Follow these step to tune DELAY_MICROSECONDS for maximum scan rate within debounce times:
|
||||||
|
Initialize DELAY_MICROSECONDS in your sketch:
|
||||||
|
const unsigned int Row::DELAY_MICROSECONDS = 1000;
|
||||||
|
Add this to the sketche's loop() function:
|
||||||
|
keybrd.print_microseconds_per_scan();
|
||||||
|
Compile and load the sketch into the microcontroller, which will print the actual microseconds_per_scan
|
||||||
|
Incrementaly adjust the DELAY_MICROSECONDS untill the printed microseconds_per_scan is near the switches bounce time
|
||||||
|
A switche's debounce time can be obtained from the switche's datasheet
|
||||||
|
Cherry MX has 5ms bounce time http://www.cherrycorp.com/english/switches/key/mx.htm
|
||||||
|
hasu measured Cherry MX bounce times .3ms to 1.4ms http://geekhack.org/index.php?topic=42385.0
|
||||||
|
Tactile switch MJTP series bounce 10 ms http://www.apem.com/files/apem/brochures/MJTP_6MM.pdf
|
||||||
|
Optic switches 0 bounce time because optic doesn't bounce
|
||||||
|
|
||||||
|
Slow-scan trick for debug message that print too fast
|
||||||
|
Keyboard.print(F("debug message"));
|
||||||
|
Change DELAY_MICROSECONDS to a large number like 10000
|
||||||
|
That way printing debug messages is slowed to a managable rate
|
||||||
|
*/
|
||||||
|
class Row
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Key *const *const ptrsKeys; //array of Key pointers
|
||||||
|
|
||||||
|
RowPort &refRowPort; //this row's IC port
|
||||||
|
const uint8_t rowPin; //bitwise, 1 indicates IC pin connected to this row
|
||||||
|
|
||||||
|
ColPort *const *const ptrsColPorts; //array of column ports
|
||||||
|
const uint8_t colPortCount;
|
||||||
|
|
||||||
|
static const unsigned int DELAY_MICROSECONDS; //delay between each Row scan for debouncing
|
||||||
|
uint8_t samples[SAMPLE_COUNT]; //bitwise, one bit per key, most resent readings
|
||||||
|
uint8_t samplesIndex; //samples[] current write index
|
||||||
|
uint8_t debounced; //bitwise, one bit per key, debounced value of readings
|
||||||
|
virtual void keyWasPressed();
|
||||||
|
public:
|
||||||
|
Row( RowPort &refRowPort, const uint8_t rowPin,
|
||||||
|
ColPort *const ptrsColPorts[], const uint8_t colPortCount,
|
||||||
|
Key *const ptrsKeys[])
|
||||||
|
: ptrsKeys(ptrsKeys), refRowPort(refRowPort), rowPin(rowPin),
|
||||||
|
ptrsColPorts(ptrsColPorts), colPortCount(colPortCount),
|
||||||
|
samplesIndex(0), debounced(0) { }
|
||||||
|
|
||||||
|
Key* getPtrKey(uint8_t col) const;
|
||||||
|
void process(const bool activeHigh);
|
||||||
|
void scan(const bool activeHigh);
|
||||||
|
uint8_t getRowState(uint16_t& rowEnd, const bool activeHigh);
|
||||||
|
uint8_t debounce(const uint8_t rowState); //switch debouncer and I2C error correction
|
||||||
|
void detectEdge(uint8_t newDebounced, uint8_t& isFallingEdge, uint8_t& isRisingEdge);
|
||||||
|
void pressRelease(const uint16_t rowEnd, const uint8_t isFallingEdge, const uint8_t isRisingEdge);
|
||||||
|
};
|
||||||
|
#endif
|
16
src/RowPort.h
Normal file
16
src/RowPort.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef ROWPORT_H
|
||||||
|
#define ROWPORT_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
RowPort is an abstract base class.
|
||||||
|
Port classes are the keybrd library's interface to microcontoller ports or I/O expander ports.
|
||||||
|
*/
|
||||||
|
class RowPort
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void setActivePinHigh(const uint8_t activePin)=0;
|
||||||
|
virtual void setActivePinLow(const uint8_t activePin)=0;
|
||||||
|
};
|
||||||
|
#endif
|
34
src/RowPort_AVR_Optic.cpp
Normal file
34
src/RowPort_AVR_Optic.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "RowPort_AVR_Optic.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
configures row port's DDRx and PORTx pins as output.
|
||||||
|
*/
|
||||||
|
RowPort_AVR_Optic::RowPort_AVR_Optic
|
||||||
|
(volatile unsigned char& DDRx, volatile unsigned char& PORTx)
|
||||||
|
: DDR(DDRx = ~0), PORT(PORTx)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/*
|
||||||
|
activePin is a port mask, where active pin is 1.
|
||||||
|
*/
|
||||||
|
void RowPort_AVR_Optic::setActivePinLow(const uint8_t activePin)
|
||||||
|
{
|
||||||
|
PORT &= ~activePin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
activePin is port mask, where active pin is 1.
|
||||||
|
|
||||||
|
The delayMicroseconds() is for DodoHand keyboard's optic switches.
|
||||||
|
Strobe needs to be turned on for >= 300µs before the columns are read.
|
||||||
|
During this time the state of the columns are settling into their actual values.
|
||||||
|
Seems to be necessary in order to allow the phototransistors to turn completely off.
|
||||||
|
(delay is not need for I/O expander because time between I2C Transmissions)
|
||||||
|
(Teensy2 ATMEGA32U4 16 MHz is a 0.0625 µs period)
|
||||||
|
*/
|
||||||
|
void RowPort_AVR_Optic::setActivePinHigh(const uint8_t activePin)
|
||||||
|
{
|
||||||
|
//strobe row on
|
||||||
|
PORT |= activePin;
|
||||||
|
delayMicroseconds(300); //wait for the column value to stabilize after strobe
|
||||||
|
}
|
40
src/RowPort_AVR_Optic.h
Normal file
40
src/RowPort_AVR_Optic.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef ROWPORT_AVR_OPTIC_H
|
||||||
|
#define ROWPORT_AVR_OPTIC_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <RowPort.h>
|
||||||
|
|
||||||
|
/* One AVR microcontroller port connected to matrix rows.
|
||||||
|
|
||||||
|
setActivePinHigh() has a delay to allow phototransistors time to sense strobe
|
||||||
|
(DodoHand has optic switches with phototransistors).
|
||||||
|
|
||||||
|
Instantiation
|
||||||
|
------------
|
||||||
|
The constructor configures all pins of port as output (for strobe pins and LED).
|
||||||
|
The 'x' in parameters DDRx, PORTx, and PINx should all be the same letter.
|
||||||
|
|
||||||
|
Example instantiation for row port F:
|
||||||
|
RowPort_AVR_Optic rowPortF(DDRF, PORTF);
|
||||||
|
|
||||||
|
Diode orientation
|
||||||
|
----------------
|
||||||
|
Rows, columns, and diode orientation are explained in Matrix.h
|
||||||
|
|
||||||
|
*/
|
||||||
|
class RowPort_AVR_Optic : public RowPort
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const volatile unsigned char& DDR; //Data Direction Register
|
||||||
|
|
||||||
|
protected:
|
||||||
|
volatile unsigned char& PORT; //PORT register
|
||||||
|
|
||||||
|
public:
|
||||||
|
//The constructor initialization list is in .cpp
|
||||||
|
RowPort_AVR_Optic(volatile unsigned char& DDRx, volatile unsigned char& PORTx);
|
||||||
|
|
||||||
|
virtual void setActivePinLow(const uint8_t activePin); //activePin is a port mask
|
||||||
|
virtual void setActivePinHigh(const uint8_t activePin);
|
||||||
|
};
|
||||||
|
#endif
|
46
src/RowPort_MCP23018.cpp
Normal file
46
src/RowPort_MCP23018.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include "RowPort_MCP23018.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
configures column port's IODIR, GPIO.
|
||||||
|
*/
|
||||||
|
RowPort_MCP23018::RowPort_MCP23018(IOExpanderPort& port)
|
||||||
|
: port(port), IODIR(port.num), GPIO(port.num + 0x12)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void RowPort_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(0); //0=configure as output (for strobe pins and LED pins)
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sets activePin pin output to low.
|
||||||
|
activePin is port mask, where active-low pin is 1.
|
||||||
|
*/
|
||||||
|
void RowPort_MCP23018::setActivePinLow(const uint8_t activePin)
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(GPIO);
|
||||||
|
Wire.write(port.outputVal &= ~activePin);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sets activePin pin output to high, does not reset the other pins because they might be used by LEDs.
|
||||||
|
activePin is port mask, where active-high pin is 1.
|
||||||
|
*/
|
||||||
|
void RowPort_MCP23018::setActivePinHigh(const uint8_t activePin)
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(GPIO);
|
||||||
|
Wire.write(port.outputVal |= activePin);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
46
src/RowPort_MCP23018.h
Normal file
46
src/RowPort_MCP23018.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#ifndef ROWPORT_MCP23018_H
|
||||||
|
#define ROWPORT_MCP23018_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <RowPort.h>
|
||||||
|
#include "IOExpanderPort.h"
|
||||||
|
|
||||||
|
/* One MCP23018 I/O expander port connected to matrix rows.
|
||||||
|
|
||||||
|
begin() configures column port's IODIR, GPIO.
|
||||||
|
This should normally be called only once.
|
||||||
|
|
||||||
|
Instantiation
|
||||||
|
------------
|
||||||
|
Example instantiation for row port A:
|
||||||
|
IOExpanderPort portA(0, ~0);
|
||||||
|
RowPort_MCP23018 rowPortA(portA);
|
||||||
|
Example instantiation for row port B:
|
||||||
|
IOExpanderPort portB(1, ~0);
|
||||||
|
RowPort_MCP23018 rowPortB(portB);
|
||||||
|
|
||||||
|
Diode orientation
|
||||||
|
----------------
|
||||||
|
Rows, columns, and diode orientation are explained in Matrix.h
|
||||||
|
|
||||||
|
MCP23018 data sheet
|
||||||
|
------------------
|
||||||
|
http://ww1.microchip.com/downloads/en/DeviceDoc/22103a.pdf
|
||||||
|
*/
|
||||||
|
class RowPort_MCP23018 : public RowPort
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IOExpanderPort& port;
|
||||||
|
const uint8_t IODIR; //Input/Ouput Direction register address
|
||||||
|
const uint8_t GPIO; //General Purpose Input/Ouput register address
|
||||||
|
|
||||||
|
public:
|
||||||
|
//The constructor initialization list is in .cpp
|
||||||
|
RowPort_MCP23018(IOExpanderPort& port);
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
virtual void setActivePinLow(const uint8_t activePin); //activePin is a port mask
|
||||||
|
virtual void setActivePinHigh(const uint8_t activePin);
|
||||||
|
};
|
||||||
|
#endif
|
46
src/RowPort_PCA9655E.cpp
Normal file
46
src/RowPort_PCA9655E.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include "RowPort_PCA9655E.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
configures column port's configuration and output.
|
||||||
|
*/
|
||||||
|
RowPort_PCA9655E::RowPort_PCA9655E(IOExpanderPort& port)
|
||||||
|
: port(port), configurationByteCommand(port.num + 6), outputByteCommand(port.num + 2)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void RowPort_PCA9655E::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(configurationByteCommand);
|
||||||
|
Wire.write(0); //0=configure as output (for strobe pins and LED)
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sets activePin pin output to low, does not reset the other pins because they might be used by LEDs.
|
||||||
|
activePin is port mask, where active pin is 1.
|
||||||
|
*/
|
||||||
|
void RowPort_PCA9655E::setActivePinLow(const uint8_t activePin)
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(outputByteCommand);
|
||||||
|
Wire.write(port.outputVal &= ~activePin);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
sets activePin pin output to high.
|
||||||
|
activePin is port mask, where active pin is 1.
|
||||||
|
*/
|
||||||
|
void RowPort_PCA9655E::setActivePinHigh(const uint8_t activePin)
|
||||||
|
{
|
||||||
|
Wire.beginTransmission(port.ADDR);
|
||||||
|
Wire.write(outputByteCommand);
|
||||||
|
Wire.write(port.outputVal |= activePin);
|
||||||
|
Wire.endTransmission();
|
||||||
|
}
|
48
src/RowPort_PCA9655E.h
Normal file
48
src/RowPort_PCA9655E.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#ifndef ROWPORT_PCA9655E_H
|
||||||
|
#define ROWPORT_PCA9655E_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <RowPort.h>
|
||||||
|
#include "IOExpanderPort.h"
|
||||||
|
|
||||||
|
/* One PCA9655E I/O expander port connected to matrix rows.
|
||||||
|
|
||||||
|
begin() configures column port's configuration and output.
|
||||||
|
This should normally be called only once.
|
||||||
|
|
||||||
|
Instantiation
|
||||||
|
------------
|
||||||
|
Example instantiation for row port 0:
|
||||||
|
IOExpanderPort port0(0, 0);
|
||||||
|
RowPort_PCA9655E rowPort0(port0);
|
||||||
|
|
||||||
|
Example instantiation for row port 1:
|
||||||
|
IOExpanderPort port1(1, 0);
|
||||||
|
RowPort_PCA9655E rowPort1(port1);
|
||||||
|
|
||||||
|
Diode orientation
|
||||||
|
----------------
|
||||||
|
Rows, columns, and diode orientation are explained in Matrix.h
|
||||||
|
|
||||||
|
PCA9655E data sheet
|
||||||
|
----------------
|
||||||
|
http://www.onsemi.com/pub_link/Collateral/PCA9655E-D.PDF
|
||||||
|
*/
|
||||||
|
|
||||||
|
class RowPort_PCA9655E : public RowPort
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IOExpanderPort& port;
|
||||||
|
const uint8_t configurationByteCommand;
|
||||||
|
const uint8_t outputByteCommand;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//The constructor initialization list is in .cpp
|
||||||
|
RowPort_PCA9655E(IOExpanderPort& port);
|
||||||
|
void begin();
|
||||||
|
|
||||||
|
virtual void setActivePinLow(const uint8_t activePin); //activePin is a port mask
|
||||||
|
virtual void setActivePinHigh(const uint8_t activePin);
|
||||||
|
};
|
||||||
|
#endif
|
31
src/StateLayers.cpp
Normal file
31
src/StateLayers.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "StateLayers.h"
|
||||||
|
|
||||||
|
void StateLayers::hold(const uint8_t layer)
|
||||||
|
{
|
||||||
|
setActiveLayer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateLayers::unhold(const uint8_t layer)
|
||||||
|
{
|
||||||
|
if (layer == activeLayer);
|
||||||
|
{
|
||||||
|
setActiveLayer(lockedLayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateLayers::lock(const uint8_t layer)
|
||||||
|
{
|
||||||
|
setActiveLayer(layer);
|
||||||
|
lockedLayer = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
//could set LED indicator lights in setActiveLayer()
|
||||||
|
void StateLayers::setActiveLayer(const uint8_t layer)
|
||||||
|
{
|
||||||
|
activeLayer = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t StateLayers::getActiveLayer()
|
||||||
|
{
|
||||||
|
return activeLayer;
|
||||||
|
}
|
25
src/StateLayers.h
Normal file
25
src/StateLayers.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef LAYERSTATE_H
|
||||||
|
#define LAYERSTATE_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <StateLayersInterface.h>
|
||||||
|
//#include <LED.h>
|
||||||
|
|
||||||
|
/* basic StateLayers for keyboard.
|
||||||
|
When pressed, Code_Layer objects call StateLayers functions lock() or hold().
|
||||||
|
When pressed, Layered objects call StateLayers function getActiveLayer().
|
||||||
|
*/
|
||||||
|
class StateLayers : public StateLayersInterface
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
uint8_t activeLayer; //currently active layer
|
||||||
|
uint8_t lockedLayer; //most recently pressed lock layer
|
||||||
|
virtual void setActiveLayer(const uint8_t layer);
|
||||||
|
public:
|
||||||
|
StateLayers() : activeLayer(0), lockedLayer(0) {}
|
||||||
|
virtual void hold(uint8_t layer); //set activeLayer
|
||||||
|
virtual void unhold(const uint8_t layer); //restore activeLayer to lockedLayer
|
||||||
|
virtual void lock(uint8_t layer); //set activeLayer and lock it
|
||||||
|
virtual uint8_t getActiveLayer();
|
||||||
|
};
|
||||||
|
#endif
|
11
src/StateLayersInterface.h
Normal file
11
src/StateLayersInterface.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef STATELAYERSINTERFACE_H
|
||||||
|
#define STATELAYERSINTERFACE_H
|
||||||
|
|
||||||
|
/* StateLayersInterface in an interface class
|
||||||
|
*/
|
||||||
|
class StateLayersInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual uint8_t getActiveLayer()=0;
|
||||||
|
};
|
||||||
|
#endif
|
36
src/getFreeSRAM.h
Normal file
36
src/getFreeSRAM.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// getFreeSRAM.h copied from
|
||||||
|
// http://andybrown.me.uk/2011/01/01/debugging-avr-dynamic-memory-allocation/
|
||||||
|
/*
|
||||||
|
* memdebug.h
|
||||||
|
*
|
||||||
|
* Created on: 15 Dec 2010
|
||||||
|
* Author: Andy Brown
|
||||||
|
*
|
||||||
|
* Use without attribution is permitted provided that this
|
||||||
|
* header remains intact and that these terms and conditions
|
||||||
|
* are followed:
|
||||||
|
*
|
||||||
|
* http://andybrown.me.uk/ws/terms-and-conditions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
extern unsigned int __bss_end;
|
||||||
|
extern unsigned int __heap_start;
|
||||||
|
extern void *__brkval;
|
||||||
|
|
||||||
|
//measure and return amount of free SRAM
|
||||||
|
uint16_t getFreeSRAM()
|
||||||
|
{
|
||||||
|
uint8_t newVariable;
|
||||||
|
// if heap is empty, use bss as start memory address
|
||||||
|
if ((uint16_t)__brkval == 0)
|
||||||
|
{
|
||||||
|
return (((uint16_t)&newVariable) - ((uint16_t)&__bss_end));
|
||||||
|
}
|
||||||
|
// else use heap end as the start of the memory address
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (((uint16_t)&newVariable) - ((uint16_t)__brkval));
|
||||||
|
}
|
||||||
|
};
|
179
src/objects_scancode.h
Normal file
179
src/objects_scancode.h
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/* Include this file in multiple-layer keybrd sketches.
|
||||||
|
This file instandiates Code objects.
|
||||||
|
|
||||||
|
The scancode macros are defined in the top part of
|
||||||
|
Arduino\hardware\teensy\cores\teensy\keylayouts.h which is intended for use in "normal" programs.
|
||||||
|
This has been tested on teensy2.0.
|
||||||
|
*/
|
||||||
|
#include <Code_Null.h>
|
||||||
|
#include <Code_Sc.h>
|
||||||
|
#include <Code_ScS.h>
|
||||||
|
#include <Code_Shift.h>
|
||||||
|
|
||||||
|
// ********** SCANCODES *********
|
||||||
|
Code_Shift s_shift(MODIFIERKEY_LEFT_SHIFT);
|
||||||
|
Code_Shift s_shift_L(MODIFIERKEY_LEFT_SHIFT);
|
||||||
|
Code_Shift s_shift_R(MODIFIERKEY_RIGHT_SHIFT);
|
||||||
|
|
||||||
|
Code_Sc s_ctrl(MODIFIERKEY_CTRL);
|
||||||
|
Code_Sc s_alt(MODIFIERKEY_ALT);
|
||||||
|
Code_Sc s_gui(MODIFIERKEY_GUI);
|
||||||
|
Code_Sc s_leftCtrl(MODIFIERKEY_LEFT_CTRL);
|
||||||
|
Code_Sc s_leftAlt(MODIFIERKEY_LEFT_ALT);
|
||||||
|
Code_Sc s_leftGUI(MODIFIERKEY_LEFT_GUI);
|
||||||
|
Code_Sc s_rightCtrl(MODIFIERKEY_RIGHT_CTRL);
|
||||||
|
Code_Sc s_rightAlt(MODIFIERKEY_RIGHT_ALT);
|
||||||
|
Code_Sc s_rightGUI(MODIFIERKEY_RIGHT_GUI);
|
||||||
|
|
||||||
|
Code_Sc s_MVolumeInc(KEY_MEDIA_VOLUME_INC);
|
||||||
|
Code_Sc s_MVolumeDec(KEY_MEDIA_VOLUME_DEC);
|
||||||
|
Code_Sc s_MMute(KEY_MEDIA_MUTE);
|
||||||
|
Code_Sc s_MPlayPause(KEY_MEDIA_PLAY_PAUSE);
|
||||||
|
Code_Sc s_MNextTrack(KEY_MEDIA_NEXT_TRACK);
|
||||||
|
Code_Sc s_MPrevTrack(KEY_MEDIA_PREV_TRACK);
|
||||||
|
Code_Sc s_MStop(KEY_MEDIA_STOP);
|
||||||
|
Code_Sc s_MEject(KEY_MEDIA_EJECT);
|
||||||
|
|
||||||
|
Code_Sc s_a(KEY_A);
|
||||||
|
Code_Sc s_b(KEY_B);
|
||||||
|
Code_Sc s_c(KEY_C);
|
||||||
|
Code_Sc s_d(KEY_D);
|
||||||
|
Code_Sc s_e(KEY_E);
|
||||||
|
Code_Sc s_f(KEY_F);
|
||||||
|
Code_Sc s_g(KEY_G);
|
||||||
|
Code_Sc s_h(KEY_H);
|
||||||
|
Code_Sc s_i(KEY_I);
|
||||||
|
Code_Sc s_j(KEY_J);
|
||||||
|
Code_Sc s_k(KEY_K);
|
||||||
|
Code_Sc s_l(KEY_L);
|
||||||
|
Code_Sc s_m(KEY_M);
|
||||||
|
Code_Sc s_n(KEY_N);
|
||||||
|
Code_Sc s_o(KEY_O);
|
||||||
|
Code_Sc s_p(KEY_P);
|
||||||
|
Code_Sc s_q(KEY_Q);
|
||||||
|
Code_Sc s_r(KEY_R);
|
||||||
|
Code_Sc s_s(KEY_S);
|
||||||
|
Code_Sc s_t(KEY_T);
|
||||||
|
Code_Sc s_u(KEY_U);
|
||||||
|
Code_Sc s_v(KEY_V);
|
||||||
|
Code_Sc s_w(KEY_W);
|
||||||
|
Code_Sc s_x(KEY_X);
|
||||||
|
Code_Sc s_y(KEY_Y);
|
||||||
|
Code_Sc s_z(KEY_Z);
|
||||||
|
|
||||||
|
Code_Sc s_1(KEY_1);
|
||||||
|
Code_Sc s_2(KEY_2);
|
||||||
|
Code_Sc s_3(KEY_3);
|
||||||
|
Code_Sc s_4(KEY_4);
|
||||||
|
Code_Sc s_5(KEY_5);
|
||||||
|
Code_Sc s_6(KEY_6);
|
||||||
|
Code_Sc s_7(KEY_7);
|
||||||
|
Code_Sc s_8(KEY_8);
|
||||||
|
Code_Sc s_9(KEY_9);
|
||||||
|
Code_Sc s_0(KEY_0);
|
||||||
|
|
||||||
|
Code_Sc s_enter(KEY_ENTER);
|
||||||
|
Code_Sc s_esc(KEY_ESC);
|
||||||
|
Code_Sc s_backspace(KEY_BACKSPACE);
|
||||||
|
Code_Sc s_tab(KEY_TAB);
|
||||||
|
Code_Sc s_space(KEY_SPACE);
|
||||||
|
Code_Sc s_minus(KEY_MINUS);
|
||||||
|
Code_Sc s_equal(KEY_EQUAL);
|
||||||
|
Code_Sc s_leftBracket(KEY_LEFT_BRACE); //[ ("brace" means curly bracket {})
|
||||||
|
Code_Sc s_rightBracket(KEY_RIGHT_BRACE); //]
|
||||||
|
Code_Sc s_backslash(KEY_BACKSLASH);
|
||||||
|
Code_Sc s_semicolon(KEY_SEMICOLON);
|
||||||
|
Code_Sc s_quote(KEY_QUOTE);
|
||||||
|
Code_Sc s_graves(KEY_TILDE); //`
|
||||||
|
//Code_Sc s_graves(ASCII_60); //`
|
||||||
|
Code_Sc s_comma(KEY_COMMA);
|
||||||
|
Code_Sc s_period(KEY_PERIOD);
|
||||||
|
Code_Sc s_slash(KEY_SLASH);
|
||||||
|
Code_Sc s_capsLock(KEY_CAPS_LOCK);
|
||||||
|
|
||||||
|
Code_Sc s_F1(KEY_F1);
|
||||||
|
Code_Sc s_F2(KEY_F2);
|
||||||
|
Code_Sc s_F3(KEY_F3);
|
||||||
|
Code_Sc s_F4(KEY_F4);
|
||||||
|
Code_Sc s_F5(KEY_F5);
|
||||||
|
Code_Sc s_F6(KEY_F6);
|
||||||
|
Code_Sc s_F7(KEY_F7);
|
||||||
|
Code_Sc s_F8(KEY_F8);
|
||||||
|
Code_Sc s_F9(KEY_F9);
|
||||||
|
Code_Sc s_F10(KEY_F10);
|
||||||
|
Code_Sc s_F11(KEY_F11);
|
||||||
|
Code_Sc s_F12(KEY_F12);
|
||||||
|
|
||||||
|
Code_Sc s_printscreen(KEY_PRINTSCREEN);
|
||||||
|
Code_Sc s_scrollLock(KEY_SCROLL_LOCK);
|
||||||
|
Code_Sc s_pause(KEY_PAUSE);
|
||||||
|
Code_Sc s_insert(KEY_INSERT);
|
||||||
|
Code_Sc s_home(KEY_HOME);
|
||||||
|
Code_Sc s_pageUp(KEY_PAGE_UP);
|
||||||
|
Code_Sc s_delete(KEY_DELETE);
|
||||||
|
Code_Sc s_end(KEY_END);
|
||||||
|
Code_Sc s_pageDown(KEY_PAGE_DOWN);
|
||||||
|
Code_Sc s_right(KEY_RIGHT); //arrow
|
||||||
|
Code_Sc s_left(KEY_LEFT);
|
||||||
|
Code_Sc s_down(KEY_DOWN);
|
||||||
|
Code_Sc s_up(KEY_UP);
|
||||||
|
Code_Sc s_numLock(KEY_NUM_LOCK);
|
||||||
|
|
||||||
|
Code_Sc s_padSlash(KEYPAD_SLASH);
|
||||||
|
Code_Sc s_padAsterix(KEYPAD_ASTERIX);
|
||||||
|
Code_Sc s_padMinus(KEYPAD_MINUS);
|
||||||
|
Code_Sc s_padPlus(KEYPAD_PLUS);
|
||||||
|
Code_Sc s_padEnter(KEYPAD_ENTER);
|
||||||
|
Code_Sc s_pad1(KEYPAD_1);
|
||||||
|
Code_Sc s_pad2(KEYPAD_2);
|
||||||
|
Code_Sc s_pad3(KEYPAD_3);
|
||||||
|
Code_Sc s_pad4(KEYPAD_4);
|
||||||
|
Code_Sc s_pad5(KEYPAD_5);
|
||||||
|
Code_Sc s_pad6(KEYPAD_6);
|
||||||
|
Code_Sc s_pad7(KEYPAD_7);
|
||||||
|
Code_Sc s_pad8(KEYPAD_8);
|
||||||
|
Code_Sc s_pad9(KEYPAD_9);
|
||||||
|
Code_Sc s_pad0(KEYPAD_0);
|
||||||
|
Code_Sc s_padPeriod(KEYPAD_PERIOD);
|
||||||
|
|
||||||
|
Code_Sc s_menu(KEY_MENU);
|
||||||
|
Code_Sc s_F13(KEY_F13);
|
||||||
|
Code_Sc s_F14(KEY_F14);
|
||||||
|
Code_Sc s_F15(KEY_F15);
|
||||||
|
Code_Sc s_F16(KEY_F16);
|
||||||
|
Code_Sc s_F17(KEY_F17);
|
||||||
|
Code_Sc s_F18(KEY_F18);
|
||||||
|
Code_Sc s_F19(KEY_F19);
|
||||||
|
Code_Sc s_F20(KEY_F20);
|
||||||
|
Code_Sc s_F21(KEY_F21);
|
||||||
|
Code_Sc s_F22(KEY_F22);
|
||||||
|
Code_Sc s_F23(KEY_F23);
|
||||||
|
Code_Sc s_F24(KEY_F24);
|
||||||
|
|
||||||
|
// ********** SCANCODES SHIFTED *********
|
||||||
|
// shifted objects are named after ascii symbol names
|
||||||
|
Code_ScS s_exclamation(KEY_1);
|
||||||
|
Code_ScS s_at(KEY_2);
|
||||||
|
Code_ScS s_number(KEY_3); //#
|
||||||
|
Code_ScS s_dollar(KEY_4);
|
||||||
|
Code_ScS s_percent(KEY_5);
|
||||||
|
Code_ScS s_circumflex(KEY_6); //^
|
||||||
|
Code_ScS s_ampersand(KEY_7);
|
||||||
|
Code_ScS s_asterix(KEY_8);
|
||||||
|
Code_ScS s_leftParen(KEY_9); //parenthesis
|
||||||
|
Code_ScS s_rightParen(KEY_0);
|
||||||
|
|
||||||
|
Code_ScS s_underscore(KEY_MINUS);
|
||||||
|
Code_ScS s_plus(KEY_EQUAL);
|
||||||
|
Code_ScS s_leftBrace(KEY_LEFT_BRACE); //{
|
||||||
|
Code_ScS s_rightBrace(KEY_RIGHT_BRACE); //}
|
||||||
|
Code_ScS s_vertBar(KEY_BACKSLASH); //|
|
||||||
|
Code_ScS s_colon(KEY_SEMICOLON);
|
||||||
|
Code_ScS s_doubleQuote(KEY_QUOTE);
|
||||||
|
Code_ScS s_tilde(KEY_TILDE);
|
||||||
|
Code_ScS s_lessThan(KEY_COMMA);
|
||||||
|
Code_ScS s_greaterThan(KEY_PERIOD);
|
||||||
|
Code_ScS s_question(KEY_SLASH);
|
||||||
|
|
||||||
|
// ********** MISC CODES *********
|
||||||
|
Code_Null code_null; //usefull for blank keys
|
39
src/objects_scancodeNotShifted.h
Normal file
39
src/objects_scancodeNotShifted.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/* This file instandiates Code_ScNS objects for multiple-layer keybrd sketches.
|
||||||
|
The scancode is always sent in the unshifted state regardless of shift key position.
|
||||||
|
Letters will still print as capital if CapsLck is on.
|
||||||
|
|
||||||
|
If your uC is low on memory, copy needed lines rather than including the entire file.
|
||||||
|
With the Arduino IDE, objects consume memory if they are used or not.
|
||||||
|
|
||||||
|
The scancode macros are defined in the top part of
|
||||||
|
Arduino\hardware\teensy\cores\teensy\keylayouts.h which is intended for use in "normal" programs.
|
||||||
|
This has been tested on teensy2.0.
|
||||||
|
*/
|
||||||
|
#include <Code_ScNS.h>
|
||||||
|
#include <Code_ScNS_00.h>
|
||||||
|
|
||||||
|
// ********** SCANCODES NOT SHIFTED *********
|
||||||
|
Code_ScNS_00 sns_00; //double zero
|
||||||
|
|
||||||
|
Code_ScNS sns_1(KEY_1); //could get similar effect with s_pad1
|
||||||
|
Code_ScNS sns_2(KEY_2);
|
||||||
|
Code_ScNS sns_3(KEY_3);
|
||||||
|
Code_ScNS sns_4(KEY_4);
|
||||||
|
Code_ScNS sns_5(KEY_5);
|
||||||
|
Code_ScNS sns_6(KEY_6);
|
||||||
|
Code_ScNS sns_7(KEY_7);
|
||||||
|
Code_ScNS sns_8(KEY_8);
|
||||||
|
Code_ScNS sns_9(KEY_9);
|
||||||
|
Code_ScNS sns_0(KEY_0);
|
||||||
|
|
||||||
|
Code_ScNS sns_minus(KEY_MINUS); //could get similar effect with s_padMinus
|
||||||
|
Code_ScNS sns_equal(KEY_EQUAL);
|
||||||
|
Code_ScNS sns_leftBracket(KEY_LEFT_BRACE); //[ ("brace" means curly bracket {})
|
||||||
|
Code_ScNS sns_rightBracket(KEY_RIGHT_BRACE); //]
|
||||||
|
Code_ScNS sns_backslash(KEY_BACKSLASH);
|
||||||
|
Code_ScNS sns_semicolon(KEY_SEMICOLON);
|
||||||
|
Code_ScNS sns_quote(KEY_QUOTE);
|
||||||
|
Code_ScNS sns_tilde(KEY_TILDE);
|
||||||
|
Code_ScNS sns_comma(KEY_COMMA);
|
||||||
|
Code_ScNS sns_period(KEY_PERIOD);
|
||||||
|
Code_ScNS sns_slash(KEY_SLASH);
|
BIN
tutorials/breadboard_keyboard_supplies.ods
Normal file
BIN
tutorials/breadboard_keyboard_supplies.ods
Normal file
Binary file not shown.
BIN
tutorials/images/120px-Diode_pinout_en_fr.svg.png
Normal file
BIN
tutorials/images/120px-Diode_pinout_en_fr.svg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
tutorials/images/breadboard_big.jpg
Normal file
BIN
tutorials/images/breadboard_big.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 333 KiB |
BIN
tutorials/images/breadboard_keyboard_2x2_labeled.jpg
Normal file
BIN
tutorials/images/breadboard_keyboard_2x2_labeled.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 476 KiB |
@ -0,0 +1,163 @@
|
|||||||
|
/* keybrd_single-layer_2_annotated.ino
|
||||||
|
|
||||||
|
This sketch:
|
||||||
|
is a simple 1-layer keyboard
|
||||||
|
runs on the first two rows and columns of a breadboard keyboard
|
||||||
|
is annotated with a walk-through narrative
|
||||||
|
|
||||||
|
This layout table shows how keys are arranged on the keyboard:
|
||||||
|
|
||||||
|
| Layout | **0** | **1** |
|
||||||
|
|:------:|-------|-------|
|
||||||
|
| **0** | a | b |
|
||||||
|
| **1** | c | shift |
|
||||||
|
|
||||||
|
The layout's row and column numbers are in the headers.
|
||||||
|
Each cell in the table's body represents a key.
|
||||||
|
|
||||||
|
The sketch is annotated with a walk-through narrative enclosed in comment blocks.
|
||||||
|
Each comment block explains the next one or two lines of code.
|
||||||
|
|
||||||
|
keybrd is instantiated under the "GLOBAL" heading. Most of the sketch is in global space.
|
||||||
|
keybrd runs at the end of this sketch, under the "MAIN" heading.
|
||||||
|
*/
|
||||||
|
// ################## GLOBAL ###################
|
||||||
|
// ================= INCLUDES ==================
|
||||||
|
/*
|
||||||
|
The compiler copies #included files into the sketch.
|
||||||
|
All the includes in this sketch are to keybrd library classes.
|
||||||
|
*/
|
||||||
|
//Ports
|
||||||
|
#include <RowPort_AVR_Optic.h>
|
||||||
|
#include <ColPort_AVR.h>
|
||||||
|
|
||||||
|
//Codes
|
||||||
|
#include <Code_Sc.h>
|
||||||
|
|
||||||
|
//Matrix
|
||||||
|
#include <Row.h>
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
// ============ SPEED CONFIGURATIONS ============
|
||||||
|
/*
|
||||||
|
DELAY_MICROSECONDS specifies the amount of delay between row scans.
|
||||||
|
Keyboard switches are made of moving contacts.
|
||||||
|
When the contacts close, they bounce apart one or more times before making steady contact.
|
||||||
|
DELAY_MICROSECONDS gives the switches time to debounce.
|
||||||
|
|
||||||
|
DELAY_MICROSECONDS is a static variable of class Row.
|
||||||
|
*/
|
||||||
|
const unsigned int Row::DELAY_MICROSECONDS = 1000;
|
||||||
|
|
||||||
|
// =================== PORTS ===================
|
||||||
|
/*
|
||||||
|
A micro-controller has one or more ports. Each port has one or more pins.
|
||||||
|
These pins are connected to the keyboard's rows and columns.
|
||||||
|
|
||||||
|
The RowPort constructor parameters specify the port's registers.
|
||||||
|
*/
|
||||||
|
RowPort_AVR_Optic rowPortF(DDRF, PORTF);
|
||||||
|
|
||||||
|
/*
|
||||||
|
The ColPort constructor parameters specify the port's registers and the port pins to read:
|
||||||
|
A number to the right of "1<<" is the pin number to read. 1<<0 reads pin 0, and 1<<1 reads pin 1.
|
||||||
|
*/
|
||||||
|
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<0 | 1<<1 );
|
||||||
|
|
||||||
|
/*
|
||||||
|
ColPort pointers are placed in an array because some keyboards use multiple column ports.
|
||||||
|
This sketch only has one column port.
|
||||||
|
|
||||||
|
sizeof() is used to compute the number of array elements.
|
||||||
|
This eliminates the risk of forgetting to update the count after adding or removing an element.
|
||||||
|
*/
|
||||||
|
ColPort* const ptrsColPorts[] = { &colPortB };
|
||||||
|
const uint8_t COL_PORT_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts);
|
||||||
|
|
||||||
|
// =================== CODES ===================
|
||||||
|
/*
|
||||||
|
The CODES section instantiates four codes, one for each item in the layout.
|
||||||
|
|
||||||
|
The Code_Sc constructor takes one scancode ("Sc" means "scancode").
|
||||||
|
When Code_Sc is pressed, it sends its scancode.
|
||||||
|
|
||||||
|
The Code object names in this sketch start with a "s_" prefix.
|
||||||
|
*/
|
||||||
|
Code_Sc s_a(KEY_A);
|
||||||
|
Code_Sc s_b(KEY_B);
|
||||||
|
Code_Sc s_c(KEY_C);
|
||||||
|
Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT);
|
||||||
|
|
||||||
|
// ================== MATRIX ===================
|
||||||
|
/*
|
||||||
|
The MATRIX section instantiates the components of the matrix:
|
||||||
|
Codes are grouped into rows.
|
||||||
|
Rows are grouped into a matrix.
|
||||||
|
|
||||||
|
How the matrix works:
|
||||||
|
1) The matrix scans one row at a time.
|
||||||
|
2) If a row detects a key press, it notifies the code.
|
||||||
|
3) The code sends its scancode.
|
||||||
|
*/
|
||||||
|
// ------------------- ROWS --------------------
|
||||||
|
/*
|
||||||
|
Here we group Code pointers into rows.
|
||||||
|
Codes are a kind of Key. Array ptrsKeys_0[] contains two pointers to Key objects.
|
||||||
|
|
||||||
|
The Row constructor parameters are:
|
||||||
|
one rowPort
|
||||||
|
one row pin
|
||||||
|
an array of colPorts, and the number of colPorts
|
||||||
|
an array of Key pointers
|
||||||
|
|
||||||
|
The Row objects names in this sketch start with a "row_" followed by a row number.
|
||||||
|
*/
|
||||||
|
Key* const ptrsKeys_0[] = { &s_a, &s_b };
|
||||||
|
Row row_0(rowPortF, 1<<0, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_0);
|
||||||
|
|
||||||
|
Key* const ptrsKeys_1[] = { &s_c, &s_shift };
|
||||||
|
Row row_1(rowPortF, 1<<1, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
HOW ROW OBJECTS WORK
|
||||||
|
When a row is scanned, the row strobes the row pin, and the column ports read their column pins.
|
||||||
|
If a row detects a key press, it notifies the key which then sends its scancode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ------------------ MATRIX -------------------
|
||||||
|
/*
|
||||||
|
Here we group Row pointers into a matrix.
|
||||||
|
Array ptrsRows[] contains two pointers to Row objects.
|
||||||
|
*/
|
||||||
|
Row* const ptrsRows[] = { &row_0, &row_1 };
|
||||||
|
const uint8_t ROW_COUNT = sizeof(ptrsRows)/sizeof(*ptrsRows);
|
||||||
|
|
||||||
|
/*
|
||||||
|
The Matrix constructor parameters are:
|
||||||
|
one array of Row pointers, and the number of rows
|
||||||
|
'0' for active low or '1' for active high
|
||||||
|
WARNING: the tutorial sketches all have '1' for active high to be compatible with DH.
|
||||||
|
The breadboard keyboard described in tutorial_1 is active low.
|
||||||
|
For active low, change the '1' to a '0':
|
||||||
|
*/
|
||||||
|
Matrix matrix(ptrsRows, ROW_COUNT, 1);
|
||||||
|
|
||||||
|
// ################### MAIN ####################
|
||||||
|
/*
|
||||||
|
Aruduino IDE copies Functions setup() and loop() into main().
|
||||||
|
|
||||||
|
setup() initialized the keybrd.
|
||||||
|
Keyboard.begin() should be called once to initialize.
|
||||||
|
*/
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Keyboard.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
loop() continually scans the Matrix object.
|
||||||
|
*/
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
matrix.scan();
|
||||||
|
}
|
@ -0,0 +1,151 @@
|
|||||||
|
/* keybrd_3_multi-layer_annotated.ino
|
||||||
|
|
||||||
|
This sketch:
|
||||||
|
is a simple 2-layer keyboard
|
||||||
|
runs on the first two rows and columns of a breadboard keyboard
|
||||||
|
is annotated with a walk-through narrative
|
||||||
|
|
||||||
|
This layout table shows how keys are arranged on the keyboard:
|
||||||
|
|
||||||
|
| Layout | **0** | **1** |
|
||||||
|
|:------:|-------|-------|
|
||||||
|
| **0** | a 1 | b 2 |
|
||||||
|
| **1** | fn | shift |
|
||||||
|
|
||||||
|
The layout's row and column numbers are in the headers.
|
||||||
|
Each cell in the table's body represents a key.
|
||||||
|
The layered keys in row 0 have two layers; one character for each layer.
|
||||||
|
Letters 'a' and 'b' are on the normal layer. Numbers '1' and '2' are one the fn layer.
|
||||||
|
Holding the fn key down makes it the active layer. Releasing the fn key restores the normal layer.
|
||||||
|
*/
|
||||||
|
// ################## GLOBAL ###################
|
||||||
|
// ================= INCLUDES ==================
|
||||||
|
//Ports
|
||||||
|
#include <RowPort_AVR_Optic.h>
|
||||||
|
#include <ColPort_AVR.h>
|
||||||
|
|
||||||
|
//Codes
|
||||||
|
#include <Code_Sc.h>
|
||||||
|
#include <StateLayers.h>
|
||||||
|
#include <Code_LayerHold.h>
|
||||||
|
#include <Key_LayeredKeysArray.h>
|
||||||
|
|
||||||
|
//Matrix
|
||||||
|
#include <Row.h>
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
// ============ SPEED CONFIGURATIONS ============
|
||||||
|
const unsigned int Row::DELAY_MICROSECONDS = 1000;
|
||||||
|
|
||||||
|
// =================== PORTS ===================
|
||||||
|
RowPort_AVR_Optic rowPortF(DDRF, PORTF);
|
||||||
|
|
||||||
|
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<0 | 1<<1 );
|
||||||
|
|
||||||
|
ColPort* const ptrsColPorts[] = { &colPortB };
|
||||||
|
const uint8_t COL_PORT_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts);
|
||||||
|
|
||||||
|
// =================== CODES ===================
|
||||||
|
/*
|
||||||
|
The CODES section instantiates six codes, one for each item in the layout:
|
||||||
|
s_a s_1 s_b s_2
|
||||||
|
l_fn s_shift
|
||||||
|
*/
|
||||||
|
// ---------------- LAYER CODE -----------------
|
||||||
|
/*
|
||||||
|
enum assings layer numbers to the layers.
|
||||||
|
*/
|
||||||
|
enum layers { NORMAL, FN };
|
||||||
|
/*
|
||||||
|
stateLayer keeps track of the active layer. The default layer number is 0.
|
||||||
|
*/
|
||||||
|
StateLayers stateLayer;
|
||||||
|
/*
|
||||||
|
The Code_LayerHold constructor parameter specifies a layer number and the StateLayers it calls.
|
||||||
|
When l_fn is pressed, it tells stateLayer to change the active layer to 1.
|
||||||
|
When l_fn is released, it tells stateLayer to restore the normal layer.
|
||||||
|
*/
|
||||||
|
Code_LayerHold l_fn(FN, stateLayer);
|
||||||
|
|
||||||
|
// ---------------- SCAN CODES -----------------
|
||||||
|
Code_Sc s_a(KEY_A);
|
||||||
|
Code_Sc s_b(KEY_B);
|
||||||
|
Code_Sc s_1(KEY_1);
|
||||||
|
Code_Sc s_2(KEY_2);
|
||||||
|
Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT);
|
||||||
|
|
||||||
|
// ================== MATRIX ===================
|
||||||
|
/*
|
||||||
|
The MATRIX section instantiates the components of the matrix:
|
||||||
|
Codes are grouped into keys.
|
||||||
|
Keys are grouped into rows.
|
||||||
|
Rows are grouped into a matrix.
|
||||||
|
*/
|
||||||
|
// ------------------- KEYS --------------------
|
||||||
|
/*
|
||||||
|
Here we group Code pointers into keys.
|
||||||
|
Array ptrsCodes_00[] contains two pointers to Code objects.
|
||||||
|
|
||||||
|
Key_LayeredKeysArray constructor parameters are:
|
||||||
|
one array of Code pointers
|
||||||
|
|
||||||
|
Key_LayeredKeysArray objects are multi-layered - one Code object per layer.
|
||||||
|
Layer numbers are array indexes for the Key_LayeredKeysArray.
|
||||||
|
Defining layer numbers with enum insures that the layer numbers are a series starting at 0.
|
||||||
|
|
||||||
|
The Key object names in this sketch start with a "k_" followed by matrix-row-column coordinates.
|
||||||
|
*/
|
||||||
|
Key* const ptrsCodes_00[] = { &s_a, &s_1 };
|
||||||
|
Key_LayeredKeysArray k_00(ptrsCodes_00);
|
||||||
|
|
||||||
|
Key* const ptrsCodes_01[] = { &s_b, &s_2 };
|
||||||
|
Key_LayeredKeysArray k_01(ptrsCodes_01);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Key_LayeredKeysArray has a static variable refStateLayers defined here.
|
||||||
|
It is a reference to stateLayer.
|
||||||
|
*/
|
||||||
|
StateLayersInterface& Key_LayeredKeysArray::refStateLayers = stateLayer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
HOW LAYERED OBJECTS WORK
|
||||||
|
When a Key_LayeredKeysArray object is pressed,
|
||||||
|
it gets the active layer from stateLayer and then sends the scancode for the active layer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ------------------- ROWS --------------------
|
||||||
|
/*
|
||||||
|
Here we group Key pointers into rows.
|
||||||
|
Array ptrsKeys_0[] contains two pointers to Key_LayeredKeyArray objects.
|
||||||
|
*/
|
||||||
|
Key* const ptrsKeys_0[] = { &k_00, &k_01 };
|
||||||
|
Row row_0(rowPortF, 1<<0, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Codes are a kind of Key that only have one layer.
|
||||||
|
So rows can contain multi-leyered a mix of keys and codes.
|
||||||
|
Array ptrsKeys_1[] contains two Code pointers.
|
||||||
|
*/
|
||||||
|
Key* const ptrsKeys_1[] = { &l_fn, &s_shift };
|
||||||
|
Row row_1(rowPortF, 1<<1, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_1);
|
||||||
|
|
||||||
|
// ------------------ MATRIX -------------------
|
||||||
|
/*
|
||||||
|
Here we group Row pointers into a matrix.
|
||||||
|
Array ptrsRows[] contains two pointers to Row objects.
|
||||||
|
*/
|
||||||
|
Row* const ptrsRows[] = { &row_0, &row_1 };
|
||||||
|
const uint8_t ROW_COUNT = sizeof(ptrsRows)/sizeof(*ptrsRows);
|
||||||
|
|
||||||
|
Matrix matrix(ptrsRows, ROW_COUNT, 1);
|
||||||
|
|
||||||
|
// ################### MAIN ####################
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Keyboard.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
matrix.scan();
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
/* keybrd_3_autoShift_annotated.ino
|
||||||
|
|
||||||
|
This sketch:
|
||||||
|
is a simple 2-layer keyboard with AutoShift
|
||||||
|
runs on the first two rows and columns of a breadboard keyboard
|
||||||
|
is annotated with a walk-through narrative
|
||||||
|
|
||||||
|
This layout table shows how keys are arranged on the keyboard:
|
||||||
|
|
||||||
|
| Layout | **0** | **1** |
|
||||||
|
|:------:|-------|-------|
|
||||||
|
| **0** | a ! | b @ |
|
||||||
|
| **1** | fn | shift |
|
||||||
|
|
||||||
|
The layered keys in row 0 have two layers; one character for each layer.
|
||||||
|
Letters 'a' and 'b' are on the normal layer. Symbols '!' and '@' are one the fn layer.
|
||||||
|
Holding the fn key down makes it the active layer. Releasing the fn key restores the normal layer.
|
||||||
|
*/
|
||||||
|
// ################## GLOBAL ###################
|
||||||
|
// ================= INCLUDES ==================
|
||||||
|
//Ports
|
||||||
|
#include <RowPort_AVR_Optic.h>
|
||||||
|
#include <ColPort_AVR.h>
|
||||||
|
|
||||||
|
//Codes
|
||||||
|
#include <Code_Sc.h>
|
||||||
|
#include <Code_ScS.h>
|
||||||
|
#include <Code_Shift.h>
|
||||||
|
#include <StateLayers.h>
|
||||||
|
#include <Code_LayerHold.h>
|
||||||
|
#include <Key_LayeredKeysArray.h>
|
||||||
|
|
||||||
|
//Matrix
|
||||||
|
#include <Row.h>
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
// ============ SPEED CONFIGURATIONS ============
|
||||||
|
const unsigned int Row::DELAY_MICROSECONDS = 1000;
|
||||||
|
|
||||||
|
// =================== PORTS ===================
|
||||||
|
RowPort_AVR_Optic rowPortF(DDRF, PORTF);
|
||||||
|
|
||||||
|
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<0 | 1<<1 );
|
||||||
|
|
||||||
|
ColPort* const ptrsColPorts[] = { &colPortB };
|
||||||
|
const uint8_t COL_PORT_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts);
|
||||||
|
|
||||||
|
// =================== CODES ===================
|
||||||
|
/*
|
||||||
|
The CODES section instantiates six codes, one for each item in the layout:
|
||||||
|
s_a s_exclamation s_b s_at
|
||||||
|
l_fn s_shift
|
||||||
|
*/
|
||||||
|
// ---------------- LAYER CODE -----------------
|
||||||
|
enum layers { NORMAL, FN };
|
||||||
|
|
||||||
|
StateLayers stateLayer;
|
||||||
|
Code_LayerHold l_fn(FN, stateLayer);
|
||||||
|
|
||||||
|
// ---------------- SCAN CODES -----------------
|
||||||
|
/*
|
||||||
|
The Code_Sc constructor takes one scancode ("Sc" means "scancode").
|
||||||
|
When Code_Sc is pressed, it sends its scancode.
|
||||||
|
*/
|
||||||
|
Code_Sc s_a(KEY_A);
|
||||||
|
Code_Sc s_b(KEY_B);
|
||||||
|
/*
|
||||||
|
The Code_ScS constructor takes one scancode to be shifted ("ScS" means "scancode shifted").
|
||||||
|
When Code_ScS is pressed, it calls Code_AutoShift before sending its scancode.
|
||||||
|
*/
|
||||||
|
Code_ScS s_exclamation(KEY_1);
|
||||||
|
Code_ScS s_at(KEY_2);
|
||||||
|
|
||||||
|
// ----------------- SHIFT CODE ----------------
|
||||||
|
/*
|
||||||
|
The Code_Shift constructor takes one scancode.
|
||||||
|
*/
|
||||||
|
Code_Shift s_shift(MODIFIERKEY_LEFT_SHIFT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Code_Shift pointers are placed in an array because most keyboards have a left and right shift.
|
||||||
|
This sketch only has one shift code.
|
||||||
|
*/
|
||||||
|
Code_Shift* const ptrsS[] = { &s_shift };
|
||||||
|
|
||||||
|
/*
|
||||||
|
Code_AutoShift is the base class of Codes_ScS (Codes_ScS is explained in the preceding section).
|
||||||
|
It has two static variables, ptrsShifts and shiftCount, which are defined here.
|
||||||
|
ptrsShifts is the array of Code_Shift pointers; one pointer for each shift key.
|
||||||
|
*/
|
||||||
|
Code_Shift* const* const Code_AutoShift::ptrsShifts = ptrsS;
|
||||||
|
const uint8_t Code_AutoShift::shiftCount = sizeof(ptrsShifts)/sizeof(*ptrsShifts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
HOW AUTOSHIFT WORKS
|
||||||
|
When a modifier key is pressed, a standard keyboard driver will temporarily modify the normal action of another key when pressed together.
|
||||||
|
KEY_1 writes '1'
|
||||||
|
MODIFIERKEY_LEFT_SHIFT + KEY_1 writes '!'
|
||||||
|
|
||||||
|
KEY_2 writes '2'
|
||||||
|
MODIFIERKEY_LEFT_SHIFT + KEY_2 writes '@'
|
||||||
|
|
||||||
|
Code_ScS takes care of the MODIFIERKEY_LEFT_SHIFT automatically
|
||||||
|
When the user presses '!' or '@' on the fn layer:
|
||||||
|
Code_AutoShift checks the position of each shift key
|
||||||
|
Code_ScS sends MODIFIERKEY_LEFT_SHIFT scancode if needed
|
||||||
|
Code_ScS sends its scancode
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ================== MATRIX ===================
|
||||||
|
// ------------------- KEYS --------------------
|
||||||
|
Key* const ptrsCodes_00[] = { &s_a, &s_exclamation };
|
||||||
|
Key_LayeredKeysArray k_00(ptrsCodes_00);
|
||||||
|
|
||||||
|
Key* const ptrsCodes_01[] = { &s_b, &s_at };
|
||||||
|
Key_LayeredKeysArray k_01(ptrsCodes_01);
|
||||||
|
|
||||||
|
StateLayersInterface& Key_LayeredKeysArray::refStateLayers = stateLayer;
|
||||||
|
|
||||||
|
// ------------------- ROWS --------------------
|
||||||
|
Key* const ptrsKeys_0[] = { &k_00, &k_01 };
|
||||||
|
Row row_0(rowPortF, 1<<0, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_0);
|
||||||
|
|
||||||
|
Key* const ptrsKeys_1[] = { &l_fn, &s_shift };
|
||||||
|
Row row_1(rowPortF, 1<<1, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_1);
|
||||||
|
|
||||||
|
// ------------------ MATRIX -------------------
|
||||||
|
Row* const ptrsRows[] = { &row_0, &row_1 };
|
||||||
|
const uint8_t ROW_COUNT = sizeof(ptrsRows)/sizeof(*ptrsRows);
|
||||||
|
|
||||||
|
Matrix matrix(ptrsRows, ROW_COUNT, 1);
|
||||||
|
|
||||||
|
// ################### MAIN ####################
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Keyboard.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
matrix.scan();
|
||||||
|
}
|
@ -0,0 +1,171 @@
|
|||||||
|
/* keybrd_4_split_with_IOE_annotated.ino
|
||||||
|
|
||||||
|
This sketch:
|
||||||
|
is a simple 1-layer keyboard
|
||||||
|
runs on two matrices of a breadboard keyboard
|
||||||
|
is annotated with a walk-through narrative
|
||||||
|
|
||||||
|
This layout table shows left and right matrices:
|
||||||
|
|
||||||
|
| Left | **0** | **1** | | Right | **0** | **1** |
|
||||||
|
|:-----:|-------|-------|-|:-----:|-------|-------|
|
||||||
|
| **0** | a | b | | **0** | 1 | 2 |
|
||||||
|
| **1** | shift | c | | **1** | 3 | shift |
|
||||||
|
|
||||||
|
MARTIX NAMING CONVENTION
|
||||||
|
Since this keyboard has two matrices, we need a naming convention to distinguish the matrices.
|
||||||
|
Matrix IDs are the letters 'L' and 'R' (left and right).
|
||||||
|
Port object names and Port pointer array names end with matrix ID:
|
||||||
|
port1_R
|
||||||
|
rowPortF_L rowPort1_R
|
||||||
|
port0_R
|
||||||
|
colPortB_L colPort0_R
|
||||||
|
ptrsColPorts_L ptrsColPorts_R
|
||||||
|
COL_PORT_L_COUNT COL_PORT_R_COUNT
|
||||||
|
|
||||||
|
Key pointer array names and Row objects names end with matrix ID and row number:
|
||||||
|
ptrsKeys_L0 ptrsKeys_R0
|
||||||
|
row_L0 row_R0
|
||||||
|
|
||||||
|
Matrix object names end with matrix ID:
|
||||||
|
matrix_L matrix_R
|
||||||
|
*/
|
||||||
|
// ################## GLOBAL ###################
|
||||||
|
// ================= INCLUDES ==================
|
||||||
|
//Ports
|
||||||
|
#include <RowPort_AVR_Optic.h>
|
||||||
|
#include <ColPort_AVR.h>
|
||||||
|
#include <IOExpanderPort.h>
|
||||||
|
#include <RowPort_PCA9655E.h>
|
||||||
|
#include <ColPort_PCA9655E.h>
|
||||||
|
|
||||||
|
//Codes
|
||||||
|
#include <Code_Sc.h>
|
||||||
|
|
||||||
|
//Matrix
|
||||||
|
#include <Row.h>
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
// ============ SPEED CONFIGURATIONS ============
|
||||||
|
const unsigned int Row::DELAY_MICROSECONDS = 1000;
|
||||||
|
|
||||||
|
// ================ LEFT PORTS =================
|
||||||
|
/*
|
||||||
|
The left matrix is scanned by a micro-controller.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
// =============== RIGHT PORTS =================
|
||||||
|
/*
|
||||||
|
The right matrix is scanned by an I/O expander.
|
||||||
|
|
||||||
|
The micro-controller and I/O expander communicates via I2C bus.
|
||||||
|
Three hardware pins (AD0, AD1, AD2) are used to configure the I2C address of the I/O expander.
|
||||||
|
ADDR is a static variable of class IOExpanderPort. The I2C address of this I/O expander is 0x18.
|
||||||
|
|
||||||
|
An I/O expander used on a matrix has two ports. Each port has eight pins.
|
||||||
|
One port is connected to the matrix's rows. The other port is connected to the matrix's columns.
|
||||||
|
|
||||||
|
The IOExpanderPort constructor parameters specify the port number and initial output value.
|
||||||
|
I/O Expander and AVR have similar constructor parameters for RowPort and ColPort.
|
||||||
|
*/
|
||||||
|
const uint8_t IOExpanderPort::ADDR = 0x18;
|
||||||
|
|
||||||
|
/*
|
||||||
|
port1_R uses port 1 with an initial output value of 0.
|
||||||
|
*/
|
||||||
|
IOExpanderPort port1_R(1, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
The RowPort_PCA9655E constructor parameter specifies the IOExpanderPort.
|
||||||
|
*/
|
||||||
|
RowPort_PCA9655E rowPort1_R(port1_R);
|
||||||
|
|
||||||
|
/*
|
||||||
|
port0_R uses port 0 with an initial output value of 0.
|
||||||
|
*/
|
||||||
|
IOExpanderPort port0_R(0, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
The ColPort_PCA9655E constructor parameter specifies the IOExpanderPort and the port pins to read:
|
||||||
|
A number to the right of "1<<" is the pin number to read. 1<<0 reads pin 0, and 1<<1 reads pin 1.
|
||||||
|
*/
|
||||||
|
ColPort_PCA9655E colPort0_R(port0_R, 1<<0 | 1<<1 );
|
||||||
|
|
||||||
|
/*
|
||||||
|
ColPort pointers are placed in an array because some keyboards use multiple column ports.
|
||||||
|
This sketch only has one column port.
|
||||||
|
|
||||||
|
sizeof() is used to compute the number of array elements.
|
||||||
|
This eliminates the risk of forgetting to update the count after adding or removing an element.
|
||||||
|
*/
|
||||||
|
ColPort* const ptrsColPorts_R[] = { &colPort0_R };
|
||||||
|
const uint8_t COL_PORT_R_COUNT = sizeof(ptrsColPorts_R)/sizeof(*ptrsColPorts_R);
|
||||||
|
|
||||||
|
// =================== CODES ===================
|
||||||
|
/*
|
||||||
|
Codes are not grouped into left and right because codes are independent of layout.
|
||||||
|
- a keyboard can have differnt layouts
|
||||||
|
- some codes may appear on both matrices
|
||||||
|
*/
|
||||||
|
Code_Sc s_shiftL(MODIFIERKEY_LEFT_SHIFT);
|
||||||
|
Code_Sc s_shiftR(MODIFIERKEY_RIGHT_SHIFT);
|
||||||
|
|
||||||
|
Code_Sc s_a(KEY_A);
|
||||||
|
Code_Sc s_b(KEY_B);
|
||||||
|
Code_Sc s_c(KEY_C);
|
||||||
|
Code_Sc s_1(KEY_1);
|
||||||
|
Code_Sc s_2(KEY_2);
|
||||||
|
Code_Sc s_3(KEY_3);
|
||||||
|
|
||||||
|
// ================ LEFT MATRIX ================
|
||||||
|
// ---------------- LEFT ROWS ------------------
|
||||||
|
Key* const ptrsKeys_L0[] = { &s_a, &s_b };
|
||||||
|
Row row_L0(rowPortF_L, 1<<0, ptrsColPorts_L, COL_PORT_L_COUNT, ptrsKeys_L0);
|
||||||
|
|
||||||
|
Key* const ptrsKeys_L1[] = { &s_c, &s_shiftL };
|
||||||
|
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);
|
||||||
|
|
||||||
|
// ================ RIGHT MATRIX ===============
|
||||||
|
// ---------------- RIGHT ROWS -----------------
|
||||||
|
Key* const ptrsKeys_R0[] = { &s_1, &s_2 };
|
||||||
|
Row row_R0(rowPort1_R, 1<<0, ptrsColPorts_R, COL_PORT_R_COUNT, ptrsKeys_R0);
|
||||||
|
|
||||||
|
Key* const ptrsKeys_R1[] = { &s_3, &s_shiftR };
|
||||||
|
Row row_R1(rowPort1_R, 1<<1, ptrsColPorts_R, COL_PORT_R_COUNT, ptrsKeys_R1);
|
||||||
|
|
||||||
|
// ---------------- RIGHT MATRIX ---------------
|
||||||
|
Row* const ptrsRows_R[] = { &row_R0, &row_R1 };
|
||||||
|
const uint8_t ROW_R_COUNT = sizeof(ptrsRows_R)/sizeof(*ptrsRows_R);
|
||||||
|
|
||||||
|
Matrix matrix_R(ptrsRows_R, ROW_R_COUNT, 1);
|
||||||
|
|
||||||
|
// ################### MAIN ####################
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Call begin() for I/O expander's rowPort and colPort.
|
||||||
|
*/
|
||||||
|
rowPort1_R.begin();
|
||||||
|
colPort0_R.begin();
|
||||||
|
Keyboard.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
loop() continually scans both Matrix objects.
|
||||||
|
*/
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
matrix_L.scan();
|
||||||
|
matrix_R.scan();
|
||||||
|
}
|
25
tutorials/tutorial_0_introduction.md
Normal file
25
tutorials/tutorial_0_introduction.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Tutorial 0 - Introduction
|
||||||
|
=========================
|
||||||
|
The first two tutorials are intended to be read in sequence:
|
||||||
|
1. Breadboard keyboard
|
||||||
|
2. Single-layer keybrd
|
||||||
|
You can write a keyboard firmware after reading tutorial 2.
|
||||||
|
Topics covered in tutorial 2 apply to all keybrd sketches.
|
||||||
|
The remaining tutorials can be read in any sequence, and highlight topics that may or may not be useful to your keyboard design.
|
||||||
|
|
||||||
|
The tutorials assume the reader:
|
||||||
|
* is familiar with C++
|
||||||
|
* is new to Arduino, firmware, controllers, and the internal workings of keyboards
|
||||||
|
|
||||||
|
Each tutorial presents an example sketch.
|
||||||
|
All the example sketches have 2 to 8 keys and run on a breadboard keyboard.
|
||||||
|
|
||||||
|
todo all the tutorial sketches are tested on teensy 2.0 and PCA9655E-D IOE
|
||||||
|
in July, sketches will be changed to Teensy LC and MCP23018 IOE
|
||||||
|
|
||||||
|
You will need a breadboard keyboard with a Teensy 2.0 controller to run the tutorial sketches.
|
||||||
|
If you use a different controller, you may have to change port classes.
|
||||||
|
If you already have a keyboard with an Arduino compatible controller, you can use that instead of a breadboard keyboard.
|
||||||
|
|
||||||
|
[breadboard keyboard with 2 rows and 2 columns]
|
||||||
|
(images/breadboard_keyboard_2x2_labeled.jpg "2x2 breadboard keyboard")
|
17
tutorials/tutorial_10_writing_your_own_port_classes.md
Normal file
17
tutorials/tutorial_10_writing_your_own_port_classes.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Tutorial 8 - writing your own port classes
|
||||||
|
==========================================
|
||||||
|
Port classes are the keybrd library's interface to microcontoller ports or I/O expander ports.
|
||||||
|
|
||||||
|
To write your own port classes:
|
||||||
|
1) Get a copy of the controller or I/O expander datasheet.
|
||||||
|
2) Study keybrd port classes that use a similar IC.
|
||||||
|
3) Consider looking for other open-source keyboard code that uses the same IC e.g. TMK keyboard firmware.
|
||||||
|
4) Write your RowPort_* class to inherit from RowPort class.
|
||||||
|
5) Write your ColPort_* class to inherit from ColPort class.
|
||||||
|
6) Consider testing on a breadboard keyboard.
|
||||||
|
|
||||||
|
Writing port classes is the most technically demanding task in the keybrd library.
|
||||||
|
If you have not read a controller datasheet or I/O expander datasheet before,
|
||||||
|
consider designing your keyboard around one of the controllers or I/O expanders
|
||||||
|
that already have port classes in the keybrd library.
|
||||||
|
|
86
tutorials/tutorial_1_breadboard_keyboard.md
Normal file
86
tutorials/tutorial_1_breadboard_keyboard.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
Tutorial 0 - breadboard keyboard
|
||||||
|
================================
|
||||||
|
When you finish this tutorial you will have a working keyboard and understand how its key matrix works.
|
||||||
|
|
||||||
|
[breadboard keyboard with 2 rows and 2 columns]
|
||||||
|
(images/breadboard_keyboard_2x2_labeled.jpg "2x2 breadboard keyboard")
|
||||||
|
|
||||||
|
## Why a breadboard keyboard is useful
|
||||||
|
A breadboard is the easiest way to learn keyboard electronics.
|
||||||
|
Electronics are fickle, you won't get everything right the first time, there is a learning curve.
|
||||||
|
Breadboards make the learning electronics faster and fun.
|
||||||
|
|
||||||
|
Breadboard keyboards have row-column matrices and diodes just like the big keyboards.
|
||||||
|
Compared to full size keyboards on PCBs, breadboard keyboards are easier to work with because:
|
||||||
|
* Parts can be reused in many different configurations
|
||||||
|
* A small keyboard is easier to trouble shoot
|
||||||
|
* Mistakes are easily corrected because no desoldering
|
||||||
|
|
||||||
|
Breadboard keyboards are useful for:
|
||||||
|
* learning keyboard electronics - diodes, micro controllers, I/O expanders
|
||||||
|
* learning the firmware development process
|
||||||
|
* prototyping circuits before making a PCB
|
||||||
|
* testing firmware concepts before building the keyboard hardware
|
||||||
|
|
||||||
|
## How a breadboard works
|
||||||
|
To understand the breadboard keyboard you will need to know the internal parts of a breadboard:
|
||||||
|
* terminal strip
|
||||||
|
* power rail
|
||||||
|
|
||||||
|
These are explained in [How to Use a Breadboard](https://learn.sparkfun.com/tutorials/how-to-use-a-breadboard)
|
||||||
|
|
||||||
|
## Breadboard keyboard starter kit
|
||||||
|
The parts needed to build all the Breadboard Keyboards in the keybrd tutorials are listed in [breadboard_keyboard_supplies.ods](breadboard_keyboard_supplies.ods).
|
||||||
|
|
||||||
|
Wire cutters (or nail clippers) is the only required tool.
|
||||||
|
A multi-meter is useful for trouble shooting.
|
||||||
|
|
||||||
|
## Building a basic breadboard keyboard
|
||||||
|
The basic breadboard has 4 switches and a microcontroller.
|
||||||
|
|
||||||
|
[breadboard keyboard with 2 rows and 2 columns of keys]
|
||||||
|
(images/breadboard_keyboard_2x2_labeled.jpg "2x2 breadboard keyboard")
|
||||||
|
|
||||||
|
The key matrix has two rows and two columns.
|
||||||
|
Breadboard power rails are repurposed as matrix rows.
|
||||||
|
Short bare wires connect switches into matrix columns.
|
||||||
|
Diodes connect switches to rows.
|
||||||
|
|
||||||
|
The green rectangle on the right is the Teensy 2.0 microcontroller.
|
||||||
|
The matrix rows and columns connect to the microcontroller via jumper wires.
|
||||||
|
|
||||||
|
Breadboard keyboard assembly instructions:
|
||||||
|
|
||||||
|
1. Cut leads to length.
|
||||||
|
* tactile-switch-lead length 6 to 8 mm
|
||||||
|
* diodes 22 to 24 mm total end-to-end length, and save the cut offs, they will be used in steps 2 and 3
|
||||||
|
2. Insert parts into the breadboard as shown in the picture.
|
||||||
|
* orient the switches such that the leads are on separate terminal strips
|
||||||
|
* orient diodes with cathode (banded end) towards the row (power strip)
|
||||||
|
* use the diode cut offs to connect switches into columns
|
||||||
|
3. Insert jumper wires connecting Teensy2 to the matrix rows and columns.
|
||||||
|
* follow pin connections table (below) and consult pinout diagram in
|
||||||
|
[Connecting Teensy 2.0 to a Keyboard](connecting_teensy2_to_keyboard.md)
|
||||||
|
|
||||||
|
**Teensy 2.0 pin connections table**
|
||||||
|
|
||||||
|
| Pin number | Row Column |
|
||||||
|
|------------|-------------|
|
||||||
|
| 21 | row_0 | todo this table might not match the sketches
|
||||||
|
| 20 | row_1 |
|
||||||
|
| 0 | col_0 |
|
||||||
|
| 1 | col_1 |
|
||||||
|
|
||||||
|
## Compiling and loading the keyboard firmware
|
||||||
|
Follow the [keybrd Library User's Guide](todo /doc/keybrd_library_user_guide.md) to set up the Arduino environment and to compile and load keybrd firmware onto the keyboard's controller.
|
||||||
|
|
||||||
|
## How a keyboard matrix works
|
||||||
|
Now that you have built your first breadboard keyboard, you can dig in and learn how it actually works.
|
||||||
|
This excellent article explains how the microcontroller, matrix, switches and diodes work together:
|
||||||
|
[How a Key Matrix Work](http://pcbheaven.com/wikipages/How_Key_Matrices_Works/)
|
||||||
|
|
||||||
|
## Bigger breadboard keyboards
|
||||||
|
Sometimes its useful to prototype the full keyboard matrix before designing the PCB.
|
||||||
|
Several breadboards can be tied together into one.
|
||||||
|
|
||||||
|
[big breadboard keyboard](breadboard_big.jpg "breadboard_big.jpg")
|
16
tutorials/tutorial_2_single-layer_keyboard.md
Normal file
16
tutorials/tutorial_2_single-layer_keyboard.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Tutorial 2 - single-layer keyboard
|
||||||
|
=======================================
|
||||||
|
This annotated sketch explains how the keybrd library works:
|
||||||
|
[keybrd_2_single-layer_annotated.ino](keybrd_proj/keybrd/examples/tutorials/keybrd_2_single-layer_annotated/keybrd_2_single-layer_annotated.ino)
|
||||||
|
|
||||||
|
You can view the class definitions in the [keybrd library](keybrd/src/).
|
||||||
|
|
||||||
|
After reading the sketch you will be to modify it to suite your own single-layer keyboard design.
|
||||||
|
|
||||||
|
## Exercises
|
||||||
|
1) Add a third column to the breadboard and sketch.
|
||||||
|
|
||||||
|
| Layout | **0** | **1** | **2** |
|
||||||
|
|:------:|-------|-------|-------|
|
||||||
|
| **0** | a | b | c |
|
||||||
|
| **1** | 1 | 2 | shift |
|
66
tutorials/tutorial_3a_multi-layer_keyboard.md
Normal file
66
tutorials/tutorial_3a_multi-layer_keyboard.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
Tutorial 3a - multi-layer keyboard
|
||||||
|
==================================
|
||||||
|
After reading this tutorial you will be able to be able to modify a multi-layer keybrd sketch to suite your own multi-layer keyboard design.
|
||||||
|
|
||||||
|
## Multi-layer nomenclature
|
||||||
|
**[layers](http://deskthority.net/wiki/Layer)** are key bindings provided by the keyboard firmware. For example,
|
||||||
|
* The full-size [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 used to identify a layer.
|
||||||
|
|
||||||
|
**active layer** - is the layer currently used by the keyboard.
|
||||||
|
|
||||||
|
**layer scheme** - is a system for changing layers while typing (a single-layer scheme does not change layers).
|
||||||
|
|
||||||
|
## A simple multi-layer keybrd sketch
|
||||||
|
This annotated sketch demonstrates the multi-layer feature:
|
||||||
|
[keybrd_3_multi-layer_annotated.ino](keybrd_proj/keybrd/examples/tutorials/keybrd_3_multi-layer_annotated/keybrd_3_multi-layer_annotated.ino)
|
||||||
|
|
||||||
|
## Layer scheme classes
|
||||||
|
The walkthrough example covered the most basic classes.
|
||||||
|
This section takes a general view of layer scheme classes.
|
||||||
|
You can view all the class definitions in the [keybrd library](keybrd/src/).
|
||||||
|
|
||||||
|
### StateLayer
|
||||||
|
StateLayer object has an active layer. StateLayer keeps its active layer up to date.
|
||||||
|
There is only one StateLayer class:
|
||||||
|
* StateLayer
|
||||||
|
|
||||||
|
### Layer
|
||||||
|
Layer objects control the active layer. For example, there could be one layer key for each layer.
|
||||||
|
When a Layer object is pressed, it tells StateLayer to change the active layer.
|
||||||
|
Example Layer classes include:
|
||||||
|
* Code_LayerHold
|
||||||
|
* Code_LayerLock
|
||||||
|
|
||||||
|
### Multi-layered
|
||||||
|
Layered objects contain one scancode for each layer.
|
||||||
|
When a Layered object is pressed, it gets the active layer from StateLayer, and then sends the scancode of the active layer.
|
||||||
|
Example Layered classes include:
|
||||||
|
* Code_LayeredScSc
|
||||||
|
* Code_LayeredCodeSc
|
||||||
|
* Code_LayeredCodeCode
|
||||||
|
* Key_LayeredKeysArray
|
||||||
|
|
||||||
|
## Single-layer Codes
|
||||||
|
Most Code objects only have one scancode or one layer code.
|
||||||
|
They do are not affected by the active layer.
|
||||||
|
Example single-layer Code classes include:
|
||||||
|
* Code_Sc
|
||||||
|
* Code_ScS
|
||||||
|
* Code_ScNS
|
||||||
|
* Code_Shift
|
||||||
|
* Code_LayerHold
|
||||||
|
* Code_LayerLock
|
||||||
|
|
||||||
|
(Future version of keybrd library may change all Code classes to Key classes.)
|
||||||
|
|
||||||
|
## Exercises
|
||||||
|
1) Modify the keybrd_3_multi-layer_annotated.ino sketch to use two Code_LayerLock objects.
|
||||||
|
|
||||||
|
| Layout | **0** | **1** |
|
||||||
|
|:------:|--------|--------|
|
||||||
|
| **0** | a 1 | b 2 |
|
||||||
|
| **1** | layer0 | layer1 |
|
23
tutorials/tutorial_3b_autoShift.md
Normal file
23
tutorials/tutorial_3b_autoShift.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Tutorial 3b - autoShift
|
||||||
|
=======================
|
||||||
|
After reading this tutorial your keyboard will be able to be able to automatically shifted characters.
|
||||||
|
|
||||||
|
## AutoShift
|
||||||
|
Some mulit-layer keyboards have a symbols layer that writes symbols and numbers without using the shift key:
|
||||||
|
|
||||||
|
~ ! @ # $ % ^ & * () _ {} | < > ? 1 2 3 4 5 6 7 8 9 0
|
||||||
|
|
||||||
|
The keybrd library does this by automatically sending the MODIFIERKEY_SHIFT scancode.
|
||||||
|
This annotated sketch demonstrates the AutoShift feature: [keybrd_3_autoShift_annotated.ino](keybrd_proj/keybrd/examples/keybrd_3_autoShift_annotated/keybrd_3_autoShift_annotated.ino)
|
||||||
|
|
||||||
|
Two keybrd classes use AutoShift:
|
||||||
|
* Code_ScS
|
||||||
|
* Code_ScNS
|
||||||
|
|
||||||
|
## Exercises
|
||||||
|
1) Modify the keybrd_3_autoShift_annotated sketch to make a 3-layer keyboard with two Code_LayerHold objects.
|
||||||
|
|
||||||
|
| Layout | **0** | **1** |
|
||||||
|
|:------:|-------|-------|
|
||||||
|
| **0** | a ! 6 | b @ 7 |
|
||||||
|
| **1** | sym | num |
|
89
tutorials/tutorial_4_split_keyboard_with_IOE.md
Normal file
89
tutorials/tutorial_4_split_keyboard_with_IOE.md
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
keybrd Tutorial 4 - split keyboard with I/O Expander
|
||||||
|
====================================================
|
||||||
|
After reading this tutorial you will be able to be able to modify a 2-matrix keybrd sketch to suite your own split keyboard design.
|
||||||
|
|
||||||
|
## Overview of split keyboard with I/O Expander
|
||||||
|
The breadboard in this picture models a split keyboard.
|
||||||
|
![breadboard keyboard with 2 rows and 4 columns of keys]
|
||||||
|
(images/breadboard_keyboard_2x4_labeled.jpg "2x5 breadboard keyboard")
|
||||||
|
|
||||||
|
The right matrix is connected to a microcontroller.
|
||||||
|
The left matrix is connected to a I/O expander.
|
||||||
|
|
||||||
|
There is a total of 4 matrix rows, each on a dedicated power rail.
|
||||||
|
|
||||||
|
The microcontroller and I/O expander communicate by [I2C](http://en.wikipedia.org/wiki/I%C2%B2C) via 4 jumper wires:
|
||||||
|
* ground
|
||||||
|
* power
|
||||||
|
* Serial CLock input (SCL)
|
||||||
|
* Serial DAta I/O (SDA)
|
||||||
|
|
||||||
|
The two resistors near the microcontroller pull-up voltage on the SCL and SDA pins.
|
||||||
|
|
||||||
|
The I/O expander has a small notch on one end, which identifies the end with pin 1.
|
||||||
|
In the picture, pin 1 is on the right end.
|
||||||
|
|
||||||
|
## Building a split keyboard with I/O Expander
|
||||||
|
The split keyboard is built on the Basic Breadboard Keyboard described in
|
||||||
|
tutorial_0_keybrd_breadboard.md > Building a Basic Breadboard Keyboard
|
||||||
|
|
||||||
|
Follow these instructions to add a second matrix to the Basic Breadboard Keyboard:
|
||||||
|
4. Insert I2C jumper wires and pull-up resistors connecting to Teensy2.
|
||||||
|
* follow the I2C and pull-up resistors tables (below) and consult Teensy pinout diagram in
|
||||||
|
[Connecting Teensy 2.0 to a Keyboard](connecting_teensy2_to_keyboard.md)
|
||||||
|
|
||||||
|
todo these tables might not match the sketch
|
||||||
|
|
||||||
|
**Teensy 2.0 pin connections tables**
|
||||||
|
|
||||||
|
| Pin Number | Row Column |
|
||||||
|
|------------|-------------|
|
||||||
|
| 21 | row_R0 |
|
||||||
|
| 20 | row_R1 |
|
||||||
|
| 0 | col_R0 |
|
||||||
|
| 1 | col_R1 |
|
||||||
|
|
||||||
|
| Pin Number | I2C |
|
||||||
|
|------------|-------------|
|
||||||
|
| GND | ground |
|
||||||
|
| VCC | power |
|
||||||
|
| 5 | SCL |
|
||||||
|
| 6 | SDA |
|
||||||
|
|
||||||
|
| Pin Number | 4.7K Ohms Pull-up Resistor |
|
||||||
|
|------------|-------------|
|
||||||
|
| 5 | VCC |
|
||||||
|
| 6 | VCC |
|
||||||
|
|
||||||
|
5. Insert jumper wires to connect MCP23018 I/O expander
|
||||||
|
* follow pin connections tables (below) and consult pinout diagram in
|
||||||
|
[Connecting MCP23018 I/O Expander to a Keyboard](connecting_MCP23018_to_keyboard.md)
|
||||||
|
|
||||||
|
**MCP23018 I/O expander pin connections tables**
|
||||||
|
|
||||||
|
| Pin Number | Row Column |
|
||||||
|
|------------|-------------|
|
||||||
|
| 3 | row_L0 |
|
||||||
|
| 4 | row_L1 |
|
||||||
|
| 20 | col_L0 |
|
||||||
|
| 21 | col_L1 |
|
||||||
|
| 22 | col_L2 |
|
||||||
|
|
||||||
|
| Pin Number | I2C |
|
||||||
|
|------------|-------------|
|
||||||
|
| 1 | ground |
|
||||||
|
| 11 | power |
|
||||||
|
| 12 | SCL |
|
||||||
|
| 13 | SDA |
|
||||||
|
|
||||||
|
| Pin Number | Jump to Pin |
|
||||||
|
|------------|-------------|
|
||||||
|
| 11 | 16 |
|
||||||
|
| 1 | 15 |
|
||||||
|
|
||||||
|
todo add capacitor
|
||||||
|
|
||||||
|
## Sketch for split keyboard with I/O Expander
|
||||||
|
An annotated sketch for the split keyboard with I/O Expander is on
|
||||||
|
[keybrd_4_split_with_IOE_annotated.ino](keybrd_4_split_with_IOE_annotated/keybrd_4_split_with_IOE_annotated.ino)
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
Tutorial 7a - using someone else's keybrd extension library
|
||||||
|
========================================================
|
||||||
|
The keybrd library contains the foundation classes for creating a keyboard firmware.
|
||||||
|
keybrd extension libraries extend the main keyboard library.
|
||||||
|
|
||||||
|
keybrd extension library names are prefixed by "keybrd_" and are listed in:
|
||||||
|
* [Arduino Playground](http://playground.arduino.cc/Main/InterfacingWithHardware#keyb) > find "keybrd"
|
||||||
|
* Arduino Library-Manager (Arduino IDE > Sketch > Include Library > Manage Libraries > Filter your search: keybrd)
|
||||||
|
|
||||||
|
Instructions for installing a library are at:
|
||||||
|
http://www.arduino.cc/en/Guide/Libraries
|
@ -0,0 +1,80 @@
|
|||||||
|
Tutorial 7b - creating and publishing your own keybrd extension library
|
||||||
|
=======================================================================
|
||||||
|
Listing your keybrd extension library allows others to find and install your library.
|
||||||
|
The keybrd extension library name should start with "keybrd_" so that it is easy for people to find.
|
||||||
|
The directory structure of the library depends on where it will be listed.
|
||||||
|
|
||||||
|
## Publishing anywhere with listing on Arduino Playground LibraryList
|
||||||
|
Arduino Playground LibraryList can list a library with any directory structure.
|
||||||
|
The directory structure of your keybrd extension library can be as simple as:
|
||||||
|
|
||||||
|
keybrd_MyKeyboard/
|
||||||
|
class1.cpp
|
||||||
|
class1.h
|
||||||
|
class2.cpp
|
||||||
|
class2.h
|
||||||
|
..
|
||||||
|
instantiations_codes.h
|
||||||
|
instantiations_ports.h
|
||||||
|
instantiations_matrix.h
|
||||||
|
doc/
|
||||||
|
keybrd_MyKeyboard_guide
|
||||||
|
examples/
|
||||||
|
keybrd_MyKeyboard1/
|
||||||
|
keybrd_MyKeyboard1.ino
|
||||||
|
keybrd_MyKeyboard2/
|
||||||
|
keybrd_MyKeyboard2.ino
|
||||||
|
|
||||||
|
[Arduino playground](http://playground.arduino.cc/) is a wiki.
|
||||||
|
Instructions for listing a library on the Arduino playgound LibraryList are at:
|
||||||
|
http://playground.arduino.cc/Code/Library#Sharing
|
||||||
|
|
||||||
|
Add your keybrd library to the Keyboard/Keypads sublist:
|
||||||
|
http://playground.arduino.cc/Main/InterfacingWithHardware#keyb
|
||||||
|
|
||||||
|
## Publishing on GitHub with listing on Arduino Library-Manager and Arduino Playground LibraryList
|
||||||
|
The advantage of using GitHub is that users can submit pull requests.
|
||||||
|
The advantage of using Arduino Library-Manager is that users can install your library through the Arduino IDE.
|
||||||
|
|
||||||
|
Arduino Library-Manager is particular about directory structures it accepts.
|
||||||
|
The directory structure of your keybrd extension library to look like this:
|
||||||
|
|
||||||
|
keybrd_MyKeyboard/
|
||||||
|
library.properties
|
||||||
|
doc/
|
||||||
|
keybrd_MyKeyboard_guide
|
||||||
|
examples/
|
||||||
|
keybrd_MyKeyboard1/
|
||||||
|
keybrd_MyKeyboard1.ino
|
||||||
|
keybrd_MyKeyboard2/
|
||||||
|
keybrd_MyKeyboard2.ino
|
||||||
|
src/
|
||||||
|
class1.cpp
|
||||||
|
class1.h
|
||||||
|
class2.cpp
|
||||||
|
class2.h
|
||||||
|
..
|
||||||
|
instantiations_codes.h
|
||||||
|
instantiations_ports.h
|
||||||
|
instantiations_matrix.h
|
||||||
|
|
||||||
|
The library.properties file is described in
|
||||||
|
https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-specification
|
||||||
|
Example library.properties file:
|
||||||
|
name=keybrd_MyKeyboard
|
||||||
|
version=1.2.3
|
||||||
|
author=Me
|
||||||
|
maintainer=Me
|
||||||
|
sentence=An extension to the keybrd library for the My keyboard.
|
||||||
|
paragraph=This library demonstrates my feature.
|
||||||
|
category=Device Control
|
||||||
|
url=https://github.com/Me/keybrd_MyKeyboard
|
||||||
|
architectures=Teensy LC
|
||||||
|
|
||||||
|
Instructions for listing a library on Arduino Library Manager are at:
|
||||||
|
https://github.com/arduino/Arduino/wiki/Library-Manager-FAQ
|
||||||
|
|
||||||
|
After it has been accepted into the Arduino IDE Library Manager, add your library to the Arduino Playground LibraryList.
|
||||||
|
[Arduino playground](http://playground.arduino.cc/) is a wiki.
|
||||||
|
Sign in at http://playground.arduino.cc/Main/LibraryList and add keybrd libraries to Keyboard/Keypads sublist:
|
||||||
|
http://playground.arduino.cc/Main/InterfacingWithHardware#keyb
|
@ -0,0 +1,32 @@
|
|||||||
|
Tutorial 8 - breaking up a sketch into smaller files
|
||||||
|
====================================================
|
||||||
|
A keybrd sketch can become quite lengthy, which can make it harder to navigate and understand.
|
||||||
|
|
||||||
|
The keybrd_DH sketch has about 800 lines of code; 700 of which are for instantiating objects.
|
||||||
|
The object instantiations are grouped into four files located in the keybrd_DH library, and included in keybrd_DH.ino:
|
||||||
|
|
||||||
|
// ========= OBJECT INSTANTIATIONS =============
|
||||||
|
#include <instantiations_ports.h>
|
||||||
|
#include <instantiations_LEDs.h>
|
||||||
|
#include <instantiations_codes.h>
|
||||||
|
#include <instantiations_matrix.h>
|
||||||
|
|
||||||
|
Splitting your code into groups of instantiations also provides organizational and reusability benefits.
|
||||||
|
|
||||||
|
Example 1.
|
||||||
|
You have three versions of LED indicators you are experimenting with:
|
||||||
|
instantiations_LEDs_1.h
|
||||||
|
instantiations_LEDs_2.h
|
||||||
|
instantiations_LEDs_3.h
|
||||||
|
|
||||||
|
Example 2.
|
||||||
|
You use Colemak and want QWERTY users to to try your keyboard design.
|
||||||
|
So you publish your keybrd extension library with two versions of instantiations_matrix.h:
|
||||||
|
instantiations_matrix_QWERTY.h
|
||||||
|
instantiations_matrix_colemak.h
|
||||||
|
|
||||||
|
Example 3.
|
||||||
|
Someone wants to try the layout in your keybrd extension library, but their controller and matrix are different.
|
||||||
|
So they replace two of your object instantiation files with their own:
|
||||||
|
instantiations_ports.h
|
||||||
|
instantiations_matrix.h
|
Reference in New Issue
Block a user