Implemented LilyGO T-Lora Pager (#295)

This commit is contained in:
flip
2025-07-23 20:45:57 +00:00
committed by GitHub
parent ab4cf79a47
commit 00b62a2831
36 changed files with 2013 additions and 9 deletions
+5
View File
@@ -0,0 +1,5 @@
idf_component_register(
SRC_DIRS "Source"
INCLUDE_DIRS "Source"
REQUIRES Tactility
)
+18
View File
@@ -0,0 +1,18 @@
Copyright 2023 Anthony DiGirolamo
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+4
View File
@@ -0,0 +1,4 @@
# TCA8418 I2C Controlled Keypad Scan IC With Integrated ESD Protection
[Datasheet](https://www.ti.com/lit/ds/symlink/tca8418.pdf?ts=1751500237439)
[Original implementation](https://github.com/AnthonyDiGirolamo/i2c-thumb-keyboard/tree/master) by Anthony DiGirolamo
+202
View File
@@ -0,0 +1,202 @@
#include "Tca8418.h"
#include <Tactility/Log.h>
#define TAG "tca8418"
namespace registers {
static const uint8_t CFG = 0x01U;
static const uint8_t KP_GPIO1 = 0x1DU;
static const uint8_t KP_GPIO2 = 0x1EU;
static const uint8_t KP_GPIO3 = 0x1FU;
static const uint8_t KEY_EVENT_A = 0x04U;
static const uint8_t KEY_EVENT_B = 0x05U;
static const uint8_t KEY_EVENT_C = 0x06U;
static const uint8_t KEY_EVENT_D = 0x07U;
static const uint8_t KEY_EVENT_E = 0x08U;
static const uint8_t KEY_EVENT_F = 0x09U;
static const uint8_t KEY_EVENT_G = 0x0AU;
static const uint8_t KEY_EVENT_H = 0x0BU;
static const uint8_t KEY_EVENT_I = 0x0CU;
static const uint8_t KEY_EVENT_J = 0x0DU;
} // namespace registers
void Tca8418::init(uint8_t numrows, uint8_t numcols) {
/*
* | ADDRESS | REGISTER NAME | REGISTER DESCRIPTION | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0 |
* |---------+---------------+----------------------+------+------+------+------+------+------+------+------|
* | 0x1D | KP_GPIO1 | Keypad/GPIO Select 1 | ROW7 | ROW6 | ROW5 | ROW4 | ROW3 | ROW2 | ROW1 | ROW0 |
* | 0x1E | KP_GPIO2 | Keypad/GPIO Select 2 | COL7 | COL6 | COL5 | COL4 | COL3 | COL2 | COL1 | COL0 |
* | 0x1F | KP_GPIO3 | Keypad/GPIO Select 3 | N/A | N/A | N/A | N/A | N/A | N/A | COL9 | COL8 |
*/
num_rows = numrows;
num_cols = numcols;
// everything enabled in key scan mode
uint8_t enabled_rows = 0x3F;
uint16_t enabled_cols = 0x3FF;
writeRegister8(registers::KP_GPIO1, enabled_rows);
writeRegister8(registers::KP_GPIO2, (uint8_t)(0xFF & enabled_cols));
writeRegister8(registers::KP_GPIO3, (uint8_t)(0x03 & (enabled_cols >> 8)));
/*
* BIT: NAME
*
* 7: AI
* Auto-increment for read and write operations; See below table for more information
* 0 = disabled
* 1 = enabled
*
* 6: GPI_E_CFG
* GPI event mode configuration
* 0 = GPI events are tracked when keypad is locked
* 1 = GPI events are not tracked when keypad is locked
*
* 5: OVR_FLOW_M
* Overflow mode
* 0 = disabled; Overflow data is lost
* 1 = enabled; Overflow data shifts with last event pushing first event out
*
* 4: INT_CFG
* Interrupt configuration
* 0 = processor interrupt remains asserted (or low) if host tries to clear interrupt while there is
* still a pending key press, key release or GPI interrupt
* 1 = processor interrupt is deasserted for 50 μs and reassert with pending interrupts
*
* 3: OVR_FLOW_IEN
* Overflow interrupt enable
* 0 = disabled; INT is not asserted if the FIFO overflows
* 1 = enabled; INT becomes asserted if the FIFO overflows
*
* 2: K_LCK_IEN
* Keypad lock interrupt enable
* 0 = disabled; INT is not asserted after a correct unlock key sequence
* 1 = enabled; INT becomes asserted after a correct unlock key sequence
*
* 1: GPI_IEN
* GPI interrupt enable to host processor
* 0 = disabled; INT is not asserted for a change on a GPI
* 1 = enabled; INT becomes asserted for a change on a GPI
*
* 0: KE_IEN
* Key events interrupt enable to host processor
* 0 = disabled; INT is not asserted when a key event occurs
* 1 = enabled; INT becomes asserted when a key event occurs
*/
// 10111001 xB9 -- fifo overflow enabled
// 10011001 x99 -- fifo overflow disabled
writeRegister8(registers::CFG, 0x99);
clear_released_list();
clear_pressed_list();
}
bool Tca8418::update() {
last_update_micros = this_update_micros;
uint8_t key_code, key_down, key_event, key_row, key_col;
key_event = get_key_event();
// TODO: read gpio R7/R6 status? 0x14 bits 7&6
// read(0x14, &new_keycode)
// TODO: use tick function to get an update delta time
this_update_micros = 0;
delta_micros = this_update_micros - last_update_micros;
if (key_event > 0) {
key_code = key_event & 0x7F;
key_down = (key_event & 0x80) >> 7;
key_row = key_code / num_cols;
key_col = key_code % num_cols;
// always clear the released list
clear_released_list();
if (key_down) {
add_pressed_key(key_row, key_col);
// TODO reject ghosts (assume multiple key presses with the same hold time are ghosts.)
} else {
add_released_key(key_row, key_col);
remove_pressed_key(key_row, key_col);
}
return true;
}
// Increment hold times for pressed keys
for (int i = 0; i < pressed_key_count; i++) {
pressed_list[i].hold_time += delta_micros;
}
return false;
}
void Tca8418::add_pressed_key(uint8_t row, uint8_t col) {
if (pressed_key_count >= KEY_EVENT_LIST_SIZE)
return;
pressed_list[pressed_key_count].row = row;
pressed_list[pressed_key_count].col = col;
pressed_list[pressed_key_count].hold_time = 0;
pressed_key_count++;
}
void Tca8418::add_released_key(uint8_t row, uint8_t col) {
if (released_key_count >= KEY_EVENT_LIST_SIZE)
return;
released_key_count++;
released_list[0].row = row;
released_list[0].col = col;
}
void Tca8418::remove_pressed_key(uint8_t row, uint8_t col) {
if (pressed_key_count == 0)
return;
// delete the pressed key
for (int i = 0; i < pressed_key_count; i++) {
if (pressed_list[i].row == row &&
pressed_list[i].col == col) {
// shift remaining keys left one index
for (int j = i; i < pressed_key_count; j++) {
if (j == KEY_EVENT_LIST_SIZE - 1)
break;
pressed_list[j].row = pressed_list[j + 1].row;
pressed_list[j].col = pressed_list[j + 1].col;
pressed_list[j].hold_time = pressed_list[j + 1].hold_time;
}
break;
}
}
pressed_key_count--;
}
void Tca8418::clear_pressed_list() {
for (int i = 0; i < KEY_EVENT_LIST_SIZE; i++) {
pressed_list[i].row = 255;
pressed_list[i].col = 255;
}
pressed_key_count = 0;
}
void Tca8418::clear_released_list() {
for (int i = 0; i < KEY_EVENT_LIST_SIZE; i++) {
released_list[i].row = 255;
released_list[i].col = 255;
}
released_key_count = 0;
}
uint8_t Tca8418::get_key_event() {
uint8_t new_keycode = 0;
readRegister8(registers::KEY_EVENT_A, new_keycode);
return new_keycode;
}
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include <array>
#include <Tactility/hal/i2c/I2cDevice.h>
#define TCA8418_ADDRESS 0x34U
#define KEY_EVENT_LIST_SIZE 10
class Tca8418 final : public tt::hal::i2c::I2cDevice {
private:
uint8_t tca8418_address;
uint32_t last_update_micros;
uint32_t this_update_micros;
uint8_t new_pressed_keys_count;
void clear_released_list();
void clear_pressed_list();
void add_pressed_key(uint8_t row, uint8_t col);
void add_released_key(uint8_t row, uint8_t col);
void remove_pressed_key(uint8_t row, uint8_t col);
void write(uint8_t register_address, uint8_t data);
bool read(uint8_t register_address, uint8_t* data);
public:
struct PressedKey {
uint8_t row;
uint8_t col;
uint32_t hold_time;
};
struct ReleasedKey {
uint8_t row;
uint8_t col;
};
std::string getName() const final { return "TCA8418"; }
std::string getDescription() const final { return "I2C-controlled keyboard scan IC"; }
explicit Tca8418(i2c_port_t port) : I2cDevice(port, TCA8418_ADDRESS) {
delta_micros = 0;
last_update_micros = 0;
this_update_micros = 0;
}
~Tca8418() {}
uint8_t num_rows;
uint8_t num_cols;
uint32_t delta_micros;
std::array<PressedKey, KEY_EVENT_LIST_SIZE> pressed_list;
std::array<ReleasedKey, KEY_EVENT_LIST_SIZE> released_list;
uint8_t pressed_key_count;
uint8_t released_key_count;
void init(uint8_t numrows, uint8_t numcols);
bool update();
uint8_t get_key_event();
bool button_pressed(uint8_t row, uint8_t button_bit_position);
bool button_released(uint8_t row, uint8_t button_bit_position);
bool button_held(uint8_t row, uint8_t button_bit_position);
};