|
|
@@ -8,6 +8,8 @@
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
+#include "esphome/core/application.h"
|
|
|
+#include "esphome/core/hal.h"
|
|
|
#include "esphome/components/spi/spi.h"
|
|
|
|
|
|
class SPISettings {
|
|
|
@@ -36,9 +38,27 @@ class SPIClass : public esphome::spi::SPIDevice<esphome::spi::BIT_ORDER_MSB_FIRS
|
|
|
// every panel command. write_byte() only ever asks to write, so it works
|
|
|
// whether or not MISO is wired.
|
|
|
uint8_t transfer(uint8_t data) {
|
|
|
+ // A full-frame refresh calls this thousands of times back-to-back (a
|
|
|
+ // 4-color panel's write loop alone is tens of thousands of bytes), with
|
|
|
+ // no yield anywhere in GxEPD2's own per-byte loops, and ESP-IDF's
|
|
|
+ // polling-mode SPI transaction doesn't yield the scheduler while it
|
|
|
+ // waits either. Left unchecked, that starves this core's idle task
|
|
|
+ // long enough to trip ITS watchdog - a hard crash, not just a stall.
|
|
|
+ // App.feed_wdt() alone doesn't fix this: it resets our own task's
|
|
|
+ // watchdog, not the idle task's, and a plain yield() just hands the
|
|
|
+ // CPU right back to us since nothing else made us not-ready. Only a
|
|
|
+ // real blocking delay actually lets a lower-priority task like idle
|
|
|
+ // run, so force one periodically.
|
|
|
+ if ((this->transfer_count_++ & 0xFF) == 0) {
|
|
|
+ esphome::App.feed_wdt();
|
|
|
+ esphome::delay(1);
|
|
|
+ }
|
|
|
this->write_byte(data);
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+ private:
|
|
|
+ uint32_t transfer_count_{0};
|
|
|
};
|
|
|
|
|
|
// Unused: every gxepd2 instance overrides this via selectSPI() with its own
|