Archived
1
0

add config_keybrd.h with typedef read_pins_t, read_pins_mask_t

This commit is contained in:
wolfv6 2016-06-21 20:40:35 -06:00
parent 04ab6ebe72
commit 48a3a41db1
22 changed files with 108 additions and 60 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

35
src/config_keybrd.h Normal file
View File

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

View File

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