lis2dh12 (0.2.0)

Published 2026-07-13 10:15:20 +00:00 by faicel

Installation

[registries.forgejo]
index = "sparse+" # Sparse index
# index = "" # Git

[net]
git-fetch-with-cli = true
cargo add lis2dh12@0.2.0 --registry forgejo

About this package

LIS2DH12 Accelerometer Driver

no_std library for the STMicroelectronics LIS2DH12 3-axis accelerometer sensor, using the I2C interface.

Features

  • no_std compatible for embedded systems
  • Uses embedded-hal traits for I2C
  • Implements standard Accelerometer and RawAccelerometer traits from the accelerometer crate
  • Support for multiple measurement ranges (±2g, ±4g, ±8g, ±16g)
  • Support for different power modes (high resolution, normal, low power)
  • Configurable data rates (1 Hz to 5.376 kHz)
  • Reads acceleration data on all 3 axes (X, Y, Z)
  • Interrupt support for threshold detection on INT1 and INT2 pins
  • 6D position detection
  • 32-level embedded FIFO (Stream, FIFO, Stream-to-FIFO modes)
  • Embedded temperature sensor
  • Built-in MEMS self-test
  • Click/tap and double-click detection
  • Free-fall detection and activity/inactivity (sleep-to-wake)

Usage

Basic Example

use lis2dh12::{Lis2dh12, Range, DataRate, SlaveAddr};
use embedded_hal::i2c::I2c;
use accelerometer::Accelerometer;

// Initialize the driver with SA0 pin LOW (0x18)
let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

// Configure measurement range (±2g)
accel.set_range(Range::G2)?;

// Configure data rate (100 Hz)
accel.set_data_rate(DataRate::Hz_100)?;

// Read acceleration data using the Accelerometer trait
let accel_data = accel.accel_norm()?;
println!("X: {} g, Y: {} g, Z: {} g", accel_data.x, accel_data.y, accel_data.z);

Advanced Configuration

use lis2dh12::{Lis2dh12, Range, DataRate, PowerMode, SlaveAddr};
use accelerometer::Accelerometer;

let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

// Configure for high resolution (12-bit)
accel.set_power_mode(PowerMode::HighResolution)?;

// Configure for ±4g
accel.set_range(Range::G4)?;

// Configure for 400 Hz
accel.set_data_rate(DataRate::Hz_400)?;

// Read data using the trait method
let data = accel.accel_norm()?;

// Or read raw values
use accelerometer::RawAccelerometer;
let raw_data = accel.accel_raw()?;

I2C Addresses

The LIS2DH12 supports two I2C addresses depending on the SA0 pin state:

  • 0x18 : SA0 = LOW
  • 0x19 : SA0 = HIGH

Use the SlaveAddr enum to specify the address explicitly:

use lis2dh12::{Lis2dh12, SlaveAddr};

// Address when SA0 pin is LOW (0x18)
let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

// Address when SA0 pin is HIGH (0x19)
let mut accel = Lis2dh12::new(i2c, SlaveAddr::High)?;

The constants LIS2DH12_I2C_ADDRESS_LOW and LIS2DH12_I2C_ADDRESS_HIGH are also available in the driver module.

Measurement Ranges

  • Range::G2 : ±2g (1 mg/LSB in high resolution mode)
  • Range::G4 : ±4g (2 mg/LSB in high resolution mode)
  • Range::G8 : ±8g (4 mg/LSB in high resolution mode)
  • Range::G16 : ±16g (12 mg/LSB in high resolution mode)

Power Modes

  • PowerMode::HighResolution : High resolution mode (12-bit)
  • PowerMode::Normal : Normal mode (10-bit)
  • PowerMode::LowPower : Low power mode (8-bit, 16 mg/LSB at ±2g)

Data Rates

  • DataRate::PowerDown : Power down mode
  • DataRate::Hz_1 : 1 Hz
  • DataRate::Hz_10 : 10 Hz
  • DataRate::Hz_25 : 25 Hz
  • DataRate::Hz_50 : 50 Hz
  • DataRate::Hz_100 : 100 Hz
  • DataRate::Hz_200 : 200 Hz
  • DataRate::Hz_400 : 400 Hz
  • DataRate::Hz_1600_LP : 1.6 kHz (low power mode only)
  • DataRate::Hz_5376 : 1.344 kHz (normal/high resolution) or 5.376 kHz (low power)

Interrupts

The LIS2DH12 supports two interrupt pins (INT1 and INT2) for threshold detection and other events.

Basic Interrupt Configuration

use lis2dh12::{Lis2dh12, InterruptConfig, InterruptMode, InterruptPin, SlaveAddr};

