update diagrams and tutorials: 0 1 2 3a
This commit is contained in:
parent
47e9d49127
commit
2e77a18847
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
10
src/Row.cpp
10
src/Row.cpp
@ -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
|
||||
------------------
|
||||
|
83
tutorials/tutorial_4b_split_keyboard_with_shift_registers.md
Normal file
83
tutorials/tutorial_4b_split_keyboard_with_shift_registers.md
Normal 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
|
||||
|
53
tutorials/tutorial_4c_split_keyboard_with_IOE.md
Normal file
53
tutorials/tutorial_4c_split_keyboard_with_IOE.md
Normal 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>.
|
@ -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.
|
||||
|
||||
|
Reference in New Issue
Block a user