From 340168cf2d894c82d1d24dcb56e6086412811731 Mon Sep 17 00:00:00 2001 From: Andrew Villeneuve Date: Thu, 7 May 2026 17:14:49 -0700 Subject: [PATCH] Initial display_partial() implementation --- .../waveshare_epaper_2bit.cpp | 91 ++++++++++++++++++- .../waveshare_epaper_2bit.h | 7 ++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/custom_components/waveshare_epaper_2bit/waveshare_epaper_2bit.cpp b/custom_components/waveshare_epaper_2bit/waveshare_epaper_2bit.cpp index 787c5a6..2ea136d 100644 --- a/custom_components/waveshare_epaper_2bit/waveshare_epaper_2bit.cpp +++ b/custom_components/waveshare_epaper_2bit/waveshare_epaper_2bit.cpp @@ -1887,17 +1887,26 @@ static const uint8_t GRAY4_LUT_2IN9[159] = { }; void WaveshareEPaper2P9InV2R22Bpp::initialize() { + RAMAllocator allocator; if (this->buffer2_ == nullptr) { - RAMAllocator allocator; this->buffer2_ = allocator.allocate(this->get_buffer_length_()); if (this->buffer2_ == nullptr) { ESP_LOGE(TAG, "Could not allocate buffer2 for 2bpp display!"); return; } } - // With Gray4 LUT, bit=0 = white. Init both buffers to white. + if (this->old_buffer_bw_ == nullptr) { + this->old_buffer_bw_ = allocator.allocate(this->get_buffer_length_()); + if (this->old_buffer_bw_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate old_buffer_bw for 2bpp display!"); + return; + } + } + // Gray4 LUT: bit=0=white. Init framebuffers to white. + // old_buffer_bw_ uses standard convention (bit=1=white) so init to 0xFF. memset(this->buffer_, 0x00, this->get_buffer_length_()); memset(this->buffer2_, 0x00, this->get_buffer_length_()); + memset(this->old_buffer_bw_, 0xFF, this->get_buffer_length_()); this->reset_(); this->wait_until_idle_(); @@ -1978,6 +1987,84 @@ void WaveshareEPaper2P9InV2R22Bpp::display() { this->command(0x22); this->data(0xC7); this->command(0x20); + + // Update the old-frame reference for subsequent partial updates. + // buffer_ uses bit=0=white (Gray4 convention); old_buffer_bw_ needs + // bit=1=white (standard partial-LUT convention), so store the inverse. + if (this->old_buffer_bw_ != nullptr) { + for (uint32_t i = 0; i < this->get_buffer_length_(); i++) + this->old_buffer_bw_[i] = ~this->buffer_[i]; + } +} + +void WaveshareEPaper2P9InV2R22Bpp::display_partial() { + // Skip if the display is still busy — natural throttle for rapid drag events. + // The next on_value call will trigger another update once the display is ready. + if (this->busy_pin_ != nullptr && this->busy_pin_->digital_read()) + return; + + this->write_lut_(PARTIAL_UPD_2IN9_LUT, PARTIAL_UPD_2IN9_LUT_SIZE); + + this->command(0x37); + this->data(0x00); this->data(0x00); this->data(0x00); this->data(0x00); this->data(0x00); + this->data(0x40); this->data(0x00); this->data(0x00); this->data(0x00); this->data(0x00); + + this->command(0x3C); + this->data(0x80); + + this->command(0x22); + this->data(0xC0); + this->command(0x20); + + // Wait for the LUT load sequence to complete before writing pixel data. + // This is typically ~50ms and must complete before we can clock in new data. + if (!this->wait_until_idle_()) + return; + + // Full window + this->command(0x44); + this->data(0x00); + this->data(((this->get_width_controller() - 1) >> 3) & 0xFF); + this->command(0x45); + this->data(0x00); this->data(0x00); + this->data((this->get_height_internal() - 1) & 0xFF); + this->data(((this->get_height_internal() - 1) >> 8) & 0xFF); + this->command(0x4E); this->data(0x00); + this->command(0x4F); this->data(0x00); this->data(0x00); + + // 0x26 = old frame reference in standard convention (bit=1=white). + // This tells the partial LUT the previous pixel states so it can apply + // correct transition waveforms (BW, WB, WW, BB) per pixel. + this->command(0x26); + this->start_data_(); + if (this->old_buffer_bw_ != nullptr) { + this->write_array(this->old_buffer_bw_, this->get_buffer_length_()); + } else { + // Fallback: assume all-white previous state + for (uint32_t i = 0; i < this->get_buffer_length_(); i++) + this->write_byte(0xFF); + } + this->end_data_(); + + // 0x24 = new frame in standard convention (bit=1=white). + // buffer_ uses bit=0=white (Gray4 convention), so invert it. + this->command(0x24); + this->start_data_(); + for (uint32_t i = 0; i < this->get_buffer_length_(); i++) + this->write_byte(~this->buffer_[i]); + this->end_data_(); + + // Update old-frame reference before activating so it's ready for the next call. + if (this->old_buffer_bw_ != nullptr) { + for (uint32_t i = 0; i < this->get_buffer_length_(); i++) + this->old_buffer_bw_[i] = ~this->buffer_[i]; + } + + // Activate partial update — fire and return without waiting. + // Display is now busy; subsequent calls will skip until it completes. + this->command(0x22); + this->data(0x0F); + this->command(0x20); } void WaveshareEPaper2P9InV2R22Bpp::write_lut_(const uint8_t *lut, const uint8_t size) { diff --git a/custom_components/waveshare_epaper_2bit/waveshare_epaper_2bit.h b/custom_components/waveshare_epaper_2bit/waveshare_epaper_2bit.h index 248a28d..9b32a2b 100644 --- a/custom_components/waveshare_epaper_2bit/waveshare_epaper_2bit.h +++ b/custom_components/waveshare_epaper_2bit/waveshare_epaper_2bit.h @@ -448,6 +448,10 @@ class WaveshareEPaper2P9InV2R22Bpp : public WaveshareEPaper { void display() override; + // Fast 1-bit partial refresh. Non-blocking: skips if display is busy. + // Suitable for animation feedback; grayscale is restored on next display(). + void display_partial(); + void fill(Color color) override; void dump_config() override; @@ -471,6 +475,9 @@ class WaveshareEPaper2P9InV2R22Bpp : public WaveshareEPaper { uint32_t full_update_every_{30}; uint32_t at_update_{0}; uint8_t *buffer2_{nullptr}; + // Tracks last-sent 1-bit state in standard convention (bit=1=white) for use + // as the 0x26 old-frame reference in partial updates. + uint8_t *old_buffer_bw_{nullptr}; private: void reset_();