let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

// Configure INT1 to trigger when X-axis exceeds high threshold
let config = InterruptConfig::new()
    .with_x_high(true)
    .with_mode(InterruptMode::Or); // OR mode (interrupt if any condition is met)

accel.configure_interrupt(InterruptPin::Int1, config)?;

// Set threshold to 32 (32 * 16 mg = 512 mg in ±2g range)
accel.set_interrupt_threshold(InterruptPin::Int1, 32)?;

// Set minimum duration to 2 samples
accel.set_interrupt_duration(InterruptPin::Int1, 2)?;

// Enable the interrupt pin (active high)
accel.enable_interrupt_pin(InterruptPin::Int1, true, true)?;

Reading Interrupt Source

In your interrupt handler, read the interrupt source to determine what triggered the interrupt:

// In your interrupt handler:
let source = accel.read_interrupt_source(InterruptPin::Int1)?;

if source.active {
    if source.x_high {
        // X-axis high threshold exceeded
    }
    if source.y_high {
        // Y-axis high threshold exceeded
    }
    // ... check other axes
}

Important: Reading the interrupt source register clears the interrupt flag. Always read it in your interrupt handler.

Multiple Axes and AND/OR Mode

// Configure interrupt for multiple axes in OR mode
let config = InterruptConfig::new()
    .with_x_high(true)
    .with_y_high(true)
    .with_z_high(true)
    .with_mode(InterruptMode::Or); // OR: interrupt if ANY axis exceeds threshold

accel.configure_interrupt(InterruptPin::Int1, config)?;

// OR configure in AND mode
let config = InterruptConfig::new()
    .with_x_high(true)
    .with_y_high(true)
    .with_mode(InterruptMode::And); // AND: interrupt only if ALL enabled axes exceed threshold

accel.configure_interrupt(InterruptPin::Int1, config)?;

6D Position Detection

The LIS2DH12 can detect position changes in 6 directions:

let config = InterruptConfig::new()
    .with_six_d(true) // Enable 6D detection
    .with_x_high(true)
    .with_x_low(true)
    .with_y_high(true)
    .with_y_low(true)
    .with_z_high(true)
    .with_z_low(true);

accel.configure_interrupt(InterruptPin::Int1, config)?;

FIFO

The LIS2DH12 embeds a 32-level FIFO per axis. Use it to buffer samples at high ODR and read them in bursts, reducing CPU load.

Configure FIFO

use lis2dh12::{Lis2dh12, FifoConfig, FifoMode, SlaveAddr};

let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

// Stream mode: oldest samples overwritten when full
let config = FifoConfig::new(FifoMode::Stream).with_watermark(16);
accel.configure_fifo(config)?;

// Enable watermark interrupt on INT1
accel.enable_fifo_interrupts(true, false)?;

FIFO modes

Mode Behavior
FifoMode::Bypass FIFO disabled (default after reset)
FifoMode::Fifo Stops collecting when full (32 samples)
FifoMode::Stream Overwrites oldest samples when full
FifoMode::StreamToFifo Stream until trigger, then FIFO mode

Read FIFO data

use lis2dh12::{FIFO_CAPACITY, Acceleration};

let mut samples = [Acceleration::new(0.0, 0.0, 0.0); FIFO_CAPACITY];
let count = accel.read_fifo(&mut samples)?;

for sample in &samples[..count] {
    // process sample.x, sample.y, sample.z
}

// Check status
let status = accel.fifo_status()?;
if status.overrun {
  accel.fifo_reset()?;
}

Note: Each burst read from OUT_X_L pops samples from the FIFO. Use a buffer of at least FIFO_CAPACITY (32) elements to drain the entire FIFO in one call.

Temperature sensor

The LIS2DH12 includes an on-chip temperature sensor for relative temperature monitoring and accelerometer drift compensation. It is not a high-precision absolute thermometer; calibrate against a known reference for absolute readings.

Enable and read

use lis2dh12::{Lis2dh12, SlaveAddr};

let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

accel.enable_temperature_sensor()?;

if accel.temperature_status()?.data_available {
    let temp = accel.read_temperature()?;
    println!("Temperature: {} °C", temp.celsius);
}

Calibration

Apply a board-specific offset for absolute accuracy:

// Measured -3 °C offset at known 20 °C reference
let calibrated = temp.with_calibration_offset(-3.0);

Conversion

Raw values use the ST formula: (raw / 256.0) + 25.0 °C. Resolution is 10-bit in normal/high-resolution mode, 8-bit in low-power mode.

Self-test

