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
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();
}
@ -31,8 +31,9 @@ bool EInk::supports(UpdateTypes type)
void EInk::beginPolling(uint32_t interval, uint32_t expectedDuration)
{
updateRunning = true;
updateBegunAt = millis();
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
// 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
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())
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
updateRunning = false; // Change what we report via EInk::busy()
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
virtual bool isUpdateDone() = 0; // Check once if update finished
virtual void finalizeUpdate() {} // Run any post-update code
bool failed = false; // If an error occurred during update
private:
int32_t runOnce() override; // Repeated checking if update finished
const UpdateTypes supportedUpdateTypes; // Capabilities of a derived display class
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 pollingBegunAt = 0; // To timeout during polling
uint32_t pollingExpectedDuration = 0; // To timeout during polling
};
} // 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();
}
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
while (digitalRead(pin_busy) == HIGH)
while (digitalRead(pin_busy) == HIGH) {
// Check for timeout
if (millis() - startMs > timeout) {
failed = true;
break;
}
yield();
}
}
void SSD16XX::reset()
@ -62,6 +77,11 @@ void SSD16XX::reset()
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);
digitalWrite(pin_dc, LOW); // DC pin low indicates command
digitalWrite(pin_cs, LOW);
@ -78,6 +98,11 @@ void SSD16XX::sendData(uint8_t data)
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);
digitalWrite(pin_dc, HIGH); // DC pin HIGH indicates data, instead of command
digitalWrite(pin_cs, LOW);

View File

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