Initial display_partial() implementation

This commit is contained in:
2026-05-07 17:14:49 -07:00
parent 8fa53ac24f
commit 340168cf2d
2 changed files with 96 additions and 2 deletions
@@ -1887,17 +1887,26 @@ static const uint8_t GRAY4_LUT_2IN9[159] = {
}; };
void WaveshareEPaper2P9InV2R22Bpp::initialize() { void WaveshareEPaper2P9InV2R22Bpp::initialize() {
if (this->buffer2_ == nullptr) {
RAMAllocator<uint8_t> allocator; RAMAllocator<uint8_t> allocator;
if (this->buffer2_ == nullptr) {
this->buffer2_ = allocator.allocate(this->get_buffer_length_()); this->buffer2_ = allocator.allocate(this->get_buffer_length_());
if (this->buffer2_ == nullptr) { if (this->buffer2_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate buffer2 for 2bpp display!"); ESP_LOGE(TAG, "Could not allocate buffer2 for 2bpp display!");
return; 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->buffer_, 0x00, this->get_buffer_length_());
memset(this->buffer2_, 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->reset_();
this->wait_until_idle_(); this->wait_until_idle_();
@@ -1978,6 +1987,84 @@ void WaveshareEPaper2P9InV2R22Bpp::display() {
this->command(0x22); this->command(0x22);
this->data(0xC7); this->data(0xC7);
this->command(0x20); 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) { void WaveshareEPaper2P9InV2R22Bpp::write_lut_(const uint8_t *lut, const uint8_t size) {
@@ -448,6 +448,10 @@ class WaveshareEPaper2P9InV2R22Bpp : public WaveshareEPaper {
void display() override; 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 fill(Color color) override;
void dump_config() override; void dump_config() override;
@@ -471,6 +475,9 @@ class WaveshareEPaper2P9InV2R22Bpp : public WaveshareEPaper {
uint32_t full_update_every_{30}; uint32_t full_update_every_{30};
uint32_t at_update_{0}; uint32_t at_update_{0};
uint8_t *buffer2_{nullptr}; 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: private:
void reset_(); void reset_();