Archived
1
0

update tutorials 0 to 6

This commit is contained in:
wolfv6 2016-07-17 20:03:03 -06:00
parent 31902a4c7f
commit b276d217b3
49 changed files with 795 additions and 432 deletions

View File

@ -146,25 +146,29 @@ where
The first field are mandatory, the version optional.
## Active state and diode orientation
Active state is set in the sketch by static variables STROBE_ON and STROBE_OFF.
Active state is set in the sketch by variables STROBE_ON and STROBE_OFF.
The following instructions are for setting active state for a Scanner_uC class.
Scanner_ShiftRegs74HC165 and Scanner_Port classes is similar.
For active low
For active low:
* Use internal pull-down resistors.
* Orient diodes with cathode (banded end) towards the write pins (row)
* Use these two lines in the sketch:
```
const bool Scanner_uC::STROBE_ON = LOW;
const bool Scanner_uC::STROBE_OFF = HIGH;
```
For active high
* Use external 10k pull-down resistors.
For active high:
* Add an external 10k pull-down resistor to each read pin.
* Orient diodes with cathode (banded end) towards the read pins.
* Use these two lines in the sketch:
```
const bool Scanner_uC::STROBE_ON = HIGH;
const bool Scanner_uC::STROBE_OFF = LOW;
```
![Diode](https://github.com/wolfv6/keybrd/blob/master/tutorials/images/120px-Diode_pinout_en_fr.svg.png)
![Diode](../tutorials/keybrd_1_breadboard_images/120px-Diode_pinout_en_fr.svg.png)
Diagram is of typical through-the-hole [diode](https://en.wikipedia.org/wiki/Diode) in same alignment as diode symbol.
Cross bar and band depict the cathode.

View File

@ -5,7 +5,7 @@ The keyboard hardware for this sketch has 4 shift registers,
with every 4th input pin connected to a pull-down resistor and matrix column, also the 31st key.
Unused input pins are not grounded, so add this line to Scanner_ShiftRegs74HC165::scan():
//clear unpowered pins (for testing on breadboard)
rowState &= 0b11110001000100010001000100010001; //todo
rowState &= 0b11110001000100010001000100010001;
Layout Layout
| Left | **0**|**1**| | Right |**0**|**1**|**2**|**3**|**4**|**5**|**6**|**7**|**8**|
@ -31,13 +31,13 @@ Unused input pins are not grounded, so add this line to Scanner_ShiftRegs74HC165
// =============== CONFIGURATION ===============
ScanDelay scanDelay(9000);
//active state of left matrix
//set left matrix for active low
const bool Scanner_uC::STROBE_ON = LOW;
const bool Scanner_uC::STROBE_OFF = HIGH;
const uint8_t Scanner_ShiftRegs74HC165::SHIFT_LOAD = 10;
//active state of right matrix
//set right matrix for active low
const bool Scanner_ShiftRegs74HC165::STROBE_ON = LOW;
const bool Scanner_ShiftRegs74HC165::STROBE_OFF = HIGH;
@ -50,22 +50,6 @@ uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins);
// ==================== LEDs ===================
LED_uC LED1(16);
//sometimes OS takes 6 seconds to recongnize keyboard, LED blinks from the begining
void wait()
{
for (uint8_t count = 0; count < 6; count++)
{
//print count
Keyboard.print(count);
Keyboard.print(F(" "));
//blink LED
LED1.on();
delay(500);
LED1.off();
delay(500);
}
}
// =================== CODES ===================
Code_Sc s_a(KEY_A);
Code_Sc s_b(KEY_B);
@ -159,7 +143,7 @@ void setup()
row_R0.begin();
row_R1.begin();
wait();
debug.wait_for_OS(LED1, 6);
Keyboard.println(F("keybrd_shift_reg.ino"));
}

View File

@ -10,7 +10,7 @@ Example initialization:
const Code_Shift s_shift(MODIFIERKEY_LEFT_SHIFT);
const Code_Shift *const ptrsS[] = { &s_shift };
const Code_Shift *const *const Code_AutoShift::ptrsShifts = ptrsS;
const uint8_t Code_AutoShift::shiftCount = sizeof(ptrsShifts)/sizeof(*ptrsShifts);
const uint8_t Code_AutoShift::shiftCount = sizeof(ptrsS)/sizeof(*ptrsS);
The two Code_Shift pointer arrays (ptrsShifts and ptrsS) must have distinct names.
Automatic shifting is usful on multi-layered keyboards.

View File

@ -1,6 +1,6 @@
#include "Debug.h"
void Debug::print_microseconds_per_scan()
void Debug::printMicrosecondsPerScan()
{
if (millis() >= nextTime)
{
@ -11,7 +11,7 @@ void Debug::print_microseconds_per_scan()
}
scanCount++;
}
void Debug::print_scans_per_second()
void Debug::printScansPerSecond()
{
if (millis() >= nextTime)
{
@ -22,3 +22,21 @@ void Debug::print_scans_per_second()
}
scanCount++;
}
//Sometimes OS takes 6 seconds to recongnize keyboard.
//wait_for_OS() will blink LED and count up once per second for specified number of seconds.
void Debug::wait_for_OS(LED& led, const uint8_t seconds)
{
for (uint8_t elapsed = 0; elapsed < seconds; elapsed++)
{
//print seconds elapsed
Keyboard.print(elapsed);
Keyboard.print(F(" "));
//blink LED
led.on();
delay(500);
led.off();
delay(500);
}
}

View File

@ -1,15 +1,17 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <Arduino.h>
class Debug
{
private:
unsigned long nextTime = 0;
unsigned int scanCount = 0;
public:
void print_microseconds_per_scan(); //print microseconds per scan every second
void print_scans_per_second(); //print scans per second every second
};
#endif
#ifndef DEBUG_H
#define DEBUG_H
#include <Arduino.h>
#include <LED.h>
class Debug
{
private:
unsigned long nextTime = 0;
unsigned int scanCount = 0;
public:
void printMicrosecondsPerScan(); //print microseconds per scan every second
void printScansPerSecond(); //print scans per second every second
void wait_for_OS(LED& led, uint8_t seconds); //wait for OS to recongnize keyboard
};
#endif

View File

