lis2de12 0.1.3

Blocking and async driver for the LIS2DE12 accelerometer with I2C and SPI support
Documentation

lis2de12

Platform-agnostic Rust driver for the ST LIS2DE12 ultra-low-power 3-axis accelerometer. The crate targets no_std environments, generates its register API at build time from a YAML manifest, and supports both blocking and asynchronous I²C and SPI transports.


Features

  • I²C and SPI support - Use either interface with the same high-level API
  • High-level blocking driver implementing accelerometer::RawAccelerometer
  • Optional async driver (Lis2de12Async) for both I²C and SPI
  • Auto-generated register-level API via device-driver
  • no_std compatible with optional defmt logging integration

Getting Started

Add the crate to your Cargo.toml:

[dependencies]
lis2de12 = "0.1.2"
embedded-hal = "1.0"

# Optional features:
# lis2de12 = { version = "0.1.2", features = ["async"] }
# lis2de12 = { version = "0.1.2", features = ["defmt-03"] }

Blocking I²C usage

use embedded_hal::i2c::I2c;
use lis2de12::{Lis2de12, SlaveAddr};

fn init_driver<I2C>(bus: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
where
    I2C: I2c,
{
    // Create driver using I²C interface
    let mut lis = Lis2de12::new_i2c(bus, SlaveAddr::default())?;
    let accel = lis.read_g()?;
    defmt::info!("Acceleration: {:?} g", accel);
    Ok(())
}

Blocking SPI usage

use embedded_hal::spi::SpiDevice;
use lis2de12::Lis2de12;

fn init_driver_spi<SPI>(spi: SPI) -> Result<(), lis2de12::Error<SPI::Error>>
where
    SPI: SpiDevice,
{
    // Create driver using SPI interface
    let mut lis = Lis2de12::new_spi(spi)?;
    let accel = lis.read_g()?;
    defmt::info!("Acceleration: {:?} g", accel);
    Ok(())
}

Note: The LIS2DE12 requires SPI Mode 0 or Mode 3 (CPOL=0, CPHA=0 or CPOL=1, CPHA=1). The driver uses the SpiDevice trait which handles chip select automatically.


Configuring the sensor

Use Lis2de12Config to bring the device out of power-down with your preferred output data rate, operating mode, scale, axis enables, and temperature sensing:

use embedded_hal::i2c::I2c;
use lis2de12::{
    AxesEnable, Fs, Lis2de12, Lis2de12Config, OperatingMode, Odr, SlaveAddr,
};

fn init_with_config<I2C>(i2c: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
where
    I2C: I2c,
{
    let config = Lis2de12Config {
        odr: Odr::Hz50,
        mode: OperatingMode::Normal,
        scale: Fs::G4,
        axes: AxesEnable { x: true, y: true, z: true },
        block_data_update: true,
        temperature_enable: true,
        ..Default::default()
    };

    let mut lis = Lis2de12::new_i2c_with_config(i2c, SlaveAddr::default(), config)?;
    // Or for SPI: Lis2de12::new_spi_with_config(spi, config)?
    defmt::info!("Sensor configured with 50 Hz output");
    Ok(())
}

Asynchronous usage (Embassy, RTIC, etc.)

Enable the async feature and depend on embedded-hal-async:

use embedded_hal_async::i2c::I2c;
use lis2de12::{Lis2de12Async, SlaveAddr};

async fn init_async<I2C>(i2c: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
where
    I2C: I2c,
{
    // Async I²C
    let mut lis = Lis2de12Async::new_i2c(i2c, SlaveAddr::default()).await?;
    // Or async SPI: Lis2de12Async::new_spi(spi).await?

    let accel = lis.read_g().await?;
    defmt::info!("Acceleration: {:?} g", accel);
    Ok(())
}

Reading scaled samples

Both blocking and async drivers provide read_raw, read_mg, and read_g helpers to obtain measurements in the format that best suits your application:

use embedded_hal::i2c::I2c;
use lis2de12::{Lis2de12, SlaveAddr};

fn read_scaled<I2C>(bus: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
where
    I2C: I2c,
{
    let mut lis = Lis2de12::new_i2c(bus, SlaveAddr::default())?;
    let raw = lis.read_raw()?;  // Raw ADC values
    let mg = lis.read_mg()?;    // milli-g (integer)
    let g = lis.read_g()?;      // g (floating point)
    defmt::info!("raw={:?}, mg={:?}, g={:?}", raw, mg, g);
    Ok(())
}

The async variant exposes the same API surface, returning futures for each method.


FIFO configuration and burst reads

Configure the hardware FIFO to collect XYZ frames and drain them in bursts when the watermark is reached:

use embedded_hal::i2c::I2c;
use lis2de12::{
    FifoConfig, FifoMode, Lis2de12, Lis2de12Config, SlaveAddr,
};

fn read_fifo<I2C>(bus: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
where
    I2C: I2c,
{
    let mut config = Lis2de12Config::default();
    config.fifo = FifoConfig::enabled(FifoMode::Stream).with_watermark(8);

    let mut lis = Lis2de12::new_i2c_with_config(bus, SlaveAddr::default(), config)?;
    let status = lis.fifo_status()?;
    if status.has_data() {
        let mut frames = [[0u8; lis2de12::FIFO_FRAME_BYTES]; 4];
        let read = lis.read_fifo_frames(&mut frames)?;
        defmt::info!("drained {} frames", read);
    }
    Ok(())
}

Async users get identical functionality via Lis2de12Async::read_fifo_frame, read_fifo_frames, and drain_fifo.


Register-level access

For advanced configuration you can interact with the generated register API directly:

use lis2de12::{Lis2de12, Odr, SlaveAddr};

fn configure_normal_mode<I2C>(i2c: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
where
    I2C: embedded_hal::i2c::I2c,
{
    let mut lis = Lis2de12::new_i2c(i2c, SlaveAddr::default())?;

    lis
        .device()
        .ctrl_reg_1()
        .write(|reg| {
            reg.set_odr(Odr::Hz100);
            reg.set_lpen(false); // normal mode (low-power disabled)
            reg.set_xen(true);
            reg.set_yen(true);
            reg.set_zen(true);
        })?;
    Ok(())
}

Feature flags

Feature Default Description
async Enables the async driver (pulls in embedded-hal-async)
defmt-03 Enables defmt logging support compatible with device-driver

Build-time code generation

During cargo build, the build.rs script reads src/lis2de12.yaml, generates the register accessor API, and writes it into $OUT_DIR. End users do not need to run any manual code-generation steps.


Development

# Suggested commands
cargo fmt --all
cargo clippy --all-targets --all-features
cargo test --all-features
cargo publish --dry-run

CI workflows verify formatting, lints, and no_std builds.


License

Licensed under either of

at your option.


Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the project by you shall be dual licensed as above (MIT OR Apache-2.0), without any additional terms or conditions.


Links