Bug reports | 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). | 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) | * Complete sketch (copy & paste, attachment, or a link to the code) | ||||
* Screenshot or the exact text of error messages | * Screenshot or the exact text of error messages | ||||
* Describe the observed behavior and explain which behavior you expected | * Describe the observed behavior and explain which behavior you expected | ||||
A thriving project is more than a pile of code. | 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. | 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. | 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. | 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: | Text file documentation style guide: | ||||
* Use Markdown with a .md suffix. | * Use Markdown with a .md suffix. | ||||
* "Underline" first-level (=) and second-level (-) headings (because easier to read in plain text). | * "Underline" first-level (=) and second-level (-) headings (because easier to read in plain text). | ||||
* Capitalize first letter of headings (no extra capitalization in headings). | * 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 | Submitting a pull request | ||||
------------------------- | ------------------------- | ||||
Pull request is the preferred way to contribute code and documentation. | Pull request is the preferred way to contribute code and documentation. |
----------------------------- | ----------------------------- | ||||
A [minimal keybrd sketch](/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino) | A [minimal keybrd sketch](/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino) | ||||
is 40 lines of code for a 4-key keyboard. | 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. | The sketch is small because the keybrd library takes care of the low-level details. | ||||
It runs the breadboard keyboard in this picture. | It runs the breadboard keyboard in this picture. | ||||
Example complex keybrd sketch | 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. | 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> | [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> | [mainSketch.ino](https://github.com/wolfv6/keybrd_DH/blob/master/examples/keybrd_DH/mainSketch.cpp)<br> |
keybrd version 0.x.x is for initial development. | keybrd version 0.x.x is for initial development. | ||||
keybrd version 1.0.0 will be released when the public API is stable. | keybrd version 1.0.0 will be released when the public API is stable. | ||||
<!-- | |||||
< !-- | |||||
Unreleased | 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) | 0.5.0 (2016-07-22) | ||||
------------------ | ------------------ | ||||
* Enhancements | * Enhancements |
Scanner_uC Scanner_IOE Scanner_ShiftRegsPISO | Scanner_uC Scanner_IOE Scanner_ShiftRegsPISO | ||||
PortIOE | |||||
PortInterface | PortInterface | ||||
/ \ | / \ | ||||
Port_PCA9655E Port_MCP23S17 (one Port class for each IOE type) | Port_PCA9655E Port_MCP23S17 (one Port class for each IOE type) | ||||
LEDInterface | LEDInterface | ||||
/ \ | / \ | ||||
LED_uC LED_IOE | |||||
LED_uC LED_Port | |||||
DebouncerInterface | DebouncerInterface | ||||
/ | \ / \ | / | \ / \ | ||||
strobePin PortWrite PortRead Code Code_LEDLock | strobePin PortWrite PortRead Code Code_LEDLock | ||||
| \ / \ | | | \ / \ | | ||||
| PortIOE readPins LED_IOE | |||||
| PortIOE readPins LED_Port | |||||
\___________________________/ \ | \___________________________/ \ | ||||
pin | pin | ||||
Example keybrd sketches are in the examples and tutorials directories. | Example keybrd sketches are in the examples and tutorials directories. | ||||
Extension libraries have their example sketches similarly located. | 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 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 | The following instructions are for setting active state for a Scanner_uC class | ||||
(Scanner_ShiftRegs74HC165 and Scanner_Port classes is similar). | (Scanner_ShiftRegs74HC165 and Scanner_Port classes is similar). | ||||
For active low: | For active low: | ||||
* Orient diodes with cathode (banded end) towards the write pins (row) | * 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: | For active high: | ||||
* Add an external 10k pull-down resistor to each read pin. | * Add an external 10k pull-down resistor to each read pin. | ||||
* Orient diodes with cathode (banded end) towards the read pins. | * 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 | Troubleshooting check list |
#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 |
#include "LED_IOE.h" | |||||
#include "LED_Port.h" | |||||
void LED_IOE::on() | |||||
void LED_Port::on() | |||||
{ | { | ||||
refPort.write(pin, HIGH); | refPort.write(pin, HIGH); | ||||
} | } | ||||
void LED_IOE::off() | |||||
void LED_Port::off() | |||||
{ | { | ||||
refPort.write(pin, LOW); | refPort.write(pin, LOW); | ||||
} | } |
#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 |
/* Basic LayerState with layer LED indictor lights. | /* Basic LayerState with layer LED indictor lights. | ||||
begin() should be called once to turn on LED for initial active layer. | 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 | class LayerState_LED : public LayerState | ||||
{ | { |
Example instantiation: | Example instantiation: | ||||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | 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 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 | //remaining pins can be used for LEDs | ||||
Diode orientation | Diode orientation |
Example instantiation: | Example instantiation: | ||||
const uint8_t IOE_ADDR = 0x20; //PCA9655E address, all 3 ADDR pins are grounded | 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 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 | //remaining pins can be used for LEDs | ||||
Diode orientation | Diode orientation |
#define CONFIG_KEYBRD_H | #define CONFIG_KEYBRD_H | ||||
#include <inttypes.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. | 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. | 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_uC: read_pins_t bits >= Scanner_uC::readPinCount | ||||
For Scanner_ShiftRegsPISO: read_pins_t bits >= Scanner_ShiftRegsPISO::byte_count * 8 | 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) | (For Scanner_IOE: I/O expanders are assumed to have 8 bits per port or less) |
#ifndef INSTANTIATIONS_SCANCODE_H | |||||
#define INSTANTIATIONS_SCANCODE_H | |||||
/* Include this file in multiple-layer keybrd sketches. | /* Include this file in multiple-layer keybrd sketches. | ||||
This file instandiates Code objects. | This file instandiates Code objects. | ||||
#ifndef INSTANTIATIONS_SCANCODENOTSHIFTED_H | |||||
#define INSTANTIATIONS_SCANCODENOTSHIFTED_H | |||||
/* This file instandiates Code_ScNS objects for multiple-layer keybrd sketches. | /* 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. | The scancode is always sent in the unshifted state regardless of shift key position. | ||||
Letters will still print as capital if CapsLck is on. | Letters will still print as capital if CapsLck is on. |
uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | 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. | "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); | Scanner_uC scanner(LOW, readPins, readPinCount); | ||||
/* HOW SCANNER OBJECTS WORK | /* 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. | If a key is pressed, the LOW strobe pulls that readPin LOW. | ||||
Then the scanner reads its readPins. | Then the scanner reads its readPins. | ||||
*/ | */ | ||||
Here we pack Code objects into Row objects. | Here we pack Code objects into Row objects. | ||||
The Row objects names in this sketch start with a "row_" followed by a row number. | 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 }; | Key* ptrsKeys_0[] = { &s_1, &s_2 }; | ||||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | ||||
/* | /* | ||||
loop() continually scans the matrix, one row at a time. | 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. | And when a key press is detected, the row sends the key's scancode. | ||||
scanDelay creates time intervals between matrix scans. | scanDelay creates time intervals between matrix scans. |
| **1** | fn | shift | | | **1** | fn | shift | | ||||
Each cell in the table's body represents a key. | 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. | "fn" is a layer key. Holding the fn key down makes it the active layer. | ||||
Releasing the fn key restores the normal 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 ################### | // ################## GLOBAL ################### | ||||
// ================= INCLUDES ================== | // ================= INCLUDES ================== | ||||
// =================== CODES =================== | // =================== CODES =================== | ||||
/* ---------------- LAYER CODE ----------------- | /* ---------------- LAYER CODE ----------------- | ||||
enum assigns layerId numbers to the layers. | 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 }; | enum layerIds { NORMAL, FN }; | ||||
LayerState layerState; | 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, | 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); | Code_LayerHold l_fn(FN, layerState); | ||||
/* =================== KEYS ==================== | /* =================== KEYS ==================== | ||||
Here we pack Codes into 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. | The Key object names in this sketch start with a "k_" followed by row-column coordinates. | ||||
*/ | */ | ||||
*/ | */ | ||||
/* =================== ROWS ==================== | /* =================== 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. | Rows are composed of a Key-pointer array. | ||||
Codes are a kind of Key that only have one layer. | 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). | 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 }; | Key* const ptrsKeys_0[] = { &k_00, &k_01 }; | ||||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); |
LayerState layerState; | 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. | When l_sym is pressed, SYM becomes the active layer. | ||||
*/ | */ | ||||
Code_LayerLock l_normal(ALPHA, layerState); | Code_LayerLock l_normal(ALPHA, layerState); |
/* keybrd_3c_sublayerNull.ino | /* keybrd_3c_sublayerNull.ino | ||||
This sketch: | 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 | runs on the first three columns of a breadboard keyboard | ||||
| Layout | **0** | **1** | **2** | | | Layout | **0** | **1** | **2** | | ||||
Scanner_uC scanner(LOW, readPins, readPinCount); | Scanner_uC scanner(LOW, readPins, readPinCount); | ||||
// =================== CODES =================== | // =================== CODES =================== | ||||
/* ---------------- LAYER CODE ----------------- | |||||
One LayerState object manages all 3 layers. | |||||
*/ | |||||
enum layerIds { ALPHA, SYM, NUM }; | 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 ----------------- | // ---------------- SCAN CODES ----------------- | ||||
Code_Sc s_a(KEY_A); | Code_Sc s_a(KEY_A); | ||||
Code_Null code_null; | Code_Null code_null; | ||||
/* =================== KEYS ==================== | /* =================== 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* const ptrsKeys_00[] = { &s_a, &s_minus, &s_1 }; | ||||
Key_LayeredKeys k_00(ptrsKeys_00); | Key_LayeredKeys k_00(ptrsKeys_00); | ||||
Key_LayeredKeys k_01(ptrsKeys_01); | 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. | 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 }; | Key* const ptrsKeys_02[] = { &s_c, &l_num, &code_null }; | ||||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | ||||
Row row_0(scanner, 0, ptrsKeys_0, keyCount_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); | uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); | ||||
Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | ||||
/* keybrd_3d_sublayerNested.ino | /* keybrd_3d_sublayerNested.ino | ||||
This sketch: | 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 | runs on the first three columns of a breadboard keyboard | ||||
| Layout | **0** | **1** | **2** | | | Layout | **0** | **1** | **2** | | ||||
Scanner_uC scanner(LOW, readPins, readPinCount); | 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 }; | 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 }; | 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 ----------------- | // ---------------- SCAN CODES ----------------- | ||||
Code_Sc s_a(KEY_A); | Code_Sc s_a(KEY_A); | ||||
Code_Sc s_1(KEY_1); | Code_Sc s_1(KEY_1); | ||||
/* =================== KEYS ==================== | /* =================== 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.) | (The Num sublayer only has one key because small example. Usually sublayers have multiple keys.) | ||||
*/ | */ | ||||
Key* const ptrsKeys_sub00[] = { &s_minus, &s_1 }; | Key* const ptrsKeys_sub00[] = { &s_minus, &s_1 }; | ||||
Key_LayeredKeys1 k_sub00(ptrsKeys_sub00); | 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* const ptrsKeys_00[] = { &s_a, &k_sub00 }; | ||||
Key_LayeredKeys k_00(ptrsKeys_00); | Key_LayeredKeys k_00(ptrsKeys_00); | ||||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | ||||
Row row_0(scanner, 0, ptrsKeys_0, keyCount_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); | uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); | ||||
Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | ||||
/* keybrd_3e_sublayerNestedScSc.ino | /* keybrd_3e_sublayerNestedScSc.ino | ||||
This sketch: | 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 | runs on the first three columns of a breadboard keyboard | ||||
| Layout | **0** | **1** | **2** | | | Layout | **0** | **1** | **2** | | ||||
Scanner_uC scanner(LOW, readPins, readPinCount); | Scanner_uC scanner(LOW, readPins, readPinCount); | ||||
// =================== CODES =================== | // =================== CODES =================== | ||||
// ---------------- LAYER CODE ----------------- | |||||
// ---------------- LAYER GROUP ---------------- | |||||
enum layerIds { ALPHA, SYM }; | 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 }; | 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 ----------------- | // ---------------- SCAN CODES ----------------- | ||||
Code_Sc s_a(KEY_A); | Code_Sc s_a(KEY_A); | ||||
Code_Sc s_1(KEY_1); | Code_Sc s_1(KEY_1); | ||||
/* =================== KEYS ==================== | /* =================== 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); | 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* const ptrsKeys_00[] = { &s_a, &sub_00 }; | ||||
Key_LayeredKeys k_00(ptrsKeys_00); | Key_LayeredKeys k_00(ptrsKeys_00); |
/* | /* | ||||
Normally all strobe pins are on one port, and all the read pins are on the other port. | 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. | 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. | readPins is a bit pattern, where 0=output, 1=input. | ||||
In portA, the first two pins are set to input for reading. | In portA, the first two pins are set to input for reading. | ||||
"<<" (bit shift left) and "|" (OR) are bitwise operators. | "<<" (bit shift left) and "|" (OR) are bitwise operators. | ||||
/* =================== ROWS ==================== | /* =================== ROWS ==================== | ||||
Left row names contain the letter 'L', while right row names conatain the letter 'R'. | 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: | strobePin has one of two formats: | ||||
* if refScanner a Scanner_uC, then strobePin is an Arduino pin number connected to this row | * 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 | * otherwise strobePin is a bit pattern, 1 indicating an IC pin connected to the row |
*/ | */ | ||||
LED_uC LED_normal(16); | LED_uC LED_normal(16); | ||||
LED_uC LED_fn(17); | LED_uC LED_fn(17); | ||||
LED_uC LED_CapsLck(21); | |||||
LED_uC LED_capsLck(21); | |||||
// =================== CODES =================== | // =================== CODES =================== | ||||
/* ---------------- LAYER CODE ----------------- | /* ---------------- 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 LayerState_LED turns on the LED of the active layer. | ||||
The prtsLayerLEDs[] array contains one LED per layer. | The prtsLayerLEDs[] array contains one LED per layer. | ||||
The active layerId is used as an index to dereference the prtsLayerLEDs[] array. | 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); | LayerState_LED layerState(prtsLayerLEDs); | ||||
Code_LayerHold l_fn(FN, layerState); | Code_LayerHold l_fn(FN, layerState); | ||||
/* ---------------- SCAN CODES ----------------- | /* ---------------- SCAN CODES ----------------- | ||||
When a Code_LEDLock object is pressed, it sends its scancode and updates the its LED. | 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_a(KEY_A); | ||||
Code_Sc s_x(KEY_X); | Code_Sc s_x(KEY_X); |
This sketch: | This sketch: | ||||
is a simple 1-layer keyboard with CapsLck indicator LED on I/O expander | 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: | This layout table shows left and right matrices: | ||||
//right matrix | //right matrix | ||||
#include <Port_MCP23S17.h> | #include <Port_MCP23S17.h> | ||||
#include <Scanner_IOE.h> | #include <Scanner_IOE.h> | ||||
#include <LED_IOE.h> | |||||
#include <LED_Port.h> | |||||
// ============ SPEED CONFIGURATION ============ | // ============ SPEED CONFIGURATION ============ | ||||
ScanDelay scanDelay(9000); | ScanDelay scanDelay(9000); | ||||
Scanner_uC scanner_L(LOW, readPins, readPinCount); | Scanner_uC scanner_L(LOW, readPins, readPinCount); | ||||
// ----------------- LEFT LEDs ----------------- | // ----------------- LEFT LEDs ----------------- | ||||
LED_uC LED_CapsLck(21); | |||||
LED_uC LED_capsLck(21); | |||||
// --------------- RIGHT SCANNER --------------- | // --------------- RIGHT SCANNER --------------- | ||||
const uint8_t IOE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | 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); | 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 =================== | // =================== CODES =================== | ||||
// ---------------- LAYER CODE ----------------- | // ---------------- LAYER CODE ----------------- | ||||
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 }; //array index matches enum layerIds | ||||
LayerState_LED layerState(prtsLayerLEDs); | LayerState_LED layerState(prtsLayerLEDs); | ||||
Code_Sc s_equal(KEY_EQUAL); | Code_Sc s_equal(KEY_EQUAL); | ||||
Code_Sc s_slash(KEY_SLASH); | 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 ==================== | // =================== KEYS ==================== | ||||
//row0 | //row0 | ||||
const uint8_t KEY_COUNT_R1 = sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1); | const uint8_t KEY_COUNT_R1 = sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1); | ||||
Row row_R1(scanner_R, 1<<1, ptrsKeys_R1, KEY_COUNT_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() | void setup() | ||||
{ | { | ||||
Keyboard.begin(); | Keyboard.begin(); | ||||
scanner_R.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() | void loop() |
/* keybrd_6_active_high.ino | /* keybrd_6_active_high.ino | ||||
This sketch: | 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** | | | Layout | **0** | **1** | | ||||
|:------:|-------|-------| | |:------:|-------|-------| | ||||
| **0** | 1 | a | | |||||
| **1** | b | c | | |||||
| **0** | 1 | 2 | | |||||
| **1** | a | b | | |||||
*/ | */ | ||||
// ################## GLOBAL ################### | // ################## GLOBAL ################### | ||||
// ================= INCLUDES ================== | // ================= INCLUDES ================== | ||||
#include <ScanDelay.h> | |||||
#include <Code_Sc.h> | #include <Code_Sc.h> | ||||
#include <Row_uC.h> | |||||
#include <Row.h> | |||||
#include <Scanner_uC.h> | |||||
#include <ScanDelay.h> | |||||
// ============ SPEED CONFIGURATION ============ | // ============ SPEED CONFIGURATION ============ | ||||
ScanDelay scanDelay(9000); | 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 =================== | // =================== CODES =================== | ||||
Code_Sc s_1(KEY_1); | |||||
Code_Sc s_a(KEY_A); | Code_Sc s_a(KEY_A); | ||||
Code_Sc s_b(KEY_B); | 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 ==================== | // =================== 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 #################### | // ################### MAIN #################### | ||||
void setup() | void setup() |
* Tutorial 1 builds a breadboard keyboard and covers basic keyboard-hardware knowledge. | * 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. | * 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 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. | Tutorials from 8 up are advance topics about the keybrd library. | ||||
The tutorials assume the reader: | The tutorials assume the reader: |
Breadboard keyboards are useful for: | Breadboard keyboards are useful for: | ||||
* learning keyboard electronics - microcontroller, key matrix, diode, shift registers, I/O expander | * 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 | * prototyping circuits before making a PCB | ||||
Breadboard keyboard starter kit | Breadboard keyboard starter kit | ||||
------------------------------------ | ------------------------------------ | ||||
The basic breadboard keyboard has 4 switches. | 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 Teensy LC microcontroller is on the left. | ||||
A key matrix with 4 switches is to the right. | A key matrix with 4 switches is to the right. | ||||
A jumper connects the top row to the microcontroller. | A jumper connects the top row to the microcontroller. | ||||
A short wire connects the bottom 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 2 and 3 use the same basic breadboard keyboard pictured above. | ||||
Tutorials 4, 5, and 6 add more components to the basic breadboard keyboard. | Tutorials 4, 5, and 6 add more components to the basic breadboard keyboard. | ||||
* Teensy LC is on the left | * Teensy LC is on the left | ||||
* switch leads are oriented to connect diodes to columns (pictured below) | * switch leads are oriented to connect diodes to columns (pictured below) | ||||
* diode cut offs connect terminal strips into columns | * 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") | ![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. | 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). | * [Teensy LC pinout diagram](https://www.pjrc.com/teensy/card6a_rev2.png). | ||||
The keybrd library uses the word "strobe", which means powering one row for a very short time. | 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. | 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 | Exercises | ||||
--------- | --------- |
After reading the sketch you will be able to modify it to suite your own single-layer keyboard design. | 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 | Exercises | ||||
--------- | --------- |
Multi-layer nomenclature | Multi-layer nomenclature | ||||
------------------------ | ------------------------ | ||||
**[layers](http://deskthority.net/wiki/Layer)** - are key bindings provided by the keyboard firmware. For example, | **[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). | * 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. | * The [Neo layout](http://neo-layout.org/index_en.html) has 6 layers. | ||||
**active layer** - is the layer currently used by the keyboard. | **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). | **layer scheme** - is a system for changing the active layer while typing (a single-layer scheme does not change layers). | ||||
Code classes | 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_ScS | ||||
* Code_ScNS | * Code_ScNS | ||||
* Code_Shift | * Code_Shift | ||||
* Code_LayerHold | * Code_LayerHold | ||||
* Code_LayerLock | * 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 | 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. | 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). | 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 annotations explain how multi-layer keyboards work. | ||||
The sketch uses three layer-scheme classes: | The sketch uses three layer-scheme classes: | ||||
``` | ``` | ||||
class Code_Layer | class Code_Layer | ||||
{ | { | ||||
int layerId; | |||||
const int layerId; | |||||
LayerState& refLayerState; | LayerState& refLayerState; | ||||
press() { refLayerState.setActiveLayer(layerId); } | press() { refLayerState.setActiveLayer(layerId); } | ||||
}; | }; | ||||
}; | }; | ||||
``` | ``` | ||||
**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. | When a Key_LayeredKeys object is pressed, it gets the active layerId from LayerState, and sends the corresponding key. | ||||
``` | ``` | ||||
class Key_LayeredKeys | class Key_LayeredKeys | ||||
* Key_LayeredScSc (covered in next tutorial) | * Key_LayeredScSc (covered in next tutorial) | ||||
* Key_LayeredCodeSc | * 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 | Exercises | ||||
--------- | --------- | ||||
1) Compile and run [keybrd_3a_multi-layerHold.ino](keybrd_3a_multi-layerHold/keybrd_3a_multi-layerHold.ino) | 1) Compile and run [keybrd_3a_multi-layerHold.ino](keybrd_3a_multi-layerHold/keybrd_3a_multi-layerHold.ino) |
Tutorial 3cde - sublayer keyboard | 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 | Sublayer nomenclature | ||||
--------------------- | --------------------- | ||||
These definitions are specific to the keybrd library. | 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** | | | Layout | **0** | **1** | **2** | | ||||
|:------:|:-----:|:-----:|:-----:| | |:------:|:-----:|:-----:|:-----:| | ||||
| **1** | Alpha | Sym | Enter | | | **1** | Alpha | Sym | Enter | | ||||
Each cell in the table's body represents a key. | 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. | 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. | 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. | 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 | Sketch 3e - specialized layered keys | ||||
------------------------------------ | ------------------------------------ | ||||
Refer to keybrd_3e_sublayerNestedScSc.ino | |||||
Key_LayeredKeys constructor takes any number of code or key arguments. | Key_LayeredKeys constructor takes any number of code or key arguments. | ||||
Key_LayeredScSc is more specialized. It's constructor takes exactly two scancode 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_Layered classes include: | ||||
* Key_LayeredKeys (any number of codes or keys) | * Key_LayeredKeys (any number of codes or keys) | ||||
* Key_LayeredScSc (specialized for two scancodes) | * Key_LayeredScSc (specialized for two scancodes) | ||||
* Key_LayeredCodeSc (specialized for one code and one scancode) | * 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** | | | Layout | **0** | **1** | **2** | | ||||
|:------:|:-----:|:-----:|:-----:| | |:------:|:-----:|:-----:|:-----:| | ||||
| **0** | a - 1 | b = | c Ent | | | **0** | a - 1 | b = | c Ent | | ||||
| **1** | Alpha | Sym | Num | | | **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 | Complex layerschemes | ||||
-------------------- | -------------------- | ||||
The basic LayerState class used in the tutorials is sufficient for implementing many layer schemes. | 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. | 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. | 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. | 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: | DH Code_Layer classes include: | ||||
* Code_LayerLock | * Code_LayerLock | ||||
* Key_LayeredNumber | * Key_LayeredNumber | ||||
* Key_LayeredNumber_00 | * Key_LayeredNumber_00 | ||||
Exercises | 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** | | | Layout | **0** | **1** | **2** | | ||||
|:------:|:-----:|:-----:|:-----:| | |:------:|:-----:|:-----:|:-----:| | ||||
| **0** | a - 1 | b = 2 | c Num | | | **0** | a - 1 | b = 2 | c Num | | ||||
| **1** | Alpha | Sym | Enter | | | **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 | | |||||
The [keybrd_3f_autoShift.ino](keybrd_3f_autoShift/keybrd_3f_autoShift.ino) sketch explains the AutoShift feature. | 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). | 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 | Exercises | ||||
--------- | --------- |
## Split keyboard connections table | ## Split keyboard connections table | ||||
| connection type | controller pins | wire count | max keys | | | 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 | 3 | 7 | 12 | | ||||
| just cable | 4 | 8 | 16 | | | just cable | 4 | 8 | 16 | | ||||
| just cable | 4 | 9 | 20 | | | just cable | 4 | 9 | 20 | | ||||
Fewer wires makes a cable more flexible. | Fewer wires makes a cable more flexible. | ||||
A flexibility cable makes it easy to position the keyboard and route the cable. | 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. | 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. | 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 | ## Cables table | ||||
| connector name | wire count | | | connector name | wire count | | ||||
|:-----------------------------------------------------:|:----------:| | |||||
|-------------------------------------------------------|:----------:| | |||||
| TRRS | 4 | | | TRRS | 4 | | ||||
| 6-pin mini-DIN connector (PS/2) | 4 | | | 6-pin mini-DIN connector (PS/2) | 4 | | ||||
| USB 2 | 4 | | | USB 2 | 4 | | ||||
It's available at Walmart (9/19/16) if you want to feel the merchandise before you buy. | It's available at Walmart (9/19/16) if you want to feel the merchandise before you buy. | ||||
All the modular connectors are flat. | 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> | <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>. | <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>. |
Overview of split keyboard with shift registers | 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. | 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. | 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 | Building a split breadboard keyboard with shift registers | ||||
--------------------------------------------------------- | --------------------------------------------------------- | ||||
Add components to the breadboard as shown in the picture. | 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. | 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. | 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). | 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) | 74HC165 left (lower half of breadboard) | ||||
``` | ``` | ||||
![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 | 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. | [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. | ||||
--------- | --------- | ||||
1. Guess what happens if an unused input pin is not grounded? Try it. | 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> | <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>. | <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 | |||||
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. | 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 | 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 (blue buses) are connected to the microcontroller. | ||||
Two rows (red buses) are connected to the I/O expander. | 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| | |CONNECTION |Teensy LC|MCP23S17| | ||||
|:------------------:|---------|--------| | |:------------------:|---------|--------| | ||||
|Master In, Slave Out| MISO0 | SO | | |Master In, Slave Out| MISO0 | SO | | ||||
|Chip Select | CS0 | /CS | | |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 | <!-- todo schematic with IOE power decoupling capacitor | ||||
This schematic was written by consulting the I/O expander's datasheet and using the ?? tool. --> | This schematic was written by consulting the I/O expander's datasheet and using the ?? tool. --> |
Tutorial 5a - indicator LEDs | Tutorial 5a - indicator LEDs | ||||
============================ | ============================ | ||||
Keyboards often have LEDs to indicate CapsLock, NumLock, and other states. | 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 | Adding LEDs to the basic breadboard keyboard | ||||
-------------------------------------------- | -------------------------------------------- | ||||
LED anodes (the longer lead) are powered by pins 16, 17, and 21. | 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. | 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 | 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. | 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 | LED forward voltage | ||||
------------------- | ------------------- | ||||
Most blue and green LEDs have about 3.3 forward voltage, which does not reliably illuminate on Teensy LC's 3.3 volts. | 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. | Most red and yellow LEDs have around 2.2 forward voltage. | ||||
So use red and yellow LEDs on Teensy LC. | 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 | 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. | 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. | Less resistance makes the LED brighter. | ||||
Too little resistance will burn out the LED. | 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. | The current supplied to an LED should always be limited by a resistor or some other device. | ||||
LED current limiting resistor values | 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 | output-pin Supply Voltage Vs | ||||
LED Forward Voltage Vf | LED Forward Voltage Vf | ||||
From Ohm's Law, minimum current limiting restiance R = (Vs - Vf) / 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: | Teensy LC output-pin capacities are: | ||||
* four 20 mA pins (5, 16, 17, 21) | * four 20 mA pins (5, 16, 17, 21) | ||||
* Teensy LC on-board LED is on pin 13. | * 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. | 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. | 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 | http://www.rfcafe.com/references/electrical/resistor-values.htm | ||||
Calculate the minimum resistance needed for your LED and Supply Voltage. | 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> | <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>. | <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>. |
Adding LEDs to the basic breadboard keyboard | 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. | Add components to the breadboard as shown in the picture. | ||||
The two clear plastic cylinders are red and yellow LEDs. | The two clear plastic cylinders are red and yellow LEDs. | ||||
Each LED is in series with a 4.7k Ohm current limiting resistor. | 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. | 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 | keybrd sketch with LEDs on IOE | ||||
------------------------------ | ------------------------------ |
Tutorial 6 - Active high | 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 | Pull-up resistors | ||||
----------------- | ----------------- | ||||
Active low | 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. | "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. | 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. | 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 | Active high | ||||
"Active high" means that if a switch is pressed (active), the read pin is 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. | 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. | 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. | 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 | * 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 | 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> | <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>. | <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>. |