summaryrefslogtreecommitdiff
path: root/tmp117/src/conf.rs
diff options
context:
space:
mode:
authorunitexe <unitexe70@gmail.com>2026-04-08 23:53:17 -0500
committerunitexe <unitexe70@gmail.com>2026-04-08 23:54:28 -0500
commitde3f7ed45185f3a678ba0de04d98cc9ac92de0c8 (patch)
tree3db0159c1370f240d42c1e8b89a67247538f43d1 /tmp117/src/conf.rs
Initial commit
Diffstat (limited to 'tmp117/src/conf.rs')
-rw-r--r--tmp117/src/conf.rs547
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());
+ }
+}