Larger SAMPLE_COUNTs are more reliable but consume more memory, where | Larger SAMPLE_COUNTs are more reliable but consume more memory, where | ||||
SAMPLE_COUNT*ROW_COUNT = bytes of memory consumed by keyboard | SAMPLE_COUNT*ROW_COUNT = bytes of memory consumed by keyboard | ||||
SAMPLE_COUNT = 4 is very reliable for a 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 | /* debounce() function | ||||
Parameter rowState is bitwise, 1 means pressed, 0 means released. | Parameter rowState is bitwise, 1 means pressed, 0 means released. |
class Row : public RowBase | class Row : public RowBase | ||||
{ | { | ||||
private: | 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 samples[SAMPLE_COUNT]; //bitwise, one bit per key, most recent readings | ||||
uint8_t samplesIndex; //samples[] current write index | uint8_t samplesIndex; //samples[] current write index | ||||
virtual uint8_t debounce(const uint8_t rowState); | virtual uint8_t debounce(const uint8_t rowState); |
uint16_t rowEnd; //1 bit marks positioned after last key of row | uint16_t rowEnd; //1 bit marks positioned after last key of row | ||||
uint8_t debouncedChanged; //1 means debounced changed | uint8_t debouncedChanged; //1 means debounced changed | ||||
wait(); | |||||
scan(activeHigh); //save column-port-pin values to portState | scan(activeHigh); //save column-port-pin values to portState | ||||
rowState = getRowState(rowEnd, activeHigh); | rowState = getRowState(rowEnd, activeHigh); | ||||
debouncedChanged = debounce(rowState); | debouncedChanged = debounce(rowState); | ||||
pressRelease(rowEnd, debouncedChanged); | pressRelease(rowEnd, debouncedChanged); | ||||
} | } | ||||
/* wait() delay's scan to give switches time to debounce. | |||||
This version of wait() is very simple. More sophisticated versions can override this one. | |||||
For fastest response time, wait() should be placed before scan() or after pressRelease() | |||||
(waiting between strobe and send would unnecessarily delay send). | |||||
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 | |||||
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. | |||||
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. | |||||
*/ | |||||
void RowBase::wait() | void RowBase::wait() | ||||
{ | { | ||||
delayMicroseconds(DELAY_MICROSECONDS); //delay between Row scans to debounce switches | delayMicroseconds(DELAY_MICROSECONDS); //delay between Row scans to debounce switches |
class RowBase | class RowBase | ||||
{ | { | ||||
private: | private: | ||||
static const unsigned int DELAY_MICROSECONDS; //delay between each Row scan for debouncing | |||||
Key *const *const ptrsKeys; //array of Key pointers | Key *const *const ptrsKeys; //array of Key pointers | ||||
RowPort &refRowPort; //this row's IC port | RowPort &refRowPort; //this row's IC port | ||||
ColPort *const *const ptrsColPorts; //array of column ports | ColPort *const *const ptrsColPorts; //array of column ports | ||||
const uint8_t colPortCount; | const uint8_t colPortCount; | ||||
void wait(); | |||||
void scan(const bool activeHigh); | void scan(const bool activeHigh); | ||||
uint8_t getRowState(uint16_t& rowEnd, const bool activeHigh); | uint8_t getRowState(uint16_t& rowEnd, const bool activeHigh); | ||||
virtual uint8_t debounce(const uint8_t rowState)=0; | virtual uint8_t debounce(const uint8_t rowState)=0; |