Skip to content

Fix unbounded BUSY-pin spin in Panel_IT8951::_write_args() - #246

Merged
lovyan03 merged 3 commits into
m5stack:developfrom
thecybershotguy:fix/it8951-write-args-busy-timeout
Aug 13, 2026
Merged

Fix unbounded BUSY-pin spin in Panel_IT8951::_write_args()#246
lovyan03 merged 3 commits into
m5stack:developfrom
thecybershotguy:fix/it8951-write-args-busy-timeout

Conversation

@thecybershotguy

Copy link
Copy Markdown

Summary

  • Panel_IT8951::_write_args() polls the BUSY GPIO between argument words with a raw while (!lgfx::gpio_in(_cfg.pin_busy)); — unlike every other BUSY wait in this file, it has no timeout.
  • If BUSY ever gets stuck asserted (seen in practice after a panel power brownout, e.g. a boost-converter glitch when switching the M5Paper's e-paper supply between USB and battery), this loop spins forever with no way to recover short of a hardware power cycle. The rest of the calling stack (main loop, display flush, etc.) hangs with it.
  • _write_args() can't just call the existing _wait_busy() helper here, because _wait_busy() toggles chip-select (cs_control(true)/cs_control(false)) around its wait, while _write_args() deliberately holds CS asserted continuously across the whole multi-word burst (toggling CS per word would break the IT8951 multi-word write protocol).
  • Fix: extract the bounded, timeout-based BUSY poll out of _wait_busy() into a new CS-free helper, _wait_busy_pin(timeout = 4096) (same default 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, with CS left untouched, and bails out (return false) if BUSY doesn't clear within the timeout instead of spinning forever.

Why

Found while diagnosing a reproducible freeze on an M5Paper-based device: unplugging USB power (running on battery) reliably hung the device within 1–2 minutes, with no crash or reboot — just a dead main loop (no display updates, no input handling, no further activity of any kind). That pointed at a brief brownout on the panel's own boost converter during the power-source transition, which can leave the IT8951's BUSY line stuck asserted. Once that happens, the old unbounded loop in _write_args() never returns.

This mirrors the existing timeout/recovery behavior _wait_busy() already has for every other BUSY wait in this file — _write_args() was the one place that could still hang forever.

Testing

  • Read through the diff against every other BUSY-wait site in Panel_IT8951.cpp/.hpp to confirm timeout value (4096 ms) and CS-handling behavior are unchanged for existing callers.
  • No functional change to the success path: _write_args() still writes each word only after BUSY deasserts, CS is still held continuously across the burst.
  • Verified the fix off-target with a small fault-injection harness: the exact loop body from master and the exact loop body from this branch, run against a fake BUSY pin that's held stuck. Result — same behavior as this PR claims:
    • Normal operation (BUSY clears quickly): both versions return in ~30ms, identical.
    • BUSY stuck (simulated brownout): the master loop does not return at all (confirmed hung past a 3s watchdog); this branch's loop returns false at ~4096ms via the same wait_busy: timeout log line _wait_busy() already emits elsewhere in the file.
  • Not yet run against real M5Paper hardware — happy to verify against a specific reproduction if maintainers want it before merging.

lovyan03 and others added 3 commits July 9, 2026 14:18
_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 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prevents Panel_IT8951::_write_args() from potentially hanging forever by replacing an unbounded BUSY-pin spin with a bounded, timeout-based poll that preserves the IT8951 multi-word CS-burst requirements.

Changes:

  • Extracted a CS-free BUSY polling helper (_wait_busy_pin(timeout)) with the same default timeout used elsewhere.
  • Refactored _wait_busy() to wrap _wait_busy_pin() while preserving its CS toggling behavior for existing callers.
  • Updated _write_args() to use _wait_busy_pin() between argument words and fail out on timeout instead of spinning indefinitely.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/lgfx/v1/panel/Panel_IT8951.hpp Declares the new _wait_busy_pin() helper used to implement bounded BUSY polling without CS toggling.
src/lgfx/v1/panel/Panel_IT8951.cpp Implements _wait_busy_pin(), refactors _wait_busy() to call it, and replaces the unbounded BUSY spin in _write_args() with a timeout-based check.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/lgfx/v1/panel/Panel_IT8951.cpp
@lovyan03
lovyan03 changed the base branch from master to develop August 13, 2026 01:03
@lovyan03
lovyan03 merged commit 6368a16 into m5stack:develop Aug 13, 2026
23 checks passed
@lovyan03

Copy link
Copy Markdown
Collaborator

Hello, @thecybershotguy
Thank you for your contribution !

@thecybershotguy
thecybershotguy deleted the fix/it8951-write-args-busy-timeout branch August 14, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants