Skip to content

Commit

Permalink
Sync I2CKeyPad 0.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
RobTillaart committed Jul 15, 2024
1 parent 3435e44 commit 75fcf18
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 76 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).


## [0.3.0] - 2024-07-15
- sync with I2CKeyPad 0.5.0
- implement **debounceThreshold**
- add constant **I2C_KEYPAD8x8_THRESHOLD**
- update **getChar()** to support **I2C_KEYPAD8x8_THRESHOLD**
- update readme.md
- update unit test
- update keywords.txt
- minor edits

----

## [0.2.0] - 2023-11-09
- simplify begin()
- update readme.md
- minor edits.

----

## [0.1.1] - 2022-11-12
- Add RP2040 support to build-CI.
Expand Down
113 changes: 74 additions & 39 deletions I2CKeyPad8x8.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// FILE: I2CKeyPad8x8.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// VERSION: 0.3.0
// PURPOSE: Arduino library for 8x8 or smaller KeyPad connected to an I2C PCF8575.
// URL: https://github.com/RobTillaart/I2CKeyPad8x8

Expand All @@ -14,6 +14,8 @@ I2CKeyPad8x8::I2CKeyPad8x8(const uint8_t deviceAddress, TwoWire *wire)
_lastKey = I2C_KEYPAD8x8_NOKEY;
_address = deviceAddress;
_wire = wire;
_debounceThreshold = 0;
_lastRead = 0;
}


Expand All @@ -34,48 +36,24 @@ bool I2CKeyPad8x8::isConnected()

uint8_t I2CKeyPad8x8::getKey()
{
// key = row + 8 x col
uint8_t key = 0;

// mask = 8 rows as input pull up, 8 columns as output
uint16_t rows = _read(0xFF00);

// check if single line has gone low.
if (rows == 0xFF00) return I2C_KEYPAD8x8_NOKEY;
else if (rows == 0xFE00) key = 0;
else if (rows == 0xFD00) key = 1;
else if (rows == 0xFB00) key = 2;
else if (rows == 0xF700) key = 3;
else if (rows == 0xEF00) key = 4;
else if (rows == 0xDF00) key = 5;
else if (rows == 0xBF00) key = 6;
else if (rows == 0x7F00) key = 7;
else return I2C_KEYPAD8x8_FAIL;

// 8 columns as input pull up, 8 rows as output
uint16_t cols = _read(0x00FF);
// check if single line has gone low.
if (cols == 0x00FF) return I2C_KEYPAD8x8_NOKEY;
else if (cols == 0x00FE) key += 0;
else if (cols == 0x00FD) key += 8;
else if (cols == 0x00FB) key += 16;
else if (cols == 0x00F7) key += 24;
else if (cols == 0x00EF) key += 32;
else if (cols == 0x00DF) key += 40;
else if (cols == 0x00BF) key += 48;
else if (cols == 0x007F) key += 56;
else return I2C_KEYPAD8x8_FAIL;

_lastKey = key;
if (_debounceThreshold > 0)
{
uint32_t now = micros();
if (now - _debounceThreshold < _lastRead)
{
return I2C_KEYPAD8x8_THRESHOLD;
}
_lastRead = now;
}

return key; // 0..65
return _getKey8x8();
}


uint8_t I2CKeyPad8x8::getLastKey()
{
return _lastKey;
};
}


// to check "press any key"
Expand All @@ -89,14 +67,19 @@ bool I2CKeyPad8x8::isPressed()

uint8_t I2CKeyPad8x8::getChar()
{
return _keyMap[getKey()];
};
uint8_t key = getKey();
if (key != I2C_KEYPAD8x8_THRESHOLD)
{
return _keyMap[key];
}
return I2C_KEYPAD8x8_THRESHOLD;
}


uint8_t I2CKeyPad8x8::getLastChar()
{
return _keyMap[_lastKey];
};
}


void I2CKeyPad8x8::loadKeyMap(char * keyMap)
Expand All @@ -105,6 +88,18 @@ void I2CKeyPad8x8::loadKeyMap(char * keyMap)
}


void I2CKeyPad8x8::setDebounceThreshold(uint16_t value)
{
_debounceThreshold = value;
}


uint16_t I2CKeyPad8x8::getDebounceThreshold()
{
return _debounceThreshold;
}