The built-in self-test applies an electrostatic force to the MEMS element to verify sensor health without physically moving the device. Useful at boot or in production.

use lis2dh12::{Lis2dh12, SelfTestMode, SlaveAddr};

let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

let result = accel.run_self_test(SelfTestMode::Test0)?;

if result.passed {
    println!("Self-test OK (dX={}, dY={}, dZ={})", result.delta_x, result.delta_y, result.delta_z);
} else {
    println!("Self-test FAILED");
}

Expected per-axis delta: 17–360 LSB at ±2g normal mode (scaled for other ranges/modes). High-resolution mode waits for 8 samples after enabling; normal/low-power waits for 2.

Click detection

Detect single and double taps on each axis. Useful for user input (knock, double-tap gestures).

use lis2dh12::{Lis2dh12, ClickConfig, ClickTiming, InterruptPin, SlaveAddr};

let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

// Enable single + double click on X-axis
accel.configure_click(
    ClickConfig::new().with_x_single(true).with_x_double(true)
)?;

// Threshold ~15.6 mg/LSB at ±2g (1 LSB = full_scale/128)
accel.set_click_threshold(20, true)?;

// Timing in units of 1/ODR (e.g. at 100 Hz, 10 = 100 ms)
accel.set_click_timing(
    ClickTiming::new()
        .with_time_limit(10)
        .with_time_latency(20)
        .with_time_window(50)
        .with_latch(true)
)?;

accel.enable_click_high_pass_filter(true)?;
accel.enable_click_interrupt(InterruptPin::Int1, true, true)?;

// In interrupt handler:
let source = accel.read_click_source()?;
if source.double_click && source.x {
    // double-tap on X
}

Free-fall and activity detection

Free-fall (chute libre)

Detects when all axes fall below a threshold simultaneously (near-zero apparent acceleration). Uses interrupt generator INT1 or INT2.

use lis2dh12::{Lis2dh12, FreeFallConfig, InterruptPin, SlaveAddr};

let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

// ~350 mg threshold, ~15 ms @ 400 Hz (±2g)
accel.configure_free_fall(InterruptPin::Int1, FreeFallConfig::recommended_2g())?;
accel.enable_free_fall_interrupt(InterruptPin::Int1, true, true)?;

// In INT1 handler:
let source = accel.read_interrupt_source(InterruptPin::Int1)?;
if source.active {
    // free-fall detected
}

Threshold LSB: 16 mg @ ±2g via Range::threshold_mg_per_lsb().

Activity / inactivity (sleep-to-wake)

Hardware automatically switches to 10 Hz low power when acceleration stays below threshold for the configured duration. Restores full ODR when motion resumes.

use lis2dh12::{Lis2dh12, ActivityConfig, SlaveAddr};

let mut accel = Lis2dh12::new(i2c, SlaveAddr::Low)?;

// 16 LSB = 256 mg @ ±2g; duration = (8×4+1)/ODR seconds
accel.configure_activity(ActivityConfig::new(16, 4))?;
accel.enable_activity_interrupt(true, true)?; // INT2 only

SPI interface

Enable the spi feature and depend on the internal tools crate (register feature) for SPI writes:

[dependencies]
lis2dh12 = { path = "...", default-features = false, features = ["spi"] }
use lis2dh12::{Lis2dh12, SpiBus};

let bus = SpiBus { spi, cs, delay };
let mut accel = Lis2dh12::new_spi(bus)?;

// Optional 3-wire mode (CTRL_REG4.SIM)
accel.set_spi_three_wire(true)?;

Hardware notes (datasheet):

  • Tie CS high when using I2C; drive CS from the MCU in SPI mode
  • SA0 pin: high = I2C address 0x19 / SPI SDO available; low = 0x18 / 3-wire capable
  • SPI mode 0/3, max 10 MHz

Single-byte and burst SPI transfers use tools::register::st_mems (STMicro MEMS framing).

Tests

Tests use embedded-hal-mock to simulate the I2C and SPI interfaces:

cd lis2dh12
cargo test
cargo test --features spi

Documentation

For more information about the LIS2DH12, consult the official datasheet from STMicroelectronics.

Dependencies

ID Version
accelerometer ^0.12
cast ^0.3
embedded-hal ^1.0.0
tools =0.3.0-rc.0
embedded-hal-mock ^0.11.1
Details
Cargo
2026-07-13 10:15:20 +00:00
11
644 KiB
Assets (1)
Versions (6) View all
1.0.5 2026-07-25
1.0.3 2026-07-25
1.0.1 2026-07-24
1.0.0 2026-07-24
0.2.0 2026-07-13