#include "Tca8418.h" #include #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; }