@ -4,12 +4,12 @@
#include <inttypes.h>
#include <LED.h>
/* A LED_uC object is an Aduino pin that is used to power an LED on and off.
/* A LED_uC turns LED on and off.
*/
class LED_uC: public LED
{
private:
const uint8_t pin;
const uint8_t pin; //Aduino pin that is connected to an LED
public:
LED_uC(const uint8_t pin): pin(pin)

View File

@ -19,7 +19,8 @@ void LayerState::lock(const uint8_t layer)
lockedLayer = layer;
}
//Derived classes override setActiveLayer() to also set LED indicator lights.
/*Derived classes override setActiveLayer() to also set LED indicator lights e.g. LayerState_LED
*/
void LayerState::setActiveLayer(const uint8_t layer)
{
activeLayer = layer;

View File

@ -4,7 +4,7 @@
#include <inttypes.h>
#include <LayerStateInterface.h>
/* basic LayerState for keyboard.
/* Basic LayerState for keyboard.
When pressed, Code_Layer objects call LayerState functions lock() or hold().
When pressed, Layered objects call LayerState function getActiveLayer().
*/

13
src/LayerState_LED.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "LayerState_LED.h"
void LayerState_LED::begin()
{
ptrsLEDs[getActiveLayer()]->on();
}
void LayerState_LED::setActiveLayer(const uint8_t layer)
{
ptrsLEDs[activeLayer]->off();
activeLayer = layer;
ptrsLEDs[activeLayer]->on();
}

21
src/LayerState_LED.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef LAYERSTATE_LED_H
#define LAYERSTATE_LED_H
#include <Arduino.h>
#include <inttypes.h>
#include <LayerState.h>
#include <LED.h>
/* Basic LayerState with layer LED indictor lights.
begin() should be called once to turn on LED for initial active layer.
*/
class LayerState_LED : public LayerState
{
private:
LED*const *const ptrsLEDs; //array of LEDs, where layer id is array index
virtual void setActiveLayer(const uint8_t layer); //set active layer and turn on it's LED
public:
LayerState_LED(LED*const ptrsLEDs[]): ptrsLEDs(ptrsLEDs) {}
void begin();
};
#endif

View File

@ -6,6 +6,9 @@
/*
PortRead is an abstract base class.
Port classes are the keybrd library's interface to microcontroller ports or I/O expander ports.
If your 8-bit AVR (Teensy 2) is running low on memory, using a smaller read_pins_t type saves SRAM.
Details are in config_key.h
*/
class PortRead
{

View File

@ -4,7 +4,8 @@
configures column port's configuration, input, and pins.
*/
PortRead_PCA9655E::PortRead_PCA9655E (PortIOE& port, const uint8_t readPins)
: PortRead(readPins), port(port), configurationByteCommand(port.num + 6), inputByteCommand(port.num)
: PortRead(readPins), port(port),
configurationByteCommand(port.num + 6), inputByteCommand(port.num)
{}
void PortRead_PCA9655E::begin()

View File

@ -6,6 +6,9 @@
/*
PortWrite is an abstract base class.
Port classes are the keybrd library's interface to microcontroller ports or I/O expander ports.
If your 8-bit AVR (Teensy 2) is running low on memory, using a smaller read_pins_t type saves SRAM.
Details are in config_key.h
*/
class PortWrite
{

View File

@ -34,9 +34,6 @@ read_pins_t Scanner_ShiftRegs74HC165::scan()
//strobe row off
digitalWrite(strobePin, STROBE_OFF);
//for testing on breadboard, clear unpowered pins
readState &= 0b11110001000100010001000100010001; //todo delete this line
return readState;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,15 +1,15 @@
/* keybrd_single-layer_2_annotated.ino
/* keybrd_2_single-layer.ino
This sketch:
is a simple 1-layer keyboard
is firmware for a simple 1-layer keyboard
runs on the first two rows and columns of a breadboard keyboard
This layout table shows how keys are arranged on the keyboard:
| Layout | **0** | **1** |
|:------:|-------|-------|
| **0** | a | b |
| **1** | c | shift |
| **0** | shift | a |
| **1** | b | c |
The layout's row and column numbers are in the headers.
Each cell in the table's body represents a key.
@ -17,136 +17,76 @@ Each cell in the table's body represents a key.
The sketch is annotated with a walk-through narrative enclosed in comment blocks.
Each comment block explains the next one or two lines of code.
keybrd is instantiated under the "GLOBAL" heading. Most of the sketch is in global space.
keybrd runs at the end of this sketch, under the "MAIN" heading.
keybrd objects are instantiated under the "GLOBAL" heading.
The keyboard runs at the end of the sketch, under the "MAIN" heading.
*/
// ################## GLOBAL ###################
// ================= INCLUDES ==================
/*
All the includes in this sketch are to keybrd library classes.
*/
//Ports
#include <RowPort_AVR_Optic.h>
#include <ColPort_AVR.h>
//Codes
#include <ScanDelay.h>
#include <Code_Sc.h>
#include <Row_uC.h>
//Matrix
#include <Row.h>
#include <Matrix.h>
// ============ SPEED CONFIGURATIONS ============
/*
DELAY_MICROSECONDS specifies the amount of delay between row scans.
/* ============ 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.
DELAY_MICROSECONDS gives the switches time to debounce.
DELAY_MICROSECONDS is a static variable of class Row.
ScanDelay gives the switches time to debounce.
*/
const unsigned int Row::DELAY_MICROSECONDS = 1000;
ScanDelay scanDelay(9000);
// =================== PORTS ===================
/*
A micro-controller has one or more ports. Each port has one or more pins.
These pins are connected to the keyboard's rows and columns.
rowPortF will strobe PORTF one row at a time.
/* ================ 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).
*/
RowPort_AVR_Optic rowPortF(DDRF, PORTF);
const bool Scanner_uC::STROBE_ON = LOW; //set matrix for active low
const bool Scanner_uC::STROBE_OFF = HIGH;
/*
A number to the right of "1<<" is a pin number to read.
colPortB will read PORTB's pin 0 and pin 1
*/
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<0 | 1<<1 );
/*
ColPort pointers are placed in an array because some keyboards use multiple column ports.
This sketch only has one column port.
/* ================= PINS =================
Micro-controller 14 and 15 are connected to the matrix columns.
These readPins detect which keys are pressed while a row is strobed.
sizeof() is used to compute the number of array elements.
This eliminates the risk of forgetting to update the count after adding or removing an element.
*/
ColPort* const ptrsColPorts[] = { &colPortB };
const uint8_t COL_PORT_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts);
uint8_t readPins[] = {14, 15};
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins);
// =================== CODES ===================
/*
The CODES section instantiates four codes, one for each item in the layout.
/* =================== CODES ===================
Four Codes are instantiated, one for each key in the layout.
The Code object names in this sketch start with a "s_" prefix.
The Code_Sc constructor takes one scancode ("Sc" means "scancode").
When Code_Sc is pressed, it sends its scancode.
The Code object names in this sketch start with a "s_" prefix.
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);
// ================== MATRIX ===================
/*
The MATRIX section instantiates the components of the matrix:
Codes are grouped into rows.
Rows are grouped into a matrix.
How the matrix works:
1) The matrix scans one row at a time.
2) If a row detects a key press, it notifies the code.
3) The code sends its scancode.
*/
// ------------------- ROWS --------------------
/*
Here we group Code pointers into rows.
Codes are a kind of Key. Array ptrsKeys_0[] contains two pointers to Key objects.
The Row constructor parameters are:
one rowPort
one row pin
an array of colPorts, and the number of colPorts
an array of Key pointers
/* =================== 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) stobePin 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.
*/
Key* const ptrsKeys_0[] = { &s_a, &s_b };
Row row_0(rowPortF, 1<<0, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_0);
Key* ptrsKeys_0[] = { &s_shift, &s_a };
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0);
Key* const ptrsKeys_1[] = { &s_c, &s_shift };
Row row_1(rowPortF, 1<<1, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_1);
Key* ptrsKeys_1[] = { &s_b, &s_c };
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1);
/*
HOW ROW OBJECTS WORK
When a row is scanned, the row strobes the row pin, and the column ports read their column pins.
If a row detects a key press, it notifies the key which then sends its scancode.
*/
// ------------------ MATRIX -------------------
/*
Here we group Row pointers into a matrix.
Array ptrsRows[] contains two pointers to Row objects.
*/
Row* const ptrsRows[] = { &row_0, &row_1 };
const uint8_t ROW_COUNT = sizeof(ptrsRows)/sizeof(*ptrsRows);
/*
The Matrix constructor parameters are:
one array of Row pointers
the number of Row pointers
'0' for active low or '1' for active high
WARNING: the tutorial sketches all have '1' for active high to be compatible with DH.
The breadboard keyboard described in tutorial_1 is active low.
For active low, change the '1' to a '0':
*/
Matrix matrix(ptrsRows, ROW_COUNT, 1);
// ################### MAIN ####################
/*
Aruduino IDE copies Functions setup() and loop() into main().
setup() initialized the keybrd.
Keyboard.begin() should be called once to initialize.
/* ################### MAIN ####################
setup() is used to initialize the keyboard firmware. Keyboard.begin() should be called once.
*/
void setup()
{
@ -154,9 +94,16 @@ void setup()
}
/*
loop() continually scans the Matrix object.
loop() continually scans the matrix, one row at a time.
Each row object strobes the 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.
*/
void loop()
{
matrix.scan();
row_0.process();
row_1.process();
scanDelay.delay();
}

