axp2101-dd

Crates.ioaxp2101-dd
lib.rsaxp2101-dd
version0.2.2
created_at2025-11-28 01:04:09.302211+00
updated_at2025-12-01 17:08:45.795226+00
descriptionA driver for the AXP2101 power management IC (uses device-driver crate)
homepage
repositoryhttps://github.com/okhsunrog/axp2101-dd
max_upload_size
id1954660
size1,511,824
Danila Gornushko (okhsunrog)

documentation

https://docs.rs/axp2101-dd

README

AXP2101 Power Management IC Driver (axp2101-dd)

Crates.io Docs.rs License: MIT OR Apache-2.0 Build Status

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, enabling device-driver to generate a type-safe, low-level register access API. This approach enhances maintainability and extensibility.
  • Unified Async/Blocking API: Uses the bisync crate 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 ll field of the Axp2101/Axp2101Async struct) offers direct, type-safe access to all registers defined in device.yaml via raw values or enums.
  • Peripheral Control: Manages 5 DC-DCs, 11 LDOs, ADC conversions, interrupts, battery charging, fuel gauge, and power settings.
  • no_std and no-alloc: Optimized for bare-metal and RTOS environments.
  • Optional Logging: Supports defmt and the log facade 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.yaml for 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_std and no-alloc.
  • Optional Logging: Supports defmt and log facade.

Getting Started

  1. 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-hal crate for your use case, no need for both

    • Use embedded-hal for blocking drivers (Axp2101)
    • Use embedded-hal-async for async drivers (Axp2101Async)
  2. 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?;
      

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(|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);
})?;

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(|w| {
    w.set_vbat_ch_en(true);  // Only modify this field, others preserved
})?;

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(|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?;

Field Naming Convention

Register and field names in the LL API follow snake_case and are derived from the names in device.yaml:

  • Register: BatteryDetectionControlbattery_detection_control()
  • Field: bat_det_enset_bat_det_en() / bat_det_en()
  • Register: AdcChannelEnable0adc_channel_enable_0()
  • Field: vbat_ch_enset_vbat_ch_en() / vbat_ch_en()

Finding Register/Field Names

  1. Check device.yaml - All registers and fields are documented there
  2. Use IDE autocomplete - Type axp.ll. to see all available registers
  3. Read a register - Use .read() then autocomplete to see available field getters
  4. 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.

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

Datasheet

Feature Flags

  • 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 }.

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.

Commit count: 0

cargo fmt