@@ -0,0 +1,12 @@ | |||
#ifndef DEBOUNCERINTERFACE_H | |||
#define DEBOUNCERINTERFACE_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; | |||
}; | |||
#endif |
@@ -0,0 +1,66 @@ | |||
/* debounce() function | |||
Debounce uses multiple samples to debounces switch states, | |||
where each sample contains the switch states for a row of switches, one bit per switch. | |||
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). | |||
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 | |||
Dr. Marty's debounce algorithm: | |||
Periodically read samples and update the state when a number consecutive sample bits are equal. | |||
Output from keybrd/examples/debounce_unit_test.cpp with SAMPLE_COUNT 4: | |||
button pressed: 100000001111111110000 | |||
bouncy signal: 100001001111011110000 | |||
debounced signal: 000000000001111111110 | |||
isFallingEdge: 000000000000000000001 | |||
isRisingEdge: 000000000001000000000 | |||
There is a latency equal to SAMPLE_COUNT, between button press and debounced signal. | |||
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 = 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. | |||
*/ | |||
#include "Debouncer_4Samples.h" | |||
/* debounce() sets debounced and returns debouncedChanged. All variables are bitwise. | |||
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) | |||
{ | |||
uint8_t previousDebounced; //bitwise, 1 means pressed, 0 means released | |||
uint8_t all_1 = ~0; //bitwise | |||
uint8_t all_0 = 0; //bitwise | |||
samples[samplesIndex] = rawSignal; //insert rawSignal into samples[] ring buffer | |||
if (++samplesIndex >= SAMPLE_COUNT) //if end of ring buffer | |||
{ | |||
samplesIndex = 0; //wrap samplesIndex to beginning of ring buffer | |||
} | |||
for (uint8_t j = 0; j < SAMPLE_COUNT; j++) //traverse the sample[] ring buffer | |||
{ | |||
all_1 &= samples[j]; //1 if all samples are 1 | |||
all_0 |= samples[j]; //0 if all samples are 0 | |||
} | |||
previousDebounced = debounced; | |||
// update debounced if all the samples agree with one another | |||
// if all samples=1 then debounced=1 | |||
// elseif all samples=0 then debounced=0 | |||
// else debounced=previousDebounced i.e. no change | |||
debounced = all_1 | (all_0 & previousDebounced); | |||
return debounced xor previousDebounced; | |||
} |
@@ -0,0 +1,22 @@ | |||
#ifndef DEBOUNCER_4SAMPLES_H | |||
#define DEBOUNCER_4SAMPLES_H | |||
#include <Arduino.h> | |||
#include <inttypes.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. | |||
*/ | |||
class Debouncer_4Samples : public DebouncerInterface | |||
{ | |||
private: | |||
uint8_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); | |||
}; | |||
#endif | |||
@@ -1,70 +1,10 @@ | |||
/* debounce() function | |||
Debounce uses multiple samples to debounces switch states, | |||
where each sample contains the switch states for a row of switches, one bit per switch. | |||
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). | |||
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 | |||
Dr. Marty's debounce algorithm: | |||
Periodically read samples and update the state when a number consecutive sample bits are equal. | |||
Output from keybrd/examples/debounce_unit_test.cpp with SAMPLE_COUNT 4: | |||
button pressed: 100000001111111110000 | |||
bouncy signal: 100001001111011110000 | |||
debounced signal: 000000000001111111110 | |||
isFallingEdge: 000000000000000000001 | |||
isRisingEdge: 000000000001000000000 | |||
There is a latency equal to SAMPLE_COUNT, between button press and debounced signal. | |||
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. | |||
Keyboards with a long I2C wire or in environment with strong electromagnetic interference (EMI) | |||
need a larger SAMPLE_COUNT for reliability. | |||
Larger SAMPLE_COUNTs are more reliable but consume more memory, where | |||
SAMPLE_COUNT*ROW_COUNT = bytes of memory consumed by keyboard | |||
SAMPLE_COUNT = 4 is very reliable for a keyboard. | |||
*/ | |||
/* debounce() function | |||
Parameter rowState is bitwise, 1 means pressed, 0 means released. | |||
Returns bitwise debouncedChanged. | |||
/* debounce() sets debounced and returns debouncedChanged. All variables are bitwise. | |||
For parameter, 1 means pressed, 0 means released. | |||
For return, 1 means debounced changed. | |||
*/ | |||
#include "Row.h" | |||
uint8_t Row::debounce(const uint8_t rowState) | |||
{ | |||
uint8_t previousDebounced; //bitwise, one bit per key | |||
uint8_t debouncedChanged; //bitwise, 1 means debounced changed | |||
uint8_t all_1 = ~0; //bitwise | |||
uint8_t all_0 = 0; //bitwise | |||
samples[samplesIndex] = rowState; //insert rowState into samples[] ring buffer | |||
if (++samplesIndex >= SAMPLE_COUNT) //if end of ring buffer | |||
{ | |||
samplesIndex = 0; //wrap samplesIndex to beginning of ring buffer | |||
} | |||
for (uint8_t j = 0; j < SAMPLE_COUNT; j++) //traverse the sample[] ring buffer | |||
{ | |||
all_1 &= samples[j]; //1 if all samples are 1 | |||
all_0 |= samples[j]; //0 if all samples are 0 | |||
} | |||
previousDebounced = debounced; | |||
// update newDebounce if all the samples agree with one another | |||
// if all samples=1 then debounced=1 | |||
// elseif all samples=0 then debounced=0 | |||
// else debounced=previousDebounced i.e. no change | |||
debounced = all_1 | (all_0 & previousDebounced); | |||
debouncedChanged = debounced xor previousDebounced; | |||
return debouncedChanged; | |||
return debouncer.debounce(rowState, debounced); | |||
} |
@@ -2,43 +2,29 @@ | |||
#define ROW_H | |||
#include <RowBase.h> | |||
#define SAMPLE_COUNT 4 //number of consecutive equal bits needed to change a debounced bit | |||
#include <Debouncer_4Samples.h> | |||
/* | |||
Configuration | |||
------------- | |||
#define SAMPLE_COUNT in this header file. | |||
define and initilize DELAY_MICROSECONDS in sketch. | |||
const unsigned int Row::DELAY_MICROSECONDS = 0; | |||
const unsigned int RowBase::DELAY_MICROSECONDS = 0; | |||
Instantiation | |||
------------ | |||
Example instantiation of a row: | |||
RowPort_AVR rowPortF(DDRF, PORTF); | |||
ColPort_AVR colPortB(DDRB, PORTB, PINB, 1<<0 | 1<<1 | 1<<2 | 1<<3 ); | |||
ColPort_AVR colPortD(DDRD, PORTD, PIND, 1<<2 | 1<<3 ); | |||
ColPort* const ptrsColPorts[] = { &colPortB, &colPortD }; | |||
const uint8_t COL_PORTS_COUNT = sizeof(ptrsColPorts)/sizeof(*ptrsColPorts); | |||
const PROGMEM Key* const ptrsKeys_0[] = { &k_00, &k_01, &k_02, &k_03, &k_04, &k_05 }; | |||
Row row_0(ptrsKeys_0, &rowPortF, 1<<0, ptrsColPorts, COL_PORTS_COUNT); | |||
Number of ColPort::colPins should equal number of keys in Row::ptrsKeys array | |||
if a pin is missing, a key will be unresposive | |||
if a Key pointer is missing, the keyboard will fail in an unprdictable way | |||
------------- | |||
todo - see Row_DH | |||
*/ | |||
class Row : public RowBase | |||
{ | |||
private: | |||
uint8_t samples[SAMPLE_COUNT]; //bitwise, one bit per key, most recent readings | |||
uint8_t samplesIndex; //samples[] current write index | |||
Debouncer_4Samples debouncer; | |||
virtual uint8_t debounce(const uint8_t rowState); | |||
public: | |||
Row( RowPort &refRowPort, const uint8_t rowPin, | |||
ColPort *const ptrsColPorts[], const uint8_t colPortCount, Key *const ptrsKeys[]) | |||
: RowBase(refRowPort, rowPin, ptrsColPorts, colPortCount, ptrsKeys), samplesIndex(0) { } | |||
: RowBase(refRowPort, rowPin, ptrsColPorts, colPortCount, ptrsKeys) | |||
{ | |||
Debouncer_4Samples debouncer; | |||
} | |||
}; | |||
#endif |