From d0f9c8213857c47812b064f3a7ca18ac4a0f0f06 Mon Sep 17 00:00:00 2001 From: thecybershotguy Date: Wed, 12 Aug 2026 13:35:39 -0600 Subject: [PATCH] Fix unbounded BUSY-pin spin in Panel_IT8951::_write_args() _write_args() polled the IT8951's BUSY GPIO between argument words with a raw while(!gpio_in(pin)) loop, unlike every other BUSY wait in this file. If BUSY gets stuck asserted (seen after a panel power brownout, e.g. switching an M5Paper's e-paper supply between USB and battery), this spins forever with no recovery short of a power cycle, hanging the whole calling stack. _write_args() can't just call the existing _wait_busy() helper because it toggles chip-select, while _write_args() must hold CS asserted continuously across a multi-word burst. Extract the bounded, timeout-based poll out of _wait_busy() into a new CS-free helper, _wait_busy_pin(timeout = 4096), matching the timeout already used elsewhere in this file. _wait_busy() now wraps it with CS handling exactly as before; _write_args() calls it directly per word and bails out instead of spinning forever. Co-Authored-By: Claude Sonnet 5 --- src/lgfx/v1/panel/Panel_IT8951.cpp | 29 +++++++++++++++++++++++++---- src/lgfx/v1/panel/Panel_IT8951.hpp | 1 + 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/lgfx/v1/panel/Panel_IT8951.cpp b/src/lgfx/v1/panel/Panel_IT8951.cpp index 223b4a0f..d3beaef4 100644 --- a/src/lgfx/v1/panel/Panel_IT8951.cpp +++ b/src/lgfx/v1/panel/Panel_IT8951.cpp @@ -241,10 +241,14 @@ IT8951 Registers defines _bus->endTransaction(); } - bool Panel_IT8951::_wait_busy(uint32_t timeout) + // Bounded poll of the BUSY pin, without touching chip-select. + // Returns false after `timeout` ms if BUSY never deasserts (e.g. the + // panel's boost converter browned out and left BUSY stuck), instead of + // spinning forever. Shared by _wait_busy() below and by _write_args(), + // which must keep CS asserted continuously across a multi-word burst + // and therefore can't use _wait_busy() directly. + bool Panel_IT8951::_wait_busy_pin(uint32_t timeout) { - _bus->wait(); - cs_control(true); if (_cfg.pin_busy >= 0 && !lgfx::gpio_in(_cfg.pin_busy)) { auto start_ms = millis(); @@ -264,6 +268,17 @@ IT8951 Registers defines } } while (!lgfx::gpio_in(_cfg.pin_busy)); } + return true; + } + + bool Panel_IT8951::_wait_busy(uint32_t timeout) + { + _bus->wait(); + cs_control(true); + if (!_wait_busy_pin(timeout)) + { + return false; + } cs_control(false); return true; } @@ -339,7 +354,13 @@ IT8951 Registers defines { uint32_t buf = getSwap16(args[i]); _bus->wait(); - while (!lgfx::gpio_in(_cfg.pin_busy)); + // CS must stay asserted for the whole multi-word burst, so this + // can't go through _wait_busy() (it toggles CS). A stuck BUSY + // line must still fail out here rather than hang forever. + if (!_wait_busy_pin()) + { + return false; + } _bus->writeData(buf, 16); } while ( ++i < length ); return true; diff --git a/src/lgfx/v1/panel/Panel_IT8951.hpp b/src/lgfx/v1/panel/Panel_IT8951.hpp index 0a44a700..89033e56 100644 --- a/src/lgfx/v1/panel/Panel_IT8951.hpp +++ b/src/lgfx/v1/panel/Panel_IT8951.hpp @@ -88,6 +88,7 @@ namespace lgfx uint16_t _vcom = 0; bool _wait_busy( uint32_t timeout = 4096); + bool _wait_busy_pin( uint32_t timeout = 4096); bool _write_command( uint16_t cmd); bool _write_word( uint16_t data); bool _write_args( uint16_t cmd, uint16_t *args, int32_t length);