icm20948/
interrupt.rs

1//! Interrupt configuration and management
2//!
3//! The ICM-20948 has a single interrupt pin (INT1) that can be configured to trigger on various events:
4//! - Raw data ready
5//! - FIFO watermark reached
6//! - FIFO overflow
7//! - Wake-on-motion (`WoM`)
8//! - DMP interrupt
9//! - I2C master operations
10//!
11//! # Example
12//!
13//! ```ignore
14//! # use icm20948::{Icm20948Driver, interrupt::{InterruptConfig, InterruptPinConfig}};
15//! # let mut imu: Icm20948Driver<_> = todo!();
16//! // Configure interrupt pin as active-low, open-drain
17//! let pin_config = InterruptPinConfig {
18//!     active_low: true,
19//!     open_drain: true,
20//!     latch_enabled: true,
21//!     clear_on_any_read: false,
22//! };
23//! imu.configure_interrupt_pin(&pin_config)?;
24//!
25//! // Enable data ready interrupt
26//! let int_config = InterruptConfig {
27//!     raw_data_ready: true,
28//!     ..Default::default()
29//! };
30//! imu.configure_interrupts(&int_config)?;
31//! # Ok::<(), icm20948::Error<()>>(())
32//! ```
33/// Interrupt pin electrical configuration
34#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36#[allow(clippy::struct_excessive_bools)]
37pub struct InterruptPinConfig {
38    /// Active low (true) or active high (false)
39    pub active_low: bool,
40    /// Open-drain (true) or push-pull (false)
41    pub open_drain: bool,
42    /// Latch interrupt until cleared
43    pub latch_enabled: bool,
44    /// Clear interrupt status on any register read (true) or only on status read (false)
45    pub clear_on_any_read: bool,
46}
47
48impl InterruptPinConfig {
49    /// Create configuration for active-low, open-drain interrupt (common for I2C)
50    pub const fn i2c_default() -> Self {
51        Self {
52            active_low: true,
53            open_drain: true,
54            latch_enabled: true,
55            clear_on_any_read: false,
56        }
57    }
58
59    /// Create configuration for active-high, push-pull interrupt (common for SPI)
60    pub const fn spi_default() -> Self {
61        Self {
62            active_low: false,
63            open_drain: false,
64            latch_enabled: true,
65            clear_on_any_read: false,
66        }
67    }
68}
69
70/// Interrupt source configuration
71#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73#[allow(clippy::struct_excessive_bools)]
74pub struct InterruptConfig {
75    /// Enable raw data ready interrupt (fires when new sensor data is available)
76    pub raw_data_ready: bool,
77    /// Enable FIFO overflow interrupt
78    pub fifo_overflow: bool,
79    /// Enable FIFO watermark interrupt
80    pub fifo_watermark: bool,
81    /// Enable wake-on-motion interrupt
82    pub wake_on_motion: bool,
83    /// Enable DMP interrupt
84    pub dmp: bool,
85    /// Enable I2C master interrupt
86    pub i2c_master: bool,
87    /// Enable PLL ready interrupt
88    pub pll_ready: bool,
89}
90
91impl InterruptConfig {
92    /// Create configuration with only data ready interrupt enabled
93    pub const fn data_ready_only() -> Self {
94        Self {
95            raw_data_ready: true,
96            fifo_overflow: false,
97            fifo_watermark: false,
98            wake_on_motion: false,
99            dmp: false,
100            i2c_master: false,
101            pll_ready: false,
102        }
103    }
104
105    /// Create configuration for FIFO batch reading
106    pub const fn fifo_batch() -> Self {
107        Self {
108            raw_data_ready: false,
109            fifo_overflow: true,
110            fifo_watermark: true,
111            wake_on_motion: false,
112            dmp: false,
113            i2c_master: false,
114            pll_ready: false,
115        }
116    }
117
118    /// Create configuration for wake-on-motion
119    pub const fn wake_on_motion_only() -> Self {
120        Self {
121            raw_data_ready: false,
122            fifo_overflow: false,
123            fifo_watermark: false,
124            wake_on_motion: true,
125            dmp: false,
126            i2c_master: false,
127            pll_ready: false,
128        }
129    }
130
131    /// Check if any interrupt is enabled
132    pub const fn any_enabled(&self) -> bool {
133        self.raw_data_ready
134            || self.fifo_overflow
135            || self.fifo_watermark
136            || self.wake_on_motion
137            || self.dmp
138            || self.i2c_master
139            || self.pll_ready
140    }
141}
142
143/// Interrupt status flags
144#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
145#[cfg_attr(feature = "defmt", derive(defmt::Format))]
146#[allow(clippy::struct_excessive_bools)]
147pub struct InterruptStatus {
148    /// Raw data ready interrupt flag
149    pub raw_data_ready: bool,
150    /// FIFO overflow interrupt flag
151    pub fifo_overflow: bool,
152    /// FIFO watermark interrupt flag
153    pub fifo_watermark: bool,
154    /// Wake-on-motion interrupt flag
155    pub wake_on_motion: bool,
156    /// DMP interrupt flag
157    pub dmp: bool,
158    /// I2C master interrupt flag
159    pub i2c_master: bool,
160    /// PLL ready interrupt flag
161    pub pll_ready: bool,
162}
163
164impl InterruptStatus {
165    /// Create empty interrupt status
166    pub const fn new() -> Self {
167        Self {
168            raw_data_ready: false,
169            fifo_overflow: false,
170            fifo_watermark: false,
171            wake_on_motion: false,
172            dmp: false,
173            i2c_master: false,
174            pll_ready: false,
175        }
176    }
177
178    /// Check if any interrupt flag is set
179    pub const fn any_set(&self) -> bool {
180        self.raw_data_ready
181            || self.fifo_overflow
182            || self.fifo_watermark
183            || self.wake_on_motion
184            || self.dmp
185            || self.i2c_master
186            || self.pll_ready
187    }
188
189    /// Clear all interrupt flags
190    pub const fn clear() -> Self {
191        Self::new()
192    }
193
194    /// Convert interrupt status to raw register value
195    pub const fn to_raw(&self) -> u8 {
196        let mut value = 0u8;
197        if self.raw_data_ready {
198            value |= 0x01;
199        }
200        if self.fifo_overflow {
201            value |= 0x02;
202        }
203        if self.fifo_watermark {
204            value |= 0x04;
205        }
206        if self.i2c_master {
207            value |= 0x08;
208        }
209        if self.dmp {
210            value |= 0x10;
211        }
212        if self.pll_ready {
213            value |= 0x20;
214        }
215        if self.wake_on_motion {
216            value |= 0x40;
217        }
218        value
219    }
220}
221
222/// FSYNC (Frame Synchronization) configuration
223#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
224#[cfg_attr(feature = "defmt", derive(defmt::Format))]
225pub struct FsyncConfig {
226    /// Enable FSYNC interrupt mode
227    pub enable: bool,
228    /// FSYNC pin active low
229    pub active_low: bool,
230}
231
232/// Data ready status for all sensors
233#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
234#[cfg_attr(feature = "defmt", derive(defmt::Format))]
235#[allow(clippy::struct_excessive_bools)]
236pub struct DataReadyStatus {
237    /// Raw data ready for sensor register 0
238    pub sensor0_ready: bool,
239    /// Raw data ready for sensor register 1
240    pub sensor1_ready: bool,
241    /// Raw data ready for sensor register 2
242    pub sensor2_ready: bool,
243    /// Raw data ready for sensor register 3
244    pub sensor3_ready: bool,
245}
246
247impl DataReadyStatus {
248    /// Create empty data ready status
249    pub const fn new() -> Self {
250        Self {
251            sensor0_ready: false,
252            sensor1_ready: false,
253            sensor2_ready: false,
254            sensor3_ready: false,
255        }
256    }
257
258    /// Check if any sensor has data ready
259    pub const fn any_ready(&self) -> bool {
260        self.sensor0_ready || self.sensor1_ready || self.sensor2_ready || self.sensor3_ready
261    }
262}
263
264/// Wake-on-motion status for each axis
265#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
266#[cfg_attr(feature = "defmt", derive(defmt::Format))]
267pub struct WomStatus {
268    /// Motion detected on X-axis
269    pub x_motion: bool,
270    /// Motion detected on Y-axis
271    pub y_motion: bool,
272    /// Motion detected on Z-axis
273    pub z_motion: bool,
274}
275
276impl WomStatus {
277    /// Create empty `WoM` status
278    pub const fn new() -> Self {
279        Self {
280            x_motion: false,
281            y_motion: false,
282            z_motion: false,
283        }
284    }
285
286    /// Check if motion was detected on any axis
287    pub const fn any_motion(&self) -> bool {
288        self.x_motion || self.y_motion || self.z_motion
289    }
290
291    /// Get number of axes with motion detected
292    pub const fn motion_count(&self) -> u8 {
293        (self.x_motion as u8) + (self.y_motion as u8) + (self.z_motion as u8)
294    }
295}
296
297#[cfg(test)]
298mod tests {
299    use super::*;
300
301    #[test]
302    fn test_interrupt_pin_config_default() {
303        let config = InterruptPinConfig::default();
304        assert!(!config.active_low);
305        assert!(!config.open_drain);
306        assert!(!config.latch_enabled);
307    }
308
309    #[test]
310    fn test_interrupt_pin_config_i2c() {
311        let config = InterruptPinConfig::i2c_default();
312        assert!(config.active_low);
313        assert!(config.open_drain);
314        assert!(config.latch_enabled);
315    }
316
317    #[test]
318    fn test_interrupt_pin_config_spi() {
319        let config = InterruptPinConfig::spi_default();
320        assert!(!config.active_low);
321        assert!(!config.open_drain);
322        assert!(config.latch_enabled);
323    }
324
325    #[test]
326    fn test_interrupt_config_default() {
327        let config = InterruptConfig::default();
328        assert!(!config.any_enabled());
329    }
330
331    #[test]
332    fn test_interrupt_config_data_ready() {
333        let config = InterruptConfig::data_ready_only();
334        assert!(config.raw_data_ready);
335        assert!(config.any_enabled());
336        assert!(!config.fifo_overflow);
337    }
338
339    #[test]
340    fn test_interrupt_config_fifo() {
341        let config = InterruptConfig::fifo_batch();
342        assert!(!config.raw_data_ready);
343        assert!(config.fifo_overflow);
344        assert!(config.fifo_watermark);
345        assert!(config.any_enabled());
346    }
347
348    #[test]
349    fn test_interrupt_status() {
350        let mut status = InterruptStatus::new();
351        assert!(!status.any_set());
352
353        status.raw_data_ready = true;
354        assert!(status.any_set());
355
356        let status = InterruptStatus::clear();
357        assert!(!status.any_set());
358    }
359
360    #[test]
361    fn test_data_ready_status() {
362        let mut status = DataReadyStatus::new();
363        assert!(!status.any_ready());
364
365        status.sensor0_ready = true;
366        assert!(status.any_ready());
367    }
368
369    #[test]
370    fn test_wom_status() {
371        let mut status = WomStatus::new();
372        assert!(!status.any_motion());
373        assert_eq!(status.motion_count(), 0);
374
375        status.x_motion = true;
376        assert!(status.any_motion());
377        assert_eq!(status.motion_count(), 1);
378
379        status.y_motion = true;
380        assert_eq!(status.motion_count(), 2);
381
382        status.z_motion = true;
383        assert_eq!(status.motion_count(), 3);
384    }
385
386    #[test]
387    fn test_fsync_config_default() {
388        let config = FsyncConfig::default();
389        assert!(!config.enable);
390        assert!(!config.active_low);
391    }
392}