* 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>
224 lines
7.6 KiB
C++
224 lines
7.6 KiB
C++
/*
|
||
|
||
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
|