diff --git a/src/GxEPD2_BW.h b/src/GxEPD2_BW.h index 4a3d6f0..44be16f 100644 --- a/src/GxEPD2_BW.h +++ b/src/GxEPD2_BW.h @@ -12,6 +12,11 @@ #ifndef _GxEPD2_BW_H_ #define _GxEPD2_BW_H_ +// This version of meshtastic/GxEPD2 has been modified (defiled) to allow nextPage() to run full refreshes asynchronously +// This macro ensures that the relevant modifications to meshtastic/firmware will only run with a correctly modified version of GxEPD2 +// Note: this async behavior is unrelated to the callback system implemented in newer versions of ZinggJM/GxEPD2 +#define HAS_EINK_ASYNCFULL + // uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX //#include @@ -239,6 +244,16 @@ class GxEPD2_BW : public GxEPD2_GFX_BASE_CLASS _second_phase = false; } + // Transplanted from nextPage(), non-paged, full refresh + // This is the code which runs after the refresh() call + // Method is called by meshtastic/firmware, once polling GxEPD2_BW::isBusy() reports that the physical refresh is complete + void endAsyncFull() { + if (epd2.hasFastPartialUpdate) { + epd2.writeImageAgain(_buffer, 0, 0, WIDTH, HEIGHT); + } + epd2.powerOff(); + } + bool nextPage() { if (1 == _pages) @@ -258,12 +273,16 @@ class GxEPD2_BW : public GxEPD2_GFX_BASE_CLASS { epd2.writeImageForFullRefresh(_buffer, 0, 0, WIDTH, HEIGHT); epd2.refresh(false); +#if defined(USE_EINK_DYNAMICDISPLAY) // This macro defined in meshtastic/firmware + // -- meshtastic: moved to endAsyncFull() -- +#else if (epd2.hasFastPartialUpdate) { epd2.writeImageAgain(_buffer, 0, 0, WIDTH, HEIGHT); //epd2.refresh(true); // not needed } epd2.powerOff(); +#endif } return false; } diff --git a/src/GxEPD2_EPD.cpp b/src/GxEPD2_EPD.cpp index 243f400..59aba58 100644 --- a/src/GxEPD2_EPD.cpp +++ b/src/GxEPD2_EPD.cpp @@ -96,6 +96,10 @@ void GxEPD2_EPD::_reset() void GxEPD2_EPD::_waitWhileBusy(const char* comment, uint16_t busy_time) { + // For full refreshes, meshtastic/firmare will poll GxEPD2_EPD::isBusy() instead of waiting here + if (_isUpdatingFull(comment)) + return; + if (_busy >= 0) { delay(1); // add some margin to become active @@ -241,3 +245,19 @@ void GxEPD2_EPD::_endTransfer() if (_cs >= 0) digitalWrite(_cs, HIGH); _spi.endTransaction(); } + +// Polled by meshtastic/firmware, during async full-refresh +bool GxEPD2_EPD::isBusy() { + return (digitalRead(_busy) == _busy_level); +} + +// Used to skip _waitWhileBusy(), for meshtastic async +bool GxEPD2_EPD::_isUpdatingFull(const char* comment) { + // On first update, a fast-refresh might also clear screen using a full-refresh + // If meshtastic/firmware asked for fast-refresh, it will expect this full-refresh to block. So: force that to happen + if (_initial_refresh) + return false; + + // True if comment matches + return ( strcmp(comment, "_Update_Full") == 0 ); +} diff --git a/src/GxEPD2_EPD.h b/src/GxEPD2_EPD.h index 2d78ca3..704b55a 100644 --- a/src/GxEPD2_EPD.h +++ b/src/GxEPD2_EPD.h @@ -91,6 +91,7 @@ class GxEPD2_EPD { return (a > b ? a : b); }; + bool isBusy(); // Used in meshtastic/firmware, to poll after nextPage(), for async full refresh protected: void _reset(); void _waitWhileBusy(const char* comment = 0, uint16_t busy_time = 5000); @@ -104,6 +105,7 @@ class GxEPD2_EPD void _startTransfer(); void _transfer(uint8_t value); void _endTransfer(); + bool _isUpdatingFull(const char* comment); // Meshtastic: Determine if _waitWhileBusy() should be skipped (for async), by comparing the comment string.. protected: int8_t _cs, _dc, _rst, _busy, _busy_level; uint32_t _busy_timeout; diff --git a/src/epd/GxEPD2_213_FC1.cpp b/src/epd/GxEPD2_213_FC1.cpp index 31d1b1c..99fe166 100644 --- a/src/epd/GxEPD2_213_FC1.cpp +++ b/src/epd/GxEPD2_213_FC1.cpp @@ -38,6 +38,7 @@ void GxEPD2_213_FC1::clearScreen(uint8_t value) _writeScreenBuffer(0x13, value); // Clear "NEW" (red) mem _writeScreenBuffer(0x10, value); // Clear "OLD" (black) mem refresh(false); // Full refresh + _PowerOff(); _initial_write = false; _initial_refresh = false; }