Skip to content

axp192

X-Powers AXP192 — the power-management IC on the M5Stack Core2 (and the M5StickC family).

Layer 3: it knows the PMIC, not the chip or board driving it, and talks through a [BusHandle] on I2C at [ADDR]. What each rail powers is a board fact and lives in the board manifest, not here — the same DCDC3 that is the Core2’s LCD backlight is something else on another board.

The AXP192 has three buck converters (DCDC1–3) and two LDOs (LDO2–3), each enabled by one bit of the output-control register (0x12) and set by a voltage register. This driver models them as [Rail] and a const config table transliterated from the hardware-agnostic reference driver tuupola/axp192 (MIT) — min/max/step in millivolts, the voltage register, and where the value sits in it:

Rail enable bit (0x12) voltage reg range / step
DCDC1 0 0x26[6:0] 700–3500 / 25 mV
DCDC2 4 0x23[5:0] 700–2275 / 25 mV
DCDC3 1 0x27[6:0] 700–3500 / 25 mV
LDO2 2 0x28[7:4] 1800–3300 / 100 mV
LDO3 3 0x28[3:0] 1800–3300 / 100 mV

LDO2 and LDO3 share one register (0x28), one in each nibble, so setting one must preserve the other — hence every voltage write is read-modify-write and masked to the rail’s field. The same is true of the single enable register, which is why enabling a rail reads 0x12 first rather than writing a fresh byte.

DCDC1 is the system rail — this driver never asserts a policy about it

Section titled “DCDC1 is the system rail — this driver never asserts a policy about it”

On the Core2, DCDC1 powers the ESP32 running this code. Turning it off or moving its voltage under the CPU browns the board out. This driver exposes DCDC1 like any other rail because it is one, but a board must never list it for automatic bring-up; that invariant is enforced where the board’s rail list is applied (see the board crate’s power_init), not here — a driver that silently refused a rail would be lying about what the part can do.

Voltages are u16 millivolts and the encode/decode is integer arithmetic, for the same reason the IMU driver keeps to integers: a float in a driver is a float in whatever calls it, and on Xtensa that pulls the FPU into every context switch that touches it.

struct Axp192<'a> { /* private fields */ }

An AXP192 on a bus already addressed to [ADDR].

enum AxpError {
Bus(api::bus::BusError),
VoltageOutOfRange { rail: Rail, millivolts: u16 },
UnsupportedGpio(u8),
}

Why an AXP192 operation failed.

Implements: Clone, Copy, Debug, Eq, From, PartialEq, StructuralPartialEq

References: BusError, Rail

enum Rail {
Dcdc1,
Dcdc2,
Dcdc3,
Ldo2,
Ldo3,
}

One of the AXP192’s five switchable outputs.

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq

const ADDR: u8;

I2C address of the AXP192. Fixed in the part; not a board fact.

type AxpResult<T> = Result<T, AxpError>;

Result of an AXP192 operation.

References: AxpError