E-Ink: change inaccurate terminology (#3269)

Follows a discussion with @markbirss on discord
This commit is contained in:
todd-herbert 2024-02-24 02:45:23 +13:00 committed by GitHub
parent f95b90364a
commit 3ad34f8759
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 62 deletions

View File

@ -18,7 +18,7 @@ SPIClass *hspi = NULL;
#define TECHO_DISPLAY_MODEL GxEPD2_154_D67 #define TECHO_DISPLAY_MODEL GxEPD2_154_D67
#elif defined(RAK4630) #elif defined(RAK4630)
// GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122 - changed from GxEPD2_213_B74 - which was not going to give partial update // GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122 - changed from GxEPD2_213_B74 - which was not going to give fast refresh
// support // support
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN #define TECHO_DISPLAY_MODEL GxEPD2_213_BN
@ -131,8 +131,8 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
// No need to grab this lock because we are on our own SPI bus // No need to grab this lock because we are on our own SPI bus
// concurrency::LockGuard g(spiLock); // concurrency::LockGuard g(spiLock);
#if defined(USE_EINK_DYNAMIC_PARTIAL) #if defined(USE_EINK_DYNAMIC_REFRESH)
// Decide if update is partial or full // Decide between full refresh, fast refresh, or skipping the update
bool continueUpdate = determineRefreshMode(); bool continueUpdate = determineRefreshMode();
if (!continueUpdate) if (!continueUpdate)
return false; return false;
@ -165,12 +165,12 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
adafruitDisplay->nextPage(); adafruitDisplay->nextPage();
#elif defined(RAK4630) || defined(MAKERPYTHON) #elif defined(RAK4630) || defined(MAKERPYTHON)
// RAK14000 2.13 inch b/w 250x122 actually now does support partial updates // RAK14000 2.13 inch b/w 250x122 actually now does support fast refresh
// Full update mode (slow) // Full update mode (slow)
// adafruitDisplay->display(false); // FIXME, use partial update mode // adafruitDisplay->display(false); // FIXME, use fast refresh mode
// Only enable for e-Paper with support for partial updates and comment out above adafruitDisplay->display(false); // Only enable for e-Paper with support for fast updates and comment out above adafruitDisplay->display(false);
// 1.54 inch 200x200 - GxEPD2_154_M09 // 1.54 inch 200x200 - GxEPD2_154_M09
// 2.13 inch 250x122 - GxEPD2_213_BN // 2.13 inch 250x122 - GxEPD2_213_BN
// 2.9 inch 296x128 - GxEPD2_290_T5D // 2.9 inch 296x128 - GxEPD2_290_T5D
@ -203,7 +203,7 @@ void EInkDisplay::display(void)
// at least one forceDisplay() keyframe. This prevents flashing when we should the critical // at least one forceDisplay() keyframe. This prevents flashing when we should the critical
// bootscreen (that we want to look nice) // bootscreen (that we want to look nice)
#ifdef USE_EINK_DYNAMIC_PARTIAL #ifdef USE_EINK_DYNAMIC_REFRESH
lowPriority(); lowPriority();
forceDisplay(); forceDisplay();
highPriority(); highPriority();
@ -257,9 +257,9 @@ bool EInkDisplay::connect()
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY); auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel); adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0)); adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
// RAK14000 2.13 inch b/w 250x122 does actually now support partial updates // RAK14000 2.13 inch b/w 250x122 does actually now support fast refresh
adafruitDisplay->setRotation(3); adafruitDisplay->setRotation(3);
// Partial update support for 1.54, 2.13 RAK14000 b/w , 2.9 and 4.2 // Fast refresh support for 1.54, 2.13 RAK14000 b/w , 2.9 and 4.2
// adafruitDisplay->setRotation(1); // adafruitDisplay->setRotation(1);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
} else { } else {
@ -347,10 +347,10 @@ bool EInkDisplay::connect()
return true; return true;
} }
// Use a mix of full and partial refreshes, to preserve display health // Use a mix of full refresh, fast refresh, and update skipping, to balance urgency and display health
#if defined(USE_EINK_DYNAMIC_PARTIAL) #if defined(USE_EINK_DYNAMIC_REFRESH)
// Suggest that subsequent updates should use partial-refresh // Suggest that subsequent updates should use fast-refresh
void EInkDisplay::highPriority() void EInkDisplay::highPriority()
{ {
isHighPriority = true; isHighPriority = true;
@ -368,8 +368,8 @@ void EInkDisplay::demandFullRefresh()
demandingFull = true; demandingFull = true;
} }
// configure display for partial-refresh // configure display for fast-refresh
void EInkDisplay::configForPartialRefresh() void EInkDisplay::configForFastRefresh()
{ {
// Display-specific code can go here // Display-specific code can go here
#if defined(PRIVATE_HW) #if defined(PRIVATE_HW)
@ -390,7 +390,7 @@ void EInkDisplay::configForFullRefresh()
#endif #endif
} }
#ifdef EINK_PARTIAL_ERASURE_LIMIT #ifdef EINK_FASTREFRESH_ERASURE_LIMIT
// Count black pixels in an image. Used for "erasure tracking" // Count black pixels in an image. Used for "erasure tracking"
int32_t EInkDisplay::countBlackPixels() int32_t EInkDisplay::countBlackPixels()
{ {
@ -422,16 +422,16 @@ bool EInkDisplay::tooManyErasures()
prevBlackCount = blackCount; prevBlackCount = blackCount;
// Log the running total - help devs setup new boards // Log the running total - help devs setup new boards
LOG_DEBUG("Dynamic Partial: erasedSinceFull=%hu, EINK_PARTIAL_ERASURE_LIMIT=%hu\n", erasedSinceFull, LOG_DEBUG("Dynamic Refresh: erasedSinceFull=%hu, EINK_FASTREFRESH_ERASURE_LIMIT=%hu\n", erasedSinceFull,
EINK_PARTIAL_ERASURE_LIMIT); EINK_FASTREFRESH_ERASURE_LIMIT);
// Check if too many pixels have been erased // Check if too many pixels have been erased
if (erasedSinceFull > EINK_PARTIAL_ERASURE_LIMIT) if (erasedSinceFull > EINK_FASTREFRESH_ERASURE_LIMIT)
return true; // Too many return true; // Too many
else else
return false; // Still okay return false; // Still okay
} }
#endif // ifdef EINK_PARTIAL_BRIGHTEN_LIMIT_PX #endif // ifdef EINK_FASTREFRESH_ERASURE_LIMIT
bool EInkDisplay::newImageMatchesOld() bool EInkDisplay::newImageMatchesOld()
{ {
@ -452,7 +452,7 @@ bool EInkDisplay::newImageMatchesOld()
return hashMatches; return hashMatches;
} }
// Change between partial and full refresh config, or skip update, balancing urgency and display health. // Choose between, full-refresh, fast refresh, and update skipping, to balance urgency and display health.
bool EInkDisplay::determineRefreshMode() bool EInkDisplay::determineRefreshMode()
{ {
uint32_t now = millis(); uint32_t now = millis();
@ -466,8 +466,8 @@ bool EInkDisplay::determineRefreshMode()
} }
// Abort: if too soon for a new frame (unless demanding full) // Abort: if too soon for a new frame (unless demanding full)
if (!demandingFull && isHighPriority && partialRefreshCount > 0 && sinceLast < highPriorityLimitMsec) { if (!demandingFull && isHighPriority && fastRefreshCount > 0 && sinceLast < highPriorityLimitMsec) {
LOG_DEBUG("Dynamic Partial: update skipped. Exceeded EINK_HIGHPRIORITY_LIMIT_SECONDS\n"); LOG_DEBUG("Dynamic Refresh: update skipped. Exceeded EINK_HIGHPRIORITY_LIMIT_SECONDS\n");
missedHighPriorityUpdate = true; missedHighPriorityUpdate = true;
return false; return false;
} }
@ -479,16 +479,16 @@ bool EInkDisplay::determineRefreshMode()
if (demandingFull) if (demandingFull)
needsFull = true; needsFull = true;
// Check if old image (partial) should be redrawn (as full), for image quality // Check if old image (fast-refresh) should be redrawn (as full), for image quality
if (partialRefreshCount > 0 && !isHighPriority) if (fastRefreshCount > 0 && !isHighPriority)
needsFull = true; needsFull = true;
// If too many partials, require a full-refresh (display health) // If too many fast updates, require a full-refresh (display health)
if (partialRefreshCount >= partialRefreshLimit) if (fastRefreshCount >= fastRefreshLimit)
needsFull = true; needsFull = true;
#ifdef EINK_PARTIAL_ERASURE_LIMIT #ifdef EINK_FASTREFRESH_ERASURE_LIMIT
// Some displays struggle with erasing black pixels to white, during partial refresh // Some displays struggle with erasing black pixels to white, during fast-refresh
if (tooManyErasures()) if (tooManyErasures())
needsFull = true; needsFull = true;
#endif #endif
@ -510,29 +510,29 @@ bool EInkDisplay::determineRefreshMode()
// If options require a full refresh // If options require a full refresh
if (!isHighPriority || needsFull) { if (!isHighPriority || needsFull) {
if (partialRefreshCount > 0) if (fastRefreshCount > 0)
configForFullRefresh(); configForFullRefresh();
LOG_DEBUG("Dynamic Partial: conditions met for full-refresh\n"); LOG_DEBUG("Dynamic Refresh: conditions met for full-refresh\n");
partialRefreshCount = 0; fastRefreshCount = 0;
needsFull = false; needsFull = false;
demandingFull = false; demandingFull = false;
erasedSinceFull = 0; // Reset the count for EINK_PARTIAL_ERASURE_LIMIT - tracks ghosting buildup erasedSinceFull = 0; // Reset the count for EINK_FASTREFRESH_ERASURE_LIMIT - tracks ghosting buildup
} }
// If options allow a partial refresh // If options allow a fast-refresh
else { else {
if (partialRefreshCount == 0) if (fastRefreshCount == 0)
configForPartialRefresh(); configForFastRefresh();
LOG_DEBUG("Dynamic Partial: conditions met for partial-refresh\n"); LOG_DEBUG("Dynamic Refresh: conditions met for fast-refresh\n");
partialRefreshCount++; fastRefreshCount++;
} }
lastUpdateMsec = now; // Mark time for rate limiting lastUpdateMsec = now; // Mark time for rate limiting
return true; // Instruct calling method to continue with update return true; // Instruct calling method to continue with update
} }
#endif // End USE_EINK_DYNAMIC_PARTIAL #endif // End USE_EINK_DYNAMIC_REFRESH
#endif #endif