//////////////////////////////////////////////////////
//
// PROTECTED
Expand All @@ -129,5 +124,45 @@ uint16_t I2CKeyPad8x8::_read(uint16_t mask)
}


uint8_t I2CKeyPad8x8::_getKey8x8()
{
// key = row + 8 x col
uint8_t key = 0;

// mask = 8 rows as input pull up, 8 columns as output
uint16_t rows = _read(0xFF00);

// check if single line has gone low.
if (rows == 0xFF00) return I2C_KEYPAD8x8_NOKEY;
else if (rows == 0xFE00) key = 0;
else if (rows == 0xFD00) key = 1;
else if (rows == 0xFB00) key = 2;
else if (rows == 0xF700) key = 3;
else if (rows == 0xEF00) key = 4;
else if (rows == 0xDF00) key = 5;
else if (rows == 0xBF00) key = 6;
else if (rows == 0x7F00) key = 7;
else return I2C_KEYPAD8x8_FAIL;

// 8 columns as input pull up, 8 rows as output
uint16_t cols = _read(0x00FF);
// check if single line has gone low.
if (cols == 0x00FF) return I2C_KEYPAD8x8_NOKEY;
else if (cols == 0x00FE) key += 0;
else if (cols == 0x00FD) key += 8;
else if (cols == 0x00FB) key += 16;
else if (cols == 0x00F7) key += 24;
else if (cols == 0x00EF) key += 32;
else if (cols == 0x00DF) key += 40;
else if (cols == 0x00BF) key += 48;
else if (cols == 0x007F) key += 56;
else return I2C_KEYPAD8x8_FAIL;

_lastKey = key;

return key; // 0..65
}


// -- END OF FILE --

14 changes: 11 additions & 3 deletions I2CKeyPad8x8.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// FILE: I2CKeyPad8x8.h
// AUTHOR: Rob Tillaart
// VERSION: 0.2.0
// VERSION: 0.3.0
// PURPOSE: Arduino library for 8x8 or smaller KeyPad connected to an I2C PCF8575.
// URL: https://github.com/RobTillaart/I2CKeyPad

Expand All @@ -11,17 +11,18 @@
#include "Wire.h"


#define I2C_KEYPAD8x8_LIB_VERSION (F("0.2.0"))
#define I2C_KEYPAD8x8_LIB_VERSION (F("0.3.0"))

#define I2C_KEYPAD8x8_NOKEY 64
#define I2C_KEYPAD8x8_FAIL 65

#define I2C_KEYPAD8x8_THRESHOLD 255

class I2CKeyPad8x8
{
public:
I2CKeyPad8x8(const uint8_t deviceAddress, TwoWire *wire = &Wire);

// call Wire.begin() first!
bool begin();
bool isConnected();

Expand All @@ -37,15 +38,22 @@ class I2CKeyPad8x8
uint8_t getLastChar();
void loadKeyMap(char * keyMap); // char[65]

// value in microseconds, max 65535 us
void setDebounceThreshold(uint16_t value = 0);
uint16_t getDebounceThreshold();


protected:
uint8_t _address;
uint8_t _lastKey;
uint16_t _read(uint16_t mask);
uint16_t _debounceThreshold;
uint32_t _lastRead;

TwoWire* _wire;

char * _keyMap = NULL;
uint8_t _getKey8x8();
};


Expand Down
85 changes: 56 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,19 @@ EXPERIMENTAL (first tests ==> OK)
The I2CKeyPad8x8 library implements the reading of a 8x8 keypad by means of a PCF8575.
Smaller keypads, meaning less columns or rows (e.g. 5x4) can be read with it too.

#### Breaking change

Since 0.3.0 the library can set a debounce threshold.
If this is set (> 0) the **getKey()** and **getChar()** functions
can return **I2C_KEYPAD_THRESHOLD** (255).

#### Related

Relates strongly to https://github.com/RobTillaart/I2CKeyPad. which is an 8x8 version using **PCF8574**.
Relates strongly to https://github.com/RobTillaart/I2CKeyPad. which is an 4x4 version using **PCF8574**.

- https://github.com/RobTillaart/PCF8575
- https://github.com/RobTillaart/AnalogKeypad
- https://github.com/RobTillaart/I2CKeyPad4x4
- https://github.com/RobTillaart/I2CKeyPad8x8


Expand All @@ -36,19 +44,18 @@ See the conceptual schema below.
It might take some trying to get the correct pins connected.

