Adds a basic implementation of `displayWindow` which displays a subset of the current frame buffer to the screen. It is significantly less performant than a full screen reload, so it shouldn't be used unless the underlying content is hard to get/rerender. I've also spotted a little bit of ghosting, but it's not fully clear as to why that is. My only thought it the LUT and grayscale mode isn't fully disengaged or the RED RAM buffer isn't correctly setup (but afaict it should be populated correctly). Going to merge this to continue testing this feature out in the wild, but consider it experimental.
102 lines
3.1 KiB
C++
102 lines
3.1 KiB
C++
#pragma once
|
|
#include <Arduino.h>
|
|
#include <SPI.h>
|
|
|
|
class EInkDisplay {
|
|
public:
|
|
// Constructor with pin configuration
|
|
EInkDisplay(int8_t sclk, int8_t mosi, int8_t cs, int8_t dc, int8_t rst, int8_t busy);
|
|
|
|
// Destructor
|
|
~EInkDisplay() = default;
|
|
|
|
// Refresh modes (guarded to avoid redefinition in test builds)
|
|
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
|
|
};
|
|
|
|
// Initialize the display hardware and driver
|
|
void begin();
|
|
|
|
// Display dimensions
|
|
static constexpr uint16_t DISPLAY_WIDTH = 800;
|
|
static constexpr uint16_t DISPLAY_HEIGHT = 480;
|
|
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;
|
|
|
|
#ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE
|
|
void swapBuffers();
|
|
#endif
|
|
void setFramebuffer(const uint8_t* bwBuffer) const;
|
|
|
|
void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer);
|
|
void copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer);
|
|
void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer);
|
|
#ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE
|
|
void cleanupGrayscaleBuffers(const uint8_t* bwBuffer);
|
|
#endif
|
|
|
|
void displayBuffer(RefreshMode mode = FAST_REFRESH);
|
|
// EXPERIMENTAL: Windowed update - display only a rectangular region
|
|
void displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
|
void displayGrayBuffer(bool turnOffScreen = false);
|
|
|
|
void refreshDisplay(RefreshMode mode = FAST_REFRESH, bool turnOffScreen = false);
|
|
|
|
// debug function
|
|
void grayscaleRevert();
|
|
|
|
// LUT control
|
|
void setCustomLUT(bool enabled, const unsigned char* lutData = nullptr);
|
|
|
|
// Power management
|
|
void deepSleep();
|
|
|
|
// Access to frame buffer
|
|
uint8_t* getFrameBuffer() const {
|
|
return frameBuffer;
|
|
}
|
|
|
|
// Save the current framebuffer to a PBM file (desktop/test builds only)
|
|
void saveFrameBufferAsPBM(const char* filename);
|
|
|
|
private:
|
|
// Pin configuration
|
|
int8_t _sclk, _mosi, _cs, _dc, _rst, _busy;
|
|
|
|
// Frame buffer (statically allocated)
|
|
uint8_t frameBuffer0[BUFFER_SIZE];
|
|
uint8_t* frameBuffer;
|
|
#ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE
|
|
uint8_t frameBuffer1[BUFFER_SIZE];
|
|
uint8_t* frameBufferActive;
|
|
#endif
|
|
|
|
// SPI settings
|
|
SPISettings spiSettings;
|
|
|
|
// State
|
|
bool isScreenOn;
|
|
bool customLutActive;
|
|
bool inGrayscaleMode;
|
|
bool drawGrayscale;
|
|
|
|
// Low-level display control
|
|
void resetDisplay();
|
|
void sendCommand(uint8_t command);
|
|
void sendData(uint8_t data);
|
|
void sendData(const uint8_t* data, uint16_t length);
|
|
void waitWhileBusy(const char* comment = nullptr);
|
|
void initDisplayController();
|
|
|
|
// Low-level display operations
|
|
void setRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
|
void writeRamBuffer(uint8_t ramBuffer, const uint8_t* data, uint32_t size);
|
|
};
|