LIS3DH (lis3dh v0.1.0)
Copy MarkdownDriver for the STMicroelectronics LIS3DH 3-axis MEMS accelerometer.
Communicates over I²C (or any other Wafer
transport that implements the Wafer.I2C protocol).
Protocol notes
The LIS3DH uses a standard byte-oriented I²C register protocol. Multi-byte reads and writes only auto-increment the register address when bit 7 of the sub-address is set; this driver sets that bit unconditionally on every transaction, which is harmless for single-byte access and required for bursts.
I²C address
The 7-bit address is 0b0011000x where x is the value of the SA0 pin
(also called SDO):
SA0 = GND→0x18(default).SA0 = VDD→0x19.
Example
{:ok, i2c} = Wafer.Driver.Circuits.I2C.acquire(bus_name: "i2c-1", address: 0x18)
{:ok, acc} = LIS3DH.acquire(conn: i2c)
Summary
Functions
Wrap an existing Wafer connection in a LIS3DH struct.
Configure the accelerometer's operating mode, ODR, range, axis enables, and
block-data-update setting. Caches the chosen :operating_mode and :range
on the struct so subsequent reads can scale samples without re-reading the
config registers.
Configure sleep-to-wake / return-to-sleep by writing ACT_THS and
ACT_DUR.
Configure click / double-click / tap detection by writing CLICK_CFG,
CLICK_THS, TIME_LIMIT, TIME_LATENCY, and TIME_WINDOW.
Configure a free-fall detector on the given interrupt pin.
Configure the on-chip high-pass filter via CTRL_REG2.
Configure an inertial interrupt (1 or 2) by writing INT*_CFG, INT*_THS,
and INT*_DURATION atomically.
Configure a motion (wake-up) detector on the given interrupt pin.
Configure 6D or 4D orientation detection on the given interrupt pin.
The default 7-bit I²C address (0x18, SA0 pin tied to GND). The alternate
address 0x19 is selected by tying SA0 to VDD.
Populate the cached :operating_mode and :range by reading CTRL_REG1
and CTRL_REG4. Useful after acquire/1 when the device has already been
configured by some other process.
Disable activity detection by writing 0 to ACT_THS.
Clear TEMP_CFG_REG.ADC_EN, disabling all three auxiliary ADC channels.
Mask out the given routing bits in CTRL_REG3 (INT1 routing).
Mask out the given routing bits in CTRL_REG6 (INT2 routing).
Clear TEMP_CFG_REG.TEMP_EN (leaving ADC_EN alone).
Enable the on-chip auxiliary ADC by setting TEMP_CFG_REG.ADC_EN. The ADC
samples at the configured CTRL_REG1.ODR. Requires :block_data_update
(CTRL_REG4.BDU) to be :hold for consistent reads — configure_accelerometer/2
defaults to that already.
OR-in the given routing bits in CTRL_REG3 (INT1 routing). Leaves the
other bits untouched, so it composes cleanly with LIS3DH.Sampler which
also writes the FIFO bits in this register.
OR-in the given routing bits in CTRL_REG6 (INT2 routing). Preserves the
INT_POLARITY bit and any others not in events.
Enable the embedded temperature sensor by setting both TEMP_CFG_REG.ADC_EN
and TEMP_CFG_REG.TEMP_EN. The temperature reading is routed to channel 3
of the auxiliary ADC; read it via read_temperature/1.
The expected WHO_AM_I value (0x33) returned by an unmodified LIS3DH.
Set CTRL_REG1.ODR to 0000 (power-down mode), preserving the other
fields.
Set CTRL_REG1.ODR to a non-zero rate without changing the other fields,
bringing the sensor out of power-down. Equivalent to a write to CTRL_REG1
with the chosen ODR while preserving the LPen and axis enable bits.
Read the accelerometer x/y/z sample and return scaled values in m/s².
Read auxiliary ADC channel 1, 2, or 3 and return the absolute voltage in millivolts.
Read the CLICK_SRC register and decode it. Reading clears the latched
flags if LIR_Click was set during configure.
Read the INT*_SRC register. Reading clears the latched flags if latching
is enabled (LIR_INTx in CTRL_REG5).
Read the REFERENCE register. With :normal_with_reset HPF mode (the
default after power-up), this read also resets the high-pass filter's
internal state.
Read the embedded temperature sensor on auxiliary ADC channel 3 and return
the delta temperature in °C, relative to the 25 °C factory
calibration point (i.e. add 25.0 for the absolute reading).
Refresh the internal trim registers from non-volatile memory by setting
CTRL_REG5.BOOT. Blocks for 5 ms to give the device time
to finish the boot sequence before returning.
Toggle 4D detection for the given pin via CTRL_REG5.D4D_INT1 /
D4D_INT2. 4D restricts 6D detection to the X/Y plane (Z position
ignored). Has no effect unless INT*_CFG.6D is also set.
Toggle interrupt latching for the given pin via CTRL_REG5.LIR_INT1 /
LIR_INT2. When latched, the interrupt pin stays asserted until the
corresponding INT*_SRC register is read.
Set the active level for both INT pins via CTRL_REG6.INT_POLARITY.
Set the CTRL_REG4.ST self-test field while preserving the other bits.
Read the device's WHO_AM_I register.
Write the REFERENCE register (used as the HPF reference in :reference mode).
Types
@type acquire_option() :: {:conn, Wafer.Conn.t()} | {:verify_who_am_i, boolean()} | {:reboot, boolean()}
@type t() :: %LIS3DH{ conn: Wafer.Conn.t(), operating_mode: LIS3DH.Config.operating_mode() | nil, range: LIS3DH.Config.range() | nil }
@type who_am_i() :: byte()
Functions
@spec acquire([acquire_option()]) :: {:ok, t()} | {:error, term()}
Wrap an existing Wafer connection in a LIS3DH struct.
Options
:conn(required) — a Wafer connection that implements theWafer.I2Cprotocol, e.g.Wafer.Driver.Circuits.I2CorWafer.Driver.Fake.:verify_who_am_i(defaulttrue) — whentrue, readWHO_AM_Iand return{:error, {:who_am_i_mismatch, got: byte, expected: 0x33}}if the device does not identify as a LIS3DH.:reboot(defaultfalse) — whentrue, setCTRL_REG5.BOOTto refresh the internal trim registers from non-volatile memory and block for 5 ms before returning. Useful after power glitches or when you suspect the trim values have been corrupted.
Configure the accelerometer's operating mode, ODR, range, axis enables, and
block-data-update setting. Caches the chosen :operating_mode and :range
on the struct so subsequent reads can scale samples without re-reading the
config registers.
See LIS3DH.Config.encode_ctrl_reg_1/1 and
LIS3DH.Config.encode_ctrl_reg_4/1 for the supported options. :mode and
:odr are required.
Writes CTRL_REG4 first (range / HR / BDU), then CTRL_REG1 (ODR / LPen /
axes), so the device is fully reconfigured before sampling resumes.
Configure sleep-to-wake / return-to-sleep by writing ACT_THS and
ACT_DUR.
When acceleration falls below :threshold_mg for the configured
:duration, the device automatically switches to low-power mode at 10 Hz
ODR regardless of the original CTRL_REG1 / CTRL_REG4 settings. When
acceleration rises above the threshold, the device restores the original
configuration.
Options
:threshold_mg— threshold in milli-g (required). Uses the same LSB table asINT*_THS. Pass0to disable activity detection.:duration—0..255(required). One LSB corresponds to(8 × duration + 1) / ODRseconds per datasheet §8.36.
Requires the accelerometer range to be cached on the struct.
Configure click / double-click / tap detection by writing CLICK_CFG,
CLICK_THS, TIME_LIMIT, TIME_LATENCY, and TIME_WINDOW.
Options
:events— list ofLIS3DH.Click.click_event/0to enable (required; pass[]to disable all).:threshold_mg— threshold in milli-g (required). Same LSB table asINT*_THS.:latched— whentrue, the click interrupt stays high untilCLICK_SRCis read (defaultfalse).:time_limit—0..127count of1/ODRperiods, the max click pulse width (required).:time_latency—0..255count of1/ODRperiods, the dead time after a click (required).:time_window—0..255count of1/ODRperiods, the search window for the second click of a double-click (default0).
Requires the accelerometer range to be cached on the struct.
@spec configure_free_fall(t(), LIS3DH.Interrupts.pin(), keyword()) :: {:ok, t()} | {:error, term()}
Configure a free-fall detector on the given interrupt pin.
Free-fall is signalled when the magnitude of acceleration on all three axes falls below a threshold for a configurable duration (i.e. the device is in true free fall, ~0 g on every axis).
Options
:threshold_mg— threshold in milli-g (default350, the AN3308 recommended value). Lower thresholds trigger more easily.:duration—0..127count of1/ODRperiods (default5).
Configure the on-chip high-pass filter via CTRL_REG2.
See LIS3DH.Config.encode_ctrl_reg_2/1 for the supported options.
@spec configure_inertial_interrupt(t(), LIS3DH.Interrupts.pin(), keyword()) :: {:ok, t()} | {:error, term()}
Configure an inertial interrupt (1 or 2) by writing INT*_CFG, INT*_THS,
and INT*_DURATION atomically.
Options
:mode—LIS3DH.Interrupts.aoi_mode/0(default:or).:axes— list ofLIS3DH.Interrupts.axis_event/0to enable.:threshold_mg— non-negative integer threshold in milli-g. The LSB size depends on the cached:range; this function reads the cached value and rounds the threshold to fit.:duration—0..127count of1/ODRperiods the condition must hold before the interrupt fires (default0).
Requires the accelerometer range to be cached on the struct.
@spec configure_motion(t(), LIS3DH.Interrupts.pin(), keyword()) :: {:ok, t()} | {:error, term()}
Configure a motion (wake-up) detector on the given interrupt pin.
Motion is signalled when any enabled axis exceeds the threshold for the configured duration.
Options
:threshold_mg— threshold in milli-g (no default, must be specified).:duration—0..127count of1/ODRperiods (default0).:axes— list ofLIS3DH.Interrupts.axis_event/0(default[:x_high, :y_high, :z_high]).
@spec configure_orientation(t(), LIS3DH.Interrupts.pin(), keyword()) :: {:ok, t()} | {:error, term()}
Configure 6D or 4D orientation detection on the given interrupt pin.
Options
:mode—:movement(interrupt fires on transitions between known zones) or:position(interrupt stays asserted while inside a known zone). Default:position.:detection—:six_d(default, all six face-down/face-up directions) or:four_d(X/Y plane only, Z ignored — for portrait/landscape).:axes— list ofLIS3DH.Interrupts.axis_event/0to enable (default all six).:threshold_mg— threshold in milli-g (no default; the zone half-width is typically chosen so two zones don't overlap).:duration—0..127count of1/ODRperiods (default0).
Writes the configured INT*_CFG, INT*_THS, INT*_DURATION and also
toggles CTRL_REG5.D4D_INT* to match the :detection choice.
@spec default_i2c_address() :: 24
The default 7-bit I²C address (0x18, SA0 pin tied to GND). The alternate
address 0x19 is selected by tying SA0 to VDD.
Populate the cached :operating_mode and :range by reading CTRL_REG1
and CTRL_REG4. Useful after acquire/1 when the device has already been
configured by some other process.
Disable activity detection by writing 0 to ACT_THS.
Clear TEMP_CFG_REG.ADC_EN, disabling all three auxiliary ADC channels.
@spec disable_int1_routing(t(), [int1_event]) :: {:ok, t()} | {:error, term()} when int1_event: :click | :ia1 | :ia2 | :zyxda | :adc_drdy_321 | :fifo_watermark | :fifo_overrun
Mask out the given routing bits in CTRL_REG3 (INT1 routing).
@spec disable_int2_routing(t(), [int2_event]) :: {:ok, t()} | {:error, term()} when int2_event: :click | :ia1 | :ia2 | :boot | :activity
Mask out the given routing bits in CTRL_REG6 (INT2 routing).
Clear TEMP_CFG_REG.TEMP_EN (leaving ADC_EN alone).
Enable the on-chip auxiliary ADC by setting TEMP_CFG_REG.ADC_EN. The ADC
samples at the configured CTRL_REG1.ODR. Requires :block_data_update
(CTRL_REG4.BDU) to be :hold for consistent reads — configure_accelerometer/2
defaults to that already.
@spec enable_int1_routing(t(), [int1_event]) :: {:ok, t()} | {:error, term()} when int1_event: :click | :ia1 | :ia2 | :zyxda | :adc_drdy_321 | :fifo_watermark | :fifo_overrun
OR-in the given routing bits in CTRL_REG3 (INT1 routing). Leaves the
other bits untouched, so it composes cleanly with LIS3DH.Sampler which
also writes the FIFO bits in this register.
Valid events: :click, :ia1, :ia2, :zyxda, :adc_drdy_321,
:fifo_watermark, :fifo_overrun.
@spec enable_int2_routing(t(), [int2_event]) :: {:ok, t()} | {:error, term()} when int2_event: :click | :ia1 | :ia2 | :boot | :activity
OR-in the given routing bits in CTRL_REG6 (INT2 routing). Preserves the
INT_POLARITY bit and any others not in events.
Valid events: :click, :ia1, :ia2, :boot, :activity.
Enable the embedded temperature sensor by setting both TEMP_CFG_REG.ADC_EN
and TEMP_CFG_REG.TEMP_EN. The temperature reading is routed to channel 3
of the auxiliary ADC; read it via read_temperature/1.
@spec expected_who_am_i() :: 51
The expected WHO_AM_I value (0x33) returned by an unmodified LIS3DH.
Set CTRL_REG1.ODR to 0000 (power-down mode), preserving the other
fields.
@spec power_on(t(), LIS3DH.Config.odr()) :: {:ok, t()} | {:error, term()}
Set CTRL_REG1.ODR to a non-zero rate without changing the other fields,
bringing the sensor out of power-down. Equivalent to a write to CTRL_REG1
with the chosen ODR while preserving the LPen and axis enable bits.
Read the accelerometer x/y/z sample and return scaled values in m/s².
Requires :operating_mode and :range to be cached on the struct — call
configure_accelerometer/2 or detect_configuration/1 first.
Read auxiliary ADC channel 1, 2, or 3 and return the absolute voltage in millivolts.
The chip's ADC input range is centred on 1200 mV with a
±400 mV span, so the returned value is in
800..1600 mV.
ADC resolution depends on the operating mode (10-bit in normal /
high-resolution, 8-bit in low-power), so this function requires
:operating_mode to be cached on the struct.
@spec read_click_source(t()) :: {:ok, LIS3DH.Click.source_flags()} | {:error, term()}
Read the CLICK_SRC register and decode it. Reading clears the latched
flags if LIR_Click was set during configure.
@spec read_interrupt_source(t(), LIS3DH.Interrupts.pin()) :: {:ok, LIS3DH.Interrupts.source_flags()} | {:error, term()}
Read the INT*_SRC register. Reading clears the latched flags if latching
is enabled (LIR_INTx in CTRL_REG5).
Read the REFERENCE register. With :normal_with_reset HPF mode (the
default after power-up), this read also resets the high-pass filter's
internal state.
Read the embedded temperature sensor on auxiliary ADC channel 3 and return
the delta temperature in °C, relative to the 25 °C factory
calibration point (i.e. add 25.0 for the absolute reading).
Only the OUT_ADC3_H byte carries temperature data — sensitivity is
1 LSB/°C and resolution is 8-bit regardless of operating mode
(datasheet §3.2). The full 16-bit word is still read so BDU=:hold
unlatches cleanly.
Requires the temperature sensor to be enabled via
enable_temperature_sensor/1.
Refresh the internal trim registers from non-volatile memory by setting
CTRL_REG5.BOOT. Blocks for 5 ms to give the device time
to finish the boot sequence before returning.
@spec set_4d_detection(t(), LIS3DH.Interrupts.pin(), boolean()) :: {:ok, t()} | {:error, term()}
Toggle 4D detection for the given pin via CTRL_REG5.D4D_INT1 /
D4D_INT2. 4D restricts 6D detection to the X/Y plane (Z position
ignored). Has no effect unless INT*_CFG.6D is also set.
@spec set_interrupt_latching(t(), LIS3DH.Interrupts.pin(), boolean()) :: {:ok, t()} | {:error, term()}
Toggle interrupt latching for the given pin via CTRL_REG5.LIR_INT1 /
LIR_INT2. When latched, the interrupt pin stays asserted until the
corresponding INT*_SRC register is read.
Set the active level for both INT pins via CTRL_REG6.INT_POLARITY.
polarity is :active_high (default after reset) or :active_low.
@spec set_self_test(t(), LIS3DH.Config.self_test_mode()) :: {:ok, t()} | {:error, term()}
Set the CTRL_REG4.ST self-test field while preserving the other bits.
The recommended self-test procedure (per ST application note AN3308) is:
- Power up the device and
configure_accelerometer/2for normal mode, ±2g, 50 Hz, BDU=:hold. - Wait for stable output (≥ a few ODR periods) and average several baseline samples.
- Call
set_self_test(acc, :self_test_0)and wait for the documented turn-on time (90 ms typical). - Average several test samples; the per-axis delta vs. the baseline must fall within the limits in datasheet table 4.
- Restore with
set_self_test(acc, :off). - Optionally repeat with
:self_test_1for the alternate direction.
This helper just toggles the ST field; the user owns the timing, averaging, and pass/fail check.
Read the device's WHO_AM_I register.
Write the REFERENCE register (used as the HPF reference in :reference mode).