#pragma once #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "esp_lcd_touch_gt911.h" #include "common/semaphore_guard.h" #include #include #include "epd_handler.h" // Refresh mode configuration #define DISPLAY_WIDTH 800 #define DISPLAY_HEIGHT 480 // forward declarations class EInkDisplayHandler; struct RefreshArea { public: RefreshArea(int32_t x_start, int32_t y_start, int32_t x_end, int32_t y_end) : x1(x_start), y1(y_start), x2(x_end), y2(y_end) { } int32_t x1; int32_t y1; int32_t x2; int32_t y2; // reset to empty area void reset() { x1 = y1 = x2 = y2 = 0; } // expand area to include another area void expand_to_include(const RefreshArea& other) { expand_to_include(other.x1, other.y1, other.x2, other.y2); } void expand_to_include(int32_t x1, int32_t y1, int32_t x2, int32_t y2) { const bool force_update = is_empty(); if (x1 < this->x1 || force_update) this->x1 = x1; if (y1 < this->y1 || force_update) this->y1 = y1; if (x2 > this->x2 || force_update) this->x2 = x2; if (y2 > this->y2 || force_update) this->y2 = y2; } bool is_empty() const { return (x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0); } uint32_t area() const { if (is_empty()) return 0; return (x2 - x1 + 1) * (y2 - y1 + 1); } }; class EInkDisplayHandler { public: EInkDisplayHandler(); virtual ~EInkDisplayHandler(); esp_err_t init_devices(EventGroupHandle_t system_event_group = nullptr); esp_err_t refresh_display(void); esp_err_t full_write(const uint8_t* framebuffer, const bool white_basemap = true); esp_err_t partial_refresh(const uint8_t* framebuffer, const RefreshArea& area, const bool is_last_partial_update = true); esp_err_t clear_display(void); esp_err_t deep_sleep_display(void); // Request a full refresh on next flush void request_full_refresh(void); bool is_busy() { return epd_handler_.is_busy(); } esp_lcd_touch_handle_t get_touch_handle() const { return tp_handle_; } private: esp_err_t init_display_pins_(void); esp_err_t epd_init_internal_(uint32_t transaction_id); // full fast refresh init esp_err_t epd_init_partial_internal_(uint32_t transaction_id); // partial refresh init (within existing transaction) esp_err_t init_touch_(void); // write to the internal draw buffer void write_to_buffer_(const uint8_t* src, const RefreshArea& area); // write the internal draw buffer to the display's old sram esp_err_t refresh_old_buffer_(uint32_t transaction_id); EPDHandler epd_handler_; uint32_t partial_refresh_count_ = 0; bool force_full_refresh_ = false; std::atomic is_deep_sleep_ { false }; SemaphoreHandle_t refresh_mutex_ = nullptr; esp_lcd_panel_io_handle_t tp_io_handle_ = nullptr; esp_lcd_touch_handle_t tp_handle_ = nullptr; // this buffer reflects the current display state (1=black, 0=white) uint8_t* draw_buffer_ = nullptr; uint8_t* old_buffer_ = nullptr; RefreshArea refresh_area_ = { 0, 0, 0, 0 }; };