View File

@ -1,18 +1,14 @@
/* keybrd_3_multi-layer_annotated.ino
This sketch:
is a simple 2-layer keyboard
is firmware for a simple 2-layer keyboard
runs on the first two rows and columns of a breadboard keyboard
is annotated with a walk-through narrative
This layout table shows how keys are arranged on the keyboard:
| Layout | **0** | **1** |
|:------:|-------|-------|
| **0** | a 1 | b 2 |
| **1** | fn | shift |
| **0** | shift | a 1 |
| **1** | fn | b 2 |
The layout's row and column numbers are in the headers.
Each cell in the table's body represents a key.
The layered keys in row 0 have two layers; one character for each layer.
Letters 'a' and 'b' are on the normal layer. Numbers '1' and '2' are one the fn layer.
@ -20,50 +16,46 @@ Holding the fn key down makes it the active layer. Releasing the fn key restore
*/
// ################## GLOBAL ###################
// ================= INCLUDES ==================
//Ports
#include <RowPort_AVR_Optic.h>
#include <ColPort_AVR.h>
//Codes
//Keys
#include <Code_Sc.h>
#include <LayerState.h>
#include <Code_LayerHold.h>
#include <Key_LayeredKeysArray.h>
//Matrix
#include <Row.h>
#include <Matrix.h>
#include <Row_uC.h>
#include <ScanDelay.h>
// ============ SPEED CONFIGURATIONS ============
const unsigned int Row::DELAY_MICROSECONDS = 1000;
// ============ SPEED CONFIGURATION ============
ScanDelay scanDelay(9000);
// =================== PORTS ===================
RowPort_AVR_Optic rowPortF(DDRF, PORTF);
// ================ ACTIVE STATE ===============
const bool Scanner_uC::STROBE_ON = LOW;
const bool Scanner_uC::STROBE_OFF = HIGH;
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<0 | 1<<1 );
// =================== PINS ====================
uint8_t readPins[] = {14, 15};
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins);
ColPort* const ptrsColPorts[] = { &colPortB };
const uint8_t COL_PORT_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts);
// =================== CODES ===================
/*
The CODES section instantiates six codes, one for each item in the layout:
s_a s_1 s_b s_2
l_fn s_shift
/* =================== CODES ===================
The CODES section instantiates six codes, one for each item in the layout.
*/
// ---------------- LAYER CODE -----------------
/*
enum assings Id numbers to the layers.
/* ---------------- LAYER CODE -----------------
enum assigns id numbers to the layers.
*/
enum layers { NORMAL, FN };
/*
layerState keeps track of the active layer. The default layer number is 0.
/* layerState keeps track of the active layer.
*/
LayerState layerState;
/*
The Code_LayerHold constructor parameter specifies a layer Id number and a 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) the LayerState
When l_fn is pressed, it tells layerState to change the active layer to 1.
When l_fn is released, it tells layerState to restore the normal layer.
When l_fn is released, it tells layerState that layer 1 is released, and layerState restores the default layer.
*/
Code_LayerHold l_fn(FN, layerState);
@ -74,70 +66,42 @@ Code_Sc s_1(KEY_1);
Code_Sc s_2(KEY_2);
Code_Sc s_shift(MODIFIERKEY_LEFT_SHIFT);
// ================== MATRIX ===================
/*
The MATRIX section instantiates the components of the matrix:
Codes are grouped into keys.
Keys are grouped into rows.
Rows are grouped into a matrix.
*/
// ------------------- KEYS --------------------
/*
Here we group Code pointers into keys.
Array ptrsCodes_00[] contains two pointers to Code objects.
Key_LayeredKeysArray constructor parameters are:
one array of Code pointers
Key_LayeredKeysArray objects are multi-layered - one Code object per layer.
Layer Id numbers are used as array indexes for the Key_LayeredKeysArray.
Defining layer Id numbers with enum insures that they are a series of intergers starting at 0.
/* =================== 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 matrix-row-column coordinates.
*/
Key* const ptrsCodes_00[] = { &s_a, &s_1 };
Key_LayeredKeysArray k_00(ptrsCodes_00);
Key* const ptrsCodes_01[] = { &s_b, &s_2 };
Key* const ptrsCodes_01[] = { &s_a, &s_1 };
Key_LayeredKeysArray k_01(ptrsCodes_01);
/*
Key_LayeredKeysArray has a static variable refLayerState defined here.
It is a reference to layerState.
Key* const ptrsCodes_11[] = { &s_b, &s_2 };
Key_LayeredKeysArray k_11(ptrsCodes_11);
/* Key_LayeredKeysArray has a reference to layerState.
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 from layerState and then sends the scancode for the active layer.
/* 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.
*/
// ------------------- ROWS --------------------
/*
Here we group Key pointers into rows.
Array ptrsKeys_0[] contains two pointers to Key_LayeredKeyArray objects.
*/
Key* const ptrsKeys_0[] = { &k_00, &k_01 };
Row row_0(rowPortF, 1<<0, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_0);
/* =================== ROWS ====================
Here we pack Key pointers into row objects.
/*
Codes are a kind of Key that only have one layer.
So rows can contain multi-leyered a mix of keys and codes.
Array ptrsKeys_1[] contains two Code pointers.
So rows can contain a mix of multi-layered keys and codes.
Arrays ptrsKeys_0[] and ptrsKeys_1[] contain both Key pointers and Code pointers.
*/
Key* const ptrsKeys_1[] = { &l_fn, &s_shift };
Row row_1(rowPortF, 1<<1, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_1);
Key* const ptrsKeys_0[] = { &s_shift, &k_01 };
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0);
// ------------------ MATRIX -------------------
/*
Here we group Row pointers into a matrix.
Array ptrsRows[] contains two pointers to Row objects.
*/
Row* const ptrsRows[] = { &row_0, &row_1 };
const uint8_t ROW_COUNT = sizeof(ptrsRows)/sizeof(*ptrsRows);
Matrix matrix(ptrsRows, ROW_COUNT, 1);
Key* const ptrsKeys_1[] = { &l_fn, &k_11 };
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1);
// ################### MAIN ####################
void setup()
@ -147,5 +111,7 @@ void setup()
void loop()
{
matrix.scan();
row_0.process();
row_1.process();
scanDelay.delay();
}

View File

