Fix: partial refresh, but color still in negative

This commit is contained in:
GW_MC
2026-01-29 13:14:39 +08:00
parent d940027e9c
commit f433abb9ec

View File

@@ -12,16 +12,20 @@
#define MINIMUM_PIN_SETUP_DELAY_MS 10 #define MINIMUM_PIN_SETUP_DELAY_MS 10
#define MINIMUM_POWER_ON_DELAY_MS 100 #define MINIMUM_POWER_ON_DELAY_MS 100
static uint8_t black_data[DISPLAY_BUFFER_SIZE]; // all black data static uint8_t* DRAW_BUFFER; // 1 bit per pixel
static uint8_t white_data[DISPLAY_BUFFER_SIZE]; // all white data static uint8_t* OLD_DRAW_BUFFER; // 1 bit per pixel
static uint8_t DRAW_BUFFER[DISPLAY_WIDTH * DISPLAY_HEIGHT / 8]; // 1 bit per pixel static uint8_t* black_data;
static uint8_t OLD_DRAW_BUFFER[DISPLAY_WIDTH * DISPLAY_HEIGHT / 8]; // 1 bit per pixel static uint8_t* white_data;
EInkDisplayHandler::EInkDisplayHandler() { EInkDisplayHandler::EInkDisplayHandler() {
memset(black_data, 0xFF, sizeof(black_data)); // eink uses 1 for black black_data = static_cast<uint8_t*>(heap_caps_malloc(DISPLAY_BUFFER_SIZE, MALLOC_CAP_SPIRAM));
memset(white_data, 0x00, sizeof(white_data)); white_data = static_cast<uint8_t*>(heap_caps_malloc(DISPLAY_BUFFER_SIZE, MALLOC_CAP_SPIRAM));
memset(DRAW_BUFFER, 0x00, sizeof(DRAW_BUFFER)); // start with all white (0 = white in e-ink) DRAW_BUFFER = static_cast<uint8_t*>(heap_caps_malloc(DISPLAY_BUFFER_SIZE, MALLOC_CAP_SPIRAM));
memset(OLD_DRAW_BUFFER, 0x00, sizeof(OLD_DRAW_BUFFER)); // start with all white (0 = white in e-ink) OLD_DRAW_BUFFER = static_cast<uint8_t*>(heap_caps_malloc(DISPLAY_BUFFER_SIZE, MALLOC_CAP_SPIRAM));
memset(black_data, 0xFF, DISPLAY_BUFFER_SIZE); // eink uses 1 for black
memset(white_data, 0x00, DISPLAY_BUFFER_SIZE);
memset(DRAW_BUFFER, 0x00, DISPLAY_BUFFER_SIZE); // start with all white (0 = white in e-ink)
memset(OLD_DRAW_BUFFER, 0x00, DISPLAY_BUFFER_SIZE); // start with all white (0 = white in e-ink)
draw_buffer_ = DRAW_BUFFER; draw_buffer_ = DRAW_BUFFER;
old_buffer_ = OLD_DRAW_BUFFER; old_buffer_ = OLD_DRAW_BUFFER;
@@ -41,6 +45,18 @@ EInkDisplayHandler::~EInkDisplayHandler() {
if (tp_io_handle_ != nullptr) { if (tp_io_handle_ != nullptr) {
esp_lcd_panel_io_del(tp_io_handle_); esp_lcd_panel_io_del(tp_io_handle_);
} }
if (black_data != nullptr) {
heap_caps_free(black_data);
}
if (white_data != nullptr) {
heap_caps_free(white_data);
}
if (DRAW_BUFFER != nullptr) {
heap_caps_free(DRAW_BUFFER);
}
if (OLD_DRAW_BUFFER != nullptr) {
heap_caps_free(OLD_DRAW_BUFFER);
}
} }
esp_err_t EInkDisplayHandler::deep_sleep_display(void) { esp_err_t EInkDisplayHandler::deep_sleep_display(void) {
@@ -205,27 +221,11 @@ esp_err_t EInkDisplayHandler::full_write(const uint8_t* framebuffer, const bool
return ESP_OK; return ESP_OK;
} }
// TODO: Partial refresh is inverted in color
esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_framebuffer, const RefreshArea& incoming_area, const bool is_last_partial_update) { esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_framebuffer, const RefreshArea& incoming_area, const bool is_last_partial_update) {
ESP_LOGI(TAG, "Starting partial refresh (0.3 seconds)..."); ESP_LOGI(TAG, "Starting partial refresh (0.3 seconds)...");
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
{
TransactionGuard transaction_guard(this->epd_handler_);
err = transaction_guard.begin(pdMS_TO_TICKS(5000));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to begin transaction for partial refresh: %s", esp_err_to_name(err));
return err;
}
// Wake display from deep sleep INSIDE the transaction to prevent race conditions
if (is_deep_sleep_) {
err = epd_init_partial_internal_(transaction_guard.transaction_id());
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize EPD for partial refresh: %s", esp_err_to_name(err));
return err;
}
}
write_to_buffer_(incoming_partial_framebuffer, incoming_area); write_to_buffer_(incoming_partial_framebuffer, incoming_area);
// Always expand refresh_area_ to include incoming_area // Always expand refresh_area_ to include incoming_area
@@ -235,6 +235,27 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
ESP_LOGI(TAG, "Partial refresh skipped (not last partial update)"); ESP_LOGI(TAG, "Partial refresh skipped (not last partial update)");
return ESP_OK; return ESP_OK;
} }
{
TransactionGuard transaction_guard(this->epd_handler_);
err = transaction_guard.begin(pdMS_TO_TICKS(5000));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to begin transaction for partial refresh: %s", esp_err_to_name(err));
return err;
}
// Wake display from deep sleep INSIDE the transaction to prevent race conditions
if (is_deep_sleep_) {
err = epd_init_internal_(transaction_guard.transaction_id());
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize EPD for partial refresh: %s", esp_err_to_name(err));
return err;
}
err = refresh_old_buffer_(transaction_guard.transaction_id());
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to refresh old buffer during partial refresh init: %s", esp_err_to_name(err));
return err;
}
}
RefreshArea area = refresh_area_; RefreshArea area = refresh_area_;
if (area.x1 % 8 != 0 || area.x2 % 8 != 7) { if (area.x1 % 8 != 0 || area.x2 % 8 != 7) {
@@ -248,7 +269,9 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
const uint32_t area_height = area.y2 - area.y1 + 1; const uint32_t area_height = area.y2 - area.y1 + 1;
const size_t partial_buffer_size = area_width_bytes * area_height; const size_t partial_buffer_size = area_width_bytes * area_height;
uint8_t* partial_buffer = new uint8_t[partial_buffer_size]; // uint8_t* partial_buffer = new uint8_t[partial_buffer_size];
uint8_t* partial_buffer = static_cast<uint8_t*>(heap_caps_malloc(partial_buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL));
if (partial_buffer == nullptr) { if (partial_buffer == nullptr) {
ESP_LOGE(TAG, "Failed to allocate partial buffer for partial refresh"); ESP_LOGE(TAG, "Failed to allocate partial buffer for partial refresh");
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
@@ -329,7 +352,7 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
err = epd_handler_.epd_write_cmd(0x13, transaction_guard.transaction_id()); err = epd_handler_.epd_write_cmd(0x13, transaction_guard.transaction_id());
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send new data command for partial refresh: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to send new data command for partial refresh: %s", esp_err_to_name(err));
delete[] partial_buffer; heap_caps_free(partial_buffer);
return err; return err;
} }
@@ -339,16 +362,19 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
err = epd_handler_.transfer_spi_data(partial_buffer, partial_buffer_size, transaction_guard.transaction_id()); err = epd_handler_.transfer_spi_data(partial_buffer, partial_buffer_size, transaction_guard.transaction_id());
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send partial_buffer data for partial refresh: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to send partial_buffer data for partial refresh: %s", esp_err_to_name(err));
delete[] partial_buffer; heap_caps_free(partial_buffer);
return err; return err;
} }
memcpy(old_buffer_, draw_buffer_, DISPLAY_BUFFER_SIZE);
} }
// Clean up partial buffer // Clean up partial buffer
delete[] partial_buffer; heap_caps_free(partial_buffer);
// Step 6: Trigger partial display refresh (DRF) // Step 6: Trigger partial display refresh (DRF)
err = epd_handler_.epd_write_cmd(0x11, transaction_guard.transaction_id()); // Use 0x12 (Display Update) command - same as full refresh, per sample code
err = epd_handler_.epd_write_cmd(0x12, transaction_guard.transaction_id());
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send display refresh command for partial refresh: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to send display refresh command for partial refresh: %s", esp_err_to_name(err));
return err; return err;
@@ -398,7 +424,6 @@ esp_err_t EInkDisplayHandler::partial_refresh(const uint8_t* incoming_partial_fr
return err; return err;
} }
memcpy(old_buffer_, draw_buffer_, DISPLAY_BUFFER_SIZE);
refresh_area_.reset(); refresh_area_.reset();
@@ -702,7 +727,7 @@ esp_err_t EInkDisplayHandler::init_touch_() {
} }
esp_err_t EInkDisplayHandler::refresh_old_buffer_(uint32_t transaction_id) { esp_err_t EInkDisplayHandler::refresh_old_buffer_(uint32_t transaction_id) {
ESP_LOGI(TAG, "Refreshing display SRAM to match current buffers..."); ESP_LOGI(TAG, "Refreshing display SRAM to restore state after wake...");
esp_err_t err; esp_err_t err;
err = epd_handler_.epd_write_cmd(0x92, transaction_id); // enter normal mode err = epd_handler_.epd_write_cmd(0x92, transaction_id); // enter normal mode
@@ -710,7 +735,11 @@ esp_err_t EInkDisplayHandler::refresh_old_buffer_(uint32_t transaction_id) {
ESP_LOGE(TAG, "Failed to enter normal mode: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to enter normal mode: %s", esp_err_to_name(err));
return err; return err;
} }
// Send command to write old data (0x10)
// Write OLD data (0x10) as all 0x00 (white in e-ink terms)
// This tells the controller: "assume display was all white"
// Matches sample's EPD_WhiteScreen_ALL() which uses 0x00 for old SRAM
// The differential refresh: old=0 + new=0 → stay white, old=0 + new=1 → drive to black
err = epd_handler_.epd_write_cmd(0x10, transaction_id); err = epd_handler_.epd_write_cmd(0x10, transaction_id);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send old data command: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to send old data command: %s", esp_err_to_name(err));
@@ -720,25 +749,25 @@ esp_err_t EInkDisplayHandler::refresh_old_buffer_(uint32_t transaction_id) {
// Send the old buffer as old data // Send the old buffer as old data
err = epd_handler_.transfer_spi_data(old_buffer_, DISPLAY_BUFFER_SIZE, transaction_id); err = epd_handler_.transfer_spi_data(old_buffer_, DISPLAY_BUFFER_SIZE, transaction_id);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send old buffer data: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to send white baseline to old SRAM: %s", esp_err_to_name(err));
return err; return err;
} }
// Also write to new data register (0x13) to ensure consistent baseline // Write NEW data (0x13) with the actual display content
// This prevents undefined state after hardware reset // This restores the display to show old_buffer_ content
err = epd_handler_.epd_write_cmd(0x13, transaction_id); err = epd_handler_.epd_write_cmd(0x13, transaction_id);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send new data command: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to send new data command: %s", esp_err_to_name(err));
return err; return err;
} }
// Send the draw buffer as new data (should match old_buffer_ after full refresh) // Send the last displayed content to new SRAM
err = epd_handler_.transfer_spi_data(old_buffer_, DISPLAY_BUFFER_SIZE, transaction_id); err = epd_handler_.transfer_spi_data(old_buffer_, DISPLAY_BUFFER_SIZE, transaction_id);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send draw buffer data: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to send display content to new SRAM: %s", esp_err_to_name(err));
return err; return err;
} }
ESP_LOGI(TAG, "Display SRAM refreshed successfully"); ESP_LOGI(TAG, "Display SRAM restored successfully");
return ESP_OK; return ESP_OK;
} }