diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ca513f..c1dfd68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [0.4.1] - 2024-07-14 -- update readme.md +- Fix #21, add section to readme.md +- Fix #22, implement **debounceThreshold** +- add constant **I2C_KEYPAD_THRESHOLD** +- update unit test +- minor edits ## [0.4.0] - 2023-11-09 diff --git a/I2CKeyPad .cpp b/I2CKeyPad .cpp index 81b6b28..934c8f3 100644 --- a/I2CKeyPad .cpp +++ b/I2CKeyPad .cpp @@ -15,6 +15,8 @@ I2CKeyPad::I2CKeyPad(const uint8_t deviceAddress, TwoWire *wire) _address = deviceAddress; _wire = wire; _mode = I2C_KEYPAD_4x4; + _debounceThreshold = 0; + _lastRead = 0; } @@ -28,6 +30,16 @@ bool I2CKeyPad::begin() uint8_t I2CKeyPad::getKey() { + if (_debounceThreshold > 0) + { + uint32_t now = micros(); + if (now - _debounceThreshold < _lastRead) + { + return I2C_KEYPAD_THRESHOLD; + } + _lastRead = now; + } + if (_mode == I2C_KEYPAD_5x3) return _getKey5x3(); if (_mode == I2C_KEYPAD_6x2) return _getKey6x2(); if (_mode == I2C_KEYPAD_8x1) return _getKey8x1(); @@ -39,7 +51,7 @@ uint8_t I2CKeyPad::getKey() uint8_t I2CKeyPad::getLastKey() { return _lastKey; -}; +} // to check "press any key" @@ -61,13 +73,13 @@ bool I2CKeyPad::isConnected() uint8_t I2CKeyPad::getChar() { return _keyMap[getKey()]; -}; +} uint8_t I2CKeyPad::getLastChar() { return _keyMap[_lastKey]; -}; +} void I2CKeyPad::loadKeyMap(char * keyMap) @@ -95,6 +107,18 @@ uint8_t I2CKeyPad::getKeyPadMode() } +void I2CKeyPad::setDebounceThreshold(uint16_t value) +{ + _debounceThreshold = value; +} + + +uint16_t I2CKeyPad::getDebounceThreshold() +{ + return _debounceThreshold; +} + + ////////////////////////////////////////////////////// // // PROTECTED @@ -118,7 +142,7 @@ uint8_t I2CKeyPad::_read(uint8_t mask) uint8_t I2CKeyPad::_getKey4x4() { - // key = row + 4 x col + // key = row + 4 x column uint8_t key = 0; // mask = 4 rows as input pull up, 4 columns as output @@ -150,7 +174,7 @@ uint8_t I2CKeyPad::_getKey4x4() // not tested uint8_t I2CKeyPad::_getKey5x3() { - // key = row + 5 x col + // key = row + 5 x column uint8_t key = 0; // mask = 5 rows as input pull up, 3 columns as output @@ -182,7 +206,7 @@ uint8_t I2CKeyPad::_getKey5x3() // not tested uint8_t I2CKeyPad::_getKey6x2() { - // key = row + 6 x col + // key = row + 6 x column uint8_t key = 0; // mask = 6 rows as input pull up, 2 columns as output diff --git a/I2CKeyPad.h b/I2CKeyPad.h index 1b22dbc..9da3aff 100644 --- a/I2CKeyPad.h +++ b/I2CKeyPad.h @@ -15,6 +15,9 @@ #define I2C_KEYPAD_NOKEY 16 #define I2C_KEYPAD_FAIL 17 +// +#define I2C_KEYPAD_THRESHOLD 255 + // experimental #define I2C_KEYPAD_4x4 44 @@ -29,37 +32,42 @@ class I2CKeyPad I2CKeyPad(const uint8_t deviceAddress, TwoWire *wire = &Wire); // call Wire.begin() first! - bool begin(); + bool begin(); // get raw key's 0..15 - uint8_t getKey(); - uint8_t getLastKey(); + uint8_t getKey(); + uint8_t getLastKey(); - bool isPressed(); - bool isConnected(); + bool isPressed(); + bool isConnected(); // get 'translated' keys // user must load KeyMap, there is no check. - uint8_t getChar(); - uint8_t getLastChar(); - void loadKeyMap(char * keyMap); // char[19] + uint8_t getChar(); + uint8_t getLastChar(); + void loadKeyMap(char * keyMap); // char[19] // mode functions - experimental - void setKeyPadMode(uint8_t mode = I2C_KEYPAD_4x4); - uint8_t getKeyPadMode(); + void setKeyPadMode(uint8_t mode = I2C_KEYPAD_4x4); + uint8_t getKeyPadMode(); + + // value in microseconds, max 65535 us + void setDebounceThreshold(uint16_t value = 0); + uint16_t getDebounceThreshold(); protected: - uint8_t _address; - uint8_t _lastKey; - uint8_t _mode; - uint8_t _read(uint8_t mask); - uint8_t _getKey4x4(); - - // experimental - could be public ?! - uint8_t _getKey5x3(); - uint8_t _getKey6x2(); - uint8_t _getKey8x1(); + uint8_t _address; + uint8_t _lastKey; + uint8_t _mode; + uint8_t _read(uint8_t mask); + uint16_t _debounceThreshold; + uint32_t _lastRead; + + uint8_t _getKey4x4(); + uint8_t _getKey5x3(); + uint8_t _getKey6x2(); + uint8_t _getKey8x1(); TwoWire* _wire; diff --git a/README.md b/README.md index 9c0a245..21a71a3 100644 --- a/README.md +++ b/README.md @@ -38,18 +38,18 @@ See the conceptual schema below. It might take some trying to get the correct pins connected. ``` - PROC PCF8574 KEYPAD - +--------+ +--------+ +--------+ - | | | 0|----------|R | - | SDA |--------| 1|----------|O | - | SCL |--------| 2|----------|W | - | | | 3|----------|S | - | | | | | | - | | | 4|----------|C | - | | | 5|----------|O | - | | | 6|----------|L | - | | | 7|----------|S | - +--------+ +--------+ +--------+ + PROC PCF8574 KEYPAD + +--------+ +---------+ +---------+ + | | | 0 |<-------->| R | + | SDA |<------>| 1 |<-------->| O | + | SCL |------->| 2 |<-------->| W | + | | | 3 |<-------->| S | + | | | | | | + | | | 4 |<-------->| C | + | | | 5 |<-------->| O | + | | | 6 |<-------->| L | + | | | 7 |<-------->| S | + +--------+ +---------+ +---------+ ``` @@ -65,6 +65,8 @@ These devices are identical in behaviour although there are two distinct address | PCF8574 | 0x20 to 0x27 | same range as PCF8575 ! | | PCF8574A | 0x38 to 0x3F | +Be careful to select an unique I2C address for every device on the bus. + ### I2C multiplexing @@ -156,7 +158,24 @@ It is even possible to change the mapping runtime after each key. Note: a keyMap char array may be longer than 18 characters, but only the first 18 are used. The length is **NOT** checked upon loading. -Note: The 5x3, 6x2 and the 8x1 modi also uses a keymap of length 18. +Note: The 5x3, 6x2 and the 8x1 modi also uses a key map of length 18. + + +### Debouncing threshold + +**Experimental** since version 0.4.1, the library implements a debounce threshold. +If a key bounces, it can trigger multiple interrupts, while the purpose is to +act like only one keypress. The debounce threshold prevents reading a key too fast. The default value of the threshold is zero to be backwards compatible. +The value is set in microseconds, with a maximum of 65535 ~65 milliseconds, +which is about 16 keys per second. + + // value in microseconds, max 65535 us +- **void setDebounceThreshold(uint16_t value = 0)** set the threshold, +default to zero to reset its value. +- **uint16_t getDebounceThreshold()** return the set threshold. + +If a debounce threshold is set, and **getKey()** is called too fast, +it will return **I2C_KEYPAD_THRESHOLD**. ### Basic working @@ -164,6 +183,8 @@ Note: The 5x3, 6x2 and the 8x1 modi also uses a keymap of length 18. After the **keypad.begin()** the sketch calls the **keyPad.getKey()** to read values from the keypad. - If no key is pressed **I2CKEYPAD_NOKEY** code (16) is returned. - If the read value is not valid, e.g. two keys pressed, **I2CKEYPAD_FAIL** code (17) is returned. +- If a debounce threshold is set, **I2C_KEYPAD_THRESHOLD** might be returned. +See section above. - Otherwise a number 0..15 is returned. Note NOKEY and FAIL bot have bit 4 set, all valid keys don't. @@ -180,11 +201,6 @@ This makes checking the keypad far more efficient as one does not need to poll o See examples. -## Operation - -See examples - - ## Future #### Must diff --git a/test/unit_test_001.cpp b/test/unit_test_001.cpp index 239d791..b81d111 100644 --- a/test/unit_test_001.cpp +++ b/test/unit_test_001.cpp @@ -43,6 +43,7 @@ unittest(test_constants) { assertEqual(16, I2C_KEYPAD_NOKEY); assertEqual(17, I2C_KEYPAD_FAIL); + assertEqual(255, I2C_KEYPAD_THRESHOLD); assertEqual(44, I2C_KEYPAD_4x4); assertEqual(53, I2C_KEYPAD_5x3); @@ -100,6 +101,22 @@ unittest(test_KeyMap) } +unittest(test_debounce_threshold) +{ + const uint8_t KEYPAD_ADDRESS = 0x38; + I2CKeyPad keyPad(KEYPAD_ADDRESS); + + // default 0 + assertEqual(0, keyPad.getDebounceThreshold()); + + for (uint16_t th = 5000; th < 60000; th += 5000) + { + keyPad.setDebounceThreshold(th); + assertEqual(th, keyPad.getDebounceThreshold()); + } +} + + // Issues with Wire - to be investigated... // // unittest(test_read)