Archived
1
0

add RowBase class, move debounce() to Row, add simple Row_DH::debounce()

This commit is contained in:
wolfv6 2016-06-02 09:58:33 -06:00
parent 90c420491e
commit b6b6e9fee9
8 changed files with 148 additions and 41 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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