BMI323 (bmi323 v0.1.0)

Copy Markdown

Driver for the Bosch BMI323 6-DoF inertial measurement unit.

Communicates over I²C (or any other Wafer transport that implements the Wafer.I2C protocol).

Protocol notes

The BMI323 uses an unusual word-oriented register protocol:

  • Every register is 16 bits wide, little-endian.
  • Every read returns two dummy bytes before the payload; this driver strips them transparently.
  • Writes have no dummy bytes — just the 8-bit address followed by the 16-bit value(s).
  • Multi-word reads and writes auto-increment the register address.

Example

{:ok, i2c} = Wafer.Driver.Circuits.I2C.acquire(bus_name: "i2c-1", address: 0x68)
{:ok, bmi} = BMI323.acquire(conn: i2c)

Summary

Functions

Wrap an existing Wafer connection in a BMI323 struct.

Read the device's CHIP_ID register, returning the 7-bit identifier (high byte of the 16-bit register is reserved and discarded).

Configure the accelerometer and cache the chosen range for later scaling.

Configure the gyroscope and cache the chosen range for later scaling.

The default 7-bit I²C address (0x68, SDO pin tied to GND).

Populate the cached ranges by reading ACC_CONF and GYR_CONF.

The expected CHIP_ID value (0x43) returned by an unmodified BMI323.

Read the accelerometer x/y/z sample and return scaled values in m/s².

Read the gyroscope x/y/z sample and return scaled values in rad/s.

Read accelerometer + gyroscope + temperature in a single 7-word burst.

Read the 32-bit sensor time counter.

Read the temperature sample and return °C. Returns {:error, :invalid_sample} if the device reports the invalid sentinel 0x8000.

The sensor time counter's tick period in microseconds (39.0625 µs).

Issue a soft reset by writing the 0xDEAF command to the CMD register.

Types

acquire_option()

@type acquire_option() ::
  {:conn, Wafer.Conn.t()}
  | {:soft_reset, boolean()}
  | {:verify_chip_id, boolean()}

axes()

@type axes() :: %{x: float(), y: float(), z: float()}

chip_id()

@type chip_id() :: byte()

imu_sample()

@type imu_sample() :: %{
  accelerometer: axes(),
  gyroscope: axes(),
  temperature: float()
}

t()

@type t() :: %BMI323{
  accelerometer_range: BMI323.Config.accelerometer_range() | nil,
  conn: Wafer.Conn.t(),
  gyroscope_range: BMI323.Config.gyroscope_range() | nil
}

Functions

acquire(opts)

@spec acquire([acquire_option()]) :: {:ok, t()} | {:error, term()}

Wrap an existing Wafer connection in a BMI323 struct.

Options

  • :conn (required) — a Wafer connection that implements the Wafer.I2C protocol, e.g. Wafer.Driver.Circuits.I2C or Wafer.Driver.Fake.
  • :soft_reset (default false) — when true, issue a soft reset immediately after wrapping the connection. See soft_reset/1.
  • :verify_chip_id (default true) — when true, read CHIP_ID and return {:error, {:chip_id_mismatch, got: byte, expected: 0x43}} if the device does not identify as a BMI323.

chip_id(bmi)

@spec chip_id(t()) :: {:ok, chip_id()} | {:error, term()}

Read the device's CHIP_ID register, returning the 7-bit identifier (high byte of the 16-bit register is reserved and discarded).

configure_accelerometer(bmi, opts)

@spec configure_accelerometer(
  t(),
  keyword()
) :: {:ok, t()} | {:error, term()}

Configure the accelerometer and cache the chosen range for later scaling.

See BMI323.Config.encode_acc_conf/1 for the supported options. :mode and :odr are required; :range, :bandwidth, and :averaging have defaults.

configure_gyroscope(bmi, opts)

@spec configure_gyroscope(
  t(),
  keyword()
) :: {:ok, t()} | {:error, term()}

Configure the gyroscope and cache the chosen range for later scaling.

See BMI323.Config.encode_gyr_conf/1 for the supported options.

default_i2c_address()

@spec default_i2c_address() :: 104

The default 7-bit I²C address (0x68, SDO pin tied to GND).

The alternate address 0x69 is selected by tying SDO to VDDIO.

detect_ranges(bmi)

@spec detect_ranges(t()) :: {:ok, t()} | {:error, term()}

Populate the cached ranges by reading ACC_CONF and GYR_CONF.

Useful after acquire/1 when the device has already been configured by some other process and you need to scale samples without reconfiguring.

expected_chip_id()

@spec expected_chip_id() :: 67

The expected CHIP_ID value (0x43) returned by an unmodified BMI323.

read_accelerometer(bmi)

@spec read_accelerometer(t()) :: {:ok, axes()} | {:error, term()}

Read the accelerometer x/y/z sample and return scaled values in m/s².

Requires the accelerometer range to be cached on the struct — call configure_accelerometer/2 or detect_ranges/1 first.

read_gyroscope(bmi)

@spec read_gyroscope(t()) :: {:ok, axes()} | {:error, term()}

Read the gyroscope x/y/z sample and return scaled values in rad/s.

Requires the gyroscope range to be cached on the struct — call configure_gyroscope/2 or detect_ranges/1 first.

read_imu(bmi)

@spec read_imu(t()) :: {:ok, imu_sample()} | {:error, term()}

Read accelerometer + gyroscope + temperature in a single 7-word burst.

Requires both ranges to be cached on the struct.

read_sensor_time(bmi)

@spec read_sensor_time(t()) :: {:ok, non_neg_integer()} | {:error, term()}

Read the 32-bit sensor time counter.

One LSB equals 39.0625 µs. Multiply by sensor_time_tick_us/0 to get microseconds.

read_temperature(bmi)

@spec read_temperature(t()) :: {:ok, float()} | {:error, term()}

Read the temperature sample and return °C. Returns {:error, :invalid_sample} if the device reports the invalid sentinel 0x8000.

sensor_time_tick_us()

@spec sensor_time_tick_us() :: float()

The sensor time counter's tick period in microseconds (39.0625 µs).

soft_reset(bmi)

@spec soft_reset(t()) :: {:ok, t()} | {:error, term()}

Issue a soft reset by writing the 0xDEAF command to the CMD register.

The device returns to suspend mode and all user configuration is restored to its power-on default. Blocks for 10 ms to satisfy the device's post-reset start-up time before returning.