@ -3,14 +3,11 @@
This sketch:
is a simple 2-layer keyboard with AutoShift
runs on the first two rows and columns of a breadboard keyboard
is annotated with a walk-through narrative
This layout table shows how keys are arranged on the keyboard:
| Layout | **0** | **1** |
|:------:|-------|-------|
| **0** | a ! | b @ |
| **1** | fn | shift |
| **0** | shift | a ! |
| **1** | fn | b @ |
The layered keys in row 0 have two layers; one character for each layer.
Letters 'a' and 'b' are on the normal layer. Symbols '!' and '@' are one the fn layer.
@ -18,11 +15,8 @@ Holding the fn key down makes it the active layer. Releasing the fn key restore
*/
// ################## GLOBAL ###################
// ================= INCLUDES ==================
//Ports
#include <RowPort_AVR_Optic.h>
#include <ColPort_AVR.h>
//Codes
//Keys
#include <Code_Sc.h>
#include <Code_ScS.h>
#include <Code_Shift.h>
@ -31,41 +25,35 @@ Holding the fn key down makes it the active layer. Releasing the fn key restore
#include <Key_LayeredKeysArray.h>
//Matrix
#include <Row.h>
#include <Matrix.h>
#include <Row_uC.h>
#include <ScanDelay.h>
// ============ SPEED CONFIGURATIONS ============
const unsigned int Row::DELAY_MICROSECONDS = 1000;
// ============ SPEED CONFIGURATION ============
ScanDelay scanDelay(9000);
// =================== PORTS ===================
RowPort_AVR_Optic rowPortF(DDRF, PORTF);
// ================ ACTIVE STATE ===============
const bool Scanner_uC::STROBE_ON = LOW;
const bool Scanner_uC::STROBE_OFF = HIGH;
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<0 | 1<<1 );
ColPort* const ptrsColPorts[] = { &colPortB };
const uint8_t COL_PORT_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts);
// =================== PINS ====================
uint8_t readPins[] = {14, 15};
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins);
// =================== CODES ===================
/*
The CODES section instantiates six codes, one for each item in the layout:
s_a s_exclamation s_b s_at
l_fn s_shift
*/
// ---------------- LAYER CODE -----------------
enum layers { NORMAL, FN };
LayerState layerState;
Code_LayerHold l_fn(FN, layerState);
// ---------------- SCAN CODES -----------------
/*
The Code_Sc constructor takes one scancode ("Sc" means "scancode").
When Code_Sc is pressed, it sends its scancode.
/* ---------------- SCAN CODES -----------------
The "Sc" in Code_Sc means "scancode".
When a Code_Sc is pressed, it sends its scancode.
*/
Code_Sc s_a(KEY_A);
Code_Sc s_b(KEY_B);
/*
The Code_ScS constructor takes one scancode to be shifted ("ScS" means "scancode shifted").
/* The "ScS" in Code_ScS means "scancode shifted".
When Code_ScS is pressed, it calls Code_AutoShift before sending its scancode.
*/
Code_ScS s_exclamation(KEY_1);
@ -73,7 +61,7 @@ Code_ScS s_at(KEY_2);
// ----------------- SHIFT CODE ----------------
/*
The Code_Shift constructor takes one scancode.
The Code_Shift constructor takes one shift scancode.
*/
Code_Shift s_shift(MODIFIERKEY_LEFT_SHIFT);
@ -82,24 +70,19 @@ Code_Shift pointers are placed in an array because most keyboards have a left an
This sketch only has one shift code.
*/
Code_Shift* const ptrsS[] = { &s_shift };
/*
Code_AutoShift is the base class of Codes_ScS (Codes_ScS is explained in the preceding section).
It has two static variables, ptrsShifts and shiftCount, which are defined here.
ptrsShifts is the array of Code_Shift pointers; one pointer for each shift key.
*/
Code_Shift* const* const Code_AutoShift::ptrsShifts = ptrsS;
const uint8_t Code_AutoShift::shiftCount = sizeof(ptrsShifts)/sizeof(*ptrsShifts);
const uint8_t Code_AutoShift::shiftCount = sizeof(ptrsS)/sizeof(*ptrsS);
/*
HOW AUTOSHIFT WORKS
When a modifier key is pressed, a standard keyboard driver will temporarily modify the normal action of another key when pressed together.
HOW SHIFT WORKS
When a shift key is pressed, a standard keyboard driver will temporarily modify the normal action of another key when pressed together.
KEY_1 writes '1'
MODIFIERKEY_LEFT_SHIFT + KEY_1 writes '!'
KEY_2 writes '2'
MODIFIERKEY_LEFT_SHIFT + KEY_2 writes '@'
HOW AUTOSHIFT WORKS
Code_ScS takes care of the MODIFIERKEY_LEFT_SHIFT automatically
When the user presses '!' or '@' on the fn layer:
Code_AutoShift checks the position of each shift key
@ -107,28 +90,21 @@ When the user presses '!' or '@' on the fn layer:
Code_ScS sends its scancode
*/
// ================== MATRIX ===================
// ------------------- KEYS --------------------
Key* const ptrsCodes_00[] = { &s_a, &s_exclamation };
Key_LayeredKeysArray k_00(ptrsCodes_00);
Key* const ptrsCodes_01[] = { &s_b, &s_at };
// =================== KEYS ====================
Key* const ptrsCodes_01[] = { &s_a, &s_exclamation };
Key_LayeredKeysArray k_01(ptrsCodes_01);
Key* const ptrsCodes_11[] = { &s_b, &s_at };
Key_LayeredKeysArray k_11(ptrsCodes_11);
LayerStateInterface& Key_LayeredKeysArray::refLayerState = layerState;
// ------------------- ROWS --------------------
Key* const ptrsKeys_0[] = { &k_00, &k_01 };
Row row_0(rowPortF, 1<<0, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_0);
// =================== ROWS ====================
Key* const ptrsKeys_0[] = { &s_shift, &k_01 };
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0);
Key* const ptrsKeys_1[] = { &l_fn, &s_shift };
Row row_1(rowPortF, 1<<1, ptrsColPorts, COL_PORT_COUNT, ptrsKeys_1);
// ------------------ MATRIX -------------------
Row* const ptrsRows[] = { &row_0, &row_1 };
const uint8_t ROW_COUNT = sizeof(ptrsRows)/sizeof(*ptrsRows);
Matrix matrix(ptrsRows, ROW_COUNT, 1);
Key* const ptrsKeys_1[] = { &l_fn, &k_11 };
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1);
// ################### MAIN ####################
void setup()
@ -138,5 +114,7 @@ void setup()
void loop()
{
matrix.scan();
row_0.process();
row_1.process();
scanDelay.delay();
}

View File

