drv8301-dd 0.2.0

A driver for the DRV8301 gate driver IC (uses device-driver crate)
Documentation
# DRV8301 Gate Driver IC Driver (drv8301-dd)

[![Crates.io](https://img.shields.io/crates/v/drv8301-dd.svg)](https://crates.io/crates/drv8301-dd)
[![Docs.rs](https://docs.rs/drv8301-dd/badge.svg)](https://docs.rs/drv8301-dd)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/License-MIT%20OR%20Apache--2.0-blue.svg)](https://opensource.org/licenses)

This crate provides a no_std driver for the DRV8301 three-phase gate driver IC, a highly integrated motor driver from Texas Instruments. The project aims to support the full functionality of the DRV8301, 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 DRV8301's registers, with device.yaml providing a comprehensive and accurate description of all registers and their fields.

## Overview

The `drv8301-dd` driver offers:

- **Declarative Configuration:** The DRV8301 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 (`Drv8301Async`) and blocking (`Drv8301`) drivers from the same codebase, with no feature flags required.
- **High-Level and Low-Level APIs:**
  - High-level methods simplify tasks like configuring overcurrent protection, PWM modes, and shunt amplifier gains.
  - Low-level API (via the `ll` field of the `Drv8301`/`Drv8301Async` struct) offers direct, type-safe access to all registers defined in `device.yaml`.
- **Motor Control Features:** Manages gate driver outputs, overcurrent protection, temperature monitoring, and current sensing.
- **`no_std` and `no-alloc`:** Optimized for bare-metal and RTOS environments.
- **Optional Logging:** Supports `defmt` and the `log` facade for debugging.

## 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)
  - Three-phase half-bridge gate driver outputs
  - Configurable overcurrent protection (VDS thresholds, OCP modes)
  - Current shunt amplifiers with selectable gain (10/20/40/80 V/V)
  - PWM mode selection (6-PWM or 3-PWM)
  - Status monitoring (faults, temperature, voltage)
  - DC calibration modes
- **`no_std` and `no-alloc`**.
- **Optional Logging:** Supports `defmt` and `log` facade.

## Getting Started

1. **Add `drv8301-dd` to `Cargo.toml`:**

   ```toml
   [dependencies]
   drv8301-dd = "0.2.0"
   # For blocking usage (Drv8301):
   embedded-hal = "1.0.0"
   # For async usage (Drv8301Async):
   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 (`Drv8301`)
   > - Use `embedded-hal-async` for async drivers (`Drv8301Async`)

2. **Instantiate the driver with your SPI device:**

   - **Blocking:**
     ```rust
     use drv8301_dd::{Drv8301, OcAdjSet, OcpMode, ShuntAmplifierGain};
     use embedded_hal::spi::SpiDevice;

     let spi_device = /* your SPI device */;
     let mut drv = Drv8301::new(spi_device);

     // Configure overcurrent protection
     drv.set_oc_threshold(OcAdjSet::Vds250mV)?;
     drv.set_ocp_mode(OcpMode::CurrentLimit)?;

     // Set PWM mode and amplifier gain
     drv.set_pwm_mode(false)?;  // 6-PWM mode
     drv.set_shunt_amplifier_gain(ShuntAmplifierGain::Gain20)?;

     // Read device status
     let device_id = drv.get_device_id()?;
     let has_fault = drv.has_fault()?;

     // Get comprehensive fault status
     let status = drv.get_fault_status()?;
     if status.has_overcurrent() {
         // Handle overcurrent on any phase
     }
     if status.phase_a_overcurrent() {
         // Phase A specific fault
     }
     ```

   - **Async:**
     ```rust
     use drv8301_dd::{Drv8301Async, OcAdjSet, OcpMode, ShuntAmplifierGain};
     use embedded_hal_async::spi::SpiDevice;

     let spi_device = /* your SPI device */;
     let mut drv = Drv8301Async::new(spi_device);

     // Configure overcurrent protection
     drv.set_oc_threshold(OcAdjSet::Vds250mV).await?;
     drv.set_ocp_mode(OcpMode::CurrentLimit).await?;

     // Set PWM mode and amplifier gain
     drv.set_pwm_mode(false).await?;  // 6-PWM mode
     drv.set_shunt_amplifier_gain(ShuntAmplifierGain::Gain20).await?;

     // Read device status
     let device_id = drv.get_device_id().await?;
     let has_fault = drv.has_fault().await?;

     // Get comprehensive fault status
     let status = drv.get_fault_status().await?;
     if status.has_overcurrent() {
         // Handle overcurrent on any phase
     }
     if status.has_thermal() {
         // Handle overtemperature warning or shutdown
     }
     ```

## Low-Level API Usage

The driver provides direct access to all DRV8301 registers through the low-level API via `drv.ll`. This API is automatically generated from [`device.yaml`](device.yaml) and provides type-safe access to all register fields.

### Reading Registers

Use `.read()` to read a register and access its fields:

```rust
// Read status register 1
let status1 = drv.ll.status_register_1().read()?;
let has_fault = status1.fault();
let gvdd_uv = status1.gvdd_uv();
let otw = status1.otw();

// Read status register 2
let status2 = drv.ll.status_register_2().read()?;
let device_id = status2.device_id();

// Read control register 1
let ctrl1 = drv.ll.control_register_1().read()?;
let gate_current = ctrl1.gate_current();
let gate_reset = ctrl1.gate_reset();
```

### Writing Registers

Use `.write()` with a closure to modify register fields. The closure receives a mutable reference to the register structure:

```rust
// Configure control register 1
drv.ll.control_register_1().write(|w| {
    w.set_gate_current(GateCurrent::Ma1700);
    w.set_gate_reset(false);
    w.set_pwm_mode(PwmMode::SixPwm);
    w.set_oc_adj_set(OcAdjSet::Vds250mV);
})?;

// Configure control register 2
drv.ll.control_register_2().write(|w| {
    w.set_octw_set(OctwSet::Both);
    w.set_gain(ShuntAmplifierGain::Gain20);
    w.set_dc_cal_ch1(false);
    w.set_dc_cal_ch2(false);
    w.set_ocp_mode(OcpMode::CurrentLimit);
})?;
```

### Modifying Registers

Use `.modify()` to read-modify-write, preserving other fields:

```rust
// Enable DC calibration for channel 1 without affecting other settings
// modify() reads the register, applies your changes, then writes it back
drv.ll.control_register_2().modify(|w| {
    w.set_dc_cal_ch1(true);  // Only modify this field, others preserved
})?;
```

### Async Low-Level API

The low-level API has async versions for use with `Drv8301Async`. Simply append `_async` to the method name:

```rust
// Async read
let status1 = drv.ll.status_register_1().read_async().await?;
let status2 = drv.ll.status_register_2().read_async().await?;

// Async write
drv.ll.control_register_1().write_async(|w| {
    w.set_gate_current(GateCurrent::Ma1700);
    w.set_pwm_mode(PwmMode::SixPwm);
}).await?;

// Async modify
drv.ll.control_register_2().modify_async(|w| {
    w.set_gain(ShuntAmplifierGain::Gain40);
}).await?;
```

### Field Naming Convention

Register and field names in the LL API follow snake_case and are derived from the names in [`device.yaml`](device.yaml):

- Register: `StatusRegister1``status_register_1()`
- Field: `gate_current``set_gate_current()` / `gate_current()`
- Register: `ControlRegister2``control_register_2()`
- Field: `dc_cal_ch1``set_dc_cal_ch1()` / `dc_cal_ch1()`

### Finding Register/Field Names

1. **Check [`device.yaml`]device.yaml** - All registers and fields are documented there
2. **Use IDE autocomplete** - Type `drv.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](https://esp-rs.github.io/book/installation/)). Both examples demonstrate high-level convenience methods and low-level register API usage.

- **Async Example:** [`examples/test_drv_async.rs`]examples/test_drv_async.rs
  ```bash
  cargo run --release --example test_drv_async --features defmt
  ```
- **Blocking Example:** [`examples/test_drv_blocking.rs`]examples/test_drv_blocking.rs
  ```bash
  cargo run --release --example test_drv_blocking --features defmt
  ```

## Register Map

The DRV8301 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).
- Enumerations for field values (e.g., gate currents, OCP modes, amplifier gains).
- Reset values and descriptions based on the datasheet.

## Hardware Setup

The DRV8301 requires SPI communication with specific characteristics:

- **SPI Mode:** CPOL=0, CPHA=1 (Mode 1)
- **Clock Frequency:** Up to 10 MHz
- **Frame Format:** 16-bit MSB first
- **Voltage Levels:** 3.3V or 5V logic compatible
- **Connections:** SCLK, MISO, MOSI, CS (chip select)

## Datasheet

- [DRV8301 Datasheet - PDF]docs/drv8301.pdf
- [DRV8301 Datasheet - Markdown]docs/drv8301.md (OCR-converted for easier reference)

## 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

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., motor control sequences, calibration routines).
- Enhancing documentation with additional examples or clarifications.
- Reporting issues or suggesting improvements.
- Suggesting 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.