diff --git a/src/DebouncerInterface.h b/src/DebouncerInterface.h new file mode 100644 index 0000000..aa7e251 --- /dev/null +++ b/src/DebouncerInterface.h @@ -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 diff --git a/src/Debouncer_4Samples.cpp b/src/Debouncer_4Samples.cpp new file mode 100644 index 0000000..a22a4f0 --- /dev/null +++ b/src/Debouncer_4Samples.cpp @@ -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; +} diff --git a/src/Debouncer_4Samples.h b/src/Debouncer_4Samples.h new file mode 100644 index 0000000..9955582 --- /dev/null +++ b/src/Debouncer_4Samples.h @@ -0,0 +1,22 @@ +#ifndef DEBOUNCER_4SAMPLES_H +#define DEBOUNCER_4SAMPLES_H +#include +#include +#include + +#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 + diff --git a/src/Row.cpp b/src/Row.cpp index a348859..8928abd 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -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); } diff --git a/src/Row.h b/src/Row.h index 5bfaefa..6dc2577 100644 --- a/src/Row.h +++ b/src/Row.h @@ -2,43 +2,29 @@ #define ROW_H #include - -#define SAMPLE_COUNT 4 //number of consecutive equal bits needed to change a debounced bit +#include /* 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