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..47b798f 100644 --- a/src/GxEPD2_EPD.cpp +++ b/src/GxEPD2_EPD.cpp @@ -96,6 +96,12 @@ 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 (for EInkDynamicDisplay only) +#if defined(USE_EINK_DYNAMICDISPLAY) + if (_isUpdatingFull(comment)) + return; +#endif + if (_busy >= 0) { delay(1); // add some margin to become active @@ -241,3 +247,14 @@ 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) { + // 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 b724a15..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; } @@ -158,19 +159,10 @@ void GxEPD2_213_FC1::powerOff() _PowerOff(); } -// Put the display into an extra-low power state. Hard reset required to wake. -// Caution: will wipe display memory - problematic for fast-refresh, so not used by meshtastic +// No hibernate for this display, only power off. Preserves image memory for fast refresh. void GxEPD2_213_FC1::hibernate() { _PowerOff(); - if (_rst >= 0) - { - _writeCommand(0x07); // deep sleep mode - _writeData(0xA5); // enter deep sleep - _hibernating = true; - _configured_for_full = false; - _configured_for_fast = false; - } } void GxEPD2_213_FC1::_PowerOn() @@ -280,7 +272,6 @@ void GxEPD2_213_FC1::_Update_Full() _PowerOn(); _writeCommand(0x12); _waitWhileBusy("_Update_Full", full_refresh_time); - _PowerOff(); } void GxEPD2_213_FC1::_Update_Part() @@ -289,7 +280,6 @@ void GxEPD2_213_FC1::_Update_Part() _PowerOn(); _writeCommand(0x12); _waitWhileBusy("_Update_Part", partial_refresh_time); - _PowerOff(); } // Fast refresh waveform is unofficial (experimental?) diff --git a/src/epd/GxEPD2_213_FC1.h b/src/epd/GxEPD2_213_FC1.h index f07293d..74dafd2 100644 --- a/src/epd/GxEPD2_213_FC1.h +++ b/src/epd/GxEPD2_213_FC1.h @@ -44,7 +44,7 @@ class GxEPD2_213_FC1 : public GxEPD2_EPD void refresh(bool partial_update_mode = false); // screen refresh from controller memory to full screen void refresh(int16_t x, int16_t y, int16_t w, int16_t h); // screen refresh from controller memory, fast-refresh void powerOff(); // turns off generation of panel driving voltages, avoids screen fading over time - void hibernate(); // turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0) + void hibernate(); // For this display, no deep sleep, only power off. Preserves image memory for fast refresh // Unimplemented for meshtastic public: