diff --git a/libs/display/EInkDisplay/include/EInkDisplay.h b/libs/display/EInkDisplay/include/EInkDisplay.h index d591ba4a..13a395c7 100644 --- a/libs/display/EInkDisplay/include/EInkDisplay.h +++ b/libs/display/EInkDisplay/include/EInkDisplay.h @@ -121,6 +121,17 @@ class EInkDisplay { // Power management void deepSleep(); + // Optional hooks fired around long BUSY waits. A refresh takes ~0.3-2 s during + // which the CPU only polls the BUSY pin; these let host firmware save power in + // that window (e.g. reduce the CPU clock) without the SDK knowing the policy. + // The begin hook fires once a wait exceeds BUSY_WAIT_HOOK_THRESHOLD_MS (so + // short command waits never pay for it); the matching end hook fires when the + // wait completes. Plain function pointers; both default to disabled. + void setBusyWaitHooks(void (*beginHook)(), void (*endHook)()) { + _busyWaitBeginHook = beginHook; + _busyWaitEndHook = endHook; + } + // Access to frame buffer uint8_t* getFrameBuffer() const { return frameBuffer; @@ -133,6 +144,11 @@ class EInkDisplay { // Internal geometry setter used by setDisplayX3(). void setDisplayDimensions(uint16_t width, uint16_t height); + // Busy-wait hooks (see setBusyWaitHooks) + static constexpr unsigned long BUSY_WAIT_HOOK_THRESHOLD_MS = 20; + void (*_busyWaitBeginHook)() = nullptr; + void (*_busyWaitEndHook)() = nullptr; + // Pin configuration int8_t _sclk, _mosi, _cs, _dc, _rst, _busy; diff --git a/libs/display/EInkDisplay/src/EInkDisplay.cpp b/libs/display/EInkDisplay/src/EInkDisplay.cpp index 3640aad5..4d65cdb3 100644 --- a/libs/display/EInkDisplay/src/EInkDisplay.cpp +++ b/libs/display/EInkDisplay/src/EInkDisplay.cpp @@ -579,10 +579,19 @@ void EInkDisplay::waitForRefresh(const char *comment) { void EInkDisplay::pollBusy(const char *comment, const char *completeWord) { unsigned long start = millis(); + // The begin hook fires lazily, only once the wait has proven long (see + // setBusyWaitHooks); hookFired guarantees the end hook is balanced with it. + bool hookFired = false; + bool x3SawLow = false; if (!_x3Mode) { // X4: BUSY held HIGH while busy, drops LOW when done. while (digitalRead(_busy) == HIGH) { delay(1); + if (!hookFired && _busyWaitBeginHook != nullptr && + millis() - start > BUSY_WAIT_HOOK_THRESHOLD_MS) { + hookFired = true; + _busyWaitBeginHook(); + } if (millis() - start > 30000) break; } @@ -595,23 +604,29 @@ void EInkDisplay::pollBusy(const char *comment, const char *completeWord) { // LOW -> HIGH edge. If we never observe the LOW phase the operation // either completed faster than we could see or was a no-op, and we // skip the completion log line. - bool sawLow = false; while (digitalRead(_busy) == HIGH) { delay(1); if (millis() - start > 1000) break; } if (digitalRead(_busy) == LOW) { - sawLow = true; + x3SawLow = true; while (digitalRead(_busy) == LOW) { delay(1); + if (!hookFired && _busyWaitBeginHook != nullptr && + millis() - start > BUSY_WAIT_HOOK_THRESHOLD_MS) { + hookFired = true; + _busyWaitBeginHook(); + } if (millis() - start > 30000) break; } } - if (!sawLow) - return; } + if (hookFired && _busyWaitEndHook != nullptr) + _busyWaitEndHook(); + if (_x3Mode && !x3SawLow) + return; if (comment && Serial) Serial.printf("[%lu] %s: %s (%lu ms)\n", millis(), completeWord, comment, millis() - start);