add RowBase class, move debounce() to Row, add simple Row_DH::debounce()
This commit is contained in:
parent
90c420491e
commit
b6b6e9fee9
@ -177,7 +177,6 @@ Refer to it like a table of contents while reading the keybrd library.
|
||||
if key is pressed
|
||||
set rowState bit
|
||||
Row::debounce() debounce
|
||||
Row::detectEdge() detect edge
|
||||
Row::pressRelease() for each key in row
|
||||
if rising edge
|
||||
Key_*::press() scanCode->press()
|
||||
|
@ -92,7 +92,7 @@ Key* const ptrsKeys_L1[] = { ptrsLayout[1][0], ptrsLayout[1][1] };
|
||||
Row row_L1(rowPortF_L, 1<<1, ptrsColPorts_L, COL_PORT_L_COUNT, ptrsKeys_L1);
|
||||
|
||||
// -------------- LEFT MATRIX ------------------
|
||||
Row* const ptrsRows_L[] = { &row_L0, &row_L1 };
|
||||
RowBase* const ptrsRows_L[] = { &row_L0, &row_L1 };
|
||||
const uint8_t ROW_L_COUNT = sizeof(ptrsRows_L)/sizeof(*ptrsRows_L);
|
||||
|
||||
Matrix matrix_L(ptrsRows_L, ROW_L_COUNT, 1);
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <inttypes.h>
|
||||
#include "Code.h"
|
||||
|
||||
/* Class Code_LayeredCodeScBase is a 2-layer code, one object for each layer e.g.
|
||||
/* Class Code_LayeredCodeScBase is a 2-layer code, with one object for each layer e.g.
|
||||
layer0: ms_up //mouse up
|
||||
layer1: KEY_UP //up arrow
|
||||
When the key is pressed, the active layer is retrieved from refLayerState,
|
||||
|
94
src/Row.cpp
Normal file
94
src/Row.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/* 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.
|
||||
|
||||
Avoid sampling the switch input at a rate synchronous to events in the environment
|
||||
that might create periodic EMI. For instance, 50 and 60 Hz.
|
||||
|
||||
A keyboard with a faster scan rate responds faster.
|
||||
Follow these step to tune DELAY_MICROSECONDS for maximum scan rate for a given SAMPLE_COUNT:
|
||||
Initialize DELAY_MICROSECONDS in your sketch:
|
||||
const unsigned int Row::DELAY_MICROSECONDS = 1000;
|
||||
Add this to the sketch's loop() function:
|
||||
debug.print_microseconds_per_scan();
|
||||
Compile and load the sketch into the microcontroller; microseconds_per_scan is printed every second.
|
||||
Adjust the value of DELAY_MICROSECONDS and repeat until:
|
||||
debug.print_microseconds_per_scan() <= DEBOUNCE_TIME / SAMPLE_COUNT
|
||||
|
||||
DEBOUNCE_TIME can be obtained from the switch's datasheet. Some switch bounce times are:
|
||||
Cherry MX specifies 5msec bounce time http://www.cherrycorp.com/english/switches/key/mx.htm
|
||||
hasu measured Cherry MX bounce times .3ms to 1.4ms http://geekhack.org/index.php?topic=42385.0
|
||||
Tactile switch MJTP series bounce 10 ms http://www.apem.com/files/apem/brochures/MJTP_6MM.pdf
|
||||
|
||||
Polling I2C may slow the scan rate enough so that no additional delay is needed:
|
||||
const unsigned int Row::DELAY_MICROSECONDS = 0;
|
||||
|
||||
Slow-scan trick for debug messages that print too fast:
|
||||
change DELAY_MICROSECONDS to a large number like 10000
|
||||
That way debug messages are printed at a managable rate.
|
||||
*/
|
||||
/* debounce() function
|
||||
Parameter rowState is bitwise, 1 means pressed, 0 means released.
|
||||
Returns bitwise debouncedChanged.
|
||||
*/
|
||||
#include "Row.h"
|
||||
|
||||
uint8_t Row::debounce(const uint8_t rowState)
|
||||
{
|
||||
uint8_t debounced; //bitwise, 1 means pressed, 0 means released
|
||||
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
|
||||
}
|
||||
|
||||
// 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;
|
||||
previousDebounced = debounced;
|
||||
return debouncedChanged;
|
||||
}
|
45
src/Row.h
Normal file
45
src/Row.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef ROW_H
|
||||
#define ROW_H
|
||||
|
||||
#include <RowBase.h>
|
||||
|
||||
#define SAMPLE_COUNT 4 //number of consecutive equal bits needed to change a debounced bit
|
||||
|
||||
/*
|
||||
Configuration
|
||||
-------------
|
||||
#define SAMPLE_COUNT in this header file.
|
||||
define and initilize DELAY_MICROSECONDS in sketch.
|
||||
const unsigned int Row::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
|
||||
*/
|
||||
class Row : public RowBase
|
||||
{
|
||||
private:
|
||||
static const unsigned int DELAY_MICROSECONDS; //delay between each Row scan for debouncing
|
||||
uint8_t samples[SAMPLE_COUNT]; //bitwise, one bit per key, most recent readings
|
||||
uint8_t samplesIndex; //samples[] current write index
|
||||
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) { }
|
||||
};
|
||||
#endif
|
@ -15,6 +15,11 @@ void RowBase::process(const bool activeHigh)
|
||||
pressRelease(rowEnd, debouncedChanged);
|
||||
}
|
||||
|
||||
void RowBase::wait()
|
||||
{
|
||||
delayMicroseconds(DELAY_MICROSECONDS); //delay between Row scans to debounce switches
|
||||
}
|
||||
|
||||
/*
|
||||
Strobes the row and reads the columns.
|
||||
Strobe is on for shortest possible time to preserve IR LED on DodoHand's optic switch.
|
||||
|
@ -6,43 +6,7 @@
|
||||
#include <RowPort.h>
|
||||
#include <ColPort.h>
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
A keyboard with a faster scan rate is more resposive.
|
||||
Follow these step to tune DELAY_MICROSECONDS for maximum scan rate within debounce times:
|
||||
Initialize DELAY_MICROSECONDS in your sketch:
|
||||
const unsigned int Row::DELAY_MICROSECONDS = 1000;
|
||||
Add this to the sketche's loop() function:
|
||||
keybrd.print_microseconds_per_scan();
|
||||
Compile and load the sketch into the microcontroller, which will print the actual microseconds_per_scan
|
||||
Incrementaly adjust the DELAY_MICROSECONDS untill the printed microseconds_per_scan is near the switches bounce time
|
||||
A switche's debounce time can be obtained from the switche's datasheet
|
||||
Cherry MX has 5ms bounce time http://www.cherrycorp.com/english/switches/key/mx.htm
|
||||
hasu measured Cherry MX bounce times .3ms to 1.4ms http://geekhack.org/index.php?topic=42385.0
|
||||
Tactile switch MJTP series bounce 10 ms http://www.apem.com/files/apem/brochures/MJTP_6MM.pdf
|
||||
Optic switches 0 bounce time because optic doesn't bounce
|
||||
|
||||
Slow-scan trick for debug message that print too fast
|
||||
Keyboard.print(F("debug message"));
|
||||
Change DELAY_MICROSECONDS to a large number like 10000
|
||||
That way printing debug messages is slowed to a managable rate
|
||||
/* RowBase is an abstract base class.
|
||||
*/
|
||||
class RowBase
|
||||
{
|
||||
@ -58,7 +22,6 @@ class RowBase
|
||||
void scan(const bool activeHigh);
|
||||
uint8_t getRowState(uint16_t& rowEnd, const bool activeHigh);
|
||||
virtual uint8_t debounce(const uint8_t rowState)=0;
|
||||
//void detectEdge(uint8_t debounced, uint8_t& isFallingEdge, uint8_t& isRisingEdge);
|
||||
void pressRelease(const uint16_t rowEnd, const uint8_t debouncedChanged);
|
||||
virtual void keyWasPressed();
|
||||
protected:
|
||||
|
@ -43,4 +43,5 @@ void RowPort_PCA9655E::setActivePinHigh(const uint8_t activePin)
|
||||
Wire.write(outputByteCommand);
|
||||
Wire.write(port.outputVal |= activePin);
|
||||
Wire.endTransmission();
|
||||
//todo delayMicroseconds(1500);
|
||||
}
|
||||
|
Reference in New Issue
Block a user