lis2dh12 (0.2.0)
Installation
[registries.forgejo]
index = "sparse+ " # Sparse index
# index = " " # Git
[net]
git-fetch-with-cli = truecargo add lis2dh12@0.2.0 --registry forgejoAbout this package
LIS2DH12 Accelerometer Driver
no_std library for the STMicroelectronics LIS2DH12 3-axis accelerometer sensor, using the I2C interface.
Features
no_stdcompatible for embedded systems- Uses
embedded-haltraits for I2C - Implements standard
AccelerometerandRawAccelerometertraits from theaccelerometercrate - 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 = LOW0x19: 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 modeDataRate::Hz_1: 1 HzDataRate::Hz_10: 10 HzDataRate::Hz_25: 25 HzDataRate::Hz_50: 50 HzDataRate::Hz_100: 100 HzDataRate::Hz_200: 200 HzDataRate::Hz_400: 400 HzDataRate::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 |