# AXP2101 Power Management IC Driver (axp2101-dd)

[![Crates.io](https://img.shields.io/crates/v/axp2101-dd.svg)](https://crates.io/crates/axp2101-dd)
[![Docs.rs](https://docs.rs/axp2101-dd/badge.svg)](https://docs.rs/axp2101-dd)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/License-MIT%20OR%20Apache--2.0-blue.svg)](https://opensource.org/licenses)
[![Build Status](https://img.shields.io/github/actions/workflow/status/okhsunrog/axp2101-dd/rust_ci.yml?logo=github)](https://github.com/okhsunrog/axp2101-dd/actions/workflows/rust_ci.yml)

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`](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`](https://github.com/JM4ier/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`:**

   ```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:**
     ```rust
     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:**
     ```rust
     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?;
     ```

## Examples

Examples for ESP32-C3 using `esp-hal` are included. Setup is required (see [esp-hal docs](https://esp-rs.github.io/book/installation/)).

- **Async Example:** [`examples/test_pmic_async.rs`](examples/test_pmic_async.rs)
  ```bash
  cargo run --release --example test_pmic_async --features defmt
  ```
- **Blocking Example:** [`examples/test_pmic_blocking.rs`](examples/test_pmic_blocking.rs)
  ```bash
  cargo run --release --example test_pmic_blocking --features defmt
  ```

## Register Map

The AXP2101 register map is defined in [`device.yaml`](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.

## 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](LICENSE-MIT) or [Apache License 2.0](LICENSE-APACHE), at your option.
