ph-qmi8658 Driver
Async #![no_std] driver for the QMI8658C
6-axis IMU (3-axis accelerometer + 3-axis gyroscope + temperature sensor) from QST
Corporation. Built on embedded-hal-async with I2C and SPI transport support.
MSRV: 1.92.0
This README covers the driver's usage flows with code examples. For module structure and data-flow diagrams, see ARCHITECTURE.md. If you change driver behavior or flow sequencing, update both documents to keep them aligned with the code.
Driver Capabilities
- Configurable accelerometer (2/4/8/16 g) and gyroscope (16 to 2048 dps) ranges and ODRs
- FIFO buffering (stream, FIFO, and watermark modes) with burst and manual read paths
- Interrupt routing to INT1/INT2 pins with CTRL9 handshake support
- Wake-on-motion detection for low-power applications
- Sync-sample data locking for coherent multi-register reads
- Self-test, host-delta offset calibration, and on-demand calibration
- Integer scaling helpers (
ScaleFactorratios, no floats); optionalfixed-point conversions - Init-sequence helper macro to reduce boilerplate
Usage Examples
All examples below assume an async context, an I2C bus, and a DelayNs
implementation. Use the Common Setup snippet once, then follow the flow examples.
Common Setup
use ;
use DelayNs;
use I2c;
async
Initialization
Reset the sensor, verify the chip ID, and apply the default configuration.
// Known address
imu.init.await?;
// Probe multiple addresses
let address = imu.init_with_addresses.await?;
let _ = address;
If init or init_with_addresses returns Error::NotReady, delay briefly and retry.
Initialization Macro
The qmi8658_init_sequence! macro combines address probing, interrupt configuration,
and sensor configuration into a single call.
use ;
let config = new;
let irq = new.with_ctrl9_handshake_statusint;
let address = qmi8658_init_sequence!?;
let _ = address;
Configuration
Set accelerometer and gyroscope ranges, output data rates, and low-pass filter modes.
use ;
let accel = new;
let config = new.with_accel_config.without_gyro;
imu.set_config;
imu.apply_config.await?;
Interrupt Routing + Status
Route data-ready and motion events to physical INT pins and read back interrupt status.
use ;
let irq = new
.with_ctrl9_handshake_statusint
.with_motion_pin;
imu.apply_interrupt_config.await?;
let status = imu.read_interrupt_status.await?;
let _ = status;
Raw Data Reads
Read individual sensor outputs or a full block (timestamp + temperature + accel + gyro).
let block = imu.read_raw_block.await?;
let accel = imu.read_accel_raw.await?;
let gyro = imu.read_gyro_raw.await?;
let temp = imu.read_temperature_raw.await?;
let ts = imu.read_timestamp.await?;
let _ = ;
Scaling Helpers (Integer, No Floats)
Convert raw counts to physical units using integer ScaleFactor ratios. Useful on
targets without an FPU.
use ;
let accel_scale = accel_mg_per_lsb;
let gyro_scale = gyro_mdps_per_lsb;
// Example conversion for a single axis (i16 raw -> i32 milli-units).
let ax_mg = / accel_scale.denominator;
let gx_mdps = / gyro_scale.denominator;
let _ = ;
FIFO Burst Read + Decode
Stream sensor data through the hardware FIFO and iterate over decoded frames.
use ;
let fifo = new;
imu.apply_fifo_config.await?;
imu.reset_fifo_with_delay.await?;
let mut buffer = ;
let readout = imu.read_fifo_burst.await?;
let format = imu.fifo_frame_format;
for frame in new
FIFO Manual Read Sequence
Step through the FIFO read protocol manually for fine-grained control.
let mut buffer = ;
imu.request_fifo_read.await?;
imu.wait_ctrl9_done.await?;
imu.enable_fifo_read_mode.await?;
let timestamp = imu.read_fifo_data.await?;
imu.finish_fifo_read.await?;
let _ = timestamp;
Sync Sample (Data-Lock)
Lock a coherent snapshot of all sensor registers and read them atomically.
// For I2C/I3C, disable AHB clock gating while sync sample is active.
imu.set_ahb_clock_gating_with_delay.await?;
imu.set_sync_sample.await?;
let sample = imu.read_sync_sample.await?;
let _ = sample;
imu.set_sync_sample.await?;
imu.set_ahb_clock_gating_with_delay.await?;
Wake on Motion (WoM)
Configure low-power wake-on-motion detection to trigger on acceleration exceeding a threshold.
use ;
let accel = new;
imu.set_config;
imu.apply_config.await?;
let wom = new;
imu.enable_wom.await?;
// ... wait for motion / handle interrupt ...
imu.disable_wom.await?;
Self-Test & Calibration
Run the built-in self-test, apply host-delta offsets, or trigger on-demand calibration.
let accel_report = imu.run_accel_self_test.await?;
let gyro_report = imu.run_gyro_self_test.await?;
let axes = imu.read_self_test_axes.await?;
imu.apply_accel_host_delta_offset_with_delay.await?;
imu.apply_gyro_host_delta_offset_with_delay.await?;
let bias = imu.copy_gyro_bias_and_read.await?;
imu.run_on_demand_calibration.await?;
let _ = ;
Operating Modes
Switch between accelerometer-only, gyroscope-only, or dual-sensor modes. The driver returns the required stabilization delay.
use OperatingMode;
let delay_ns = imu.set_mode.await?;
if delay_ns > 0
imu.set_mode_with_delay.await?;
Target Platforms
The driver is #![no_std] and builds for any target that supports embedded-hal-async.
Tested targets include:
ESP32 (Xtensa) — requires the Espressif esp toolchain:
xtensa-esp32-none-elfxtensa-esp32s2-none-elfxtensa-esp32s3-none-elf
ESP32 (RISC-V):
riscv32imc-unknown-none-elfriscv32imac-unknown-none-elf
ARM Cortex-M — standard Rust toolchains:
thumbv6m-none-eabi,thumbv7m-none-eabi,thumbv7em-none-eabi,thumbv7em-none-eabihf,thumbv8m.base-none-eabi,thumbv8m.main-none-eabi,thumbv8m.main-none-eabihf
Cargo Features
| Feature | Description |
|---|---|
defmt |
Enable defmt::Format derives on public types for structured logging |
fixed |
Enable fixed-point conversion helpers (I32F32) for raw-to-physical-unit math |
No features are enabled by default.
Testing
The driver has unit tests covering configuration validation, data decoding, FIFO frame parsing, and interrupt/status decoding. Run them with:
End-to-end hardware validation uses the apps/qa-runner app on
ESP32-S3.
Release Checklist
See RELEASE_CHECKLIST.md.