@@ -16,11 +16,12 @@ or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | |||
Bug reports | |||
----------- | |||
A bug report is the first step in making the keybrd library work the way it's supposed to work. | |||
Submit bug reports to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | |||
A bug report is the first step in finding a bug. | |||
Once it is found, correcting it is usually relatively easy. | |||
Please submit bug reports to [GitHub issues](https://github.com/wolfv6/Keybrd/issues) | |||
or [geekhack thread](https://geekhack.org/index.php?topic=83599.0). | |||
Please provide enough information so we can reproduce the bug behaviour! | |||
Provide enough information so we can reproduce the buggy behaviour! | |||
* Complete sketch (copy & paste, attachment, or a link to the code) | |||
* Screenshot or the exact text of error messages | |||
* Describe the observed behavior and explain which behavior you expected | |||
@@ -48,20 +49,39 @@ Any project requires various kinds of contributions to succeed. | |||
A thriving project is more than a pile of code. | |||
It's the packaging, explanation, outreach, and empathy of maintainers that make a good project great. | |||
User Contributions can be in the form of: | |||
* Blog - You have a fresh perspective of how the keybrd library works. | |||
This makes you the perfect person to write an introductory blog explaining the project. | |||
A healthy project needs the perspective of many people. | |||
* Documentation - Suggest a clarification, simplification, correction, or other improvement. | |||
### Beta testing | |||
keybrd library has been Alpha tested on the DodoHand keyboard, tutorial sketches, Teensy 2.0, and Teensy LC. What we need now are Beta testers: | |||
* use the tutorials (feedback from noobs is especially valuable) | |||
* use the keybrd library to implement your own keyboard design | |||
Feedback from Beta testers will be used to make improvements to the keybrd library. | |||
### Schematics | |||
The most glaring deficiency is the tutorials' lack of schematics. | |||
Schematics would be an improvement over the current photos. | |||
Schematics are not my area of expertise. Use what ever you think would be the best solution for the tutorials: | |||
* schematics | |||
* breadboard drawings | |||
* Arduino simulation software | |||
Contributions of tutorial schematics would benefit users new to the keybrd library. | |||
### Documentation | |||
Suggest a clarification, simplification, correction, or other improvement. | |||
We need the perspective of people new to the project to see these things. | |||
Sometimes just changing a word or two makes a big difference. | |||
* [Current user contributions](https://geekhack.org/index.php?topic=83599.msg2223776#msg2223776) highlights contributions that are needed for the keybrd project's current stage of development. | |||
Text file documentation style guide: | |||
* Use Markdown with a .md suffix. | |||
* "Underline" first-level (=) and second-level (-) headings (because easier to read in plain text). | |||
* Capitalize first letter of headings (no extra capitalization in headings). | |||
### Blog | |||
You have a fresh perspective of how the keybrd library works. | |||
This makes you the perfect person to write an introductory blog explaining the project. | |||
A healthy project needs the perspective of many people. | |||
Submitting a pull request | |||
------------------------- | |||
Pull request is the preferred way to contribute code and documentation. |
@@ -27,6 +27,7 @@ Example minimal keybrd sketch | |||
----------------------------- | |||
A [minimal keybrd sketch](/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino) | |||
is 40 lines of code for a 4-key keyboard. | |||
It scans a key matrix just like the big keyboards. | |||
The sketch is small because the keybrd library takes care of the low-level details. | |||
It runs the breadboard keyboard in this picture. | |||
@@ -34,9 +35,11 @@ It runs the breadboard keyboard in this picture. | |||
Example complex keybrd sketch | |||
----------------------------- | |||
keybrd_DH and its instantiation files contain about 800 lines of code. | |||
It emulates the DataHand keyboard. | |||
The keybrd_DH sketch is a showcase of the keybrd library's capability. | |||
It emulates the DataHand keyboard, which has the most complex layout I know of. | |||
Its layout has 52 keys, 3 primary layers, 5 sub-layers, 2 matrices, 8 LEDs, and blinking LEDs. | |||
Most layouts are much simpler. | |||
keybrd_DH and its instantiation files contain about 800 lines of code. | |||
[keybrd_DH_library_developer_guide.md](https://github.com/wolfv6/keybrd_DH/blob/master/doc/keybrd_DH_library_developer_guide.md)<br> | |||
[mainSketch.ino](https://github.com/wolfv6/keybrd_DH/blob/master/examples/keybrd_DH/mainSketch.cpp)<br> |
@@ -6,11 +6,34 @@ This project adheres to [Semantic Versioning 2.0.0](http://semver.org/). | |||
keybrd version 0.x.x is for initial development. | |||
keybrd version 1.0.0 will be released when the public API is stable. | |||
<!-- | |||
< !-- | |||
Unreleased | |||
---------- | |||
--> | |||
0.6.0 (2016-09-28) | |||
------------------ | |||
* Enhancements | |||
* Add Port_MCP23S17 | |||
* Add Scanner_ShiftRegsPISOSingleRow and Scanner_ShiftRegsPISOMultiRow | |||
* Refine and update tutorials | |||
* Add tutorial_3cde_sublayer_keyboard.md | |||
* Add tutorial_4_connecting_split_keyboards.md | |||
* Add tutorial_4b_split_keyboard_with_shift_registers.md | |||
* Add tutorial_4c_split_keyboard_with_IOE.md | |||
* Add tutorial_5b_LED_on_IOE.md | |||
* Backward incompatible changes | |||
* Move scanner instantiation from Row_* to sketch | |||
* Delete Row_uC and Row_IOE, and replace them with Row | |||
* Combine PortWrite_PCA9655E and PortRead_PCA9655E into Port_PCA9655E | |||
* Delete PortIOE, and move PortIOE variables to IOE port classes | |||
* Rename LED_PCA9655E to LED_Port | |||
* Rename Scanner_Port to Scanner_IOE | |||
* Rename Key_LayeredKeysArray to Key_LayeredKeys | |||
* Rename Code_Layered* to Key_Layered* | |||
* Rename object_*.h files to instantiations_*.h | |||
0.5.0 (2016-07-22) | |||
------------------ | |||
* Enhancements |
@@ -24,8 +24,6 @@ Keybrd library class inheritance diagram | |||
Scanner_uC Scanner_IOE Scanner_ShiftRegsPISO | |||
PortIOE | |||
PortInterface | |||
/ \ | |||
Port_PCA9655E Port_MCP23S17 (one Port class for each IOE type) | |||
@@ -33,7 +31,7 @@ Keybrd library class inheritance diagram | |||
LEDInterface | |||
/ \ | |||
LED_uC LED_IOE | |||
LED_uC LED_Port | |||
DebouncerInterface | |||
@@ -118,7 +116,7 @@ Dependency diagram of example I/O expander matrix with LEDs | |||
/ | \ / \ | |||
strobePin PortWrite PortRead Code Code_LEDLock | |||
| \ / \ | | |||
| PortIOE readPins LED_IOE | |||
| PortIOE readPins LED_Port | |||
\___________________________/ \ | |||
pin | |||
@@ -115,36 +115,25 @@ Example keybrd sketches | |||
Example keybrd sketches are in the examples and tutorials directories. | |||
Extension libraries have their example sketches similarly located. | |||
The example sketch names use the following conventions. | |||
**keybrd_feature_version.ino** | |||
where | |||
* **keybrd** is the library name e.g. keybrd, keybrd_DH | |||
* **feature** is a distinguishing feature of the keybrd sketch e.g. keyboard name, sound, layout | |||
* **version** is the sketch's version number (optional) | |||
Active state and diode orientation | |||
---------------------------------- | |||
Active state is set in the sketch by variables STROBE_ON and STROBE_OFF. | |||
Active state is set in the sketch by the scanner. | |||
The following instructions are for setting active state for a Scanner_uC class | |||
(Scanner_ShiftRegs74HC165 and Scanner_Port classes is similar). | |||
For active low: | |||
* Orient diodes with cathode (banded end) towards the write pins (row) | |||
* Define strobe on and strobe off in the sketch like this: | |||
* Instantiate the scanner in the sketch with strobeOn LOW, like this: | |||
``` | |||
const bool Scanner_uC::STROBE_ON = LOW; | |||
const bool Scanner_uC::STROBE_OFF = HIGH; | |||
Scanner_uC scanner(LOW, readPins, readPinCount); | |||
``` | |||
For active high: | |||
* Add an external 10k pull-down resistor to each read pin. | |||
* Orient diodes with cathode (banded end) towards the read pins. | |||
* Define strobe on and strobe off in the sketch like this: | |||
* Instantiate the scanner in the sketch with strobeOn HIGH, like this: | |||
``` | |||
const bool Scanner_uC::STROBE_ON = HIGH; | |||
const bool Scanner_uC::STROBE_OFF = LOW; | |||
Scanner_uC scanner(HIGH, readPins, readPinCount); | |||
``` | |||
Troubleshooting check list |
@@ -1,24 +0,0 @@ | |||
#ifndef LED_IOE_H | |||
#define LED_IOE_H | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <Wire.h> | |||
#include <LEDInterface.h> | |||
#include <PortInterface.h> | |||
/* A LED_IOE object is an I/O expander pin that is connected to an LED indicator light. | |||
Input/Ouput Direction configuration are set to ouput in PortWrite_*.begin() and PortRead_*.begin(). todo PortRead_*?? | |||
*/ | |||
class LED_IOE : public LEDInterface | |||
{ | |||
private: | |||
PortInterface& refPort; | |||
const uint8_t pin; //bit pattern, 1 is IOE pin to LED | |||
public: | |||
LED_IOE(PortInterface& refPort, const uint8_t pin) | |||
: refPort(refPort), pin(pin) {} | |||
virtual void on(); | |||
virtual void off(); | |||
}; | |||
#endif |
@@ -1,11 +1,11 @@ | |||
#include "LED_IOE.h" | |||
#include "LED_Port.h" | |||
void LED_IOE::on() | |||
void LED_Port::on() | |||
{ | |||
refPort.write(pin, HIGH); | |||
} | |||
void LED_IOE::off() | |||
void LED_Port::off() | |||
{ | |||
refPort.write(pin, LOW); | |||
} |
@@ -0,0 +1,28 @@ | |||
#ifndef LED_PORT_H | |||
#define LED_PORT_H | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <Wire.h> | |||
#include <LEDInterface.h> | |||
#include <PortInterface.h> | |||
/* An LED_Port object is an I/O expander pin that is connected to an LED indicator light. | |||
Example initialization: | |||
const uint8_t IOE_ADDR = 0x20; | |||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); | |||
LED_Port LED_fn(portA, 1<<5); | |||
*/ | |||
class LED_Port : public LEDInterface | |||
{ | |||
private: | |||
PortInterface& refPort; | |||
const uint8_t pin; //bit pattern, 1 is IOE pin to LED | |||
public: | |||
LED_Port(PortInterface& refPort, const uint8_t pin) | |||
: refPort(refPort), pin(pin) {} | |||
virtual void on(); | |||
virtual void off(); | |||
}; | |||
#endif |
@@ -8,6 +8,9 @@ | |||
/* Basic LayerState with layer LED indictor lights. | |||
begin() should be called once to turn on LED for initial active layer. | |||
If LED is on Scanner_IOE, LayerState_LED::begin() should be called after Scanner_IOE::begin() | |||
so that scanner's ports can turn on LayerState_LED's default-layer LED. | |||
*/ | |||
class LayerState_LED : public LayerState | |||
{ |
@@ -22,7 +22,7 @@ readPins parameter configures port's pins. | |||
Example instantiation: | |||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||
Port_MCP23S17 portB(IOE_ADDR, 1, 0); //all pins are set to output for strobes and LEDs | |||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //first two pins are set to input for reading, | |||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //pin 0 and pin 1 are set to input for reading, | |||
//remaining pins can be used for LEDs | |||
Diode orientation |
@@ -22,7 +22,7 @@ Instantiation | |||
Example instantiation: | |||
const uint8_t IOE_ADDR = 0x20; //PCA9655E address, all 3 ADDR pins are grounded | |||
Port_PCA9655E portB(IOE_ADDR, 1, 0); //all pins are set to output for strobes and LEDs | |||
Port_PCA9655E portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //first two pins are set to input for reading, | |||
Port_PCA9655E portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //pin 0 and pin 1 are set to input for reading, | |||
//remaining pins can be used for LEDs | |||
Diode orientation |
@@ -2,13 +2,20 @@ | |||
#define CONFIG_KEYBRD_H | |||
#include <inttypes.h> | |||
/* size of read_pins_t depends on the maximum number of pins scanned by RowScanner. | |||
By default, read_pins_t is set to the largest type. | |||
/* The maximum number of pins scanned by RowScanner depends on size of read_pins_t. | |||
By default, read_pins_t is set to uint32_t. | |||
If your 8-bit AVR (Teensy 2) is running low on memory, using a smaller type saves SRAM. | |||
Using smaller types on a 32-bit uC (Teensy LC) would accomplish nothing. | |||
*/ | |||
/* Use a read_pins_t size that covers all read pins of all Scanner objects i.e. | |||
read_pins_t is used in: | |||
Row bit patterns | |||
ScannerInterface::scan() | |||
Scanner_ShiftRegsPISO::scan() | |||
Scanner_uC::scan() | |||
DebouncerInterface::debounce() | |||
Debouncer_Samples::debounce() | |||
Use a read_pins_t size that covers all read pins of all Scanner objects i.e. | |||
For Scanner_uC: read_pins_t bits >= Scanner_uC::readPinCount | |||
For Scanner_ShiftRegsPISO: read_pins_t bits >= Scanner_ShiftRegsPISO::byte_count * 8 | |||
(For Scanner_IOE: I/O expanders are assumed to have 8 bits per port or less) |
@@ -1,3 +1,6 @@ | |||
#ifndef INSTANTIATIONS_SCANCODE_H | |||
#define INSTANTIATIONS_SCANCODE_H | |||
/* Include this file in multiple-layer keybrd sketches. | |||
This file instandiates Code objects. | |||
@@ -1,3 +1,6 @@ | |||
#ifndef INSTANTIATIONS_SCANCODENOTSHIFTED_H | |||
#define INSTANTIATIONS_SCANCODENOTSHIFTED_H | |||
/* 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. |
@@ -47,14 +47,15 @@ uint8_t readPins[] = {14, 15}; | |||
uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||
/* | |||
The first parameter of the scanner constructor defines the logic level for the strobes. | |||
Scanner_uC constructor parameters are: strobeOn, readPins[], readPinCount. | |||
strobeOn defines the logic level for strobes, HIGH or LOW. | |||
"Active low" means that if a switch is pressed (active), the read pin is low. | |||
The scanner uses readPins, readPinCount to read the colums. | |||
The scanner uses readPins and readPinCount to read the colums. | |||
*/ | |||
Scanner_uC scanner(LOW, readPins, readPinCount); | |||
/* HOW SCANNER OBJECTS WORK | |||
The scanner object strobes a row. | |||
The Scanner object strobes a row. | |||
If a key is pressed, the LOW strobe pulls that readPin LOW. | |||
Then the scanner reads its readPins. | |||
*/ | |||
@@ -76,11 +77,9 @@ Code_Sc s_2(KEY_2); | |||
Here we pack Code objects into Row objects. | |||
The Row objects names in this sketch start with a "row_" followed by a row number. | |||
Row constructor has four parameters: | |||
1) scanner | |||
2) strobePin connected to the row. | |||
3) ptrsKeys[] containing all the Code objects of the row, one Code object per key. | |||
4) the number of keys in the row. | |||
Row constructor parameters are: scanner, strobePin, ptrsKeys[], keyCount. | |||
strobePin is the Arduino pin number connected to the row. | |||
ptrsKeys[] contains all the Code objects of the row, one Code object per key. | |||
*/ | |||
Key* ptrsKeys_0[] = { &s_1, &s_2 }; | |||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | |||
@@ -100,7 +99,7 @@ void setup() | |||
/* | |||
loop() continually scans the matrix, one row at a time. | |||
Each row object strobes its strobePin and reads the readPins. | |||
Each Row object strobes its strobePin and reads the readPins. | |||
And when a key press is detected, the row sends the key's scancode. | |||
scanDelay creates time intervals between matrix scans. |
@@ -10,10 +10,11 @@ This sketch: | |||
| **1** | fn | shift | | |||
Each cell in the table's body represents a key. | |||
The keys in column 1 have two characters each, one character for each layer. | |||
Each element in a cell represents a scancode or layer code. | |||
The keys in row 0 have two characters each, one character for each layer. | |||
Letters 'a' and 'b' are on the normal layer. Symbols '-' and '=' are on the fn layer. | |||
"fn" is a layer key. Holding the fn key down makes it the active layer. | |||
Releasing the fn key restores the normal layer. | |||
Letters 'a' and 'b' are on the normal layer. Symbols '-' and '=' are on the fn layer. | |||
*/ | |||
// ################## GLOBAL ################### | |||
// ================= INCLUDES ================== | |||
@@ -40,7 +41,7 @@ Scanner_uC scanner(LOW, readPins, readPinCount); | |||
// =================== CODES =================== | |||
/* ---------------- LAYER CODE ----------------- | |||
enum assigns layerId numbers to the layers. | |||
NORMAL=0 and FN=1. LayerState's default layerId is 0. | |||
NORMAL=0 and FN=1. | |||
*/ | |||
enum layerIds { NORMAL, FN }; | |||
@@ -50,12 +51,14 @@ layerState keeps track of the active layer. | |||
LayerState layerState; | |||
/* | |||
The Code_LayerHold constructor has two parameters: | |||
1) the layerId that will be the active layer while the key is held down | |||
2) a LayerState that will keep track of the active layer | |||
When l_fn is pressed, it tells layerState to change the active layer to FN. | |||
Code_LayerHold constructor parameters are: layerId, LayerState. | |||
layerState is assigned to layer FN. | |||
layerState also has a default layer 0, which implicitly is layer NORMAL. | |||
FN is the active layer while the key is held down. | |||
In this example, when l_fn is pressed, it tells layerState to change the active layer to FN. | |||
When l_fn is released, it tells layerState that layer FN is released, | |||
and layerState restores the active layer to NORMAL (sets active layer to the default layerId 0). | |||
and layerState restores the active layer to default layerId 0 (NORMAL). | |||
*/ | |||
Code_LayerHold l_fn(FN, layerState); | |||
@@ -68,7 +71,7 @@ Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT); | |||
/* =================== KEYS ==================== | |||
Here we pack Codes into keys. | |||
The Key_LayeredKeys constructor takes one array of Code pointers - one Code object per layer. | |||
ptrsKeys_00[] contains all the Code objects of the key, one Code object per layer. | |||
The Key object names in this sketch start with a "k_" followed by row-column coordinates. | |||
*/ | |||
@@ -90,13 +93,13 @@ The Code object then sends its scancode over USB. | |||
*/ | |||
/* =================== ROWS ==================== | |||
Here we pack Key pointers into row objects. | |||
Here we pack Key pointers into Row objects. | |||
Rows are composed of a Key-pointer array. | |||
Codes are a kind of Key that only have one layer. | |||
Thus rows can contain a mix of codes and multi-layered keys (subtype polymorphism). | |||
In this example, Key-pointer arrays contain both Code pointers (&s_shift and &l_fn) | |||
and Key pointers (&k_01 and &k_11). | |||
In this example, Key-pointer arrays contain both Code pointers (&l_fn and &s_shift) | |||
and Key pointers (&k_00 and &k_01). | |||
*/ | |||
Key* const ptrsKeys_0[] = { &k_00, &k_01 }; | |||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); |
@@ -44,10 +44,10 @@ enum layerIds { ALPHA, SYM }; | |||
LayerState layerState; | |||
/* | |||
The Code_LayerLock constructor has two parameters: | |||
1) the layerId that becomes the active layer when the key is pressed | |||
2) a LayerState that will keep track of the active layer | |||
When l_normal is pressed, ALPHA becomes the active layer. | |||
Code_LayerLock constructor parameters are: layerId, LayerState. | |||
layerId becomes the active layer when the key is pressed. | |||
LayerState keeps track of the active layer. | |||
In this example, when l_normal is pressed, ALPHA becomes the active layer. | |||
When l_sym is pressed, SYM becomes the active layer. | |||
*/ | |||
Code_LayerLock l_normal(ALPHA, layerState); |
@@ -1,7 +1,7 @@ | |||
/* keybrd_3c_sublayerNull.ino | |||
This sketch: | |||
is firmware for layout with 2 layers plus 1 sublayer. | |||
is firmware for layout with two layers plus one sublayer. | |||
runs on the first three columns of a breadboard keyboard | |||
| Layout | **0** | **1** | **2** | | |||
@@ -35,18 +35,23 @@ uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||
Scanner_uC scanner(LOW, readPins, readPinCount); | |||
// =================== CODES =================== | |||
/* ---------------- LAYER CODE ----------------- | |||
One LayerState object manages all 3 layers. | |||
*/ | |||
enum layerIds { ALPHA, SYM, NUM }; | |||
LayerState layerState; | |||
LayerState groupState; | |||
Code_LayerLock l_normal(ALPHA, layerState); | |||
Code_LayerLock l_sym(SYM, layerState); | |||
Code_LayerHold l_num(NUM, layerState); | |||
/* | |||
groupState is assigned to layers ALPHA, SYM, and NUM. | |||
*/ | |||
Code_LayerLock l_alpha(ALPHA, groupState); | |||
Code_LayerLock l_sym(SYM, groupState); | |||
Code_LayerHold l_num(NUM, groupState); | |||
LayerStateInterface& Key_LayeredKeys::refLayerState = layerState; | |||
/* | |||
groupState is assigned to Key_LayeredKeys. | |||
When a Key_LayeredKeys object is pressed, groupState returns the active layerId. | |||
Thus groupState manages a layer group delineated by all layers in Key_LayeredKeys objects. | |||
*/ | |||
LayerStateInterface& Key_LayeredKeys::refLayerState = groupState; | |||
// ---------------- SCAN CODES ----------------- | |||
Code_Sc s_a(KEY_A); | |||
@@ -61,12 +66,7 @@ Code_Sc s_1(KEY_1); | |||
Code_Null code_null; | |||
/* =================== KEYS ==================== | |||
When a Key_LayeredKeys is pressed, layerState returns the active layerId, | |||
which could be any of the layerIds in l_normal, l_sym, l_num. | |||
The layout has one key with 3 layers, and two keys with 2 layers. | |||
But the layer scheme has 3 layers for all three keys. | |||
The extra layers are filled with duplicate codes and null codes. | |||
The layout's Num layer only covers the first key: s_1 | |||
*/ | |||
Key* const ptrsKeys_00[] = { &s_a, &s_minus, &s_1 }; | |||
Key_LayeredKeys k_00(ptrsKeys_00); | |||
@@ -78,8 +78,8 @@ Key* const ptrsKeys_01[] = { &s_b, &s_equal, &s_equal }; | |||
Key_LayeredKeys k_01(ptrsKeys_01); | |||
/* | |||
code_null occupies layer 2. Class Code_Null doesn't do anything. It is useful for blank codes. | |||
Remember to fill all layers with codes. | |||
code_null occupies layer 2. Class Code_Null doesn't do anything. | |||
It is useful for blank codes. Remember to fill all layers of a Key_Layered object with codes. | |||
If the code_null were omitted from the array, dereferencing ptrsKeys_02[2] could cause a crash. | |||
*/ | |||
Key* const ptrsKeys_02[] = { &s_c, &l_num, &code_null }; | |||
@@ -90,7 +90,7 @@ Key* const ptrsKeys_0[] = { &k_00, &k_01, &k_02 }; | |||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | |||
Row row_0(scanner, 0, ptrsKeys_0, keyCount_0); | |||
Key* const ptrsKeys_1[] = { &l_normal, &l_sym, &s_enter }; | |||
Key* const ptrsKeys_1[] = { &l_alpha, &l_sym, &s_enter }; | |||
uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); | |||
Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | |||
@@ -1,7 +1,7 @@ | |||
/* keybrd_3d_sublayerNested.ino | |||
This sketch: | |||
is firmware for layout with 2 layers plus 1 sublayer. | |||
is firmware for layout with two layers plus one sublayer. | |||
runs on the first three columns of a breadboard keyboard | |||
| Layout | **0** | **1** | **2** | | |||
@@ -34,35 +34,47 @@ uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||
Scanner_uC scanner(LOW, readPins, readPinCount); | |||
// =================== CODES =================== | |||
// ----------------- LAYER CODE ---------------- | |||
/* =================== CODES =================== | |||
Each LayerState object can manage one layer group. This sketch uses two LayerState objects. | |||
*/ | |||
// ---------------- LAYER GROUP ---------------- | |||
enum layers { ALPHA, SYM }; | |||
LayerState layerState; | |||
Code_LayerLock l_normal(ALPHA, layerState); | |||
Code_LayerLock l_sym(SYM, layerState); | |||
/* | |||
groupState keeps track of a layer group's active layer. | |||
*/ | |||
LayerState groupState; | |||
/* | |||
Key_LayeredKeys are associated with layerState. | |||
groupState is assigned to layers ALPHA and SYM. | |||
*/ | |||
LayerStateInterface& Key_LayeredKeys::refLayerState = layerState; | |||
Code_LayerLock l_alpha(ALPHA, groupState); | |||
Code_LayerLock l_sym(SYM, groupState); | |||
/* ---------------- SUBLAYER CODE -------------- | |||
Sublayers are implemented just like primary layers. | |||
/* | |||
groupState manages a layer group delineated by all layers that are in Key_LayeredKeys objects. | |||
*/ | |||
LayerStateInterface& Key_LayeredKeys::refLayerState = groupState; | |||
// --------------- LAYER SUBGROUP -------------- | |||
enum subLayers { SUBSYM, SUBNUM }; | |||
LayerState sublayerState; | |||
/* | |||
subgroupState keeps track of a layer subgroup's active layer. | |||
*/ | |||
LayerState subgroupState; | |||
Code_LayerHold l_num(SUBNUM, sublayerState); | |||
/* | |||
subgroupState is assigned to layer SUBNUM. | |||
subgroupState also has a default layer 0, which implicitly is layer SUBSYM. | |||
*/ | |||
Code_LayerHold l_num(SUBNUM, subgroupState); | |||
/* | |||
Key_LayeredKeys1 are associated with sublayerState. | |||
Key_LayeredKeys (in layer) and Key_LayeredKeys1 (in sublayer) classes are nearly identical, | |||
only the static refLayerState are different. | |||
Key_LayeredKeys and Key_LayeredKeys1 are identical classes with distinct static refLayerState. | |||
subgroupState manages a layer group delineated by all layers that are in Key_LayeredKeys1 objects. | |||
*/ | |||
LayerStateInterface& Key_LayeredKeys1::refLayerState = sublayerState; | |||
LayerStateInterface& Key_LayeredKeys1::refLayerState = subgroupState; | |||
// ---------------- SCAN CODES ----------------- | |||
Code_Sc s_a(KEY_A); | |||
@@ -76,17 +88,17 @@ Code_Sc s_enter(KEY_ENTER); | |||
Code_Sc s_1(KEY_1); | |||
/* =================== KEYS ==================== | |||
The key k_sub00 contains codes for layerIds SUBSYM and SUBNUM. | |||
k_sub00 contains codes for sub layers SUBSYM and SUBNUM. | |||
k_sub00 gets it's active layer from subgroupState. | |||
(The Num sublayer only has one key because small example. Usually sublayers have multiple keys.) | |||
*/ | |||
Key* const ptrsKeys_sub00[] = { &s_minus, &s_1 }; | |||
Key_LayeredKeys1 k_sub00(ptrsKeys_sub00); | |||
/* | |||
k_sub00 is nested in k_00. | |||
The key k_00 contains code and key for layerIds ALPHA and SYM. | |||
Notice that k_sub00 is of type Key_LayeredKeys1, while k_00 is of type Key_LayeredKeys. | |||
k_sub00 and k_00 are associated with distinct LayerStates. | |||
k_00 contains code and key for layers ALPHA and SYM. | |||
k_00 gets it's active layer from groupState. | |||
k_sub00 is nested in layer SYM. | |||
*/ | |||
Key* const ptrsKeys_00[] = { &s_a, &k_sub00 }; | |||
Key_LayeredKeys k_00(ptrsKeys_00); | |||
@@ -102,7 +114,7 @@ Key* const ptrsKeys_0[] = { &k_00, &k_01, &k_02 }; | |||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | |||
Row row_0(scanner, 0, ptrsKeys_0, keyCount_0); | |||
Key* const ptrsKeys_1[] = { &l_normal, &l_sym, &s_enter }; | |||
Key* const ptrsKeys_1[] = { &l_alpha, &l_sym, &s_enter }; | |||
uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); | |||
Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | |||
@@ -1,7 +1,7 @@ | |||
/* keybrd_3e_sublayerNestedScSc.ino | |||
This sketch: | |||
is firmware for layout 2 layers plus 1 sublayer. | |||
is firmware for layout two layers plus one sublayer. | |||
runs on the first three columns of a breadboard keyboard | |||
| Layout | **0** | **1** | **2** | | |||
@@ -35,30 +35,30 @@ uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||
Scanner_uC scanner(LOW, readPins, readPinCount); | |||
// =================== CODES =================== | |||
// ---------------- LAYER CODE ----------------- | |||
// ---------------- LAYER GROUP ---------------- | |||
enum layerIds { ALPHA, SYM }; | |||
LayerState layerState; | |||
LayerState groupState; | |||
Code_LayerLock l_normal(ALPHA, layerState); | |||
Code_LayerLock l_sym(SYM, layerState); | |||
Code_LayerLock l_normal(ALPHA, groupState); | |||
Code_LayerLock l_sym(SYM, groupState); | |||
/* | |||
Key_LayeredKeys are associated with layerState. | |||
groupState manages a layer group delineated by all layers that are in Key_LayeredKeys objects. | |||
*/ | |||
LayerStateInterface& Key_LayeredKeys::refLayerState = layerState; | |||
LayerStateInterface& Key_LayeredKeys::refLayerState = groupState; | |||
// ---------------- SUBLAYER CODE -------------- | |||
// --------------- LAYER SUBGROUP -------------- | |||
enum subLayers { SUBSYM, SUBNUM }; | |||
LayerState sublayerState; | |||
LayerState subgroupState; | |||
Code_LayerHold l_num(SUBNUM, sublayerState); | |||
Code_LayerHold l_num(SUBNUM, subgroupState); | |||
/* | |||
Key_LayeredScSc is associated with layerState. | |||
subgroupState manages a layer group delineated by all layers that are in Key_LayeredScSc objects. | |||
*/ | |||
LayerStateInterface& Key_LayeredScSc::refLayerState = sublayerState; | |||
LayerStateInterface& Key_LayeredScSc::refLayerState = subgroupState; | |||
// ---------------- SCAN CODES ----------------- | |||
Code_Sc s_a(KEY_A); | |||
@@ -72,16 +72,15 @@ Code_Sc s_enter(KEY_ENTER); | |||
Code_Sc s_1(KEY_1); | |||
/* =================== KEYS ==================== | |||
The key k_sub00 contains codes for layerIds SUBSYM and SUBNUM. | |||
Key_LayeredScSc takes two scancode arguments. | |||
(The Num sublayer only has one key because small example. Usually sublayers have multiple keys.) | |||
k_sub00 contains codes for sub layers SUBSYM and SUBNUM. | |||
k_sub00 gets it's active layer from subgroupState. | |||
*/ | |||
Key_LayeredScSc sub_00(KEY_MINUS, KEY_1); | |||
/* | |||
k_sub00 is nested in k_00. | |||
The key k_00 contains code and key for layerIds ALPHA and SYM. | |||
k_sub00 and k_00 are associated with distinct LayerStates. | |||
k_00 contains code and key for layers ALPHA and SYM. | |||
k_00 gets it's active layer from groupState. | |||
k_sub00 is nested in layer SYM. | |||
*/ | |||
Key* const ptrsKeys_00[] = { &s_a, &sub_00 }; | |||
Key_LayeredKeys k_00(ptrsKeys_00); |
@@ -43,7 +43,7 @@ const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR p | |||
/* | |||
Normally all strobe pins are on one port, and all the read pins are on the other port. | |||
In this example, portB stobes the row while portA reads the colums. | |||
Port_MCP23S17 constructor parameters are: deviceAddr, portNum, readPins | |||
Port_MCP23S17 constructor parameters are: deviceAddr, portNum, readPins. | |||
readPins is a bit pattern, where 0=output, 1=input. | |||
In portA, the first two pins are set to input for reading. | |||
"<<" (bit shift left) and "|" (OR) are bitwise operators. | |||
@@ -67,8 +67,7 @@ Code_Sc s_4(KEY_4); | |||
/* =================== ROWS ==================== | |||
Left row names contain the letter 'L', while right row names conatain the letter 'R'. | |||
The first parameteer of a Row constructor specifies the scanner. | |||
The second parameter of the Row constructor specifies the Row's strobePin. | |||
Row constructor parameters are: scanner, strobePin, ptrsKeys[], keyCount. | |||
strobePin has one of two formats: | |||
* if refScanner a Scanner_uC, then strobePin is an Arduino pin number connected to this row | |||
* otherwise strobePin is a bit pattern, 1 indicating an IC pin connected to the row |
@@ -39,28 +39,28 @@ In this example, the LED_uC objects are named after the states they indicate. | |||
*/ | |||
LED_uC LED_normal(16); | |||
LED_uC LED_fn(17); | |||
LED_uC LED_CapsLck(21); | |||
LED_uC LED_capsLck(21); | |||
// =================== CODES =================== | |||
/* ---------------- LAYER CODE ----------------- | |||
LayerState_LED is similar to LayerState, introduced in keybrd_3a_multi-layerHold.ino, but with LEDs. | |||
LayerState_LED is similar to LayerState introduced in keybrd_3a_multi-layerHold.ino, but with LEDs. | |||
The LayerState_LED turns on the LED of the active layer. | |||
The prtsLayerLEDs[] array contains one LED per layer. | |||
The active layerId is used as an index to dereference the prtsLayerLEDs[] array. | |||
*/ | |||
enum layers { NORMAL, FN }; | |||
enum layerIds { NORMAL, FN }; | |||
LEDInterface* prtsLayerLEDs[] = { &LED_normal, &LED_fn }; //array index matches enum layerIds | |||
LEDInterface* prtsLayerLEDs[] = { &LED_normal, &LED_fn }; //enum layerIds align with array index | |||
LayerState_LED layerState(prtsLayerLEDs); | |||
Code_LayerHold l_fn(FN, layerState); | |||
/* ---------------- SCAN CODES ----------------- | |||
When a Code_LEDLock object is pressed, it sends its scancode and updates the its LED. | |||
Scancodes can be one of KEY_CAPS_LOCK, KEY_SCROLL_LOCK, or KEY_NUM_LOCK. | |||
For example, when o_capsLock is pressed, it sends KEY_CAPS_LOCK scancode and updates LED_CapsLck. | |||
Scancodes can be one of: KEY_CAPS_LOCK, KEY_SCROLL_LOCK, or KEY_NUM_LOCK. | |||
For example, when o_capsLock is pressed, it sends KEY_CAPS_LOCK scancode and updates LED_capsLck. | |||
*/ | |||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_CapsLck); | |||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_capsLck); | |||
Code_Sc s_a(KEY_A); | |||
Code_Sc s_x(KEY_X); |
@@ -2,8 +2,8 @@ | |||
This sketch: | |||
is a simple 1-layer keyboard with CapsLck indicator LED on I/O expander | |||
runs on two matrices of a breadboard keyboard | |||
modified keybrd_4c_split_keyboard_with_IOE.ino by adding LED_CapsLck | |||
runs on a two-matrix breadboard keyboard | |||
modified keybrd_4c_split_keyboard_with_IOE.ino by adding LED_capsLck | |||
This layout table shows left and right matrices: | |||
@@ -30,7 +30,7 @@ This layout table shows left and right matrices: | |||
//right matrix | |||
#include <Port_MCP23S17.h> | |||
#include <Scanner_IOE.h> | |||
#include <LED_IOE.h> | |||
#include <LED_Port.h> | |||
// ============ SPEED CONFIGURATION ============ | |||
ScanDelay scanDelay(9000); | |||
@@ -43,22 +43,24 @@ const uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||
Scanner_uC scanner_L(LOW, readPins, readPinCount); | |||
// ----------------- LEFT LEDs ----------------- | |||
LED_uC LED_CapsLck(21); | |||
LED_uC LED_capsLck(21); | |||
// --------------- RIGHT SCANNER --------------- | |||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read and LED | |||
Port_MCP23S17 portB(IOE_ADDR, 1, 0); //for strobe and LED | |||
Port_MCP23S17 portA(IOE_ADDR, 0, 1<<0 | 1<<1 ); //for read and LED | |||
Port_MCP23S17 portB(IOE_ADDR, 1, 0); //for strobe and LED | |||
Scanner_IOE scanner_R(LOW, portB, portA); | |||
// ---------------- RIGHT LEDs ----------------- | |||
LED_IOE LED_normal(portA, 1<<5); | |||
LED_IOE LED_fn(portB, 1<<4); | |||
/* ---------------- RIGHT LEDs ----------------- | |||
The LED_Port constructor parameters are a port and pin number that is connected to an LED. | |||
*/ | |||
LED_Port LED_normal(portA, 1<<5); | |||
LED_Port LED_fn(portB, 1<<4); | |||
// =================== CODES =================== | |||
// ---------------- LAYER CODE ----------------- | |||
enum layers { NORMAL, FN }; | |||
enum layerIds { NORMAL, FN }; | |||
LEDInterface* prtsLayerLEDs[] = { &LED_normal, &LED_fn }; //array index matches enum layerIds | |||
LayerState_LED layerState(prtsLayerLEDs); | |||
@@ -82,7 +84,7 @@ Code_Sc s_minus(KEY_MINUS); | |||
Code_Sc s_equal(KEY_EQUAL); | |||
Code_Sc s_slash(KEY_SLASH); | |||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_CapsLck); | |||
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_capsLck); | |||
// =================== KEYS ==================== | |||
//row0 | |||
@@ -126,12 +128,15 @@ Key* ptrsKeys_R1[] = { &k_12, &k_13 }; | |||
const uint8_t KEY_COUNT_R1 = sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1); | |||
Row row_R1(scanner_R, 1<<1, ptrsKeys_R1, KEY_COUNT_R1); | |||
// ################### MAIN #################### | |||
/* ################### MAIN #################### | |||
LayerState_LED::begin() is called after Scanner_IOE::begin() | |||
so that scanner's ports can turn on LayerState_LED's default-layer LED. | |||
*/ | |||
void setup() | |||
{ | |||
Keyboard.begin(); | |||
scanner_R.begin(); | |||
layerState.begin();//must be after scanner begin for IOE ?? todo | |||
layerState.begin(); //call LayerState_LED::begin() after Scanner_IOE::begin() | |||
} | |||
void loop() |
@@ -1,51 +1,51 @@ | |||
/* keybrd_6_active_high.ino | |||
This sketch: | |||
is the tutorial 1 sketch with STROBE_ON/STROBE_OFF values swapped | |||
is active high 1-layer keyboard | |||
runs on the first two rows and columns of a active-high breadboard keyboard | |||
is keybrd_2_single-layer.ino modified to be active high | |||
requires two external pull-down resistors on the first two columns | |||
| Layout | **0** | **1** | | |||
|:------:|-------|-------| | |||
| **0** | 1 | a | | |||
| **1** | b | c | | |||
| **0** | 1 | 2 | | |||
| **1** | a | b | | |||
*/ | |||
// ################## GLOBAL ################### | |||
// ================= INCLUDES ================== | |||
#include <ScanDelay.h> | |||
#include <Code_Sc.h> | |||
#include <Row_uC.h> | |||
#include <Row.h> | |||
#include <Scanner_uC.h> | |||
#include <ScanDelay.h> | |||
// ============ SPEED CONFIGURATION ============ | |||
ScanDelay scanDelay(9000); | |||
/* ================ ACTIVE STATE =============== | |||
STROBE_ON and STROBE_OFF define the logic levels for the strobe. | |||
"Active high" means that if a switch is pressed (active), the read pin is high. | |||
To make this sketch active high, STROBE_ON should be HIGH. | |||
// ================== SCANNER ================== | |||
uint8_t readPins[] = {14, 15}; | |||
uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||
Compared active low, STROBE_ON/STROBE_OFF values swapped. | |||
/* | |||
Scanner_uC constructor parameters are: strobeOn, readPins[], readPinCount. | |||
strobeOn defines the logic level for strobes, HIGH or LOW. | |||
"Active high" means that if a switch is pressed (active), the read pin is high. | |||
*/ | |||
const bool Scanner_uC::STROBE_ON = HIGH; //set matrix for active high | |||
const bool Scanner_uC::STROBE_OFF = LOW; | |||
// ================= PINS ================= | |||
uint8_t readPins[] = {14, 15}; | |||
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins); | |||
Scanner_uC scanner(HIGH, readPins, readPinCount); | |||
// =================== CODES =================== | |||
Code_Sc s_1(KEY_1); | |||
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); | |||
// =================== ROWS ==================== | |||
Key* ptrsKeys_0[] = { &s_1, &s_a }; | |||
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0); | |||
Key* ptrsKeys_0[] = { &s_1, &s_2 }; | |||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | |||
Row row_0(scanner, 0, ptrsKeys_0, keyCount_0); | |||
Key* ptrsKeys_1[] = { &s_b, &s_c }; | |||
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1); | |||
Key* ptrsKeys_1[] = { &s_a, &s_b }; | |||
uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); | |||
Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | |||
// ################### MAIN #################### | |||
void setup() |
@@ -4,8 +4,8 @@ The first two tutorials are intended to be read in sequence: | |||
* Tutorial 1 builds a breadboard keyboard and covers basic keyboard-hardware knowledge. | |||
* Tutorial 2 covers basic keybrd-sketch knowledge needed to understand the remaining tutorials. | |||
Tutorials from 3 up can be read in any order. | |||
Tutorials 2 through 7 use the keyboard breadboard that was built in tutorial 1. | |||
Tutorials from 3 up can be read in any order. | |||
Tutorials from 8 up are advance topics about the keybrd library. | |||
The tutorials assume the reader: |
@@ -18,7 +18,7 @@ Compared to PCBs, breadboard keyboards make learning faster because: | |||
Breadboard keyboards are useful for: | |||
* learning keyboard electronics - microcontroller, key matrix, diode, shift registers, I/O expander | |||
* learning the firmware development workflow | |||
* learning the keyboard-development workflow | |||
* prototyping circuits before making a PCB | |||
Breadboard keyboard starter kit | |||
@@ -62,7 +62,7 @@ Building a basic breadboard keyboard | |||
------------------------------------ | |||
The basic breadboard keyboard has 4 switches. | |||
![basic breadboard keyboard](keybrd_1_breadboard/breadboard_keyboard_2x2.JPG "basic breadboard keyboard") | |||
![basic breadboard keyboard](keybrd_1_breadboard/basic_breadboard_keyboard_front.JPG "basic breadboard keyboard") | |||
A Teensy LC microcontroller is on the left. | |||
A key matrix with 4 switches is to the right. | |||
@@ -75,7 +75,7 @@ Two bus strips are used as matrix rows. | |||
A jumper connects the top row to the microcontroller. | |||
A short wire connects the bottom row to the microcontroller. | |||
Switch-diode pairs, in series, connect rows to columns. | |||
A switches and diodes connect rows to columns. | |||
Tutorials 2 and 3 use the same basic breadboard keyboard pictured above. | |||
Tutorials 4, 5, and 6 add more components to the basic breadboard keyboard. | |||
@@ -97,11 +97,11 @@ Breadboard keyboard assembly instructions: | |||
* Teensy LC is on the left | |||
* switch leads are oriented to connect diodes to columns (pictured below) | |||
* diode cut offs connect terminal strips into columns | |||
* diodes connect switches to rows; orient diodes with cathode (banded end) towards the row (blue bus) | |||
* diodes connect switches to rows; orient diodes with cathode (banded end) towards the rows (blue bus) | |||
![switch orientation](keybrd_1_breadboard/switch_orientation.JPG "switch orientation") | |||
![basic breadboard keyboard overhead](keybrd_1_breadboard/breadboard_keyboard_2x2_overhead.JPG "basic breadboard keyboard overhead") | |||
![basic breadboard keyboard overhead](keybrd_1_breadboard/basic_breadboard_keyboard_overhead.JPG "basic breadboard keyboard overhead") | |||
3. Insert jumper wires to connect Arduino pins to the matrix rows and columns. | |||
* [Teensy LC pinout diagram](https://www.pjrc.com/teensy/card6a_rev2.png). | |||
@@ -140,7 +140,8 @@ The breadboard keyboards in this series of tutorials do it the other way: | |||
The keybrd library uses the word "strobe", which means powering one row for a very short time. | |||
Strobe pins are output pins connected to rows. | |||
One row at a time is strobed for the purpose of reading input pins. | |||
One row at a time is strobed. | |||
While a row is strobed, input pins connected to the columns sense which buttons are pressed. | |||
Exercises | |||
--------- |
@@ -6,7 +6,7 @@ The sketch will run on the basic breadboard keyboard described in [tutorial_1_br | |||
After reading the sketch you will be able to modify it to suite your own single-layer keyboard design. | |||
![basic breadboard keyboard](keybrd_1_breadboard/breadboard_keyboard_2x2.JPG "basic breadboard keyboard") | |||
![basic breadboard keyboard](keybrd_1_breadboard/basic_breadboard_keyboard_front.JPG "basic breadboard keyboard") | |||
Exercises | |||
--------- |
@@ -5,7 +5,7 @@ When you finish this tutorial you will be able to be able to modify a multi-laye | |||
Multi-layer nomenclature | |||
------------------------ | |||
**[layers](http://deskthority.net/wiki/Layer)** - are key bindings provided by the keyboard firmware. For example, | |||
* The classic [IBM PC keyboard](http://en.wikipedia.org/wiki/IBM_PC_keyboard) has one layer. | |||
* The classic [IBM Model M 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. | |||
@@ -13,30 +13,30 @@ Multi-layer nomenclature | |||
**active layer** - is the layer currently used by the keyboard. | |||
**default layer** - is the active layer when the keyboard starts up (in class LayerState, default layerId=0). | |||
**default layer** - is the active layer when the keyboard starts up. | |||
**layer scheme** - is a system for changing the active layer while typing (a single-layer scheme does not change layers). | |||
Code classes | |||
------------ | |||
Code objects only have one scancode or code. | |||
Example single-layer Code classes include: | |||
* Code_Sc (used in keybrd_2_single-layer.ino) | |||
Code objects only have one scancode or one layer code. | |||
Example Code classes include: | |||
* Code_Sc | |||
* Code_ScS | |||
* Code_ScNS | |||
* Code_Shift | |||
* Code_LayerHold | |||
* Code_LayerLock | |||
Single-layer keybrd sketches have one Code object per key. | |||
Multi-layer keybrd sketches have multiple Code objects per key, one code for each layer. | |||
Single-layer keys contain one Code object. | |||
Multi-layer keys contain multiple Code objects, one code for each layer. | |||
A simple multi-layer keybrd sketch | |||
---------------------------------- | |||
The [keybrd_3a_multi-layerHold.ino](keybrd_3a_multi-layerHold/keybrd_3a_multi-layerHold.ino) sketch is for a simple two-layer keyboard. | |||
It will run on the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md). | |||
![basic breadboard keyboard](keybrd_1_breadboard/breadboard_keyboard_2x2.JPG "basic breadboard keyboard") | |||
![basic breadboard keyboard](keybrd_1_breadboard/basic_breadboard_keyboard_front.JPG "basic breadboard keyboard") | |||
The sketch annotations explain how multi-layer keyboards work. | |||
The sketch uses three layer-scheme classes: | |||
@@ -56,7 +56,7 @@ When a Code_Layer object is pressed, it tells LayerState to update the active la | |||
``` | |||
class Code_Layer | |||
{ | |||
int layerId; | |||
const int layerId; | |||
LayerState& refLayerState; | |||
press() { refLayerState.setActiveLayer(layerId); } | |||
}; | |||
@@ -73,8 +73,8 @@ class LayerState | |||
}; | |||
``` | |||
**Key_LayeredKeys** objects contain an array of keys, one key for each layer. | |||
Key_LayeredKeys objects use layerIds as Key_LayeredKeys indexes. | |||
**Key_LayeredKeys** objects contain arrays of keys, one key for each layer. | |||
Key_LayeredKeys objects use layerIds as array indexes. | |||
When a Key_LayeredKeys object is pressed, it gets the active layerId from LayerState, and sends the corresponding key. | |||
``` | |||
class Key_LayeredKeys | |||
@@ -123,6 +123,58 @@ Key_Layered classes include: | |||
* Key_LayeredScSc (covered in next tutorial) | |||
* Key_LayeredCodeSc | |||
The association between Codes, Keys, and Rows | |||
--------------------------------------------- | |||
Codes, Keys, and Rows are associated by class compositions: | |||
``` | |||
Each Code object contains one scancode or one layercode. | |||
Each Key contains either | |||
* one Code object (single-layer) | |||
* multiple Code objects (multi-layer) | |||
* Key object (key nested in key) | |||
Each Row contains Key objects. | |||
``` | |||
You may have been wondering why Code pointers are in Key pointers arrays. | |||
You don't need to know the reasons to write a sketch. | |||
For the curious, two reasons are explained below. | |||
1) Single-layer keys is the first reason you see Code pointers in a Key pointers array. | |||
Rows contain keys. The keys can be Single-layer or Multi-layer. | |||
Wrapping a code in a single-layer key before placing it a row is tedious. | |||
It is more convenient to place a code directly in a row. | |||
Codes are a kind of Key (polymorphism), so that rows can contain codes and keys. | |||
From keybrd_3a_multi-layerHold.ino: | |||
``` | |||
Key* const ptrsKeys_0[] = { &k_00, &k_01 }; | |||
Row row_0(scanner, 0, ptrsKeys_0, keyCount_0); | |||
Key* const ptrsKeys_1[] = { &l_fn, &s_shift }; | |||
Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | |||
``` | |||
row0's ptrsKeys_0[] array contains pointers to Keys. | |||
row1's ptrsKeys_1[] array contains pointers to Codes. | |||
2) Sublayers (nested keys) is the second reason you see Code pointers in a Key pointers array. | |||
Layered keys usually contain just codes. When nesting keys, layered keys contain keys. | |||
Codes are a kind of Key (polymorphism), so that layered keys can contain both codes and keys. | |||
From keybrd_3d_sublayerNested.ino: | |||
``` | |||
Key* const ptrsKeys_sub00[] = { &s_minus, &s_1 }; | |||
Key_LayeredKeys1 k_sub00(ptrsKeys_sub00); | |||
Key* const ptrsKeys_00[] = { &s_a, &k_sub00 }; | |||
Key_LayeredKeys k_00(ptrsKeys_00); | |||
``` | |||
k_00's ptrsKeys_00[] array contains pointers to code s_a and key k_sub00. | |||
Exercises | |||
--------- | |||
1) Compile and run [keybrd_3a_multi-layerHold.ino](keybrd_3a_multi-layerHold/keybrd_3a_multi-layerHold.ino) |
@@ -1,26 +1,20 @@ | |||
Tutorial 3cde - sublayer keyboard | |||
================================= | |||
This tutorial assumes the reader understands the previous tutorial. | |||
This tutorial assumes you have read tutorial_3ab_multi-layer_keyboard. | |||
When you finish this tutorial you will be able to be able to modify a multi-layer keybrd sketch to write your very own multi-layer keyboard firmware. | |||
When you finish this tutorial you will be able to be able to modify a multi-layer keybrd sketch. | |||
Sublayer nomenclature | |||
--------------------- | |||
These definitions are specific to the keybrd library. | |||
**layer group** - is a group of layers that occupy the same keys. | |||
**layer group** - is a group of layers that occupy a group of keys. | |||
**primary layer group** - is a layer group containing the default layer. | |||
**layer subgroup** - is a layer group nested in a layer of another layer group. | |||
**sublayer group** - is a layer group nested in a layer of another layer group. | |||
**primary layer** - is a layer within the primary layer group. | |||
**sublayer** - is a layer within the sublayer group. | |||
Layer scheme with a sublayer | |||
---------------------------- | |||
This tutorial has 3 example sketches, all using this layout: | |||
Layout with a sublayer | |||
---------------------- | |||
The next three example sketches all use this layout: | |||
| Layout | **0** | **1** | **2** | | |||
|:------:|:-----:|:-----:|:-----:| | |||
@@ -28,92 +22,134 @@ This tutorial has 3 example sketches, all using this layout: | |||
| **1** | Alpha | Sym | Enter | | |||
Each cell in the table's body represents a key. | |||
Bottom row keys have one layer. | |||
Top row keys have 2 or 3 layers. | |||
Each element in a cell represents a scancode or layer code. | |||
Pressing the "Alpha" layer key locks the Alpha layer. | |||
Pressing the Alpha-layer key locks the Alpha layer. | |||
Letters 'a' 'b' 'c' are on the Alpha layer. | |||
Pressing the "Sym" layer key locks the Sym layer. | |||
Pressing the Sym-layer key locks the Sym layer. | |||
Symbols '-' '=' and "Num" layer key are on the Sym layer. | |||
If the keyboard is locked on the Sym layer, holding Num down makes Num the active layer. | |||
Releasing the Num key restores the Sym layer. | |||
If the keyboard is locked on the Sym layer, holding Num down makes it the active layer. | |||
Number '1' is on the Num sublayer. | |||
Releasing the Num key makes the locked layer active. | |||
todo [pic of 3 col bb kb] | |||
Example sketches 3c, 3d, and 3e implement the above layout. | |||
Each sketch uses a different layer scheme. | |||
Three example sketches implement the above layout using differently layer schemes. | |||
Which layer scheme is best depends on the layout. | |||
The sketches will run on the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md) with a 3rd column added to pin 16: | |||
Sketch 3c - one layer group | |||
--------------------------- | |||
Refer to keybrd_3c_sublayerNull.ino | |||
![3-column breadboard keyboard](keybrd_3c_sublayerNull/front.JPG "3-column breadboard keyboard") | |||
The top row has one layer group with 3 layers. | |||
Num layer is unused to the right of 1. | |||
Duplicate codes and null codes fill the unused space. | |||
It's a bit of a kludge, but can be simple if there is little unused space. | |||
Sketch 3c - two layer groups | |||
---------------------------- | |||
This layer scheme has two layer groups: | |||
layerState contains the active layer for the layer group. | |||
| group_1 | **0** | **1** | **2** | | |||
|:-------:|:-----:|:-----:|:----------:| | |||
| **0** | a - 1 | b = = | c Num null | layer group with three layers: Alpha Sym Num | |||
| **1** | | | | | |||
Sketch 3d - two layer groups | |||
---------------------------- | |||
Refer to keybrd_3d_sublayerNestedKeys.ino | |||
| group_0 | **0** | **1** | **2** | | |||
|:-------:|:-----:|:-----:|:-----:| | |||
| **0** | | | | | |||
| **1** | Alpha | Sym | Enter | layer group with a one layer | |||
group_1 covers the entire top row, therefore Num layer covers the entire top row. | |||
Meanwhile, the layout's Num layer only covers the first key. | |||
The unused num-layer space is filled with duplicate and null codes. | |||
A little filler is the simplest way in some situations. | |||
The layer scheme is implemented in keybrd_3c_sublayerNull.ino | |||
The null is not really needed because null is on the same key as Num layer code. | |||
And Num is the only layer code that can activate the Num layer. | |||
The null key would be needed if another Num key where added to the layout. | |||
The top row has two layer groups. | |||
* NORMAL+SYM is the primary layer group. It covers the top-row keys. | |||
* SYM1+NUM1 is a sublayer group nested in the SYM layer. The subgroup covers the top-left key. | |||
The next example sketch has three layer groups that fit the layout perfectly. | |||
Two layer groups model the logic of the layout accurately | |||
(the previous example was a kluge because it only had one layer group). | |||
Sketch 3d - three layer groups | |||
------------------------------ | |||
This layer scheme has three layer groups: | |||
There should be one LayerState object for each layer group. In this example: | |||
* layerState contains the active layer for the primary layer group. | |||
* sublayerState contains the active layer for the sublayer group. | |||
| group_2 | **0** | **1** | **2** | | |||
|:-------:|:-----:|:-----:|:-----:| | |||
| **0** | - 1 | | | layer subgroup with two layers: Sym Num | |||
| **1** | | | | | |||
| group_1 | **0** | **1** | **2** | | |||
|:-------:|:---------:|:-----:|:-----:| | |||
| **0** | a group_2 | b = | c Num | layer group with two layers: Alpha Sym | |||
| **1** | | | | | |||
| group_0 | **0** | **1** | **2** | | |||
|:-------:|:-----:|:-----:|:-----:| | |||
| **0** | | | | | |||
| **1** | Alpha | Sym | Enter | layer group with a one layer | |||
The three layer groups model the logic of the layout accurately, without resorting to fillers. | |||
The layer scheme is implemented in keybrd_3d_sublayerNestedKeys.ino. | |||
Sketch 3e - specialized layered keys | |||
------------------------------------ | |||
Refer to keybrd_3e_sublayerNestedScSc.ino | |||
Key_LayeredKeys constructor takes any number of code or key arguments. | |||
Key_LayeredScSc is more specialized. It's constructor takes exactly two scancode arguments. | |||
A Key_LayeredScSc object could be instantiated with Key_LayeredKeysArray. | |||
But using Key_LayeredScSc has advantages when a large sublayer group has two layers: | |||
* no array is created for the two scancodes | |||
* less clutter in sketch | |||
* save SRAM | |||
Key_LayeredScSc has advantages when a large layer group has two layers: | |||
* no array is created for the two scancodes, which means less clutter in the sketch | |||
* uses less SRAM | |||
The layer scheme is implemented in keybrd_3e_sublayerNestedScSc.ino. | |||
It is similar to the previous sketch, but Key_LayeredKeysArray1 is replaced with Key_LayeredScSc. | |||
Key_Layered classes include: | |||
* Key_LayeredKeys (any number of codes or keys) | |||
* Key_LayeredScSc (specialized for two scancodes) | |||
* Key_LayeredCodeSc (specialized for one code and one scancode) | |||
Sublayer layer-key placement | |||
Subgroup layer-key placement | |||
---------------------------- | |||
A layer key to a sublayer can be place in one of two places: | |||
* on layer the sublayer group is nested in (layout above has Num-layer key on Sym layer) | |||
* on a single-layer key (layout below has Num-layer key on bottom row) (some people would not call this arrangement a sublayer) | |||
A subgroup's layer key(s) can be placed in one of two ways. | |||
1) A subgroup and it's layer key(s) on the same layer. | |||
This layout has a '1' in the Num layer and the Num layer key on the Sym layer: | |||
| Layout | **0** | **1** | **2** | | |||
|:------:|:-----:|:-----:|:-----:| | |||
| **0** | a - 1 | b = | c Num | | |||
| **1** | Alpha | Sym | Enter | | |||
This arrangement presents the Num layer as a "sublayer". | |||
2) A subgroup with it's layer key(s) on single-layer key(s). | |||
This layout has a '1' in the Num layer and the Num layer key on the bottom row: | |||
| Layout | **0** | **1** | **2** | | |||
|:------:|:-----:|:-----:|:-----:| | |||
| **0** | a - 1 | b = | c Ent | | |||
| **1** | Alpha | Sym | Num | | |||
This arrangement presents the Num layer as just another layer that happens to cover fewer keys than Alpha and Sym layers. | |||
The top row is easily implemented in one layer group with duplicate keys filling the Num layer: | |||
| group_1 | **0** | **1** | **2** | | |||
|:-------:|:-----:|:------:|:---------:| | |||
| **0** | a - 1 | b = = | c Ent Ent | layer group with three layers: Alpha Sym Num | |||
| **1** | | | | | |||
Complex layerschemes | |||
-------------------- | |||
The basic LayerState class used in the tutorials is sufficient for implementing many layer schemes. | |||
More complicated layer schemes would need custom LayerState classes, and possibly custom Code_Layer and Key_Layered classes as well. | |||
Any layer scheme can be implemented with the right custom layer classes. | |||
[keybrd_DH](https://github.com/wolfv6/keybrd_DH) is an example of a complex layer scheme (it emulates the DataHand keyboard). | |||
The keybrd_DH sketch is a showcase of the keybrd library's capability. | |||
It's the most complex layer scheme I know of. | |||
Don't let the complexity scare you; most layer schemes are much simpler. | |||
[keybrd_DH](https://github.com/wolfv6/keybrd_DH) is an example of a complex layer scheme. | |||
It emulates the DataHand keyboard, which has the most complex layout I know of. | |||
Most layer schemes are much simpler. | |||
Layer-scheme classes used by keybrd_DH are listed below. | |||
The layer scheme classes are organized into three categories, as is "Layer-scheme classes" in the previous tutorial. | |||
Most of the layer-scheme classes are custom classes, which reside in the keybrd_DH library. | |||
Most of the layer-scheme classes are custom classes which reside in the keybrd_DH library. | |||
DH Code_Layer classes include: | |||
* Code_LayerLock | |||
@@ -135,12 +171,22 @@ DH Key_Layered classes include: | |||
* Key_LayeredNumber | |||
* Key_LayeredNumber_00 | |||
Exercises | |||
--------- | |||
Modify keybrd_3e_sublayerNestedScSc.ino by adding the number '2' to the Num layer group. | |||
The layout is below. | |||
1) Modify keybrd_3e_sublayerNestedScSc.ino to match the following layout | |||
(add the number '2' to the Num layer): | |||
| Layout | **0** | **1** | **2** | | |||
|:------:|:-----:|:-----:|:-----:| | |||
| **0** | a - 1 | b = 2 | c Num | | |||
| **1** | Alpha | Sym | Enter | | |||
2) Modify keybrd_3c_sublayerNull.ino to match the following layout | |||
(this was described above in "Subgroup layer-key placement"): | |||
| Layout | **0** | **1** | **2** | | |||
|:------:|:-----:|:-----:|:-----:| | |||
| **0** | a - 1 | b = | c Ent | | |||
| **1** | Alpha | Sym | Num | | |||
@@ -11,9 +11,9 @@ Two keybrd classes use AutoShift: | |||
The [keybrd_3f_autoShift.ino](keybrd_3f_autoShift/keybrd_3f_autoShift.ino) sketch explains the AutoShift feature. | |||
It will run on the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md). | |||
After reading the sketch you too will be able to automatically shifted characters. | |||
After reading the sketch you too will be able to automatically shift characters. | |||
![basic breadboard keyboard](keybrd_1_breadboard/breadboard_keyboard_2x2.JPG "basic breadboard keyboard") | |||
![basic breadboard keyboard](keybrd_1_breadboard/basic_breadboard_keyboard_front.JPG "basic breadboard keyboard") | |||
Exercises | |||
--------- |
@@ -10,8 +10,8 @@ The preferred connection method depends on the number of keys, number of availab | |||
## Split keyboard connections table | |||
| connection type | controller pins | wire count | max keys | | |||
|:----------------------:|:---------------:|:----------:|:--------:| | |||
| just cable | 3 | 6 | 9 | | |||
|------------------------|:---------------:|:----------:|:--------:| | |||
| just cable | 3 | 6 | 9 | | |||
| just cable | 3 | 7 | 12 | | |||
| just cable | 4 | 8 | 16 | | |||
| just cable | 4 | 9 | 20 | | |||
@@ -24,16 +24,18 @@ The preferred connection method depends on the number of keys, number of availab | |||
Fewer wires makes a cable more flexible. | |||
A flexibility cable makes it easy to position the keyboard and route the cable. | |||
But if there are enough pins on the controller, just using a cable with more wires is simpler and costs less. | |||
But if there are enough pins on the controller, using just a cable with more wires is simpler and costs less. | |||
I/O Expanders can power LEDs, while PISO shift registers can not. | |||
I2C is a little slow if the I/O expander is scanning more than 4 rows. | |||
The keybrd_DH (DodoHand) sketch polls 5 rows over I2C at 7.5ms per keyboard scan. | |||
Which is acceptable, but faster would be nicer. | |||
## Cables table | |||
| connector name | wire count | | |||
|:-----------------------------------------------------:|:----------:| | |||
|-------------------------------------------------------|:----------:| | |||
| TRRS | 4 | | |||
| 6-pin mini-DIN connector (PS/2) | 4 | | |||
| USB 2 | 4 | | |||
@@ -52,7 +54,7 @@ The 8-wire "GearIT Cat 6 Ethernet Flat Patch Cable 7 Feet" is very flexible. | |||
It's available at Walmart (9/19/16) if you want to feel the merchandise before you buy. | |||
All the modular connectors are flat. | |||
For prototyping on perfboards, consider a 0.1” header. | |||
For prototyping on perfboards, consider a 0.1" header. | |||
<br> | |||
<a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. |
@@ -4,35 +4,34 @@ When you finish this tutorial you will be able to be able to modify a split keyb | |||
Overview of split keyboard with shift registers | |||
------------------------------------------------ | |||
Only the right matrix is shown. The left matrix is not needed and was omitted to reduce clutter. | |||
The breadboard in the following picture models a split keyboard. | |||
The green rectangle on the left is a Teensy LC micro controller. | |||
The black rectangles in the middle are two SN74HC165N shift registers daisy chained together. | |||
The micro controller and shift registers are connected via 5 jumper wires. | |||
Only the right matrix is shown. The left matrix is not needed for this demonstration and was omitted to reduce clutter. | |||
The right-matrix layout has 2 rows and 7 columns. | |||
Electronically, the matrix only has one row. | |||
Electronically, there is only one row of keys. | |||
Diodes are not needed because there is only one row. | |||
The two black rectangles are SN74HC165N shift registers daisy chained together. | |||
Shift register details are in the SN74HC165N datasheet. | |||
![breadboard keyboard with shift_registers](keybrd_4b_split_keyboard_with_shift_registers/shift_reg_front.JPG ) | |||
![breadboard keyboard with shift_registers](keybrd_4b_split_keyboard_with_shift_registers/front.JPG "breadboard keyboard with shift_registers") | |||
Building a split breadboard keyboard with shift registers | |||
--------------------------------------------------------- | |||
Add components to the breadboard as shown in the picture. | |||
Refer to the SN74HC165N datasheet to locate its pins. | |||
Each shift register has a small notch on one end to identify pin 1. | |||
In the picture, pin 1s are on the left end, towards the controller. | |||
In the picture, SN74HC165N pin 1s are on the left end, towards the controller. | |||
Shift registers are chained together by colored wires that lay flat on the breadboard. | |||
Each shift register has 8 parallel input pins, 4 on each side. | |||
There are 14 keys, so 2 of the input pins are unused. | |||
Used input pins are connected to 10k pull-down resistor which are grounded (blue bus). | |||
Unused input pins are grounded (blue bus). | |||
A decoupling capacitor between the power and ground wires prevents power disturbance. | |||
14 input pins and their keys are connected to 10k pull-down resistor which are grounded (blue bus). | |||
2 input pins are not used, they are grounded (blue bus). | |||
Switches are connected to power (red bus) and shift register input pins (jumpers). | |||
I apologize for not providing a schematic. This table should help you figure out the pictures: | |||
This table lists what gets connected to the 74HC165 pins: | |||
``` | |||
74HC165 left (lower half of breadboard) | |||
@@ -59,6 +58,14 @@ SER 10 serial input blue wire to next QH | |||
``` | |||
![breadboard keyboard with shift_registers](keybrd_4b_split_keyboard_with_shift_registers/overhead.JPG ) | |||
![breadboard keyboard with shift_registers](keybrd_4b_split_keyboard_with_shift_registers/back.JPG ) | |||
A decoupling capacitor between the power and ground wires suppresses noise. | |||
I apologize for not providing a schematic. | |||
Sketch for split keyboard with shift registers | |||
---------------------------------------------- | |||
[keybrd_4b_split_keyboard_with_shift_registers.ino](keybrd_4b_split_keyboard_with_shift_registers/keybrd_4b_split_keyboard_with_shift_registers.ino) is a simple sketch with two shift registers. | |||
@@ -68,16 +75,5 @@ Exercises | |||
--------- | |||
1. Guess what happens if an unused input pin is not grounded? Try it. | |||
2. Add a left matrix to the controller. | |||
There is room between Teensy and the shift registers for a 1-column matrix. | |||
The bus strips are occupied by the right keys, so use terminal strips instead. | |||
Other wise it is similar to the 2-column matrix in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md). | |||
The sketch already has code for both left and right matrix. | |||
Notice that the left matrix is active low, while the right matrix is active high. | |||
<br> | |||
<a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. | |||
## parts for breadboard_keyboard_supplies.ods | |||
@@ -1,22 +1,39 @@ | |||
keybrd Tutorial 4 - split keyboard with I/O Expander | |||
keybrd Tutorial 4c - split keyboard with I/O Expander | |||
==================================================== | |||
When you finish 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_2x5_labeled.jpg "2x5 breadboard keyboard") | |||
The breadboard in the following picture models a split keyboard. | |||
The green rectangle on the left is a Teensy LC micro controller. | |||
The black rectangle on the right is a MCP23S17 I/O expander. | |||
The breadboard's four bus strips are used as rows. | |||
The two ICs communicates via SPI protocol, where Teensy LC is the master and MCP23S17 is slave. | |||
The two ICs are connected via six jumper wires. | |||
The breadboard's four bus strips are used as matrix rows. | |||
Two rows (blue buses) are connected to the microcontroller. | |||
Two rows (red buses) are connected to the I/O expander. | |||
The I/O expander is a MCP23S17. | |||
It has a small notch on one end, which identifies pin 1. | |||
In the picture, pin 1 is on the left end. | |||
![breadboard keyboard with IOE](keybrd_4c_split_keyboard_with_IOE/front.JPG "breadboard keyboard with IOE") | |||
Building a split keyboard with I/O Expander | |||
------------------------------------------- | |||
Starting with the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md), add parts as described above. | |||
Refer to the MCP23S17 datasheet to locate its pins. | |||
In the picture, MCP23S17 pin 1 is on the left end, towards the controller. | |||
The MCP23S17 I/O expander has two ports. Each port has eight pins. | |||
Port B is connected to the matrix's rows (red bus). | |||
Port A is connected to the matrix's columns (jumpers). | |||
MCP23S17's device address is configured by hardware pins. | |||
All three address pins are grounded. | |||
The MCP23S17's /RESET pin is connected to VDD. | |||
The MCP23S17 communicates via SPI protocol, where Teensy LC is the master, and MCP23S17 is slave. | |||
The Teensy LC and MCP23S17 are connected via 6 jumper wires: | |||
This table lists the six wires connecting Teensy LC to MCP23S17: | |||
|CONNECTION |Teensy LC|MCP23S17| | |||
|:------------------:|---------|--------| | |||
@@ -27,21 +44,7 @@ The Teensy LC and MCP23S17 are connected via 6 jumper wires: | |||
|Master In, Slave Out| MISO0 | SO | | |||
|Chip Select | CS0 | /CS | | |||
A decoupling capacitor suppresses high-frequency noise from the power supply. | |||
MCP23S17's I/O expander address is configured by hardware pins. | |||
The MCP23S17 with all address pins grounded has an device address of 0x20. | |||
The MCP23S17's /RESET pin is connected to VDD. | |||
The MCP23S17 I/O expander has two ports. Each port has eight pins. | |||
Port B is connected to the matrix's rows. | |||
Port A is connected to the matrix's columns. | |||
Building a split keyboard with I/O Expander | |||
------------------------------------------- | |||
Starting with the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md), add parts as described above. | |||
Refer to the MCP23S17 datasheet to locate its pins. | |||
A decoupling capacitor between the power and ground wires suppresses noise. | |||
<!-- todo schematic with IOE power decoupling capacitor | |||
This schematic was written by consulting the I/O expander's datasheet and using the ?? tool. --> |
@@ -1,7 +1,7 @@ | |||
Tutorial 5a - indicator LEDs | |||
============================ | |||
Keyboards often have LEDs to indicate CapsLock, NumLock, and other states. | |||
It's one of the first things we look at when a keyboard produces unexpected results. | |||
It's one of the first things we check when a keyboard produces unexpected results. | |||
Adding LEDs to the basic breadboard keyboard | |||
-------------------------------------------- | |||
@@ -14,12 +14,11 @@ Each LED is in series with a 4.7k Ohm current limiting resistor. | |||
LED anodes (the longer lead) are powered by pins 16, 17, and 21. | |||
LED cathodes (the shorter lead) are connected to a grounded terminal strip. | |||
!["LEDs"](keybrd_5_LEDs/LEDs_back.JPG "LEDs") | |||
!["back"](keybrd_5a_LED_on_uC/back.JPG "back") | |||
keybrd sketch with LEDs | |||
----------------------- | |||
The [keybrd_5a_LED_on_uC.ino](keybrd_5a_LED_on_uC/keybrd_5a_LED_on_uC.ino) sketch will run on the above breadboard keyboard. | |||
As usual, the sketch annotations explain the code. | |||
LED forward voltage | |||
------------------- | |||
@@ -29,23 +28,22 @@ Forward voltage is published in the LED's datasheet. | |||
Most blue and green LEDs have about 3.3 forward voltage, which does not reliably illuminate on Teensy LC's 3.3 volts. | |||
Most red and yellow LEDs have around 2.2 forward voltage. | |||
So use red and yellow LEDs on Teensy LC. | |||
Low-current LEDs can go as low as 1.6 forward voltage. | |||
Some low-current LEDs have 1.6 forward voltage. | |||
LED brightness | |||
-------------- | |||
The amount of current (I) going through an LED is directly proportional to how bright it appears. | |||
The amount of current going through an LED is directly proportional to how bright it appears. | |||
By picking the correct resistor, you have full control over how bright the LED appears. | |||
Lets see how much visual difference resistance makes. | |||
Replace an LED's 4.7k Ohm resistor with a 68 Ohm resistor. | |||
Less resistance makes the LED brighter. | |||
Too little resistance will burn out the LED. | |||
The current supplied to an LED should always be limited by a resistor or some other device. | |||
LED current limiting resistor values | |||
------------------------------------ | |||
This formula calculates the minimum resistance for maximum LED brightness: | |||
In the section we will compute the minimum resistor value for maximum LED brightness. | |||
This formula calculates the minimum resistance: | |||
``` | |||
output-pin Supply Voltage Vs | |||
LED Forward Voltage Vf | |||
@@ -53,10 +51,7 @@ Forward Current If | |||
From Ohm's Law, minimum current limiting restiance R = (Vs - Vf) / If | |||
``` | |||
For Forward Current, use the smaller of: | |||
* Current capacity of output pin | |||
* Continuous Forward Current of LED | |||
Voltages and current capacities are published in datasheets and sometimes pinout diagrams. | |||
Teensy LC output-pin capacities are: | |||
* four 20 mA pins (5, 16, 17, 21) | |||
@@ -64,17 +59,18 @@ Teensy LC output-pin capacities are: | |||
* Teensy LC on-board LED is on pin 13. | |||
It has a current-limiting resistor on the board, and does not provide enough power for another LED. | |||
Voltages and current capacities are published in datasheets and sometimes pinout diagrams. | |||
For Teensy LC 20 mA pin and the TT Electronics OVLLx8C7 LED: | |||
Continuous Forward Current of TT Electronics OVLLx8C7 red or yellow LED is 30 mA. | |||
Plug the numbers into Ohm's Law: | |||
``` | |||
output-pin Supply Voltage Vs = 3.3 volts | |||
LED Forward Voltage Vf = 2.2 volts | |||
output-pin Supply Voltage Vs = 3.3 volts | |||
LED Forward Voltage Vf = 2.2 volts | |||
use the smaller of: | |||
max pin Current If = 20 mA | |||
max LED Current If = 30 mA | |||
For Forward Current, use the smaller of: | |||
current capacity of output pin If = 20 mA | |||
continuous Forward Current of LED If = 30 mA | |||
minimum current limiting restiance R = (Vs - Vf) / If = 55 Ohms | |||
minimum current limiting restiance is R = (Vs - Vf) / If = 55 Ohms | |||
``` | |||
Add a safety margin for resistor tolerances (1%, 2%, 5%, 10%), and round up to a standard value. | |||
http://www.rfcafe.com/references/electrical/resistor-values.htm | |||
@@ -103,5 +99,9 @@ From your LED's datasheet, find: | |||
Calculate the minimum resistance needed for your LED and Supply Voltage. | |||
2) See how bright an LED can get. | |||
In the above breadboard keyboard, replace an LED's 4.7k Ohm resistor with a 68 Ohm resistor. | |||
Make sure use a controller pin with a 20mA capacity (Teensy LC pins 16, 17, 21 have a 20mA capacity). | |||
<br> | |||
<a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. |
@@ -7,16 +7,20 @@ and familiar with I/O expanders from tutorial_4c_split_keyboard_with_IOE.md. | |||
Adding LEDs to the basic breadboard keyboard | |||
-------------------------------------------- | |||
The breadboard keyboard modifies the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md) | |||
The breadboard keyboard modifies the split breadboard keyboard described in [tutorial_4c_split_keyboard_with_IOE.md](tutorial_4c_split_keyboard_with_IOE.md) | |||
Add components to the breadboard as shown in the picture. | |||
The two clear plastic cylinders are red and yellow LEDs. | |||
Each LED is in series with a 4.7k Ohm current limiting resistor. | |||
LED anodes (the longer lead) are powered by pins GPA6 and GPB6. | |||
LED anodes (the longer lead) are powered by pins GPA5 and GPB4. | |||
LED cathodes (the shorter lead) are connected to a grounded terminal strip. | |||
!["LEDs"](keybrd_5_LEDs/LEDs_back.JPG "LEDs") | |||
!["front"](keybrd_5b_LED_on_IOE/front.JPG "front") | |||
!["back"](keybrd_5b_LED_on_IOE/back.JPG "back") | |||
!["LEDs"](keybrd_5b_LED_on_IOE/LEDs.JPG "LEDs") | |||
keybrd sketch with LEDs on IOE | |||
------------------------------ |
@@ -1,7 +1,5 @@ | |||
Tutorial 6 - Active high | |||
========================= | |||
This tutorial pulls together several concepts needed to understand active state in the context of a keyboard. | |||
Skip to the end of this tutorial if you just want to copy an active-high keyboard. | |||
======================== | |||
Pull-up resistors | |||
----------------- | |||
@@ -10,29 +8,18 @@ Here is a [good tutorial on Pull-up Resistors](https://learn.sparkfun.com/tutori | |||
Active low | |||
---------- | |||
All the keyboards up to this point in the tutorial series have used active low with internal pull-up resistors. | |||
Most the keyboards up to this point in the tutorial series have used active low with internal pull-up resistors. | |||
"Active low" means that if a switch is pressed (active state), the read pin is low. | |||
When the switch is released (inactive state), the pull-up resistor pulls the read pin high. | |||
The following table traces the strobe current from left to right (0 is ground, 1 is power). | |||
If the switch is closed, the strobe current passes through the switch and pulls the read pin low. | |||
If the switch is open, the pull-up resistor pulls the read pin high. | |||
<br> | |||
|Strobe pin on | Diode orientation | Switch position | Pull resistor | Read pin state | | |||
|:------------:|:------------------:|:---------------:|:-------------:|:---------------:| | |||
| 0 | cathode -:<- anode | close | 1 pull-up | 0 active low | | |||
| 0 | cathode -:<- anode | open | 1 pull-up | 1 inactive high | | |||
<br> | |||
Arduino boards have internal pull-up resistors, which saves on parts and labor compared to manually adding external pull resistors. | |||
To make a keyboard active low: | |||
* Orient diodes with cathode (banded end) towards the write pins (row) | |||
* Define strobe on and strobe off in the sketch like this: | |||
To make a key matrix active low: | |||
* Orient diodes with cathode (banded end) towards the strobe pins (row) | |||
* Instantiate the scanner in the sketch with strobeOn LOW, like this: | |||
``` | |||
const bool Scanner_uC::STROBE_ON = LOW; | |||
const bool Scanner_uC::STROBE_OFF = HIGH; | |||
Scanner_uC scanner(LOW, readPins, readPinCount); | |||
``` | |||
Active high | |||
@@ -40,41 +27,28 @@ Active high | |||
"Active high" means that if a switch is pressed (active), the read pin is high. | |||
When the switch is released (inactive), the pull-down resistor pulls the read pin low. | |||
The following table traces the strobe current from left to right (0 is ground, 1 is power). | |||
If the switch is closed, the strobe current passes through the switch and pulls the read pin high. | |||
If the switch is open, the pull-down resistor pulls the read pin low. | |||
<br> | |||
|Strobe pin on | Diode orientation | Switch position | Pull resistor | Read pin state | | |||
|:------------:|:------------------:|:---------------:|:-------------:|:---------------:| | |||
| 1 | anode ->:- cathode | close | 0 pull-down | 1 active high | | |||
| 1 | anode ->:- cathode | open | 0 pull-down | 0 inactive low | | |||
<br> | |||
Arduino boards do not have internal pull-down resistors. | |||
If you want to use active low, you will have to add external pull-down resistors to the read pins. | |||
To make a keyboard active high: | |||
* Add an external 10k pull-down resistor to each read pin | |||
To make a key matrix active high: | |||
* Add an external 10k Ohm pull-down resistor to each read pin | |||
* Orient diodes with cathode (banded end) towards the read pins | |||
* Define strobe on and off in the sketch like this: | |||
* Instantiate the scanner in the sketch with strobeOn HIGH, like this: | |||
``` | |||
const bool Scanner_uC::STROBE_ON = HIGH; | |||
const bool Scanner_uC::STROBE_OFF = LOW; | |||
Scanner_uC scanner(HIGH, readPins, readPinCount); | |||
``` | |||
Making a breadboard keyboard active-high | |||
---------------------------------------- | |||
This tutorial converts the basic breadboard keyboard from tutorial 1 to active high. | |||
By comparing the above tables, one can see what changes need to be made: | |||
* add external pull-down resistors to the read pins | |||
* flip the diodes so that the cathode (banded end) are towards the read pins | |||
* swap the STROBE_ON and STROBE_OFF values | |||
The pull-down resistors plug into ground (red bus) and column read pins. | |||
Make the following changes to the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md): | |||
* Add an external 10k Ohm pull-down resistor (red bus) to each read pin | |||
* Orient diodes with cathode (banded end) towards the read pins | |||
The [keybrd_6_active_highsketch.ino](keybrd_6_active_high/keybrd_6_active_high.ino) is the tutorial 1 sketch with STROBE_ON and STROBE_OFF values swapped. | |||
![pull-down resistors](keybrd_6_active_high/back.JPG "pull-down resistors") | |||
![pull_down_resistors.JPG](keybrd_6_active_high/pull_down_resistors.JPG "Active-high diodes and pull-down resistors") | |||
keybrd sketch with active-high | |||
------------------------------ | |||
The [keybrd_6_active_high.ino](keybrd_6_active_high/keybrd_6_active_high.ino) sketch will run on the above breadboard keyboard. | |||
<br> | |||
<a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. |