mirror of
https://github.com/meshtastic/firmware.git
synced 2025-07-30 02:15:41 +00:00

* initial draft * fix touchscreen * fix touchscreen * optimize GPS * battery management * cleanup comments * enable vibration motor * refactored TCA8418Keyboard * update HW_VENDOR id * manual fixes after merge * fix keyboard/BQ27220 detection * add BQ27220 * modify charge voltage and current * update XpowerLib * design capacity * try-fix charge behavior * improve Vbus detection * moved variant into esp32s3 folder --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
372 lines
9.6 KiB
C++
372 lines
9.6 KiB
C++
// Based on the MPR121 Keyboard and Adafruit TCA8418 library
|
|
|
|
#include "TCA8418KeyboardBase.h"
|
|
#include "configuration.h"
|
|
|
|
#include <Arduino.h>
|
|
|
|
// 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);
|
|
}
|
|
} |