Skip to content

Stop a low charge voltage request from raising the charge voltage - #333

Merged
lovyan03 merged 2 commits into
m5stack:developfrom
ainyan03:charge_voltage_clamp
Aug 20, 2026
Merged

Stop a low charge voltage request from raising the charge voltage#333
lovyan03 merged 2 commits into
m5stack:developfrom
ainyan03:charge_voltage_clamp

Conversation

@ainyan03

Copy link
Copy Markdown
Contributor

What is wrong

Both setChargeVoltage implementations subtracted a bias from the argument before comparing it against their step table:

max_mV = (max_mV / 10) - 410;
if (max_mV > 436 - 410) { max_mV = 436 - 410; }

The argument is unsigned, so a request below the lowest step wraps around and is then clamped to the opposite end of the table. Asking for a gentler charge voltage produces a harsher one:

request AXP192 (REG33H[6:5]) AXP2101 (REG64H)
3800 - 4090 mV 4.36 V code 0 (reserved in current datasheet revisions)
4100 - 4500 mV correct correct
4600 mV and above correct code 0

A sketch that sets a lower ceiling for a small cell — M5.Power.setChargeVoltage(4000) — configures 4.36 V on the AXP192 instead.

The AXP2101 table also carried a 4.6 V entry. The current datasheet revisions document the constant-voltage register as 4.0 V through 4.4 V with code 0 reserved, so a request that reached that entry wrapped the index onto the reserved code as well.

What changed

Compare in millivolts against the real steps, take the highest step that does not exceed the request, and fall back to the lowest step when the request is under all of them. Every request inside the supported range keeps its previous result — only the two broken edges move.

Both headers now state the supported steps and what happens on either side of them, which nothing documented before.

Notes

  • An early AXP2101 datasheet revision assigned code 0 to 4.6 V, while current revisions reserve it. No battery this library runs on charges to 4.6 V, so a request that high is held at the highest step every revision agrees on rather than sent to a code whose meaning depends on the silicon. The comment records this rather than asserting one revision.
  • The parameter stays uint16_t, so a negative argument still converts to a large positive one and selects the highest step. That is unchanged by this PR and is better addressed where the setter contract itself is revised.
  • setChargeCurrent has the same shape of problem (a request below the lowest step is raised to it, and on the AXP2101 the 25/50/75 mA steps are unreachable). Left out of this PR deliberately: it changes what setChargeCurrent(0) means, which deserves its own discussion.

Verified on hardware

Register read-back after each request, with the original value restored afterwards:

  • Core2 (AXP192, chip id 0x03) — 3800 / 4000 / 4090 mV now select 4.10 V where they selected 4.36 V; 4100 / 4150 / 4200 / 4350 / 4360 / 4400 / 4600 / 5000 mV unchanged.
  • Core2 v1.1 (AXP2101, chip id 0x4A) — 3800 - 4090 mV now select 4.00 V and 4600 mV and above select 4.40 V, where both wrote code 0; 4100 - 4400 mV unchanged.

Both setChargeVoltage implementations subtracted a bias from the argument
before comparing it against their step table. The argument is unsigned, so
a request below the lowest step wrapped around and then clamped to the
opposite end of the table: on the AXP192 a request for 4.0V configured
4.36V, and on the AXP2101 the same request selected the reserved code 0.
Asking for a gentler charge voltage gave a harsher one.

The AXP2101 table also carried a 4.6V step that the part does not have -
its constant-voltage register goes up to 4.4V - so a request that reached
that entry wrapped the index back onto the reserved code as well.

Compare in millivolts against the real steps instead, take the highest
step that does not exceed the request, and fall back to the lowest step
when the request is under all of them. Every request inside the supported
range keeps its previous result.
Review pointed out that the AXP2101 constant-voltage register is documented
differently across datasheet revisions: the current ones reserve code 0
while an early one gave it to 4.6V. The change had asserted flatly that no
step above 4.4V exists, which only holds for the revisions available now.

State what every revision agrees on instead, and give the reason for
holding a high request at 4.4V: no battery this library runs on charges to
4.6V, so the code whose meaning depends on the silicon is worth avoiding.

Both headers now carry the supported steps and what happens on either side
of them, which nothing stated before.
@lovyan03
lovyan03 merged commit f4a0cc4 into m5stack:develop Aug 20, 2026
27 checks passed
@ainyan03
ainyan03 deleted the charge_voltage_clamp branch August 20, 2026 06:07
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