2525
2626using namespace NicheGraphics ::Drivers;
2727
28+ #if defined(T5_S3_EPAPER_PRO_V2)
29+ // FastEPD helper symbols are defined in FastEPD.inl with C++ linkage.
30+ extern void bbepPCA9535DigitalWrite (uint8_t pin, uint8_t value);
31+ extern uint8_t bbepPCA9535DigitalRead (uint8_t pin);
32+ extern int bbepI2CWrite (unsigned char iAddr, unsigned char *pData, int iLen);
33+ extern int bbepI2CReadRegister (unsigned char iAddr, unsigned char u8Register, unsigned char *pData, int iLen);
34+ #endif
35+
36+ namespace
37+ {
38+ #if defined(T5_S3_EPAPER_PRO_V2)
39+ // FastEPD default V2 power callback blocks forever waiting for PWRGOOD.
40+ // Replace it with a timeout-safe version so boot never deadlocks.
41+ int safeEPDiyV7EinkPower (void *pBBEP, int bOn)
42+ {
43+ static bool warnedPgood = false ;
44+ static bool warnedTpsPg = false ;
45+ static bool warnedTpsWrite = false ;
46+
47+ FASTEPDSTATE *pState = static_cast <FASTEPDSTATE *>(pBBEP);
48+ if (!pState) {
49+ return BBEP_ERROR_BAD_PARAMETER;
50+ }
51+
52+ if (bOn == pState->pwr_on ) {
53+ return BBEP_SUCCESS;
54+ }
55+
56+ if (bOn) {
57+ bbepPCA9535DigitalWrite (8 , 1 ); // OE on
58+ bbepPCA9535DigitalWrite (9 , 1 ); // GMOD on
59+ bbepPCA9535DigitalWrite (13 , 1 ); // WAKEUP on
60+ bbepPCA9535DigitalWrite (11 , 1 ); // PWRUP on
61+ bbepPCA9535DigitalWrite (12 , 1 ); // VCOM CTRL on
62+ delay (1 );
63+
64+ const uint32_t pgoodStart = millis ();
65+ bool pgoodSeen = false ;
66+ while (!bbepPCA9535DigitalRead (14 )) { // CFG_PIN_PWRGOOD
67+ if ((millis () - pgoodStart) > 1200 ) {
68+ if (!warnedPgood) {
69+ LOG_WARN (" ED047TC1: PWRGOOD timeout, continuing with fallback power-on path" );
70+ warnedPgood = true ;
71+ }
72+ break ;
73+ }
74+ delay (1 );
75+ }
76+ if (bbepPCA9535DigitalRead (14 )) {
77+ pgoodSeen = true ;
78+ }
79+
80+ uint8_t ucTemp[4 ] = {0 };
81+ ucTemp[0 ] = 0x01 ; // TPS_REG_ENABLE
82+ ucTemp[1 ] = 0x3f ; // enable rails
83+ const int tpsEnableRc = bbepI2CWrite (0x68 , ucTemp, 2 );
84+
85+ const int vcom = pState->iVCOM / -10 ;
86+ ucTemp[0 ] = 3 ; // VCOM registers 3+4 (L + H)
87+ ucTemp[1 ] = static_cast <uint8_t >(vcom);
88+ ucTemp[2 ] = static_cast <uint8_t >(vcom >> 8 );
89+ const int tpsVcomRc = bbepI2CWrite (0x68 , ucTemp, 3 );
90+ if ((tpsEnableRc == 0 || tpsVcomRc == 0 ) && !warnedTpsWrite) {
91+ LOG_WARN (" ED047TC1: TPS write did not ACK, continuing with fallback" );
92+ warnedTpsWrite = true ;
93+ }
94+
95+ int iTimeout = 0 ;
96+ uint8_t u8Value = 0 ;
97+ while (iTimeout < 220 && ((u8Value & 0xfa ) != 0xfa )) {
98+ bbepI2CReadRegister (0x68 , 0x0F , &u8Value, 1 ); // TPS_REG_PG
99+ iTimeout++;
100+ delay (1 );
101+ }
102+ if (iTimeout >= 220 && !warnedTpsPg) {
103+ if (pgoodSeen) {
104+ LOG_WARN (" ED047TC1: TPS power-good register timeout, panel may still work" );
105+ } else {
106+ LOG_WARN (" ED047TC1: TPS power-good register timeout after PWRGOOD fallback" );
107+ }
108+ warnedTpsPg = true ;
109+ }
110+
111+ pState->pwr_on = 1 ;
112+ } else {
113+ bbepPCA9535DigitalWrite (8 , 0 ); // OE off
114+ bbepPCA9535DigitalWrite (9 , 0 ); // GMOD off
115+ bbepPCA9535DigitalWrite (11 , 0 ); // PWRUP off
116+ bbepPCA9535DigitalWrite (12 , 0 ); // VCOM CTRL off
117+ delay (1 );
118+ bbepPCA9535DigitalWrite (13 , 0 ); // WAKEUP off
119+ pState->pwr_on = 0 ;
120+ }
121+
122+ return BBEP_SUCCESS;
123+ }
124+ #endif
125+
126+ class SafeFastEPD : public FASTEPD
127+ {
128+ public:
129+ void installSafePowerHandler ()
130+ {
131+ #if defined(T5_S3_EPAPER_PRO_V2)
132+ _state.pfnEinkPower = safeEPDiyV7EinkPower;
133+ #endif
134+ }
135+ };
136+ } // namespace
137+
28138void ED047TC1::begin (SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_busy, uint8_t pin_rst)
29139{
30140 // Parallel display — SPI parameters are not used
@@ -34,24 +144,48 @@ void ED047TC1::begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_
34144 (void )pin_busy;
35145 (void )pin_rst;
36146
37- epaper = new FASTEPD;
147+ SafeFastEPD *safeEpaper = new SafeFastEPD;
148+ epaper = safeEpaper;
38149
150+ int initRc = BBEP_ERROR_BAD_PARAMETER;
39151#if defined(T5_S3_EPAPER_PRO_V1)
40- epaper->initPanel (BB_PANEL_LILYGO_T5PRO, 28000000 );
152+ initRc = epaper->initPanel (BB_PANEL_LILYGO_T5PRO, 28000000 );
41153#elif defined(T5_S3_EPAPER_PRO_V2)
42- epaper->initPanel (BB_PANEL_LILYGO_T5PRO_V2, 28000000 );
154+ initRc = epaper->initPanel (BB_PANEL_LILYGO_T5PRO_V2, 28000000 );
43155 // Initialize all PCA9535 port-0 pins as outputs / HIGH
44156 for (int i = 0 ; i < 8 ; i++) {
45157 epaper->ioPinMode (i, OUTPUT);
46158 epaper->ioWrite (i, HIGH);
47159 }
160+ // On this board, the physical side key is labeled IO48; electrically it maps to PCA9535 IO12 (bit 2 on port-1).
161+ // FastEPD's generic V7 init drives 8..13 as outputs; force IO12 back to input
162+ // so variant touch-control polling can read the key reliably.
163+ epaper->ioPinMode (10 , INPUT);
48164#else
49165#error "ED047TC1 driver: unsupported variant — define T5_S3_EPAPER_PRO_V1 or T5_S3_EPAPER_PRO_V2"
50166#endif
51167
52- epaper->setMode (BB_MODE_1BPP);
53- epaper->clearWhite ();
54- epaper->fullUpdate (true ); // Blocking initial clear
168+ if (initRc != BBEP_SUCCESS) {
169+ LOG_ERROR (" ED047TC1 initPanel failed rc=%d" , initRc);
170+ return ;
171+ }
172+
173+ safeEpaper->installSafePowerHandler ();
174+
175+ const int modeRc = epaper->setMode (BB_MODE_1BPP);
176+ if (modeRc != BBEP_SUCCESS) {
177+ LOG_WARN (" ED047TC1 setMode failed rc=%d" , modeRc);
178+ }
179+
180+ const int clearRc = epaper->clearWhite ();
181+ if (clearRc != BBEP_SUCCESS) {
182+ LOG_WARN (" ED047TC1 clearWhite failed rc=%d" , clearRc);
183+ }
184+
185+ const int fullRc = epaper->fullUpdate (true ); // Blocking initial clear
186+ if (fullRc != BBEP_SUCCESS) {
187+ LOG_WARN (" ED047TC1 initial fullUpdate failed rc=%d" , fullRc);
188+ }
55189}
56190
57191void ED047TC1::update (uint8_t *imageData, UpdateTypes type)
@@ -111,9 +245,8 @@ void ED047TC1::update(uint8_t *imageData, UpdateTypes type)
111245 epaper->fullUpdate (CLEAR_SLOW, false );
112246 epaper->backupPlane (); // Sync pPrevious so next partialUpdate has a correct baseline
113247 } else {
114- // FAST: true partial update — compares pCurrent vs pPrevious and only applies
115- // the update waveform to rows that actually changed. Unchanged rows get a neutral
116- // signal (no visible effect). partialUpdate() updates pPrevious internally.
248+ // FAST: true partial update - compares pCurrent vs pPrevious and only applies
249+ // update waveform to rows that changed. partialUpdate() updates pPrevious.
117250 epaper->partialUpdate (false , 0 , dstTotalRows - 1 );
118251 }
119252}
0 commit comments