From 3bcad749da0dac6d2ac3a02a88e8b2bc9a12d298 Mon Sep 17 00:00:00 2001 From: yaozhenquan <1756989764@qq.com> Date: Thu, 27 Aug 2026 14:06:34 +0800 Subject: [PATCH] Tune SSD1677 LUTs for PaperMono text/quality modes --- src/lgfx/v1/panel/Panel_SSD1677.cpp | 114 ++++++++++++++++++++-------- src/lgfx/v1/panel/Panel_SSD1677.hpp | 2 + 2 files changed, 86 insertions(+), 30 deletions(-) diff --git a/src/lgfx/v1/panel/Panel_SSD1677.cpp b/src/lgfx/v1/panel/Panel_SSD1677.cpp index 232f7005..f1700796 100644 --- a/src/lgfx/v1/panel/Panel_SSD1677.cpp +++ b/src/lgfx/v1/panel/Panel_SSD1677.cpp @@ -74,45 +74,67 @@ namespace lgfx // which prevents image-correlated ghosting over repeated refreshes. // The timing groups repeat the tail to stabilize the black and white // endpoints. + // static constexpr uint8_t lut_quality[110] = { + // 0x66, 0x66, 0x00, 0x4A, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, // white + // 0x66, 0x66, 0x80, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // light + // 0x66, 0x66, 0x88, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // dark + // 0x66, 0x66, 0xA8, 0x44, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // black + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VCOM + // 0x05, 0x05, 0x05, 0x05, 0x00, + // 0x05, 0x05, 0x05, 0x05, 0x01, + // 0x08, 0x0B, 0x02, 0x03, 0x01, + // 0x0C, 0x02, 0x07, 0x02, 0x01, + // 0x01, 0x00, 0x02, 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, + // 0x22, 0x22, 0x22, 0x22, 0x22, // frame rate + // 0x17, 0x46, 0xA8, 0x36, 0x30, // VGH, VSH1(+16V), VSH2, VSL(-16V), VCOM(-1.2V) + // }; + static constexpr uint8_t lut_quality[110] = { - 0x66, 0x66, 0x00, 0x4A, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, // white - 0x66, 0x66, 0x80, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // light - 0x66, 0x66, 0x88, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // dark - 0x66, 0x66, 0xA8, 0x44, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // black - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VCOM - 0x05, 0x05, 0x05, 0x05, 0x00, - 0x05, 0x05, 0x05, 0x05, 0x01, - 0x08, 0x0B, 0x02, 0x03, 0x01, - 0x0C, 0x02, 0x07, 0x02, 0x01, - 0x01, 0x00, 0x02, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0xaa, 0x55, 0xaa, 0xaa, 0xaa, 0xa0, 0x00, 0x00, 0x00, + 0x55, 0xaa, 0x55, 0xaa, 0xaa, 0xa8, 0x00, 0x00, 0x00, 0x00, + 0x55, 0xaa, 0x55, 0xaa, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x0e, 0x0e, 0x0e, 0x00, + 0x0a, 0x0a, 0x0a, 0x0a, 0x00, + 0x0e, 0x0e, 0x0e, 0x0e, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x22, // frame rate + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // frame rate 0x17, 0x46, 0xA8, 0x36, 0x30, // VGH, VSH1(+16V), VSH2, VSL(-16V), VCOM(-1.2V) }; - // Text: 64-frame absolute four-gray waveform for Mode 1. + + // GC1 binary full-refresh waveform used by epd_text. The final five bytes + // explicitly set VGH, VSH1, VSH2, VSL and VCOM for every text refresh. static constexpr uint8_t lut_text[110] = { - 0x55, 0x55, 0x55, 0x55, 0x55, 0x5A, 0xAA, 0xAA, 0x00, 0x00, // white - 0xAA, 0x95, 0x55, 0x55, 0x55, 0x5A, 0x82, 0xA0, 0x00, 0x00, // light - 0xAA, 0xA5, 0x55, 0x55, 0x55, 0x5A, 0xA0, 0x00, 0x00, 0x00, // dark - 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x50, 0x00, 0x00, // black - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VCOM - 0x02, 0x02, 0x02, 0x02, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x00, - 0x02, 0x02, 0x02, 0x02, 0x00, + 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x09, 0x0A, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, // frame rate - 0x17, 0x41, 0xA8, 0x32, 0x30, // VGH, VSH1(+15V), VSH2, VSL(-15V), VCOM(-1.2V) + 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, + 0x22, 0x22, 0x22, 0x22, 0x22, + 0x17, 0x41, 0xA8, 0x32, 0x30, }; // Fast: 48-frame absolute four-gray waveform for Mode 2. @@ -885,6 +907,38 @@ namespace lgfx return false; } + bool Panel_SSD1677_4Gray::_refresh_mode1_text(const uint8_t* planeL, + const uint8_t* planeM, + const uint8_t* lut) + { + (void)planeL; + if (!planeM || !lut || !_ensure_known_face()) { return false; } + + const auto full = _full_range(); + const uint32_t row_bytes = ((_cfg.panel_width + 7) & ~7) >> 3; + uint8_t zero_row[128] = {}; + + // Match the source binary text test: the old plane is fixed at zero and the + // current monochrome image is taken from planeM. + _set_ram_area(full.left, full.top, full.right + 1, full.bottom + 1); + _bus->writeCommand(CMD_WRITE_RAM_RED, 8); + for (uint32_t y = 0; y < _cfg.panel_height; ++y) + { + _bus->writeBytes(zero_row, row_bytes, true, false); + } + + _set_ram_area(full.left, full.top, full.right + 1, full.bottom + 1); + _bus->writeCommand(CMD_WRITE_RAM_BW, 8); + _bus->writeBytes(planeM, _get_plane_length(), true, false); + send_lut(_bus, lut); + + // 0x07 becomes 0xC7 when the analog section is off: Mode 1 refresh, + // followed by analog and oscillator power-down. + if (!_activate(CTRL1_NORMAL, 0x07, true, false, false)) { return false; } + _optical_state = optical_state_t::gray4; + return true; + } + bool Panel_SSD1677_4Gray::_refresh_mode1_absolute( const uint8_t* lsb, const uint8_t* msb, const uint8_t* lut) { @@ -997,7 +1051,7 @@ namespace lgfx success = _refresh_mode1_absolute(planeL, planeM, lut_quality); break; case epd_mode_t::epd_text: - success = _refresh_mode1_absolute(planeL, planeM, lut_text); + success = _refresh_mode1_text(planeL, planeM, lut_text); break; case epd_mode_t::epd_fast: success = _refresh_mode2_absolute(planeL, planeM, lut_fast); diff --git a/src/lgfx/v1/panel/Panel_SSD1677.hpp b/src/lgfx/v1/panel/Panel_SSD1677.hpp index 5f25b775..b442dec8 100644 --- a/src/lgfx/v1/panel/Panel_SSD1677.hpp +++ b/src/lgfx/v1/panel/Panel_SSD1677.hpp @@ -152,6 +152,8 @@ namespace lgfx bool mode2_activation, bool enforce_refresh_minimum = true); bool _reset_controller_and_face(void); bool _ensure_known_face(void); + bool _refresh_mode1_text(const uint8_t* planeL, const uint8_t* planeM, + const uint8_t* lut); bool _refresh_mode1_absolute(const uint8_t* lsb, const uint8_t* msb, const uint8_t* lut); bool _refresh_mode2_absolute(const uint8_t* lsb, const uint8_t* msb,