| Crates.io | axp2101-dd |
| lib.rs | axp2101-dd |
| version | 0.2.2 |
| created_at | 2025-11-28 01:04:09.302211+00 |
| updated_at | 2025-12-01 17:08:45.795226+00 |
| description | A driver for the AXP2101 power management IC (uses device-driver crate) |
| homepage | |
| repository | https://github.com/okhsunrog/axp2101-dd |
| max_upload_size | |
| id | 1954660 |
| size | 1,511,824 |
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.
The axp2101-dd driver offers:
device.yaml, enabling device-driver to generate a type-safe, low-level register access API. This approach enhances maintainability and extensibility.bisync crate to provide both asynchronous (Axp2101Async) and blocking (Axp2101) drivers from the same codebase, with no feature flags required.ll field of the Axp2101/Axp2101Async struct) offers direct, type-safe access to all registers defined in device.yaml via raw values or enums.no_std and no-alloc: Optimized for bare-metal and RTOS environments.defmt and the log facade for debugging.Caution! The AXP2101 controls power to the microcontroller and critical components. Incorrect configuration may cause malfunctions, data loss, or hardware damage.
device.yaml.device-driver.device.yaml for details)
no_std and no-alloc.defmt and log facade.Add axp2101-dd to Cargo.toml:
[dependencies]
axp2101-dd = "0.1.0"
# For blocking usage (Axp2101):
embedded-hal = "1.0.0"
# For async usage (Axp2101Async):
embedded-hal-async = "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)
Instantiate the driver with your I2C bus:
Blocking:
use axp2101_dd::{Axp2101, DcId, LdoId, AdcChannel};
use embedded_hal::i2c::I2c;
let i2c_bus_impl = /* your I2C bus */;
let mut axp = Axp2101::new(i2c_bus_impl);
// Configure power outputs
axp.set_dcdc_voltage(DcId::Dcdc1, 3300)?;
axp.set_dcdc_enable(DcId::Dcdc1, true)?;
axp.set_ldo_voltage_mv(LdoId::Aldo1, 3300)?;
axp.set_ldo_enable(LdoId::Aldo1, true)?;
// Enable and read ADC
axp.set_adc_channel_enable(AdcChannel::BatteryVoltage, true)?;
let battery_mv = axp.get_battery_voltage_mv()?;
Async:
use axp2101_dd::{Axp2101Async, DcId, LdoId, AdcChannel};
use embedded_hal_async::i2c::I2c;
let i2c_bus_impl = /* your I2C bus */;
let mut axp = Axp2101Async::new(i2c_bus_impl);
// Configure power outputs
axp.set_dcdc_voltage(DcId::Dcdc1, 3300).await?;
axp.set_dcdc_enable(DcId::Dcdc1, true).await?;
axp.set_ldo_voltage_mv(LdoId::Aldo1, 3300).await?;
axp.set_ldo_enable(LdoId::Aldo1, true).await?;
// Enable and read ADC
axp.set_adc_channel_enable(AdcChannel::BatteryVoltage, true).await?;
let battery_mv = axp.get_battery_voltage_mv().await?;
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.
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
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(|w| {
w.set_bat_det_en(true);
})?;
// Configure ADC channels
axp.ll.adc_channel_enable_0().write(|w| {
w.set_vbat_ch_en(true); // Battery voltage ADC
w.set_vbus_ch_en(true); // VBUS voltage ADC
w.set_vsys_ch_en(true); // System voltage ADC
w.set_tdie_ch_en(true); // Die temperature ADC
})?;
// Configure common config register
axp.ll.common_config().write(|w| {
w.set_discharge_off_enable(true);
w.set_pwrok_restart_enable(true);
})?;
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(|w| {
w.set_vbat_ch_en(true); // Only modify this field, others preserved
})?;
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(|w| {
w.set_bat_det_en(true);
}).await?;
// Async modify
axp.ll.adc_channel_enable_0().modify_async(|w| {
w.set_gpadc_ch_en(true);
}).await?;
Register and field names in the LL API follow snake_case and are derived from the names in device.yaml:
BatteryDetectionControl → battery_detection_control()bat_det_en → set_bat_det_en() / bat_det_en()AdcChannelEnable0 → adc_channel_enable_0()vbat_ch_en → set_vbat_ch_en() / vbat_ch_en()device.yaml - All registers and fields are documented thereaxp.ll. to see all available registers.read() then autocomplete to see available field gettersExamples 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.
examples/test_pmic_async.rs
cargo run --release --example test_pmic_async --features defmt
examples/test_pmic_blocking.rs
cargo run --release --example test_pmic_blocking --features defmt
The AXP2101 register map is defined in device.yaml, which device-driver uses to generate Rust code. This file specifies:
Access the low-level API via axp.ll (e.g., axp.ll.power_status().read()). High-level methods provide convenient access to common features.
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:
default = []: No default features; async and blocking drivers are always available.std: Enables std features for thiserror.log: Enables log facade logging. Requires log = { version = "0.4", optional = true }.defmt: Enables defmt logging. Requires defmt = { version = "1.0", optional = true }.Please submit issues, fork the repository, and create pull requests.
This project is dual-licensed under the MIT License or Apache License 2.0, at your option.