Implemented LilyGO T-Lora Pager (#295)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "Source"
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility
|
||||
)
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
Reference in New Issue
Block a user