Add EINK_DISPLAY_SINGLE_BUFFER_MODE build flag and allow for single buffer rendering

This commit is contained in:
Dave Allie
2025-12-16 22:07:52 +11:00
parent 54e6368c01
commit 9536359647
2 changed files with 42 additions and 6 deletions

View File

@@ -30,12 +30,17 @@ class EInkDisplay {
void clearScreen(uint8_t color = 0xFF) const; 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 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(); void swapBuffers();
#endif
void setFramebuffer(const uint8_t* bwBuffer) const; void setFramebuffer(const uint8_t* bwBuffer) const;
void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer); void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer);
void copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer); void copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer);
void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer); 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); void displayBuffer(RefreshMode mode = FAST_REFRESH);
void displayGrayBuffer(bool turnOffScreen = false); void displayGrayBuffer(bool turnOffScreen = false);
@@ -65,10 +70,11 @@ class EInkDisplay {
// Frame buffer (statically allocated) // Frame buffer (statically allocated)
uint8_t frameBuffer0[BUFFER_SIZE]; uint8_t frameBuffer0[BUFFER_SIZE];
uint8_t frameBuffer1[BUFFER_SIZE];
uint8_t* frameBuffer; uint8_t* frameBuffer;
#ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE
uint8_t frameBuffer1[BUFFER_SIZE];
uint8_t* frameBufferActive; uint8_t* frameBufferActive;
#endif
// SPI settings // SPI settings
SPISettings spiSettings; SPISettings spiSettings;

View File

@@ -115,7 +115,9 @@ EInkDisplay::EInkDisplay(int8_t sclk, int8_t mosi, int8_t cs, int8_t dc, int8_t
_rst(rst), _rst(rst),
_busy(busy), _busy(busy),
frameBuffer(nullptr), frameBuffer(nullptr),
#ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE
frameBufferActive(nullptr), frameBufferActive(nullptr),
#endif
customLutActive(false) { customLutActive(false) {
Serial.printf("[%lu] EInkDisplay: Constructor called\n", millis()); Serial.printf("[%lu] EInkDisplay: Constructor called\n", millis());
Serial.printf("[%lu] SCLK=%d, MOSI=%d, CS=%d, DC=%d, RST=%d, BUSY=%d\n", millis(), sclk, mosi, cs, dc, rst, busy); Serial.printf("[%lu] SCLK=%d, MOSI=%d, CS=%d, DC=%d, RST=%d, BUSY=%d\n", millis(), sclk, mosi, cs, dc, rst, busy);
@@ -125,13 +127,19 @@ void EInkDisplay::begin() {
Serial.printf("[%lu] EInkDisplay: begin() called\n", millis()); Serial.printf("[%lu] EInkDisplay: begin() called\n", millis());
frameBuffer = frameBuffer0; frameBuffer = frameBuffer0;
#ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE
frameBufferActive = frameBuffer1; frameBufferActive = frameBuffer1;
#endif
// Initialize to white // Initialize to white
memset(frameBuffer0, 0xFF, BUFFER_SIZE); memset(frameBuffer0, 0xFF, BUFFER_SIZE);
#ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE
Serial.printf("[%lu] Static frame buffer (%lu bytes = 48KB)\n", millis(), BUFFER_SIZE);
#else
memset(frameBuffer1, 0xFF, BUFFER_SIZE); memset(frameBuffer1, 0xFF, BUFFER_SIZE);
Serial.printf("[%lu] Static frame buffers (2 x %lu bytes = 96KB)\n", millis(), BUFFER_SIZE); Serial.printf("[%lu] Static frame buffers (2 x %lu bytes = 96KB)\n", millis(), BUFFER_SIZE);
#endif
Serial.printf("[%lu] Initializing e-ink display driver...\n", millis()); Serial.printf("[%lu] Initializing e-ink display driver...\n", millis());
// Initialize SPI with custom pins // Initialize SPI with custom pins
@@ -351,11 +359,13 @@ void EInkDisplay::setFramebuffer(const uint8_t* bwBuffer) const {
memcpy(frameBuffer, bwBuffer, BUFFER_SIZE); memcpy(frameBuffer, bwBuffer, BUFFER_SIZE);
} }
#ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE
void EInkDisplay::swapBuffers() { void EInkDisplay::swapBuffers() {
uint8_t* temp = frameBuffer; uint8_t* temp = frameBuffer;
frameBuffer = frameBufferActive; frameBuffer = frameBufferActive;
frameBufferActive = temp; frameBufferActive = temp;
} }
#endif
void EInkDisplay::grayscaleRevert() { void EInkDisplay::grayscaleRevert() {
if (!inGrayscaleMode) { if (!inGrayscaleMode) {
@@ -386,6 +396,18 @@ void EInkDisplay::copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t*
writeRamBuffer(CMD_WRITE_RAM_RED, msbBuffer, BUFFER_SIZE); writeRamBuffer(CMD_WRITE_RAM_RED, msbBuffer, BUFFER_SIZE);
} }
#ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE
/**
* In single buffer mode, this should be called with the previously written BW buffer
* to reconstruct the RED buffer for proper differential fast refreshes following a
* grayscale display.
*/
void EInkDisplay::cleanupGrayscaleBuffers(const uint8_t* bwBuffer) {
setRamArea(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
writeRamBuffer(CMD_WRITE_RAM_RED, bwBuffer, BUFFER_SIZE);
}
#endif
void EInkDisplay::displayBuffer(RefreshMode mode) { void EInkDisplay::displayBuffer(RefreshMode mode) {
if (!isScreenOn) { if (!isScreenOn) {
// Force half refresh if screen is off // Force half refresh if screen is off
@@ -408,14 +430,22 @@ void EInkDisplay::displayBuffer(RefreshMode mode) {
} else { } else {
// For fast refresh, write to BW buffer only // For fast refresh, write to BW buffer only
writeRamBuffer(CMD_WRITE_RAM_BW, frameBuffer, BUFFER_SIZE); writeRamBuffer(CMD_WRITE_RAM_BW, frameBuffer, BUFFER_SIZE);
// In single buffer mode, the RED RAM should already contain the previous frame
// In dual buffer mode, we write back frameBufferActive which is the last frame
#ifndef EINK_DISPLAY_SINGLE_BUFFER_MODE
writeRamBuffer(CMD_WRITE_RAM_RED, frameBufferActive, BUFFER_SIZE); writeRamBuffer(CMD_WRITE_RAM_RED, frameBufferActive, BUFFER_SIZE);
#endif
} }
// swap active buffer for next time
swapBuffers();
// Refresh the display // Refresh the display
refreshDisplay(mode); refreshDisplay(mode);
#ifdef EINK_DISPLAY_SINGLE_BUFFER_MODE
// In single buffer mode always sync RED RAM after refresh to prepare for next fast refresh
// This ensures RED contains the currently displayed frame for differential comparison
setRamArea(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
writeRamBuffer(CMD_WRITE_RAM_RED, frameBuffer, BUFFER_SIZE);
#endif
} }
void EInkDisplay::displayGrayBuffer(const bool turnOffScreen) { void EInkDisplay::displayGrayBuffer(const bool turnOffScreen) {