@@ -7,10 +7,17 @@ keybrd version 1.0.0 will be released when the public API is stable. | |||
## [Unreleased][unreleased] | |||
## [0.3.2] - 2016-06-21 | |||
### Added | |||
config_keybrd.h for size configurations. | |||
RowScanner_SPI-ShiftRegisters for compact split keyboards up to 32 keys per matrix. | |||
LED_PinNumber for controlling indicator lights by pin number. | |||
## [0.3.2] - 2016-06-10 | |||
### Changed | |||
* Changed uC from scanning port arrays to scanning Arduino pins. | |||
Thereby added support for Arduino boards, Teensy 3, and Teensy LC micro controllers. | |||
* Changed uC from scanning port arrays to scanning Arduino pins, thereby adding support for: | |||
Arduino boards, Teensy 3, and Teensy LC micro controllers | |||
up to 31x31 matrix capability | |||
* Changed IOE from scanning port arrays to scanning single ports. | |||
* Moved scanner and debouncer into their own classes. | |||
@@ -2,16 +2,14 @@ planned_features is a view of where the keybrd project is headed. | |||
Top priority | |||
============ | |||
Add support for shift registers, for compact split keyboards up to 32 keys per matrix. | |||
MCP23S18 I/O expander with Serial Peripheral Interface (SPI) | |||
Med priority | |||
============ | |||
Add 16x16 matrix capability (currently limited to 8x8 matrices) | |||
Add matrix-to-layout mapping array (to decouple matrix from layout) | |||
Low priority | |||
============ | |||
Add matrix-to-layout mapping array (to decouple matrix from layout) | |||
Update tutorials: | |||
* Currently tutorial sketches are obsolete and won't compile | |||
* Change tutorial sketches from teensy 2.0 and PCA9655E-D IOE to Teensy LC and MCP23018 IOE |
@@ -1,12 +1,14 @@ | |||
#ifndef DEBOUNCERINTERFACE_H | |||
#define DEBOUNCERINTERFACE_H | |||
#include <config_keybrd.h> | |||
/* DebouncerInterface is an interface class. | |||
debounce() takes rawSignal and returns debounced signal. Signals are bitwise. | |||
*/ | |||
class DebouncerInterface | |||
{ | |||
public: | |||
virtual uint8_t debounce(const uint8_t rawSignal, uint8_t& debounced)=0; | |||
virtual read_pins_t debounce(const read_pins_t rawSignal, read_pins_t& debounced)=0; | |||
}; | |||
#endif |
@@ -4,10 +4,10 @@ where each sample contains the switch states for a row of switches, one bit per | |||
Debounce uses Dr. Marty's debounce algorithm from | |||
http://drmarty.blogspot.com.br/2009/05/best-switch-debounce-routine-ever.html | |||
I2C and TWI protocols do not include any Packet Error Checking (PEC). | |||
SPI, I2C, and TWI protocols do not include any Packet Error Checking (PEC). | |||
The goal of Marty's debounce routine is to reject spurious signals, | |||
which is useful when connecting split keyboards with a cable using I2C or TWI. | |||
Was tested on split keyboard with 3-meter long telephone wire to I/O expander | |||
which is useful when connecting split keyboards with a cable using SPI, I2C, or TWI. | |||
This class was tested on split keyboard with a 3-meter long telephone wire to I/O expander. | |||
Dr. Marty's debounce algorithm: | |||
Periodically read samples and update the state when a number consecutive sample bits are equal. | |||
@@ -23,11 +23,11 @@ There is a latency equal to SAMPLE_COUNT, between button press and debounced sig | |||
samples[SAMPLE_COUNT] is a ring buffer. samplesIndex is it's current write index. | |||
SAMPLE_COUNT is the number of consecutive equal samples needed to debounce. | |||
SAMPLE_COUNT is a macro because it defines samples[SAMPLE_COUNT] array size at compile time. | |||
SAMPLE_COUNT should be at lease 1. | |||
SAMPLE_COUNT is defined in config_keybrd.h and should be at lease 1. | |||
SAMPLE_COUNT = 4 is very reliable for a keyboard. | |||
Keyboards with a long I2C wire or in environment with strong electromagnetic interference (EMI) | |||
may need a larger SAMPLE_COUNT for reliability. | |||
Split keyboards with a long connecting wire or in environment with | |||
strong electromagnetic interference (EMI) may need a larger SAMPLE_COUNT for reliability. | |||
*/ | |||
#include "Debouncer_4Samples.h" | |||
@@ -35,11 +35,11 @@ may need a larger SAMPLE_COUNT for reliability. | |||
For parameters, 1 means pressed, 0 means released. | |||
For return, 1 means debounced changed. | |||
*/ | |||
uint8_t Debouncer_4Samples::debounce(const uint8_t rawSignal, uint8_t& debounced) | |||
read_pins_t Debouncer_4Samples::debounce(const read_pins_t rawSignal, read_pins_t& debounced) | |||
{ | |||
uint8_t previousDebounced; //bitwise, 1 means pressed, 0 means released | |||
uint8_t all_1 = ~0; //bitwise | |||
uint8_t all_0 = 0; //bitwise | |||
read_pins_t previousDebounced; //bitwise, 1 means pressed, 0 means released | |||
read_pins_t all_1 = ~0; //bitwise | |||
read_pins_t all_0 = 0; //bitwise | |||
samples[samplesIndex] = rawSignal; //insert rawSignal into samples[] ring buffer | |||
@@ -2,21 +2,20 @@ | |||
#define DEBOUNCER_4SAMPLES_H | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <config_keybrd.h> | |||
#include <DebouncerInterface.h> | |||
#define SAMPLE_COUNT 4 //number of consecutive equal bits needed to change a debounced bit | |||
/* Debouncer_4Samples | |||
Configuration: #define SAMPLE_COUNT in this header file. | |||
Configuration: #define SAMPLE_COUNT in config_keybrd.h | |||
*/ | |||
class Debouncer_4Samples : public DebouncerInterface | |||
{ | |||
private: | |||
uint8_t samples[SAMPLE_COUNT]; //bitwise, one bit per key, most recent readings | |||
read_pins_t samples[SAMPLE_COUNT]; //bitwise, one bit per key, most recent readings | |||
uint8_t samplesIndex; //samples[] current write index | |||
public: | |||
Debouncer_4Samples(): samplesIndex(0) {} | |||
virtual uint8_t debounce(const uint8_t rawSignal, uint8_t& debounced); | |||
virtual read_pins_t debounce(const read_pins_t rawSignal, read_pins_t& debounced); | |||
}; | |||
#endif | |||
@@ -5,9 +5,9 @@ process() scans the row and calls any newly pressed or released keys. | |||
void RowBase::process() | |||
{ | |||
//these variables are all bitwise, one bit per key | |||
uint8_t rowState; //1 means pressed, 0 means released | |||
uint16_t rowEnd; //1 bit marks positioned after last key of row | |||
uint8_t debouncedChanged; //1 means debounced changed | |||
read_pins_t rowState; //1 means pressed, 0 means released | |||
read_pins_mask_t rowEnd; //1 bit marks positioned after last key of row | |||
read_pins_t debouncedChanged; //1 means debounced changed | |||
wait(); | |||
rowState = scan(rowEnd); | |||
@@ -56,11 +56,11 @@ pressRelease() calls key's press() or release() function if it was pressed or re | |||
Both parameters are bitwise. | |||
rowEnd bit marks positioned immediatly after last key of row. | |||
*/ | |||
void RowBase::pressRelease(const uint16_t rowEnd, const uint8_t debouncedChanged) | |||
void RowBase::pressRelease(const read_pins_mask_t rowEnd, const read_pins_t debouncedChanged) | |||
{ | |||
uint8_t isFallingEdge; //1 means falling edge | |||
uint8_t isRisingEdge; //1 means rising edge | |||
uint16_t rowMask; //bitwise, active col bit is 1 (same type as rowEnd) | |||
read_pins_t isFallingEdge; //bitwise, 1 means falling edge | |||
read_pins_t isRisingEdge; //bitwise, 1 means rising edge | |||
read_pins_t rowMask; //bitwise, active col bit is 1 (same type as rowEnd) | |||
uint8_t col; //index for ptrsKeys[col] array | |||
//bit=1 if last debounced changed from 1 to 0, else bit=0 |
@@ -2,6 +2,7 @@ | |||
#define ROWBASE_H | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <config_keybrd.h> | |||
#include <Key.h> | |||
/* RowBase is an abstract base class. | |||
@@ -14,14 +15,14 @@ class RowBase | |||
virtual void keyWasPressed(); | |||
protected: | |||
uint8_t debounced; //bitwise, 1 means pressed, 0 means released | |||
read_pins_t debounced; //bitwise, 1 means pressed, 0 means released | |||
void wait(); | |||
void pressRelease(const uint16_t rowEnd, const uint8_t debouncedChanged); | |||
void pressRelease(const read_pins_mask_t rowEnd, const read_pins_t debouncedChanged); | |||
public: | |||
RowBase(Key *const ptrsKeys[]) : ptrsKeys(ptrsKeys), debounced(0) { } | |||
virtual void process(); | |||
virtual uint8_t scan(uint16_t& rowEnd)=0; | |||
virtual uint8_t debounce(const uint8_t rowState, uint8_t& debounced)=0; | |||
virtual read_pins_t scan(read_pins_mask_t& rowEnd)=0; | |||
virtual read_pins_t debounce(const read_pins_t rowState, read_pins_t& debounced)=0; | |||
}; | |||
#endif |
@@ -3,11 +3,12 @@ | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <config_keybrd.h> | |||
class RowScannerInterface | |||
{ | |||
public: | |||
virtual uint8_t scan(uint16_t& rowEnd)=0; | |||
virtual read_pins_t scan(read_pins_mask_t& rowEnd)=0; | |||
}; | |||
#endif | |||
@@ -39,9 +39,9 @@ https://www.arduino.cc/en/Reference/DigitalWrite | |||
https://www.arduino.cc/en/Reference/DigitalRead | |||
https://www.arduino.cc/en/Reference/Constants > Digital Pins modes: INPUT, INPUT_PULLUP, and OUTPUT | |||
*/ | |||
uint8_t RowScanner_PinsArray::scan(uint16_t& rowEnd) | |||
read_pins_t RowScanner_PinsArray::scan(read_pins_mask_t& rowEnd) | |||
{ | |||
uint8_t rowState = 0; | |||
read_pins_t rowState = 0; //bitwise | |||
rowEnd = 1; | |||
//strobe row on |
@@ -2,11 +2,14 @@ | |||
#define ROWSCANNER_PINSARRAY_H | |||
#include <Arduino.h> | |||
#include <inttypes.h> | |||
#include <config_keybrd.h> | |||
#include <RowScannerInterface.h> | |||
#include <RowPort.h> | |||
#include <ColPort.h> | |||
/* RowScanner_PinsArray class uses Arduino pin numbers (not port pin numbers). | |||
The maximum keys per row is 31, because Arduino's largest type is 32 bits and rowEnd consumes the last bit. | |||
Constructor is in RowScanner_PinsArray.cpp | |||
*/ | |||
class RowScanner_PinsArray : public RowScannerInterface | |||
{ | |||
@@ -18,8 +21,8 @@ class RowScanner_PinsArray : public RowScannerInterface | |||
public: | |||
RowScanner_PinsArray(const uint8_t strobePin, | |||
const uint8_t readPins[], const uint8_t READ_PIN_COUNT); | |||
virtual uint8_t scan(uint16_t& rowEnd); | |||
uint8_t getRowState(uint16_t& rowEnd); | |||
virtual read_pins_t scan(read_pins_mask_t& rowEnd); | |||
//read_pins_t getRowState(read_pins_mask_t& rowEnd); | |||
}; | |||
#endif | |||
@@ -3,7 +3,7 @@ | |||
Strobes the row and reads the columns. | |||
Sets rowEnd and returns rowState. | |||
*/ | |||
uint8_t RowScanner_PinsBitwise::scan(uint16_t& rowEnd) | |||
read_pins_t RowScanner_PinsBitwise::scan(read_pins_mask_t& rowEnd) | |||
{ | |||
//strobe row on | |||
if (activeHigh) | |||
@@ -39,7 +39,7 @@ rowEnd is a bitwise row mask, one col per bit, where active col bit is 1. | |||
At end of function, 1 bit marks place immediatly after last key of row. | |||
rowEnd is a larger type than portMask so that it can not overflow. | |||
*/ | |||
uint8_t RowScanner_PinsBitwise::getRowState(uint16_t& rowEnd) | |||
uint8_t RowScanner_PinsBitwise::getRowState(read_pins_mask_t& rowEnd) | |||
{ | |||
uint8_t rowState = 0; //bitwise, one key per bit, 1 means key is pressed | |||
uint8_t portMask; //bitwise, 1 bit is a colPortState position |
@@ -7,6 +7,7 @@ | |||
#include <ColPort.h> | |||
/* RowScanner_PinsBitwise uses bit manipulation to read all pins of one port. | |||
The maximum keys per row is 8, because ports have a maximum of 8 pins each. | |||
*/ | |||
class RowScanner_PinsBitwise : public RowScannerInterface | |||
{ | |||
@@ -20,7 +21,7 @@ class RowScanner_PinsBitwise : public RowScannerInterface | |||
: refRowPort(refRowPort), strobePin(strobePin), | |||
refColPort(refColPort) {} | |||
static const bool activeHigh; //logic level of strobe pin: 0=activeLow, 1=activeHigh | |||
virtual uint8_t scan(uint16_t& rowEnd); | |||
uint8_t getRowState(uint16_t& rowEnd); | |||
virtual read_pins_t scan(read_pins_mask_t& rowEnd); | |||
uint8_t getRowState(read_pins_mask_t& rowEnd); | |||
}; | |||
#endif |
@@ -9,10 +9,10 @@ void RowScanner_SPIShiftRegisters::begin() | |||
/* | |||
Sets rowEnd and returns rowState. | |||
*/ | |||
uint8_t RowScanner_SPIShiftRegisters::scan(uint16_t& rowEnd) | |||
read_pins_t RowScanner_SPIShiftRegisters::scan(read_pins_mask_t& rowEnd) | |||
{ | |||
//todo rowEnd, rowState, return int size depend on BYTE_COUNT, like in send() | |||
uint8_t rowState; | |||
read_pins_t rowState = 0; | |||
digitalWrite(SS, LOW); | |||
digitalWrite(SS, HIGH); |
@@ -8,6 +8,7 @@ | |||
#include <ColPort.h> | |||
/* RowScanner_SPIShiftRegisters reads all shift registers in a daisy chain. | |||
The maximum keys per row is 31, because Arduino's largest type is 32 bits and rowEnd consumes the last bit. | |||
//todo delete: Assumes only one row of shift registers is connected (no Slave Select). | |||
*/ | |||
class RowScanner_SPIShiftRegisters : public RowScannerInterface | |||
@@ -17,9 +18,9 @@ class RowScanner_SPIShiftRegisters : public RowScannerInterface | |||
const uint8_t BYTE_COUNT; //number of shift registers | |||
const uint8_t KEY_COUNT; //number of keys in row | |||
public: | |||
RowScanner_SPIShiftRegisters(const uint8_t SS, uint8_t BYTE_COUNT, uint16_t KEY_COUNT) | |||
RowScanner_SPIShiftRegisters(const uint8_t SS, uint8_t BYTE_COUNT, uint8_t KEY_COUNT) | |||
: SS(SS), BYTE_COUNT(BYTE_COUNT), KEY_COUNT(KEY_COUNT) {} | |||
virtual uint8_t scan(uint16_t& rowEnd); | |||
virtual read_pins_t scan(read_pins_mask_t& rowEnd); | |||
void begin(); | |||
}; | |||
#endif |
@@ -1,11 +1,11 @@ | |||
#include "Row_IOE.h" | |||
uint8_t Row_IOE::scan(uint16_t& rowEnd) | |||
read_pins_t Row_IOE::scan(read_pins_mask_t& rowEnd) | |||
{ | |||
return scanner.scan(rowEnd); | |||
} | |||
uint8_t Row_IOE::debounce(const uint8_t rowState, uint8_t& debounced) | |||
read_pins_t Row_IOE::debounce(const read_pins_t rowState, read_pins_t& debounced) | |||
{ | |||
return debouncer.debounce(rowState, debounced); | |||
} |
@@ -40,7 +40,7 @@ class Row_IOE : public RowBase | |||
Row_IOE( RowPort& refRowPort, const uint8_t strobePin, | |||
ColPort& refColPort, Key *const ptrsKeys[]) | |||
: RowBase(ptrsKeys), scanner(refRowPort, strobePin, refColPort) { } | |||
uint8_t scan(uint16_t& rowEnd); | |||
uint8_t debounce(const uint8_t rowState, uint8_t& debounced); | |||
read_pins_t scan(read_pins_mask_t& rowEnd); | |||
read_pins_t debounce(const read_pins_t rowState, read_pins_t& debounced); | |||
}; | |||
#endif |
@@ -5,12 +5,12 @@ void Row_ShiftRegisters::begin() | |||
scanner.begin(); | |||
} | |||
uint8_t Row_ShiftRegisters::scan(uint16_t& rowEnd) | |||
read_pins_t Row_ShiftRegisters::scan(read_pins_mask_t& rowEnd) | |||
{ | |||
return scanner.scan(rowEnd); | |||
} | |||
uint8_t Row_ShiftRegisters::debounce(const uint8_t rowState, uint8_t& debounced) | |||
read_pins_t Row_ShiftRegisters::debounce(const read_pins_t rowState, read_pins_t& debounced) | |||
{ | |||
return debouncer.debounce(rowState, debounced); | |||
} |
@@ -29,10 +29,10 @@ class Row_ShiftRegisters : public RowBase | |||
RowScanner_SPIShiftRegisters scanner; | |||
Debouncer_4Samples debouncer; | |||
public: | |||
Row_ShiftRegisters(const uint8_t SS, uint8_t BYTE_COUNT, Key *const ptrsKeys[], uint16_t KEY_COUNT) | |||
Row_ShiftRegisters(const uint8_t SS, uint8_t BYTE_COUNT, Key *const ptrsKeys[], uint8_t KEY_COUNT) | |||
: RowBase(ptrsKeys), scanner(SS, BYTE_COUNT, KEY_COUNT) { } | |||
void begin(); | |||
uint8_t scan(uint16_t& rowEnd); | |||
uint8_t debounce(const uint8_t rowState, uint8_t& debounced); | |||
read_pins_t scan(read_pins_mask_t& rowEnd); | |||
read_pins_t debounce(const read_pins_t rowState, read_pins_t& debounced); | |||
}; | |||
#endif |
@@ -1,11 +1,11 @@ | |||
#include "Row_uC.h" | |||
uint8_t Row_uC::scan(uint16_t& rowEnd) | |||
read_pins_t Row_uC::scan(read_pins_mask_t& rowEnd) | |||
{ | |||
return scanner.scan(rowEnd); | |||
} | |||
uint8_t Row_uC::debounce(const uint8_t rowState, uint8_t& debounced) | |||
read_pins_t Row_uC::debounce(const read_pins_t rowState, read_pins_t& debounced) | |||
{ | |||
return debouncer.debounce(rowState, debounced); | |||
} |
@@ -35,7 +35,7 @@ class Row_uC : public RowBase | |||
Row_uC(const uint8_t strobePin, const uint8_t readPins[], const uint8_t READ_PIN_COUNT, | |||
Key *const ptrsKeys[]) | |||
: RowBase(ptrsKeys), scanner(strobePin, readPins, READ_PIN_COUNT) { } | |||
uint8_t scan(uint16_t& rowEnd); | |||
uint8_t debounce(const uint8_t rowState, uint8_t& debounced); | |||
read_pins_t scan(read_pins_mask_t& rowEnd); | |||
read_pins_t debounce(const read_pins_t rowState, read_pins_t& debounced); | |||
}; | |||
#endif |
@@ -0,0 +1,35 @@ | |||
#ifndef CONFIG_KEYBRD_H | |||
#define CONFIG_KEYBRD_H | |||
#include <inttypes.h> | |||
/* size of read_pins_t and read_pins_mask_t depends on the maximum number of pins scanned by RowScanner. | |||
By default, read_pins_t and read_pins_mask_t are set to the largest type. | |||
If your 8-bit AVR is running low on memory, using a smaller type saves SRAM. | |||
Using smaller types on a 32-bit uC (Teensy LC) would accomplish nothing. | |||
*/ | |||
/* Uncomment a typedef read_pins_t that covers all col pins of the RowScanner object with the most col pins i.e. | |||
For RowScanner_PinsArray, RowScanner_PinsArray::READ_PIN_COUNT | |||
For RowScanner_SPIShiftRegisters, RowScanner_SPIShiftRegisters::KEY_COUNT | |||
For RowScanner_PinsBitwise, cover the last 1 bit in RowScanner_PinsBitwise::strobePin | |||
*/ | |||
typedef uint8_t read_pins_t; | |||
//typedef uint16_t read_pins_t; | |||
//typedef uint32_t read_pins_t; | |||
/* read_pins_mask_t is only used for rowEnd, which extends one bit beyond the last col pin. | |||
uncomment typedef that covers one bit beyond the last col pin. | |||
This could be the same typedef as read_pins_t, or the next larger typedef. | |||
*/ | |||
typedef uint8_t read_pins_mask_t; | |||
//typedef uint16_t read_pins_mask_t; | |||
//typedef uint32_t read_pins_mask_t; | |||
/* SAMPLE_COUNT = 4 is very reliable for a keyboard. | |||
Split keyboards with a long connecting wire or in environment with | |||
strong electromagnetic interference (EMI) may need a larger SAMPLE_COUNT for reliability. | |||
SAMPLE_COUNT is used in Debouncer_Samples.h | |||
*/ | |||
#define SAMPLE_COUNT 4 //number of consecutive equal bits needed to change a debounced bit | |||
#endif |
@@ -7,8 +7,8 @@ All the tutorial example sketches run on breadboard keyboards that have 2 to 8 k | |||
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. | |||
You won't get everything right the first time. | |||
There is a learning curve. | |||
Compared to PCBs, breadboard keyboards are easier to learn on because: | |||
* Mistakes are easily corrected; no soldering and desoldering |