mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-16 01:52:04 +00:00
Merge pull request #3356 from todd-herbert/eink-special-frames
Handle "special-frames" with EInkDynamicDisplay
This commit is contained in:
commit
6a27e62bcf
@ -25,19 +25,19 @@ EInkDynamicDisplay::~EInkDynamicDisplay()
|
|||||||
// Screen requests a BACKGROUND frame
|
// Screen requests a BACKGROUND frame
|
||||||
void EInkDynamicDisplay::display()
|
void EInkDynamicDisplay::display()
|
||||||
{
|
{
|
||||||
setFrameFlag(BACKGROUND);
|
addFrameFlag(BACKGROUND);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Screen requests a RESPONSIVE frame
|
// Screen requests a RESPONSIVE frame
|
||||||
bool EInkDynamicDisplay::forceDisplay(uint32_t msecLimit)
|
bool EInkDynamicDisplay::forceDisplay(uint32_t msecLimit)
|
||||||
{
|
{
|
||||||
setFrameFlag(RESPONSIVE);
|
addFrameFlag(RESPONSIVE);
|
||||||
return update(); // (Unutilized) Base class promises to return true if update ran
|
return update(); // (Unutilized) Base class promises to return true if update ran
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add flag for the next frame
|
// Add flag for the next frame
|
||||||
void EInkDynamicDisplay::setFrameFlag(frameFlagTypes flag)
|
void EInkDynamicDisplay::addFrameFlag(frameFlagTypes flag)
|
||||||
{
|
{
|
||||||
// OR the new flag into the existing flags
|
// OR the new flag into the existing flags
|
||||||
this->frameFlags = (frameFlagTypes)(this->frameFlags | flag);
|
this->frameFlags = (frameFlagTypes)(this->frameFlags | flag);
|
||||||
@ -96,20 +96,49 @@ bool EInkDynamicDisplay::update()
|
|||||||
{
|
{
|
||||||
// Detemine the refresh mode to use, and start the update
|
// Detemine the refresh mode to use, and start the update
|
||||||
bool refreshApproved = determineMode();
|
bool refreshApproved = determineMode();
|
||||||
if (refreshApproved)
|
if (refreshApproved) {
|
||||||
EInkDisplay::forceDisplay(0); // Bypass base class' own rate-limiting system
|
EInkDisplay::forceDisplay(0); // Bypass base class' own rate-limiting system
|
||||||
|
storeAndReset(); // Store the result of this loop for next time. Note: call *before* endOrDetach()
|
||||||
#if defined(HAS_EINK_ASYNCFULL)
|
endOrDetach(); // endUpdate() right now, or set the async refresh flag (if FULL and HAS_EINK_ASYNCFULL)
|
||||||
if (refreshApproved)
|
} else
|
||||||
endOrDetach(); // Either endUpdate() right now (fast refresh), or set the async flag (full refresh)
|
storeAndReset(); // No update, no post-update code, just store the results
|
||||||
#endif
|
|
||||||
|
|
||||||
return refreshApproved; // (Unutilized) Base class promises to return true if update ran
|
return refreshApproved; // (Unutilized) Base class promises to return true if update ran
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Figure out who runs the post-update code
|
||||||
|
void EInkDynamicDisplay::endOrDetach()
|
||||||
|
{
|
||||||
|
// If the GxEPD2 version reports that it has the async modifications
|
||||||
|
#ifdef HAS_EINK_ASYNCFULL
|
||||||
|
if (previousRefresh == FULL) {
|
||||||
|
asyncRefreshRunning = true; // Set the flag - picked up at start of determineMode(), next loop.
|
||||||
|
|
||||||
|
if (previousFrameFlags & BLOCKING)
|
||||||
|
awaitRefresh();
|
||||||
|
else
|
||||||
|
LOG_DEBUG("Async full-refresh begins\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast Refresh
|
||||||
|
else if (previousRefresh == FAST)
|
||||||
|
EInkDisplay::endUpdate(); // Still block while updating, but EInkDisplay needs us to call endUpdate() ourselves.
|
||||||
|
|
||||||
|
// Fallback - If using an unmodified version of GxEPD2 for some reason
|
||||||
|
#else
|
||||||
|
if (previousRefresh == FULL || previousRefresh == FAST) { // If refresh wasn't skipped (on unspecified..)
|
||||||
|
LOG_WARN(
|
||||||
|
"GxEPD2 version has not been modified to support async refresh; using fallback behavior. Please update lib_deps in "
|
||||||
|
"variant's platformio.ini file\n");
|
||||||
|
EInkDisplay::endUpdate();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Assess situation, pick a refresh type
|
// Assess situation, pick a refresh type
|
||||||
bool EInkDynamicDisplay::determineMode()
|
bool EInkDynamicDisplay::determineMode()
|
||||||
{
|
{
|
||||||
|
checkInitialized();
|
||||||
checkForPromotion();
|
checkForPromotion();
|
||||||
#if defined(HAS_EINK_ASYNCFULL)
|
#if defined(HAS_EINK_ASYNCFULL)
|
||||||
checkAsyncFullRefresh();
|
checkAsyncFullRefresh();
|
||||||
@ -117,10 +146,8 @@ bool EInkDynamicDisplay::determineMode()
|
|||||||
checkRateLimiting();
|
checkRateLimiting();
|
||||||
|
|
||||||
// If too soon for a new frame, or display busy, abort early
|
// If too soon for a new frame, or display busy, abort early
|
||||||
if (refresh == SKIPPED) {
|
if (refresh == SKIPPED)
|
||||||
storeAndReset();
|
|
||||||
return false; // No refresh
|
return false; // No refresh
|
||||||
}
|
|
||||||
|
|
||||||
// -- New frame is due --
|
// -- New frame is due --
|
||||||
|
|
||||||
@ -131,11 +158,11 @@ bool EInkDynamicDisplay::determineMode()
|
|||||||
// Once mode determined, any remaining checks will bypass
|
// Once mode determined, any remaining checks will bypass
|
||||||
checkCosmetic();
|
checkCosmetic();
|
||||||
checkDemandingFast();
|
checkDemandingFast();
|
||||||
|
checkFrameMatchesPrevious();
|
||||||
checkConsecutiveFastRefreshes();
|
checkConsecutiveFastRefreshes();
|
||||||
#ifdef EINK_LIMIT_GHOSTING_PX
|
#ifdef EINK_LIMIT_GHOSTING_PX
|
||||||
checkExcessiveGhosting();
|
checkExcessiveGhosting();
|
||||||
#endif
|
#endif
|
||||||
checkFrameMatchesPrevious();
|
|
||||||
checkFastRequested();
|
checkFastRequested();
|
||||||
|
|
||||||
if (refresh == UNSPECIFIED)
|
if (refresh == UNSPECIFIED)
|
||||||
@ -152,12 +179,27 @@ bool EInkDynamicDisplay::determineMode()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Return - call a refresh or not?
|
// Return - call a refresh or not?
|
||||||
if (refresh == SKIPPED) {
|
if (refresh == SKIPPED)
|
||||||
storeAndReset();
|
|
||||||
return false; // Don't trigger a refresh
|
return false; // Don't trigger a refresh
|
||||||
} else {
|
else
|
||||||
storeAndReset();
|
|
||||||
return true; // Do trigger a refresh
|
return true; // Do trigger a refresh
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this the very first frame?
|
||||||
|
void EInkDynamicDisplay::checkInitialized()
|
||||||
|
{
|
||||||
|
if (!initialized) {
|
||||||
|
// Undo GxEPD2_BW::partialWindow(), if set by developer in EInkDisplay::connect()
|
||||||
|
configForFullRefresh();
|
||||||
|
|
||||||
|
// Clear any existing image, so we can draw logo with fast-refresh, but also to set GxEPD2_EPD::_initial_write
|
||||||
|
adafruitDisplay->clearScreen();
|
||||||
|
|
||||||
|
LOG_DEBUG("initialized, ");
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
// Use a fast-refresh for the next frame; no skipping or else blank screen when waking from deep sleep
|
||||||
|
addFrameFlag(DEMAND_FAST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,14 +211,14 @@ void EInkDynamicDisplay::checkForPromotion()
|
|||||||
|
|
||||||
switch (previousReason) {
|
switch (previousReason) {
|
||||||
case ASYNC_REFRESH_BLOCKED_DEMANDFAST:
|
case ASYNC_REFRESH_BLOCKED_DEMANDFAST:
|
||||||
setFrameFlag(DEMAND_FAST);
|
addFrameFlag(DEMAND_FAST);
|
||||||
break;
|
break;
|
||||||
case ASYNC_REFRESH_BLOCKED_COSMETIC:
|
case ASYNC_REFRESH_BLOCKED_COSMETIC:
|
||||||
setFrameFlag(COSMETIC);
|
addFrameFlag(COSMETIC);
|
||||||
break;
|
break;
|
||||||
case ASYNC_REFRESH_BLOCKED_RESPONSIVE:
|
case ASYNC_REFRESH_BLOCKED_RESPONSIVE:
|
||||||
case EXCEEDED_RATELIMIT_FAST:
|
case EXCEEDED_RATELIMIT_FAST:
|
||||||
setFrameFlag(RESPONSIVE);
|
addFrameFlag(RESPONSIVE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -226,7 +268,7 @@ void EInkDynamicDisplay::checkCosmetic()
|
|||||||
if (frameFlags & COSMETIC) {
|
if (frameFlags & COSMETIC) {
|
||||||
refresh = FULL;
|
refresh = FULL;
|
||||||
reason = FLAGGED_COSMETIC;
|
reason = FLAGGED_COSMETIC;
|
||||||
LOG_DEBUG("refresh=FULL, reason=FLAGGED_COSMETIC\n");
|
LOG_DEBUG("refresh=FULL, reason=FLAGGED_COSMETIC, frameFlags=0x%x\n", frameFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,22 +283,7 @@ void EInkDynamicDisplay::checkDemandingFast()
|
|||||||
if (frameFlags & DEMAND_FAST) {
|
if (frameFlags & DEMAND_FAST) {
|
||||||
refresh = FAST;
|
refresh = FAST;
|
||||||
reason = FLAGGED_DEMAND_FAST;
|
reason = FLAGGED_DEMAND_FAST;
|
||||||
LOG_DEBUG("refresh=FAST, reason=FLAGGED_DEMAND_FAST\n");
|
LOG_DEBUG("refresh=FAST, reason=FLAGGED_DEMAND_FAST, frameFlags=0x%x\n", frameFlags);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Have too many fast-refreshes occured consecutively, since last full refresh?
|
|
||||||
void EInkDynamicDisplay::checkConsecutiveFastRefreshes()
|
|
||||||
{
|
|
||||||
// If a decision was already reached, don't run the check
|
|
||||||
if (refresh != UNSPECIFIED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If too many FAST refreshes consecutively - force a FULL refresh
|
|
||||||
if (fastRefreshCount >= EINK_LIMIT_FASTREFRESH) {
|
|
||||||
refresh = FULL;
|
|
||||||
reason = EXCEEDED_LIMIT_FASTREFRESH;
|
|
||||||
LOG_DEBUG("refresh=FULL, reason=EXCEEDED_LIMIT_FASTREFRESH\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +303,7 @@ void EInkDynamicDisplay::checkFrameMatchesPrevious()
|
|||||||
if (frameFlags == BACKGROUND && fastRefreshCount > 0) {
|
if (frameFlags == BACKGROUND && fastRefreshCount > 0) {
|
||||||
refresh = FULL;
|
refresh = FULL;
|
||||||
reason = REDRAW_WITH_FULL;
|
reason = REDRAW_WITH_FULL;
|
||||||
LOG_DEBUG("refresh=FULL, reason=REDRAW_WITH_FULL\n");
|
LOG_DEBUG("refresh=FULL, reason=REDRAW_WITH_FULL, frameFlags=0x%x\n", frameFlags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -284,7 +311,22 @@ void EInkDynamicDisplay::checkFrameMatchesPrevious()
|
|||||||
// Not redrawn, not COSMETIC, not DEMAND_FAST
|
// Not redrawn, not COSMETIC, not DEMAND_FAST
|
||||||
refresh = SKIPPED;
|
refresh = SKIPPED;
|
||||||
reason = FRAME_MATCHED_PREVIOUS;
|
reason = FRAME_MATCHED_PREVIOUS;
|
||||||
LOG_DEBUG("refresh=SKIPPED, reason=FRAME_MATCHED_PREVIOUS\n");
|
LOG_DEBUG("refresh=SKIPPED, reason=FRAME_MATCHED_PREVIOUS, frameFlags=0x%x\n", frameFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have too many fast-refreshes occured consecutively, since last full refresh?
|
||||||
|
void EInkDynamicDisplay::checkConsecutiveFastRefreshes()
|
||||||
|
{
|
||||||
|
// If a decision was already reached, don't run the check
|
||||||
|
if (refresh != UNSPECIFIED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If too many FAST refreshes consecutively - force a FULL refresh
|
||||||
|
if (fastRefreshCount >= EINK_LIMIT_FASTREFRESH) {
|
||||||
|
refresh = FULL;
|
||||||
|
reason = EXCEEDED_LIMIT_FASTREFRESH;
|
||||||
|
LOG_DEBUG("refresh=FULL, reason=EXCEEDED_LIMIT_FASTREFRESH, frameFlags=0x%x\n", frameFlags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No objections, we can perform fast-refresh, if desired
|
// No objections, we can perform fast-refresh, if desired
|
||||||
@ -298,7 +340,8 @@ void EInkDynamicDisplay::checkFastRequested()
|
|||||||
// If we want BACKGROUND to use fast. (FULL only when a limit is hit)
|
// If we want BACKGROUND to use fast. (FULL only when a limit is hit)
|
||||||
refresh = FAST;
|
refresh = FAST;
|
||||||
reason = BACKGROUND_USES_FAST;
|
reason = BACKGROUND_USES_FAST;
|
||||||
LOG_DEBUG("refresh=FAST, reason=BACKGROUND_USES_FAST, fastRefreshCount=%lu\n", fastRefreshCount);
|
LOG_DEBUG("refresh=FAST, reason=BACKGROUND_USES_FAST, fastRefreshCount=%lu, frameFlags=0x%x\n", fastRefreshCount,
|
||||||
|
frameFlags);
|
||||||
#else
|
#else
|
||||||
// If we do want to use FULL for BACKGROUND updates
|
// If we do want to use FULL for BACKGROUND updates
|
||||||
refresh = FULL;
|
refresh = FULL;
|
||||||
@ -311,7 +354,7 @@ void EInkDynamicDisplay::checkFastRequested()
|
|||||||
if (frameFlags & RESPONSIVE) {
|
if (frameFlags & RESPONSIVE) {
|
||||||
refresh = FAST;
|
refresh = FAST;
|
||||||
reason = NO_OBJECTIONS;
|
reason = NO_OBJECTIONS;
|
||||||
LOG_DEBUG("refresh=FAST, reason=NO_OBJECTIONS, fastRefreshCount=%lu\n", fastRefreshCount);
|
LOG_DEBUG("refresh=FAST, reason=NO_OBJECTIONS, fastRefreshCount=%lu, frameFlags=0x%x\n", fastRefreshCount, frameFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,6 +378,7 @@ void EInkDynamicDisplay::hashImage()
|
|||||||
// Store the results of determineMode() for future use, and reset for next call
|
// Store the results of determineMode() for future use, and reset for next call
|
||||||
void EInkDynamicDisplay::storeAndReset()
|
void EInkDynamicDisplay::storeAndReset()
|
||||||
{
|
{
|
||||||
|
previousFrameFlags = frameFlags;
|
||||||
previousRefresh = refresh;
|
previousRefresh = refresh;
|
||||||
previousReason = reason;
|
previousReason = reason;
|
||||||
|
|
||||||
@ -391,7 +435,7 @@ void EInkDynamicDisplay::checkExcessiveGhosting()
|
|||||||
if (ghostPixelCount > EINK_LIMIT_GHOSTING_PX) {
|
if (ghostPixelCount > EINK_LIMIT_GHOSTING_PX) {
|
||||||
refresh = FULL;
|
refresh = FULL;
|
||||||
reason = EXCEEDED_GHOSTINGLIMIT;
|
reason = EXCEEDED_GHOSTINGLIMIT;
|
||||||
LOG_DEBUG("refresh=FULL, reason=EXCEEDED_GHOSTINGLIMIT\n");
|
LOG_DEBUG("refresh=FULL, reason=EXCEEDED_GHOSTINGLIMIT, frameFlags=0x%x\n", frameFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,17 +483,17 @@ void EInkDynamicDisplay::checkAsyncFullRefresh()
|
|||||||
// It is only equipped to intercept calls to nextPage()
|
// It is only equipped to intercept calls to nextPage()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out who runs the post-update code
|
// Hold control while an async refresh runs
|
||||||
void EInkDynamicDisplay::endOrDetach()
|
void EInkDynamicDisplay::awaitRefresh()
|
||||||
{
|
{
|
||||||
if (previousRefresh == FULL) { // Note: previousRefresh is the refresh from this loop.
|
// Continually poll the BUSY pin
|
||||||
asyncRefreshRunning = true; // Set the flag - picked up at start of determineMode(), next loop.
|
while (adafruitDisplay->epd2.isBusy())
|
||||||
LOG_DEBUG("Async full-refresh begins\n");
|
yield();
|
||||||
}
|
|
||||||
|
|
||||||
// Fast Refresh
|
// End the full-refresh process
|
||||||
else
|
adafruitDisplay->endAsyncFull(); // Run the end of nextPage() code
|
||||||
EInkDisplay::endUpdate(); // Still block while updating, but EInkDisplay needs us to call endUpdate() ourselves.
|
EInkDisplay::endUpdate(); // Run base-class code to finish off update (NOT our derived class override)
|
||||||
|
asyncRefreshRunning = false; // Unset the flag
|
||||||
}
|
}
|
||||||
#endif // HAS_EINK_ASYNCFULL
|
#endif // HAS_EINK_ASYNCFULL
|
||||||
|
|
||||||
|
@ -28,8 +28,9 @@ class EInkDynamicDisplay : public EInkDisplay
|
|||||||
RESPONSIVE = (1 << 1), // For frames via forceDisplay()
|
RESPONSIVE = (1 << 1), // For frames via forceDisplay()
|
||||||
COSMETIC = (1 << 2), // For splashes
|
COSMETIC = (1 << 2), // For splashes
|
||||||
DEMAND_FAST = (1 << 3), // Special case only
|
DEMAND_FAST = (1 << 3), // Special case only
|
||||||
|
BLOCKING = (1 << 4), // Modifier - block while refresh runs
|
||||||
};
|
};
|
||||||
void setFrameFlag(frameFlagTypes flag);
|
void addFrameFlag(frameFlagTypes flag);
|
||||||
|
|
||||||
// Set the correct frame flag, then call universal "update()" method
|
// Set the correct frame flag, then call universal "update()" method
|
||||||
void display() override;
|
void display() override;
|
||||||
@ -48,7 +49,6 @@ class EInkDynamicDisplay : public EInkDisplay
|
|||||||
ASYNC_REFRESH_BLOCKED_COSMETIC,
|
ASYNC_REFRESH_BLOCKED_COSMETIC,
|
||||||
ASYNC_REFRESH_BLOCKED_RESPONSIVE,
|
ASYNC_REFRESH_BLOCKED_RESPONSIVE,
|
||||||
ASYNC_REFRESH_BLOCKED_BACKGROUND,
|
ASYNC_REFRESH_BLOCKED_BACKGROUND,
|
||||||
DISPLAY_NOT_READY_FOR_FULL,
|
|
||||||
EXCEEDED_RATELIMIT_FAST,
|
EXCEEDED_RATELIMIT_FAST,
|
||||||
EXCEEDED_RATELIMIT_FULL,
|
EXCEEDED_RATELIMIT_FULL,
|
||||||
FLAGGED_COSMETIC,
|
FLAGGED_COSMETIC,
|
||||||
@ -67,14 +67,16 @@ class EInkDynamicDisplay : public EInkDisplay
|
|||||||
void applyRefreshMode(); // Run any relevant GxEPD2 code, so next update will use correct refresh type
|
void applyRefreshMode(); // Run any relevant GxEPD2 code, so next update will use correct refresh type
|
||||||
void adjustRefreshCounters(); // Update fastRefreshCount
|
void adjustRefreshCounters(); // Update fastRefreshCount
|
||||||
bool update(); // Trigger the display update - determine mode, then call base class
|
bool update(); // Trigger the display update - determine mode, then call base class
|
||||||
|
void endOrDetach(); // Run the post-update code, or delegate it off to checkAsyncFullRefresh()
|
||||||
|
|
||||||
// Checks as part of determineMode()
|
// Checks as part of determineMode()
|
||||||
|
void checkInitialized(); // Is this the very first frame?
|
||||||
void checkForPromotion(); // Was a frame skipped (rate, display busy) that should have been a FAST refresh?
|
void checkForPromotion(); // Was a frame skipped (rate, display busy) that should have been a FAST refresh?
|
||||||
void checkRateLimiting(); // Is this frame too soon?
|
void checkRateLimiting(); // Is this frame too soon?
|
||||||
void checkCosmetic(); // Was the COSMETIC flag set?
|
void checkCosmetic(); // Was the COSMETIC flag set?
|
||||||
void checkDemandingFast(); // Was the DEMAND_FAST flag set?
|
void checkDemandingFast(); // Was the DEMAND_FAST flag set?
|
||||||
void checkConsecutiveFastRefreshes(); // Too many fast-refreshes consecutively?
|
|
||||||
void checkFrameMatchesPrevious(); // Does the new frame match the existing display image?
|
void checkFrameMatchesPrevious(); // Does the new frame match the existing display image?
|
||||||
|
void checkConsecutiveFastRefreshes(); // Too many fast-refreshes consecutively?
|
||||||
void checkFastRequested(); // Was the flag set for RESPONSIVE, or only BACKGROUND?
|
void checkFastRequested(); // Was the flag set for RESPONSIVE, or only BACKGROUND?
|
||||||
|
|
||||||
void resetRateLimiting(); // Set previousRunMs - this now counts as an update, for rate-limiting
|
void resetRateLimiting(); // Set previousRunMs - this now counts as an update, for rate-limiting
|
||||||
@ -82,14 +84,16 @@ class EInkDynamicDisplay : public EInkDisplay
|
|||||||
void storeAndReset(); // Keep results of determineMode() for later, tidy-up for next call
|
void storeAndReset(); // Keep results of determineMode() for later, tidy-up for next call
|
||||||
|
|
||||||
// What we are determining for this frame
|
// What we are determining for this frame
|
||||||
frameFlagTypes frameFlags = BACKGROUND; // Frame type(s) - determineMode() input
|
frameFlagTypes frameFlags = BACKGROUND; // Frame characteristics - determineMode() input
|
||||||
refreshTypes refresh = UNSPECIFIED; // Refresh type - determineMode() output
|
refreshTypes refresh = UNSPECIFIED; // Refresh type - determineMode() output
|
||||||
reasonTypes reason = NO_OBJECTIONS; // Reason - why was refresh type used
|
reasonTypes reason = NO_OBJECTIONS; // Reason - why was refresh type used
|
||||||
|
|
||||||
// What happened last time determineMode() ran
|
// What happened last time determineMode() ran
|
||||||
refreshTypes previousRefresh = UNSPECIFIED; // (Previous) Outcome
|
frameFlagTypes previousFrameFlags = BACKGROUND; // (Previous) Frame flags
|
||||||
reasonTypes previousReason = NO_OBJECTIONS; // (Previous) Reason
|
refreshTypes previousRefresh = UNSPECIFIED; // (Previous) Outcome
|
||||||
|
reasonTypes previousReason = NO_OBJECTIONS; // (Previous) Reason
|
||||||
|
|
||||||
|
bool initialized = false; // Have we drawn at least one frame yet?
|
||||||
uint32_t previousRunMs = -1; // When did determineMode() last run (rather than rejecting for rate-limiting)
|
uint32_t previousRunMs = -1; // When did determineMode() last run (rather than rejecting for rate-limiting)
|
||||||
uint32_t imageHash = 0; // Hash of the current frame. Don't bother updating if nothing has changed!
|
uint32_t imageHash = 0; // Hash of the current frame. Don't bother updating if nothing has changed!
|
||||||
uint32_t previousImageHash = 0; // Hash of the previous update's frame
|
uint32_t previousImageHash = 0; // Hash of the previous update's frame
|
||||||
@ -108,10 +112,16 @@ class EInkDynamicDisplay : public EInkDisplay
|
|||||||
// Conditional - async full refresh - only with modified meshtastic/GxEPD2
|
// Conditional - async full refresh - only with modified meshtastic/GxEPD2
|
||||||
#if defined(HAS_EINK_ASYNCFULL)
|
#if defined(HAS_EINK_ASYNCFULL)
|
||||||
void checkAsyncFullRefresh(); // Check the status of "async full-refresh"; run the post-update code if the hardware is ready
|
void checkAsyncFullRefresh(); // Check the status of "async full-refresh"; run the post-update code if the hardware is ready
|
||||||
void endOrDetach(); // Run the post-update code, or delegate it off to checkAsyncFullRefresh()
|
void awaitRefresh(); // Hold control while an async refresh runs
|
||||||
void endUpdate() override {} // Disable base-class behavior of running post-update immediately after forceDisplay()
|
void endUpdate() override {} // Disable base-class behavior of running post-update immediately after forceDisplay()
|
||||||
bool asyncRefreshRunning = false; // Flag, checked by checkAsyncFullRefresh()
|
bool asyncRefreshRunning = false; // Flag, checked by checkAsyncFullRefresh()
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tidier calls to addFrameFlag() from outside class
|
||||||
|
#define EINK_ADD_FRAMEFLAG(display, flag) static_cast<EInkDynamicDisplay *>(display)->addFrameFlag(EInkDynamicDisplay::flag)
|
||||||
|
|
||||||
|
#else // !USE_EINK_DYNAMICDISPLAY
|
||||||
|
// Dummy-macro, removes the need for include guards
|
||||||
|
#define EINK_ADD_FRAMEFLAG(display, flag)
|
||||||
#endif
|
#endif
|
@ -260,6 +260,10 @@ static void drawWelcomeScreen(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
|||||||
/// Used on eink displays while in deep sleep
|
/// Used on eink displays while in deep sleep
|
||||||
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
|
// Next frame should use full-refresh, and block while running, else device will sleep before async callback
|
||||||
|
EINK_ADD_FRAMEFLAG(display, COSMETIC);
|
||||||
|
EINK_ADD_FRAMEFLAG(display, BLOCKING);
|
||||||
|
|
||||||
drawIconScreen("Sleeping...", display, state, x, y);
|
drawIconScreen("Sleeping...", display, state, x, y);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1170,6 +1174,7 @@ int32_t Screen::runOnce()
|
|||||||
break;
|
break;
|
||||||
case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
|
case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
|
||||||
case Cmd::STOP_BOOT_SCREEN:
|
case Cmd::STOP_BOOT_SCREEN:
|
||||||
|
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
|
||||||
setFrames();
|
setFrames();
|
||||||
break;
|
break;
|
||||||
case Cmd::PRINT:
|
case Cmd::PRINT:
|
||||||
@ -1350,6 +1355,7 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
|||||||
{
|
{
|
||||||
LOG_DEBUG("showing bluetooth screen\n");
|
LOG_DEBUG("showing bluetooth screen\n");
|
||||||
showingNormalScreen = false;
|
showingNormalScreen = false;
|
||||||
|
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
|
||||||
|
|
||||||
static FrameCallback frames[] = {drawFrameBluetooth};
|
static FrameCallback frames[] = {drawFrameBluetooth};
|
||||||
snprintf(btPIN, sizeof(btPIN), "%06u", pin);
|
snprintf(btPIN, sizeof(btPIN), "%06u", pin);
|
||||||
@ -1367,6 +1373,7 @@ void Screen::handleShutdownScreen()
|
|||||||
{
|
{
|
||||||
LOG_DEBUG("showing shutdown screen\n");
|
LOG_DEBUG("showing shutdown screen\n");
|
||||||
showingNormalScreen = false;
|
showingNormalScreen = false;
|
||||||
|
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
|
||||||
|
|
||||||
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||||
drawFrameText(display, state, x, y, "Shutting down...");
|
drawFrameText(display, state, x, y, "Shutting down...");
|
||||||
@ -1380,6 +1387,7 @@ void Screen::handleRebootScreen()
|
|||||||
{
|
{
|
||||||
LOG_DEBUG("showing reboot screen\n");
|
LOG_DEBUG("showing reboot screen\n");
|
||||||
showingNormalScreen = false;
|
showingNormalScreen = false;
|
||||||
|
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
|
||||||
|
|
||||||
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||||
drawFrameText(display, state, x, y, "Rebooting...");
|
drawFrameText(display, state, x, y, "Rebooting...");
|
||||||
@ -1392,6 +1400,7 @@ void Screen::handleStartFirmwareUpdateScreen()
|
|||||||
{
|
{
|
||||||
LOG_DEBUG("showing firmware screen\n");
|
LOG_DEBUG("showing firmware screen\n");
|
||||||
showingNormalScreen = false;
|
showingNormalScreen = false;
|
||||||
|
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
|
||||||
|
|
||||||
static FrameCallback frames[] = {drawFrameFirmware};
|
static FrameCallback frames[] = {drawFrameFirmware};
|
||||||
setFrameImmediateDraw(frames);
|
setFrameImmediateDraw(frames);
|
||||||
|
@ -16,7 +16,7 @@ build_flags =
|
|||||||
-D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached.
|
-D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached.
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${esp32s3_base.lib_deps}
|
${esp32s3_base.lib_deps}
|
||||||
https://github.com/meshtastic/GxEPD2/
|
https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a
|
||||||
adafruit/Adafruit BusIO@^1.13.2
|
adafruit/Adafruit BusIO@^1.13.2
|
||||||
lewisxhe/PCF8563_Library@^1.0.1
|
lewisxhe/PCF8563_Library@^1.0.1
|
||||||
upload_speed = 115200
|
upload_speed = 115200
|
@ -16,7 +16,7 @@ build_flags =
|
|||||||
;-D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached.
|
;-D EINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached.
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${esp32s3_base.lib_deps}
|
${esp32s3_base.lib_deps}
|
||||||
https://github.com/meshtastic/GxEPD2/
|
https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a
|
||||||
adafruit/Adafruit BusIO@^1.13.2
|
adafruit/Adafruit BusIO@^1.13.2
|
||||||
lewisxhe/PCF8563_Library@^1.0.1
|
lewisxhe/PCF8563_Library@^1.0.1
|
||||||
upload_speed = 115200
|
upload_speed = 115200
|
@ -20,7 +20,7 @@ build_flags = ${nrf52840_base.build_flags} -Ivariants/t-echo
|
|||||||
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/t-echo>
|
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/t-echo>
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${nrf52840_base.lib_deps}
|
${nrf52840_base.lib_deps}
|
||||||
https://github.com/meshtastic/GxEPD2
|
https://github.com/meshtastic/GxEPD2#55f618961db45a23eff0233546430f1e5a80f63a
|
||||||
adafruit/Adafruit BusIO@^1.13.2
|
adafruit/Adafruit BusIO@^1.13.2
|
||||||
lewisxhe/PCF8563_Library@^1.0.1
|
lewisxhe/PCF8563_Library@^1.0.1
|
||||||
;upload_protocol = fs
|
;upload_protocol = fs
|
||||||
|
Loading…
Reference in New Issue
Block a user