Files
meshtastic_firmware/src/graphics/niche/Drivers/EInk/ED047TC1.cpp
T
a522973fe7 fix(t5s3-epaper): ED047TC1 display margins, remove calibration diagnostic, fix touch threshold (#10304)
* fix(t5s3-epaper): increase ED047TC1 margins to 16px on all sides

Tested on device — 16px uniform margins look noticeably cleaner than
the previous asymmetric 8–9px values. Canvas shrinks from 944×523 to
928×508 (H_OFFSET_BYTES=2, V_OFFSET_TOP/BOTTOM=16).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(t5s3-epaper): remove EINK_EDGE_LINES calibration diagnostic from ED047TC1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(t5s3-epaper): align TOUCH_THRESHOLD_X with TOUCH_THRESHOLD_Y (40px)

X axis threshold was 60 while Y was 40, causing asymmetric swipe detection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
2026-06-01 21:29:51 -04:00

224 lines
7.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
NicheGraphics parallel E-Ink driver for the LilyGo T5-S3-ePaper-Pro (ED047TC1).
InkHUD buffer format : 1bpp, horizontal bytes, MSB = leftmost pixel, 1 = white
FastEPD buffer format: 1bpp, horizontal bytes, MSB = leftmost pixel, 1 = white
Both formats share the same pixel layout and polarity (1 = white, 0 = black).
The InkHUD safe-area buffer (928×508) is copied into the centre of the physical
960×540 FastEPD buffer so content clears the panel's inactive edge border.
See ED047TC1.h for the H_OFFSET_BYTES / V_OFFSET_TOP / V_OFFSET_BOTTOM constants.
*/
#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
#ifdef T5_S3_EPAPER_PRO
#include "./ED047TC1.h"
#include "FastEPD.h"
#include "configuration.h"
using namespace NicheGraphics::Drivers;
#if defined(T5_S3_EPAPER_PRO_V2)
// FastEPD helper symbols are defined in FastEPD.inl with C++ linkage.
extern void bbepPCA9535DigitalWrite(uint8_t pin, uint8_t value);
extern uint8_t bbepPCA9535DigitalRead(uint8_t pin);
extern int bbepI2CWrite(unsigned char iAddr, unsigned char *pData, int iLen);
extern int bbepI2CReadRegister(unsigned char iAddr, unsigned char u8Register, unsigned char *pData, int iLen);
#endif
namespace
{
#if defined(T5_S3_EPAPER_PRO_V2)
// FastEPD default V2 power callback blocks forever waiting for PWRGOOD.
// Replace it with a timeout-safe version so boot never deadlocks.
int safeEPDiyV7EinkPower(void *pBBEP, int bOn)
{
static bool warnedPgood = false;
static bool warnedTpsPg = false;
static bool warnedTpsWrite = false;
FASTEPDSTATE *pState = static_cast<FASTEPDSTATE *>(pBBEP);
if (!pState) {
return BBEP_ERROR_BAD_PARAMETER;
}
if (bOn == pState->pwr_on) {
return BBEP_SUCCESS;
}
if (bOn) {
bbepPCA9535DigitalWrite(8, 1); // OE on
bbepPCA9535DigitalWrite(9, 1); // GMOD on
bbepPCA9535DigitalWrite(13, 1); // WAKEUP on
bbepPCA9535DigitalWrite(11, 1); // PWRUP on
bbepPCA9535DigitalWrite(12, 1); // VCOM CTRL on
delay(1);
const uint32_t pgoodStart = millis();
bool pgoodSeen = false;
while (!bbepPCA9535DigitalRead(14)) { // CFG_PIN_PWRGOOD
if ((millis() - pgoodStart) > 1200) {
if (!warnedPgood) {
LOG_WARN("ED047TC1: PWRGOOD timeout, continuing with fallback power-on path");
warnedPgood = true;
}
break;
}
delay(1);
}
if (bbepPCA9535DigitalRead(14)) {
pgoodSeen = true;
}
uint8_t ucTemp[4] = {0};
ucTemp[0] = 0x01; // TPS_REG_ENABLE
ucTemp[1] = 0x3f; // enable rails
const int tpsEnableRc = bbepI2CWrite(0x68, ucTemp, 2);
const int vcom = pState->iVCOM / -10;
ucTemp[0] = 3; // VCOM registers 3+4 (L + H)
ucTemp[1] = static_cast<uint8_t>(vcom);
ucTemp[2] = static_cast<uint8_t>(vcom >> 8);
const int tpsVcomRc = bbepI2CWrite(0x68, ucTemp, 3);
if ((tpsEnableRc == 0 || tpsVcomRc == 0) && !warnedTpsWrite) {
LOG_WARN("ED047TC1: TPS write did not ACK, continuing with fallback");
warnedTpsWrite = true;
}
int iTimeout = 0;
uint8_t u8Value = 0;
while (iTimeout < 220 && ((u8Value & 0xfa) != 0xfa)) {
bbepI2CReadRegister(0x68, 0x0F, &u8Value, 1); // TPS_REG_PG
iTimeout++;
delay(1);
}
if (iTimeout >= 220 && !warnedTpsPg) {
if (pgoodSeen) {
LOG_WARN("ED047TC1: TPS power-good register timeout, panel may still work");
} else {
LOG_WARN("ED047TC1: TPS power-good register timeout after PWRGOOD fallback");
}
warnedTpsPg = true;
}
pState->pwr_on = 1;
} else {
bbepPCA9535DigitalWrite(8, 0); // OE off
bbepPCA9535DigitalWrite(9, 0); // GMOD off
bbepPCA9535DigitalWrite(11, 0); // PWRUP off
bbepPCA9535DigitalWrite(12, 0); // VCOM CTRL off
delay(1);
bbepPCA9535DigitalWrite(13, 0); // WAKEUP off
pState->pwr_on = 0;
}
return BBEP_SUCCESS;
}
#endif
class SafeFastEPD : public FASTEPD
{
public:
void installSafePowerHandler()
{
#if defined(T5_S3_EPAPER_PRO_V2)
_state.pfnEinkPower = safeEPDiyV7EinkPower;
#endif
}
};
} // namespace
void ED047TC1::begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst)
{
// Parallel display — SPI parameters are not used
(void)spi;
(void)pin_dc;
(void)pin_cs;
(void)pin_busy;
(void)pin_rst;
SafeFastEPD *safeEpaper = new SafeFastEPD;
epaper = safeEpaper;
int initRc = BBEP_ERROR_BAD_PARAMETER;
#if defined(T5_S3_EPAPER_PRO_V1)
initRc = epaper->initPanel(BB_PANEL_LILYGO_T5PRO, 28000000);
#elif defined(T5_S3_EPAPER_PRO_V2)
initRc = epaper->initPanel(BB_PANEL_LILYGO_T5PRO_V2, 28000000);
// Initialize all PCA9535 port-0 pins as outputs / HIGH
for (int i = 0; i < 8; i++) {
epaper->ioPinMode(i, OUTPUT);
epaper->ioWrite(i, HIGH);
}
// On this board, the physical side key is labeled IO48; electrically it maps to PCA9535 IO12 (bit 2 on port-1).
// FastEPD's generic V7 init drives 8..13 as outputs; force IO12 back to input
// so variant touch-control polling can read the key reliably.
epaper->ioPinMode(10, INPUT);
#else
#error "ED047TC1 driver: unsupported variant — define T5_S3_EPAPER_PRO_V1 or T5_S3_EPAPER_PRO_V2"
#endif
if (initRc != BBEP_SUCCESS) {
LOG_ERROR("ED047TC1 initPanel failed rc=%d", initRc);
return;
}
safeEpaper->installSafePowerHandler();
const int modeRc = epaper->setMode(BB_MODE_1BPP);
if (modeRc != BBEP_SUCCESS) {
LOG_WARN("ED047TC1 setMode failed rc=%d", modeRc);
}
const int clearRc = epaper->clearWhite();
if (clearRc != BBEP_SUCCESS) {
LOG_WARN("ED047TC1 clearWhite failed rc=%d", clearRc);
}
const int fullRc = epaper->fullUpdate(true); // Blocking initial clear
if (fullRc != BBEP_SUCCESS) {
LOG_WARN("ED047TC1 initial fullUpdate failed rc=%d", fullRc);
}
}
void ED047TC1::update(uint8_t *imageData, UpdateTypes type)
{
if (!epaper)
return;
// InkHUD renders into a DISPLAY_WIDTH × DISPLAY_HEIGHT safe-area buffer.
// We need to place that into the centre of the physical 960×540 FastEPD buffer,
// leaving blank margins at every edge to avoid the panel's inactive border.
const uint32_t srcRowBytes = (DISPLAY_WIDTH + 7) / 8; // bytes per row in InkHUD buffer (118)
const uint32_t dstRowBytes = (960 + 7) / 8; // bytes per row in physical buffer (120)
const uint32_t dstTotalRows = 540;
uint8_t *cur = epaper->currentBuffer();
// Fill physical buffer with white (0xFF = white in FastEPD 1bpp)
memset(cur, 0xFF, dstRowBytes * dstTotalRows);
// Copy each InkHUD row into the physical buffer with horizontal + vertical offsets
for (uint32_t row = 0; row < DISPLAY_HEIGHT; row++) {
const uint8_t *srcRow = imageData + row * srcRowBytes;
uint8_t *dstRow = cur + (row + V_OFFSET_TOP) * dstRowBytes + H_OFFSET_BYTES;
memcpy(dstRow, srcRow, srcRowBytes);
}
if (type == FULL) {
epaper->fullUpdate(CLEAR_SLOW, false);
epaper->backupPlane(); // Sync pPrevious so next partialUpdate has a correct baseline
} else {
// FAST: true partial update - compares pCurrent vs pPrevious and only applies
// update waveform to rows that changed. partialUpdate() updates pPrevious.
epaper->partialUpdate(false, 0, dstTotalRows - 1);
}
}
#endif // T5_S3_EPAPER_PRO
#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS