lsm9ds0 0.3.0

Platform-agnostic async driver for the LSM9DS0 IMU
Documentation

LSM9DS0

A platform-agnostic Rust driver for the ST LSM9DS0 9-axis IMU (3D accelerometer, 3D gyroscope, 3D magnetometer).

Built on embedded-hal-async traits for I2C and SPI communication.

Features

  • I2C and SPI interface support
  • Configurable sensor ranges and data rates
  • Temperature sensor
  • FIFO support
  • Interrupt configuration
  • no_std compatible

Usage

Add the dependency to your Cargo.toml:

[dependencies]
lsm9ds0 = "0.1"

I2C

use lsm9ds0::{
    AccelDataRate, GyroDataRate, I2cInterface, Lsm9ds0, Lsm9ds0Config, MagMode,
};
use embassy_time::Delay;

let interface = I2cInterface::init(i2c);

let config = Lsm9ds0Config::new()
    .with_gyro_enabled(true)
    .with_gyro_data_rate(GyroDataRate::Hz95)
    .with_accel_data_rate(AccelDataRate::Hz100)
    .with_mag_mode(MagMode::ContinuousConversion)
    .with_temperature_enabled(true)
    .with_auto_calibration(lsm9ds0::Orientation::ZUp);

let mut imu = Lsm9ds0::new_with_config(interface, config);
imu.init(&mut Delay).await?;

let (gx, gy, gz) = imu.read_gyro().await?;   // DegreesPerSecond
let (ax, ay, az) = imu.read_accel().await?;  // GForce
let (mx, my, mz) = imu.read_mag().await?;    // Gauss
let temp = imu.read_temp().await?;           // Celsius

SPI

The LSM9DS0 has separate chip selects for the gyroscope and accelerometer/magnetometer. Provide two SpiDevice instances:

use lsm9ds0::{Lsm9ds0, SpiInterface};
use embassy_time::Delay;

let interface = SpiInterface::init(gyro_spi, accel_mag_spi);
let mut imu = Lsm9ds0::new(interface);
imu.init(&mut Delay).await?;

Configuration

Sensor parameters can be set at initialization or changed at runtime:

use lsm9ds0::{AccelScale, GyroDataRate, GyroScale, MagScale, Orientation};
use embassy_time::Delay;

// At initialization
let config = Lsm9ds0Config::new()
    .with_gyro_scale(GyroScale::Dps500)
    .with_accel_scale(AccelScale::G4)
    .with_mag_scale(MagScale::Gauss8);

// At runtime
imu.set_gyro_scale(GyroScale::Dps2000).await?;
imu.set_accel_data_rate(AccelDataRate::Hz400).await?;

// Calibrate the gyroscope and accelerometer during runtime
imu.calibrate_bias(&mut Delay, Orientation::ZUp).await?;

Blocking I2C / SPI

This driver is built on embedded-hal-async traits, but it works with blocking buses too. The async methods on the driver still require .await, but if the underlying bus is blocking, they complete immediately with no executor overhead.

You need two things:

  1. An adapter that wraps your blocking embedded_hal::i2c::I2c (or SpiDevice) to satisfy the async trait. The async fn just calls the blocking method and returns — no actual suspension:
struct BlockingI2c<I>(I);

impl<I: embedded_hal::i2c::I2c> embedded_hal::i2c::ErrorType for BlockingI2c<I> {
    type Error = I::Error;
}

impl<I: embedded_hal::i2c::I2c> embedded_hal_async::i2c::I2c for BlockingI2c<I> {
    async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
        self.0.read(address, read)
    }
    async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
        self.0.write(address, write)
    }
    async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
        self.0.write_read(address, write, read)
    }
    async fn transaction(&mut self, address: u8, operations: &mut [embedded_hal::i2c::Operation<'_>]) -> Result<(), Self::Error> {
        self.0.transaction(address, operations)
    }
}
  1. A trivial executor like embassy_futures::block_on to run the async code synchronously:
use embassy_futures::block_on;

let interface = I2cInterface::init(BlockingI2c(my_blocking_i2c));
let mut imu = Lsm9ds0::new_with_config(interface, config);

block_on(async {
    imu.init(&mut delay).await?;
    let (gx, gy, gz) = imu.read_gyro().await?;
});

See the examples/rp2040/src/bin/simple-i2c-blocking.rs example for a complete working implementation.

Default Configuration

The driver's default configuration matches the device's power-on-reset register values from the datasheet, with two exceptions where the actual hardware defaults differ from the documented values:

Register Datasheet Actual
CTRL_REG7_XM 0x02 0x03
INT_CTRL_REG_M 0x00 0xE8

Examples

See the examples/ directory for platform-specific examples, including RP2040.

References

License

MIT