Skip to content

Power: PaperMono: do not touch the charger when it is in LED mode - #394

Merged
lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:papermono_gate_guard
Oct 7, 2026
Merged

lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:papermono_gate_guard

Conversation

@ainyan03

@ainyan03 ainyan03 commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

On PaperMono, M5.Power.getChargeState() / isCharging() / setBatteryCharge() can stall the board (screen frozen, USB port gone, only the power key recovers it) when the USB charger is in LED mode.

Why it happens

  • The IP2316 charger latches its mode when VIN is applied: I2C mode only if SCL and SDA are both High at that moment, otherwise LED mode until VIN is removed.
  • On PaperMono these lines are pulled High only while the level-shifter gate (IOE1 GPIO11) is open. Plugging USB in with the gate closed — always the case while the board is powered off — gives LED mode.
  • In LED mode the charger drives LED1/LED2 (= SCL/SDA) at about 500 Hz. The charge APIs opened the gate and polled 0x75 up to 64 times on that disturbed internal bus, which stalls the board. Writes in that state can also land corrupted (an IOE1 output byte written as 0x30 read back as 0x1A).

What this changes

  • Before any I2C traffic, the bus is watched with plain GPIO reads for 4.5 ms after opening the gate. If a line goes Low, the gate is closed again between the charger pulses and the whole IOE1 output byte is confirmed by reading it back.
  • LED mode is remembered until the PM1 reports no VIN. In LED mode getChargeState() returns undetermined and setBatteryCharge() returns false.
  • A gate that cannot be confirmed closed is retried first on the next call of either API.

Tested on PaperMono

case result
USB plugged in with the gate closed (LED mode), getChargeState() x100 no stall, undetermined x100, IOE1 output byte unchanged
same, setBatteryCharge(true) returns false, byte unchanged
USB removed not_charging
USB plugged in with the gate open (I2C mode), getChargeState() x20 charging x20, as before

Limits (also in the code): VIN removed and reapplied between two calls is not seen, so LED mode stays remembered; only these two APIs retry an unconfirmed close.

The IP2316 latches its mode when VIN is applied: I2C mode only if SCL and SDA are both
High at that moment, otherwise LED mode until VIN is removed. On PaperMono the lines are
only pulled High while the level-shifter gate (IOE1 GPIO11) is open, so plugging USB in
with the gate closed, which is always the case while powered off, gives LED mode. In LED
mode the charger drives LED1/LED2 (= SCL/SDA) at about 500 Hz. getChargeState() and
setBatteryCharge() opened the gate and polled 0x75 up to 64 times on that disturbed bus,
which stalled the board until the power key was pressed; writes in that state can also
land corrupted.

The gate is now opened with the IOE1 output byte saved first, and the bus is checked with
plain GPIO reads (own outputs disabled) for 4.5 ms before any I2C traffic; a watch with a
long gap between reads is repeated, and a bus that could not be watched is an io_error.
If a line goes Low, the charger is in LED mode: the gate is closed again between the
charger pulses and the whole byte is confirmed by reading it back (up to 20 tries). A
gate that cannot be confirmed closed is remembered with the byte to restore, and both
APIs retry closing it before any other bus access (io_error / false until it succeeds).
LED mode is remembered, only on a clean judgement with VIN present, until the PM1
reports no VIN; both getChargeState() and setBatteryCharge() read the power source for
this. In LED mode getChargeState() returns undetermined and setBatteryCharge() returns
false. The sequence assumes no other task uses the internal bus meanwhile, like the rest
of the power functions.

Limits: VIN removed and reapplied between two calls is not seen, so LED mode stays
remembered; only these two APIs retry an unconfirmed close.
@lovyan03
lovyan03 merged commit d58a006 into m5stack:develop Oct 7, 2026
30 checks passed
@ainyan03
ainyan03 deleted the papermono_gate_guard branch October 7, 2026 06:45
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.

2 participants