AXP2101 Power Management IC Driver (axp2101-dd)
This crate provides a no_std driver for the AXP2101 power management IC, a highly integrated PMIC for single-cell Li-battery applications. The project aims to support the full functionality of the AXP2101 PMIC, leveraging the device-driver crate with a declarative YAML manifest (device.yaml) for a robust, type-safe register map definition. The low-level API covers 100% of the AXP2101's registers, with device.yaml providing a comprehensive and accurate description of all registers and their fields. While the low-level API is complete, some high-level convenience methods for easier access may still be added in the future.
Overview
The axp2101-dd driver offers:
- Declarative Configuration: The AXP2101 register map is defined in
device.yaml, enablingdevice-driverto generate a type-safe, low-level register access API. This approach enhances maintainability and extensibility. - Unified Async/Blocking API: Uses the
bisynccrate to provide both asynchronous (Axp2101Async) and blocking (Axp2101) drivers from the same codebase, with no feature flags required. - High-Level and Low-Level APIs:
- High-level methods simplify tasks like setting DC-DC/LDO voltages, reading ADC values, managing battery charging.
- Low-level API (via the
llfield of theAxp2101/Axp2101Asyncstruct) offers direct, type-safe access to all registers defined indevice.yamlvia raw values or enums.
- Peripheral Control: Manages 5 DC-DCs, 11 LDOs, ADC conversions, interrupts, battery charging, fuel gauge, and power settings.
no_stdandno-alloc: Optimized for bare-metal and RTOS environments.- Optional Logging: Supports
defmtand thelogfacade for debugging.
⚠️ Warning! ⚠️
Caution! The AXP2101 controls power to the microcontroller and critical components. Incorrect configuration may cause malfunctions, data loss, or hardware damage.
- Always consult the official AXP2101 datasheet before modifying power settings.
- Verify voltage and current limits for your device and components.
- Exercise caution when adjusting output voltages or charging parameters.
- The authors are not liable for damage caused by misuse.
Features
- Declarative Register Map: Defined in
device.yaml. - Unified Async/Blocking API: Both async and blocking drivers are always available; no feature flags required.
- Type-Safe Register Access: Generated by
device-driver. - Comprehensive Control: (See
device.yamlfor details)- 5 DC-DC converters (DCDC1-5) with voltage control.
- 11 LDO outputs (ALDO1-4, BLDO1-2, DLDO1-2, CPUSLDO, RTCLDO1-2).
- Battery charging configuration and status.
- 14-bit ADC readings (battery voltage, VBUS, VSYS, die temperature, TS pin, GPADC).
- Fuel gauge for battery percentage.
- Interrupt management.
- Power key (PWRON) parameters.
no_stdandno-alloc.- Optional Logging: Supports
defmtandlogfacade.
Getting Started
-
Add
axp2101-ddtoCargo.toml:[] = "0.1.0" # For blocking usage (Axp2101): = "1.0.0" # For async usage (Axp2101Async): = "1.0.0"Note: Add the relevant
embedded-halcrate for your use case, no need for both- Use
embedded-halfor blocking drivers (Axp2101) - Use
embedded-hal-asyncfor async drivers (Axp2101Async)
- Use
-
Instantiate the driver with your I2C bus:
-
Blocking:
use ; use I2c; let i2c_bus_impl = /* your I2C bus */; let mut axp = new; // Configure power outputs axp.set_dcdc_voltage?; axp.set_dcdc_enable?; axp.set_ldo_voltage_mv?; axp.set_ldo_enable?; // Enable and read ADC axp.set_adc_channel_enable?; let battery_mv = axp.get_battery_voltage_mv?; -
Async:
use ; use I2c; let i2c_bus_impl = /* your I2C bus */; let mut axp = new; // Configure power outputs axp.set_dcdc_voltage.await?; axp.set_dcdc_enable.await?; axp.set_ldo_voltage_mv.await?; axp.set_ldo_enable.await?; // Enable and read ADC axp.set_adc_channel_enable.await?; let battery_mv = axp.get_battery_voltage_mv.await?;
-
Low-Level API Usage
The driver provides direct access to all AXP2101 registers through the low-level API via axp.ll. This API is automatically generated from device.yaml and provides type-safe access to all register fields.
Reading Registers
Use .read() to read a register and access its fields:
// Read battery percentage (State of Charge)
let soc = axp.ll.battery_percentage.read?;
let percentage = soc.percentage; // Returns 0-100%
// Read power status
let status = axp.ll.power_status.read?;
let battery_present = status.battery_present;
let vbus_good = status.vbus_good;
// Read chip ID and access individual fields
let chip_id = axp.ll.chip_id.read?;
let id_high = chip_id.chip_id_high; // Upper 2 bits
let version = chip_id.chip_version; // Version bits
let id_low = chip_id.chip_id_low; // Lower 4 bits
Writing Registers
Use .write() with a closure to modify register fields. The closure receives a mutable reference to the register structure:
// Enable battery detection
axp.ll.battery_detection_control.write?;
// Configure ADC channels
axp.ll.adc_channel_enable_0.write?;
// Configure common config register
axp.ll.common_config.write?;
Modifying Registers
Use .modify() to read-modify-write, preserving other fields:
// Enable one ADC channel without affecting others
// modify() reads the register, applies your changes, then writes it back
axp.ll.adc_channel_enable_0.modify?;
Async Low-Level API
The low-level API has async versions for use with Axp2101Async. Simply append _async to the method name:
// Async read
let chip_id = axp.ll.chip_id.read_async.await?;
let soc = axp.ll.battery_percentage.read_async.await?;
// Async write
axp.ll.battery_detection_control.write_async.await?;
// Async modify
axp.ll.adc_channel_enable_0.modify_async.await?;
Field Naming Convention
Register and field names in the LL API follow snake_case and are derived from the names in device.yaml:
- Register:
BatteryDetectionControl→battery_detection_control() - Field:
bat_det_en→set_bat_det_en()/bat_det_en() - Register:
AdcChannelEnable0→adc_channel_enable_0() - Field:
vbat_ch_en→set_vbat_ch_en()/vbat_ch_en()
Finding Register/Field Names
- Check
device.yaml- All registers and fields are documented there - Use IDE autocomplete - Type
axp.ll.to see all available registers - Read a register - Use
.read()then autocomplete to see available field getters - Write a register - The closure parameter has autocomplete for all setters
Examples
Examples for ESP32-C3 using esp-hal are included. Setup is required (see esp-hal docs). Both examples demonstrate high-level convenience methods and low-level register API usage.
- Async Example:
examples/test_pmic_async.rs - Blocking Example:
examples/test_pmic_blocking.rs
Register Map
The AXP2101 register map is defined in device.yaml, which device-driver uses to generate Rust code. This file specifies:
- Register names, addresses, and sizes.
- Field names, bit positions, and access modes (Read-Only, Read-Write, Write-1-Clear).
- Enumerations for field values (e.g., charging currents, voltage settings).
- Reset values and descriptions based on the datasheet.
Access the low-level API via axp.ll (e.g., axp.ll.power_status().read()). High-level methods provide convenient access to common features.
Supported Devices
The AXP2101 is used in various embedded devices and development boards that require single-cell Li-battery power management with multiple voltage rails.
Known devices using the AXP2101:
- M5Stack Core2 v1.1 - ESP32-based IoT development kit with touch screen
Feature Flags
default = []: No default features; async and blocking drivers are always available.std: Enablesstdfeatures forthiserror.log: Enableslogfacade logging. Requireslog = { version = "0.4", optional = true }.defmt: Enablesdefmtlogging. Requiresdefmt = { version = "1.0", optional = true }.
Contributions are welcome! While the register map in device.yaml is complete, you can contribute by:
- Adding high-level convenience methods to simplify common operations (e.g., battery management, interrupt handling).
- Enhancing documentation with additional examples or clarifications.
- Reporting issues or suggesting improvements.
- Suggest code refactoring.
Please submit issues, fork the repository, and create pull requests.
License
This project is dual-licensed under the MIT License or Apache License 2.0, at your option.