Tasmota/lib/lib_display/UDisplay/uDisplay_touch.cpp

576 lines
13 KiB
C++

#include "uDisplay.h"
#include "uDisplay_config.h"
#ifdef USE_UNIVERSAL_TOUCH
// ===== Touch IRQ Handler =====
uint8_t ut_irq_flg;
void IRAM_ATTR ut_touch_irq(void) {
ut_irq_flg = 1;
}
// ===== Touch Initialization =====
bool uDisplay::utouch_Init(char **name) {
*name = ut_name;
if (ut_init_code) {
if (ut_reset >= 0) {
pinMode(ut_reset, OUTPUT);
digitalWrite(ut_reset, HIGH);
delay(10);
digitalWrite(ut_reset, LOW);
delay(5);
digitalWrite(ut_reset, HIGH);
delay(10);
}
if (ut_irq >= 0) {
pinMode(ut_irq, INPUT);
attachInterrupt(ut_irq, ut_touch_irq, FALLING);
}
if (ut_wire) {
// I2C touch - no SPI needed
ut_spi = nullptr;
} else if (spiController && ut_spi_nr == spiController->spi_config.bus_nr) {
// SPI touch using same bus as display
ut_spi = spiController->getSPI();
} else {
// SPI touch using different bus or display doesn't use SPI
#ifdef ESP32
ut_spi = SpiBegin(ut_spi_nr);
#endif
}
return ut_execute(ut_init_code);
}
return false;
}
// ===== Touch Detection =====
uint16_t uDisplay::touched(void) {
if (ut_irq >= 0) {
if (!ut_irq_flg) {
return false;
}
ut_irq_flg = 0;
}
if (ut_touch_code) {
return ut_execute(ut_touch_code);
}
return 0;
}
// ===== Touch Coordinate Reading =====
int16_t uDisplay::getPoint_x(void) {
if (ut_getx_code) {
return ut_execute(ut_getx_code);
}
return 0;
}
int16_t uDisplay::getPoint_y(void) {
if (ut_gety_code) {
return ut_execute(ut_gety_code);
}
return 0;
}
// ===== Touch Command Execution =====
// ===== Touch Code Translation =====
void uDisplay::ut_trans(char **sp, uint8_t **code) {
char *cp = *sp;
uint16_t wval;
uint8_t tmp_code[64];
uint8_t *ut_code = tmp_code;
while (*cp) {
if (*cp == ':' || *cp == '#') {
break;
}
if (*cp == ';') {
// skip comment line
while (*cp) {
if (*cp == '\n') {
cp++;
break;
}
cp++;
}
}
if (!strncmp(cp, "RDWM", 4)) {
// read word many
*ut_code++ = UT_RDWM;
wval = ut_par(&cp, 0);
*ut_code++ = wval>>8;
*ut_code++ = wval;
wval = ut_par(&cp, 1);
if (wval > sizeof(ut_array)) {
wval = sizeof(ut_array);
}
*ut_code++ = wval;
} else if (!strncmp(cp, "RDW", 3)) {
// read word one
*ut_code++ = UT_RDW;
wval = ut_par(&cp, 0);
*ut_code++ = wval>>8;
*ut_code++ = wval;
} else if (!strncmp(cp, "RDM", 3)) {
// read many
*ut_code++ = UT_RDM;
*ut_code++ = ut_par(&cp, 0);
wval = ut_par(&cp, 1);
if (wval > sizeof(ut_array)) {
wval = sizeof(ut_array);
}
*ut_code++ = wval;
} else if (!strncmp(cp, "RD", 2)) {
// read one
*ut_code++ = UT_RD;
*ut_code++ = ut_par(&cp, 0);
} else if (!strncmp(cp, "CPR", 3)) {
// cmp and set
*ut_code++ = UT_CPR;
*ut_code++ = ut_par(&cp, 0);
} else if (!strncmp(cp, "CPM", 3)) {
// cmp multiple and set
*ut_code++ = UT_CPM;
uint8_t num = ut_par(&cp, 0);
*ut_code++ = num;
for (uint32_t cnt = 0; cnt < num; cnt++) {
*ut_code++ = ut_par(&cp, 0);
}
} else if (!strncmp(cp, "CP", 2)) {
// cmp and set
*ut_code++ = UT_CP;
*ut_code++ = ut_par(&cp, 0);
} else if (!strncmp(cp, "RTF", 3)) {
// return when false
*ut_code++ = UT_RTF;
} else if (!strncmp(cp, "RTT", 3)) {
// return when true
*ut_code++ = UT_RTT;
} else if (!strncmp(cp, "MVB", 3)) {
// move
*ut_code++ = UT_MVB;
*ut_code++ = ut_par(&cp, 1);
*ut_code++ = ut_par(&cp, 1);
} else if (!strncmp(cp, "MV", 2)) {
// move
*ut_code++ = UT_MV;
*ut_code++ = ut_par(&cp, 1);
*ut_code++ = ut_par(&cp, 1);
} else if (!strncmp(cp, "RT", 2)) {
// return status
*ut_code++ = UT_RT;
} else if (!strncmp(cp, "WRW", 3)) {
*ut_code++ = UT_WRW;
wval = ut_par(&cp, 0);
*ut_code++ = wval>>8;
*ut_code++ = wval;
wval = ut_par(&cp, 0);
*ut_code++ = wval;
} else if (!strncmp(cp, "WR", 2)) {
*ut_code++ = UT_WR;
wval = ut_par(&cp, 0);
*ut_code++ = wval;
wval = ut_par(&cp, 0);
*ut_code++ = wval;
} else if (!strncmp(cp, "AND", 3)) {
*ut_code++ = UT_AND;
wval = ut_par(&cp, 0);
*ut_code++ = wval >> 8;
*ut_code++ = wval;
} else if (!strncmp(cp, "SCL", 3)) {
*ut_code++ = UT_SCALE;
wval = ut_par(&cp, 1);
*ut_code++ = wval >> 8;
*ut_code++ = wval;
uint32_t lval = ut_par(&cp, 2);
*ut_code++ = lval >> 24;
*ut_code++ = lval >> 16;
*ut_code++ = lval >> 8;
*ut_code++ = lval;
} else if (!strncmp(cp, "LIM", 3)) {
*ut_code++ = UT_LIM;
wval = ut_par(&cp, 1);
*ut_code++ = wval >> 8;
*ut_code++ = wval;
} else if (!strncmp(cp, "GSRT", 4)) {
*ut_code++ = UT_GSRT;
wval = ut_par(&cp, 1);
*ut_code++ = wval >> 8;
*ut_code++ = wval;
} else if (!strncmp(cp, "XPT", 3)) {
*ut_code++ = UT_XPT;
wval = ut_par(&cp, 1);
*ut_code++ = wval >> 8;
*ut_code++ = wval;
} else if (!strncmp(cp, "DBG", 3)) {
*ut_code++ = UT_DBG;
wval = ut_par(&cp, 1);
*ut_code++ = wval;
}
cp++;
}
*ut_code++ = UT_END;
*sp = cp - 1;
uint16_t memsize = (uint32_t)ut_code - (uint32_t)tmp_code;
// allocate memory
uint8_t *mp = (uint8_t*)malloc(memsize + 2);
if (mp) {
memmove(mp, tmp_code, memsize);
*code = mp;
}
}
// ===== Touch Parameter Parsing =====
uint32_t uDisplay::ut_par(char **lp, uint32_t mode) {
char *cp = *lp;
while (*cp != ' ') {
if (!cp) break;
cp++;
}
cp++;
uint32_t result;
if (!mode) {
// hex
result = strtol(cp, &cp, 16);
} else if (mode == 1) {
// word
result = strtol(cp, &cp, 10);
} else {
// float as 32bit integer
float fval = CharToFloat(cp);
result = *(uint32_t*)&fval;
while (*cp) {
if (*cp == ' ' || *cp =='\n') {
break;
}
cp++;
}
}
*lp = cp;
return result;
}
int16_t uDisplay::ut_execute(uint8_t *ut_code) {
int16_t result = 0;
uint8_t iob, len;
uint16_t wval;
while (*ut_code != UT_END) {
iob = *ut_code++;
switch (iob) {
case UT_RD:
// read 1 byte
ut_code = ut_rd(ut_code, 1, 1);
break;
case UT_RDM:
// read multiple bytes
ut_code = ut_rd(ut_code, 2, 1);
break;
case UT_RDW:
// read 1 byte
ut_code = ut_rd(ut_code, 1, 2);
break;
case UT_RDWM:
// read multiple bytes
ut_code = ut_rd(ut_code, 2, 2);
break;
case UT_WR:
ut_code = ut_wr(ut_code, 1);
break;
case UT_WRW:
ut_code = ut_wr(ut_code, 2);
break;
case UT_CP:
// compare
iob = *ut_code++;
result = (iob == ut_array[0]);
break;
case UT_CPM:
// compare multiple
len = *ut_code++;
result = 0;
for (uint32_t cnt = 0; cnt < len; cnt++) {
iob = *ut_code++;
result |= (iob == ut_array[0]);
}
break;
case UT_CPR:
// compare
iob = *ut_code++;
result = (iob == result);
break;
case UT_RTF:
// return when false
if (result == 0) {
return false;
}
break;
case UT_RTT:
// return when true
if (result > 0) {
return false;
}
break;
case UT_MVB:
// move byte from index to high or low result
wval = *ut_code++;
iob = *ut_code++;
if (wval == 0) {
result &= 0xff00;
result |= ut_array[iob];
} else {
result &= 0x00ff;
result |= (ut_array[iob] << 8);
}
break;
case UT_MV:
// move
// source
result = *ut_code++;
iob = *ut_code++;
if (iob == 1) {
result = ut_array[result];
} else if (iob == 2) {
iob = result;
result = ut_array[iob] << 8;
result |= ut_array[iob + 1];
} else {
iob = result;
result = ut_array[iob + 1] << 8;
result |= ut_array[iob];
}
result &= 0xfff;
break;
case UT_AND:
// and
wval = *ut_code++ << 8;
wval |= *ut_code++;
result &= wval;
break;
case UT_SCALE:
{
wval = *ut_code++ << 8;
wval |= *ut_code++;
result -= wval;
uint32_t lval = (uint32_t)*ut_code++ << 24;
lval |= (uint32_t)*ut_code++ << 16;
lval |= (uint32_t)*ut_code++ << 8;
lval |= (uint32_t)*ut_code++;
float fval = *(float*)&lval;
fval *= (float)result;
result = fval;
}
break;
case UT_LIM:
wval = *ut_code++ << 8;
wval |= *ut_code++;
if (result > wval) {
result = wval;
}
break;
case UT_RT:
// result
return result;
break;
case UT_GSRT:
#ifdef UDISPLAY_I80
{
// Simple resistive touch using I80 data pins
uint32_t val = get_sr_touch(panel_config->i80.data_pins_low[1], // XP
panel_config->i80.cs_pin, // XM
panel_config->i80.dc_pin, // YP
panel_config->i80.data_pins_low[0]); // YM
if (val == 0) {
return false;
}
uint16_t xp = val >> 16;
uint16_t yp = val;
wval = *ut_code++ << 8;
wval |= *ut_code++;
if (xp > wval && yp > wval) {
ut_array[0] = val >> 24;
ut_array[1] = val >> 16;
ut_array[2] = val >> 8;
ut_array[3] = val;
return true;
}
return false;
}
#endif // UDISPLAY_I80
break;
case UT_XPT:
wval = *ut_code++ << 8;
wval |= *ut_code++;
result = ut_XPT2046(wval);
break;
case UT_DBG:
// debug show result
wval = *ut_code++;
AddLog(LOG_LEVEL_INFO, PSTR("UTDBG %d: %02x : %02x,%02x,%02x,%02x"), wval, result, ut_array[0], ut_array[1], ut_array[2], ut_array[3]);
break;
case UT_END:
break;
}
}
return result;
}
// ===== Low-Level Touch Communication =====
uint8_t *uDisplay::ut_rd(uint8_t *iop, uint32_t len, uint32_t amode) {
if (ut_wire) {
// i2c mode
ut_wire->beginTransmission(ut_i2caddr);
ut_wire->write(*iop++);
if (amode == 2) {
ut_wire->write(*iop++);
}
ut_wire->endTransmission(false);
if (len > 1) {
len = *iop++;
}
ut_wire->requestFrom(ut_i2caddr, (size_t)len);
uint8_t index = 0;
while (ut_wire->available()) {
ut_array[index++] = ut_wire->read();
}
} else {
// spi mode
if (amode == 1) {
uint16_t val = *iop++;
uint16_t len = *iop++;
if (ut_spi) {
digitalWrite(ut_spi_cs, LOW);
ut_spi->beginTransaction(ut_spiSettings);
ut_spi->transfer(val);
val = ut_spi->transfer16(0);
ut_spi->endTransaction();
ut_array[len] = val << 8;
ut_array[len + 1] = val;
digitalWrite(ut_spi_cs, HIGH);
}
}
}
return iop;
}
uint8_t *uDisplay::ut_wr(uint8_t *iop, uint32_t amode) {
if (ut_wire) {
// i2c mode
ut_wire->beginTransmission(ut_i2caddr);
ut_wire->write(*iop++);
if (amode == 2) {
ut_wire->write(*iop++);
}
ut_wire->write(*iop++);
ut_wire->endTransmission(true);
} else {
// spi mode
}
return iop;
}
// ===== XPT2046 Touch Controller =====
uint16_t uDisplay::ut_XPT2046(uint16_t z_th) {
uint16_t result = 0;
if (ut_spi) {
int16_t data[6];
ut_spi->beginTransaction(ut_spiSettings);
digitalWrite(ut_spi_cs, LOW);
ut_spi->transfer(0xB1 /* Z1 */);
int16_t z1 = ut_spi->transfer16(0xC1 /* Z2 */) >> 3;
int16_t z = z1 + 4095;
int16_t z2 = ut_spi->transfer16(0x91 /* X */) >> 3;
z -= z2;
if (z >= z_th) {
ut_spi->transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy
data[0] = ut_spi->transfer16(0xD1 /* Y */) >> 3;
data[1] = ut_spi->transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements
data[2] = ut_spi->transfer16(0xD1 /* Y */) >> 3;
data[3] = ut_spi->transfer16(0x91 /* X */) >> 3;
result = 1;
} else {
data[0] = data[1] = data[2] = data[3] = 0;
}
data[4] = ut_spi->transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down
data[5] = ut_spi->transfer16(0) >> 3;
digitalWrite(ut_spi_cs, HIGH);
ut_spi->endTransaction();
uint16_t x = besttwoavg(data[0], data[2], data[4]);
uint16_t y = besttwoavg(data[1], data[3], data[5]);
ut_array[0] = x >> 8;
ut_array[1] = x;
ut_array[2] = y >> 8;
ut_array[3] = y;
}
return result;
}
// ===== Touch Data Processing =====
int16_t uDisplay::besttwoavg(int16_t x, int16_t y, int16_t z) {
int16_t da, db, dc;
int16_t reta = 0;
if (x > y) da = x - y; else da = y - x;
if (x > z) db = x - z; else db = z - x;
if (z > y) dc = z - y; else dc = y - z;
if (da <= db && da <= dc) reta = (x + y) >> 1;
else if (db <= da && db <= dc) reta = (x + z) >> 1;
else reta = (y + z) >> 1;
return (reta);
}
#endif // USE_UNIVERSAL_TOUCH