diff options
| author | unitexe <unitexe70@gmail.com> | 2026-04-08 23:53:17 -0500 |
|---|---|---|
| committer | unitexe <unitexe70@gmail.com> | 2026-04-08 23:54:28 -0500 |
| commit | de3f7ed45185f3a678ba0de04d98cc9ac92de0c8 (patch) | |
| tree | 3db0159c1370f240d42c1e8b89a67247538f43d1 /tmp117/src/conf.rs | |
Initial commit
Diffstat (limited to 'tmp117/src/conf.rs')
| -rw-r--r-- | tmp117/src/conf.rs | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/tmp117/src/conf.rs b/tmp117/src/conf.rs new file mode 100644 index 0000000..1ea275b --- /dev/null +++ b/tmp117/src/conf.rs @@ -0,0 +1,547 @@ +use core::fmt; + +#[derive(Debug, PartialEq)] +pub enum AlertPinSelect { + /// ALERT pin reflects the status of the alert flags + Alert, + + /// ALERT pin reflects the status of the data ready flag + DataReady, +} + +impl fmt::Display for AlertPinSelect { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Alert => write!(f, "Alert"), + Self::DataReady => write!(f, "Data Ready"), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum PinPolarity { + /// ALERT pin is active low + ActiveLow, + + /// ALERT pin is active high + ActiveHigh, +} + +impl fmt::Display for PinPolarity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ActiveLow => write!(f, "Active Low"), + Self::ActiveHigh => write!(f, "Active High"), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Mode { + /// Alert mode + Alert, + + /// Thermal mode + Thermal, +} + +impl fmt::Display for Mode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Alert => write!(f, "Alert"), + Self::Thermal => write!(f, "Thermal"), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum ConversionMode { + /// Continous conversion + Continuous, + + /// Shutdown + Shutdown, + + /// One-shot conversion + Oneshot, +} + +impl fmt::Display for ConversionMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Continuous => write!(f, "Continuous"), + Self::Shutdown => write!(f, "Shutdown"), + Self::Oneshot => write!(f, "One-shot"), + } + } +} + +/// Determines the number of conversion results that are collected and averaged before updating the temperature register. The average is an accumulated average, not a running average. +#[derive(Debug, PartialEq)] +pub enum ConversionAveragingMode { + /// No averaging + None, + + /// 8 averaged conversions + Avg8, + + /// 32 averaged conversions + Avg32, + + /// 64 averaged conversions + Avg64, +} + +impl ConversionAveragingMode { + fn minimum_conversion_cycle_time(&self) -> ConversionCycle { + match self { + ConversionAveragingMode::None => ConversionCycle::Microseconds15500, + ConversionAveragingMode::Avg8 => ConversionCycle::Milliseconds125, + ConversionAveragingMode::Avg32 => ConversionCycle::Milliseconds500, + ConversionAveragingMode::Avg64 => ConversionCycle::Seconds1, + } + } +} + +impl fmt::Display for ConversionAveragingMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::None => write!(f, "None"), + Self::Avg8 => write!(f, "8 conversions"), + Self::Avg32 => write!(f, "32 conversions"), + Self::Avg64 => write!(f, "64 conversions"), + } + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum ConversionCycle { + Microseconds15500, + Milliseconds125, + Milliseconds250, + Milliseconds500, + Seconds1, + Seconds4, + Seconds8, + Seconds16, +} + +impl fmt::Display for ConversionCycle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Microseconds15500 => write!(f, "15.5ms"), + Self::Milliseconds125 => write!(f, "125ms"), + Self::Milliseconds250 => write!(f, "250ms"), + Self::Milliseconds500 => write!(f, "500ms"), + Self::Seconds1 => write!(f, "1s"), + Self::Seconds4 => write!(f, "4s"), + Self::Seconds8 => write!(f, "8s"), + Self::Seconds16 => write!(f, "16s"), + } + } +} + +pub enum ConfigurationError { + CycleTimeAndAveragingModeAreIncompatible, +} + +pub struct Configuration { + /// ALERT pin select bit + pub alert_pin_select: AlertPinSelect, + + /// ALERT pin polarity bit + pub alert_pin_polarity: PinPolarity, + + /// Therm/alert mode select + pub mode: Mode, + + /// Conversion averaging modes + pub conversion_averaging_mode: ConversionAveragingMode, + + /// Conversion cycle bit + pub conversion_cycle: ConversionCycle, + + /// Conversion mode + pub conversion_mode: ConversionMode, +} + +impl Configuration { + pub fn validate(&self) -> Result<(), ConfigurationError> { + if self.conversion_cycle + < self + .conversion_averaging_mode + .minimum_conversion_cycle_time() + { + Err(ConfigurationError::CycleTimeAndAveragingModeAreIncompatible) + } else { + Ok(()) + } + } +} + +impl fmt::Display for Configuration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Alert Pin Select: {}", self.alert_pin_select)?; + writeln!(f, "Alert Pin Polarity: {}", self.alert_pin_polarity)?; + writeln!(f, "Mode: {}", self.mode)?; + writeln!( + f, + "Conversion Averaging: {}", + self.conversion_averaging_mode + )?; + writeln!(f, "Conversion Cycle: {}", self.conversion_cycle)?; + write!(f, "Conversion Mode: {}", self.conversion_mode) + } +} + +impl From<&Configuration> for u16 { + fn from(configuration: &Configuration) -> u16 { + let mut bits: u16 = 0; + + bits |= match configuration.alert_pin_select { + AlertPinSelect::Alert => 0 << 2, + AlertPinSelect::DataReady => 1 << 2, + }; + + bits |= match configuration.alert_pin_polarity { + PinPolarity::ActiveLow => 0 << 3, + PinPolarity::ActiveHigh => 1 << 3, + }; + + bits |= match configuration.mode { + Mode::Alert => 0 << 4, + Mode::Thermal => 1 << 4, + }; + + bits |= match configuration.conversion_averaging_mode { + ConversionAveragingMode::None => 0b00 << 5, + ConversionAveragingMode::Avg8 => 0b01 << 5, + ConversionAveragingMode::Avg32 => 0b10 << 5, + ConversionAveragingMode::Avg64 => 0b11 << 5, + }; + + bits |= match configuration.conversion_cycle { + ConversionCycle::Microseconds15500 => 0b000 << 7, + ConversionCycle::Milliseconds125 => 0b001 << 7, + ConversionCycle::Milliseconds250 => 0b010 << 7, + ConversionCycle::Milliseconds500 => 0b011 << 7, + ConversionCycle::Seconds1 => 0b100 << 7, + ConversionCycle::Seconds4 => 0b101 << 7, + ConversionCycle::Seconds8 => 0b110 << 7, + ConversionCycle::Seconds16 => 0b111 << 7, + }; + + bits |= match configuration.conversion_mode { + ConversionMode::Continuous => 0b00 << 10, + ConversionMode::Shutdown => 0b01 << 10, + ConversionMode::Oneshot => 0b11 << 10, + }; + + bits + } +} + +impl From<u16> for Configuration { + fn from(register: u16) -> Self { + let alert_pin_select = (register >> 2) & 0x1; + let alert_pin_polarity = (register >> 3) & 0x1; + let mode = (register >> 4) & 0x1; + let conversion_averaging_mode = (register >> 5) & 0x3; + let conversion_cycle = (register >> 7) & 0x7; + let conversion_mode = (register >> 10) & 0x3; + + let alert_pin_select = match alert_pin_select { + 0 => AlertPinSelect::Alert, + 1 => AlertPinSelect::DataReady, + _ => todo!("Handle unsupported alert pin select"), + }; + let alert_pin_polarity = match alert_pin_polarity { + 0 => PinPolarity::ActiveLow, + 1 => PinPolarity::ActiveHigh, + _ => todo!("Handle unsupported alert pin polarity"), + }; + let mode = match mode { + 0 => Mode::Alert, + 1 => Mode::Thermal, + _ => todo!("Handle unsupported mode"), + }; + let conversion_averaging_mode = match conversion_averaging_mode { + 0b00 => ConversionAveragingMode::None, + 0b01 => ConversionAveragingMode::Avg8, + 0b10 => ConversionAveragingMode::Avg32, + 0b11 => ConversionAveragingMode::Avg64, + _ => todo!("Handle unsupported conversion averaging mode"), + }; + let conversion_cycle = match conversion_cycle { + 0b000 => ConversionCycle::Microseconds15500, + 0b001 => ConversionCycle::Milliseconds125, + 0b010 => ConversionCycle::Milliseconds250, + 0b011 => ConversionCycle::Milliseconds500, + 0b100 => ConversionCycle::Seconds1, + 0b101 => ConversionCycle::Seconds4, + 0b110 => ConversionCycle::Seconds8, + 0b111 => ConversionCycle::Seconds16, + _ => todo!("Handle unsupported conversion cycle"), + }; + let conversion_mode = match conversion_mode { + 0b00 | 0b10 => ConversionMode::Continuous, + 0b01 => ConversionMode::Shutdown, + 0b11 => ConversionMode::Oneshot, + _ => todo!("Handle unsupported conversion mode"), + }; + + Self { + alert_pin_select, + alert_pin_polarity, + mode, + conversion_averaging_mode, + conversion_cycle, + conversion_mode, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // helpers to construct common configurations + fn default_configuration() -> Configuration { + Configuration { + alert_pin_select: AlertPinSelect::Alert, + alert_pin_polarity: PinPolarity::ActiveLow, + mode: Mode::Alert, + conversion_averaging_mode: ConversionAveragingMode::Avg8, + conversion_cycle: ConversionCycle::Seconds1, + conversion_mode: ConversionMode::Continuous, + } + } + + // --- From<&Configuration> for u16 --- + + #[test] + fn test_alert_pin_select_alert_bit() { + let mut config = default_configuration(); + config.alert_pin_select = AlertPinSelect::Alert; + assert_eq!(u16::from(&config) & (1 << 2), 0); + } + + #[test] + fn test_alert_pin_select_data_ready_bit() { + let mut config = default_configuration(); + config.alert_pin_select = AlertPinSelect::DataReady; + assert_ne!(u16::from(&config) & (1 << 2), 0); + } + + #[test] + fn test_pin_polarity_active_low_bit() { + let mut config = default_configuration(); + config.alert_pin_polarity = PinPolarity::ActiveLow; + assert_eq!(u16::from(&config) & (1 << 3), 0); + } + + #[test] + fn test_pin_polarity_active_high_bit() { + let mut config = default_configuration(); + config.alert_pin_polarity = PinPolarity::ActiveHigh; + assert_ne!(u16::from(&config) & (1 << 3), 0); + } + + #[test] + fn test_mode_alert_bit() { + let mut config = default_configuration(); + config.mode = Mode::Alert; + assert_eq!(u16::from(&config) & (1 << 4), 0); + } + + #[test] + fn test_mode_thermal_bit() { + let mut config = default_configuration(); + config.mode = Mode::Thermal; + assert_ne!(u16::from(&config) & (1 << 4), 0); + } + + #[test] + fn test_conversion_averaging_mode_bits() { + let mut config = default_configuration(); + + config.conversion_averaging_mode = ConversionAveragingMode::None; + assert_eq!((u16::from(&config) >> 5) & 0x3, 0b00); + + config.conversion_averaging_mode = ConversionAveragingMode::Avg8; + assert_eq!((u16::from(&config) >> 5) & 0x3, 0b01); + + config.conversion_averaging_mode = ConversionAveragingMode::Avg32; + assert_eq!((u16::from(&config) >> 5) & 0x3, 0b10); + + config.conversion_averaging_mode = ConversionAveragingMode::Avg64; + assert_eq!((u16::from(&config) >> 5) & 0x3, 0b11); + } + + #[test] + fn test_conversion_cycle_bits() { + let mut config = default_configuration(); + + config.conversion_cycle = ConversionCycle::Microseconds15500; + assert_eq!((u16::from(&config) >> 7) & 0x7, 0b000); + + config.conversion_cycle = ConversionCycle::Milliseconds125; + assert_eq!((u16::from(&config) >> 7) & 0x7, 0b001); + + config.conversion_cycle = ConversionCycle::Milliseconds250; + assert_eq!((u16::from(&config) >> 7) & 0x7, 0b010); + + config.conversion_cycle = ConversionCycle::Milliseconds500; + assert_eq!((u16::from(&config) >> 7) & 0x7, 0b011); + + config.conversion_cycle = ConversionCycle::Seconds1; + assert_eq!((u16::from(&config) >> 7) & 0x7, 0b100); + + config.conversion_cycle = ConversionCycle::Seconds4; + assert_eq!((u16::from(&config) >> 7) & 0x7, 0b101); + + config.conversion_cycle = ConversionCycle::Seconds8; + assert_eq!((u16::from(&config) >> 7) & 0x7, 0b110); + + config.conversion_cycle = ConversionCycle::Seconds16; + assert_eq!((u16::from(&config) >> 7) & 0x7, 0b111); + } + + #[test] + fn test_conversion_cycle_ordering() { + assert!(ConversionCycle::Microseconds15500 < ConversionCycle::Milliseconds125); + assert!(ConversionCycle::Milliseconds125 < ConversionCycle::Milliseconds250); + assert!(ConversionCycle::Milliseconds250 < ConversionCycle::Milliseconds500); + assert!(ConversionCycle::Milliseconds500 < ConversionCycle::Seconds1); + assert!(ConversionCycle::Seconds1 < ConversionCycle::Seconds4); + assert!(ConversionCycle::Seconds4 < ConversionCycle::Seconds8); + assert!(ConversionCycle::Seconds8 < ConversionCycle::Seconds16); + } + + #[test] + fn test_conversion_mode_bits() { + let mut config = default_configuration(); + + config.conversion_mode = ConversionMode::Continuous; + assert_eq!((u16::from(&config) >> 10) & 0x3, 0b00); + + config.conversion_mode = ConversionMode::Shutdown; + assert_eq!((u16::from(&config) >> 10) & 0x3, 0b01); + + config.conversion_mode = ConversionMode::Oneshot; + assert_eq!((u16::from(&config) >> 10) & 0x3, 0b11); + } + + // --- From<u16> for Configuration --- + + #[test] + fn test_configuration_from_factory_reset_value() { + // 0x0220 = 0b0000_0010_0010_0000 + // bits 6:5 = 0b01 -> Avg8 + // bits 11:10 = 0b00 -> Continuous + let config = Configuration::from(0x0220); + assert_eq!(config.alert_pin_select, AlertPinSelect::Alert); + assert_eq!(config.alert_pin_polarity, PinPolarity::ActiveLow); + assert_eq!(config.mode, Mode::Alert); + assert_eq!( + config.conversion_averaging_mode, + ConversionAveragingMode::Avg8 + ); + assert_eq!(config.conversion_mode, ConversionMode::Continuous); + } + + #[test] + fn test_configuration_from_thermal_mode() { + let register: u16 = 1 << 4; + let config = Configuration::from(register); + assert_eq!(config.mode, Mode::Thermal); + } + + #[test] + fn test_configuration_from_shutdown_mode() { + let register: u16 = 0b01 << 10; + let config = Configuration::from(register); + assert_eq!(config.conversion_mode, ConversionMode::Shutdown); + } + + // --- Round-trip --- + + #[test] + fn test_round_trip_factory_reset() { + let original: u16 = 0x0220; + let config = Configuration::from(original); + let restored = u16::from(&config); + assert_eq!(original, restored); + } + + #[test] + fn test_round_trip_all_fields_set() { + let config = Configuration { + alert_pin_select: AlertPinSelect::DataReady, + alert_pin_polarity: PinPolarity::ActiveHigh, + mode: Mode::Thermal, + conversion_averaging_mode: ConversionAveragingMode::Avg64, + conversion_cycle: ConversionCycle::Seconds1, + conversion_mode: ConversionMode::Oneshot, + }; + let bits = u16::from(&config); + let restored = Configuration::from(bits); + assert_eq!(restored.alert_pin_select, AlertPinSelect::DataReady); + assert_eq!(restored.alert_pin_polarity, PinPolarity::ActiveHigh); + assert_eq!(restored.mode, Mode::Thermal); + assert_eq!( + restored.conversion_averaging_mode, + ConversionAveragingMode::Avg64 + ); + assert_eq!(restored.conversion_cycle, ConversionCycle::Seconds1); + assert_eq!(restored.conversion_mode, ConversionMode::Oneshot); + } + + #[test] + fn test_validate_valid_combinations() { + let mut config = default_configuration(); + + // None allows any cycle time + config.conversion_averaging_mode = ConversionAveragingMode::None; + config.conversion_cycle = ConversionCycle::Microseconds15500; + assert!(config.validate().is_ok()); + + // Avg8 allows 125ms and above + config.conversion_averaging_mode = ConversionAveragingMode::Avg8; + config.conversion_cycle = ConversionCycle::Milliseconds125; + assert!(config.validate().is_ok()); + + // Avg32 allows 500ms and above + config.conversion_averaging_mode = ConversionAveragingMode::Avg32; + config.conversion_cycle = ConversionCycle::Milliseconds500; + assert!(config.validate().is_ok()); + + // Avg64 allows 1s and above + config.conversion_averaging_mode = ConversionAveragingMode::Avg64; + config.conversion_cycle = ConversionCycle::Seconds1; + assert!(config.validate().is_ok()); + } + + #[test] + fn test_validate_invalid_combinations() { + let mut config = default_configuration(); + + // Avg8 does not allow 15.5ms + config.conversion_averaging_mode = ConversionAveragingMode::Avg8; + config.conversion_cycle = ConversionCycle::Microseconds15500; + assert!(config.validate().is_err()); + + // Avg32 does not allow 125ms or 250ms + config.conversion_averaging_mode = ConversionAveragingMode::Avg32; + config.conversion_cycle = ConversionCycle::Milliseconds125; + assert!(config.validate().is_err()); + + config.conversion_cycle = ConversionCycle::Milliseconds250; + assert!(config.validate().is_err()); + + // Avg64 does not allow anything below 1s + config.conversion_averaging_mode = ConversionAveragingMode::Avg64; + config.conversion_cycle = ConversionCycle::Milliseconds500; + assert!(config.validate().is_err()); + } +} |
