@@ -12,55 +12,6 @@ This guide is for the maintainers and developers of the keybrd library and it's | |||
It is assumed the reader is familiar with the C++ language including pointers, objects, classes, static class variables, composition, aggregation, inheritance, polymorphism, and enum. | |||
Row, Scanner, and Debouncer classes use bit manipulation. | |||
Custom Row classes | |||
------------------ | |||
Row classes are central to the keybrd library. | |||
Row is an abstract base class that allows flexibility in designing derived Row classes: | |||
* Row functions can be overridden in a derived class | |||
* choice of Debouncers | |||
* choice of Scanners | |||
This example illustrates the custom Row classes for a fictional keybrd_Ext extension library. | |||
The keybrd_Ext library is for a split keyboard with a matrix on each hand and sticky keys. | |||
Row_Ext::keyWasPressed() overrides Row::keyWasPressed() which is used to unstick sticky keys. | |||
Row_Ext_uC and Row_Ext_ShiftRegisters are a custom classes composed of stock keybrd library classes.<br> | |||
Row_Ext_uC uses Scanner_uC to scan the primary matrix.<br> | |||
Row_Ext_ShiftRegisters uses Scanner_ShiftRegs74HC165 to scan the peripheral matrix. | |||
Class inheritance diagram | |||
``` | |||
Row | |||
| | |||
Row_Ext (override Row::keyWasPressed() ) | |||
/ \ | |||
Row_Ext_uC Row_Ext_ShiftRegisters (inherit Row_Ext::keyWasPressed() ) | |||
Scanner_uC Scanner_ShiftRegs74HC165 | |||
``` | |||
Dependency diagram | |||
``` | |||
________ Row_Ext_uC[1] ______________ | |||
/ | \ | |||
Scanner_uC[1] Debouncer_Samples[1] Key[1..*] | |||
/ | | |||
strobePin[1] Code[1..*] | |||
_________ Row_Ext_ShiftRegisters[1] ________ | |||
/ \ \ | |||
Scanner_ShiftRegs74HC165[1] Debouncer_Samples[1] Key[1..*] | |||
| | | |||
strobePin[1] Code[1..*] | |||
``` | |||
Class inheritance diagrams | |||
-------------------------- | |||
@@ -73,15 +24,15 @@ Keybrd library class inheritance diagram | |||
Scanner_uC Scanner_IOE Scanner_ShiftRegsPISO | |||
PortIOE | |||
PortIOE | |||
PortWrite | |||
| | |||
PortWrite_PCA9655E (one PortWrite class for each IOE type) | |||
PortWrite | |||
/ \ | |||
PortWrite_PCA9655E PortWrite_MCP23S17 (one PortWrite class for each IOE type) | |||
PortRead | |||
| | |||
PortRead_PCA9655E (one PortRead class for each IOE type) | |||
PortRead | |||
/ \ | |||
PortRead_PCA9655E PortRead_MCP23S17 (one PortRead class for each IOE type) | |||
_ LED _ | |||
/ \ | |||
@@ -127,48 +78,48 @@ Dependency diagrams | |||
Dependency diagram of example single-layer keyboard with LEDs | |||
``` | |||
_ Row_uC[1..*] _ | |||
/ | \ | |||
Scanner_uC Debouncer Keys[1..*] __ | |||
| \ | |||
Code[1..*] Code_LEDLock[1..*] | |||
| | |||
LED_PinNumber[1] | |||
____ Row ______ | |||
/ | \ | |||
Scanner_uC Debouncer Keys __ | |||
| | \ | |||
readPins Code Code_LEDLock | |||
| | |||
LED_PinNumber | |||
``` | |||
Dependency diagram of example multi-layer keyboard with layer LEDs | |||
``` | |||
LayerStates[1..*] | |||
________ Row_uC[1..*] ___________/__ | \ | |||
/ | \ / \ | \ | |||
Scanner_uC[1] Debouncer[1] Keys[1..*] / Code_Layer[1..*] LED_PinNumber[0..*] | |||
| / | |||
Code[1..*] | |||
LayerStates | |||
___________ Row _______/__ | \ | |||
/ / \ / \ | \ | |||
Scanner_uC Debouncer Keys / Code_Layer LED_PinNumber | |||
| \ / | |||
readPins Code | |||
``` | |||
Dependency diagram of example peripheral matrix with shift registers | |||
Dependency diagram of example shift registers Row | |||
``` | |||
Row_ShiftRegisters[1..*] | |||
/ \ \ | |||
RowScanner_ShiftRegisters Debouncer Keys[1..*] | |||
| | |||
Code[1..*] | |||
_______ Row _______ | |||
/ | \ | |||
RowScanner_ShiftRegsPISO Debouncer Keys | |||
| | |||
Code | |||
``` | |||
Dependency diagram of example peripheral matrix with I/O Expander and LEDs | |||
Dependency diagram of example I/O expander matrix with LEDs | |||
``` | |||
_____ Row_IOE[1..*] _________ | |||
/ \ \ | |||
__ Scanner_Port[1] __ Debouncer[1] Keys[1..*] __ | |||
/ | \ | \ | |||
PortWrite[1] strobePin[1] PortRead[1] Code[1..*] Code_LEDLock[1..*] | |||
\ / \ | | |||
\ / ColPins[1..*] LED[1] | |||
\ / | |||
PortIOE[0..*] | |||
_________ Row _________ | |||
/ \ \ | |||
__ Scanner_IOE __ Debouncer Keys | |||
/ | \ / \ | |||
strobePin PortWrite PortRead Code Code_LEDLock | |||
| \ / \ | | |||
| PortIOE readPins LED | |||
\___________________________/ \ | |||
pin | |||
``` | |||
@@ -246,7 +197,6 @@ Trace of keybrd scan | |||
Arduino does not have a debugger. | |||
So here is a list of functions in the order that they are called. | |||
The trace is of a one-row single-layer keybrd scan. | |||
Refer to it like a table of contents while reading the keybrd library. | |||
``` | |||
loop() for each row |
@@ -175,6 +175,9 @@ In this example, row_0 has 2 read pins and 2 keys: | |||
Key* ptrsKeys_0[] = { &s_a, &s_b }; | |||
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0); | |||
``` | |||
* The scanner should have enough readPins to cover all the keys of the longest row. | |||
(rows with fewer keys will have unused read pins) | |||
* read_pins_t size in keybrd/src/config_keybrd.h file should cover all the read pins. | |||
* Some of the constructors take array-element-count arguments, make sure that the correct counts are passed to the constructors. Or use sizeof() like the preceding example. | |||
* For multi-layered keyboards, the number of codes in each Key_Layered should equal the number of layers. | |||
@@ -26,9 +26,9 @@ ScanDelay scanDelay(9000); | |||
// ================ LEFT SCANNER =============== | |||
uint8_t readPins_L[] = {0, 1}; | |||
uint8_t readPinCount_L = sizeof(readPins_L)/sizeof(*readPins_L); | |||
uint8_t READPIN_COUNT_L = sizeof(readPins_L)/sizeof(*readPins_L); | |||
Scanner_uC scanner_L(HIGH, readPins_L, readPinCount_L); | |||
Scanner_uC scanner_L(HIGH, readPins_L, READPIN_COUNT_L); | |||
// =============== RIGHT SCANNER =============== | |||
const uint8_t PortIOE::DEVICE_ADDR = 0x18; |
@@ -1,6 +1,7 @@ | |||
#include "Debouncer_Not.h" | |||
/* debounce() sets debounced and returns debouncedChanged. All variables are bitwise. | |||
/* debounce() sets debounced and returns debouncedChanged. | |||
All parameters and variables are bitwise. | |||
For parameters, 1 means pressed, 0 means released. | |||
For return, 1 means debounced changed. | |||
*/ |
@@ -31,7 +31,8 @@ strong electromagnetic interference (EMI) may need a larger SAMPLE_COUNT_MACRO f | |||
*/ | |||
#include "Debouncer_Samples.h" | |||
/* debounce() sets debounced and returns debouncedChanged. All variables are bitwise. | |||
/* debounce() sets debounced and returns debouncedChanged. | |||
All parameters and variables are bitwise. | |||
For parameters, 1 means pressed, 0 means released. | |||
For return, 1 means debounced changed. | |||
*/ |
@@ -15,7 +15,7 @@ class LED_PCA9655E: public LED | |||
//PortIOE& port; | |||
//const uint8_t outputByteCommand; //General Purpose Input/Ouput register address | |||
PortWrite_PCA9655E& refPort; | |||
const uint8_t pin; //bitwise pin to LED | |||
const uint8_t pin; //bitwise IOE pin to LED | |||
public: | |||
LED_PCA9655E(PortWrite_PCA9655E& refPort, const uint8_t pin) |
@@ -29,7 +29,7 @@ portNumber: If the I/O expander uses port letters, use 0 inplace of A, use 1 inp | |||
struct PortIOE | |||
{ | |||
static const uint8_t DEVICE_ADDR; | |||
const uint8_t num; //port number | |||
const uint8_t num; //port identification number | |||
uint8_t outputVal; //bitwise value of output register for LEDs | |||
PortIOE(const uint8_t portNumber) |
@@ -4,7 +4,7 @@ | |||
*/ | |||
uint8_t PortMCP23S17::transfer(const uint8_t command, const uint8_t registerAddr, const uint8_t data) | |||
{ | |||
uint8_t portState; //bit wise | |||
uint8_t portState; //bitwise | |||
digitalWrite(SS, LOW); //enable Slave Select | |||
SPI.transfer(command); //write or read command |
@@ -9,7 +9,7 @@ void PortRead_MCP23S17::begin(const uint8_t strobeOn) | |||
{ | |||
pullUp = readPins; | |||
} | |||
else | |||
else //active high requires external pull-down resistors | |||
{ | |||
pullUp = 0; | |||
} |
@@ -26,13 +26,13 @@ void Row::process() | |||
/* | |||
send() calls key's press() or release() function if key was pressed or released. | |||
Both parameters are bitwise. | |||
Parameter debouncedChanged is bitwise. | |||
*/ | |||
void Row::send(const uint8_t keyCount, const read_pins_t debouncedChanged) | |||
{ | |||
read_pins_t isFallingEdge; //bitwise, 1 means falling edge | |||
read_pins_t isRisingEdge; //bitwise, 1 means rising edge | |||
read_pins_t readMask; //bitwise, active bit is 1 | |||
read_pins_t readPosition; //bitwise, active bit is 1 | |||
uint8_t i; //index for ptrsKeys[i] array | |||
//bit=1 if last debounced changed from 1 to 0, else bit=0 | |||
@@ -41,15 +41,15 @@ void Row::send(const uint8_t keyCount, const read_pins_t debouncedChanged) | |||
//bit=1 if last debounced changed from 0 to 1, else bit=0 | |||
isRisingEdge = debouncedChanged & debounced; | |||
for (readMask=1, i=0; i < keyCount; readMask<<=1, i++) //for each key in row | |||
for (readPosition=1, i=0; i < keyCount; readPosition<<=1, i++) //for each key in row | |||
{ | |||
//release before press avoids impossible key sequence | |||
if (readMask & isFallingEdge) //if key was released | |||
if (readPosition & isFallingEdge) //if key was released | |||
{ | |||
ptrsKeys[i]->release(); | |||
} | |||
if (readMask & isRisingEdge) //if key was pressed | |||
if (readPosition & isRisingEdge) //if key was pressed | |||
{ | |||
ptrsKeys[i]->press(); | |||
keyWasPressed(); |
@@ -7,11 +7,12 @@ | |||
#include <Key.h> | |||
#include <ScannerInterface.h> | |||
#include <Debouncer_Samples.h> | |||
#include <Debouncer_Not.h> | |||
/* | |||
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_IOE, then strobePin is bitwise, 1 indicating IC pin connected to this row | |||
* otherwise strobePin is bitwise, 1 indicating an IC pin connected to this row | |||
*/ | |||
class Row | |||
{ | |||
@@ -25,7 +26,8 @@ class Row | |||
Key *const *const ptrsKeys; //array of Key pointers | |||
protected: | |||
const uint8_t keyCount; //number of read pins | |||
Debouncer_Samples debouncer; | |||
//Debouncer_Samples debouncer; | |||
Debouncer_Not debouncer; //todo | |||
read_pins_t debounced; //bitwise state of keys after debouncing, 1=pressed, 0=released | |||
public: | |||
Row(ScannerInterface& refScanner, const uint8_t strobePin, |
@@ -17,8 +17,8 @@ void Scanner_IOE::begin() | |||
} | |||
/* scan() is called on every iteration of sketch loop(). | |||
strobePin is bitwise, 1 means that row pin is active. | |||
scan() strobes the row's strobePin and retuns state of port's input pins. | |||
Bitwise variables are 1 bit per key. | |||
*/ | |||
read_pins_t Scanner_IOE::scan(const uint8_t strobePin) | |||
{ | |||
@@ -27,6 +27,7 @@ read_pins_t Scanner_IOE::scan(const uint8_t strobePin) | |||
//strobe on | |||
refPortWrite.write(strobePin, strobeOn); | |||
delayMicroseconds(3); //time to stabilize voltage | |||
//delayMicroseconds(300); //todo | |||
//read the port pins | |||
readState = refPortRead.read(); |
@@ -8,11 +8,14 @@ If your 8-bit AVR (Teensy 2) is running low on memory, using a smaller type save | |||
Using smaller types on a 32-bit uC (Teensy LC) would accomplish nothing. | |||
*/ | |||
/* Use a read_pins_t size that covers the last 1 bit in bitwise Scanner_IOE::strobePin. | |||
/* Use a read_pins_t size that covers all read pins of all Scanner objects i.e. | |||
For Scanner_uC: read_pins_t bits >= Scanner_uC::readPinCount | |||
For Scanner_ShiftRegsPISO: read_pins_t bits >= Scanner_ShiftRegsPISO::byte_count * 8 | |||
(For Scanner_IOE: I/O expanders are assumed to have 8 bits per port or less) | |||
*/ | |||
//typedef uint8_t read_pins_t; | |||
typedef uint8_t read_pins_t; | |||
//typedef uint16_t read_pins_t; | |||
typedef uint32_t read_pins_t; | |||
//typedef uint32_t read_pins_t; | |||
/* SAMPLE_COUNT_MACRO is used in Debouncer_Samples.h | |||
SAMPLE_COUNT_MACRO = 4 is very reliable for a keyboard. |
@@ -2,15 +2,15 @@ | |||
| Layout | **0** | **1** | | |||
|:------:|-------|-------| | |||
| **0** | 1 | a | | |||
| **1** | 2 | b | | |||
| **0** | 1 | 2 | | |||
| **1** | a | b | | |||
*/ | |||
// ################## GLOBAL ################### | |||
// ================= INCLUDES ================== | |||
#include <ScanDelay.h> | |||
#include <Code_Sc.h> | |||
#include <Row.h> | |||
#include <Scanner_uC.h> | |||
#include <ScanDelay.h> | |||
// ============ SPEED CONFIGURATION ============ | |||
ScanDelay scanDelay(9000); | |||
@@ -29,11 +29,11 @@ Code_Sc s_1(KEY_1); | |||
Code_Sc s_2(KEY_2); | |||
// =================== ROWS ==================== | |||
Key* ptrsKeys_0[] = { &s_1, &s_a }; | |||
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_2, &s_b }; | |||
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); | |||
@@ -2,20 +2,20 @@ | |||
This sketch: | |||
is firmware for a simple 1-layer keyboard | |||
runs on the first two rows and columns of a breadboard keyboard | |||
runs on two rows and two columns of a breadboard keyboard | |||
This layout table shows how keys are arranged on the keyboard: | |||
| Layout | **0** | **1** | | |||
|:------:|-------|-------| | |||
| **0** | shift | a | | |||
| **1** | b | c | | |||
| **0** | 1 | 2 | | |||
| **1** | a | b | | |||
The layout's row and column numbers are in the headers. | |||
Each cell in the table's body represents a key. | |||
The following sketch is annotated with a walk-through narrative enclosed in comment blocks. | |||
Each comment block explains the next one or two lines of code. | |||
Each comment block explains one or two lines of code after the comnent. | |||
keybrd objects are instantiated under the "GLOBAL" heading. | |||
The keyboard runs at the end of the sketch, under the "MAIN" heading. | |||
@@ -24,37 +24,40 @@ The keyboard runs at the end of the sketch, under the "MAIN" heading. | |||
/* ================= INCLUDES ================== | |||
All the includes in this sketch are to keybrd library classes. | |||
*/ | |||
#include <ScanDelay.h> | |||
#include <Code_Sc.h> | |||
#include <Row_uC.h> | |||
#include <Row.h> | |||
#include <Scanner_uC.h> | |||
#include <ScanDelay.h> | |||
/* ============ SPEED CONFIGURATION ============ | |||
ScanDelay specifies microsecond between matrix scans. | |||
Keyboard switches are made of moving contacts. | |||
When the contacts close, they bounce apart one or more times before making steady contact. | |||
ScanDelay gives the switches time to debounce. | |||
ScanDelay specifies microsecond between matrix scans. | |||
*/ | |||
ScanDelay scanDelay(9000); | |||
/* ================ ACTIVE STATE =============== | |||
The read pins detect which keys are pressed while a row is strobed. | |||
STROBE_ON and STROBE_OFF define the logic levels for the strobe. | |||
"Active low" means that if a switch is pressed (active), the read pin is low. | |||
To make this sketch active low, STROBE_ON should be LOW (tutorial 6 coveres this in more detail). | |||
*/ | |||
const bool Scanner_uC::STROBE_ON = LOW; //set scanner for active low | |||
const bool Scanner_uC::STROBE_OFF = HIGH; | |||
/* ================= PINS ================= | |||
Microcontroller 14 and 15 are connected to the matrix columns. | |||
These readPins detect which keys are pressed while a row is strobed. | |||
/* ================== SCANNER ================== | |||
Microcontroller pins 14 and 15 are connected to the matrix columns. | |||
sizeof() is used to compute the number of array elements. | |||
This eliminates the risk of forgetting to update the count | |||
This eliminates the risk of a programmer forgetting to update a count | |||
after adding or removing an element from the array. | |||
*/ | |||
uint8_t readPins[] = {14, 15}; | |||
uint8_t READ_PIN_COUNT = 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. | |||
"Active low" means that if a switch is pressed (active), the read pin is low. | |||
The scanner uses readPins, readPinCount to read the colums. | |||
*/ | |||
Scanner_uC scanner(LOW, readPins, readPinCount); | |||
/* HOW SCANNER OBJECTS WORK | |||
The scanner object strobes a row. | |||
If a key is pressed, the LOW strobe pulls that readPin LOW. | |||
Then the scanner reads its readPins. | |||
*/ | |||
/* =================== CODES =================== | |||
Four Codes are instantiated, one for each key in the layout. | |||
@@ -65,25 +68,27 @@ When Code_Sc is pressed, it sends the scancode. | |||
*/ | |||
Code_Sc s_a(KEY_A); | |||
Code_Sc s_b(KEY_B); | |||
Code_Sc s_c(KEY_C); | |||
Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT); | |||
Code_Sc s_1(KEY_1); | |||
Code_Sc s_2(KEY_2); | |||
/* =================== ROWS ==================== | |||
Here we pack Code objects into Row objects. | |||
The Row objects names in this sketch start with a "row_" followed by a row number. | |||
Row_uC constructor has four parameters: | |||
1) strobePin connected to the row. | |||
2) readPins[] connected to the colums. | |||
3) the number of readPins. | |||
4) ptrsKeys[] containing all the Code objects of the row, one Code object per key. | |||
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. | |||
*/ | |||
Key* ptrsKeys_0[] = { &s_shift, &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 #################### | |||
setup() is used to initialize the keyboard firmware. Keyboard.begin() should be called once. | |||
@@ -99,7 +104,7 @@ Each row object strobes its strobePin and reads the readPins. | |||
And when a key press is detected, the row sends the key's scancode. | |||
scanDelay creates time intervals between matrix scans. | |||
A debouncer uses this time interval to debounce key presses and releases. | |||
The delay is needed so that the debouncer is not overwelmed. | |||
*/ | |||
void loop() | |||
{ |
@@ -10,7 +10,7 @@ This sketch: | |||
| **1** | fn | b 2 | | |||
Each cell in the table's body represents a key. | |||
The layered keys in row 0 have two layers; one character for each layer. | |||
The layered keys in column 1 have two layers; one character for each layer. | |||
Letters 'a' and 'b' are on the normal layer. Numbers '1' and '2' are on the fn layer. | |||
Holding the fn key down makes it the active layer. Releasing the fn key restores the normal layer. | |||
*/ | |||
@@ -23,19 +23,18 @@ Holding the fn key down makes it the active layer. Releasing the fn key restore | |||
#include <Key_LayeredKeysArray.h> | |||
//Matrix | |||
#include <Row_uC.h> | |||
#include <Row.h> | |||
#include <Scanner_uC.h> | |||
#include <ScanDelay.h> | |||
// ============ SPEED CONFIGURATION ============ | |||
ScanDelay scanDelay(9000); | |||
// ================ ACTIVE STATE =============== | |||
const bool Scanner_uC::STROBE_ON = LOW; | |||
const bool Scanner_uC::STROBE_OFF = HIGH; | |||
// =================== PINS ==================== | |||
// ================== SCANNER ================== | |||
uint8_t readPins[] = {14, 15}; | |||
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins); | |||
uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||
Scanner_uC scanner(LOW, readPins, readPinCount); | |||
/* =================== CODES =================== | |||
The CODES section instantiates six codes, one for each item in the layout. | |||
@@ -52,8 +51,8 @@ LayerState layerState; | |||
/* | |||
NORMAL=0 and FN=1. LayerState's default layer id is 0. | |||
The Code_LayerHold constructor has two parameters: | |||
1) the layer that will be active while the key is held down. | |||
2) a LayerState | |||
1) the layer that will be active 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 1. | |||
When l_fn is released, it tells layerState that layer 1 is released, and layerState restores the default layer. | |||
*/ | |||
@@ -69,8 +68,6 @@ Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT); | |||
/* =================== KEYS ==================== | |||
Here we pack Codes into keys. | |||
The Key_LayeredKeysArray constructor takes one array of Code pointers - one Code object per layer. | |||
Key_LayeredKeysArray uses layer id numbers as array indexes. | |||
Thus Key_LayeredKeysArray calls the Code corresponding to the active layer id. | |||
The Key object names in this sketch start with a "k_" followed by row-column coordinates. | |||
*/ | |||
@@ -86,8 +83,9 @@ Thus Key_LayeredKeysArray can call layerState to get the active layer id. | |||
LayerStateInterface& Key_LayeredKeysArray::refLayerState = layerState; | |||
/* HOW LAYERED OBJECTS WORK | |||
When a Key_LayeredKeysArray object is pressed, it gets the active layer id from layerState | |||
It then uses the layer id as an array index to send the scancode for the active layer. | |||
When a Key_LayeredKeysArray object is pressed, it gets the active layer id from layerState. | |||
It then uses the layer id as an array index to call the Code of the active layer. | |||
The Code object then sends its scancode over USB. | |||
*/ | |||
/* =================== ROWS ==================== | |||
@@ -98,10 +96,12 @@ So rows can contain a mix of codes and multi-layered keys. | |||
Arrays ptrsKeys_0[] and ptrsKeys_1[] contain both Code pointers and Key pointers. | |||
*/ | |||
Key* const ptrsKeys_0[] = { &s_shift, &k_01 }; | |||
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0); | |||
uint8_t keyCount_0 = sizeof(ptrsKeys_0)/sizeof(*ptrsKeys_0); | |||
Row row_0(scanner, 0, ptrsKeys_0, keyCount_0); | |||
Key* const ptrsKeys_1[] = { &l_fn, &k_11 }; | |||
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1); | |||
uint8_t keyCount_1 = sizeof(ptrsKeys_1)/sizeof(*ptrsKeys_1); | |||
Row row_1(scanner, 1, ptrsKeys_1, keyCount_1); | |||
// ################### MAIN #################### | |||
void setup() |
@@ -1,7 +1,14 @@ | |||
/* keybrd_4c_split_with_IOE.ino | |||
This sketch: | |||
is a simple 1-layer keyboard | |||
runs on two matrices of a breadboard keyboard | |||
is annotated with a walk-through narrative | |||
This layout table shows left and right matrices: | |||
| Left | **0** | **1** | | Right | **0** | **1** | | |||
|:-----:|-------|-------| |:-----:|-------|-------| | |||
|:-----:|-------|-------|-|:-----:|-------|-------| | |||
| **1** | 1 | 2 | | **1** | 3 | 4 | | |||
| **0** | a | b | | **0** | c | d | | |||
*/ | |||
@@ -24,22 +31,44 @@ | |||
ScanDelay scanDelay(9000); | |||
// ================ LEFT SCANNER =============== | |||
/* | |||
Left matrix rows work the same as the ones in keybrd_2_single-layer.ino | |||
*/ | |||
uint8_t readPins[] = {14, 15}; | |||
uint8_t readPinCount = sizeof(readPins)/sizeof(*readPins); | |||
const uint8_t READPIN_COUNT = sizeof(readPins)/sizeof(*readPins); | |||
Scanner_uC scanner_L(LOW, readPins, readPinCount); | |||
Scanner_uC scanner_L(LOW, readPins, READPIN_COUNT); | |||
// =============== RIGHT SCANNER =============== | |||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address, all 3 ADDR pins are grounded | |||
/* | |||
The right matrix is scanned by an I/O expander. | |||
The I/O expander device address is configured by hardware pins. | |||
DEVICE_ADDR is a static variable of class PortIOE. | |||
*/ | |||
const uint8_t PortIOE::DEVICE_ADDR = 0x20; //MCP23S17 address with all 3 ADDR pins are grounded | |||
todo explain port num and shift notation << | |||
/* | |||
port_B stobes the row while port_A reads the colums. | |||
port_A is assigned port identification number 0. | |||
port_A is assigned to portRead, which reads port_A pins 0 and 1. | |||
"<<" (bit shift left) and "|" (or) are bitwise operators. | |||
Pin numbers to be read are to the right of "1<<" and delimited by "|". | |||
*/ | |||
PortIOE port_A(0); | |||
PortRead_MCP23S17 portRead_A(port_A, 1<<0 | 1<<1 ); | |||
PortRead_MCP23S17 portRead(port_A, 1<<0 | 1<<1 ); | |||
/* | |||
port_B is assigned port identification number 1. | |||
port_B is assigned to portWrite. | |||
*/ | |||
PortIOE port_B(1); | |||
//PortWrite_MCP23S17 portWrite_B(port_B); //for LEDs | |||
PortWrite_MCP23S17 portWrite_B(port_B); | |||
//PortWrite_MCP23S17 portWrite(port_B); //for LEDs todo | |||
PortWrite_MCP23S17 portWrite(port_B); | |||
Scanner_IOE scanner_R(LOW, portWrite_B, portRead_A); | |||
Scanner_IOE scanner_R(LOW, portWrite, portRead); | |||
// =================== CODES =================== | |||
Code_Sc s_a(KEY_A); | |||
@@ -53,7 +82,18 @@ Code_Sc s_3(KEY_3); | |||
Code_Sc s_4(KEY_4); | |||
// =================== ROWS ==================== | |||
/* | |||
Left row names contain the letter 'L', while right row names conatain the letter 'R'. | |||
The first parameteer of a Row constructor specifies the scanner. | |||
The second parameter of the Row constructor specifies the Row's strobePin. | |||
strobePin has one of two formats: | |||
* if refScanner a Scanner_uC, then strobePin is an Arduino pin number connected to this row | |||
* otherwise strobePin is bitwise, 1 indicating an IC pin connected to this row | |||
*/ | |||
// ---------------- LEFT ROWS ------------------ | |||
/* The left rows have a Scanner_uC and Arduino pin numbers to strobe. | |||
*/ | |||
Key* ptrsKeys_L0[] = { &s_1, &s_2 }; | |||
const uint8_t KEY_COUNT_L0 = sizeof(ptrsKeys_L0)/sizeof(*ptrsKeys_L0); | |||
Row row_L0(scanner_L, 0, ptrsKeys_L0, KEY_COUNT_L0); | |||
@@ -63,6 +103,9 @@ const uint8_t KEY_COUNT_L1 = sizeof(ptrsKeys_L1)/sizeof(*ptrsKeys_L1); | |||
Row row_L1(scanner_L, 1, ptrsKeys_L1, KEY_COUNT_L1); | |||
// ---------------- RIGHT ROWS ----------------- | |||
/* | |||
The right rows have a Scanner_IOE and pin bits to strobe. | |||
*/ | |||
Key* ptrsKeys_R0[] = { &s_3, &s_4 }; | |||
const uint8_t KEY_COUNT_R0 = sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0); | |||
Row row_R0(scanner_R, 1<<0, ptrsKeys_R0, KEY_COUNT_R0); |
@@ -2,7 +2,7 @@ Tutorial 0 - Introduction | |||
========================= | |||
The first two tutorials are intended to be read in sequence: | |||
* Tutorial 1 builds a breadboard keyboard and covers basic keyboard-hardware knowledge. | |||
* Tutorial 2 covers basic keybrd sketch knowledge needed to understand the remaining tutorials. | |||
* 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. |
@@ -2,36 +2,24 @@ Tutorial 10 - writing new IOE Port classes | |||
========================================== | |||
Port classes are the keybrd library's interface to I/O expander ports. | |||
To write a new Port class: | |||
Steps to writing a new port class: | |||
1. Get a copy of the I/O expander's datasheet. | |||
2. An I/O expander will use one of two communication protocols: [http://www.byteparadigm.com/applications/introduction-to-i2c-and-spi-protocols/](SPI or I2C). | |||
Refer to the [Arduino SPI](https://www.arduino.cc/en/Reference/SPI) | |||
or [Arduino Wire (I2C)](https://www.arduino.cc/en/Reference/Wire) library | |||
3. Get familiar with your I/O expander. | |||
Different I/O expanders use different commands (a.k.a. operation codes). | |||
* Different I/O expanders use different commands (a.k.a. operation codes). | |||
Refer to your I/O expander's datasheet for read and write commands. | |||
Search for Arduino sketch examples containing your I/O expander | |||
* Search for Arduino sketch examples containing your I/O expander | |||
([sumotoy](https://github.com/sumotoy/gpio_expander) has a large gpio expander library). | |||
Write very simple read and write examples for your I/O expander. | |||
Simple SPI I/O expander examples: | |||
todo link, pictures | |||
/home/wolfv/Documents/Arduino/demo/IOE_MCP23S17_read/ todo internal pull-up resistors | |||
/home/wolfv/Documents/Arduino/demo/IOE_MCP23S17_write/ | |||
Simple I2C I/O expander examples: | |||
todo link, pictures | |||
read | |||
write | |||
4. Study other keybrd Port classes. | |||
Port classes for SPI MCP23S17 I/O expander: | |||
*todo | |||
* | |||
* | |||
Port classes for I2C PCA9655E I/O expander: | |||
* PortWrite_PCA9655E todo link | |||
* PortRead_PCA9655E | |||
* LED_PCA9655E | |||
5. Write similar Port classes for your I/O expander. | |||
4. Study a simple keybrd sketch that uses an I/O expander. | |||
* [SPI I/O expander example sketch](keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino) | |||
* [I2C I/O expander example sketch](../examples/keybrd_PCA9655E/keybrd_PCA9655E.ino) | |||
5. Study other keybrd port classes. | |||
* SPI I/O expander port classes: PortMCP23S17 PortWrite_MCP23S17 PortRead_MCP23S17 | |||
* I2C I/O expander port classes: PortWrite_PCA9655E PortRead_PCA9655E | |||
6. Write the port classes for your I/O expander. | |||
Debugging I/O expander code is hard because SPI or I2C protocol adds a level of indirection. | |||
<br> |
@@ -3,7 +3,7 @@ Tutorial 1 - breadboard keyboard | |||
In this tutorial, you will build a breadboard keyboard with 4 keys. | |||
The keyboad will be used in tutorials 2 through 7. | |||
When you finish this tutorial you will have a working keyboard and understand how a key matrix works. | |||
When you finish this tutorial you will have a working keyboard and an understanding of how a key matrix works. | |||
Why a solderless breadboard keyboard is useful | |||
---------------------------------------------- | |||
@@ -21,8 +21,6 @@ Breadboard keyboards are useful for: | |||
* learning the firmware development workflow | |||
* prototyping circuits before making a PCB | |||
Arduino simulation software is an alternative to breadboards; I haven't tried that. | |||
Breadboard keyboard starter kit | |||
------------------------------- | |||
The parts needed to build the tutorial breadboard keyboards are listed in [breadboard_keyboard_supplies.ods](breadboard_keyboard_supplies.ods). | |||
@@ -79,15 +77,15 @@ A short wire connects the bottom row to the microcontroller. | |||
Switch-diode pairs, in series, connect rows to columns. | |||
Tutorials 2 and 3 use the basic breadboard keyboard pictured above. | |||
Tutorials 4, 5, and 6 will add more components to the basic breadboard keyboard. | |||
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. | |||
Positioning components as shown in the picture will provide space for those components. | |||
Breadboard keyboard assembly instructions: | |||
1. Bend and cut leads to fit breadboard. | |||
* tactile-switch-lead | |||
* diodes (save the cut offs for steps 2, 3, and tutorial 4) | |||
1. Shape leads to fit breadboard. | |||
* cut tactile-switch leads to length | |||
* bend and cut diode leads (save the cut offs for steps 2, 3, and tutorial 4) | |||
![bend diodes](keybrd_1_breadboard/diodes_bend_en_masse.JPG "bend diodes") | |||
@@ -99,7 +97,7 @@ Breadboard keyboard assembly instructions: | |||
* Teensy LC is on the left | |||
* switch leads are oriented to connect diodes to columns (pictured below) | |||
* diode cut offs connect terminal strips into columns | |||
* diodes connect switches to rows; orient diodes with cathode (banded end) towards the row (blue bus strip) | |||
* diodes connect switches to rows; orient diodes with cathode (banded end) towards the row (blue bus) | |||
![switch orientation](keybrd_1_breadboard/switch_orientation.JPG "switch orientation") | |||
@@ -122,11 +120,11 @@ Follow the [keybrd Library User's Guide](../doc/keybrd_library_user_guide.md) to | |||
Compile and load the [keybrd_1_breadboard.ino](/tutorials/keybrd_1_breadboard/keybrd_1_breadboard.ino) sketch into the keyboard's controller. | |||
The operating system will take 1 to 6 seconds to recognize the USB keyboard. | |||
Then pressing the keys should type the characters 1, a, b, c. | |||
Then pressing the keys should type the characters 1, 2, a, b. | |||
Congratulations, you have a working keyboard. | |||
How a key matrix works | |||
---------------------- | |||
Congratulations, you have a working breadboard keyboard. | |||
Now we fill in some details of how it all works. | |||
This excellent article explains how key matrix, diodes, and ghosting work: | |||
@@ -140,7 +138,7 @@ The breadboard keyboards in this series of tutorials do it the other way: | |||
> Output pins power rows and input pins detect the power on columns. | |||
The keybrd library uses the word "strobe". | |||
The keybrd library uses the word "strobe", which means powering one row for a very short time. | |||
Strobe pins are output pins connected to rows. | |||
One row at a time is strobed for the purpose of reading input pins. | |||
@@ -10,15 +10,17 @@ After reading the sketch you will be able to modify it to suite your own single- | |||
Exercises | |||
--------- | |||
1) Read the three class definitions #included in the sketch. | |||
1) Read the four class definitions #included in the sketch. | |||
Classes are defined in the [keybrd library](../src/). | |||
2) Add a third column to the breadboard keyboard and sketch. | |||
2) Add a fifth key to the breadboard keyboard and sketch. | |||
Not all rows have to have the same number of keys. | |||
The scanner should have enough readPins to cover all the keys of the longest row. | |||
| Layout |**0**|**1**|**2**| | |||
|:------:|:---:|:---:|:---:| | |||
| **0** | k | e | y | | |||
| **1** | b | r | d | | |||
| **0** | 1 | 2 | 3 | | |||
| **1** | a | b | | |||
<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>. |
@@ -22,7 +22,7 @@ It will run on the basic breadboard keyboard described in [tutorial_1_breadboard | |||
![basic breadboard keyboard](keybrd_1_breadboard/breadboard_keyboard_2x2.JPG "basic breadboard keyboard") | |||
Read the sketch annotations to understand how multi-layer keyboards work. | |||
The sketch annotations explain how multi-layer keyboards work. | |||
The sketch uses three layer-scheme classes: | |||
* LayerState | |||
* Code_LayerHold | |||
@@ -35,11 +35,11 @@ Pseudo code for simple layer scheme | |||
The following pseudo code is of three keybrd library classes. | |||
It has just enough detail to show the internal workings of layer schemes. | |||
**Key_Layer** objects change the active layer when pressed. | |||
**Code_Layer** objects change the active layer when pressed. | |||
The "layer" variable is a layer id number. | |||
When a Key_Layer object is pressed, it tells LayerState to update the active layer. | |||
When a Code_Layer object is pressed, it tells LayerState to update the active layer. | |||
``` | |||
class Key_Layer | |||
class Code_Layer | |||
{ | |||
int layer; | |||
LayerState& refLayerState; | |||
@@ -59,7 +59,7 @@ class LayerState | |||
``` | |||
**Key_LayeredKeysArray** objects contain an array of keys, one key for each layer. | |||
Key_LayeredKeysArray use layer ids as array indexes. | |||
Key_LayeredKeysArray objects use layer ids as Key_LayeredKeysArray indexes. | |||
When a Key_LayeredKeysArray object is pressed, it gets the active layer from LayerState, and sends the corresponding key. | |||
``` | |||
class Key_LayeredKeysArray | |||
@@ -73,9 +73,9 @@ class Key_LayeredKeysArray | |||
Dependency diagram | |||
``` | |||
+-----------+ | |||
| Key_Layer | | |||
+-----------+ | |||
+------------+ | |||
| Code_Layer | | |||
+------------+ | |||
| | |||
|setActiveLayer() | |||
| | |||
@@ -96,7 +96,7 @@ Layer-scheme classes | |||
There are several layer scheme-classes to choose from. | |||
You can view all the class definitions in the [keybrd library](../src/). | |||
Key_Layer classes include: | |||
Code_Layer classes include: | |||
* Code_LayerHold | |||
* Code_LayerLock | |||
@@ -107,10 +107,9 @@ Key_Layered classes include: | |||
* Key_LayeredKeysArray | |||
* Code_LayeredScSc | |||
* Code_LayeredCodeSc | |||
* Code_LayeredCodeCode | |||
The basic LayerState provided by the keybrd library is sufficient for implementing ordinary layer schemes. | |||
For experimental layer schemes, you would need to create a custom LayerState class, and possibly Key_Layer and Key_Layered custom layer classes as well. | |||
For experimental layer schemes, you would need to create a custom LayerState class, and possibly custom Code_Layer and Key_Layered classes as well. | |||
Single-layer Codes | |||
------------------ |
@@ -0,0 +1,83 @@ | |||
keybrd Tutorial 4b - split keyboard with shift registers | |||
======================================================== | |||
When you finish this tutorial you will be able to be able to modify a split keybrd sketch with 10 to 24 keys on the peripheral hand. | |||
Overview of split keyboard with shift registers | |||
------------------------------------------------ | |||
Only the right matrix is shown. The left matrix was omitted to reduce clutter. | |||
The layout has 2 rows and 7 columns. | |||
Electronically, the matrix only has one row. | |||
Diodes are not needed because there is only one row. | |||
The two black rectangles are 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 ) | |||
Building a split breadboard keyboard with shift registers | |||
--------------------------------------------------------- | |||
Add components to the breadboard as shown in the picture. | |||
Each shift register has a small notch on one end to identify pin 1. | |||
In the picture, pin 1s are on the left end. | |||
Shift registers are chained together by colored wires that lay flat on the breadboard. | |||
Each shift register has 8 parallel input pins, 4 on each side. | |||
There are 14 keys, so 2 of the input pins are unused. | |||
Used input pins are connected to 10k pull-down resistor which are grounded (blue bus). | |||
Unused input pins are grounded (blue bus). | |||
A decoupling capacitor between the power and ground wires prevents power disturbance. | |||
Switches are connected to power (red bus) and shift register input pins (jumpers). | |||
I apologize for not having a schematic. This table should help you figure out the pictures: | |||
``` | |||
74HC165 left (lower half of breadboard) | |||
NAME PIN# DESCRIPTION TO TEENSY LC PIN# CHAIN | |||
SH/LD 1 shift or load input CS0 10 green wire | |||
CLK 2 clock input SCK0 13 yellow wire | |||
D4 3 parallel input blue bus | |||
D5 4 parallel input blue bus | |||
D6 5 parallel input blue bus | |||
D7 6 parallel input blue bus | |||
/QH 7 ~serial output | |||
GND 8 ground gnd blue bus | |||
74HC165 right (upper half of breadboard) | |||
NAME PIN# DESCRIPTION TO TEENSY LC PIN# CHAIN | |||
VCC 16 power pin 3.3V red wire | |||
CLK INH 15 clock inhibit blue bus | |||
D3 14 parallel input blue bus | |||
D2 13 parallel input blue bus | |||
D1 12 parallel input blue bus | |||
D0 11 parallel input blue bus | |||
SER 10 serial input blue wire to next QH | |||
QH 9 serial output MISO0 12 blue wire to previous SER | |||
``` | |||
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. | |||
The sketch will run on the above breadboard keyboard. | |||
The sketch has code for both left and right matrix. | |||
Notice that the left matrix is active low, while the right matrix is active high. | |||
Exercises | |||
--------- | |||
1. Guess what happens if an unused input pin is not grounded? Try it. | |||
2. Add a left matrix to Teensy. | |||
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). | |||
<br> | |||
<a rel="license" href="https://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">keybrd tutorial</span> by <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd" property="cc:attributionName" rel="cc:attributionURL">Wolfram Volpi</a> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="https://creativecommons.org/ns" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>. | |||
## parts for breadboard_keyboard_supplies.ods | |||
@@ -0,0 +1,53 @@ | |||
keybrd Tutorial 4 - split keyboard with I/O Expander | |||
==================================================== | |||
When you finish this tutorial you will be able to be able to modify a 2-matrix keybrd sketch to suite your own split keyboard design. | |||
Overview of split keyboard with I/O Expander | |||
-------------------------------------------- | |||
The breadboard in this picture models a split keyboard. | |||
![breadboard keyboard with 2 rows and 4 columns of keys](images/breadboard_keyboard_2x5_labeled.jpg "2x5 breadboard keyboard") | |||
The breadboard's four bus strips are used as rows. | |||
Two rows (blue buses) are connected to the microcontroller. | |||
Two rows (red buses) are connected to the I/O expander. | |||
The I/O expander is a MCP23S17. | |||
It has a small notch on one end, which identifies pin 1. | |||
In the picture, pin 1 is on the right end. | |||
The MCP23S17 communicates via SPI protocol, where Teensy LC is the master, and MCP23S17 is slave. | |||
The Teensy LC and MCP23S17 are connected by 6 jumper wires: | |||
|CONNECTION |Teensy LC|MCP23S17| | |||
|:------------------:|---------|--------| | |||
|ground | GND | VSS | | |||
|power | 3.3v | VDD | | |||
|Serial Clock | SCK0 | SCK | | |||
|Master Out, Slave In| MOSI0 | SI | | |||
|Master In, Slave Out| MISO0 | SO | | |||
|Chip Select | CS0 | /CS | | |||
A decoupling capacitor suppresses high-frequency noise from the power supply. | |||
MCP23S17's I/O expander address is configured by hardware pins. | |||
The MCP23S17 with all address pins grounded has an device address of 0x20. | |||
The MCP23S17's /RESET pin is connected to VDD. | |||
The MCP23S17 I/O expander has two ports. Each port has eight pins. | |||
Port B is connected to the matrix's rows. Port A is connected to the matrix's columns. | |||
Building a split keyboard with I/O Expander | |||
------------------------------------------- | |||
Starting with the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md), add parts as described above. | |||
Refer to the MCP23S17 datasheet to locate its pins. | |||
<!-- todo schematic with IOE power decoupling capacitor | |||
This schematic was written by consulting the I/O expander's datasheet and using the ?? tool. --> | |||
Sketch for split keyboard with I/O Expander | |||
------------------------------------------- | |||
The [keybrd_4c_split_with_IOE.ino](keybrd_4c_split_with_IOE/keybrd_4c_split_with_IOE.ino) | |||
sketch explains how the I/O Expander works on a keyboard. | |||
<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>. |
@@ -70,8 +70,7 @@ By comparing the above tables, one can see what changes need to be made: | |||
* flip the diodes so that the cathode (banded end) are towards the read pins | |||
* swap the STROBE_ON and STROBE_OFF values | |||
The red bus is grounded. | |||
The pull-down resistors plug into the red bus and column read pins. | |||
The pull-down resistors plug into ground (red bus) and column 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. | |||