```
PROC PCF8575 KEYPAD
+--------+ +--------+ +--------+
| | | 0|----------|R |
| SDA |--------| .|----------|O |
| SCL |--------| .|----------|W |
| | | 7|----------|S |
| | | .| | |
| | | .| | |
| | | 8|----------|C |
| | | .|----------|O |
| | | .|----------|L |
| | | 15|----------|S |
+--------+ +--------+ +--------+
PROC PCF8575 KEYPAD
+--------+ +---------+ +---------+
| | | 0 |<-------->| R |
| SDA |<------>| . |<-------->| O |
| SCL |<------>| . |<-------->| W |
| | | 7 |<-------->| S |
| | | | | |
| | | 8 |<-------->| C |
| | | . |<-------->| O |
| | | . |<-------->| L |
| | | 15 |<-------->| S |
+--------+ +---------+ +---------+
```


Expand All @@ -59,7 +66,7 @@ It might take some trying to get the correct pins connected.
```


#### Base
### Base

- **I2CKeyPad8x8(const uint8_t deviceAddress, TwoWire \*wire = &Wire)**
The constructor sets the device address and optionally
Expand All @@ -74,12 +81,14 @@ Returns 64 if no key is pressed and 65 in case of an error.
however it is not checked if multiple keys are pressed.


#### KeyMap functions
### KeyMap functions

**loadKeyMap()** must be called before **getChar()** and **getLastChar()**!
Note: **loadKeyMap()** must be called before **getChar()** and **getLastChar()**!

- **char getChar()** returns the char corresponding to mapped key pressed.
It returns **I2C_KEYPAD_THRESHOLD** if called too fast.
- **char getLastChar()** returns the last char pressed.
This function is not affected by the debounce threshold.
- **bool loadKeyMap(char \* keyMap)** keyMap should point to a (global) char array of length 66.
This array maps index 0..63 on a char and index \[64\] maps to **I2CKeyPad8x8_NOKEY** (typical 'N')
and index \[65\] maps **I2CKeyPad8x8_FAIL** (typical 'F'). index 66 is the null char.
Expand All @@ -93,14 +102,39 @@ Note: a keyMap char array may be longer than 66 characters, but only the first 6
The length is **NOT** checked upon loading (as it may contain a NULL char).


#### Basic working
### Debouncing threshold

**Experimental**

Since version 0.3.0, 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.

The default value of the debounce threshold is zero to be backwards compatible.

// value in microseconds, max 65535 us
- **void setDebounceThreshold(uint16_t value = 0)** set the debounce threshold,
default value is zero, to reset its value.
- **uint16_t getDebounceThreshold()** returns the set debounce threshold.

If a debounce threshold is set, and **getKey()** is called too fast,
the function will return **I2C_KEYPAD8x8_THRESHOLD** (255).

Feedback welcome!

### Basic working

After the **keypad.begin()** the sketch calls the **keyPad.getKey()** to read values from the keypad.
- If no key is pressed **I2C_KEYPAD8x8_NOKEY** code (16) is returned.
- If the read value is not valid, e.g. two keys pressed, **I2CKeyPad8x8_FAIL** code (17) is returned.
- If the read value is not valid, e.g. two keys pressed, **I2C_KEYPAD8x8_FAIL** code (17) is returned.
- If a debounce threshold is set, **I2C_KEYPAD8x8_THRESHOLD** might be returned.
See section above.
- Otherwise a number 0..63 is returned.

Note NOKEY and FAIL bot have bit 8 set, all valid keys don't.
Note NOKEY and FAIL both have bit 8 set, all valid keys don't.
This allows fast checking for valid keys.

Only if a key map is loaded, the user can call **getChar()** and **getLastChar()** to get mapped keys.
Expand All @@ -109,19 +143,12 @@ Only if a key map is loaded, the user can call **getChar()** and **getLastChar()
## Interrupts

The library enables the PCF8575 to generate interrupts on the PCF8575 when a key is pressed.
This makes checking the keypad far more efficient as one does not need to poll over I2C.

See examples. (TODO)


## Operation

See examples
This makes checking the keypad far more efficient as one does not need to poll the device over I2C.
See examples.


## Future


#### Must

- update documentation
Expand Down
Loading

0 comments on commit 75fcf18

Please sign in to comment.