fix: allow timeout if display update fails

Result is not graceful, but avoids total display lockup requiring power cycle.
Typical cause of failure is poor wiring / power supply.
This commit is contained in:
Todd Herbert 2025-03-24 13:22:40 +13:00
parent b64729987e
commit 97b36f3783
4 changed files with 50 additions and 7 deletions

View File

@ -6,7 +6,7 @@ using namespace NicheGraphics::Drivers;
// Separate from EInk::begin method, as derived class constructors can probably supply these parameters as constants // Separate from EInk::begin method, as derived class constructors can probably supply these parameters as constants
EInk::EInk(uint16_t width, uint16_t height, UpdateTypes supported) EInk::EInk(uint16_t width, uint16_t height, UpdateTypes supported)
: concurrency::OSThread("E-Ink Driver"), width(width), height(height), supportedUpdateTypes(supported) : concurrency::OSThread("EInkDriver"), width(width), height(height), supportedUpdateTypes(supported)
{ {
OSThread::disable(); OSThread::disable();
} }
@ -31,8 +31,9 @@ bool EInk::supports(UpdateTypes type)
void EInk::beginPolling(uint32_t interval, uint32_t expectedDuration) void EInk::beginPolling(uint32_t interval, uint32_t expectedDuration)
{ {
updateRunning = true; updateRunning = true;
updateBegunAt = millis();
pollingInterval = interval; pollingInterval = interval;
pollingBegunAt = millis();
pollingExpectedDuration = expectedDuration;
// To minimize load, we can choose to delay polling for a few seconds, if we know roughly how long the update will take // To minimize load, we can choose to delay polling for a few seconds, if we know roughly how long the update will take
// By default, expectedDuration is 0, and we'll start polling immediately // By default, expectedDuration is 0, and we'll start polling immediately
@ -45,10 +46,25 @@ void EInk::beginPolling(uint32_t interval, uint32_t expectedDuration)
// This is what allows us to update the display asynchronously // This is what allows us to update the display asynchronously
int32_t EInk::runOnce() int32_t EInk::runOnce()
{ {
// Check for polling timeout
if (millis() - pollingBegunAt > pollingExpectedDuration * 3)
failed = true;
// Handle failure
// - polling timeout
// - other error (derived classes)
if (failed) {
LOG_WARN("Display update failed. Check wiring & power supply.");
updateRunning = false;
failed = false;
return disable();
}
// If update not yet done
if (!isUpdateDone()) if (!isUpdateDone())
return pollingInterval; // Poll again in a few ms return pollingInterval; // Poll again in a few ms
// If update done: // If update done
finalizeUpdate(); // Any post-update code: power down panel hardware, hibernate, etc finalizeUpdate(); // Any post-update code: power down panel hardware, hibernate, etc
updateRunning = false; // Change what we report via EInk::busy() updateRunning = false; // Change what we report via EInk::busy()
return disable(); // Stop polling return disable(); // Stop polling

View File

@ -41,14 +41,16 @@ class EInk : private concurrency::OSThread
void beginPolling(uint32_t interval, uint32_t expectedDuration); // Begin checking repeatedly if update finished void beginPolling(uint32_t interval, uint32_t expectedDuration); // Begin checking repeatedly if update finished
virtual bool isUpdateDone() = 0; // Check once if update finished virtual bool isUpdateDone() = 0; // Check once if update finished
virtual void finalizeUpdate() {} // Run any post-update code virtual void finalizeUpdate() {} // Run any post-update code
bool failed = false; // If an error occurred during update
private: private:
int32_t runOnce() override; // Repeated checking if update finished int32_t runOnce() override; // Repeated checking if update finished
const UpdateTypes supportedUpdateTypes; // Capabilities of a derived display class const UpdateTypes supportedUpdateTypes; // Capabilities of a derived display class
bool updateRunning = false; // see EInk::busy() bool updateRunning = false; // see EInk::busy()
uint32_t updateBegunAt = 0; // For initial pause before polling for update completion
uint32_t pollingInterval = 0; // How often to check if update complete (ms) uint32_t pollingInterval = 0; // How often to check if update complete (ms)
uint32_t pollingBegunAt = 0; // To timeout during polling
uint32_t pollingExpectedDuration = 0; // To timeout during polling
}; };
} // namespace NicheGraphics::Drivers } // namespace NicheGraphics::Drivers

View File

@ -37,11 +37,26 @@ void SSD16XX::begin(SPIClass *spi, uint8_t pin_dc, uint8_t pin_cs, uint8_t pin_b
reset(); reset();
} }
void SSD16XX::wait() // Poll the displays busy pin until an operation is complete
// Timeout and set fail flag if something went wrong and the display got stuck
void SSD16XX::wait(uint32_t timeout)
{ {
// Don't bother waiting if part of the update sequence failed
// In that situation, we're now just failing-through the process, until we can try again with next update.
if (failed)
return;
uint32_t startMs = millis();
// Busy when HIGH // Busy when HIGH
while (digitalRead(pin_busy) == HIGH) while (digitalRead(pin_busy) == HIGH) {
// Check for timeout
if (millis() - startMs > timeout) {
failed = true;
break;
}
yield(); yield();
}
} }
void SSD16XX::reset() void SSD16XX::reset()
@ -62,6 +77,11 @@ void SSD16XX::reset()
void SSD16XX::sendCommand(const uint8_t command) void SSD16XX::sendCommand(const uint8_t command)
{ {
// Abort if part of the update sequence failed
// This will unlock again once we have failed-through the entire process
if (failed)
return;
spi->beginTransaction(spiSettings); spi->beginTransaction(spiSettings);
digitalWrite(pin_dc, LOW); // DC pin low indicates command digitalWrite(pin_dc, LOW); // DC pin low indicates command
digitalWrite(pin_cs, LOW); digitalWrite(pin_cs, LOW);
@ -78,6 +98,11 @@ void SSD16XX::sendData(uint8_t data)
void SSD16XX::sendData(const uint8_t *data, uint32_t size) void SSD16XX::sendData(const uint8_t *data, uint32_t size)
{ {
// Abort if part of the update sequence failed
// This will unlock again once we have failed-through the entire process
if (failed)
return;
spi->beginTransaction(spiSettings); spi->beginTransaction(spiSettings);
digitalWrite(pin_dc, HIGH); // DC pin HIGH indicates data, instead of command digitalWrite(pin_dc, HIGH); // DC pin HIGH indicates data, instead of command
digitalWrite(pin_cs, LOW); digitalWrite(pin_cs, LOW);

View File

@ -27,7 +27,7 @@ class SSD16XX : public EInk
virtual void update(uint8_t *imageData, UpdateTypes type) override; virtual void update(uint8_t *imageData, UpdateTypes type) override;
protected: protected:
virtual void wait(); virtual void wait(uint32_t timeout = 1000);
virtual void reset(); virtual void reset();
virtual void sendCommand(const uint8_t command); virtual void sendCommand(const uint8_t command);
virtual void sendData(const uint8_t data); virtual void sendData(const uint8_t data);