diff --git a/libs/display/EInkDisplay/src/EInkDisplay.cpp b/libs/display/EInkDisplay/src/EInkDisplay.cpp index 4661113..6e06a9a 100644 --- a/libs/display/EInkDisplay/src/EInkDisplay.cpp +++ b/libs/display/EInkDisplay/src/EInkDisplay.cpp @@ -125,7 +125,40 @@ const uint8_t lut_x3_wb_full[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const uint8_t lut_x3_bb_full[] PROGMEM = { - 0x00, 0x06, 0x02, 0x06, 0x06, 0x01, 0x00, 0x05, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x10, 0x06, 0x02, 0x06, 0x06, 0x01, 0x00, 0x05, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// X3 dedicated grayscale LUTs — tuned drive strengths for 4-level gray +// All entries share the same single-phase timing so the controller scans +// every row with consistent gate timing. Source voltages differ per transition: +// VCOM: GND (stable common electrode reference) +// BB: GND (active hold — prevents floating source crosstalk) +// WW: brief VDL pulse (dark gray) +// BW: moderate VDL pulse (light gray) +// WB: GND (active hold — unused transition) +const uint8_t lut_x3_vcom_gray[] PROGMEM = { + 0x00, 0x03, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t lut_x3_ww_gray[] PROGMEM = { + // Dark gray: VS=0x20 → GND,VDL(2),GND,GND — brief pulse (sub-phase B) + 0x20, 0x03, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t lut_x3_bw_gray[] PROGMEM = { + // Light gray: VS=0x80 → VDL(3),GND,GND,GND — subtle pulse (sub-phase A, TP0=3) + 0x80, 0x03, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t lut_x3_wb_gray[] PROGMEM = { + // Active GND hold: VS=0x00 → all GND, matching timing + 0x00, 0x03, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t lut_x3_bb_gray[] PROGMEM = { + // Active GND hold: VS=0x00 → all GND, matching timing + 0x00, 0x03, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -1014,14 +1047,14 @@ void EInkDisplay::displayGrayBuffer(const bool turnOffScreen) { } }; - const uint8_t* vcom = lut_x3_vcom_full; - const uint8_t* ww = lut_x3_ww_full; - const uint8_t* bw = lut_x3_bw_full; - const uint8_t* wb = lut_x3_wb_full; - const uint8_t* bb = lut_x3_bb_full; + const uint8_t* vcom = lut_x3_vcom_gray; + const uint8_t* ww = lut_x3_ww_gray; + const uint8_t* bw = lut_x3_bw_gray; + const uint8_t* wb = lut_x3_wb_gray; + const uint8_t* bb = lut_x3_bb_gray; uint8_t dataInterval0 = 0x29; uint8_t dataInterval1 = 0x07; - if (Serial) Serial.printf("[%lu] X3_GRAY_MODE=full29\n", millis()); + if (Serial) Serial.printf("[%lu] X3_GRAY_MODE=gray_tuned\n", millis()); sendCommandDataX3(0x20, vcom, 42); sendCommandDataX3(0x21, ww, 42); sendCommandDataX3(0x22, bw, 42);