Skip to main content

ph_qmi8658/config/
mod.rs

1//! Configuration helpers for the QMI8658.
2
3pub(crate) mod common;
4pub(crate) mod mode;
5
6pub use common::{AccelConfig, AccelOutputDataRate, AccelRange, LowPassFilterMode};
7pub use common::{GyroConfig, GyroOutputDataRate, GyroRange};
8pub use mode::OperatingMode;
9pub(crate) use mode::{OperatingModeStateMachine, TransitionDelay, transition_delay};
10
11use crate::error::Error;
12use crate::register::{ctrl5, ctrl7};
13
14/// QMI8658 configuration settings.
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct Config {
18    /// Accelerometer configuration (range + output data rate). None disables the accelerometer.
19    pub accel: Option<AccelConfig>,
20    /// Gyroscope configuration (range + output data rate). None disables the gyroscope.
21    pub gyro: Option<GyroConfig>,
22}
23
24impl Default for Config {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl Config {
31    /// Creates a default configuration.
32    pub const fn new() -> Self {
33        Self {
34            accel: Some(AccelConfig::DEFAULT),
35            gyro: Some(GyroConfig::DEFAULT),
36        }
37    }
38
39    /// Sets the accelerometer configuration.
40    #[must_use]
41    pub const fn with_accel_config(mut self, accel: AccelConfig) -> Self {
42        self.accel = Some(accel);
43        self
44    }
45
46    /// Disables the accelerometer.
47    #[must_use]
48    pub const fn without_accel(mut self) -> Self {
49        self.accel = None;
50        self
51    }
52
53    /// Sets the gyroscope configuration.
54    #[must_use]
55    pub const fn with_gyro_config(mut self, gyro: GyroConfig) -> Self {
56        self.gyro = Some(gyro);
57        self
58    }
59
60    /// Disables the gyroscope.
61    #[must_use]
62    pub const fn without_gyro(mut self) -> Self {
63        self.gyro = None;
64        self
65    }
66
67    pub(crate) const fn accel_enabled(self) -> bool {
68        self.accel.is_some()
69    }
70
71    pub(crate) const fn gyro_enabled(self) -> bool {
72        self.gyro.is_some()
73    }
74
75    pub(crate) const fn ctrl2_value(self) -> u8 {
76        match self.accel {
77            Some(accel) => accel.ctrl2_value(),
78            None => 0,
79        }
80    }
81
82    pub(crate) const fn ctrl3_value(self) -> u8 {
83        match self.gyro {
84            Some(gyro) => gyro.ctrl3_value(),
85            None => 0,
86        }
87    }
88
89    pub(crate) const fn ctrl5_value(self) -> u8 {
90        let mut value = 0;
91        if let Some(gyro) = self.gyro
92            && let Some(mode) = gyro.lpf
93        {
94            value |= ctrl5::G_LPF_EN;
95            value |= (mode.bits() << ctrl5::G_LPF_MODE_SHIFT) & ctrl5::G_LPF_MODE_MASK;
96        }
97        if let Some(accel) = self.accel
98            && let Some(mode) = accel.lpf
99        {
100            value |= ctrl5::A_LPF_EN;
101            value |= (mode.bits() << ctrl5::A_LPF_MODE_SHIFT) & ctrl5::A_LPF_MODE_MASK;
102        }
103        value
104    }
105
106    pub(crate) const fn ctrl7_value(self) -> u8 {
107        let mut value = 0;
108        if self.accel_enabled() {
109            value |= ctrl7::A_EN;
110        }
111        if self.gyro_enabled() {
112            value |= ctrl7::G_EN;
113        }
114        value
115    }
116
117    pub(crate) fn validate(self) -> Result<(), Error> {
118        if let (Some(accel), Some(_gyro)) = (self.accel, self.gyro)
119            && accel.odr.is_low_power()
120        {
121            return Err(Error::InvalidData);
122        }
123        Ok(())
124    }
125
126    pub(crate) const fn target_mode(self) -> OperatingMode {
127        match (self.accel, self.gyro) {
128            (None, None) => OperatingMode::PowerOnDefault,
129            (Some(accel), None) => {
130                if accel.odr.is_low_power() {
131                    OperatingMode::LowPowerAccel
132                } else {
133                    OperatingMode::AccelOnly
134                }
135            }
136            (None, Some(_)) => OperatingMode::GyroOnly,
137            (Some(_), Some(_)) => OperatingMode::AccelGyroOnly,
138        }
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::common::{AccelConfig, AccelOutputDataRate, AccelRange, GyroConfig};
145    use super::*;
146
147    #[test]
148    fn validate_rejects_low_power_accel_with_gyro() {
149        let accel = AccelConfig::new(AccelRange::G8, AccelOutputDataRate::LowPowerHz21);
150        let config = Config::new().with_accel_config(accel);
151        assert_eq!(config.validate(), Err(Error::InvalidData));
152    }
153
154    #[test]
155    fn validate_allows_low_power_accel_without_gyro() {
156        let accel = AccelConfig::new(AccelRange::G8, AccelOutputDataRate::LowPowerHz21);
157        let config = Config::new().with_accel_config(accel).without_gyro();
158        assert_eq!(config.validate(), Ok(()));
159    }
160
161    #[test]
162    fn target_mode_tracks_config() {
163        let accel = AccelConfig::new(AccelRange::G8, AccelOutputDataRate::LowPowerHz21);
164        let gyro = GyroConfig::DEFAULT;
165        let config = Config::new().with_accel_config(accel).without_gyro();
166        assert_eq!(config.target_mode(), OperatingMode::LowPowerAccel);
167
168        let config = Config::new().with_gyro_config(gyro).without_accel();
169        assert_eq!(config.target_mode(), OperatingMode::GyroOnly);
170
171        let config = Config::new().without_accel().without_gyro();
172        assert_eq!(config.target_mode(), OperatingMode::PowerOnDefault);
173    }
174}