From 0a899b112b12c86bd1ca09c0e56af728a547109e Mon Sep 17 00:00:00 2001 From: Brian Pugh Date: Thu, 2 Jul 2026 13:43:08 -0400 Subject: [PATCH] feat: optional hooks around long e-ink BUSY waits A refresh takes ~0.3-2 s during which pollBusy() only polls the BUSY pin at full CPU clock. Add a pair of optional plain-function-pointer hooks (setBusyWaitHooks) fired when a wait exceeds 20 ms and when it completes, so host firmware can apply its own power policy (e.g. reduce the CPU clock) for the wait window. No behavior change unless hooks are installed; short command waits never trigger them. Co-Authored-By: Claude Fable 5 --- .../display/EInkDisplay/include/EInkDisplay.h | 16 +++++++++++++ libs/display/EInkDisplay/src/EInkDisplay.cpp | 23 +++++++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) 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);