diff --git a/src/configuration.h b/src/configuration.h
index c5519a54b..e574ffe82 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -114,11 +114,12 @@ along with this program. If not, see .
// Define if screen should be mirrored left to right
// #define SCREEN_MIRROR
-// I2C Keyboards (M5Stack, RAK14004, T-Deck)
+// I2C Keyboards (M5Stack, RAK14004, T-Deck, T-Deck Pro, T-Lora Pager, CardKB, BBQ10, MPR121, TCA8418)
#define CARDKB_ADDR 0x5F
#define TDECK_KB_ADDR 0x55
#define BBQ10_KB_ADDR 0x1F
#define MPR121_KB_ADDR 0x5A
+#define TCA8418_KB_ADDR 0x34
// -----------------------------------------------------------------------------
// SENSOR
@@ -156,7 +157,7 @@ along with this program. If not, see .
#define MLX90614_ADDR_DEF 0x5A
#define CGRADSENS_ADDR 0x66
#define LTR390UV_ADDR 0x53
-#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
+#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418_KB
#define PCT2075_ADDR 0x37
#define BQ27220_ADDR 0x55 // same address as TDECK_KB
#define BQ25896_ADDR 0x6B
diff --git a/src/input/TCA8418Keyboard.cpp b/src/input/TCA8418Keyboard.cpp
index 86263a1b5..bd8338acf 100644
--- a/src/input/TCA8418Keyboard.cpp
+++ b/src/input/TCA8418Keyboard.cpp
@@ -1,165 +1,5 @@
-// Based on the MPR121 Keyboard and Adafruit TCA8418 library
-
#include "TCA8418Keyboard.h"
-#include "configuration.h"
-#include
-
-// REGISTERS
-// #define _TCA8418_REG_RESERVED 0x00
-#define _TCA8418_REG_CFG 0x01 // Configuration register
-#define _TCA8418_REG_INT_STAT 0x02 // Interrupt status
-#define _TCA8418_REG_KEY_LCK_EC 0x03 // Key lock and event counter
-#define _TCA8418_REG_KEY_EVENT_A 0x04 // Key event register A
-#define _TCA8418_REG_KEY_EVENT_B 0x05 // Key event register B
-#define _TCA8418_REG_KEY_EVENT_C 0x06 // Key event register C
-#define _TCA8418_REG_KEY_EVENT_D 0x07 // Key event register D
-#define _TCA8418_REG_KEY_EVENT_E 0x08 // Key event register E
-#define _TCA8418_REG_KEY_EVENT_F 0x09 // Key event register F
-#define _TCA8418_REG_KEY_EVENT_G 0x0A // Key event register G
-#define _TCA8418_REG_KEY_EVENT_H 0x0B // Key event register H
-#define _TCA8418_REG_KEY_EVENT_I 0x0C // Key event register I
-#define _TCA8418_REG_KEY_EVENT_J 0x0D // Key event register J
-#define _TCA8418_REG_KP_LCK_TIMER 0x0E // Keypad lock1 to lock2 timer
-#define _TCA8418_REG_UNLOCK_1 0x0F // Unlock register 1
-#define _TCA8418_REG_UNLOCK_2 0x10 // Unlock register 2
-#define _TCA8418_REG_GPIO_INT_STAT_1 0x11 // GPIO interrupt status 1
-#define _TCA8418_REG_GPIO_INT_STAT_2 0x12 // GPIO interrupt status 2
-#define _TCA8418_REG_GPIO_INT_STAT_3 0x13 // GPIO interrupt status 3
-#define _TCA8418_REG_GPIO_DAT_STAT_1 0x14 // GPIO data status 1
-#define _TCA8418_REG_GPIO_DAT_STAT_2 0x15 // GPIO data status 2
-#define _TCA8418_REG_GPIO_DAT_STAT_3 0x16 // GPIO data status 3
-#define _TCA8418_REG_GPIO_DAT_OUT_1 0x17 // GPIO data out 1
-#define _TCA8418_REG_GPIO_DAT_OUT_2 0x18 // GPIO data out 2
-#define _TCA8418_REG_GPIO_DAT_OUT_3 0x19 // GPIO data out 3
-#define _TCA8418_REG_GPIO_INT_EN_1 0x1A // GPIO interrupt enable 1
-#define _TCA8418_REG_GPIO_INT_EN_2 0x1B // GPIO interrupt enable 2
-#define _TCA8418_REG_GPIO_INT_EN_3 0x1C // GPIO interrupt enable 3
-#define _TCA8418_REG_KP_GPIO_1 0x1D // Keypad/GPIO select 1
-#define _TCA8418_REG_KP_GPIO_2 0x1E // Keypad/GPIO select 2
-#define _TCA8418_REG_KP_GPIO_3 0x1F // Keypad/GPIO select 3
-#define _TCA8418_REG_GPI_EM_1 0x20 // GPI event mode 1
-#define _TCA8418_REG_GPI_EM_2 0x21 // GPI event mode 2
-#define _TCA8418_REG_GPI_EM_3 0x22 // GPI event mode 3
-#define _TCA8418_REG_GPIO_DIR_1 0x23 // GPIO data direction 1
-#define _TCA8418_REG_GPIO_DIR_2 0x24 // GPIO data direction 2
-#define _TCA8418_REG_GPIO_DIR_3 0x25 // GPIO data direction 3
-#define _TCA8418_REG_GPIO_INT_LVL_1 0x26 // GPIO edge/level detect 1
-#define _TCA8418_REG_GPIO_INT_LVL_2 0x27 // GPIO edge/level detect 2
-#define _TCA8418_REG_GPIO_INT_LVL_3 0x28 // GPIO edge/level detect 3
-#define _TCA8418_REG_DEBOUNCE_DIS_1 0x29 // Debounce disable 1
-#define _TCA8418_REG_DEBOUNCE_DIS_2 0x2A // Debounce disable 2
-#define _TCA8418_REG_DEBOUNCE_DIS_3 0x2B // Debounce disable 3
-#define _TCA8418_REG_GPIO_PULL_1 0x2C // GPIO pull-up disable 1
-#define _TCA8418_REG_GPIO_PULL_2 0x2D // GPIO pull-up disable 2
-#define _TCA8418_REG_GPIO_PULL_3 0x2E // GPIO pull-up disable 3
-// #define _TCA8418_REG_RESERVED 0x2F
-
-// FIELDS CONFIG REGISTER 1
-#define _TCA8418_REG_CFG_AI 0x80 // Auto-increment for read/write
-#define _TCA8418_REG_CFG_GPI_E_CGF 0x40 // Event mode config
-#define _TCA8418_REG_CFG_OVR_FLOW_M 0x20 // Overflow mode enable
-#define _TCA8418_REG_CFG_INT_CFG 0x10 // Interrupt config
-#define _TCA8418_REG_CFG_OVR_FLOW_IEN 0x08 // Overflow interrupt enable
-#define _TCA8418_REG_CFG_K_LCK_IEN 0x04 // Keypad lock interrupt enable
-#define _TCA8418_REG_CFG_GPI_IEN 0x02 // GPI interrupt enable
-#define _TCA8418_REG_CFG_KE_IEN 0x01 // Key events interrupt enable
-
-// FIELDS INT_STAT REGISTER 2
-#define _TCA8418_REG_STAT_CAD_INT 0x10 // Ctrl-alt-del seq status
-#define _TCA8418_REG_STAT_OVR_FLOW_INT 0x08 // Overflow interrupt status
-#define _TCA8418_REG_STAT_K_LCK_INT 0x04 // Key lock interrupt status
-#define _TCA8418_REG_STAT_GPI_INT 0x02 // GPI interrupt status
-#define _TCA8418_REG_STAT_K_INT 0x01 // Key events interrupt status
-
-// FIELDS KEY_LCK_EC REGISTER 3
-#define _TCA8418_REG_LCK_EC_K_LCK_EN 0x40 // Key lock enable
-#define _TCA8418_REG_LCK_EC_LCK_2 0x20 // Keypad lock status 2
-#define _TCA8418_REG_LCK_EC_LCK_1 0x10 // Keypad lock status 1
-#define _TCA8418_REG_LCK_EC_KLEC_3 0x08 // Key event count bit 3
-#define _TCA8418_REG_LCK_EC_KLEC_2 0x04 // Key event count bit 2
-#define _TCA8418_REG_LCK_EC_KLEC_1 0x02 // Key event count bit 1
-#define _TCA8418_REG_LCK_EC_KLEC_0 0x01 // Key event count bit 0
-
-// Pin IDs for matrix rows/columns
-enum {
- _TCA8418_ROW0, // Pin ID for row 0
- _TCA8418_ROW1, // Pin ID for row 1
- _TCA8418_ROW2, // Pin ID for row 2
- _TCA8418_ROW3, // Pin ID for row 3
- _TCA8418_ROW4, // Pin ID for row 4
- _TCA8418_ROW5, // Pin ID for row 5
- _TCA8418_ROW6, // Pin ID for row 6
- _TCA8418_ROW7, // Pin ID for row 7
- _TCA8418_COL0, // Pin ID for column 0
- _TCA8418_COL1, // Pin ID for column 1
- _TCA8418_COL2, // Pin ID for column 2
- _TCA8418_COL3, // Pin ID for column 3
- _TCA8418_COL4, // Pin ID for column 4
- _TCA8418_COL5, // Pin ID for column 5
- _TCA8418_COL6, // Pin ID for column 6
- _TCA8418_COL7, // Pin ID for column 7
- _TCA8418_COL8, // Pin ID for column 8
- _TCA8418_COL9 // Pin ID for column 9
-};
-
-#if defined(T_DECK_PRO)
-#define _TCA8418_COLS 10
-#define _TCA8418_ROWS 4
-#define _TCA8418_NUM_KEYS 35
-
-#define _TCA8418_LONG_PRESS_THRESHOLD 2000
-#define _TCA8418_MULTI_TAP_THRESHOLD 1500
-
-// Num chars per key, Modulus for rotating through characters
-uint8_t TCA8418TapMod[_TCA8418_NUM_KEYS] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3};
-
-// https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
-unsigned char TCA8418TapMap[_TCA8418_NUM_KEYS][3] = {{'p', 'P', '@'},
- {'o', 'O', '+'},
- {'i', 'I', '-'},
- {'u', 'U', '_'},
- {'y', 'Y', ')'},
- {'t', 'T', '('},
- {'r', 'R', '3'},
- {'e', 'E', '2'},
- {'w', 'W', '1'},
- {'q', 'Q', '#'},
- {_TCA8418_BSP, 0x00, 0x00},
- {'l', 'L', '"'},
- {'k', 'K', '\''},
- {'j', 'J', ';'},
- {'h', 'H', ':'},
- {'g', 'G', '/'},
- {'f', 'F', '6'},
- {'d', 'D', '5'},
- {'s', 'S', '4'},
- {'a', 'A', '*'},
- {0x0d, 0x00, 0x00},
- {'$', 0x00, 0x00},
- {'m', 'M', '.'},
- {'n', 'N', ','},
- {'b', 'B', '!'},
- {'v', 'V', '?'},
- {'c', 'C', '9'},
- {'x', 'X', '8'},
- {'z', 'Z', '7'},
- {0xa4, 0x00, 0x00},
- {0xa1, 0x00, 0x00},
- {0x00, 0x00, 0x00},
- {0x20, 0x00, 0x00},
- {0x00, 0x00, '0'},
- {0xa0, 0x00, 0x00}};
-
-unsigned char TCA8418LongPressMap[_TCA8418_NUM_KEYS] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, _TCA8418_ESC, // p,o,i,u,y,t,r,e,w,q
- 0x00, 0x00, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // bsp,l,k,j,h,g,f,d,s,a
- 0x00, 0x00, 0xb7, 0xb6, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, // ent,$,m,n,b,v,c,x,z,alt
- 0x00, 0x00, 0x00, 0x00, 0x00 // rShift,sym,space,mic,lShift
-};
-
-#else
#define _TCA8418_COLS 3
#define _TCA8418_ROWS 4
#define _TCA8418_NUM_KEYS 12
@@ -167,10 +7,12 @@ unsigned char TCA8418LongPressMap[_TCA8418_NUM_KEYS] = {
#define _TCA8418_LONG_PRESS_THRESHOLD 2000
#define _TCA8418_MULTI_TAP_THRESHOLD 750
-uint8_t TCA8418TapMod[_TCA8418_NUM_KEYS] = {13, 7, 7, 7, 7, 7,
- 9, 7, 9, 2, 2, 2}; // Num chars per key, Modulus for rotating through characters
+using Key = TCA8418KeyboardBase::TCA8418Key;
-unsigned char TCA8418TapMap[_TCA8418_NUM_KEYS][13] = {
+// Num chars per key, Modulus for rotating through characters
+static uint8_t TCA8418TapMod[_TCA8418_NUM_KEYS] = {13, 7, 7, 7, 7, 7, 9, 7, 9, 2, 2, 2};
+
+static unsigned char TCA8418TapMap[_TCA8418_NUM_KEYS][13] = {
{'1', '.', ',', '?', '!', ':', ';', '-', '_', '\\', '/', '(', ')'}, // 1
{'2', 'a', 'b', 'c', 'A', 'B', 'C'}, // 2
{'3', 'd', 'e', 'f', 'D', 'E', 'F'}, // 3
@@ -185,179 +27,35 @@ unsigned char TCA8418TapMap[_TCA8418_NUM_KEYS][13] = {
{'#', '@'}, // #
};
-unsigned char TCA8418LongPressMap[_TCA8418_NUM_KEYS] = {
- _TCA8418_ESC, // 1
- _TCA8418_UP, // 2
- _TCA8418_NONE, // 3
- _TCA8418_LEFT, // 4
- _TCA8418_NONE, // 5
- _TCA8418_RIGHT, // 6
- _TCA8418_NONE, // 7
- _TCA8418_DOWN, // 8
- _TCA8418_NONE, // 9
- _TCA8418_BSP, // *
- _TCA8418_NONE, // 0
- _TCA8418_NONE, // #
+static unsigned char TCA8418LongPressMap[_TCA8418_NUM_KEYS] = {
+ Key::ESC, // 1
+ Key::UP, // 2
+ Key::NONE, // 3
+ Key::LEFT, // 4
+ Key::NONE, // 5
+ Key::RIGHT, // 6
+ Key::NONE, // 7
+ Key::DOWN, // 8
+ Key::NONE, // 9
+ Key::BSP, // *
+ Key::NONE, // 0
+ Key::NONE, // #
};
-#endif
-TCA8418Keyboard::TCA8418Keyboard() : m_wire(nullptr), m_addr(0), readCallback(nullptr), writeCallback(nullptr)
+TCA8418Keyboard::TCA8418Keyboard()
+ : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), last_key(-1), next_key(-1), last_tap(0L), char_idx(0), tap_interval(0),
+ should_backspace(false)
{
- state = Init;
- last_key = -1;
- next_key = -1;
- should_backspace = false;
- last_tap = 0L;
- char_idx = 0;
- tap_interval = 0;
- backlight_on = true;
- queue = "";
-}
-
-void TCA8418Keyboard::begin(uint8_t addr, TwoWire *wire)
-{
- m_addr = addr;
- m_wire = wire;
-
- m_wire->begin();
-
- reset();
-}
-
-void TCA8418Keyboard::begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr)
-{
- m_addr = addr;
- m_wire = nullptr;
- writeCallback = w;
- readCallback = r;
- reset();
}
void TCA8418Keyboard::reset()
{
- LOG_DEBUG("TCA8418 Reset");
- // GPIO
- // set default all GIO pins to INPUT
- writeRegister(_TCA8418_REG_GPIO_DIR_1, 0x00);
- writeRegister(_TCA8418_REG_GPIO_DIR_2, 0x00);
-#ifndef T_DECK_PRO
+ TCA8418KeyboardBase::reset();
+
// Set COL9 as GPIO output
- writeRegister(_TCA8418_REG_GPIO_DIR_3, 0x02);
+ writeRegister(TCA8418_REG_GPIO_DIR_3, 0x02);
// Switch off keyboard backlight (COL9 = LOW)
- writeRegister(_TCA8418_REG_GPIO_DAT_OUT_3, 0x00);
-#else
- writeRegister(_TCA8418_REG_GPIO_DIR_3, 0x00);
-#endif
-
- // add all pins to key events
- writeRegister(_TCA8418_REG_GPI_EM_1, 0xFF);
- writeRegister(_TCA8418_REG_GPI_EM_2, 0xFF);
- writeRegister(_TCA8418_REG_GPI_EM_3, 0xFF);
-
- // set all pins to FALLING interrupts
- writeRegister(_TCA8418_REG_GPIO_INT_LVL_1, 0x00);
- writeRegister(_TCA8418_REG_GPIO_INT_LVL_2, 0x00);
- writeRegister(_TCA8418_REG_GPIO_INT_LVL_3, 0x00);
-
- // add all pins to interrupts
- writeRegister(_TCA8418_REG_GPIO_INT_EN_1, 0xFF);
- writeRegister(_TCA8418_REG_GPIO_INT_EN_2, 0xFF);
- writeRegister(_TCA8418_REG_GPIO_INT_EN_3, 0xFF);
-
- // Set keyboard matrix size
- matrix(_TCA8418_ROWS, _TCA8418_COLS);
- enableDebounce();
- flush();
- state = Idle;
-}
-
-bool TCA8418Keyboard::matrix(uint8_t rows, uint8_t columns)
-{
- if ((rows > 8) || (columns > 10))
- return false;
-
- // Skip zero size matrix
- if ((rows != 0) && (columns != 0)) {
- // Setup the keypad matrix.
- uint8_t mask = 0x00;
- for (int r = 0; r < rows; r++) {
- mask <<= 1;
- mask |= 1;
- }
- writeRegister(_TCA8418_REG_KP_GPIO_1, mask);
-
- mask = 0x00;
- for (int c = 0; c < columns && c < 8; c++) {
- mask <<= 1;
- mask |= 1;
- }
- writeRegister(_TCA8418_REG_KP_GPIO_2, mask);
-
- if (columns > 8) {
- if (columns == 9)
- mask = 0x01;
- else
- mask = 0x03;
- writeRegister(_TCA8418_REG_KP_GPIO_3, mask);
- }
- }
-
- return true;
-}
-
-uint8_t TCA8418Keyboard::keyCount() const
-{
- uint8_t eventCount = readRegister(_TCA8418_REG_KEY_LCK_EC);
- eventCount &= 0x0F; // lower 4 bits only
- return eventCount;
-}
-
-bool TCA8418Keyboard::hasEvent()
-{
- return queue.length() > 0;
-}
-
-void TCA8418Keyboard::queueEvent(char next)
-{
- if (next == _TCA8418_NONE) {
- return;
- }
- queue.concat(next);
-}
-
-char TCA8418Keyboard::dequeueEvent()
-{
- if (queue.length() < 1) {
- return _TCA8418_NONE;
- }
- char next = queue.charAt(0);
- queue.remove(0, 1);
- return next;
-}
-
-void TCA8418Keyboard::trigger()
-{
- if (keyCount() == 0) {
- return;
- }
- if (state != Init) {
- // Read the key register
- uint8_t k = readRegister(_TCA8418_REG_KEY_EVENT_A);
- uint8_t key = k & 0x7F;
- if (k & 0x80) {
- if (state == Idle)
- pressed(key);
- return;
- } else {
- if (state == Held) {
- released();
- }
- state = Idle;
- return;
- }
- } else {
- reset();
- }
+ writeRegister(TCA8418_REG_GPIO_DAT_OUT_3, 0x00);
}
void TCA8418Keyboard::pressed(uint8_t key)
@@ -417,7 +115,7 @@ void TCA8418Keyboard::released()
int32_t held_interval = now - last_tap;
last_tap = now;
if (tap_interval < _TCA8418_MULTI_TAP_THRESHOLD && should_backspace) {
- queueEvent(_TCA8418_BSP);
+ queueEvent(BSP);
}
if (held_interval > _TCA8418_LONG_PRESS_THRESHOLD) {
queueEvent(TCA8418LongPressMap[last_key]);
@@ -429,203 +127,11 @@ void TCA8418Keyboard::released()
}
}
-uint8_t TCA8418Keyboard::flush()
-{
- // Flush key events
- uint8_t count = 0;
- while (readRegister(_TCA8418_REG_KEY_EVENT_A) != 0)
- count++;
- // Flush gpio events
- readRegister(_TCA8418_REG_GPIO_INT_STAT_1);
- readRegister(_TCA8418_REG_GPIO_INT_STAT_2);
- readRegister(_TCA8418_REG_GPIO_INT_STAT_3);
- // Clear INT_STAT register
- writeRegister(_TCA8418_REG_INT_STAT, 3);
- return count;
-}
-
-uint8_t TCA8418Keyboard::digitalRead(uint8_t pinnum) const
-{
- if (pinnum > _TCA8418_COL9)
- return 0xFF;
-
- uint8_t reg = _TCA8418_REG_GPIO_DAT_STAT_1 + pinnum / 8;
- uint8_t mask = (1 << (pinnum % 8));
-
- // Level 0 = low other = high
- uint8_t value = readRegister(reg);
- if (value & mask)
- return HIGH;
- return LOW;
-}
-
-bool TCA8418Keyboard::digitalWrite(uint8_t pinnum, uint8_t level)
-{
- if (pinnum > _TCA8418_COL9)
- return false;
-
- uint8_t reg = _TCA8418_REG_GPIO_DAT_OUT_1 + pinnum / 8;
- uint8_t mask = (1 << (pinnum % 8));
-
- // Level 0 = low other = high
- uint8_t value = readRegister(reg);
- if (level == LOW)
- value &= ~mask;
- else
- value |= mask;
- writeRegister(reg, value);
- return true;
-}
-
-bool TCA8418Keyboard::pinMode(uint8_t pinnum, uint8_t mode)
-{
- if (pinnum > _TCA8418_COL9)
- return false;
-
- uint8_t idx = pinnum / 8;
- uint8_t reg = _TCA8418_REG_GPIO_DIR_1 + idx;
- uint8_t mask = (1 << (pinnum % 8));
-
- // Mode 0 = input 1 = output
- uint8_t value = readRegister(reg);
- if (mode == OUTPUT)
- value |= mask;
- else
- value &= ~mask;
- writeRegister(reg, value);
-
- // Pullup 0 = enabled 1 = disabled
- reg = _TCA8418_REG_GPIO_PULL_1 + idx;
- value = readRegister(reg);
- if (mode == INPUT_PULLUP)
- value &= ~mask;
- else
- value |= mask;
- writeRegister(reg, value);
-
- return true;
-}
-
-bool TCA8418Keyboard::pinIRQMode(uint8_t pinnum, uint8_t mode)
-{
- if (pinnum > _TCA8418_COL9)
- return false;
- if ((mode != RISING) && (mode != FALLING))
- return false;
-
- // Mode 0 = falling 1 = rising
- uint8_t idx = pinnum / 8;
- uint8_t reg = _TCA8418_REG_GPIO_INT_LVL_1 + idx;
- uint8_t mask = (1 << (pinnum % 8));
-
- uint8_t value = readRegister(reg);
- if (mode == RISING)
- value |= mask;
- else
- value &= ~mask;
- writeRegister(reg, value);
-
- // Enable interrupt
- reg = _TCA8418_REG_GPIO_INT_EN_1 + idx;
- value = readRegister(reg);
- value |= mask;
- writeRegister(reg, value);
-
- return true;
-}
-
-void TCA8418Keyboard::enableInterrupts()
-{
- uint8_t value = readRegister(_TCA8418_REG_CFG);
- value |= (_TCA8418_REG_CFG_GPI_IEN | _TCA8418_REG_CFG_KE_IEN);
- writeRegister(_TCA8418_REG_CFG, value);
-};
-
-void TCA8418Keyboard::disableInterrupts()
-{
- uint8_t value = readRegister(_TCA8418_REG_CFG);
- value &= ~(_TCA8418_REG_CFG_GPI_IEN | _TCA8418_REG_CFG_KE_IEN);
- writeRegister(_TCA8418_REG_CFG, value);
-};
-
-void TCA8418Keyboard::enableMatrixOverflow()
-{
- uint8_t value = readRegister(_TCA8418_REG_CFG);
- value |= _TCA8418_REG_CFG_OVR_FLOW_M;
- writeRegister(_TCA8418_REG_CFG, value);
-};
-
-void TCA8418Keyboard::disableMatrixOverflow()
-{
- uint8_t value = readRegister(_TCA8418_REG_CFG);
- value &= ~_TCA8418_REG_CFG_OVR_FLOW_M;
- writeRegister(_TCA8418_REG_CFG, value);
-};
-
-void TCA8418Keyboard::enableDebounce()
-{
- writeRegister(_TCA8418_REG_DEBOUNCE_DIS_1, 0x00);
- writeRegister(_TCA8418_REG_DEBOUNCE_DIS_2, 0x00);
- writeRegister(_TCA8418_REG_DEBOUNCE_DIS_3, 0x00);
-}
-
-void TCA8418Keyboard::disableDebounce()
-{
- writeRegister(_TCA8418_REG_DEBOUNCE_DIS_1, 0xFF);
- writeRegister(_TCA8418_REG_DEBOUNCE_DIS_2, 0xFF);
- writeRegister(_TCA8418_REG_DEBOUNCE_DIS_3, 0xFF);
-}
-
void TCA8418Keyboard::setBacklight(bool on)
{
-#ifdef T_DECK_PRO
if (on) {
- digitalWrite(KB_BL_PIN, HIGH);
+ digitalWrite(TCA8418_COL9, HIGH);
} else {
- digitalWrite(KB_BL_PIN, LOW);
+ digitalWrite(TCA8418_COL9, LOW);
}
-#else
- if (on) {
- digitalWrite(_TCA8418_COL9, HIGH);
- } else {
- digitalWrite(_TCA8418_COL9, LOW);
- }
-#endif
}
-
-uint8_t TCA8418Keyboard::readRegister(uint8_t reg) const
-{
- if (m_wire) {
- m_wire->beginTransmission(m_addr);
- m_wire->write(reg);
- m_wire->endTransmission();
-
- m_wire->requestFrom(m_addr, (uint8_t)1);
- if (m_wire->available() < 1)
- return 0;
-
- return m_wire->read();
- }
- if (readCallback) {
- uint8_t data;
- readCallback(m_addr, reg, &data, 1);
- return data;
- }
- return 0;
-}
-
-void TCA8418Keyboard::writeRegister(uint8_t reg, uint8_t value)
-{
- uint8_t data[2];
- data[0] = reg;
- data[1] = value;
-
- if (m_wire) {
- m_wire->beginTransmission(m_addr);
- m_wire->write(data, sizeof(uint8_t) * 2);
- m_wire->endTransmission();
- }
- if (writeCallback) {
- writeCallback(m_addr, data[0], &(data[1]), 1);
- }
-}
\ No newline at end of file
diff --git a/src/input/TCA8418Keyboard.h b/src/input/TCA8418Keyboard.h
index c7f3c1f28..b76916643 100644
--- a/src/input/TCA8418Keyboard.h
+++ b/src/input/TCA8418Keyboard.h
@@ -1,83 +1,23 @@
-// Based on the MPR121 Keyboard and Adafruit TCA8418 library
-#include "configuration.h"
-#include
+#include "TCA8418KeyboardBase.h"
-#define _TCA8418_NONE 0x00
-#define _TCA8418_REBOOT 0x90
-#define _TCA8418_LEFT 0xb4
-#define _TCA8418_UP 0xb5
-#define _TCA8418_DOWN 0xb6
-#define _TCA8418_RIGHT 0xb7
-#define _TCA8418_ESC 0x1b
-#define _TCA8418_BSP 0x08
-#define _TCA8418_SELECT 0x0d
-
-class TCA8418Keyboard
+/**
+ * @brief 3x4 keypad with 3 columns and 4 rows
+ */
+class TCA8418Keyboard : public TCA8418KeyboardBase
{
public:
- typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len);
+ TCA8418Keyboard();
+ void reset(void) override;
+ void setBacklight(bool on) override;
- enum KeyState { Init = 0, Idle, Held, Busy };
+ protected:
+ void pressed(uint8_t key) override;
+ void released(void) override;
- KeyState state;
int8_t last_key;
int8_t next_key;
- bool should_backspace;
uint32_t last_tap;
uint8_t char_idx;
int32_t tap_interval;
- bool backlight_on;
-
- String queue;
-
- TCA8418Keyboard();
-
- void begin(uint8_t addr = XPOWERS_AXP192_AXP2101_ADDRESS, TwoWire *wire = &Wire);
- void begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr = XPOWERS_AXP192_AXP2101_ADDRESS);
-
- void reset(void);
- // Configure the size of the keypad.
- // All other rows and columns are set as inputs.
- bool matrix(uint8_t rows, uint8_t columns);
-
- // Flush all events in the FIFO buffer + GPIO events.
- uint8_t flush(void);
-
- // Key events available in the internal FIFO buffer.
- uint8_t keyCount(void) const;
-
- void trigger(void);
- void pressed(uint8_t key);
- void released(void);
- bool hasEvent(void);
- char dequeueEvent(void);
- void queueEvent(char);
-
- uint8_t digitalRead(uint8_t pinnum) const;
- bool digitalWrite(uint8_t pinnum, uint8_t level);
- bool pinMode(uint8_t pinnum, uint8_t mode);
- bool pinIRQMode(uint8_t pinnum, uint8_t mode); // MODE FALLING or RISING
-
- // enable / disable interrupts for matrix and GPI pins
- void enableInterrupts();
- void disableInterrupts();
-
- // ignore key events when FIFO buffer is full or not.
- void enableMatrixOverflow();
- void disableMatrixOverflow();
-
- // debounce keys.
- void enableDebounce();
- void disableDebounce();
-
- void setBacklight(bool on);
-
- uint8_t readRegister(uint8_t reg) const;
- void writeRegister(uint8_t reg, uint8_t value);
-
- private:
- TwoWire *m_wire;
- uint8_t m_addr;
- i2c_com_fptr_t readCallback;
- i2c_com_fptr_t writeCallback;
+ bool should_backspace;
};
diff --git a/src/input/TCA8418KeyboardBase.cpp b/src/input/TCA8418KeyboardBase.cpp
new file mode 100644
index 000000000..aafc4c36c
--- /dev/null
+++ b/src/input/TCA8418KeyboardBase.cpp
@@ -0,0 +1,372 @@
+// Based on the MPR121 Keyboard and Adafruit TCA8418 library
+
+#include "TCA8418KeyboardBase.h"
+#include "configuration.h"
+
+#include
+
+// FIELDS CONFIG REGISTER 1
+#define _TCA8418_REG_CFG_AI 0x80 // Auto-increment for read/write
+#define _TCA8418_REG_CFG_GPI_E_CGF 0x40 // Event mode config
+#define _TCA8418_REG_CFG_OVR_FLOW_M 0x20 // Overflow mode enable
+#define _TCA8418_REG_CFG_INT_CFG 0x10 // Interrupt config
+#define _TCA8418_REG_CFG_OVR_FLOW_IEN 0x08 // Overflow interrupt enable
+#define _TCA8418_REG_CFG_K_LCK_IEN 0x04 // Keypad lock interrupt enable
+#define _TCA8418_REG_CFG_GPI_IEN 0x02 // GPI interrupt enable
+#define _TCA8418_REG_CFG_KE_IEN 0x01 // Key events interrupt enable
+
+// FIELDS INT_STAT REGISTER 2
+#define _TCA8418_REG_STAT_CAD_INT 0x10 // Ctrl-alt-del seq status
+#define _TCA8418_REG_STAT_OVR_FLOW_INT 0x08 // Overflow interrupt status
+#define _TCA8418_REG_STAT_K_LCK_INT 0x04 // Key lock interrupt status
+#define _TCA8418_REG_STAT_GPI_INT 0x02 // GPI interrupt status
+#define _TCA8418_REG_STAT_K_INT 0x01 // Key events interrupt status
+
+// FIELDS KEY_LCK_EC REGISTER 3
+#define _TCA8418_REG_LCK_EC_K_LCK_EN 0x40 // Key lock enable
+#define _TCA8418_REG_LCK_EC_LCK_2 0x20 // Keypad lock status 2
+#define _TCA8418_REG_LCK_EC_LCK_1 0x10 // Keypad lock status 1
+#define _TCA8418_REG_LCK_EC_KLEC_3 0x08 // Key event count bit 3
+#define _TCA8418_REG_LCK_EC_KLEC_2 0x04 // Key event count bit 2
+#define _TCA8418_REG_LCK_EC_KLEC_1 0x02 // Key event count bit 1
+#define _TCA8418_REG_LCK_EC_KLEC_0 0x01 // Key event count bit 0
+
+TCA8418KeyboardBase::TCA8418KeyboardBase(uint8_t rows, uint8_t columns)
+ : rows(rows), columns(columns), state(Init), queue(""), m_wire(nullptr), m_addr(0), readCallback(nullptr),
+ writeCallback(nullptr)
+{
+}
+
+void TCA8418KeyboardBase::begin(uint8_t addr, TwoWire *wire)
+{
+ m_addr = addr;
+ m_wire = wire;
+ m_wire->begin();
+ reset();
+}
+
+void TCA8418KeyboardBase::begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr)
+{
+ m_addr = addr;
+ m_wire = nullptr;
+ writeCallback = w;
+ readCallback = r;
+ reset();
+}
+
+void TCA8418KeyboardBase::reset()
+{
+ LOG_DEBUG("TCA8418 Reset");
+ // GPIO
+ // set default all GIO pins to INPUT
+ writeRegister(TCA8418_REG_GPIO_DIR_1, 0x00);
+ writeRegister(TCA8418_REG_GPIO_DIR_2, 0x00);
+ writeRegister(TCA8418_REG_GPIO_DIR_3, 0x00);
+
+ // add all pins to key events
+ writeRegister(TCA8418_REG_GPI_EM_1, 0xFF);
+ writeRegister(TCA8418_REG_GPI_EM_2, 0xFF);
+ writeRegister(TCA8418_REG_GPI_EM_3, 0xFF);
+
+ // set all pins to FALLING interrupts
+ writeRegister(TCA8418_REG_GPIO_INT_LVL_1, 0x00);
+ writeRegister(TCA8418_REG_GPIO_INT_LVL_2, 0x00);
+ writeRegister(TCA8418_REG_GPIO_INT_LVL_3, 0x00);
+
+ // add all pins to interrupts
+ writeRegister(TCA8418_REG_GPIO_INT_EN_1, 0xFF);
+ writeRegister(TCA8418_REG_GPIO_INT_EN_2, 0xFF);
+ writeRegister(TCA8418_REG_GPIO_INT_EN_3, 0xFF);
+
+ // Set keyboard matrix size
+ matrix(rows, columns);
+ enableDebounce();
+ flush();
+ state = Idle;
+}
+
+bool TCA8418KeyboardBase::matrix(uint8_t rows, uint8_t columns)
+{
+ if (rows < 1 || rows > 8 || columns < 1 || columns > 10)
+ return false;
+
+ // Setup the keypad matrix.
+ uint8_t mask = 0x00;
+ for (int r = 0; r < rows; r++) {
+ mask <<= 1;
+ mask |= 1;
+ }
+ writeRegister(TCA8418_REG_KP_GPIO_1, mask);
+
+ mask = 0x00;
+ for (int c = 0; c < columns && c < 8; c++) {
+ mask <<= 1;
+ mask |= 1;
+ }
+ writeRegister(TCA8418_REG_KP_GPIO_2, mask);
+
+ if (columns > 8) {
+ if (columns == 9)
+ mask = 0x01;
+ else
+ mask = 0x03;
+ writeRegister(TCA8418_REG_KP_GPIO_3, mask);
+ }
+
+ return true;
+}
+
+uint8_t TCA8418KeyboardBase::keyCount() const
+{
+ uint8_t eventCount = readRegister(TCA8418_REG_KEY_LCK_EC);
+ eventCount &= 0x0F; // lower 4 bits only
+ return eventCount;
+}
+
+bool TCA8418KeyboardBase::hasEvent() const
+{
+ return queue.length() > 0;
+}
+
+void TCA8418KeyboardBase::queueEvent(char next)
+{
+ if (next == NONE) {
+ return;
+ }
+ queue.concat(next);
+}
+
+char TCA8418KeyboardBase::dequeueEvent()
+{
+ if (queue.length() < 1) {
+ return NONE;
+ }
+ char next = queue.charAt(0);
+ queue.remove(0, 1);
+ return next;
+}
+
+void TCA8418KeyboardBase::trigger()
+{
+ if (keyCount() == 0) {
+ return;
+ }
+ if (state != Init) {
+ // Read the key register
+ uint8_t k = readRegister(TCA8418_REG_KEY_EVENT_A);
+ uint8_t key = k & 0x7F;
+ if (k & 0x80) {
+ if (state == Idle)
+ pressed(key);
+ return;
+ } else {
+ if (state == Held) {
+ released();
+ }
+ state = Idle;
+ return;
+ }
+ } else {
+ reset();
+ }
+}
+
+void TCA8418KeyboardBase::pressed(uint8_t key)
+{
+ // must be defined in derived class
+ LOG_ERROR("pressed() not implemented in derived class");
+}
+
+void TCA8418KeyboardBase::released()
+{
+ // must be defined in derived class
+ LOG_ERROR("released() not implemented in derived class");
+}
+
+uint8_t TCA8418KeyboardBase::flush()
+{
+ // Flush key events
+ uint8_t count = 0;
+ while (readRegister(TCA8418_REG_KEY_EVENT_A) != 0)
+ count++;
+
+ // Flush gpio events
+ readRegister(TCA8418_REG_GPIO_INT_STAT_1);
+ readRegister(TCA8418_REG_GPIO_INT_STAT_2);
+ readRegister(TCA8418_REG_GPIO_INT_STAT_3);
+
+ // Clear INT_STAT register
+ writeRegister(TCA8418_REG_INT_STAT, 3);
+ return count;
+}
+
+uint8_t TCA8418KeyboardBase::digitalRead(uint8_t pinnum) const
+{
+ if (pinnum > TCA8418_COL9)
+ return 0xFF;
+
+ uint8_t reg = TCA8418_REG_GPIO_DAT_STAT_1 + pinnum / 8;
+ uint8_t mask = (1 << (pinnum % 8));
+
+ // Level 0 = low other = high
+ uint8_t value = readRegister(reg);
+ if (value & mask)
+ return HIGH;
+ return LOW;
+}
+
+bool TCA8418KeyboardBase::digitalWrite(uint8_t pinnum, uint8_t level)
+{
+ if (pinnum > TCA8418_COL9)
+ return false;
+
+ uint8_t reg = TCA8418_REG_GPIO_DAT_OUT_1 + pinnum / 8;
+ uint8_t mask = (1 << (pinnum % 8));
+
+ // Level 0 = low other = high
+ uint8_t value = readRegister(reg);
+ if (level == LOW)
+ value &= ~mask;
+ else
+ value |= mask;
+ writeRegister(reg, value);
+ return true;
+}
+
+bool TCA8418KeyboardBase::pinMode(uint8_t pinnum, uint8_t mode)
+{
+ if (pinnum > TCA8418_COL9)
+ return false;
+
+ uint8_t idx = pinnum / 8;
+ uint8_t reg = TCA8418_REG_GPIO_DIR_1 + idx;
+ uint8_t mask = (1 << (pinnum % 8));
+
+ // Mode 0 = input 1 = output
+ uint8_t value = readRegister(reg);
+ if (mode == OUTPUT)
+ value |= mask;
+ else
+ value &= ~mask;
+ writeRegister(reg, value);
+
+ // Pullup 0 = enabled 1 = disabled
+ reg = TCA8418_REG_GPIO_PULL_1 + idx;
+ value = readRegister(reg);
+ if (mode == INPUT_PULLUP)
+ value &= ~mask;
+ else
+ value |= mask;
+ writeRegister(reg, value);
+
+ return true;
+}
+
+bool TCA8418KeyboardBase::pinIRQMode(uint8_t pinnum, uint8_t mode)
+{
+ if (pinnum > TCA8418_COL9)
+ return false;
+ if ((mode != RISING) && (mode != FALLING))
+ return false;
+
+ // Mode 0 = falling 1 = rising
+ uint8_t idx = pinnum / 8;
+ uint8_t reg = TCA8418_REG_GPIO_INT_LVL_1 + idx;
+ uint8_t mask = (1 << (pinnum % 8));
+
+ uint8_t value = readRegister(reg);
+ if (mode == RISING)
+ value |= mask;
+ else
+ value &= ~mask;
+ writeRegister(reg, value);
+
+ // Enable interrupt
+ reg = TCA8418_REG_GPIO_INT_EN_1 + idx;
+ value = readRegister(reg);
+ value |= mask;
+ writeRegister(reg, value);
+
+ return true;
+}
+
+void TCA8418KeyboardBase::enableInterrupts()
+{
+ uint8_t value = readRegister(TCA8418_REG_CFG);
+ value |= (_TCA8418_REG_CFG_GPI_IEN | _TCA8418_REG_CFG_KE_IEN);
+ writeRegister(TCA8418_REG_CFG, value);
+};
+
+void TCA8418KeyboardBase::disableInterrupts()
+{
+ uint8_t value = readRegister(TCA8418_REG_CFG);
+ value &= ~(_TCA8418_REG_CFG_GPI_IEN | _TCA8418_REG_CFG_KE_IEN);
+ writeRegister(TCA8418_REG_CFG, value);
+};
+
+void TCA8418KeyboardBase::enableMatrixOverflow()
+{
+ uint8_t value = readRegister(TCA8418_REG_CFG);
+ value |= _TCA8418_REG_CFG_OVR_FLOW_M;
+ writeRegister(TCA8418_REG_CFG, value);
+};
+
+void TCA8418KeyboardBase::disableMatrixOverflow()
+{
+ uint8_t value = readRegister(TCA8418_REG_CFG);
+ value &= ~_TCA8418_REG_CFG_OVR_FLOW_M;
+ writeRegister(TCA8418_REG_CFG, value);
+};
+
+void TCA8418KeyboardBase::enableDebounce()
+{
+ writeRegister(TCA8418_REG_DEBOUNCE_DIS_1, 0x00);
+ writeRegister(TCA8418_REG_DEBOUNCE_DIS_2, 0x00);
+ writeRegister(TCA8418_REG_DEBOUNCE_DIS_3, 0x00);
+}
+
+void TCA8418KeyboardBase::disableDebounce()
+{
+ writeRegister(TCA8418_REG_DEBOUNCE_DIS_1, 0xFF);
+ writeRegister(TCA8418_REG_DEBOUNCE_DIS_2, 0xFF);
+ writeRegister(TCA8418_REG_DEBOUNCE_DIS_3, 0xFF);
+}
+
+void TCA8418KeyboardBase::setBacklight(bool on) {}
+
+uint8_t TCA8418KeyboardBase::readRegister(uint8_t reg) const
+{
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(reg);
+ m_wire->endTransmission();
+
+ m_wire->requestFrom(m_addr, (uint8_t)1);
+ if (m_wire->available() < 1)
+ return 0;
+
+ return m_wire->read();
+ }
+ if (readCallback) {
+ uint8_t data;
+ readCallback(m_addr, reg, &data, 1);
+ return data;
+ }
+ return 0;
+}
+
+void TCA8418KeyboardBase::writeRegister(uint8_t reg, uint8_t value)
+{
+ uint8_t data[2];
+ data[0] = reg;
+ data[1] = value;
+
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(data, sizeof(uint8_t) * 2);
+ m_wire->endTransmission();
+ }
+ if (writeCallback) {
+ writeCallback(m_addr, data[0], &(data[1]), 1);
+ }
+}
\ No newline at end of file
diff --git a/src/input/TCA8418KeyboardBase.h b/src/input/TCA8418KeyboardBase.h
new file mode 100644
index 000000000..5d6c4f7e9
--- /dev/null
+++ b/src/input/TCA8418KeyboardBase.h
@@ -0,0 +1,170 @@
+// Based on the MPR121 Keyboard and Adafruit TCA8418 library
+#include "configuration.h"
+#include
+
+/**
+ * @brief TCA8418KeyboardBase is the base class for TCA8418 keyboard handling.
+ * It provides basic functionality for reading key events, managing the keyboard matrix,
+ * and handling key states. It is designed to be extended for specific keyboard implementations.
+ * It supports both I2C communication and function pointers for custom I2C operations.
+ */
+class TCA8418KeyboardBase
+{
+ public:
+ enum TCA8418Key : uint8_t {
+ NONE = 0x00,
+ BSP = 0x08,
+ TAB = 0x09,
+ SELECT = 0x0d,
+ ESC = 0x1b,
+ REBOOT = 0x90,
+ LEFT = 0xb4,
+ UP = 0xb5,
+ DOWN = 0xb6,
+ RIGHT = 0xb7,
+ BT_TOGGLE = 0xAA,
+ GPS_TOGGLE = 0x9E,
+ MUTE_TOGGLE = 0xAC,
+ SEND_PING = 0xAF,
+ BL_TOGGLE = 0xAB
+ };
+
+ typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len);
+
+ TCA8418KeyboardBase(uint8_t rows, uint8_t columns);
+
+ virtual void begin(uint8_t addr = TCA8418_KB_ADDR, TwoWire *wire = &Wire);
+ virtual void begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr = TCA8418_KB_ADDR);
+
+ virtual void reset(void);
+ virtual void trigger(void);
+
+ virtual void setBacklight(bool on);
+
+ // Key events available
+ virtual bool hasEvent(void) const;
+ virtual char dequeueEvent(void);
+
+ protected:
+ enum KeyState { Init, Idle, Held, Busy };
+
+ enum TCA8418Register : uint8_t {
+ TCA8418_REG_RESERVED = 0x00,
+ TCA8418_REG_CFG = 0x01,
+ TCA8418_REG_INT_STAT = 0x02,
+ TCA8418_REG_KEY_LCK_EC = 0x03,
+ TCA8418_REG_KEY_EVENT_A = 0x04,
+ TCA8418_REG_KEY_EVENT_B = 0x05,
+ TCA8418_REG_KEY_EVENT_C = 0x06,
+ TCA8418_REG_KEY_EVENT_D = 0x07,
+ TCA8418_REG_KEY_EVENT_E = 0x08,
+ TCA8418_REG_KEY_EVENT_F = 0x09,
+ TCA8418_REG_KEY_EVENT_G = 0x0A,
+ TCA8418_REG_KEY_EVENT_H = 0x0B,
+ TCA8418_REG_KEY_EVENT_I = 0x0C,
+ TCA8418_REG_KEY_EVENT_J = 0x0D,
+ TCA8418_REG_KP_LCK_TIMER = 0x0E,
+ TCA8418_REG_UNLOCK_1 = 0x0F,
+ TCA8418_REG_UNLOCK_2 = 0x10,
+ TCA8418_REG_GPIO_INT_STAT_1 = 0x11,
+ TCA8418_REG_GPIO_INT_STAT_2 = 0x12,
+ TCA8418_REG_GPIO_INT_STAT_3 = 0x13,
+ TCA8418_REG_GPIO_DAT_STAT_1 = 0x14,
+ TCA8418_REG_GPIO_DAT_STAT_2 = 0x15,
+ TCA8418_REG_GPIO_DAT_STAT_3 = 0x16,
+ TCA8418_REG_GPIO_DAT_OUT_1 = 0x17,
+ TCA8418_REG_GPIO_DAT_OUT_2 = 0x18,
+ TCA8418_REG_GPIO_DAT_OUT_3 = 0x19,
+ TCA8418_REG_GPIO_INT_EN_1 = 0x1A,
+ TCA8418_REG_GPIO_INT_EN_2 = 0x1B,
+ TCA8418_REG_GPIO_INT_EN_3 = 0x1C,
+ TCA8418_REG_KP_GPIO_1 = 0x1D,
+ TCA8418_REG_KP_GPIO_2 = 0x1E,
+ TCA8418_REG_KP_GPIO_3 = 0x1F,
+ TCA8418_REG_GPI_EM_1 = 0x20,
+ TCA8418_REG_GPI_EM_2 = 0x21,
+ TCA8418_REG_GPI_EM_3 = 0x22,
+ TCA8418_REG_GPIO_DIR_1 = 0x23,
+ TCA8418_REG_GPIO_DIR_2 = 0x24,
+ TCA8418_REG_GPIO_DIR_3 = 0x25,
+ TCA8418_REG_GPIO_INT_LVL_1 = 0x26,
+ TCA8418_REG_GPIO_INT_LVL_2 = 0x27,
+ TCA8418_REG_GPIO_INT_LVL_3 = 0x28,
+ TCA8418_REG_DEBOUNCE_DIS_1 = 0x29,
+ TCA8418_REG_DEBOUNCE_DIS_2 = 0x2A,
+ TCA8418_REG_DEBOUNCE_DIS_3 = 0x2B,
+ TCA8418_REG_GPIO_PULL_1 = 0x2C,
+ TCA8418_REG_GPIO_PULL_2 = 0x2D,
+ TCA8418_REG_GPIO_PULL_3 = 0x2E
+ };
+
+ // Pin IDs for matrix rows/columns
+ enum TCA8418PinId : uint8_t {
+ TCA8418_ROW0, // Pin ID for row 0
+ TCA8418_ROW1, // Pin ID for row 1
+ TCA8418_ROW2, // Pin ID for row 2
+ TCA8418_ROW3, // Pin ID for row 3
+ TCA8418_ROW4, // Pin ID for row 4
+ TCA8418_ROW5, // Pin ID for row 5
+ TCA8418_ROW6, // Pin ID for row 6
+ TCA8418_ROW7, // Pin ID for row 7
+ TCA8418_COL0, // Pin ID for column 0
+ TCA8418_COL1, // Pin ID for column 1
+ TCA8418_COL2, // Pin ID for column 2
+ TCA8418_COL3, // Pin ID for column 3
+ TCA8418_COL4, // Pin ID for column 4
+ TCA8418_COL5, // Pin ID for column 5
+ TCA8418_COL6, // Pin ID for column 6
+ TCA8418_COL7, // Pin ID for column 7
+ TCA8418_COL8, // Pin ID for column 8
+ TCA8418_COL9 // Pin ID for column 9
+ };
+
+ virtual void pressed(uint8_t key);
+ virtual void released(void);
+
+ virtual void queueEvent(char);
+
+ virtual ~TCA8418KeyboardBase() {}
+
+ protected:
+ // Set the size of the keypad matrix
+ // All other rows and columns are set as inputs.
+ bool matrix(uint8_t rows, uint8_t columns);
+
+ uint8_t keyCount(void) const;
+
+ // Flush all events in the FIFO buffer + GPIO events.
+ uint8_t flush(void);
+
+ // debounce keys.
+ void enableDebounce();
+ void disableDebounce();
+
+ // enable / disable interrupts for matrix and GPI pins
+ void enableInterrupts();
+ void disableInterrupts();
+
+ // ignore key events when FIFO buffer is full or not.
+ void enableMatrixOverflow();
+ void disableMatrixOverflow();
+
+ uint8_t digitalRead(uint8_t pinnum) const;
+ bool digitalWrite(uint8_t pinnum, uint8_t level);
+ bool pinMode(uint8_t pinnum, uint8_t mode);
+ bool pinIRQMode(uint8_t pinnum, uint8_t mode); // MODE FALLING or RISING
+ uint8_t readRegister(uint8_t reg) const;
+ void writeRegister(uint8_t reg, uint8_t value);
+
+ protected:
+ uint8_t rows;
+ uint8_t columns;
+ KeyState state;
+ String queue;
+
+ private:
+ TwoWire *m_wire;
+ uint8_t m_addr;
+ i2c_com_fptr_t readCallback;
+ i2c_com_fptr_t writeCallback;
+};
diff --git a/src/input/TDeckProKeyboard.cpp b/src/input/TDeckProKeyboard.cpp
new file mode 100644
index 000000000..098e0804a
--- /dev/null
+++ b/src/input/TDeckProKeyboard.cpp
@@ -0,0 +1,196 @@
+#if defined(T_DECK_PRO)
+
+#include "TDeckProKeyboard.h"
+
+#define _TCA8418_COLS 10
+#define _TCA8418_ROWS 4
+#define _TCA8418_NUM_KEYS 35
+
+#define _TCA8418_MULTI_TAP_THRESHOLD 1500
+
+using Key = TCA8418KeyboardBase::TCA8418Key;
+
+constexpr uint8_t modifierRightShiftKey = 31 - 1; // keynum -1
+constexpr uint8_t modifierRightShift = 0b0001;
+constexpr uint8_t modifierLeftShiftKey = 35 - 1;
+constexpr uint8_t modifierLeftShift = 0b0001;
+constexpr uint8_t modifierSymKey = 32 - 1;
+constexpr uint8_t modifierSym = 0b0010;
+constexpr uint8_t modifierAltKey = 30 - 1;
+constexpr uint8_t modifierAlt = 0b0100;
+
+// Num chars per key, Modulus for rotating through characters
+static uint8_t TDeckProTapMod[_TCA8418_NUM_KEYS] = {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5};
+
+static unsigned char TDeckProTapMap[_TCA8418_NUM_KEYS][5] = {
+ {'p', 'P', '@', 0x00, Key::SEND_PING},
+ {'o', 'O', '+'},
+ {'i', 'I', '-'},
+ {'u', 'U', '_'},
+ {'y', 'Y', ')'},
+ {'t', 'T', '(', 0x00, Key::TAB},
+ {'r', 'R', '3'},
+ {'e', 'E', '2', 0x00, Key::UP},
+ {'w', 'W', '1'},
+ {'q', 'Q', '#', 0x00, Key::ESC}, // p, o, i, u, y, t, r, e, w, q
+ {Key::BSP, 0x00, 0x00},
+ {'l', 'L', '"'},
+ {'k', 'K', '\''},
+ {'j', 'J', ';'},
+ {'h', 'H', ':'},
+ {'g', 'G', '/', 0x00, Key::GPS_TOGGLE},
+ {'f', 'F', '6', 0x00, Key::RIGHT},
+ {'d', 'D', '5'},
+ {'s', 'S', '4', 0x00, Key::LEFT},
+ {'a', 'A', '*'}, // bsp, l, k, j, h, g, f, d, s, a
+ {0x0d, 0x00, 0x00},
+ {'$', 0x00, 0x00},
+ {'m', 'M', '.', 0x00, Key::MUTE_TOGGLE},
+ {'n', 'N', ','},
+ {'b', 'B', '!', 0x00, Key::BL_TOGGLE},
+ {'v', 'V', '?'},
+ {'c', 'C', '9'},
+ {'x', 'X', '8', 0x00, Key::DOWN},
+ {'z', 'Z', '7'},
+ {0x00, 0x00, 0x00}, // Ent, $, m, n, b, v, c, x, z, alt
+ {0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00},
+ {0x20, 0x00, 0x00},
+ {0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00} // R_Shift, sym, space, mic, L_Shift
+};
+
+TDeckProKeyboard::TDeckProKeyboard()
+ : TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(-1), next_key(-1),
+ last_tap(0L), char_idx(0), tap_interval(0)
+{
+}
+
+void TDeckProKeyboard::reset()
+{
+ TCA8418KeyboardBase::reset();
+ pinMode(KB_BL_PIN, OUTPUT);
+ setBacklight(false);
+}
+
+// handle multi-key presses (shift and alt)
+void TDeckProKeyboard::trigger()
+{
+ uint8_t count = keyCount();
+ if (count == 0)
+ return;
+ for (uint8_t i = 0; i < count; ++i) {
+ uint8_t k = readRegister(TCA8418_REG_KEY_EVENT_A + i);
+ uint8_t key = k & 0x7F;
+ if (k & 0x80) {
+ pressed(key);
+ } else {
+ released();
+ state = Idle;
+ }
+ }
+}
+
+void TDeckProKeyboard::pressed(uint8_t key)
+{
+ if (state == Init || state == Busy) {
+ return;
+ }
+ if (modifierFlag && (millis() - last_modifier_time > _TCA8418_MULTI_TAP_THRESHOLD)) {
+ modifierFlag = 0;
+ }
+
+ uint8_t next_key = 0;
+ int row = (key - 1) / 10;
+ int col = (key - 1) % 10;
+
+ if (row >= _TCA8418_ROWS || col >= _TCA8418_COLS) {
+ return; // Invalid key
+ }
+
+ next_key = row * _TCA8418_COLS + col;
+ state = Held;
+
+ uint32_t now = millis();
+ tap_interval = now - last_tap;
+
+ updateModifierFlag(next_key);
+ if (isModifierKey(next_key)) {
+ last_modifier_time = now;
+ }
+
+ if (tap_interval < 0) {
+ last_tap = 0;
+ state = Busy;
+ return;
+ }
+
+ if (next_key != last_key || tap_interval > _TCA8418_MULTI_TAP_THRESHOLD) {
+ char_idx = 0;
+ } else {
+ char_idx += 1;
+ }
+
+ last_key = next_key;
+ last_tap = now;
+}
+
+void TDeckProKeyboard::released()
+{
+ if (state != Held) {
+ return;
+ }
+
+ if (last_key < 0 || last_key >= _TCA8418_NUM_KEYS) {
+ last_key = -1;
+ state = Idle;
+ return;
+ }
+
+ uint32_t now = millis();
+ last_tap = now;
+
+ if (TDeckProTapMap[last_key][modifierFlag % TDeckProTapMod[last_key]] == Key::BL_TOGGLE) {
+ toggleBacklight();
+ return;
+ }
+
+ queueEvent(TDeckProTapMap[last_key][modifierFlag % TDeckProTapMod[last_key]]);
+ if (isModifierKey(last_key) == false)
+ modifierFlag = 0;
+}
+
+void TDeckProKeyboard::setBacklight(bool on)
+{
+ if (on) {
+ digitalWrite(KB_BL_PIN, HIGH);
+ } else {
+ digitalWrite(KB_BL_PIN, LOW);
+ }
+}
+
+void TDeckProKeyboard::toggleBacklight(void)
+{
+ digitalWrite(KB_BL_PIN, !digitalRead(KB_BL_PIN));
+}
+
+void TDeckProKeyboard::updateModifierFlag(uint8_t key)
+{
+ if (key == modifierRightShiftKey) {
+ modifierFlag ^= modifierRightShift;
+ } else if (key == modifierLeftShiftKey) {
+ modifierFlag ^= modifierLeftShift;
+ } else if (key == modifierSymKey) {
+ modifierFlag ^= modifierSym;
+ } else if (key == modifierAltKey) {
+ modifierFlag ^= modifierAlt;
+ }
+}
+
+bool TDeckProKeyboard::isModifierKey(uint8_t key)
+{
+ return (key == modifierRightShiftKey || key == modifierLeftShiftKey || key == modifierAltKey || key == modifierSymKey);
+}
+
+#endif // T_DECK_PRO
\ No newline at end of file
diff --git a/src/input/TDeckProKeyboard.h b/src/input/TDeckProKeyboard.h
new file mode 100644
index 000000000..617f3f20b
--- /dev/null
+++ b/src/input/TDeckProKeyboard.h
@@ -0,0 +1,27 @@
+#include "TCA8418KeyboardBase.h"
+
+class TDeckProKeyboard : public TCA8418KeyboardBase
+{
+ public:
+ TDeckProKeyboard();
+ void reset(void) override;
+ void trigger(void) override;
+ void setBacklight(bool on) override;
+
+ protected:
+ void pressed(uint8_t key) override;
+ void released(void) override;
+
+ void updateModifierFlag(uint8_t key);
+ bool isModifierKey(uint8_t key);
+ void toggleBacklight(void);
+
+ private:
+ uint8_t modifierFlag; // Flag to indicate if a modifier key is pressed
+ uint32_t last_modifier_time; // Timestamp of the last modifier key press
+ int8_t last_key;
+ int8_t next_key;
+ uint32_t last_tap;
+ uint8_t char_idx;
+ int32_t tap_interval;
+};
diff --git a/src/input/TLoraPagerKeyboard.h b/src/input/TLoraPagerKeyboard.h
new file mode 100644
index 000000000..d31b05978
--- /dev/null
+++ b/src/input/TLoraPagerKeyboard.h
@@ -0,0 +1,12 @@
+#include "TCA8418KeyboardBase.h"
+
+class TLoraPagerKeyboard : public TCA8418KeyboardBase
+{
+ public:
+ TLoraPagerKeyboard();
+ void setBacklight(bool on) override{};
+
+ protected:
+ void pressed(uint8_t key) override{};
+ void released(void) override{};
+};
diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp
index 70e9e4365..1e8fd52b4 100644
--- a/src/input/kbI2cBase.cpp
+++ b/src/input/kbI2cBase.cpp
@@ -3,10 +3,26 @@
#include "detect/ScanI2C.h"
#include "detect/ScanI2CTwoWire.h"
+#if defined(T_DECK_PRO)
+#include "TDeckProKeyboard.h"
+#elif defined(T_LORA_PAGER)
+#include "TLoraPagerKeyboard.h"
+#else
+#include "TCA8418Keyboard.h"
+#endif
+
extern ScanI2C::DeviceAddress cardkb_found;
extern uint8_t kb_model;
-KbI2cBase::KbI2cBase(const char *name) : concurrency::OSThread(name)
+KbI2cBase::KbI2cBase(const char *name)
+ : concurrency::OSThread(name),
+#if defined(T_DECK_PRO)
+ TCAKeyboard(*(new TDeckProKeyboard()))
+#elif defined(T_LORA_PAGER)
+ TCAKeyboard(*(new TLoraPagerKeyboard()))
+#else
+ TCAKeyboard(*(new TCA8418Keyboard()))
+#endif
{
this->_originName = name;
}
@@ -43,8 +59,8 @@ int32_t KbI2cBase::runOnce()
if (cardkb_found.address == MPR121_KB_ADDR) {
MPRkeyboard.begin(MPR121_KB_ADDR, &Wire1);
}
- if (cardkb_found.address == XPOWERS_AXP192_AXP2101_ADDRESS) {
- TCAKeyboard.begin(XPOWERS_AXP192_AXP2101_ADDRESS, &Wire1);
+ if (cardkb_found.address == TCA8418_KB_ADDR) {
+ TCAKeyboard.begin(TCA8418_KB_ADDR, &Wire1);
}
break;
#endif
@@ -58,8 +74,8 @@ int32_t KbI2cBase::runOnce()
if (cardkb_found.address == MPR121_KB_ADDR) {
MPRkeyboard.begin(MPR121_KB_ADDR, &Wire);
}
- if (cardkb_found.address == XPOWERS_AXP192_AXP2101_ADDRESS) {
- TCAKeyboard.begin(XPOWERS_AXP192_AXP2101_ADDRESS, &Wire);
+ if (cardkb_found.address == TCA8418_KB_ADDR) {
+ TCAKeyboard.begin(TCA8418_KB_ADDR, &Wire);
}
break;
case ScanI2C::NO_I2C:
@@ -241,42 +257,66 @@ int32_t KbI2cBase::runOnce()
e.kbchar = 0x00;
e.source = this->_originName;
switch (nextEvent) {
- case _TCA8418_NONE:
+ case TCA8418KeyboardBase::NONE:
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
e.kbchar = 0x00;
break;
- case _TCA8418_REBOOT:
+ case TCA8418KeyboardBase::REBOOT:
e.inputEvent = ANYKEY;
e.kbchar = INPUT_BROKER_MSG_REBOOT;
break;
- case _TCA8418_LEFT:
+ case TCA8418KeyboardBase::LEFT:
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
e.kbchar = 0x00;
break;
- case _TCA8418_UP:
+ case TCA8418KeyboardBase::UP:
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
e.kbchar = 0x00;
break;
- case _TCA8418_DOWN:
+ case TCA8418KeyboardBase::DOWN:
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
e.kbchar = 0x00;
break;
- case _TCA8418_RIGHT:
+ case TCA8418KeyboardBase::RIGHT:
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
e.kbchar = 0x00;
break;
- case _TCA8418_BSP:
+ case TCA8418KeyboardBase::BSP:
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
e.kbchar = 0x08;
break;
- case _TCA8418_SELECT:
+ case TCA8418KeyboardBase::SELECT:
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
e.kbchar = 0x0d;
break;
- case _TCA8418_ESC:
+ case TCA8418KeyboardBase::ESC:
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
e.kbchar = 0x1b;
break;
+ case 0x9E:
+ e.inputEvent = ANYKEY;
+ e.kbchar = INPUT_BROKER_MSG_GPS_TOGGLE;
+ break;
+ case 0xAF:
+ e.inputEvent = ANYKEY;
+ e.kbchar = INPUT_BROKER_MSG_SEND_PING;
+ break;
+ case 0xAC:
+ e.inputEvent = ANYKEY;
+ e.kbchar = INPUT_BROKER_MSG_MUTE_TOGGLE;
+ break;
+ case 0xAA:
+ e.inputEvent = ANYKEY;
+ e.kbchar = INPUT_BROKER_MSG_BLUETOOTH_TOGGLE;
+ break;
+ case 0xAB:
+ e.inputEvent = ANYKEY;
+ e.kbchar = INPUT_BROKER_MSG_BLUETOOTH_TOGGLE;
+ break;
+ case 0x09:
+ e.inputEvent = ANYKEY;
+ e.kbchar = 0x09; // Tab
+ break;
default:
if (nextEvent > 127) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
@@ -291,6 +331,7 @@ int32_t KbI2cBase::runOnce()
LOG_DEBUG("TCA8418 Notifying: %i Char: %c", e.inputEvent, e.kbchar);
this->notifyObservers(&e);
}
+ TCAKeyboard.trigger();
}
break;
}
diff --git a/src/input/kbI2cBase.h b/src/input/kbI2cBase.h
index 8193433fe..af7602979 100644
--- a/src/input/kbI2cBase.h
+++ b/src/input/kbI2cBase.h
@@ -3,10 +3,11 @@
#include "BBQ10Keyboard.h"
#include "InputBroker.h"
#include "MPR121Keyboard.h"
-#include "TCA8418Keyboard.h"
#include "Wire.h"
#include "concurrency/OSThread.h"
+class TCA8418KeyboardBase;
+
class KbI2cBase : public Observable, public concurrency::OSThread
{
public:
@@ -22,6 +23,6 @@ class KbI2cBase : public Observable, public concurrency::OST
BBQ10Keyboard Q10keyboard;
MPR121Keyboard MPRkeyboard;
- TCA8418Keyboard TCAKeyboard;
+ TCA8418KeyboardBase &TCAKeyboard;
bool is_sym = false;
};
\ No newline at end of file