View File

@ -55,23 +55,23 @@ class EInkDisplay : public OLEDDisplay
// Connect to the display // Connect to the display
virtual bool connect() override; virtual bool connect() override;
#if defined(USE_EINK_DYNAMIC_PARTIAL) #if defined(USE_EINK_DYNAMIC_REFRESH)
// Full, partial, or skip: balance urgency with display health // Full, fast, or skip: balance urgency with display health
// Use partial refresh if EITHER: // Use fast refresh if EITHER:
// * highPriority() was set // * highPriority() was set
// * a highPriority() update was previously skipped, for rate-limiting - (EINK_HIGHPRIORITY_LIMIT_SECONDS) // * a highPriority() update was previously skipped, for rate-limiting - (EINK_HIGHPRIORITY_LIMIT_SECONDS)
// Use full refresh if EITHER: // Use full refresh if EITHER:
// * lowPriority() was set // * lowPriority() was set
// * demandFullRefresh() was called - (single shot) // * demandFullRefresh() was called - (single shot)
// * too many partial updates in a row: protect display - (EINK_PARTIAL_REPEAT_LIMIT) // * too many fast updates in a row: protect display - (EINK_FASTREFRESH_REPEAT_LIMIT)
// * no recent updates, and last update was partial: redraw for image quality (EINK_LOWPRIORITY_LIMIT_SECONDS) // * no recent updates, and last update was fast: redraw for image quality (EINK_LOWPRIORITY_LIMIT_SECONDS)
// * (optional) too many "erasures" since full-refresh (black pixels cleared to white) // * (optional) too many "erasures" since full-refresh (black pixels cleared to white)
// Rate limit if: // Rate limit if:
// * lowPriority() - (EINK_LOWPRIORITY_LIMIT_SECONDS) // * lowPriority() - (EINK_LOWPRIORITY_LIMIT_SECONDS)
// * highPriority(), if multiple partials have run back-to-back - (EINK_HIGHPRIORITY_LIMIT_SECONDS) // * highPriority(), if multiple fast updates have run back-to-back - (EINK_HIGHPRIORITY_LIMIT_SECONDS)
// Skip update entirely if ALL criteria met: // Skip update entirely if ALL criteria met:
// * new image matches old image // * new image matches old image
@ -83,29 +83,29 @@ class EInkDisplay : public OLEDDisplay
// ------------------------------------ // ------------------------------------
// To implement for your E-Ink display: // To implement for your E-Ink display:
// * edit configForPartialRefresh() // * edit configForFastRefresh()
// * edit configForFullRefresh() // * edit configForFullRefresh()
// * add macros to variant.h, and adjust to taste: // * add macros to variant.h, and adjust to taste:
/* /*
#define USE_EINK_DYNAMIC_PARTIAL #define USE_EINK_DYNAMIC_REFRESH
#define EINK_LOWPRIORITY_LIMIT_SECONDS 30 #define EINK_LOWPRIORITY_LIMIT_SECONDS 30
#define EINK_HIGHPRIORITY_LIMIT_SECONDS 1 #define EINK_HIGHPRIORITY_LIMIT_SECONDS 1
#define EINK_PARTIAL_REPEAT_LIMIT 5 #define EINK_FASTREFRESH_REPEAT_LIMIT 5
#define EINK_PARTIAL_ERASURE_LIMIT 300 // optional #define EINK_FASTREFRESH_ERASURE_LIMIT 300 // optional
*/ */
public: public:
void highPriority(); // Suggest partial refresh void highPriority(); // Suggest fast refresh
void lowPriority(); // Suggest full refresh void lowPriority(); // Suggest full refresh
void demandFullRefresh(); // For next update: explicitly request full refresh void demandFullRefresh(); // For next update: explicitly request full refresh
protected: protected:
void configForPartialRefresh(); // Display specific code to select partial refresh mode void configForFastRefresh(); // Display specific code to select fast refresh mode
void configForFullRefresh(); // Display specific code to return to full refresh mode void configForFullRefresh(); // Display specific code to return to full refresh mode
bool newImageMatchesOld(); // Is the new update actually different to the last image? bool newImageMatchesOld(); // Is the new update actually different to the last image?
bool determineRefreshMode(); // Called immediately before data written to display - choose refresh mode, or abort update bool determineRefreshMode(); // Called immediately before data written to display - choose refresh mode, or abort update
#ifdef EINK_PARTIAL_ERASURE_LIMIT #ifdef EINK_FASTREFRESH_ERASURE_LIMIT
int32_t countBlackPixels(); // Calculate the number of black pixels in the new image int32_t countBlackPixels(); // Calculate the number of black pixels in the new image
bool tooManyErasures(); // Has too much "ghosting" (black pixels erased to white) accumulated since last full-refresh? bool tooManyErasures(); // Has too much "ghosting" (black pixels erased to white) accumulated since last full-refresh?
#endif #endif
@ -114,18 +114,18 @@ class EInkDisplay : public OLEDDisplay
bool needsFull = false; // Is a full refresh forced? (display health) bool needsFull = false; // Is a full refresh forced? (display health)
bool demandingFull = false; // Was full refresh specifically requested? (splash screens, etc) bool demandingFull = false; // Was full refresh specifically requested? (splash screens, etc)
bool missedHighPriorityUpdate = false; // Was a high priority update skipped for rate-limiting? bool missedHighPriorityUpdate = false; // Was a high priority update skipped for rate-limiting?
uint16_t partialRefreshCount = 0; // How many partials have occurred since last full refresh? uint16_t fastRefreshCount = 0; // How many fast updates have occurred since last full refresh?
uint32_t lastUpdateMsec = 0; // When did the last update occur? uint32_t lastUpdateMsec = 0; // When did the last update occur?
uint32_t prevImageHash = 0; // Used to check if update will change screen image (skippable or not) uint32_t prevImageHash = 0; // Used to check if update will change screen image (skippable or not)
int32_t prevBlackCount = 0; // How many black pixels were in the previous image int32_t prevBlackCount = 0; // How many black pixels were in the previous image
uint32_t erasedSinceFull = 0; // How many black pixels have been set back to white since last full-refresh? (roughly) uint32_t erasedSinceFull = 0; // How many black pixels have been set back to white since last full-refresh? (roughly)
// Set in variant.h // Set in variant.h
const uint32_t lowPriorityLimitMsec = (uint32_t)1000 * EINK_LOWPRIORITY_LIMIT_SECONDS; // Max rate for partial refreshes const uint32_t lowPriorityLimitMsec = (uint32_t)1000 * EINK_LOWPRIORITY_LIMIT_SECONDS; // Max rate for fast refreshes
const uint32_t highPriorityLimitMsec = (uint32_t)1000 * EINK_HIGHPRIORITY_LIMIT_SECONDS; // Max rate for full refreshes const uint32_t highPriorityLimitMsec = (uint32_t)1000 * EINK_HIGHPRIORITY_LIMIT_SECONDS; // Max rate for full refreshes
const uint32_t partialRefreshLimit = EINK_PARTIAL_REPEAT_LIMIT; // Max consecutive partials, before full is triggered const uint32_t fastRefreshLimit = EINK_FASTREFRESH_REPEAT_LIMIT; // Max consecutive fast updates, before full is triggered
#else // !USE_EINK_DYNAMIC_PARTIAL #else // !USE_EINK_DYNAMIC_REFRESH
// Tolerate calls to these methods anywhere, just to be safe // Tolerate calls to these methods anywhere, just to be safe
void highPriority() {} void highPriority() {}
void lowPriority() {} void lowPriority() {}

View File

@ -6,12 +6,12 @@
#define USE_EINK #define USE_EINK
// Settings for Dynamic Partial mode // Settings for Dynamic Refresh mode
// Change between partial and full refresh config, or skip update, balancing urgency and display health. // Change between full-refresh, fast-refresh, or update-skipping, to balance urgency and display health.
#define USE_EINK_DYNAMIC_PARTIAL #define USE_EINK_DYNAMIC_REFRESH
#define EINK_LOWPRIORITY_LIMIT_SECONDS 30 #define EINK_LOWPRIORITY_LIMIT_SECONDS 30
#define EINK_HIGHPRIORITY_LIMIT_SECONDS 1 #define EINK_HIGHPRIORITY_LIMIT_SECONDS 1
#define EINK_PARTIAL_REPEAT_LIMIT 5 #define EINK_FASTREFRESH_REPEAT_LIMIT 5
/* /*
* eink display pins * eink display pins