#pragma once #include #include class HalDisplay { public: // Constructor with pin configuration HalDisplay(); // Destructor ~HalDisplay(); // Refresh modes enum RefreshMode { FULL_REFRESH, // Full refresh with complete waveform HALF_REFRESH, // Half refresh (1720ms) - balanced quality and speed FAST_REFRESH // Fast refresh using custom LUT }; // Pass seamless=true on any path where the panel already shows the // content it should after begin() returns (silent reboot's popup, // sleep-wake with a restored buffer). Skips the wakeup-gated // requestResync() and defuses the SDK's X3 _x3InitialFullSyncsRemaining // counter; otherwise the first two paints get promoted to FULL // (~770ms each on X3). void begin(bool seamless = false); // Display dimensions static constexpr uint16_t DISPLAY_WIDTH = EInkDisplay::DISPLAY_WIDTH; static constexpr uint16_t DISPLAY_HEIGHT = EInkDisplay::DISPLAY_HEIGHT; static constexpr uint16_t DISPLAY_WIDTH_BYTES = DISPLAY_WIDTH / 8; static constexpr uint32_t BUFFER_SIZE = DISPLAY_WIDTH_BYTES * DISPLAY_HEIGHT; // Frame buffer operations void clearScreen(uint8_t color = 0xFF) const; void drawImage(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem = false) const; void drawImageTransparent(const uint8_t* imageData, uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool fromProgmem = false) const; void displayBuffer(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); void refreshDisplay(RefreshMode mode = RefreshMode::FAST_REFRESH, bool turnOffScreen = false); // Power management void deepSleep(); // Access to frame buffer uint8_t* getFrameBuffer() const; // Lend the framebuffer's ~48 KB STORAGE to a memory-hungry phase (chapter // builds) without freeing it: the allocation never moves, so repeated loans // cannot fragment the heap (free+realloc measurably did). No display calls // between lend and return; the panel keeps its last refreshed image. The // buffer comes back white — redraw fully. Returns nullptr if already lent. uint8_t* lendFrameBufferStorage(uint32_t* sizeOut); void returnFrameBufferStorage(); // X3 grayscale preconditioning (OEM "AA-pre-BW(mid)" settle pass), windowed // to the gray region in physical panel coordinates (no-arg = full frame). // Call after the BW base frame is displayed and before the grayscale planes // are written; no-op on X4. See EInkDisplay::preconditionGrayscale. void preconditionGrayscale(); void preconditionGrayscale(uint16_t x, uint16_t y, uint16_t w, uint16_t h); // Display the framebuffer as the base frame for a grayscale overlay that // follows. On X3, HALF fallback first requests a resync to match // displayBuffer(HALF); FAST fallback keeps the OEM differential base waveform // ("AA-pre-BW(mid)"). Other panels display normally with `fallback` mode. void displayGrayscaleBase(RefreshMode fallback = HALF_REFRESH, bool turnOffScreen = false); void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer); void copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer); void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer); void cleanupGrayscaleBuffers(const uint8_t* bwBuffer); void displayGrayBuffer(bool turnOffScreen = false); // Tiled grayscale: stream one band of a plane (lsbPlane selects LSB/MSB RAM) // straight to the controller; supportsStripGrayscale() gates the path. See // EInkDisplay::writeGrayscalePlaneStrip. void writeGrayscalePlaneStrip(bool lsbPlane, const uint8_t* rows, uint16_t yStart, uint16_t numRows); bool supportsStripGrayscale() const; // Runtime geometry passthrough uint16_t getDisplayWidth() const; uint16_t getDisplayHeight() const; uint16_t getDisplayWidthBytes() const; uint32_t getBufferSize() const; private: EInkDisplay einkDisplay; }; extern HalDisplay display;