Skip to main content

lis2de12/
lib.rs

1//! High-level LIS2DE12 accelerometer driver with synchronous and asynchronous FIFO helpers.
2//!
3//! This driver supports both I2C and SPI communication interfaces, with blocking and async modes.
4//!
5//! # Examples
6//!
7//! ## Blocking I2C
8//!
9//! ```no_run
10//! use lis2de12::{Lis2de12, SlaveAddr};
11//! # use embedded_hal::i2c::I2c;
12//! # fn example<I2C: I2c>(i2c: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
13//! # where I2C::Error: core::fmt::Debug {
14//!
15//! // Create driver with default configuration
16//! let mut sensor = Lis2de12::new_i2c(i2c, SlaveAddr::default())?;
17//!
18//! // Read acceleration in g (floating point)
19//! let accel = sensor.read_g()?;
20//! # Ok(())
21//! # }
22//! ```
23//!
24//! ## Blocking SPI
25//!
26//! ```no_run
27//! use lis2de12::Lis2de12;
28//! # use embedded_hal::spi::SpiDevice;
29//! # fn example<SPI: SpiDevice>(spi: SPI) -> Result<(), lis2de12::Error<SPI::Error>>
30//! # where SPI::Error: core::fmt::Debug {
31//!
32//! // Create driver with SPI interface
33//! let mut sensor = Lis2de12::new_spi(spi)?;
34//!
35//! // Read acceleration in milli-g (integer)
36//! let accel_mg = sensor.read_mg()?;
37//! # Ok(())
38//! # }
39//! ```
40//!
41//! ## Async I2C
42//!
43//! ```no_run
44//! use lis2de12::{Lis2de12Async, SlaveAddr};
45//! # use embedded_hal_async::i2c::I2c;
46//! # async fn example<I2C: I2c>(i2c: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
47//! # where I2C::Error: core::fmt::Debug {
48//!
49//! // Create async driver with I2C
50//! let mut sensor = Lis2de12Async::new_i2c(i2c, SlaveAddr::default()).await?;
51//!
52//! // Read acceleration asynchronously
53//! let accel = sensor.read_g().await?;
54//! # Ok(())
55//! # }
56//! ```
57//!
58//! ## Async SPI
59//!
60//! ```no_run
61//! use lis2de12::Lis2de12Async;
62//! # use embedded_hal_async::spi::SpiDevice;
63//! # async fn example<SPI: SpiDevice>(spi: SPI) -> Result<(), lis2de12::Error<SPI::Error>>
64//! # where SPI::Error: core::fmt::Debug {
65//!
66//! // Create async driver with SPI
67//! let mut sensor = Lis2de12Async::new_spi(spi).await?;
68//!
69//! // Read acceleration asynchronously
70//! let accel = sensor.read_g().await?;
71//! # Ok(())
72//! # }
73//! ```
74//!
75//! ## Custom Configuration
76//!
77//! ```no_run
78//! use lis2de12::{Lis2de12, Lis2de12Config, SlaveAddr, Odr, Fs};
79//! # use embedded_hal::i2c::I2c;
80//! # fn example<I2C: I2c>(i2c: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
81//! # where I2C::Error: core::fmt::Debug {
82//!
83//! let config = Lis2de12Config {
84//!     odr: Odr::Hz400,           // 400 Hz output data rate
85//!     scale: Fs::G8,             // ±8g full scale
86//!     ..Default::default()
87//! };
88//!
89//! let mut sensor = Lis2de12::new_i2c_with_config(i2c, SlaveAddr::default(), config)?;
90//! # Ok(())
91//! # }
92//! ```
93//!
94//! ## Temperature Sensor
95//!
96//! ```no_run
97//! use lis2de12::{Lis2de12, Lis2de12Config, SlaveAddr};
98//! # use embedded_hal::i2c::I2c;
99//! # fn example<I2C: I2c>(i2c: I2C) -> Result<(), lis2de12::Error<I2C::Error>>
100//! # where I2C::Error: core::fmt::Debug {
101//!
102//! // Enable temperature sensor in configuration
103//! let config = Lis2de12Config {
104//!     temperature_enable: true,
105//!     ..Default::default()
106//! };
107//!
108//! let mut sensor = Lis2de12::new_i2c_with_config(i2c, SlaveAddr::default(), config)?;
109//!
110//! // Or enable it after initialization
111//! sensor.set_temperature_sensor(true)?;
112//!
113//! // Read temperature change in °C (relative to power-on reference)
114//! // Positive = warmer, negative = cooler
115//! let temp_delta = sensor.read_temperature()?;
116//!
117//! // Or read raw 16-bit left-justified value
118//! let temp_raw = sensor.read_temperature_raw()?;
119//! # Ok(())
120//! # }
121//! ```
122//!
123//! # SPI Mode
124//!
125//! The LIS2DE12 requires SPI Mode 0 or Mode 3 (CPOL=0, CPHA=0 or CPOL=1, CPHA=1).
126//! The driver uses the `embedded-hal` `SpiDevice` trait which handles chip select automatically.
127
128#![cfg_attr(not(feature = "std"), no_std)]
129#![deny(missing_docs)]
130#![deny(warnings)]
131#![allow(clippy::missing_errors_doc)]
132
133use core::cmp;
134use core::fmt::Debug;
135
136use accelerometer::vector::{F32x3, I16x3};
137use accelerometer::{Error, ErrorKind, RawAccelerometer};
138#[cfg(feature = "async")]
139use device_driver::{AsyncBufferInterface, AsyncRegisterInterface};
140use device_driver::{BufferInterface, BufferInterfaceError, RegisterInterface};
141use embedded_hal as hal;
142#[cfg(feature = "async")]
143use embedded_hal_async as hal_async;
144use hal::i2c::I2c;
145#[cfg(feature = "async")]
146use hal_async::i2c::I2c as AsyncI2c;
147#[cfg(feature = "async")]
148use hal_async::spi::SpiDevice as AsyncSpiDevice;
149
150#[allow(unsafe_code)]
151#[allow(missing_docs)]
152#[allow(clippy::doc_markdown, clippy::missing_errors_doc, clippy::identity_op)]
153mod generated {
154    device_driver::create_device!(
155        device_name: Lis2de12Device,
156        manifest: "src/lis2de12.yaml"
157    );
158}
159
160pub use generated::{Fm, Fs, Lis2de12Device, Odr, St, TempEn, field_sets};
161
162/// Number of bytes per FIFO frame (X, Y, Z 16-bit samples).
163pub const FIFO_FRAME_BYTES: usize = 6;
164/// Maximum FIFO depth in frames.
165pub const FIFO_CAPACITY: u8 = 32;
166/// FIFO frame buffer alias.
167pub type FifoFrame = [u8; FIFO_FRAME_BYTES];
168/// Highest programmable FIFO watermark level.
169pub const FIFO_WATERMARK_MAX: u8 = 31;
170
171/// High-level FIFO operating modes mirroring the `FM` register field.
172#[derive(Copy, Clone, Debug, Eq, PartialEq)]
173pub enum FifoMode {
174    /// FIFO disabled (register reads access the most recent sample).
175    Bypass,
176    /// FIFO mode (stops on full).
177    Fifo,
178    /// Stream mode (circular buffer).
179    Stream,
180    /// Stream-to-FIFO mode (stream until trigger then stop-on-full).
181    StreamToFifo,
182}
183
184impl From<FifoMode> for Fm {
185    fn from(mode: FifoMode) -> Self {
186        match mode {
187            FifoMode::Bypass => Fm::Bypass,
188            FifoMode::Fifo => Fm::Fifo,
189            FifoMode::Stream => Fm::Stream,
190            FifoMode::StreamToFifo => Fm::StreamToFifo,
191        }
192    }
193}
194
195impl From<Fm> for FifoMode {
196    fn from(mode: Fm) -> Self {
197        match mode {
198            Fm::Bypass => FifoMode::Bypass,
199            Fm::Fifo => FifoMode::Fifo,
200            Fm::Stream => FifoMode::Stream,
201            Fm::StreamToFifo => FifoMode::StreamToFifo,
202        }
203    }
204}
205
206// ============================================================================
207// Interrupt Types
208// ============================================================================
209
210/// Interrupt pin polarity.
211#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
212pub enum InterruptPolarity {
213    /// Interrupt pins are active high (default).
214    #[default]
215    ActiveHigh,
216    /// Interrupt pins are active low.
217    ActiveLow,
218}
219
220/// Latch mode for interrupt signals.
221#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
222pub enum LatchMode {
223    /// Interrupt signal is not latched: the pin stays high only for the duration of the
224    /// latency window, then clears automatically regardless of whether the source register
225    /// has been read.
226    #[default]
227    NonLatched,
228    /// Interrupt signal is latched: the pin stays high until the interrupt source register
229    /// (`INT1_SRC`, `INT2_SRC`, or `CLICK_SRC`) is read, which also clears the flag.
230    Latched,
231}
232
233/// Returns `(aoi, sixd, d4d)` register bits for a given motion detection mode.
234fn mode_bits(mode: MotionDetectionMode) -> (bool, bool, bool) {
235    match mode {
236        MotionDetectionMode::OrCombination => (false, false, false),
237        MotionDetectionMode::AndCombination => (true, false, false),
238        MotionDetectionMode::SixDirection => (false, true, false),
239        MotionDetectionMode::SixDirectionPosition => (true, true, false),
240        MotionDetectionMode::FourDirection => (false, true, true),
241        MotionDetectionMode::FourDirectionPosition => (true, true, true),
242    }
243}
244
245/// Motion detection mode determining how axis events are combined.
246#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
247pub enum MotionDetectionMode {
248    /// OR combination of enabled axis events.
249    #[default]
250    OrCombination,
251    /// AND combination of enabled axis events.
252    AndCombination,
253    /// 6-direction movement detection (all axes).
254    SixDirection,
255    /// 6-direction position detection (all axes).
256    SixDirectionPosition,
257    /// 4-direction movement detection (X/Y only; Z ignored).
258    /// Sets D4D_INTx in CTRL_REG5 alongside the SIXD bit.
259    FourDirection,
260    /// 4-direction position detection (X/Y only; Z ignored).
261    /// Sets D4D_INTx in CTRL_REG5 alongside the SIXD bit.
262    FourDirectionPosition,
263}
264
265/// Per-axis motion event enables for high/low thresholds.
266#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
267#[allow(clippy::struct_excessive_bools)]
268pub struct MotionAxesConfig {
269    /// Enable X-axis high event detection.
270    pub x_high: bool,
271    /// Enable X-axis low event detection.
272    pub x_low: bool,
273    /// Enable Y-axis high event detection.
274    pub y_high: bool,
275    /// Enable Y-axis low event detection.
276    pub y_low: bool,
277    /// Enable Z-axis high event detection.
278    pub z_high: bool,
279    /// Enable Z-axis low event detection.
280    pub z_low: bool,
281}
282
283impl MotionAxesConfig {
284    /// No axes enabled.
285    #[must_use]
286    pub const fn none() -> Self {
287        Self {
288            x_high: false,
289            x_low: false,
290            y_high: false,
291            y_low: false,
292            z_high: false,
293            z_low: false,
294        }
295    }
296
297    /// All high events enabled on all axes.
298    #[must_use]
299    pub const fn all_high() -> Self {
300        Self {
301            x_high: true,
302            x_low: false,
303            y_high: true,
304            y_low: false,
305            z_high: true,
306            z_low: false,
307        }
308    }
309
310    /// All low events enabled on all axes.
311    #[must_use]
312    pub const fn all_low() -> Self {
313        Self {
314            x_high: false,
315            x_low: true,
316            y_high: false,
317            y_low: true,
318            z_high: false,
319            z_low: true,
320        }
321    }
322
323    /// All events enabled on all axes.
324    #[must_use]
325    pub const fn all() -> Self {
326        Self {
327            x_high: true,
328            x_low: true,
329            y_high: true,
330            y_low: true,
331            z_high: true,
332            z_low: true,
333        }
334    }
335
336    /// Returns true if any axis event is enabled.
337    #[must_use]
338    pub const fn any(&self) -> bool {
339        self.x_high || self.x_low || self.y_high || self.y_low || self.z_high || self.z_low
340    }
341}
342
343/// Motion detection configuration for interrupt generators (IA1/IA2).
344#[derive(Copy, Clone, Debug, Eq, PartialEq)]
345pub struct MotionConfig {
346    /// Enable motion detection.
347    pub enable: bool,
348    /// Motion detection mode (OR/AND/6D).
349    pub mode: MotionDetectionMode,
350    /// Per-axis event enables.
351    pub axes: MotionAxesConfig,
352    /// Threshold value (0-127). LSb weight per full-scale setting:
353    /// 16 mg @ ±2 g | 32 mg @ ±4 g | 62 mg @ ±8 g | 186 mg @ ±16 g.
354    pub threshold: u8,
355    /// Duration value (0-127, `LSb` = 1/ODR).
356    pub duration: u8,
357    /// Latch mode for interrupt signal.
358    pub latch: LatchMode,
359    /// Enable high-pass filter for this interrupt.
360    pub high_pass_filter: bool,
361}
362
363impl Default for MotionConfig {
364    fn default() -> Self {
365        Self {
366            enable: false,
367            mode: MotionDetectionMode::OrCombination,
368            axes: MotionAxesConfig::none(),
369            threshold: 0,
370            duration: 0,
371            latch: LatchMode::NonLatched,
372            high_pass_filter: false,
373        }
374    }
375}
376
377impl MotionConfig {
378    /// Create a disabled motion configuration.
379    #[must_use]
380    pub const fn disabled() -> Self {
381        Self {
382            enable: false,
383            mode: MotionDetectionMode::OrCombination,
384            axes: MotionAxesConfig::none(),
385            threshold: 0,
386            duration: 0,
387            latch: LatchMode::NonLatched,
388            high_pass_filter: false,
389        }
390    }
391
392    /// Set enable state.
393    #[must_use]
394    pub const fn with_enable(mut self, enable: bool) -> Self {
395        self.enable = enable;
396        self
397    }
398
399    /// Set motion detection mode.
400    #[must_use]
401    pub const fn with_mode(mut self, mode: MotionDetectionMode) -> Self {
402        self.mode = mode;
403        self
404    }
405
406    /// Set axis configuration.
407    #[must_use]
408    pub const fn with_axes(mut self, axes: MotionAxesConfig) -> Self {
409        self.axes = axes;
410        self
411    }
412
413    /// Set threshold (0-127).
414    #[must_use]
415    pub const fn with_threshold(mut self, threshold: u8) -> Self {
416        self.threshold = if threshold > 127 { 127 } else { threshold };
417        self
418    }
419
420    /// Set duration (0-127).
421    #[must_use]
422    pub const fn with_duration(mut self, duration: u8) -> Self {
423        self.duration = if duration > 127 { 127 } else { duration };
424        self
425    }
426
427    /// Set latch mode.
428    #[must_use]
429    pub const fn with_latch(mut self, latch: LatchMode) -> Self {
430        self.latch = latch;
431        self
432    }
433
434    /// Set high-pass filter enable.
435    #[must_use]
436    pub const fn with_high_pass_filter(mut self, enable: bool) -> Self {
437        self.high_pass_filter = enable;
438        self
439    }
440}
441
442/// Per-axis click event enables for single/double click.
443#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
444#[allow(clippy::struct_excessive_bools)]
445pub struct ClickAxesConfig {
446    /// Enable X-axis single-click detection.
447    pub x_single: bool,
448    /// Enable X-axis double-click detection.
449    pub x_double: bool,
450    /// Enable Y-axis single-click detection.
451    pub y_single: bool,
452    /// Enable Y-axis double-click detection.
453    pub y_double: bool,
454    /// Enable Z-axis single-click detection.
455    pub z_single: bool,
456    /// Enable Z-axis double-click detection.
457    pub z_double: bool,
458}
459
460impl ClickAxesConfig {
461    /// No axes enabled.
462    #[must_use]
463    pub const fn none() -> Self {
464        Self {
465            x_single: false,
466            x_double: false,
467            y_single: false,
468            y_double: false,
469            z_single: false,
470            z_double: false,
471        }
472    }
473
474    /// All single-click events enabled.
475    #[must_use]
476    pub const fn all_single() -> Self {
477        Self {
478            x_single: true,
479            x_double: false,
480            y_single: true,
481            y_double: false,
482            z_single: true,
483            z_double: false,
484        }
485    }
486
487    /// All double-click events enabled.
488    #[must_use]
489    pub const fn all_double() -> Self {
490        Self {
491            x_single: false,
492            x_double: true,
493            y_single: false,
494            y_double: true,
495            z_single: false,
496            z_double: true,
497        }
498    }
499
500    /// All click events enabled.
501    #[must_use]
502    pub const fn all() -> Self {
503        Self {
504            x_single: true,
505            x_double: true,
506            y_single: true,
507            y_double: true,
508            z_single: true,
509            z_double: true,
510        }
511    }
512
513    /// Returns true if any click event is enabled.
514    #[must_use]
515    pub const fn any(&self) -> bool {
516        self.x_single
517            || self.x_double
518            || self.y_single
519            || self.y_double
520            || self.z_single
521            || self.z_double
522    }
523}
524
525/// Click detection configuration.
526#[derive(Copy, Clone, Debug, Eq, PartialEq)]
527pub struct ClickConfig {
528    /// Enable click detection.
529    pub enable: bool,
530    /// Per-axis click event enables.
531    pub axes: ClickAxesConfig,
532    /// Click threshold (0-127). 1 LSb = full_scale / 128, so:
533    /// 15.6 mg @ ±2 g | 31.2 mg @ ±4 g | 62.5 mg @ ±8 g | 187.5 mg @ ±16 g.
534    pub threshold: u8,
535    /// Time limit for click detection (0-127, `LSb` = 1/ODR).
536    pub time_limit: u8,
537    /// Time latency for double-click detection (0-255, `LSb` = 1/ODR).
538    pub time_latency: u8,
539    /// Time window for double-click detection (0-255, `LSb` = 1/ODR).
540    pub time_window: u8,
541    /// Latch mode for click interrupt.
542    pub latch: LatchMode,
543    /// Enable high-pass filter for click detection.
544    pub high_pass_filter: bool,
545}
546
547impl Default for ClickConfig {
548    fn default() -> Self {
549        Self {
550            enable: false,
551            axes: ClickAxesConfig::none(),
552            threshold: 0,
553            time_limit: 0,
554            time_latency: 0,
555            time_window: 0,
556            latch: LatchMode::NonLatched,
557            high_pass_filter: false,
558        }
559    }
560}
561
562impl ClickConfig {
563    /// Create a disabled click configuration.
564    #[must_use]
565    pub const fn disabled() -> Self {
566        Self {
567            enable: false,
568            axes: ClickAxesConfig::none(),
569            threshold: 0,
570            time_limit: 0,
571            time_latency: 0,
572            time_window: 0,
573            latch: LatchMode::NonLatched,
574            high_pass_filter: false,
575        }
576    }
577
578    /// Set enable state.
579    #[must_use]
580    pub const fn with_enable(mut self, enable: bool) -> Self {
581        self.enable = enable;
582        self
583    }
584
585    /// Set axis configuration.
586    #[must_use]
587    pub const fn with_axes(mut self, axes: ClickAxesConfig) -> Self {
588        self.axes = axes;
589        self
590    }
591
592    /// Set threshold (0-127).
593    #[must_use]
594    pub const fn with_threshold(mut self, threshold: u8) -> Self {
595        self.threshold = if threshold > 127 { 127 } else { threshold };
596        self
597    }
598
599    /// Set time limit (0-127).
600    #[must_use]
601    pub const fn with_time_limit(mut self, time_limit: u8) -> Self {
602        self.time_limit = if time_limit > 127 { 127 } else { time_limit };
603        self
604    }
605
606    /// Set time latency (0-255).
607    #[must_use]
608    pub const fn with_time_latency(mut self, time_latency: u8) -> Self {
609        self.time_latency = time_latency;
610        self
611    }
612
613    /// Set time window (0-255).
614    #[must_use]
615    pub const fn with_time_window(mut self, time_window: u8) -> Self {
616        self.time_window = time_window;
617        self
618    }
619
620    /// Set latch mode.
621    #[must_use]
622    pub const fn with_latch(mut self, latch: LatchMode) -> Self {
623        self.latch = latch;
624        self
625    }
626
627    /// Set high-pass filter enable.
628    #[must_use]
629    pub const fn with_high_pass_filter(mut self, enable: bool) -> Self {
630        self.high_pass_filter = enable;
631        self
632    }
633}
634
635/// Activity/inactivity detection configuration (sleep-to-wake).
636#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
637pub struct ActivityConfig {
638    /// Enable activity detection.
639    pub enable: bool,
640    /// Activation threshold (0-127). LSb weight per full-scale setting:
641    /// 16 mg @ ±2 g | 32 mg @ ±4 g | 62 mg @ ±8 g | 186 mg @ ±16 g.
642    pub threshold: u8,
643    /// Minimum inactivity duration before entering sleep mode (0-255).
644    /// Actual time = (8 × value + 1) / ODR. At 100 Hz, value=99 → ~8 s.
645    pub duration: u8,
646}
647
648impl ActivityConfig {
649    /// Create a disabled activity configuration.
650    #[must_use]
651    pub const fn disabled() -> Self {
652        Self {
653            enable: false,
654            threshold: 0,
655            duration: 0,
656        }
657    }
658
659    /// Set enable state.
660    #[must_use]
661    pub const fn with_enable(mut self, enable: bool) -> Self {
662        self.enable = enable;
663        self
664    }
665
666    /// Set threshold (0-127).
667    #[must_use]
668    pub const fn with_threshold(mut self, threshold: u8) -> Self {
669        self.threshold = if threshold > 127 { 127 } else { threshold };
670        self
671    }
672
673    /// Set duration (0-255).
674    #[must_use]
675    pub const fn with_duration(mut self, duration: u8) -> Self {
676        self.duration = duration;
677        self
678    }
679}
680
681/// INT1 pin routing configuration.
682#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
683#[allow(clippy::struct_excessive_bools)]
684pub struct Int1Routing {
685    /// Route click interrupt to INT1.
686    pub click: bool,
687    /// Route IA1 (motion 1) interrupt to INT1.
688    pub ia1: bool,
689    /// Route IA2 (motion 2) interrupt to INT1.
690    pub ia2: bool,
691    /// Route data ready interrupt to INT1.
692    pub data_ready: bool,
693    /// Route FIFO watermark interrupt to INT1.
694    pub fifo_watermark: bool,
695    /// Route FIFO overrun interrupt to INT1.
696    pub fifo_overrun: bool,
697}
698
699impl Int1Routing {
700    /// No interrupts routed to INT1.
701    #[must_use]
702    pub const fn none() -> Self {
703        Self {
704            click: false,
705            ia1: false,
706            ia2: false,
707            data_ready: false,
708            fifo_watermark: false,
709            fifo_overrun: false,
710        }
711    }
712
713    /// Returns true if any interrupt is routed to INT1.
714    #[must_use]
715    pub const fn any(&self) -> bool {
716        self.click || self.ia1 || self.ia2 || self.data_ready || self.fifo_watermark || self.fifo_overrun
717    }
718}
719
720/// INT2 pin routing configuration.
721#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
722#[allow(clippy::struct_excessive_bools)]
723pub struct Int2Routing {
724    /// Route click interrupt to INT2.
725    pub click: bool,
726    /// Route IA1 (motion 1) interrupt to INT2.
727    pub ia1: bool,
728    /// Route IA2 (motion 2) interrupt to INT2.
729    pub ia2: bool,
730    /// Route boot interrupt to INT2.
731    pub boot: bool,
732    /// Route activity interrupt to INT2.
733    pub activity: bool,
734}
735
736impl Int2Routing {
737    /// No interrupts routed to INT2.
738    #[must_use]
739    pub const fn none() -> Self {
740        Self {
741            click: false,
742            ia1: false,
743            ia2: false,
744            boot: false,
745            activity: false,
746        }
747    }
748
749    /// Returns true if any interrupt is routed to INT2.
750    #[must_use]
751    pub const fn any(&self) -> bool {
752        self.click || self.ia1 || self.ia2 || self.boot || self.activity
753    }
754}
755
756/// Combined interrupt routing and polarity configuration.
757#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
758pub struct InterruptConfig {
759    /// Interrupt pin polarity.
760    pub polarity: InterruptPolarity,
761    /// INT1 pin routing.
762    pub int1: Int1Routing,
763    /// INT2 pin routing.
764    pub int2: Int2Routing,
765}
766
767impl InterruptConfig {
768    /// Create a configuration with no interrupts routed.
769    #[must_use]
770    pub const fn disabled() -> Self {
771        Self {
772            polarity: InterruptPolarity::ActiveHigh,
773            int1: Int1Routing::none(),
774            int2: Int2Routing::none(),
775        }
776    }
777
778    /// Set interrupt polarity.
779    #[must_use]
780    pub const fn with_polarity(mut self, polarity: InterruptPolarity) -> Self {
781        self.polarity = polarity;
782        self
783    }
784
785    /// Set INT1 routing.
786    #[must_use]
787    pub const fn with_int1(mut self, int1: Int1Routing) -> Self {
788        self.int1 = int1;
789        self
790    }
791
792    /// Set INT2 routing.
793    #[must_use]
794    pub const fn with_int2(mut self, int2: Int2Routing) -> Self {
795        self.int2 = int2;
796        self
797    }
798}
799
800// ============================================================================
801// Interrupt Status Types
802// ============================================================================
803
804/// Motion detection status from `INT1_SRC` or `INT2_SRC` registers.
805#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
806#[allow(clippy::struct_excessive_bools)]
807pub struct MotionStatus {
808    /// Interrupt active flag.
809    pub active: bool,
810    /// X-axis high event detected.
811    pub x_high: bool,
812    /// X-axis low event detected.
813    pub x_low: bool,
814    /// Y-axis high event detected.
815    pub y_high: bool,
816    /// Y-axis low event detected.
817    pub y_low: bool,
818    /// Z-axis high event detected.
819    pub z_high: bool,
820    /// Z-axis low event detected.
821    pub z_low: bool,
822}
823
824impl MotionStatus {
825    /// Returns true if the interrupt is active.
826    #[must_use]
827    pub const fn is_active(&self) -> bool {
828        self.active
829    }
830
831    /// Returns true if any X-axis event was detected.
832    #[must_use]
833    pub const fn x_event(&self) -> bool {
834        self.x_high || self.x_low
835    }
836
837    /// Returns true if any Y-axis event was detected.
838    #[must_use]
839    pub const fn y_event(&self) -> bool {
840        self.y_high || self.y_low
841    }
842
843    /// Returns true if any Z-axis event was detected.
844    #[must_use]
845    pub const fn z_event(&self) -> bool {
846        self.z_high || self.z_low
847    }
848
849    /// Returns true if any axis event was detected.
850    #[must_use]
851    pub const fn any_event(&self) -> bool {
852        self.x_high || self.x_low || self.y_high || self.y_low || self.z_high || self.z_low
853    }
854}
855
856impl From<field_sets::Int1Src> for MotionStatus {
857    fn from(raw: field_sets::Int1Src) -> Self {
858        Self {
859            active: raw.ia(),
860            x_high: raw.xh(),
861            x_low: raw.xl(),
862            y_high: raw.yh(),
863            y_low: raw.yl(),
864            z_high: raw.zh(),
865            z_low: raw.zl(),
866        }
867    }
868}
869
870impl From<field_sets::Int2Src> for MotionStatus {
871    fn from(raw: field_sets::Int2Src) -> Self {
872        Self {
873            active: raw.ia(),
874            x_high: raw.xh(),
875            x_low: raw.xl(),
876            y_high: raw.yh(),
877            y_low: raw.yl(),
878            z_high: raw.zh(),
879            z_low: raw.zl(),
880        }
881    }
882}
883
884/// Click detection status from `CLICK_SRC` register.
885#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
886#[allow(clippy::struct_excessive_bools)]
887pub struct ClickStatus {
888    /// Click interrupt active flag.
889    pub active: bool,
890    /// Double-click detected.
891    pub double_click: bool,
892    /// Single-click detected.
893    pub single_click: bool,
894    /// Click sign (true = negative direction).
895    pub negative: bool,
896    /// X-axis click event.
897    pub x: bool,
898    /// Y-axis click event.
899    pub y: bool,
900    /// Z-axis click event.
901    pub z: bool,
902}
903
904impl ClickStatus {
905    /// Returns true if the click interrupt is active.
906    #[must_use]
907    pub const fn is_active(&self) -> bool {
908        self.active
909    }
910
911    /// Returns true if a double-click was detected.
912    #[must_use]
913    pub const fn is_double_click(&self) -> bool {
914        self.double_click
915    }
916
917    /// Returns true if a single-click was detected.
918    #[must_use]
919    pub const fn is_single_click(&self) -> bool {
920        self.single_click
921    }
922
923    /// Returns true if the click was in the negative direction.
924    #[must_use]
925    pub const fn is_negative(&self) -> bool {
926        self.negative
927    }
928
929    /// Returns true if any click event was detected on any axis.
930    #[must_use]
931    pub const fn any_event(&self) -> bool {
932        self.x || self.y || self.z
933    }
934}
935
936impl From<field_sets::ClickSrc> for ClickStatus {
937    fn from(raw: field_sets::ClickSrc) -> Self {
938        Self {
939            active: raw.ia(),
940            double_click: raw.d_click(),
941            single_click: raw.s_click(),
942            negative: raw.sign(),
943            x: raw.x(),
944            y: raw.y(),
945            z: raw.z(),
946        }
947    }
948}
949
950/// Combined interrupt sources status.
951#[derive(Copy, Clone, Debug, Default)]
952pub struct InterruptSources {
953    /// Motion 1 (IA1) status.
954    pub motion1: MotionStatus,
955    /// Motion 2 (IA2) status.
956    pub motion2: MotionStatus,
957    /// Click detection status.
958    pub click: ClickStatus,
959}
960
961impl InterruptSources {
962    /// Returns true if any interrupt source is active.
963    #[must_use]
964    pub const fn any_active(&self) -> bool {
965        self.motion1.active || self.motion2.active || self.click.active
966    }
967}
968
969/// FIFO configuration options.
970#[derive(Copy, Clone, Debug, Eq, PartialEq)]
971#[must_use]
972pub struct FifoConfig {
973    /// Enable or disable the hardware FIFO.
974    pub enable: bool,
975    /// FIFO operating mode to apply when enabled.
976    pub mode: FifoMode,
977    /// Optional watermark threshold (0–31 frames).
978    pub watermark: Option<u8>,
979}
980
981impl FifoConfig {
982    /// Disabled FIFO configuration helper.
983    #[must_use]
984    pub const fn disabled() -> Self {
985        Self {
986            enable: false,
987            mode: FifoMode::Bypass,
988            watermark: None,
989        }
990    }
991
992    /// Enabled FIFO configuration helper with the given mode.
993    #[must_use]
994    pub const fn enabled(mode: FifoMode) -> Self {
995        Self {
996            enable: true,
997            mode,
998            watermark: None,
999        }
1000    }
1001
1002    /// Attach a watermark level to this configuration.
1003    #[must_use]
1004    pub const fn with_watermark(self, watermark: u8) -> Self {
1005        Self {
1006            watermark: Some(watermark),
1007            ..self
1008        }
1009    }
1010
1011    /// Watermark value written to the device.
1012    #[must_use]
1013    fn effective_threshold(self) -> u8 {
1014        if self.enable {
1015            match self.watermark {
1016                Some(level) if level <= FIFO_WATERMARK_MAX => level,
1017                Some(_) | None => FIFO_WATERMARK_MAX,
1018            }
1019        } else {
1020            0
1021        }
1022    }
1023}
1024
1025/// Sensor operating mode.
1026///
1027/// The LIS2DE12 is an 8-bit-only device: the LPen bit in CTRL_REG1 must always be set to '1'
1028/// per the datasheet ("must be set to '1' for the correct operation of the device"). There is no
1029/// 10-bit normal mode. Both variants behave identically; `Normal` is kept only for API
1030/// compatibility and is deprecated.
1031#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1032pub enum OperatingMode {
1033    /// Deprecated — LIS2DE12 does not support 10-bit mode. Use `LowPower` instead.
1034    #[deprecated(note = "LIS2DE12 is always 8-bit; LPen must always be 1. Use LowPower instead.")]
1035    Normal,
1036    /// 8-bit low-power mode (the only valid mode for LIS2DE12).
1037    LowPower,
1038}
1039
1040impl OperatingMode {
1041    const fn lpen(self) -> bool {
1042        // LPen must always be '1' per datasheet Table 29.
1043        true
1044    }
1045}
1046
1047/// Axis enable mask.
1048#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1049pub struct AxesEnable {
1050    /// Enable X-axis measurements.
1051    pub x: bool,
1052    /// Enable Y-axis measurements.
1053    pub y: bool,
1054    /// Enable Z-axis measurements.
1055    pub z: bool,
1056}
1057
1058impl AxesEnable {
1059    /// Returns `true` if at least one axis is enabled.
1060    #[must_use]
1061    pub const fn any(self) -> bool {
1062        self.x || self.y || self.z
1063    }
1064}
1065
1066impl Default for AxesEnable {
1067    fn default() -> Self {
1068        Self {
1069            x: true,
1070            y: true,
1071            z: true,
1072        }
1073    }
1074}
1075
1076/// High-level configuration applied during initialisation or runtime updates.
1077#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1078pub struct Lis2de12Config {
1079    /// Output data rate.
1080    pub odr: Odr,
1081    /// Operating mode (resolution/power trade-off).
1082    pub mode: OperatingMode,
1083    /// Full-scale range.
1084    pub scale: Fs,
1085    /// Axis enable mask.
1086    pub axes: AxesEnable,
1087    /// Enable block data updates.
1088    pub block_data_update: bool,
1089    /// Enable temperature sensor output.
1090    pub temperature_enable: bool,
1091    /// FIFO configuration.
1092    pub fifo: FifoConfig,
1093}
1094
1095impl Default for Lis2de12Config {
1096    fn default() -> Self {
1097        Self {
1098            odr: Odr::HundredHz,
1099            mode: OperatingMode::LowPower,
1100            scale: Fs::PlusMinus2G,
1101            axes: AxesEnable::default(),
1102            block_data_update: true,
1103            temperature_enable: false,
1104            fifo: FifoConfig::disabled(),
1105        }
1106    }
1107}
1108
1109impl Lis2de12Config {
1110    fn sensitivity_g_per_lsb(self) -> f32 {
1111        // Exact values from datasheet Table 4 (mg/digit → g/digit).
1112        // ±16g does not follow the simple linear pattern; it uses 187.5 mg/digit.
1113        match self.scale {
1114            Fs::PlusMinus2G => 0.0156,
1115            Fs::PlusMinus4G => 0.0312,
1116            Fs::PlusMinus8G => 0.0625,
1117            Fs::PlusMinus16G => 0.1875,
1118        }
1119    }
1120
1121    fn sensitivity_mg_per_lsb(self) -> f32 {
1122        self.sensitivity_g_per_lsb() * 1000.0
1123    }
1124}
1125
1126/// Parsed FIFO status register snapshot.
1127#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1128#[must_use]
1129pub struct FifoStatus {
1130    /// Watermark flag.
1131    pub watermark: bool,
1132    /// Overrun flag.
1133    pub overrun: bool,
1134    /// FIFO empty flag.
1135    pub empty: bool,
1136    /// Number of unread frames (saturated to hardware capacity).
1137    pub frames: u8,
1138}
1139
1140impl FifoStatus {
1141    /// Create a status snapshot from the raw register value.
1142    #[must_use]
1143    pub fn from_raw(raw: field_sets::FifoSrcReg) -> Self {
1144        let raw_frames = raw.fss();
1145        let frames = if raw.empty() {
1146            0
1147        } else if raw.ovrn_fifo() && raw_frames == 0 {
1148            FIFO_CAPACITY
1149        } else {
1150            raw_frames.min(FIFO_CAPACITY)
1151        };
1152
1153        Self {
1154            watermark: raw.wtm(),
1155            overrun: raw.ovrn_fifo(),
1156            empty: raw.empty(),
1157            frames,
1158        }
1159    }
1160
1161    /// Number of unread frames currently buffered.
1162    #[must_use]
1163    pub const fn len(self) -> u8 {
1164        self.frames
1165    }
1166
1167    /// Returns `true` if the FIFO currently holds data.
1168    #[must_use]
1169    pub const fn has_data(self) -> bool {
1170        self.frames > 0
1171    }
1172
1173    /// Remaining capacity before the FIFO becomes full.
1174    #[must_use]
1175    pub const fn remaining_capacity(self) -> u8 {
1176        FIFO_CAPACITY.saturating_sub(self.frames)
1177    }
1178
1179    /// Returns `true` if the configured watermark has been reached.
1180    #[must_use]
1181    pub const fn is_watermark_triggered(self) -> bool {
1182        self.watermark
1183    }
1184
1185    /// Returns `true` if unread data has been overwritten.
1186    #[must_use]
1187    pub const fn is_overrun(self) -> bool {
1188        self.overrun
1189    }
1190
1191    /// Returns `true` if the FIFO is empty.
1192    #[must_use]
1193    pub const fn is_empty(self) -> bool {
1194        self.empty
1195    }
1196}
1197
1198impl From<field_sets::FifoSrcReg> for FifoStatus {
1199    fn from(raw: field_sets::FifoSrcReg) -> Self {
1200        Self::from_raw(raw)
1201    }
1202}
1203
1204fn validate_config_common<E: Debug>(config: &Lis2de12Config) -> Result<(), Error<E>> {
1205    if !config.axes.any() {
1206        return Err(Error::new(ErrorKind::Device));
1207    }
1208    if config.fifo.enable
1209        && let Some(watermark) = config.fifo.watermark
1210        && watermark > FIFO_WATERMARK_MAX
1211    {
1212        return Err(Error::new(ErrorKind::Param));
1213    }
1214    Ok(())
1215}
1216
1217/// Blocking I²C interface wrapper.
1218pub struct DeviceInterface<I2C> {
1219    /// Underlying I²C bus.
1220    pub i2c: I2C,
1221    /// Slave address.
1222    pub address: u8,
1223}
1224
1225/// Asynchronous I²C interface wrapper.
1226#[cfg(feature = "async")]
1227pub struct DeviceInterfaceAsync<I2C> {
1228    /// Underlying async I²C bus.
1229    pub i2c: I2C,
1230    /// Slave address.
1231    pub address: u8,
1232}
1233
1234/// Blocking SPI interface wrapper.
1235pub struct SpiInterface<SPI> {
1236    /// Underlying SPI bus (with CS management).
1237    pub spi: SPI,
1238}
1239
1240/// Asynchronous SPI interface wrapper.
1241#[cfg(feature = "async")]
1242pub struct SpiInterfaceAsync<SPI> {
1243    /// Underlying async SPI bus (with CS management).
1244    pub spi: SPI,
1245}
1246
1247impl<I2C> BufferInterfaceError for DeviceInterface<I2C>
1248where
1249    I2C: hal::i2c::I2c,
1250{
1251    type Error = I2C::Error;
1252}
1253
1254#[cfg(feature = "async")]
1255impl<I2C> BufferInterfaceError for DeviceInterfaceAsync<I2C>
1256where
1257    I2C: AsyncI2c,
1258{
1259    type Error = I2C::Error;
1260}
1261
1262impl<I2C> RegisterInterface for DeviceInterface<I2C>
1263where
1264    I2C: hal::i2c::I2c,
1265{
1266    type Error = I2C::Error;
1267    type AddressType = u8;
1268
1269    fn write_register(&mut self, address: Self::AddressType, _size_bits: u32, data: &[u8]) -> Result<(), Self::Error> {
1270        let mut buf = [0u8; 1 + 8];
1271        buf[0] = address;
1272        let end = 1 + data.len();
1273        buf[1..end].copy_from_slice(data);
1274        self.i2c.write(self.address, &buf[..end])
1275    }
1276
1277    fn read_register(
1278        &mut self,
1279        address: Self::AddressType,
1280        _size_bits: u32,
1281        data: &mut [u8],
1282    ) -> Result<(), Self::Error> {
1283        self.i2c.write_read(self.address, &[address], data)
1284    }
1285}
1286
1287#[cfg(feature = "async")]
1288impl<I2C> AsyncRegisterInterface for DeviceInterfaceAsync<I2C>
1289where
1290    I2C: AsyncI2c,
1291{
1292    type Error = I2C::Error;
1293    type AddressType = u8;
1294
1295    async fn write_register(
1296        &mut self,
1297        address: Self::AddressType,
1298        _size_bits: u32,
1299        data: &[u8],
1300    ) -> Result<(), Self::Error> {
1301        let mut buf = [0u8; 1 + 8];
1302        buf[0] = address;
1303        let end = 1 + data.len();
1304        buf[1..end].copy_from_slice(data);
1305        self.i2c.write(self.address, &buf[..end]).await
1306    }
1307
1308    async fn read_register(
1309        &mut self,
1310        address: Self::AddressType,
1311        _size_bits: u32,
1312        data: &mut [u8],
1313    ) -> Result<(), Self::Error> {
1314        self.i2c.write_read(self.address, &[address], data).await
1315    }
1316}
1317
1318impl<I2C> BufferInterface for DeviceInterface<I2C>
1319where
1320    I2C: hal::i2c::I2c,
1321{
1322    type AddressType = u8;
1323
1324    fn read(
1325        &mut self,
1326        address: Self::AddressType,
1327        buf: &mut [u8],
1328    ) -> Result<usize, <Self as RegisterInterface>::Error> {
1329        self.i2c.write_read(self.address, &[address], buf)?;
1330        Ok(buf.len())
1331    }
1332
1333    fn write(&mut self, address: Self::AddressType, buf: &[u8]) -> Result<usize, <Self as RegisterInterface>::Error> {
1334        let mut data = [0u8; 1 + 32];
1335        data[0] = address;
1336        let end = 1 + buf.len();
1337        data[1..end].copy_from_slice(buf);
1338        self.i2c.write(self.address, &data[..end])?;
1339        Ok(buf.len())
1340    }
1341
1342    fn flush(&mut self, _address: Self::AddressType) -> Result<(), <Self as RegisterInterface>::Error> {
1343        Ok(())
1344    }
1345}
1346
1347#[cfg(feature = "async")]
1348impl<I2C> AsyncBufferInterface for DeviceInterfaceAsync<I2C>
1349where
1350    I2C: AsyncI2c,
1351{
1352    type AddressType = u8;
1353
1354    async fn read(&mut self, address: Self::AddressType, buf: &mut [u8]) -> Result<usize, Self::Error> {
1355        self.i2c.write_read(self.address, &[address], buf).await?;
1356        Ok(buf.len())
1357    }
1358
1359    async fn write(&mut self, address: Self::AddressType, buf: &[u8]) -> Result<usize, Self::Error> {
1360        let mut data = [0u8; 1 + 32];
1361        data[0] = address;
1362        let end = 1 + buf.len();
1363        data[1..end].copy_from_slice(buf);
1364        self.i2c.write(self.address, &data[..end]).await?;
1365        Ok(buf.len())
1366    }
1367
1368    async fn flush(&mut self, _address: Self::AddressType) -> Result<(), Self::Error> {
1369        Ok(())
1370    }
1371}
1372
1373impl<SPI> BufferInterfaceError for SpiInterface<SPI>
1374where
1375    SPI: hal::spi::SpiDevice,
1376{
1377    type Error = SPI::Error;
1378}
1379
1380#[cfg(feature = "async")]
1381impl<SPI> BufferInterfaceError for SpiInterfaceAsync<SPI>
1382where
1383    SPI: hal_async::spi::SpiDevice,
1384{
1385    type Error = SPI::Error;
1386}
1387
1388impl<SPI> RegisterInterface for SpiInterface<SPI>
1389where
1390    SPI: hal::spi::SpiDevice,
1391{
1392    type Error = SPI::Error;
1393    type AddressType = u8;
1394
1395    fn write_register(&mut self, address: Self::AddressType, _size_bits: u32, data: &[u8]) -> Result<(), Self::Error> {
1396        let mut buf = [0u8; 1 + 8];
1397        buf[0] = address; // Write: bit 7 = 0
1398        let end = 1 + data.len();
1399        buf[1..end].copy_from_slice(data);
1400        self.spi.write(&buf[..end])
1401    }
1402
1403    fn read_register(
1404        &mut self,
1405        address: Self::AddressType,
1406        _size_bits: u32,
1407        data: &mut [u8],
1408    ) -> Result<(), Self::Error> {
1409        let addr_byte = 0x80 | address; // Read: bit 7 = 1
1410        self.spi.transaction(&mut [
1411            hal::spi::Operation::Write(&[addr_byte]),
1412            hal::spi::Operation::Read(data),
1413        ])
1414    }
1415}
1416
1417#[cfg(feature = "async")]
1418impl<SPI> AsyncRegisterInterface for SpiInterfaceAsync<SPI>
1419where
1420    SPI: hal_async::spi::SpiDevice,
1421{
1422    type Error = SPI::Error;
1423    type AddressType = u8;
1424
1425    async fn write_register(
1426        &mut self,
1427        address: Self::AddressType,
1428        _size_bits: u32,
1429        data: &[u8],
1430    ) -> Result<(), Self::Error> {
1431        let mut buf = [0u8; 1 + 8];
1432        buf[0] = address; // Write: bit 7 = 0
1433        let end = 1 + data.len();
1434        buf[1..end].copy_from_slice(data);
1435        self.spi.write(&buf[..end]).await
1436    }
1437
1438    async fn read_register(
1439        &mut self,
1440        address: Self::AddressType,
1441        _size_bits: u32,
1442        data: &mut [u8],
1443    ) -> Result<(), Self::Error> {
1444        let addr_byte = 0x80 | address; // Read: bit 7 = 1
1445        self.spi.transaction(&mut [
1446            hal_async::spi::Operation::Write(&[addr_byte]),
1447            hal_async::spi::Operation::Read(data),
1448        ]).await
1449    }
1450}
1451
1452impl<SPI> BufferInterface for SpiInterface<SPI>
1453where
1454    SPI: hal::spi::SpiDevice,
1455{
1456    type AddressType = u8;
1457
1458    fn read(
1459        &mut self,
1460        address: Self::AddressType,
1461        buf: &mut [u8],
1462    ) -> Result<usize, <Self as RegisterInterface>::Error> {
1463        // Multi-byte read: bit 7 = 1 (read), bit 6 = 1 (auto-increment)
1464        let addr_byte = 0xC0 | address;
1465        self.spi.transaction(&mut [
1466            hal::spi::Operation::Write(&[addr_byte]),
1467            hal::spi::Operation::Read(buf),
1468        ])?;
1469        Ok(buf.len())
1470    }
1471
1472    fn write(&mut self, address: Self::AddressType, buf: &[u8]) -> Result<usize, <Self as RegisterInterface>::Error> {
1473        let mut data = [0u8; 1 + 32];
1474        data[0] = 0x40 | address; // Write with auto-increment: bit 6 = 1
1475        let end = 1 + buf.len();
1476        data[1..end].copy_from_slice(buf);
1477        self.spi.write(&data[..end])?;
1478        Ok(buf.len())
1479    }
1480
1481    fn flush(&mut self, _address: Self::AddressType) -> Result<(), <Self as RegisterInterface>::Error> {
1482        Ok(())
1483    }
1484}
1485
1486#[cfg(feature = "async")]
1487impl<SPI> AsyncBufferInterface for SpiInterfaceAsync<SPI>
1488where
1489    SPI: hal_async::spi::SpiDevice,
1490{
1491    type AddressType = u8;
1492
1493    async fn read(&mut self, address: Self::AddressType, buf: &mut [u8]) -> Result<usize, Self::Error> {
1494        // Multi-byte read: bit 7 = 1 (read), bit 6 = 1 (auto-increment)
1495        let addr_byte = 0xC0 | address;
1496        self.spi.transaction(&mut [
1497            hal_async::spi::Operation::Write(&[addr_byte]),
1498            hal_async::spi::Operation::Read(buf),
1499        ]).await?;
1500        Ok(buf.len())
1501    }
1502
1503    async fn write(&mut self, address: Self::AddressType, buf: &[u8]) -> Result<usize, Self::Error> {
1504        let mut data = [0u8; 1 + 32];
1505        data[0] = 0x40 | address; // Write with auto-increment: bit 6 = 1
1506        let end = 1 + buf.len();
1507        data[1..end].copy_from_slice(buf);
1508        self.spi.write(&data[..end]).await?;
1509        Ok(buf.len())
1510    }
1511
1512    async fn flush(&mut self, _address: Self::AddressType) -> Result<(), Self::Error> {
1513        Ok(())
1514    }
1515}
1516
1517/// Available LIS2DE12 I²C slave addresses (controlled by the SA0 pin).
1518///
1519/// These are 7-bit addresses as required by `embedded-hal` I²C traits. The datasheet Table 15
1520/// lists 8-bit SAD+R/W bytes (0x30/0x32 write, 0x31/0x33 read); strip the R/W bit to get the
1521/// 7-bit address used here.
1522pub enum SlaveAddr {
1523    /// SA0 pulled low (7-bit address `0x18`).
1524    Default,
1525    /// SA0 pulled high (7-bit address `0x19`).
1526    Alternative,
1527}
1528
1529impl SlaveAddr {
1530    const fn addr(self) -> u8 {
1531        match self {
1532            SlaveAddr::Default => 0x18,
1533            SlaveAddr::Alternative => 0x19,
1534        }
1535    }
1536}
1537
1538/// Blocking LIS2DE12 driver.
1539pub struct Lis2de12<IFACE> {
1540    device: Lis2de12Device<IFACE>,
1541    config: Lis2de12Config,
1542}
1543
1544/// Type alias for I2C-based blocking driver.
1545pub type Lis2de12I2c<I2C> = Lis2de12<DeviceInterface<I2C>>;
1546
1547/// Type alias for SPI-based blocking driver.
1548pub type Lis2de12Spi<SPI> = Lis2de12<SpiInterface<SPI>>;
1549
1550// Generic implementation for all interface types
1551impl<IFACE> Lis2de12<IFACE>
1552where
1553    IFACE: RegisterInterface<AddressType = u8, Error = <IFACE as BufferInterfaceError>::Error> + BufferInterface<AddressType = u8>,
1554    <IFACE as RegisterInterface>::Error: Debug,
1555{
1556    /// Return the active configuration.
1557    pub const fn config(&self) -> Lis2de12Config {
1558        self.config
1559    }
1560
1561    /// Update the sensor configuration.
1562    pub fn set_config(&mut self, config: Lis2de12Config) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1563        validate_config_common::<<IFACE as RegisterInterface>::Error>(&config)?;
1564        self.apply_config(config)?;
1565        self.config = config;
1566        Ok(())
1567    }
1568
1569    /// Read raw acceleration sample triplet.
1570    pub fn read_raw(&mut self) -> Result<I16x3, Error<<IFACE as RegisterInterface>::Error>> {
1571        let frame = self.read_fifo_frame()?;
1572        Ok(decode_raw(&frame, self.config.mode))
1573    }
1574
1575    /// Read acceleration expressed in milli-g.
1576    pub fn read_mg(&mut self) -> Result<I16x3, Error<<IFACE as RegisterInterface>::Error>> {
1577        let raw = self.read_raw()?;
1578        Ok(scale_to_mg(raw, self.config.sensitivity_mg_per_lsb()))
1579    }
1580
1581    /// Read acceleration expressed in g (floating point).
1582    pub fn read_g(&mut self) -> Result<F32x3, Error<<IFACE as RegisterInterface>::Error>> {
1583        let raw = self.read_raw()?;
1584        Ok(scale_to_g(raw, self.config.sensitivity_g_per_lsb()))
1585    }
1586
1587    /// Read a single FIFO frame (XYZ sample) using blocking I²C transfers.
1588    pub fn read_fifo_frame(&mut self) -> Result<FifoFrame, Error<<IFACE as RegisterInterface>::Error>> {
1589        let mut buf: FifoFrame = [0; FIFO_FRAME_BYTES];
1590        let mut fifo = self.device.fifo_read_start();
1591        let mut offset = 0;
1592        while offset < buf.len() {
1593            let read = fifo.read(&mut buf[offset..]).map_err(Error::from)?;
1594            if read == 0 {
1595                return Err(Error::new(ErrorKind::Device));
1596            }
1597            offset += read;
1598        }
1599        Ok(buf)
1600    }
1601
1602    /// Read multiple FIFO frames into the provided slice, returning the number retrieved.
1603    /// Reading stops early if the FIFO does not currently hold enough data.
1604    pub fn read_fifo_frames(&mut self, frames: &mut [FifoFrame]) -> Result<usize, Error<<IFACE as RegisterInterface>::Error>> {
1605        if frames.is_empty() {
1606            return Ok(0);
1607        }
1608
1609        let status = self.fifo_status()?;
1610        if status.is_empty() {
1611            return Ok(0);
1612        }
1613
1614        let to_read = cmp::min(status.len() as usize, frames.len());
1615        for slot in &mut frames[..to_read] {
1616            *slot = self.read_fifo_frame()?;
1617        }
1618        Ok(to_read)
1619    }
1620
1621    /// Drain all available FIFO frames, discarding their contents.
1622    pub fn drain_fifo(&mut self) -> Result<usize, Error<<IFACE as RegisterInterface>::Error>> {
1623        let mut drained = 0;
1624        loop {
1625            let status = self.fifo_status()?;
1626            if status.is_empty() {
1627                break;
1628            }
1629            self.read_fifo_frame()?;
1630            drained += 1;
1631        }
1632        Ok(drained)
1633    }
1634
1635    /// Read the FIFO source register and return the parsed status snapshot.
1636    pub fn fifo_status(&mut self) -> Result<FifoStatus, Error<<IFACE as RegisterInterface>::Error>> {
1637        let status = self.device.fifo_src_reg().read().map_err(Error::from)?;
1638        Ok(FifoStatus::from(status))
1639    }
1640
1641    /// Enable or disable the on-die temperature sensor.
1642    pub fn set_temperature_sensor(&mut self, enable: bool) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1643        self.write_temperature_sensor(enable)?;
1644        self.config.temperature_enable = enable;
1645        Ok(())
1646    }
1647
1648    /// Read raw temperature sensor data as a 16-bit signed integer.
1649    /// The temperature sensor must be enabled via `set_temperature_sensor` or configuration.
1650    /// Returns the raw left-justified two's complement value.
1651    pub fn read_temperature_raw(&mut self) -> Result<i16, Error<<IFACE as RegisterInterface>::Error>> {
1652        let low = self.device.out_temp_l().read().map_err(Error::from)?.temp_l() as u8;
1653        let high = self.device.out_temp_h().read().map_err(Error::from)?.temp_h() as u8;
1654        Ok(i16::from_le_bytes([low, high]))
1655    }
1656
1657    /// Read temperature change in degrees Celsius.
1658    /// The temperature sensor must be enabled via `set_temperature_sensor` or configuration.
1659    ///
1660    /// Returns the temperature **delta** relative to the device's internal reference point
1661    /// (established at power-on). The sensitivity is 1 digit/°C with 8-bit resolution
1662    /// (left-justified in a 16-bit register). Positive values indicate temperature increase,
1663    /// negative values indicate decrease.
1664    ///
1665    /// # Relative vs. absolute temperature
1666    ///
1667    /// The LIS2DE12 temperature sensor measures **change**, not absolute temperature. ST's
1668    /// reference C driver (`lis2de12_from_lsb_to_celsius`) adds a fixed 25 °C offset to the
1669    /// raw value, implicitly assuming the device powers on at room temperature. That assumption
1670    /// is unreliable in practice: if the device boots at a different ambient temperature the
1671    /// offset will be wrong by exactly that difference.
1672    ///
1673    /// This driver returns the raw delta so callers can apply their own reference. To approximate
1674    /// absolute temperature using the ST convention:
1675    ///
1676    /// ```ignore
1677    /// let abs_celsius = sensor.read_temperature()? + 25.0;
1678    /// ```
1679    ///
1680    /// For accurate absolute readings, record a known ambient temperature at boot and add it
1681    /// instead of the 25 °C constant.
1682    pub fn read_temperature(&mut self) -> Result<f32, Error<<IFACE as RegisterInterface>::Error>> {
1683        let raw = self.read_temperature_raw()?;
1684        // Data is 8-bit resolution, left-justified in 16-bit register
1685        // Sensitivity: 1 digit/°C (see datasheet Table 5)
1686        // Convert from left-justified 16-bit to signed 8-bit value
1687        Ok((raw >> 8) as f32)
1688    }
1689
1690    /// Route HP-filtered data to the output registers and FIFO (FDS bit in CTRL_REG2).
1691    /// When enabled, accelerometer output values reflect the high-pass filtered signal
1692    /// rather than the raw signal.
1693    pub fn set_high_pass_to_outputs(&mut self, enable: bool) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1694        self.device
1695            .ctrl_reg_2()
1696            .modify(|reg: &mut field_sets::CtrlReg2| reg.set_fds(enable))
1697            .map_err(Error::from)
1698    }
1699
1700    /// Return `true` if HP-filtered data is currently routed to the output registers.
1701    pub fn high_pass_to_outputs(&mut self) -> Result<bool, Error<<IFACE as RegisterInterface>::Error>> {
1702        Ok(self.device.ctrl_reg_2().read().map_err(Error::from)?.fds())
1703    }
1704
1705    /// Connect (`true`) or disconnect (`false`) the internal SDO/SA0 pull-up resistor.
1706    /// The pull-up is connected by default. Disconnect it to reduce power draw or to
1707    /// avoid bus conflicts when the pin is driven externally.
1708    pub fn set_sdo_pullup_connected(&mut self, connected: bool) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1709        self.device
1710            .ctrl_reg_0()
1711            .modify(|reg: &mut field_sets::CtrlReg0| reg.set_sdo_pu_disc(!connected))
1712            .map_err(Error::from)
1713    }
1714
1715    /// Return `true` if the internal SDO/SA0 pull-up resistor is connected.
1716    pub fn sdo_pullup_connected(&mut self) -> Result<bool, Error<<IFACE as RegisterInterface>::Error>> {
1717        Ok(!self.device.ctrl_reg_0().read().map_err(Error::from)?.sdo_pu_disc())
1718    }
1719
1720    /// Issue a reboot command to reload memory content.
1721    pub fn reboot(&mut self) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1722        self.device
1723            .ctrl_reg_5()
1724            .write(|reg: &mut field_sets::CtrlReg5| reg.set_boot(true))
1725            .map_err(Error::from)
1726    }
1727
1728    // ========================================================================
1729    // Motion Detection
1730    // ========================================================================
1731
1732    /// Configure motion detection on interrupt generator 1 (IA1).
1733    pub fn set_motion1_config(&mut self, config: MotionConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1734        self.write_motion_config_int1(config)
1735    }
1736
1737    /// Configure motion detection on interrupt generator 2 (IA2).
1738    pub fn set_motion2_config(&mut self, config: MotionConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1739        self.write_motion_config_int2(config)
1740    }
1741
1742    /// Read motion detection status from interrupt generator 1 (IA1).
1743    pub fn motion1_status(&mut self) -> Result<MotionStatus, Error<<IFACE as RegisterInterface>::Error>> {
1744        let raw = self.device.int_1_src().read().map_err(Error::from)?;
1745        Ok(MotionStatus::from(raw))
1746    }
1747
1748    /// Read motion detection status from interrupt generator 2 (IA2).
1749    pub fn motion2_status(&mut self) -> Result<MotionStatus, Error<<IFACE as RegisterInterface>::Error>> {
1750        let raw = self.device.int_2_src().read().map_err(Error::from)?;
1751        Ok(MotionStatus::from(raw))
1752    }
1753
1754    // ========================================================================
1755    // Click Detection
1756    // ========================================================================
1757
1758    /// Configure click detection.
1759    pub fn set_click_config(&mut self, config: ClickConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1760        self.write_click_config(config)
1761    }
1762
1763    /// Read click detection status.
1764    pub fn click_status(&mut self) -> Result<ClickStatus, Error<<IFACE as RegisterInterface>::Error>> {
1765        let raw = self.device.click_src().read().map_err(Error::from)?;
1766        Ok(ClickStatus::from(raw))
1767    }
1768
1769    // ========================================================================
1770    // Activity Detection
1771    // ========================================================================
1772
1773    /// Configure activity/inactivity detection (sleep-to-wake).
1774    pub fn set_activity_config(&mut self, config: ActivityConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1775        self.write_activity_config(config)
1776    }
1777
1778    // ========================================================================
1779    // Interrupt Routing
1780    // ========================================================================
1781
1782    /// Configure interrupt pin routing and polarity.
1783    pub fn set_interrupt_config(&mut self, config: InterruptConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1784        self.write_interrupt_config(config)
1785    }
1786
1787    /// Set interrupt pin polarity.
1788    pub fn set_interrupt_polarity(&mut self, polarity: InterruptPolarity) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1789        self.device
1790            .ctrl_reg_6()
1791            .modify(|reg: &mut field_sets::CtrlReg6| {
1792                reg.set_int_polarity(matches!(polarity, InterruptPolarity::ActiveLow));
1793            })
1794            .map_err(Error::from)
1795    }
1796
1797    /// Read combined interrupt sources status.
1798    ///
1799    /// This reads all interrupt source registers in sequence.
1800    /// For latched interrupts, reading clears the interrupt flag.
1801    pub fn interrupt_sources(&mut self) -> Result<InterruptSources, Error<<IFACE as RegisterInterface>::Error>> {
1802        let motion1 = self.motion1_status()?;
1803        let motion2 = self.motion2_status()?;
1804        let click = self.click_status()?;
1805        Ok(InterruptSources { motion1, motion2, click })
1806    }
1807
1808    fn write_temperature_sensor(&mut self, enable: bool) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1809        self.device
1810            .temp_cfg_reg()
1811            .write(|reg: &mut field_sets::TempCfgReg| {
1812                reg.set_temp_en(if enable { TempEn::TempEn } else { TempEn::TempDis });
1813            })
1814            .map_err(Error::from)
1815    }
1816
1817    fn apply_config(&mut self, config: Lis2de12Config) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1818        self.device
1819            .ctrl_reg_1()
1820            .write(|reg: &mut field_sets::CtrlReg1| {
1821                reg.set_odr(config.odr);
1822                reg.set_lpen(config.mode.lpen());
1823                reg.set_xen(config.axes.x);
1824                reg.set_yen(config.axes.y);
1825                reg.set_zen(config.axes.z);
1826            })
1827            .map_err(Error::from)?;
1828
1829        self.device
1830            .ctrl_reg_4()
1831            .write(|reg: &mut field_sets::CtrlReg4| {
1832                reg.set_bdu(config.block_data_update);
1833                reg.set_fs(config.scale);
1834                reg.set_st(St::Normal);
1835                reg.set_sim(false);
1836            })
1837            .map_err(Error::from)?;
1838
1839        self.device
1840            .ctrl_reg_5()
1841            .write(|reg: &mut field_sets::CtrlReg5| {
1842                reg.set_fifo_en(config.fifo.enable);
1843            })
1844            .map_err(Error::from)?;
1845
1846        self.device
1847            .fifo_ctrl_reg()
1848            .write(|reg: &mut field_sets::FifoCtrlReg| {
1849                reg.set_fm(config.fifo.mode.into());
1850                reg.set_tr(false);
1851                reg.set_fth(config.fifo.effective_threshold());
1852            })
1853            .map_err(Error::from)?;
1854
1855        self.write_temperature_sensor(config.temperature_enable)?;
1856        Ok(())
1857    }
1858
1859    fn write_motion_config_int1(&mut self, config: MotionConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1860        let (aoi, sixd, d4d) = mode_bits(config.mode);
1861
1862        self.device
1863            .int_1_cfg()
1864            .write(|reg: &mut field_sets::Int1Cfg| {
1865                reg.set_aoi(aoi);
1866                reg.set_sixd(sixd);
1867                reg.set_xhie(config.axes.x_high && config.enable);
1868                reg.set_xlie(config.axes.x_low && config.enable);
1869                reg.set_yhie(config.axes.y_high && config.enable);
1870                reg.set_ylie(config.axes.y_low && config.enable);
1871                reg.set_zhie(config.axes.z_high && config.enable);
1872                reg.set_zlie(config.axes.z_low && config.enable);
1873            })
1874            .map_err(Error::from)?;
1875
1876        self.device
1877            .int_1_ths()
1878            .write(|reg: &mut field_sets::Int1Ths| {
1879                reg.set_ths(config.threshold.min(127));
1880            })
1881            .map_err(Error::from)?;
1882
1883        self.device
1884            .int_1_duration()
1885            .write(|reg: &mut field_sets::Int1Duration| {
1886                reg.set_d(config.duration.min(127));
1887            })
1888            .map_err(Error::from)?;
1889
1890        self.device
1891            .ctrl_reg_5()
1892            .modify(|reg: &mut field_sets::CtrlReg5| {
1893                reg.set_lir_int_1(matches!(config.latch, LatchMode::Latched));
1894                reg.set_d_4_d_int_1(d4d);
1895            })
1896            .map_err(Error::from)?;
1897
1898        self.device
1899            .ctrl_reg_2()
1900            .modify(|reg: &mut field_sets::CtrlReg2| {
1901                reg.set_hp_ia_1(config.high_pass_filter);
1902            })
1903            .map_err(Error::from)?;
1904
1905        Ok(())
1906    }
1907
1908    fn write_motion_config_int2(&mut self, config: MotionConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1909        let (aoi, sixd, d4d) = mode_bits(config.mode);
1910
1911        self.device
1912            .int_2_cfg()
1913            .write(|reg: &mut field_sets::Int2Cfg| {
1914                reg.set_aoi(aoi);
1915                reg.set_sixd(sixd);
1916                reg.set_xhie(config.axes.x_high && config.enable);
1917                reg.set_xlie(config.axes.x_low && config.enable);
1918                reg.set_yhie(config.axes.y_high && config.enable);
1919                reg.set_ylie(config.axes.y_low && config.enable);
1920                reg.set_zhie(config.axes.z_high && config.enable);
1921                reg.set_zlie(config.axes.z_low && config.enable);
1922            })
1923            .map_err(Error::from)?;
1924
1925        self.device
1926            .int_2_ths()
1927            .write(|reg: &mut field_sets::Int2Ths| {
1928                reg.set_ths(config.threshold.min(127));
1929            })
1930            .map_err(Error::from)?;
1931
1932        self.device
1933            .int_2_duration()
1934            .write(|reg: &mut field_sets::Int2Duration| {
1935                reg.set_d(config.duration.min(127));
1936            })
1937            .map_err(Error::from)?;
1938
1939        self.device
1940            .ctrl_reg_5()
1941            .modify(|reg: &mut field_sets::CtrlReg5| {
1942                reg.set_lir_int_2(matches!(config.latch, LatchMode::Latched));
1943                reg.set_d_4_d_int_2(d4d);
1944            })
1945            .map_err(Error::from)?;
1946
1947        self.device
1948            .ctrl_reg_2()
1949            .modify(|reg: &mut field_sets::CtrlReg2| {
1950                reg.set_hp_ia_2(config.high_pass_filter);
1951            })
1952            .map_err(Error::from)?;
1953
1954        Ok(())
1955    }
1956
1957    fn write_click_config(&mut self, config: ClickConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
1958        // Configure CLICK_CFG
1959        self.device
1960            .click_cfg()
1961            .write(|reg: &mut field_sets::ClickCfg| {
1962                reg.set_xs(config.axes.x_single && config.enable);
1963                reg.set_xd(config.axes.x_double && config.enable);
1964                reg.set_ys(config.axes.y_single && config.enable);
1965                reg.set_yd(config.axes.y_double && config.enable);
1966                reg.set_zs(config.axes.z_single && config.enable);
1967                reg.set_zd(config.axes.z_double && config.enable);
1968            })
1969            .map_err(Error::from)?;
1970
1971        // Configure threshold and latch
1972        self.device
1973            .click_ths()
1974            .write(|reg: &mut field_sets::ClickThs| {
1975                reg.set_ths(config.threshold.min(127));
1976                reg.set_lir_click(matches!(config.latch, LatchMode::Latched));
1977            })
1978            .map_err(Error::from)?;
1979
1980        // Configure time limit
1981        self.device
1982            .time_limit()
1983            .write(|reg: &mut field_sets::TimeLimit| {
1984                reg.set_tli(config.time_limit.min(127));
1985            })
1986            .map_err(Error::from)?;
1987
1988        // Configure time latency
1989        self.device
1990            .time_latency()
1991            .write(|reg: &mut field_sets::TimeLatency| {
1992                reg.set_tla(config.time_latency);
1993            })
1994            .map_err(Error::from)?;
1995
1996        // Configure time window
1997        self.device
1998            .time_window()
1999            .write(|reg: &mut field_sets::TimeWindow| {
2000                reg.set_tw(config.time_window);
2001            })
2002            .map_err(Error::from)?;
2003
2004        // Configure high-pass filter in CTRL_REG2
2005        self.device
2006            .ctrl_reg_2()
2007            .modify(|reg: &mut field_sets::CtrlReg2| {
2008                reg.set_hpclick(config.high_pass_filter);
2009            })
2010            .map_err(Error::from)?;
2011
2012        Ok(())
2013    }
2014
2015    fn write_activity_config(&mut self, config: ActivityConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
2016        // Configure activation threshold
2017        self.device
2018            .act_ths()
2019            .write(|reg: &mut field_sets::ActThs| {
2020                reg.set_acth(config.threshold.min(127));
2021            })
2022            .map_err(Error::from)?;
2023
2024        // Configure duration
2025        self.device
2026            .act_dur()
2027            .write(|reg: &mut field_sets::ActDur| {
2028                reg.set_act_d(config.duration);
2029            })
2030            .map_err(Error::from)?;
2031
2032        Ok(())
2033    }
2034
2035    fn write_interrupt_config(&mut self, config: InterruptConfig) -> Result<(), Error<<IFACE as RegisterInterface>::Error>> {
2036        // Configure INT1 routing in CTRL_REG3
2037        self.device
2038            .ctrl_reg_3()
2039            .write(|reg: &mut field_sets::CtrlReg3| {
2040                reg.set_i_1_click(config.int1.click);
2041                reg.set_i_1_ia_1(config.int1.ia1);
2042                reg.set_i_1_ia_2(config.int1.ia2);
2043                reg.set_i_1_zyxda(config.int1.data_ready);
2044                reg.set_i_1_wtm(config.int1.fifo_watermark);
2045                reg.set_i_1_overrun(config.int1.fifo_overrun);
2046            })
2047            .map_err(Error::from)?;
2048
2049        // Configure INT2 routing and polarity in CTRL_REG6
2050        self.device
2051            .ctrl_reg_6()
2052            .write(|reg: &mut field_sets::CtrlReg6| {
2053                reg.set_i_2_click(config.int2.click);
2054                reg.set_i_2_ia_1(config.int2.ia1);
2055                reg.set_i_2_ia_2(config.int2.ia2);
2056                reg.set_i_2_boot(config.int2.boot);
2057                reg.set_i_2_act(config.int2.activity);
2058                reg.set_int_polarity(matches!(config.polarity, InterruptPolarity::ActiveLow));
2059            })
2060            .map_err(Error::from)?;
2061
2062        Ok(())
2063    }
2064}
2065
2066// I2C-specific constructors and methods
2067impl<I2C> Lis2de12<DeviceInterface<I2C>>
2068where
2069    I2C: I2c,
2070    I2C::Error: Debug,
2071{
2072    /// Create a new I2C driver with the default configuration.
2073    pub fn new_i2c(i2c: I2C, addr: SlaveAddr) -> Result<Self, Error<I2C::Error>> {
2074        Self::new_i2c_with_config(i2c, addr, Lis2de12Config::default())
2075    }
2076
2077    /// Create a new I2C driver with an explicit configuration.
2078    pub fn new_i2c_with_config(i2c: I2C, addr: SlaveAddr, config: Lis2de12Config) -> Result<Self, Error<I2C::Error>> {
2079        validate_config_common::<I2C::Error>(&config)?;
2080
2081        let interface = DeviceInterface {
2082            i2c,
2083            address: addr.addr(),
2084        };
2085        let mut device = Lis2de12Device::new(interface);
2086        verify_device_id(&mut device)?;
2087        let mut this = Self { device, config };
2088        this.apply_config(config)?;
2089        Ok(this)
2090    }
2091
2092    /// Create a new driver with the default configuration (backward compatible, uses I2C).
2093    pub fn new(i2c: I2C, addr: SlaveAddr) -> Result<Self, Error<I2C::Error>> {
2094        Self::new_i2c(i2c, addr)
2095    }
2096
2097    /// Create a new driver with an explicit configuration (backward compatible, uses I2C).
2098    pub fn new_with_config(i2c: I2C, addr: SlaveAddr, config: Lis2de12Config) -> Result<Self, Error<I2C::Error>> {
2099        Self::new_i2c_with_config(i2c, addr, config)
2100    }
2101
2102    /// Access the generated register API directly.
2103    pub fn device(&mut self) -> &mut Lis2de12Device<DeviceInterface<I2C>> {
2104        &mut self.device
2105    }
2106
2107    /// Consume the driver and return the underlying I²C bus.
2108    pub fn destroy(self) -> I2C {
2109        self.device.interface.i2c
2110    }
2111}
2112
2113// SPI-specific constructors and methods
2114impl<SPI> Lis2de12<SpiInterface<SPI>>
2115where
2116    SPI: hal::spi::SpiDevice,
2117    SPI::Error: Debug,
2118{
2119    /// Create a new SPI driver with the default configuration.
2120    pub fn new_spi(spi: SPI) -> Result<Self, Error<SPI::Error>> {
2121        Self::new_spi_with_config(spi, Lis2de12Config::default())
2122    }
2123
2124    /// Create a new SPI driver with an explicit configuration.
2125    pub fn new_spi_with_config(spi: SPI, config: Lis2de12Config) -> Result<Self, Error<SPI::Error>> {
2126        validate_config_common::<SPI::Error>(&config)?;
2127
2128        let interface = SpiInterface { spi };
2129        let mut device = Lis2de12Device::new(interface);
2130        verify_device_id(&mut device)?;
2131        let mut this = Self { device, config };
2132        this.apply_config(config)?;
2133        Ok(this)
2134    }
2135
2136    /// Access the generated register API directly.
2137    pub fn device(&mut self) -> &mut Lis2de12Device<SpiInterface<SPI>> {
2138        &mut self.device
2139    }
2140
2141    /// Consume the driver and return the underlying SPI bus.
2142    pub fn destroy(self) -> SPI {
2143        self.device.interface.spi
2144    }
2145}
2146
2147impl<IFACE> RawAccelerometer<I16x3> for Lis2de12<IFACE>
2148where
2149    IFACE: RegisterInterface<AddressType = u8, Error = <IFACE as BufferInterfaceError>::Error> + BufferInterface<AddressType = u8>,
2150    <IFACE as RegisterInterface>::Error: Debug,
2151{
2152    type Error = <IFACE as RegisterInterface>::Error;
2153
2154    fn accel_raw(&mut self) -> Result<I16x3, Error<Self::Error>> {
2155        self.read_raw()
2156    }
2157}
2158
2159/// Asynchronous LIS2DE12 driver.
2160#[cfg(feature = "async")]
2161pub struct Lis2de12Async<IFACE> {
2162    device: Lis2de12Device<IFACE>,
2163    config: Lis2de12Config,
2164}
2165
2166/// Type alias for I2C-based async driver.
2167#[cfg(feature = "async")]
2168pub type Lis2de12I2cAsync<I2C> = Lis2de12Async<DeviceInterfaceAsync<I2C>>;
2169
2170/// Type alias for SPI-based async driver.
2171#[cfg(feature = "async")]
2172pub type Lis2de12SpiAsync<SPI> = Lis2de12Async<SpiInterfaceAsync<SPI>>;
2173
2174#[cfg(feature = "async")]
2175// Generic implementation for all async interface types
2176impl<IFACE> Lis2de12Async<IFACE>
2177where
2178    IFACE: AsyncRegisterInterface<AddressType = u8, Error = <IFACE as BufferInterfaceError>::Error> + AsyncBufferInterface<AddressType = u8>,
2179    <IFACE as AsyncRegisterInterface>::Error: Debug,
2180{
2181    /// Return the active configuration.
2182    pub const fn config(&self) -> Lis2de12Config {
2183        self.config
2184    }
2185
2186    /// Update the sensor configuration asynchronously.
2187    pub async fn set_config(&mut self, config: Lis2de12Config) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2188        validate_config_common::<<IFACE as AsyncRegisterInterface>::Error>(&config)?;
2189        self.apply_config(config).await?;
2190        self.config = config;
2191        Ok(())
2192    }
2193
2194    /// Read raw acceleration sample triplet asynchronously.
2195    pub async fn read_raw(&mut self) -> Result<I16x3, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2196        let frame = self.read_fifo_frame().await?;
2197        Ok(decode_raw(&frame, self.config.mode))
2198    }
2199
2200    /// Read acceleration expressed in milli-g asynchronously.
2201    pub async fn read_mg(&mut self) -> Result<I16x3, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2202        let raw = self.read_raw().await?;
2203        Ok(scale_to_mg(raw, self.config.sensitivity_mg_per_lsb()))
2204    }
2205
2206    /// Read acceleration expressed in g asynchronously.
2207    pub async fn read_g(&mut self) -> Result<F32x3, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2208        let raw = self.read_raw().await?;
2209        Ok(scale_to_g(raw, self.config.sensitivity_g_per_lsb()))
2210    }
2211
2212    /// Read a single FIFO frame asynchronously.
2213    pub async fn read_fifo_frame(&mut self) -> Result<FifoFrame, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2214        let mut buf: FifoFrame = [0; FIFO_FRAME_BYTES];
2215        let mut fifo = self.device.fifo_read_start();
2216        let mut offset = 0;
2217        while offset < buf.len() {
2218            let read = fifo.read_async(&mut buf[offset..]).await.map_err(Error::from)?;
2219            if read == 0 {
2220                return Err(Error::new(ErrorKind::Device));
2221            }
2222            offset += read;
2223        }
2224        Ok(buf)
2225    }
2226
2227    /// Read multiple FIFO frames asynchronously, returning the number retrieved.
2228    pub async fn read_fifo_frames(&mut self, frames: &mut [FifoFrame]) -> Result<usize, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2229        if frames.is_empty() {
2230            return Ok(0);
2231        }
2232
2233        let status = self.fifo_status().await?;
2234        if status.is_empty() {
2235            return Ok(0);
2236        }
2237
2238        let to_read = cmp::min(status.len() as usize, frames.len());
2239        for slot in &mut frames[..to_read] {
2240            *slot = self.read_fifo_frame().await?;
2241        }
2242        Ok(to_read)
2243    }
2244
2245    /// Drain all available FIFO frames asynchronously, discarding their contents.
2246    pub async fn drain_fifo(&mut self) -> Result<usize, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2247        let mut drained = 0;
2248        loop {
2249            let status = self.fifo_status().await?;
2250            if status.is_empty() {
2251                break;
2252            }
2253            self.read_fifo_frame().await?;
2254            drained += 1;
2255        }
2256        Ok(drained)
2257    }
2258
2259    /// Read the FIFO source register asynchronously and return the parsed status snapshot.
2260    pub async fn fifo_status(&mut self) -> Result<FifoStatus, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2261        let status = self.device.fifo_src_reg().read_async().await.map_err(Error::from)?;
2262        Ok(FifoStatus::from(status))
2263    }
2264
2265    /// Enable or disable the on-die temperature sensor asynchronously.
2266    pub async fn set_temperature_sensor(&mut self, enable: bool) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2267        self.write_temperature_sensor(enable).await?;
2268        self.config.temperature_enable = enable;
2269        Ok(())
2270    }
2271
2272    /// Read raw temperature sensor data as a 16-bit signed integer asynchronously.
2273    /// The temperature sensor must be enabled via `set_temperature_sensor` or configuration.
2274    /// Returns the raw left-justified two's complement value.
2275    pub async fn read_temperature_raw(&mut self) -> Result<i16, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2276        let low = self.device.out_temp_l().read_async().await.map_err(Error::from)?.temp_l() as u8;
2277        let high = self.device.out_temp_h().read_async().await.map_err(Error::from)?.temp_h() as u8;
2278        Ok(i16::from_le_bytes([low, high]))
2279    }
2280
2281    /// Read temperature change in degrees Celsius asynchronously.
2282    /// The temperature sensor must be enabled via `set_temperature_sensor` or configuration.
2283    ///
2284    /// Returns the temperature **delta** relative to the device's internal reference point
2285    /// (established at power-on). The sensitivity is 1 digit/°C with 8-bit resolution
2286    /// (left-justified in a 16-bit register). Positive values indicate temperature increase,
2287    /// negative values indicate decrease.
2288    ///
2289    /// # Relative vs. absolute temperature
2290    ///
2291    /// The LIS2DE12 temperature sensor measures **change**, not absolute temperature. ST's
2292    /// reference C driver (`lis2de12_from_lsb_to_celsius`) adds a fixed 25 °C offset to the
2293    /// raw value, implicitly assuming the device powers on at room temperature. That assumption
2294    /// is unreliable in practice: if the device boots at a different ambient temperature the
2295    /// offset will be wrong by exactly that difference.
2296    ///
2297    /// This driver returns the raw delta so callers can apply their own reference. To approximate
2298    /// absolute temperature using the ST convention:
2299    ///
2300    /// ```ignore
2301    /// let abs_celsius = sensor.read_temperature().await? + 25.0;
2302    /// ```
2303    ///
2304    /// For accurate absolute readings, record a known ambient temperature at boot and add it
2305    /// instead of the 25 °C constant.
2306    pub async fn read_temperature(&mut self) -> Result<f32, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2307        let raw = self.read_temperature_raw().await?;
2308        // Data is 8-bit resolution, left-justified in 16-bit register
2309        // Sensitivity: 1 digit/°C (see datasheet Table 5)
2310        // Convert from left-justified 16-bit to signed 8-bit value
2311        Ok((raw >> 8) as f32)
2312    }
2313
2314    /// Route HP-filtered data to the output registers and FIFO asynchronously.
2315    pub async fn set_high_pass_to_outputs(&mut self, enable: bool) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2316        self.device
2317            .ctrl_reg_2()
2318            .modify_async(|reg: &mut field_sets::CtrlReg2| reg.set_fds(enable))
2319            .await
2320            .map_err(Error::from)
2321    }
2322
2323    /// Return `true` if HP-filtered data is currently routed to the output registers.
2324    pub async fn high_pass_to_outputs(&mut self) -> Result<bool, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2325        Ok(self.device.ctrl_reg_2().read_async().await.map_err(Error::from)?.fds())
2326    }
2327
2328    /// Connect (`true`) or disconnect (`false`) the internal SDO/SA0 pull-up resistor asynchronously.
2329    pub async fn set_sdo_pullup_connected(&mut self, connected: bool) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2330        self.device
2331            .ctrl_reg_0()
2332            .modify_async(|reg: &mut field_sets::CtrlReg0| reg.set_sdo_pu_disc(!connected))
2333            .await
2334            .map_err(Error::from)
2335    }
2336
2337    /// Return `true` if the internal SDO/SA0 pull-up resistor is connected.
2338    pub async fn sdo_pullup_connected(&mut self) -> Result<bool, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2339        Ok(!self.device.ctrl_reg_0().read_async().await.map_err(Error::from)?.sdo_pu_disc())
2340    }
2341
2342    /// Issue a reboot command asynchronously.
2343    pub async fn reboot(&mut self) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2344        self.device
2345            .ctrl_reg_5()
2346            .write_async(|reg: &mut field_sets::CtrlReg5| reg.set_boot(true))
2347            .await
2348            .map_err(Error::from)
2349    }
2350
2351    // ========================================================================
2352    // Motion Detection (Async)
2353    // ========================================================================
2354
2355    /// Configure motion detection on interrupt generator 1 (IA1) asynchronously.
2356    pub async fn set_motion1_config(&mut self, config: MotionConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2357        self.write_motion_config_int1(config).await
2358    }
2359
2360    /// Configure motion detection on interrupt generator 2 (IA2) asynchronously.
2361    pub async fn set_motion2_config(&mut self, config: MotionConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2362        self.write_motion_config_int2(config).await
2363    }
2364
2365    /// Read motion detection status from interrupt generator 1 (IA1) asynchronously.
2366    pub async fn motion1_status(&mut self) -> Result<MotionStatus, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2367        let raw = self.device.int_1_src().read_async().await.map_err(Error::from)?;
2368        Ok(MotionStatus::from(raw))
2369    }
2370
2371    /// Read motion detection status from interrupt generator 2 (IA2) asynchronously.
2372    pub async fn motion2_status(&mut self) -> Result<MotionStatus, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2373        let raw = self.device.int_2_src().read_async().await.map_err(Error::from)?;
2374        Ok(MotionStatus::from(raw))
2375    }
2376
2377    // ========================================================================
2378    // Click Detection (Async)
2379    // ========================================================================
2380
2381    /// Configure click detection asynchronously.
2382    pub async fn set_click_config(&mut self, config: ClickConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2383        self.write_click_config(config).await
2384    }
2385
2386    /// Read click detection status asynchronously.
2387    pub async fn click_status(&mut self) -> Result<ClickStatus, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2388        let raw = self.device.click_src().read_async().await.map_err(Error::from)?;
2389        Ok(ClickStatus::from(raw))
2390    }
2391
2392    // ========================================================================
2393    // Activity Detection (Async)
2394    // ========================================================================
2395
2396    /// Configure activity/inactivity detection (sleep-to-wake) asynchronously.
2397    pub async fn set_activity_config(&mut self, config: ActivityConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2398        self.write_activity_config(config).await
2399    }
2400
2401    // ========================================================================
2402    // Interrupt Routing (Async)
2403    // ========================================================================
2404
2405    /// Configure interrupt pin routing and polarity asynchronously.
2406    pub async fn set_interrupt_config(&mut self, config: InterruptConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2407        self.write_interrupt_config(config).await
2408    }
2409
2410    /// Set interrupt pin polarity asynchronously.
2411    pub async fn set_interrupt_polarity(&mut self, polarity: InterruptPolarity) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2412        self.device
2413            .ctrl_reg_6()
2414            .modify_async(|reg: &mut field_sets::CtrlReg6| {
2415                reg.set_int_polarity(matches!(polarity, InterruptPolarity::ActiveLow));
2416            })
2417            .await
2418            .map_err(Error::from)
2419    }
2420
2421    /// Read combined interrupt sources status asynchronously.
2422    ///
2423    /// This reads all interrupt source registers in sequence.
2424    /// For latched interrupts, reading clears the interrupt flag.
2425    pub async fn interrupt_sources(&mut self) -> Result<InterruptSources, Error<<IFACE as AsyncRegisterInterface>::Error>> {
2426        let motion1 = self.motion1_status().await?;
2427        let motion2 = self.motion2_status().await?;
2428        let click = self.click_status().await?;
2429        Ok(InterruptSources { motion1, motion2, click })
2430    }
2431
2432    async fn write_temperature_sensor(&mut self, enable: bool) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2433        self.device
2434            .temp_cfg_reg()
2435            .write_async(|reg: &mut field_sets::TempCfgReg| {
2436                reg.set_temp_en(if enable { TempEn::TempEn } else { TempEn::TempDis });
2437            })
2438            .await
2439            .map_err(Error::from)
2440    }
2441
2442    async fn apply_config(&mut self, config: Lis2de12Config) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2443        self.device
2444            .ctrl_reg_1()
2445            .write_async(|reg: &mut field_sets::CtrlReg1| {
2446                reg.set_odr(config.odr);
2447                reg.set_lpen(config.mode.lpen());
2448                reg.set_xen(config.axes.x);
2449                reg.set_yen(config.axes.y);
2450                reg.set_zen(config.axes.z);
2451            })
2452            .await
2453            .map_err(Error::from)?;
2454
2455        self.device
2456            .ctrl_reg_4()
2457            .write_async(|reg: &mut field_sets::CtrlReg4| {
2458                reg.set_bdu(config.block_data_update);
2459                reg.set_fs(config.scale);
2460                reg.set_st(St::Normal);
2461                reg.set_sim(false);
2462            })
2463            .await
2464            .map_err(Error::from)?;
2465
2466        self.device
2467            .ctrl_reg_5()
2468            .write_async(|reg: &mut field_sets::CtrlReg5| {
2469                reg.set_fifo_en(config.fifo.enable);
2470            })
2471            .await
2472            .map_err(Error::from)?;
2473
2474        self.device
2475            .fifo_ctrl_reg()
2476            .write_async(|reg: &mut field_sets::FifoCtrlReg| {
2477                reg.set_fm(config.fifo.mode.into());
2478                reg.set_tr(false);
2479                reg.set_fth(config.fifo.effective_threshold());
2480            })
2481            .await
2482            .map_err(Error::from)?;
2483
2484        self.write_temperature_sensor(config.temperature_enable).await?;
2485        Ok(())
2486    }
2487
2488    async fn write_motion_config_int1(&mut self, config: MotionConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2489        let (aoi, sixd, d4d) = mode_bits(config.mode);
2490
2491        self.device
2492            .int_1_cfg()
2493            .write_async(|reg: &mut field_sets::Int1Cfg| {
2494                reg.set_aoi(aoi);
2495                reg.set_sixd(sixd);
2496                reg.set_xhie(config.axes.x_high && config.enable);
2497                reg.set_xlie(config.axes.x_low && config.enable);
2498                reg.set_yhie(config.axes.y_high && config.enable);
2499                reg.set_ylie(config.axes.y_low && config.enable);
2500                reg.set_zhie(config.axes.z_high && config.enable);
2501                reg.set_zlie(config.axes.z_low && config.enable);
2502            })
2503            .await
2504            .map_err(Error::from)?;
2505
2506        self.device
2507            .int_1_ths()
2508            .write_async(|reg: &mut field_sets::Int1Ths| {
2509                reg.set_ths(config.threshold.min(127));
2510            })
2511            .await
2512            .map_err(Error::from)?;
2513
2514        self.device
2515            .int_1_duration()
2516            .write_async(|reg: &mut field_sets::Int1Duration| {
2517                reg.set_d(config.duration.min(127));
2518            })
2519            .await
2520            .map_err(Error::from)?;
2521
2522        self.device
2523            .ctrl_reg_5()
2524            .modify_async(|reg: &mut field_sets::CtrlReg5| {
2525                reg.set_lir_int_1(matches!(config.latch, LatchMode::Latched));
2526                reg.set_d_4_d_int_1(d4d);
2527            })
2528            .await
2529            .map_err(Error::from)?;
2530
2531        self.device
2532            .ctrl_reg_2()
2533            .modify_async(|reg: &mut field_sets::CtrlReg2| {
2534                reg.set_hp_ia_1(config.high_pass_filter);
2535            })
2536            .await
2537            .map_err(Error::from)?;
2538
2539        Ok(())
2540    }
2541
2542    async fn write_motion_config_int2(&mut self, config: MotionConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2543        let (aoi, sixd, d4d) = mode_bits(config.mode);
2544
2545        self.device
2546            .int_2_cfg()
2547            .write_async(|reg: &mut field_sets::Int2Cfg| {
2548                reg.set_aoi(aoi);
2549                reg.set_sixd(sixd);
2550                reg.set_xhie(config.axes.x_high && config.enable);
2551                reg.set_xlie(config.axes.x_low && config.enable);
2552                reg.set_yhie(config.axes.y_high && config.enable);
2553                reg.set_ylie(config.axes.y_low && config.enable);
2554                reg.set_zhie(config.axes.z_high && config.enable);
2555                reg.set_zlie(config.axes.z_low && config.enable);
2556            })
2557            .await
2558            .map_err(Error::from)?;
2559
2560        // Configure threshold
2561        self.device
2562            .int_2_ths()
2563            .write_async(|reg: &mut field_sets::Int2Ths| {
2564                reg.set_ths(config.threshold.min(127));
2565            })
2566            .await
2567            .map_err(Error::from)?;
2568
2569        // Configure duration
2570        self.device
2571            .int_2_duration()
2572            .write_async(|reg: &mut field_sets::Int2Duration| {
2573                reg.set_d(config.duration.min(127));
2574            })
2575            .await
2576            .map_err(Error::from)?;
2577
2578        self.device
2579            .ctrl_reg_5()
2580            .modify_async(|reg: &mut field_sets::CtrlReg5| {
2581                reg.set_lir_int_2(matches!(config.latch, LatchMode::Latched));
2582                reg.set_d_4_d_int_2(d4d);
2583            })
2584            .await
2585            .map_err(Error::from)?;
2586
2587        self.device
2588            .ctrl_reg_2()
2589            .modify_async(|reg: &mut field_sets::CtrlReg2| {
2590                reg.set_hp_ia_2(config.high_pass_filter);
2591            })
2592            .await
2593            .map_err(Error::from)?;
2594
2595        Ok(())
2596    }
2597
2598    async fn write_click_config(&mut self, config: ClickConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2599        // Configure CLICK_CFG
2600        self.device
2601            .click_cfg()
2602            .write_async(|reg: &mut field_sets::ClickCfg| {
2603                reg.set_xs(config.axes.x_single && config.enable);
2604                reg.set_xd(config.axes.x_double && config.enable);
2605                reg.set_ys(config.axes.y_single && config.enable);
2606                reg.set_yd(config.axes.y_double && config.enable);
2607                reg.set_zs(config.axes.z_single && config.enable);
2608                reg.set_zd(config.axes.z_double && config.enable);
2609            })
2610            .await
2611            .map_err(Error::from)?;
2612
2613        // Configure threshold and latch
2614        self.device
2615            .click_ths()
2616            .write_async(|reg: &mut field_sets::ClickThs| {
2617                reg.set_ths(config.threshold.min(127));
2618                reg.set_lir_click(matches!(config.latch, LatchMode::Latched));
2619            })
2620            .await
2621            .map_err(Error::from)?;
2622
2623        // Configure time limit
2624        self.device
2625            .time_limit()
2626            .write_async(|reg: &mut field_sets::TimeLimit| {
2627                reg.set_tli(config.time_limit.min(127));
2628            })
2629            .await
2630            .map_err(Error::from)?;
2631
2632        // Configure time latency
2633        self.device
2634            .time_latency()
2635            .write_async(|reg: &mut field_sets::TimeLatency| {
2636                reg.set_tla(config.time_latency);
2637            })
2638            .await
2639            .map_err(Error::from)?;
2640
2641        // Configure time window
2642        self.device
2643            .time_window()
2644            .write_async(|reg: &mut field_sets::TimeWindow| {
2645                reg.set_tw(config.time_window);
2646            })
2647            .await
2648            .map_err(Error::from)?;
2649
2650        // Configure high-pass filter in CTRL_REG2
2651        self.device
2652            .ctrl_reg_2()
2653            .modify_async(|reg: &mut field_sets::CtrlReg2| {
2654                reg.set_hpclick(config.high_pass_filter);
2655            })
2656            .await
2657            .map_err(Error::from)?;
2658
2659        Ok(())
2660    }
2661
2662    async fn write_activity_config(&mut self, config: ActivityConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2663        // Configure activation threshold
2664        self.device
2665            .act_ths()
2666            .write_async(|reg: &mut field_sets::ActThs| {
2667                reg.set_acth(config.threshold.min(127));
2668            })
2669            .await
2670            .map_err(Error::from)?;
2671
2672        // Configure duration
2673        self.device
2674            .act_dur()
2675            .write_async(|reg: &mut field_sets::ActDur| {
2676                reg.set_act_d(config.duration);
2677            })
2678            .await
2679            .map_err(Error::from)?;
2680
2681        Ok(())
2682    }
2683
2684    async fn write_interrupt_config(&mut self, config: InterruptConfig) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>> {
2685        // Configure INT1 routing in CTRL_REG3
2686        self.device
2687            .ctrl_reg_3()
2688            .write_async(|reg: &mut field_sets::CtrlReg3| {
2689                reg.set_i_1_click(config.int1.click);
2690                reg.set_i_1_ia_1(config.int1.ia1);
2691                reg.set_i_1_ia_2(config.int1.ia2);
2692                reg.set_i_1_zyxda(config.int1.data_ready);
2693                reg.set_i_1_wtm(config.int1.fifo_watermark);
2694                reg.set_i_1_overrun(config.int1.fifo_overrun);
2695            })
2696            .await
2697            .map_err(Error::from)?;
2698
2699        // Configure INT2 routing and polarity in CTRL_REG6
2700        self.device
2701            .ctrl_reg_6()
2702            .write_async(|reg: &mut field_sets::CtrlReg6| {
2703                reg.set_i_2_click(config.int2.click);
2704                reg.set_i_2_ia_1(config.int2.ia1);
2705                reg.set_i_2_ia_2(config.int2.ia2);
2706                reg.set_i_2_boot(config.int2.boot);
2707                reg.set_i_2_act(config.int2.activity);
2708                reg.set_int_polarity(matches!(config.polarity, InterruptPolarity::ActiveLow));
2709            })
2710            .await
2711            .map_err(Error::from)?;
2712
2713        Ok(())
2714    }
2715}
2716
2717// I2C-specific async constructors and methods
2718#[cfg(feature = "async")]
2719impl<I2C> Lis2de12Async<DeviceInterfaceAsync<I2C>>
2720where
2721    I2C: AsyncI2c,
2722    I2C::Error: Debug,
2723{
2724    /// Create a new async I2C driver with the default configuration.
2725    pub async fn new_i2c(i2c: I2C, addr: SlaveAddr) -> Result<Self, Error<I2C::Error>> {
2726        Self::new_i2c_with_config(i2c, addr, Lis2de12Config::default()).await
2727    }
2728
2729    /// Create a new async I2C driver with an explicit configuration.
2730    pub async fn new_i2c_with_config(i2c: I2C, addr: SlaveAddr, config: Lis2de12Config) -> Result<Self, Error<I2C::Error>> {
2731        validate_config_common::<I2C::Error>(&config)?;
2732
2733        let interface = DeviceInterfaceAsync {
2734            i2c,
2735            address: addr.addr(),
2736        };
2737        let mut device = Lis2de12Device::new(interface);
2738        verify_device_id_async(&mut device).await?;
2739        let mut this = Self { device, config };
2740        this.apply_config(config).await?;
2741        Ok(this)
2742    }
2743
2744    /// Create a new async I2C driver with the default configuration (backward compatible).
2745    pub async fn new(i2c: I2C, addr: SlaveAddr) -> Result<Self, Error<I2C::Error>> {
2746        Self::new_i2c(i2c, addr).await
2747    }
2748
2749    /// Create a new driver with an explicit configuration (backward compatible, uses I2C).
2750    pub async fn new_with_config(i2c: I2C, addr: SlaveAddr, config: Lis2de12Config) -> Result<Self, Error<I2C::Error>> {
2751        Self::new_i2c_with_config(i2c, addr, config).await
2752    }
2753
2754    /// Access the generated register API directly.
2755    pub fn device(&mut self) -> &mut Lis2de12Device<DeviceInterfaceAsync<I2C>> {
2756        &mut self.device
2757    }
2758
2759    /// Consume the driver and return the underlying asynchronous I²C bus.
2760    pub fn destroy(self) -> I2C {
2761        self.device.interface.i2c
2762    }
2763}
2764
2765// Async SPI-specific constructors and methods
2766#[cfg(feature = "async")]
2767impl<SPI> Lis2de12Async<SpiInterfaceAsync<SPI>>
2768where
2769    SPI: AsyncSpiDevice,
2770    SPI::Error: Debug,
2771{
2772    /// Create a new asynchronous SPI driver with the default configuration.
2773    pub async fn new_spi(spi: SPI) -> Result<Self, Error<SPI::Error>> {
2774        Self::new_spi_with_config(spi, Lis2de12Config::default()).await
2775    }
2776
2777    /// Create a new asynchronous SPI driver with an explicit configuration.
2778    pub async fn new_spi_with_config(spi: SPI, config: Lis2de12Config) -> Result<Self, Error<SPI::Error>> {
2779        validate_config_common::<SPI::Error>(&config)?;
2780
2781        let interface = SpiInterfaceAsync { spi };
2782        let mut device = Lis2de12Device::new(interface);
2783        verify_device_id_async(&mut device).await?;
2784        let mut this = Self { device, config };
2785        this.apply_config(config).await?;
2786        Ok(this)
2787    }
2788
2789    /// Access the generated register API directly.
2790    pub fn device(&mut self) -> &mut Lis2de12Device<SpiInterfaceAsync<SPI>> {
2791        &mut self.device
2792    }
2793
2794    /// Consume the driver and return the underlying async SPI bus.
2795    pub fn destroy(self) -> SPI {
2796        self.device.interface.spi
2797    }
2798}
2799
2800fn verify_device_id<IFACE>(device: &mut Lis2de12Device<IFACE>) -> Result<(), Error<<IFACE as RegisterInterface>::Error>>
2801where
2802    IFACE: RegisterInterface<AddressType = u8>,
2803    <IFACE as RegisterInterface>::Error: Debug,
2804{
2805    let who = device.who_am_i().read().map_err(Error::from)?;
2806    if who != field_sets::WhoAmI::new() {
2807        return Err(Error::new(ErrorKind::Device));
2808    }
2809    Ok(())
2810}
2811
2812#[cfg(feature = "async")]
2813async fn verify_device_id_async<IFACE>(
2814    device: &mut Lis2de12Device<IFACE>,
2815) -> Result<(), Error<<IFACE as AsyncRegisterInterface>::Error>>
2816where
2817    IFACE: AsyncRegisterInterface<AddressType = u8>,
2818    <IFACE as AsyncRegisterInterface>::Error: Debug,
2819{
2820    let who = device.who_am_i().read_async().await.map_err(Error::from)?;
2821    if who != field_sets::WhoAmI::new() {
2822        return Err(Error::new(ErrorKind::Device));
2823    }
2824    Ok(())
2825}
2826
2827fn decode_raw(bytes: &FifoFrame, _mode: OperatingMode) -> I16x3 {
2828    // FIFO frame layout: [FIFO_READ_START(0x00), OUT_X_H, reserved, OUT_Y_H, reserved, OUT_Z_H].
2829    // The LIS2DE12 is always 8-bit; signed axis data sits in bytes[1], [3], [5].
2830    // Construct i16 from [0, H_byte] then arithmetic-right-shift by 8 to sign-extend.
2831    let x = i16::from_le_bytes([bytes[0], bytes[1]]);
2832    let y = i16::from_le_bytes([bytes[2], bytes[3]]);
2833    let z = i16::from_le_bytes([bytes[4], bytes[5]]);
2834    I16x3::new(x >> 8, y >> 8, z >> 8)
2835}
2836
2837fn scale_to_mg(raw: I16x3, mg_per_lsb: f32) -> I16x3 {
2838    fn round_to_i16(value: f32) -> i16 {
2839        if value >= 0.0 {
2840            (value + 0.5) as i16
2841        } else {
2842            (value - 0.5) as i16
2843        }
2844    }
2845
2846    I16x3::new(
2847        round_to_i16(raw.x as f32 * mg_per_lsb),
2848        round_to_i16(raw.y as f32 * mg_per_lsb),
2849        round_to_i16(raw.z as f32 * mg_per_lsb),
2850    )
2851}
2852
2853fn scale_to_g(raw: I16x3, g_per_lsb: f32) -> F32x3 {
2854    F32x3::new(
2855        raw.x as f32 * g_per_lsb,
2856        raw.y as f32 * g_per_lsb,
2857        raw.z as f32 * g_per_lsb,
2858    )
2859}
2860
2861#[cfg(test)]
2862mod tests {
2863    use core::f32::EPSILON;
2864
2865    use super::*;
2866
2867    #[test]
2868    fn validate_config_requires_enabled_axis() {
2869        let mut config = Lis2de12Config::default();
2870        config.axes = AxesEnable {
2871            x: false,
2872            y: false,
2873            z: false,
2874        };
2875
2876        let err = validate_config_common::<()>(&config).unwrap_err();
2877        assert_eq!(err.kind(), ErrorKind::Device);
2878    }
2879
2880    #[test]
2881    fn validate_config_enforces_watermark_limit() {
2882        let mut config = Lis2de12Config::default();
2883        config.fifo = FifoConfig::enabled(FifoMode::Fifo).with_watermark(FIFO_WATERMARK_MAX + 1);
2884
2885        let err = validate_config_common::<()>(&config).unwrap_err();
2886        assert_eq!(err.kind(), ErrorKind::Param);
2887    }
2888
2889    #[test]
2890    fn validate_config_accepts_valid_fifo_settings() {
2891        let mut config = Lis2de12Config::default();
2892        config.fifo = FifoConfig::enabled(FifoMode::Stream).with_watermark(FIFO_WATERMARK_MAX);
2893
2894        assert!(validate_config_common::<()>(&config).is_ok());
2895    }
2896
2897    #[test]
2898    fn fifo_status_parses_flags_and_length() {
2899        let mut raw = field_sets::FifoSrcReg::new();
2900        raw.set_wtm(true);
2901        raw.set_ovrn_fifo(false);
2902        raw.set_empty(false);
2903        raw.set_fss(5);
2904
2905        let status = FifoStatus::from_raw(raw);
2906        assert!(status.is_watermark_triggered());
2907        assert!(!status.is_overrun());
2908        assert!(!status.is_empty());
2909        assert_eq!(status.len(), 5);
2910        assert_eq!(status.remaining_capacity(), (FIFO_CAPACITY as u8).saturating_sub(5));
2911    }
2912
2913    #[test]
2914    fn fifo_status_reports_full_when_overrun() {
2915        let mut raw = field_sets::FifoSrcReg::new();
2916        raw.set_wtm(true);
2917        raw.set_ovrn_fifo(true);
2918        raw.set_empty(false);
2919        raw.set_fss(0);
2920
2921        let status = FifoStatus::from_raw(raw);
2922        assert!(status.is_overrun());
2923        assert_eq!(status.len(), FIFO_CAPACITY as u8);
2924    }
2925
2926    #[test]
2927    fn decode_raw_extracts_h_byte_with_sign_extension() {
2928        // FIFO frame: [pad, OUT_X_H, pad, OUT_Y_H, pad, OUT_Z_H]
2929        // Positive values: H-byte in odd positions, even positions are placeholders.
2930        let frame: FifoFrame = [0x00, 0x01, 0x00, 0x02, 0x00, 0x03];
2931        let decoded = decode_raw(&frame, OperatingMode::LowPower);
2932        assert_eq!(decoded.x, 1);
2933        assert_eq!(decoded.y, 2);
2934        assert_eq!(decoded.z, 3);
2935
2936        // Negative value: 0xFF in the H-byte position should decode to -1.
2937        let frame_neg: FifoFrame = [0x00, 0xFF, 0x00, 0xFE, 0x00, 0x80];
2938        let decoded_neg = decode_raw(&frame_neg, OperatingMode::LowPower);
2939        assert_eq!(decoded_neg.x, -1);
2940        assert_eq!(decoded_neg.y, -2);
2941        assert_eq!(decoded_neg.z, -128);
2942    }
2943
2944    #[test]
2945    fn scale_to_mg_rounds_toward_nearest() {
2946        let raw = I16x3::new(1, -1, 2);
2947        let scaled = scale_to_mg(raw, 1.5);
2948
2949        assert_eq!(scaled.x, 2);
2950        assert_eq!(scaled.y, -2);
2951        assert_eq!(scaled.z, 3);
2952    }
2953
2954    #[test]
2955    fn scale_to_g_scales_components() {
2956        let raw = I16x3::new(2, -4, 0);
2957        let scaled = scale_to_g(raw, 0.5);
2958
2959        assert!((scaled.x - 1.0).abs() <= EPSILON);
2960        assert!((scaled.y + 2.0).abs() <= EPSILON);
2961        assert!(scaled.z.abs() <= EPSILON);
2962    }
2963
2964    #[test]
2965    fn fifo_config_effective_threshold_zero_when_disabled() {
2966        let config = FifoConfig {
2967            enable: false,
2968            mode: FifoMode::Bypass,
2969            watermark: Some(12),
2970        };
2971
2972        assert_eq!(config.effective_threshold(), 0);
2973    }
2974
2975    #[test]
2976    fn fifo_config_effective_threshold_clamps_to_max() {
2977        let config = FifoConfig {
2978            enable: true,
2979            mode: FifoMode::Stream,
2980            watermark: Some(40),
2981        };
2982
2983        assert_eq!(config.effective_threshold(), FIFO_WATERMARK_MAX);
2984    }
2985
2986    #[test]
2987    fn fifo_status_reports_empty_flag() {
2988        let mut raw = field_sets::FifoSrcReg::new();
2989        raw.set_wtm(false);
2990        raw.set_ovrn_fifo(false);
2991        raw.set_empty(true);
2992        raw.set_fss(7);
2993
2994        let status = FifoStatus::from_raw(raw);
2995        assert!(status.is_empty());
2996        assert_eq!(status.len(), 0);
2997        assert_eq!(status.remaining_capacity(), FIFO_CAPACITY as u8);
2998    }
2999
3000    // ========================================================================
3001    // Interrupt Configuration Tests
3002    // ========================================================================
3003
3004    #[test]
3005    fn motion_axes_config_all_high_enables_only_high() {
3006        let axes = MotionAxesConfig::all_high();
3007        assert!(axes.x_high);
3008        assert!(!axes.x_low);
3009        assert!(axes.y_high);
3010        assert!(!axes.y_low);
3011        assert!(axes.z_high);
3012        assert!(!axes.z_low);
3013        assert!(axes.any());
3014    }
3015
3016    #[test]
3017    fn motion_axes_config_none_disables_all() {
3018        let axes = MotionAxesConfig::none();
3019        assert!(!axes.x_high);
3020        assert!(!axes.x_low);
3021        assert!(!axes.y_high);
3022        assert!(!axes.y_low);
3023        assert!(!axes.z_high);
3024        assert!(!axes.z_low);
3025        assert!(!axes.any());
3026    }
3027
3028    #[test]
3029    fn motion_config_builder_methods() {
3030        let config = MotionConfig::disabled()
3031            .with_enable(true)
3032            .with_threshold(200) // Should be clamped to 127
3033            .with_duration(64)
3034            .with_latch(LatchMode::Latched)
3035            .with_mode(MotionDetectionMode::AndCombination)
3036            .with_axes(MotionAxesConfig::all_high())
3037            .with_high_pass_filter(true);
3038
3039        assert!(config.enable);
3040        assert_eq!(config.threshold, 127); // Clamped
3041        assert_eq!(config.duration, 64);
3042        assert!(matches!(config.latch, LatchMode::Latched));
3043        assert!(matches!(config.mode, MotionDetectionMode::AndCombination));
3044        assert!(config.axes.x_high);
3045        assert!(config.high_pass_filter);
3046    }
3047
3048    #[test]
3049    fn click_axes_config_all_single_enables_only_single() {
3050        let axes = ClickAxesConfig::all_single();
3051        assert!(axes.x_single);
3052        assert!(!axes.x_double);
3053        assert!(axes.y_single);
3054        assert!(!axes.y_double);
3055        assert!(axes.z_single);
3056        assert!(!axes.z_double);
3057        assert!(axes.any());
3058    }
3059
3060    #[test]
3061    fn click_config_builder_methods() {
3062        let config = ClickConfig::disabled()
3063            .with_enable(true)
3064            .with_threshold(50)
3065            .with_time_limit(10)
3066            .with_time_latency(20)
3067            .with_time_window(100)
3068            .with_latch(LatchMode::Latched)
3069            .with_axes(ClickAxesConfig::all_double());
3070
3071        assert!(config.enable);
3072        assert_eq!(config.threshold, 50);
3073        assert_eq!(config.time_limit, 10);
3074        assert_eq!(config.time_latency, 20);
3075        assert_eq!(config.time_window, 100);
3076        assert!(matches!(config.latch, LatchMode::Latched));
3077        assert!(config.axes.x_double);
3078    }
3079
3080    #[test]
3081    fn activity_config_threshold_clamps() {
3082        let config = ActivityConfig::disabled()
3083            .with_threshold(255); // Should be clamped to 127
3084
3085        assert_eq!(config.threshold, 127);
3086    }
3087
3088    #[test]
3089    fn int1_routing_none_disables_all() {
3090        let routing = Int1Routing::none();
3091        assert!(!routing.click);
3092        assert!(!routing.ia1);
3093        assert!(!routing.ia2);
3094        assert!(!routing.data_ready);
3095        assert!(!routing.fifo_watermark);
3096        assert!(!routing.fifo_overrun);
3097        assert!(!routing.any());
3098    }
3099
3100    #[test]
3101    fn int2_routing_any_detects_enabled() {
3102        let mut routing = Int2Routing::none();
3103        assert!(!routing.any());
3104        routing.activity = true;
3105        assert!(routing.any());
3106    }
3107
3108    #[test]
3109    fn motion_status_event_detection() {
3110        let status = MotionStatus {
3111            active: true,
3112            x_high: true,
3113            x_low: false,
3114            y_high: false,
3115            y_low: true,
3116            z_high: false,
3117            z_low: false,
3118        };
3119
3120        assert!(status.is_active());
3121        assert!(status.x_event());
3122        assert!(status.y_event());
3123        assert!(!status.z_event());
3124        assert!(status.any_event());
3125    }
3126
3127    #[test]
3128    fn click_status_detection() {
3129        let status = ClickStatus {
3130            active: true,
3131            double_click: false,
3132            single_click: true,
3133            negative: true,
3134            x: false,
3135            y: false,
3136            z: true,
3137        };
3138
3139        assert!(status.is_active());
3140        assert!(!status.is_double_click());
3141        assert!(status.is_single_click());
3142        assert!(status.is_negative());
3143        assert!(status.any_event());
3144    }
3145
3146    #[test]
3147    fn interrupt_sources_any_active() {
3148        let sources = InterruptSources {
3149            motion1: MotionStatus { active: false, ..Default::default() },
3150            motion2: MotionStatus { active: true, ..Default::default() },
3151            click: ClickStatus::default(),
3152        };
3153
3154        assert!(sources.any_active());
3155    }
3156
3157    #[test]
3158    fn interrupt_config_builder() {
3159        let config = InterruptConfig::disabled()
3160            .with_polarity(InterruptPolarity::ActiveLow)
3161            .with_int1(Int1Routing { ia1: true, ..Default::default() })
3162            .with_int2(Int2Routing { activity: true, ..Default::default() });
3163
3164        assert!(matches!(config.polarity, InterruptPolarity::ActiveLow));
3165        assert!(config.int1.ia1);
3166        assert!(config.int2.activity);
3167    }
3168}