Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 219 additions & 1 deletion include/graphics/LGFX/LGFX_T_DECK.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#define LGFX_USE_V1
#include "util/ILog.h"
#include <LovyanGFX.hpp>

#ifndef SPI_FREQUENCY
Expand Down Expand Up @@ -66,15 +67,232 @@ class LGFX_Touch : public lgfx::LGFX_Device
BBCapTouch bbct;
};

class GT911_TDECK : public lgfx::Touch_GT911
{
};
class LGFX_TDECK : public LGFX_Touch

#else
// T-Deck specific configuration of GT911 touch driver
class GT911_TDECK : public lgfx::Touch_GT911
{
public:
static constexpr int GT911_I2C_ADDR_1 = 0x14;
static constexpr int GT911_I2C_ADDR_2 = 0x5D;

bool init(void) override
{
bool result = lgfx::Touch_GT911::init();
if (result) {
checkAndConfigureGT911();
}
return result;
}

private:
bool transactionWriteReadWithAddrRetry(const uint8_t *writeBuf, size_t writeLen, uint8_t *readBuf, size_t readLen)
{
auto txrx =
lgfx::i2c::transactionWriteRead(_cfg.i2c_port, _cfg.i2c_addr, writeBuf, writeLen, readBuf, readLen, _cfg.freq);
if (txrx.has_value()) {
return true;
}

int altAddr = (_cfg.i2c_addr == GT911_I2C_ADDR_1) ? GT911_I2C_ADDR_2 : GT911_I2C_ADDR_1;
txrx = lgfx::i2c::transactionWriteRead(_cfg.i2c_port, altAddr, writeBuf, writeLen, readBuf, readLen, _cfg.freq);
if (txrx.has_value()) {
ILOG_DEBUG("GT911 diagnostic read succeeded using alternate I2C address 0x%02X (configured=0x%02X)", altAddr,
_cfg.i2c_addr);
return true;
}
return false;
}

bool transactionWriteWithAddrRetry(const uint8_t *writeBuf, size_t writeLen)
{
auto tx = lgfx::i2c::transactionWrite(_cfg.i2c_port, _cfg.i2c_addr, writeBuf, writeLen, _cfg.freq);
if (tx.has_value()) {
return true;
}

int altAddr = (_cfg.i2c_addr == GT911_I2C_ADDR_1) ? GT911_I2C_ADDR_2 : GT911_I2C_ADDR_1;
tx = lgfx::i2c::transactionWrite(_cfg.i2c_port, altAddr, writeBuf, writeLen, _cfg.freq);
if (tx.has_value()) {
ILOG_DEBUG("GT911 diagnostic write succeeded using alternate I2C address 0x%02X (configured=0x%02X)", altAddr,
_cfg.i2c_addr);
return true;
}
return false;
}

static uint8_t calcGT911Checksum(const uint8_t *buf, size_t len)
{
uint8_t checksum = 0;
for (size_t i = 0; i < len; ++i) {
checksum += buf[i];
}
return static_cast<uint8_t>(~checksum + 1);
}

bool writeGT911ConfigBlock(const uint8_t (&configBlock)[184])
{
constexpr uint16_t configStart = 0x8047;
constexpr size_t maxChunk = 30;
size_t offset = 0;
while (offset < sizeof(configBlock)) {
const size_t chunk = (sizeof(configBlock) - offset > maxChunk) ? maxChunk : (sizeof(configBlock) - offset);
const uint16_t reg = static_cast<uint16_t>(configStart + offset);
uint8_t packet[2 + maxChunk] = {static_cast<uint8_t>((reg >> 8) & 0xFF), static_cast<uint8_t>(reg & 0xFF)};
for (size_t i = 0; i < chunk; ++i) {
packet[2 + i] = configBlock[offset + i];
}
if (!transactionWriteWithAddrRetry(packet, chunk + 2)) {
return false;
}
offset += chunk;
}

uint8_t checksumWrite[3] = {0x80, 0xFF, calcGT911Checksum(configBlock, sizeof(configBlock))};
if (!transactionWriteWithAddrRetry(checksumWrite, sizeof(checksumWrite))) {
return false;
}

uint8_t freshWrite[3] = {0x81, 0x00, 0x01};
return transactionWriteWithAddrRetry(freshWrite, sizeof(freshWrite));
}

bool readGT911ConfigBlock16(uint8_t (&configBlock)[16])
{
constexpr uint16_t configReg = 0x8047;
uint8_t writeBuf[2] = {static_cast<uint8_t>((configReg >> 8) & 0xFF), static_cast<uint8_t>(configReg & 0xFF)};
return transactionWriteReadWithAddrRetry(writeBuf, sizeof(writeBuf), configBlock, sizeof(configBlock));
}

void checkAndConfigureGT911(void)
{
// Log the runtime bus config that LovyanGFX selected during init.
ILOG_DEBUG("GT911 I2C config: port=%d addr=0x%02X sda=%d scl=%d freq=%u", _cfg.i2c_port, _cfg.i2c_addr, _cfg.pin_sda,
_cfg.pin_scl, _cfg.freq);

// READ PRODUCT INFO (HARDWARE ID & FIRMWARE)
String productID = readGT911String(0x8140, 4);
uint16_t fwVersion = readGT911U16(0x8144);
uint16_t xResolution = readGT911U16(0x8146);
uint16_t yResolution = readGT911U16(0x8148);

ILOG_DEBUG("--- GT911 Chip Identification ---");
ILOG_DEBUG("Product ID: %s", productID.c_str());
ILOG_DEBUG("Firmware Version: 0x%04X", fwVersion);
ILOG_DEBUG("Screen Resolution: %u x %u", xResolution, yResolution);

// READ CONFIGURATION PARAMETERS (RAM WORKING MEMORY)
uint8_t configBlockSmall[16] = {0};
if (!readGT911ConfigBlock16(configBlockSmall)) {
ILOG_ERROR("Failed to read GT911 config block at 0x8047");
return;
}

uint8_t configVersion = configBlockSmall[0]; // Register 0x8047
uint8_t moduleSwitch1 = configBlockSmall[6]; // Register 0x804D
uint8_t intTriggerMode = moduleSwitch1 & 0x03; // Lowest 2 bits

static constexpr const char *intModeNames[] = {
"0x00 (Rising Edge Trigger)",
"0x01 (Falling Edge Trigger)",
"0x02 (CONSTANT LOW LEVEL ON TOUCH)",
"0x03 (Constant High Level on Touch)",
};

ILOG_DEBUG("--- GT911 Configuration Diagnostics ---");
ILOG_DEBUG("Config Version (Reg 0x8047): 0x%02X%s", configVersion,
configVersion >= 0x5A ? " <-- WARNING: High version number! Might block lower updates." : " (Normal range)");
ILOG_DEBUG("Module_Switch1 (Reg 0x804D): 0x%02X", moduleSwitch1);
ILOG_INFO("Current TP INT Driver Mode: %s", intModeNames[intTriggerMode]);

if (intTriggerMode == 0x02) {
return;
}

uint8_t configBlock[184] = {0};
uint8_t writeReg[2] = {0x80, 0x47};
if (!transactionWriteReadWithAddrRetry(writeReg, sizeof(writeReg), configBlock, sizeof(configBlock))) {
ILOG_ERROR("Failed to read full GT911 config block for update");
return;
}

configBlock[6] = static_cast<uint8_t>((configBlock[6] & 0xFC) | 0x02);
if (configBlock[0] < 0xFF) {
++configBlock[0];
}

if (!writeGT911ConfigBlock(configBlock)) {
ILOG_ERROR("Failed to write updated GT911 config block");
return;
}

uint8_t verifyBlock[16] = {0};
if (!readGT911ConfigBlock16(verifyBlock)) {
ILOG_ERROR("GT911 config write done, but readback failed");
return;
}

uint8_t verifyVersion = verifyBlock[0];
uint8_t verifyMode = static_cast<uint8_t>(verifyBlock[6] & 0x03);
if (verifyMode == 0x02) {
ILOG_INFO("GT911 TP_INT mode confirmed constant LOW after write (version=0x%02X)", verifyVersion);
} else {
ILOG_ERROR("GT911 TP_INT verification failed: mode=0x%02X (expected 0x02), version=0x%02X", verifyMode,
verifyVersion);
}
}

// Helper function to read a string from GT911 registers
String readGT911String(uint16_t reg, int len)
{
if (len <= 0)
return "";

String result = "";
int offset = 0;
while (offset < len) {
const size_t chunk = ((len - offset) < 16) ? (len - offset) : 16;
const uint16_t addr = reg + offset;
uint8_t writeBuf[2] = {static_cast<uint8_t>((addr >> 8) & 0xFF), static_cast<uint8_t>(addr & 0xFF)};
uint8_t readBuf[16] = {};

if (!transactionWriteReadWithAddrRetry(writeBuf, sizeof(writeBuf), readBuf, chunk))
return "UNKNOWN";

for (size_t i = 0; i < chunk; ++i) {
char c = static_cast<char>(readBuf[i]);
if (c >= 32 && c <= 126)
result += c; // Only printable characters
}

offset += chunk;
}
return result;
}

// Helper function to read a 16-bit little-endian value
uint16_t readGT911U16(uint16_t reg)
{
uint8_t writeBuf[2] = {static_cast<uint8_t>((reg >> 8) & 0xFF), static_cast<uint8_t>(reg & 0xFF)};
uint8_t readBuf[2] = {};
if (!transactionWriteReadWithAddrRetry(writeBuf, sizeof(writeBuf), readBuf, sizeof(readBuf)))
return 0;

return (static_cast<uint16_t>(readBuf[1]) << 8) | readBuf[0];
}
};

