Skip to main content

ph_qmi8658/
interrupt.rs

1//! Interrupt routing and status decoding.
2
3use crate::register::{ctrl8, status_int, status0, status1};
4
5/// Interrupt pin selection (device pins).
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub enum InterruptPin {
9    /// Interrupt pin 1.
10    Int1,
11    /// Interrupt pin 2.
12    Int2,
13}
14
15/// Interrupt routing and feature enable configuration (CTRL8).
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub struct InterruptConfig {
19    /// Use STATUSINT.bit7 for CTRL9 handshake when set.
20    pub ctrl9_handshake_statusint: bool,
21    /// Route motion detection events to the selected pin.
22    pub motion_pin: InterruptPin,
23    /// Enable pedometer interrupt generation.
24    pub pedometer: bool,
25    /// Enable significant motion interrupt generation.
26    pub significant_motion: bool,
27    /// Enable no motion interrupt generation.
28    pub no_motion: bool,
29    /// Enable any motion interrupt generation.
30    pub any_motion: bool,
31    /// Enable tap interrupt generation.
32    pub tap: bool,
33}
34
35impl InterruptConfig {
36    /// Default interrupt configuration.
37    pub const DEFAULT: Self = Self {
38        ctrl9_handshake_statusint: false,
39        motion_pin: InterruptPin::Int1,
40        pedometer: false,
41        significant_motion: false,
42        no_motion: false,
43        any_motion: false,
44        tap: false,
45    };
46
47    /// Creates a new interrupt configuration.
48    pub const fn new() -> Self {
49        Self::DEFAULT
50    }
51
52    /// Sets the pin used for motion detection events.
53    #[must_use]
54    pub const fn with_motion_pin(mut self, pin: InterruptPin) -> Self {
55        self.motion_pin = pin;
56        self
57    }
58
59    /// Enables or disables the CTRL9 handshake on STATUSINT.
60    #[must_use]
61    pub const fn with_ctrl9_handshake_statusint(mut self, enable: bool) -> Self {
62        self.ctrl9_handshake_statusint = enable;
63        self
64    }
65
66    /// Enables or disables tap interrupts.
67    #[must_use]
68    pub const fn with_tap(mut self, enable: bool) -> Self {
69        self.tap = enable;
70        self
71    }
72
73    /// Enables or disables any-motion interrupts.
74    #[must_use]
75    pub const fn with_any_motion(mut self, enable: bool) -> Self {
76        self.any_motion = enable;
77        self
78    }
79
80    /// Enables or disables no-motion interrupts.
81    #[must_use]
82    pub const fn with_no_motion(mut self, enable: bool) -> Self {
83        self.no_motion = enable;
84        self
85    }
86
87    /// Enables or disables significant-motion interrupts.
88    #[must_use]
89    pub const fn with_significant_motion(mut self, enable: bool) -> Self {
90        self.significant_motion = enable;
91        self
92    }
93
94    /// Enables or disables pedometer interrupts.
95    #[must_use]
96    pub const fn with_pedometer(mut self, enable: bool) -> Self {
97        self.pedometer = enable;
98        self
99    }
100
101    pub(crate) const fn ctrl8_value(self) -> u8 {
102        let mut value = 0;
103        if self.ctrl9_handshake_statusint {
104            value |= ctrl8::CTRL9_HANDSHAKE_STATUSINT;
105        }
106        if matches!(self.motion_pin, InterruptPin::Int1) {
107            value |= ctrl8::MOTION_INT_INT1;
108        }
109        if self.pedometer {
110            value |= ctrl8::PEDOMETER_EN;
111        }
112        if self.significant_motion {
113            value |= ctrl8::SIGNIFICANT_MOTION_EN;
114        }
115        if self.no_motion {
116            value |= ctrl8::NO_MOTION_EN;
117        }
118        if self.any_motion {
119            value |= ctrl8::ANY_MOTION_EN;
120        }
121        if self.tap {
122            value |= ctrl8::TAP_EN;
123        }
124        value
125    }
126}
127
128impl Default for InterruptConfig {
129    fn default() -> Self {
130        Self::DEFAULT
131    }
132}
133
134/// Decoded interrupt and status flags.
135#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
136#[cfg_attr(feature = "defmt", derive(defmt::Format))]
137pub struct InterruptStatus {
138    /// CTRL9 command done.
139    pub cmd_done: bool,
140    /// Data locked flag.
141    pub data_locked: bool,
142    /// Data available flag.
143    pub data_available: bool,
144    /// Accelerometer data ready.
145    pub accel_ready: bool,
146    /// Gyroscope data ready.
147    pub gyro_ready: bool,
148    /// AttitudeEngine data ready.
149    pub ae_ready: bool,
150    /// Tap detected.
151    pub tap: bool,
152    /// Any motion detected.
153    pub any_motion: bool,
154    /// No motion detected.
155    pub no_motion: bool,
156    /// Significant motion detected.
157    pub significant_motion: bool,
158    /// Wake on Motion detected.
159    pub wake_on_motion: bool,
160    /// Pedometer detected.
161    pub pedometer: bool,
162}
163
164impl InterruptStatus {
165    pub(crate) const fn from_regs(status_int_reg: u8, status0_reg: u8, status1_reg: u8) -> Self {
166        Self {
167            cmd_done: (status_int_reg & status_int::CMD_DONE) != 0
168                || (status1_reg & status1::CMD_DONE) != 0,
169            data_locked: (status_int_reg & status_int::LOCKED) != 0,
170            data_available: (status_int_reg & status_int::AVAIL) != 0,
171            accel_ready: (status0_reg & status0::ACCEL_AVAIL) != 0,
172            gyro_ready: (status0_reg & status0::GYRO_AVAIL) != 0,
173            ae_ready: (status0_reg & status0::AE_AVAIL) != 0,
174            tap: (status1_reg & status1::TAP) != 0,
175            any_motion: (status1_reg & status1::ANY_MOTION) != 0,
176            no_motion: (status1_reg & status1::NO_MOTION) != 0,
177            significant_motion: (status1_reg & status1::SIGNIFICANT_MOTION) != 0,
178            wake_on_motion: (status1_reg & status1::WOM) != 0,
179            pedometer: (status1_reg & status1::PEDOMETER) != 0,
180        }
181    }
182
183    /// Returns true if any status bit is set.
184    pub const fn any(self) -> bool {
185        self.cmd_done
186            || self.data_locked
187            || self.data_available
188            || self.accel_ready
189            || self.gyro_ready
190            || self.ae_ready
191            || self.tap
192            || self.any_motion
193            || self.no_motion
194            || self.significant_motion
195            || self.wake_on_motion
196            || self.pedometer
197    }
198}
199
200/// Error returned when waiting on an interrupt pin.
201#[derive(Debug)]
202#[cfg_attr(feature = "defmt", derive(defmt::Format))]
203pub enum InterruptWaitError<E> {
204    /// Interrupt pin was not provided to the driver.
205    Missing,
206    /// Underlying pin error.
207    Pin(E),
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use crate::register::{status_int, status0, status1};
214
215    #[test]
216    fn decode_interrupt_status() {
217        let status = InterruptStatus::from_regs(
218            status_int::AVAIL,
219            status0::ACCEL_AVAIL | status0::GYRO_AVAIL,
220            status1::TAP | status1::ANY_MOTION | status1::CMD_DONE | status1::WOM,
221        );
222
223        assert!(status.cmd_done);
224        assert!(status.data_available);
225        assert!(status.accel_ready);
226        assert!(status.gyro_ready);
227        assert!(status.tap);
228        assert!(status.any_motion);
229        assert!(status.wake_on_motion);
230        assert!(!status.no_motion);
231        assert!(status.any());
232    }
233}