207 lines
6.5 KiB
C++

#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();
//delayMicroseconds(100);
}
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)
this_update_micros = 0; //micros();
delta_micros = this_update_micros - last_update_micros;
// if there is a new event
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;
}