Archived
1
0

update diagrams and tutorials: 0 1 2 3a

This commit is contained in:
wolfv6 2016-09-16 23:45:12 -06:00
parent 47e9d49127
commit 2e77a18847
25 changed files with 352 additions and 221 deletions

View File

@ -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
--------------------------
@ -76,12 +27,12 @@ Keybrd library class inheritance diagram
PortIOE
PortWrite
|
PortWrite_PCA9655E (one PortWrite class for each IOE type)
/ \
PortWrite_PCA9655E PortWrite_MCP23S17 (one PortWrite class for each IOE type)
PortRead
|
PortRead_PCA9655E (one PortRead class for each IOE type)
/ \
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..*] _
____ Row ______
/ | \
Scanner_uC Debouncer Keys[1..*] __
| \
Code[1..*] Code_LEDLock[1..*]
Scanner_uC Debouncer Keys __
| | \
readPins Code Code_LEDLock
|
LED_PinNumber[1]
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..*]
_______ Row _______
/ | \
RowScanner_ShiftRegsPISO Debouncer Keys
|
Code[1..*]
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..*] _________
_________ Row _________
/ \ \
__ Scanner_Port[1] __ Debouncer[1] Keys[1..*] __
/ | \ | \
PortWrite[1] strobePin[1] PortRead[1] Code[1..*] Code_LEDLock[1..*]
\ / \ |
\ / ColPins[1..*] LED[1]
\ /
PortIOE[0..*]
__ 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

View File

@ -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.

View File

@ -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;

View File

@ -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.
*/

View File

@ -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.
*/

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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,

View File

@ -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();

View File

@ -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.

View File

@ -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);

View File

@ -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()
{

View File

@ -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()

View File

@ -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);

View File

@ -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.

View File

@ -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>

View File

@ -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.

View File

@ -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>.

View File

@ -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
------------------

View File

@ -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

View File

@ -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>.

View File

@ -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.