class LGFX_TDECK : public lgfx::LGFX_Device
#endif
{
Panel_TDeck _panel_instance;
lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance;
lgfx::Touch_GT911 _touch_instance;
GT911_TDECK _touch_instance;
uint8_t brightness = 153;

public:
Expand Down
4 changes: 2 additions & 2 deletions include/graphics/driver/LGFXDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "util/ILog.h"
#include <functional>

constexpr uint32_t defaultLongPressTime = 600; // ms until long press is detected (lvgl default is 400)
constexpr uint32_t defaultLongPressTime = 700; // ms until long press is detected (lvgl default is 400)
constexpr uint32_t defaultGestureLimit = 10; // x/y diff pixel until a swipe gesture is detected (lvgl default is 50)

constexpr uint32_t defaultScreenTimeout = 30 * 1000;
Expand Down Expand Up @@ -323,7 +323,7 @@ template <class LGFX> void LGFXDriver<LGFX>::init(DeviceGUI *gui)

if (hasTouch()) {
DisplayDriver::touch = lv_indev_create();
DisplayDriver::touch->gesture_limit = defaultGestureLimit;
lv_indev_set_scroll_limit(DisplayDriver::touch, defaultGestureLimit);
lv_indev_set_type(DisplayDriver::touch, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(DisplayDriver::touch, touchpad_read);
lv_indev_set_display(DisplayDriver::touch, this->display);
Expand Down
11 changes: 9 additions & 2 deletions source/graphics/TFT/TFTView_320x240.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4729,7 +4729,10 @@ void TFTView_320x240::addNode(uint32_t nodeNum, uint8_t ch, const char *userShor
lv_obj_set_style_align(ui_Telemetry2Label, LV_ALIGN_TOP_RIGHT, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_align(ui_Telemetry2Label, LV_TEXT_ALIGN_RIGHT, LV_PART_MAIN | LV_STATE_DEFAULT);

// optimisation: hide all four extended labels by default; enable only when set
// optimisation: hide all 6ix extended labels by default; enable only when set
// lv_obj_add_flag(ui_lastHeardLabel, LV_OBJ_FLAG_HIDDEN); // lastHeard
lv_obj_add_flag(ui_BatteryLabel, LV_OBJ_FLAG_HIDDEN); // Autohide battery
lv_obj_add_flag(ui_SignalLabel, LV_OBJ_FLAG_HIDDEN); // Autohide signal/hops
lv_obj_add_flag(ui_PositionLabel, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(ui_Position2Label, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(ui_Telemetry1Label, LV_OBJ_FLAG_HIDDEN);
Expand Down Expand Up @@ -5043,6 +5046,7 @@ void TFTView_320x240::updateMetrics(uint32_t nodeNum, uint32_t bat_level, float
bat_level = std::min(bat_level, (uint32_t)100);
sprintf(buf, "%d%% %0.2fV", bat_level, voltage);
lv_label_set_text(it->second->LV_OBJ_IDX(node_bat_idx), buf);
lv_obj_remove_flag(it->second->LV_OBJ_IDX(node_bat_idx), LV_OBJ_FLAG_HIDDEN);
}
}
}
Expand Down Expand Up @@ -5118,6 +5122,7 @@ void TFTView_320x240::updateSignalStrength(uint32_t nodeNum, int32_t rssi, float
}
lv_label_set_text(it->second->LV_OBJ_IDX(node_sig_idx), buf);
it->second->LV_OBJ_IDX(node_sig_idx)->user_data = 0;
lv_obj_remove_flag(it->second->LV_OBJ_IDX(node_sig_idx), LV_OBJ_FLAG_HIDDEN);
}
}
}
Expand All @@ -5131,6 +5136,7 @@ void TFTView_320x240::updateHopsAway(uint32_t nodeNum, uint8_t hopsAway)
sprintf(buf, _("hops: %d"), (int)hopsAway);
lv_label_set_text(it->second->LV_OBJ_IDX(node_sig_idx), buf);
it->second->LV_OBJ_IDX(node_sig_idx)->user_data = (void *)(unsigned long)hopsAway;
lv_obj_remove_flag(it->second->LV_OBJ_IDX(node_sig_idx), LV_OBJ_FLAG_HIDDEN);
}
}
}
Expand Down Expand Up @@ -6488,12 +6494,13 @@ void TFTView_320x240::newMessage(uint32_t nodeNum, lv_obj_t *container, uint8_t
lv_obj_set_align(msgLabel, LV_ALIGN_LEFT_MID);
lv_label_set_text(msgLabel, msg);
add_style_new_message_style(msgLabel);
lv_obj_add_flag(msgLabel, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_add_event_cb(msgLabel, ui_event_chatNodeButton, LV_EVENT_CLICKED, (void *)nodeNum);

if (state == MeshtasticView::eRunning) {
lv_obj_scroll_to_view(hiddenPanel, LV_ANIM_ON);
lv_obj_move_foreground(objects.message_input_area);
}
lv_obj_add_event_cb(hiddenPanel, ui_event_chatNodeButton, LV_EVENT_CLICKED, (void *)nodeNum);
}

/**
Expand Down
Loading
Loading