@ -0,0 +1,102 @@
/* tutorial_4a_split_keyboard_with_shift_registers.ino
Tested on Teensy LC and two 74HC165 shift registers.
The right matrix has 2 shift registers daisy chained.
Every 4th input pin has a pull-up resistor and matrix column.
Unused input pins are powered.
Layout Layout
| Left | **0**| | Right |**0**|**1**|**2**|**3**|
|:-----:|------| |:-----:|-----|-----|-----|-----|
| **0** | x | | **0** | 0 | 1 | 2 | 3 |
| **1** | y | | **1** | a | b | c | d |
*/
// ################## GLOBAL ###################
// ================= INCLUDES ==================
//Codes
#include <Code_Sc.h>
#include <Code_LEDLock.h>
//Matrix
#include <SPI.h>
#include <Row_uC.h>
#include <Row_ShiftRegisters.h>
#include <ScanDelay.h>
// =============== CONFIGURATION ===============
ScanDelay scanDelay(9000);
//set left matrix for active low
const bool Scanner_uC::STROBE_ON = LOW;
const bool Scanner_uC::STROBE_OFF = HIGH;
const uint8_t Scanner_ShiftRegs74HC165::SHIFT_LOAD = 10;
//set right matrix for active low
const bool Scanner_ShiftRegs74HC165::STROBE_ON = LOW;
const bool Scanner_ShiftRegs74HC165::STROBE_OFF = HIGH;
// ================= LEFT PINS =================
uint8_t readPins[] = {14};
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins);
// =================== CODES ===================
Code_Sc s_a(KEY_A);
Code_Sc s_b(KEY_B);
Code_Sc s_c(KEY_C);
Code_Sc s_d(KEY_D);
Code_Sc s_x(KEY_X);
Code_Sc s_y(KEY_Y);
Code_Sc s_z(KEY_Z);
Code_Sc s_0(KEY_0);
Code_Sc s_1(KEY_1);
Code_Sc s_2(KEY_2);
Code_Sc s_3(KEY_3);
// ================= LEFT ROWS =================
Key* ptrsKeys_L0[] = { &s_x };
Row_uC row_L0(0, readPins, READ_PIN_COUNT, ptrsKeys_L0);
Key* ptrsKeys_L1[] = { &s_y };
Row_uC row_L1(1, readPins, READ_PIN_COUNT, ptrsKeys_L1);
/* ================= RIGHT ROWS ================
Instantiating a Row_ShiftRegistersis similar to instantiating a Row_uC.
The s_z are place holders where the input pins are powered; they should not send scancodes.
*/
//should send 0 1 2 3
Key* ptrsKeys_R0[] = { &s_0, &s_z, &s_z, &s_z, &s_1, &s_z, &s_z, &s_z,
&s_2, &s_z, &s_z, &s_z, &s_3, &s_z, &s_z, &s_z };
Row_ShiftRegisters row_R0(0, sizeof(ptrsKeys_R0)/sizeof(*ptrsKeys_R0), ptrsKeys_R0);
//should send a b c d
Key* ptrsKeys_R1[] = { &s_a, &s_z, &s_z, &s_z, &s_b, &s_z, &s_z, &s_z,
&s_c, &s_z, &s_z, &s_z, &s_d, &s_z, &s_z, &s_z };
Row_ShiftRegisters row_R1(1, sizeof(ptrsKeys_R1)/sizeof(*ptrsKeys_R1), ptrsKeys_R1);
// ################### MAIN ####################
void setup()
{
Keyboard.begin();
SPI.begin();
row_R0.begin();
row_R1.begin();
}
void loop()
{
//left matrix
row_L0.process();
row_L1.process();
//right matrix
row_R0.process();
row_R1.process();
scanDelay.delay();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

View File

@ -0,0 +1,104 @@
/* keybrd_5_LEDs.ino
This sketch:
is firmware for a simple 2-layer keyboard with three LEDs
runs on the first two rows and columns of a breadboard keyboard
| Layout | **0** | **1** |
|:------:|-------|-------|
| **0** |CapsLck| a 1 |
| **1** | fn | b 2 |
*/
// ################## GLOBAL ###################
// ================= INCLUDES ==================
//Keys
#include <Code_Sc.h>
#include <Code_LEDLock.h>
#include <LayerState_LED.h>
#include <Code_LayerHold.h>
#include <Key_LayeredKeysArray.h>
#include <Row_uC.h>
#include <ScanDelay.h>
#include <LED_uC.h>
// ============ SPEED CONFIGURATION ============
ScanDelay scanDelay(9000);
// ================ ACTIVE STATE ===============
const bool Scanner_uC::STROBE_ON = LOW;
const bool Scanner_uC::STROBE_OFF = HIGH;
// ================= PINS =================
uint8_t readPins[] = {14, 15};
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins);
/* ==================== LEDs ===================
The LED_uC constructor parameter is for an Aduino pin that is connected to an LED.
LED_uC objects are passed to other objects that want to turn the LED on or off.
In this example, the LED_uC objects are named after the states they indicate.
The prtsLayerLEDs[] array contains one LED per layer, it is used to indicate the current layer.
*/
LED_uC LED_normal(16);
LED_uC LED_fn(17);
LED_uC LED_CapsLck(21);
LED* prtsLayerLEDs[] = { &LED_normal, &LED_fn };
// =================== CODES ===================
/* ---------------- LAYER CODE -----------------
LayerState_LED is similar to LayerState, introduced in keybrd_3a_multi-layer.ino, but with LEDs.
The LayerState_LED turns on the LED of the current layer.
The active layer is used as an index to dereference the prtsLayerLEDs[] array.
*/
enum layers { NORMAL, FN };
LayerState_LED layerState(prtsLayerLEDs);
Code_LayerHold l_fn(FN, layerState);
/* ---------------- SCAN CODES -----------------
When a Code_LEDLock object is pressed, it sends a scancodes and updates the its LED.
Scancodes can be one of KEY_CAPS_LOCK, KEY_SCROLL_LOCK, or KEY_NUM_LOCK.
For example, when o_capsLock is pressed, it sends KEY_CAPS_LOCK scancode and updates LED_CapsLck.
*/
Code_LEDLock o_capsLock(KEY_CAPS_LOCK, LED_CapsLck);
Code_Sc s_a(KEY_A);
Code_Sc s_b(KEY_B);
Code_Sc s_1(KEY_1);
Code_Sc s_2(KEY_2);
// =================== KEYS ====================
Key* const ptrsCodes_01[] = { &s_a, &s_1 };
Key_LayeredKeysArray k_01(ptrsCodes_01);
Key* const ptrsCodes_11[] = { &s_b, &s_2 };
Key_LayeredKeysArray k_11(ptrsCodes_11);
LayerStateInterface& Key_LayeredKeysArray::refLayerState = layerState;
// =================== ROWS ====================
Key* const ptrsKeys_0[] = { &o_capsLock, &k_01 };
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0);
Key* const ptrsKeys_1[] = { &l_fn, &k_11 };
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1);
/* ################### MAIN ####################
layerState.begin() turns on the LED of the initial active layer.
*/
void setup()
{
Keyboard.begin();
layerState.begin();
}
void loop()
{
row_0.process();
row_1.process();
scanDelay.delay();
}

View File

@ -0,0 +1,61 @@
/* keybrd_6_active_high.ino
This sketch:
is the tutorial 2 sketch with STROBE_ON/STROBE_OFF values swapped
is active high 1-layer keyboard
runs on the first two rows and columns of a active-high breadboard keyboard
| Layout | **0** | **1** |
|:------:|-------|-------|
| **0** | shift | a |
| **1** | b | c |
*/
// ################## GLOBAL ###################
// ================= INCLUDES ==================
#include <ScanDelay.h>
#include <Code_Sc.h>
#include <Row_uC.h>
// ============ SPEED CONFIGURATION ============
ScanDelay scanDelay(9000);
/* ================ ACTIVE STATE ===============
STROBE_ON and STROBE_OFF define the logic levels for the strobe.
"Active high" means that if a switch is pressed (active), the read pin is high.
To make this sketch active high, STROBE_ON should be HIGH.
Compared active low, STROBE_ON/STROBE_OFF values swapped.
*/
const bool Scanner_uC::STROBE_ON = HIGH; //set matrix for active high
const bool Scanner_uC::STROBE_OFF = LOW;
// ================= PINS =================
uint8_t readPins[] = {14, 15};
uint8_t READ_PIN_COUNT = sizeof(readPins)/sizeof(*readPins);
// =================== CODES ===================
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);
// =================== ROWS ====================
Key* ptrsKeys_0[] = { &s_shift, &s_a };
Row_uC row_0(0, readPins, READ_PIN_COUNT, ptrsKeys_0);
Key* ptrsKeys_1[] = { &s_b, &s_c };
Row_uC row_1(1, readPins, READ_PIN_COUNT, ptrsKeys_1);
// ################### MAIN ####################
void setup()
{
Keyboard.begin();
}
void loop()
{
row_0.process();
row_1.process();
scanDelay.delay();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@ -1,30 +1,15 @@
Tutorial 0 - Introduction
=========================
The first two tutorials are intended to be read in sequence:
1. Breadboard keyboard
2. Single-layer keybrd
* Tutorial 1 builds a breadboard keyboard
* Tutorial 2 covers basic keyboard knowledge needed to understand the remaining tutorials.
Tutorial 1 is about making a breadboard keyboard, which is used in tutorials 2 through 6.
Tutorial 2 is needed to understand the remaining tutorials.
The remaining tutorials can be read in any order.
You will need a breadboard keyboard with a Teensy 2.0 controller to run tutorial sketches 2 through 6.
If you use a different controller, you may have to change pin assignments and port classes.
If you already have a keyboard with an Arduino compatible controller, you can use that instead of a breadboard keyboard.
All the tutorial sketches use 2 to 8 keys.
If your keyboard has more keys, they are simply ignored by the sketch.
Tutorials from 3 up can be read in any order.
Tutorials 2 through 7 use the keyboard breadboard that was built in tutorial 1.
Tutorials from 8 up are advance topics about the keybrd library.
The tutorials assume the reader:
* is familiar with C++
* is new to Arduino, firmware, controllers, and the internal workings of keyboards
<!-- todo -->
> Most of the tutorial sketches are obsolete and will not compile. Will be updated soon.
> The tutorial sketches will be changed to Teensy LC and MCP23018 I/O expander
> Some of the pictures do not match the sketches, they will be updated after changing to Teensy LC
> Schematic diagrams are missing from tutorials 2 and 4, they will be added after changing to Teensy LC
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/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="http://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="http://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="http://creativecommons.org/ns#" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>.

View File

@ -1,32 +1,39 @@
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.
## Why a breadboard keyboard is useful
All the tutorial example sketches run on breadboard keyboards that have 2 to 8 keys.
## Why a solderless breadboard keyboard is useful
Breadboard keyboards have row-column matrices and diodes just like the big keyboards.
A breadboard is the easiest way to learn keyboard electronics.
A novice won't get everything right the first time.
It's easy to get some detail wrong with electronics.
There is a learning curve.
Compared to PCBs, breadboard keyboards are easier to learn on because:
Learning is fun when mistakes are easily corrected.
Compared to PCBs, breadboard keyboards make learning faster because:
* Mistakes are easily corrected; no soldering and desoldering
* Parts can be reused in many different configurations
* A small keyboard is easier to trouble shoot
Breadboard keyboards are useful for:
* learning keyboard electronics - diodes, micro controllers, I/O expanders
* learning keyboard electronics - micro controllers, diodes, shift registers, I/O expanders
* learning the firmware development workflow
* prototyping circuits before making a PCB
Arduino simulation software might be another way; I haven't tried that.
## Breadboard keyboard starter kit
The parts needed to build all the tutorial Breadboard Keyboards are listed in [breadboard_keyboard_supplies.ods](breadboard_keyboard_supplies.ods).
The parts needed to build the tutorial Breadboard Keyboards are listed in [breadboard_keyboard_supplies.ods](breadboard_keyboard_supplies.ods).
The tutorials use a Teensy LC controller, but any Arduino-compatible controller should work.
You will need two tools:
* Wire cutters (or nail clipper)
* A multi-meter for trouble shooting
Wire striper and lead forming tool are optional.
## How a breadboard works
To understand the breadboard keyboard you will need to know the internal parts of a breadboard:
* bus strip
@ -39,61 +46,69 @@ This excellent article explains how the microcontroller, matrix, switches and di
[How a Key Matrix Work](http://pcbheaven.com/wikipages/How_Key_Matrices_Works/)
## Building a basic breadboard keyboard
The basic breadboard has 4 switches and a microcontroller.
The basic breadboard keyboard has 4 switches.
![breadboard keyboard with 2 rows and 2 columns](images/breadboard_keyboard_2x2_labeled.jpg "2x2 breadboard keyboard")
![basic breadboard keyboard](keybrd_1_breadboard_images/breadboard_keyboard_2x2.JPG "basic breadboard keyboard")
The key matrix has two rows and two columns.
Breadboard bus strips are used as matrix rows.
A Teensy LC microcontroller in on the left.
A key matrix with 4 switches is to the right.
The key matrix has two two columns.
Short wires connect terminal strips into matrix columns.
Switch-diode pairs connect rows to columns.
Jumper wires connect the columns to the microcontroller.
The green rectangle on the right is the Teensy 2.0 microcontroller.
The matrix rows and columns connect to the microcontroller via jumper wires.
The key matrix has two two rows.
Breadboard bus strips are matrix rows.
A jumper connects the top row to the microcontroller.
A short wire connects the bottom row to the microcontroller.
Tutorials 4,5,9 will add more components to the breadboard.
Positioning components as shown on the picture will provide space for those components, with room for up to 9 cols.
Breadboard terminal strips are indexed 1 to 63 for accurate positioning of components.
[pic of IOE, LEDs, active high on one bb]
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.
Positioning components as shown on the picture will provide space for those components.
![pic of shift registers, LEDs, active high on one bb]
Breadboard keyboard assembly instructions:
1. Cut leads to length.
* tactile-switch-lead length 6 to 8 mm
* diodes 22 to 24 mm total end-to-end length, and save the cut offs for steps 2 and 3
1. Bend and cut leads to fit breadboard.
* tactile-switch-lead
* diodes (save the cut offs for steps 2, 3, and tutorial 4)
![bend diodes](keybrd_1_breadboard_images/diodes_bend_en_masse.JPG "bend diodes")
![cut diodes](keybrd_1_breadboard_images/diodes_cut.JPG "cut diodes")
2. Insert parts into the breadboard as shown in the picture.
* Teensy LC on the terminal strip labeled 1
* The breadboard is oriented with the red bus strips on top and blue bus strips on the bottom
(this is important because tutorials will refer to the "red bus" and the "blue bus")
* Teensy LC is positioned such that:
* terminal strips above Teensy have three holes exposed
* terminal strips below Teensy have two holes exposed
(the holes will be used in later tutorials)
* switch leads are oriented to connect diodes to columns (pictured below)
* diode cut offs connect terminal strips into columns
* switch leads are oriented to connect diodes to columns
* diodes are orient with cathode (banded end) towards the row (bus strip)
3. Insert jumper wires connecting Teensy2 to the matrix rows and columns.
* follow pin connections table (below) and consult pinout diagram in
[close-up pic shows switch way half out, to show lead orientation]
[Teensy2_pinout.txt](../doc/Teensy2_pinout.txt)
* diodes connect to the blue bus, orient with cathode (banded end) towards the row (bus strip)
todo add a schematic
![switch orientation](keybrd_1_breadboard_images/switch_orientation.JPG "switch orientation")
![basic breadboard keyboard overhead](keybrd_1_breadboard_images/breadboard_keyboard_2x2_overhead.JPG "basic breadboard keyboard overhead")
<!-- This schematic was written by consulting the micro-controller's datasheet and using the ?? tool.
3. Insert jumper wires to connect Arduino pins to the matrix rows and columns.
* [Teensy LC pinout diagram](https://www.pjrc.com/teensy/card6a_rev2.png).
* row_0 is the top row, and col_0 is the left column
this table might not match the sketches, replace with a schematic
**Teensy 2.0 pin connections table**
| Pin number | Row Column |
|------------|-------------|
| 21 | row_0 |
| 20 | row_1 |
| 0 | col_0 |
| 1 | col_1 |
-->
| Pin number | connected to |
|------------|--------------|
| 0 | row_0 |
| 1 | row_1 |
| 14 | col_0 |
| 15 | col_1 |
## Compiling and loading the keyboard firmware
Follow the [keybrd Library User's Guide](../doc/keybrd_library_user_guide.md) to set up the Arduino environment and to compile and load keybrd firmware onto the keyboard's controller.
Follow the [keybrd Library User's Guide](../doc/keybrd_library_user_guide.md) to set up the Arduino environment.
## Bigger breadboard keyboards
Sometimes its useful to prototype a full keyboard matrix before designing the PCB.
Several breadboards can be tied together into one.
![big breadboard keyboard](images/breadboard_big.jpg "breadboard_big.jpg")
Compile and load the [keybrd_2_single-layer.ino](keybrd_2_single-layer/keybrd_2_single-layer.ino) sketch into the keyboard's controller.
<br><br>
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/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="http://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="http://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="http://creativecommons.org/ns#" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>.

View File

@ -2,16 +2,15 @@ Tutorial 2 - single-layer keyboard
=======================================
The easiest way to learn the keyboard library is to read some simple sketches.
[keybrd_2_single-layer_annotated.ino](keybrd_2_single-layer_annotated/keybrd_2_single-layer_annotated.ino) is a simple sketch with annotations that explain how a keybrd sketch works.
After reading the sketch you will be able to modify it to suite your own single-layer keyboard design.
The sketch will run on the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md)
You can view the class definitions in the [keybrd library](../src/).
![basic breadboard keyboard](keybrd_1_breadboard_images/breadboard_keyboard_2x2.JPG "basic breadboard keyboard")
Class definitions can be viewed in the [keybrd library](../src/).
After reading the sketch you will be able to modify it to suite your own single-layer keyboard design.
## Exercises
1) Add a third column to the breadboard keyboard and sketch.
| Layout | **0** | **1** | **2** |
|:------:|-------|-------|-------|
| **0** | a | b | c |
| **1** | 1 | 2 | shift |
<br><br>
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/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="http://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="http://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="http://creativecommons.org/ns#" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>.

View File

@ -1,9 +1,9 @@
Tutorial 3a - multi-layer keyboard
==================================
When you finish this tutorial you will be able to be able to modify a multi-layer keybrd sketch to suite your own multi-layer keyboard design.
When you finish this tutorial you will be able to be able to modify a multi-layer keybrd sketch to write your very own multi-layer keyboard design.
## Multi-layer nomenclature
**[layers](http://deskthority.net/wiki/Layer)** are key bindings provided by the keyboard firmware. For example,
**[layers](http://deskthority.net/wiki/Layer)** - are key bindings provided by the keyboard firmware. For example,
* The classic [IBM PC keyboard](http://en.wikipedia.org/wiki/IBM_PC_keyboard) has one layer.
* Many compact keyboards have an additional [Fn layer](http://en.wikipedia.org/wiki/Fn_key).
* The [Neo layout](http://neo-layout.org/index_en.html) has 6 layers.
@ -12,21 +12,36 @@ When you finish this tutorial you will be able to be able to modify a multi-laye
**active layer** - is the layer currently used by the keyboard.
**layer scheme** - is a system for changing layers while typing (a single-layer scheme does not change layers).
**layer scheme** - is a system for changing the active layer while typing (a single-layer scheme does not change layers).
## A simple multi-layer keybrd sketch
The [keybrd_3a_multi-layer.ino](keybrd_3a_multi-layer/keybrd_3a_multi-layer.ino) sketch is for a simple two-layer keyboard.
It will run on the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md)
![basic breadboard keyboard](keybrd_1_breadboard_images/breadboard_keyboard_2x2.JPG "basic breadboard keyboard")
Read the sketch annotations to understand how multi-layer keyboards work.
The sketch uses three layer-scheme classes:
* LayerState
* Code_LayerHold
* Key_LayeredKeysArray
The internal workings of these three classes are revealed in the next section.
## Pseudo code for simple layer scheme
The following pseudo code has just enough detail to show how layer schemes work.
The following is pseudo code of three keybrd library classes.
It has just enough detail to show the internal workings of layer schemes.
**Key_Layer** objects are used to select an active layer.
**Key_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.
```
class Key_Layer
{
int layer
LayerState& refLayerState
press() { refLayerState.setActiveLayer(layer) }
}
int layer;
LayerState& refLayerState;
press() { refLayerState.setActiveLayer(layer); }
};
```
**LayerState** objects keep track of the active layer.
@ -34,44 +49,44 @@ A LayerState's activeLayer is always up to date.
```
class LayerState
{
int activeLayer
setActiveLayer(int layer) { activeLayer = layer }
getActiveLayer() { return activeLayer }
}
int activeLayer;
setActiveLayer(int layer) { activeLayer = layer; }
getActiveLayer() { return activeLayer; }
};
```
**Key_Layered** objects contain multiple elements, one element for each layer.
Layer ids are used like indexes to send the appropriate element.
When a Key_Layered object is pressed, it gets the active layer from LayerState, and then sends the appropriate element.
**Key_LayeredKeysArray** objects contain an array of keys, one key for each layer.
Key_LayeredKeysArray use layer ids as array indexes to send the appropriate key.
When a Key_LayeredKeysArray object is pressed, it gets the active layer from LayerState, and sends the corresponding key.
```
class Key_Layered
class Key_LayeredKeysArray
{
Key** ptrsKeys //array of Key pointers, one Key pointer per layer
LayerState& refLayerState
press() { layer = refLayerState.getActiveLayer()
ptrsKeys[layer]->press() }
}
Key** ptrsKeys; //array of Key pointers, one Key pointer per layer
LayerState& refLayerState;
press() { layer = refLayerState.getActiveLayer();
ptrsKeys[layer]->press(); }
};
```
Dependency diagram
```
+-----------+
| Key_Layer |
+-----------+
|
|setActiveLayer()
|
v
+------------+
| LayerState |
+------------+
^
|
|getActiveLayer()
|
+-------------+
| Key_Layered |
+-------------+
+-----------+
| Key_Layer |
+-----------+
|
|setActiveLayer()
|
v
+------------+
| LayerState |
+------------+
^
|
|getActiveLayer()
|
+----------------------+
| Key_LayeredKeysArray |
+----------------------+
```
## Layer-scheme classes
There are several layer scheme-classes to choose from.
@ -85,14 +100,13 @@ A basic LayerState class is:
* LayerState
Key_Layered classes include:
* Key_LayeredKeysArray
* Code_LayeredScSc
* Code_LayeredCodeSc
* Code_LayeredCodeCode
* Key_LayeredKeysArray
## Single-layer Codes
Most Code objects only have one scancode or code.
They are not affected by the active layer.
Example single-layer Code classes include:
* Code_Sc
* Code_ScS
@ -101,21 +115,8 @@ Example single-layer Code classes include:
* Code_LayerHold
* Code_LayerLock
<!-- todo -->
(Future version of keybrd library may change all Code classes to Key classes.)
## A simple multi-layer keybrd sketch
The [keybrd_3a_multi-layer_annotated.ino](keybrd_3a_multi-layer_annotated/keybrd_3a_multi-layer_annotated.ino)
sketch uses three layer-scheme classes:
* LayerState
* Code_LayerHold
* Key_LayeredKeysArray
Annotations in the sketch explain how the multi-layer feature works.
## Exercises
1) Modify the keybrd_3_multi-layer_annotated.ino sketch to use two Code_LayerLock objects.
1) Modify the keybrd_3_multi-layer.ino sketch to use two Code_LayerLock objects.
| Layout | **0** | **1** |
|:------:|--------|--------|
@ -123,3 +124,4 @@ Annotations in the sketch explain how the multi-layer feature works.
| **1** | layer0 | layer1 |
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/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="http://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="http://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="http://creativecommons.org/ns#" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>.

View File

@ -0,0 +1,77 @@
keybrd Tutorial 4b - split keyboard with shift registers
========================================================
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 shift registers
The breadboard in the following picture models a split keyboard.
The primary matrix on the left has one column, which is read by a microcontroller pin.
The secondary matrix on the right has 4 columns, which are read by the shift register input pins.
The primary and secondary matrices share the same rows, which are strobed by micro-controller pins.
Both matrices are active low.
![breadboard keyboard with shift_registers](keybrd_4b_split_keyboard_with_shift_registers/shift_reg_front.JPG )
## Building a split keyboard with shift registers
The breadboard keyboard modifies the basic breadboard keyboard described in [tutorial_1_breadboard_keyboard.md](tutorial_1_breadboard_keyboard.md)
Add components to the breadboard as shown in the picture.
The shift register is a SN74HC165N. Details are in the SN74HC165N datasheet.
Each shift register has a small notch on one end to identify pin 1.
In the picture, 1 pins are on the right 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.
The breadboard doesn't have enough room for 16 columns; only 4 columns are connected to the shift registers.
Every 4th input pin is connected to a matrix column and a pull-up resistor.
Unused input pins are connected to power.
The red bus strips power the pull up resistors and unused input pins.
A decoupling capacitor between the power and ground wires dampens noise coming in through those wires.
![breadboard keyboard with shift_registers](keybrd_4b_split_keyboard_with_shift_registers/shift_reg_side.JPG )
![breadboard keyboard with shift_registers](keybrd_4b_split_keyboard_with_shift_registers/shift_reg_back.JPG )
Blue bus strips are used for strobing rows
I apologize for not having a schematic. This table should help you figure out the pictures:
<!-- todo add schematic -->
```
74HC165 left (upper half of breadboard)
NAME PIN# I/O DESCRIPTION DESTINATION PIN# CHAIN (wires flat on breadboard)
SH/LD 1 I shift or load input Teensy LC CS0 10 red wire
CLK 2 I clock input Teensy LC SCK0 13 green wire
D4 3 I parallel input pull-up resistor red bus
D5 4 I parallel input power red bus
D6 5 I parallel input power red bus
D7 6 I parallel input power red bus
/QH 7 O ~serial output Teensy LC MISO0 12
GND 8 ground gnd black wire
74HC165 right (lower half of breadboard)
NAME PIN# I/O DESCRIPTION DESTINATION CHAIN (wires flat on breadboard)
VCC 16 power pin Teensy LC 3.3V red bus
CLK INH 15 I clock inhibit gnd black wire
D3 14 I parallel input power red bus
D2 13 I parallel input power red bus
D1 12 I parallel input power red bus
D0 11 I parallel input pull-up resistor red bus
SER 10 I serial input next QH yellow wire
QH 9 O serial output previous SER yellow wire
```
## 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.
Annotations in the sketch explain the code.
## Exercises
1. Guess what happens if an unused input pin is not powered? Try it.
<br><br>
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/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="http://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="http://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="http://creativecommons.org/ns#" href="https://github.com/wolfv6/keybrd/issues/new" rel="cc:morePermissions">https://github.com/wolfv6/keybrd/issues/new</a>.

View File

@ -6,10 +6,14 @@ When you finish this tutorial you will be able to be able to modify a 2-matrix k
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 has four bus strips used as rows.
Two rows connected to a microcontroller, and two rows connected to a I/O expander.
The breadboard's four bus strips are used as rows.
Two rows (blue bus strips) are connected to the microcontroller.
Two rows (red bus strips) are connected to the shift registers.
The I/O expander has a small notch on one end, which identifies the end with pin 1.
The breadboard's four bus strips are used as rows.
Two rows connect to a microcontroller, and two rows connected to a I/O expander.
The I/O expander has a small notch on one end, which identifies pin 1.
In the picture, pin 1 is on the right end.
The microcontroller and I/O expander are connected by 4 jumper wires:
@ -18,7 +22,7 @@ The microcontroller and I/O expander are connected by 4 jumper wires:
* Serial CLock signal (SCL)
* Serial DAta signal (SDA)
A decoupling capacitor on the power pin dampens noise coming in through the power wire.
A decoupling capacitor on the power pin dampens noise coming in through the power and ground wires.
The microcontroller and I/O expander communicate via [I2C](http://en.wikipedia.org/wiki/I%C2%B2C) bus, which consists of two signals: SCL and SDA.
Two resistors pull-up voltage on the SCL and SDA.

View File

@ -0,0 +1,76 @@
Tutorial 6 - Active high
=========================
This tutorial pulls together several concepts needed to understand active state in the context of a keyboard.
Skip to the end of this tutorial if you just want to copy an active-high keyboard.
## Pull-up resistors
There are many sources that explain "pull-up resistors", so I won't repeat it here.
Here is a [good tutorial on Pull-up Resistors](https://learn.sparkfun.com/tutorials/pull-up-resistors/what-is-a-pull-up-resistor).
## Active low
All the preceding breadboard keyboards in this tutorial series have used active low with internal pull-up resistors.
"Active low" means that if a switch is pressed (active state), the read pin is low.
When the switch is released (inactive state), the pull-up resistor pulls the read pin high.
Active low requires pull-up resistors.
The following table traces the strobe current from left to right (0 is ground, 1 is power).
If the switch is closed, the strobe current passes through the switch and pulls the read pin low.
If the switch is open, the pull-up resistor pulls the read pin high.
|Strobe pin on | Diode orientation | Switch position | Pull resistor | Read pin state |
|:------------:|:------------------:|:---------------:|:-------------:|:---------------:|
| 0 | cathode -:<- anode | close | 1 pull-up | 0 active low |
| 0 | cathode -:<- anode | open | 1 pull-up | 1 inactive high |
Arduino boards have internal pull-up resistors, which saves on parts and labor compared to manually adding external pull resistors.
If you are designing a keyboard, go with active low.
To make a keyboard active low:
* Use internal pull-up resistors if the IC has them
* Orient diodes with cathode (banded end) towards the write pins (row)
* Define strobe on and off in the sketch like this:
```
const bool Scanner_uC::STROBE_ON = LOW;
const bool Scanner_uC::STROBE_OFF = HIGH;
```
## Active high
"Active high" means that if a switch is pressed (active), the read pin is high.
When the switch is released (inactive), the pull-down resistor pulls the read pin low.
Active high requires pull-down resistors.
The following table traces the strobe current from left to right (0 is ground, 1 is power).
If the switch is closed, the strobe current passes through the switch and pulls the read pin high.
If the switch is open, the pull-up resistor pulls the read pin low.
|Strobe pin on | Diode orientation | Switch position | Pull resistor | Read pin state |
|:------------:|:------------------:|:---------------:|:-------------:|:---------------:|
| 1 | anode ->:- cathode | close | 0 pull-down | 1 active high |
| 1 | anode ->:- cathode | open | 0 pull-down | 0 inactive low |
Arduino boards do not have internal pull-down resistors.
If you want to use active low, you will have to add external pull-down resistors to the read pins.
To make a keyboard active high:
* Add an external 10k pull-down resistor to each read pin
* Orient diodes with cathode (banded end) towards the read pins
* Define strobe on and off in the sketch like this:
```
const bool Scanner_uC::STROBE_ON = HIGH;
const bool Scanner_uC::STROBE_OFF = LOW;
```
## Making an active-high keyboard
This tutorial converts the basic breadboard keyboard from tutorial 1 to active high.
By comparing the above tables, one can see what changes need to be made:
* add external pull-down resistors to the read pins
* flip the diodes so that the cathode (banded end) are towards the read pins
* swap the STROBE_ON and STROBE_OFF values
The red bus is grounded.
The pull-down resistors plug into the red bus and column read pins.
The [keybrd_6_active_highsketch.ino](keybrd_6_active_high/keybrd_6_active_high.ino) is the tutorial 2 sketch with STROBE_ON and STROBE_OFF values swapped.
![pull_down_resistors.JPG](keybrd_6_active_high/pull_down_resistors.JPG "Active-high diodes and pull-down resistors")