From b70f4a6ffa5d677b1c9ff1a7b6f7d09d240c335d Mon Sep 17 00:00:00 2001
From: Diego Barrios Romero <eldruin@gmail.com>
Date: Wed, 31 Oct 2018 10:37:42 +0100
Subject: [PATCH] Add functions to set the temperature conversion rate

---
 README.md           |  1 +
 src/ds3232.rs       | 20 +++++++++++++++++++-
 src/ds3234.rs       | 20 +++++++++++++++++++-
 src/lib.rs          | 34 ++++++++++++++++++++++++++++++++++
 tests/common/mod.rs |  2 ++
 tests/ds3232_4.rs   | 32 ++++++++++++++++++++++++++++++++
 6 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index e0c3a9a..6388cd8 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ This driver allows you to:
 - Enable and disable the square-wave generation. See `enable_square_wave`.
 - Select the square-wave frequency. See `set_square_wave_frequency`.
 - Enable and disable the 32kHz output when battery powered. See `enable_32khz_output_on_battery`.
+- Set the temperature conversion rate. See `set_temperature_conversion_rate`.
 
 ## The devices
 
diff --git a/src/ds3232.rs b/src/ds3232.rs
index 434a280..22232dc 100644
--- a/src/ds3232.rs
+++ b/src/ds3232.rs
@@ -2,7 +2,7 @@
 
 extern crate embedded_hal as hal;
 use hal::blocking;
-use super::{ Ds323x, BitFlags, Error, ic };
+use super::{ Ds323x, TempConvRate, BitFlags, Error, ic };
 use interface::I2cInterface;
 
 impl<I2C, E> Ds323x<I2cInterface<I2C>, ic::DS3232>
@@ -30,4 +30,22 @@ where
         let status = self.status & !BitFlags::BB32KHZ;
         self.write_status_without_clearing_alarm(status)
     }
+
+    /// Set the temperature conversion rate.
+    ///
+    /// Set how often the temperature is measured and applies compensation to
+    /// the oscillator. This can be used to reduce power consumption but sudden
+    /// temperature changes will not be compensated for.
+    ///
+    /// Note: This is only available for DS3232 and DS3234 devices.
+    pub fn set_temperature_conversion_rate(&mut self, rate: TempConvRate) -> Result<(), Error<E>> {
+        let status;
+        match rate {
+            TempConvRate::_64s  => status = self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
+            TempConvRate::_128s => status = self.status & !BitFlags::CRATE1 |  BitFlags::CRATE0,
+            TempConvRate::_256s => status = self.status |  BitFlags::CRATE1 & !BitFlags::CRATE0,
+            TempConvRate::_512s => status = self.status |  BitFlags::CRATE1 |  BitFlags::CRATE0,
+        }
+        self.write_status_without_clearing_alarm(status)
+    }
 }
diff --git a/src/ds3234.rs b/src/ds3234.rs
index f406afd..07b3a96 100644
--- a/src/ds3234.rs
+++ b/src/ds3234.rs
@@ -2,7 +2,7 @@
 
 extern crate embedded_hal as hal;
 use hal::blocking;
-use super::{ Ds323x, BitFlags, Error, ic };
+use super::{ Ds323x, TempConvRate, BitFlags, Error, ic };
 use interface::SpiInterface;
 
 impl<SPI, CS, E> Ds323x<SpiInterface<SPI, CS>, ic::DS3234>
@@ -31,4 +31,22 @@ where
         let status = self.status & !BitFlags::BB32KHZ;
         self.write_status_without_clearing_alarm(status)
     }
+
+    /// Set the temperature conversion rate.
+    ///
+    /// Set how often the temperature is measured and applies compensation to
+    /// the oscillator. This can be used to reduce power consumption but sudden
+    /// temperature changes will not be compensated for.
+    ///
+    /// Note: This is only available for DS3232 and DS3234 devices.
+    pub fn set_temperature_conversion_rate(&mut self, rate: TempConvRate) -> Result<(), Error<E>> {
+        let status;
+        match rate {
+            TempConvRate::_64s  => status = self.status & !BitFlags::CRATE1 & !BitFlags::CRATE0,
+            TempConvRate::_128s => status = self.status & !BitFlags::CRATE1 |  BitFlags::CRATE0,
+            TempConvRate::_256s => status = self.status |  BitFlags::CRATE1 & !BitFlags::CRATE0,
+            TempConvRate::_512s => status = self.status |  BitFlags::CRATE1 |  BitFlags::CRATE0,
+        }
+        self.write_status_without_clearing_alarm(status)
+    }
 }
diff --git a/src/lib.rs b/src/lib.rs
index bfa358f..9a34f89 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,6 +18,7 @@
 //! - Enable and disable the square-wave generation. See [`enable_square_wave`].
 //! - Select the square-wave frequency. See [`set_square_wave_frequency`].
 //! - Enable and disable the 32kHz output when battery powered. See [`enable_32khz_output_on_battery`].
+//! - Set the temperature conversion rate. See [`set_temperature_conversion_rate`].
 //!
 //! [`get_datetime`]: struct.Ds323x.html#method.get_datetime
 //! [`get_year`]: struct.Ds323x.html#method.get_year
@@ -33,6 +34,7 @@
 //! [`enable_square_wave`]: Struct.Ds323x.html#method.enable_square_wave
 //! [`set_square_wave_frequency`]: Struct.Ds323x.html#method.set_square_wave_frequency
 //! [`enable_32khz_output_on_battery`]: Struct.Ds323x.html#method.enable_32khz_output_on_battery
+//! [`set_temperature_conversion_rate`]: Struct.Ds323x.html#method.set_temperature_conversion_rate
 //!
 //! ## The devices
 //!
@@ -361,6 +363,21 @@
 //! # }
 //! ```
 //!
+//! ### Set the temperature conversion rate to once every 128 seconds
+//!
+//! This is only available for the devices DS3232 and DS3234.
+//!
+//! ```no_run
+//! extern crate linux_embedded_hal as hal;
+//! extern crate ds323x;
+//! use ds323x::{ Ds323x, TempConvRate };
+//!
+//! # fn main() {
+//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
+//! let mut rtc = Ds323x::new_ds3232(dev);
+//! rtc.set_temperature_conversion_rate(TempConvRate::_128s).unwrap();
+//! # }
+//! ```
 
 #![deny(unsafe_code)]
 #![deny(missing_docs)]
@@ -393,6 +410,21 @@ pub enum SqWFreq {
     _8_192Hz,
 }
 
+/// Temperature conversion rate
+///
+/// This is only available on the DS3232 and DS3234 devices.
+#[derive(Debug, Clone, PartialEq)]
+pub enum TempConvRate {
+    /// Once every 64 seconds
+    _64s,
+    /// Once every 128 seconds
+    _128s,
+    /// Once every 256 seconds
+    _256s,
+    /// Once every 512 seconds
+    _512s,
+}
+
 struct Register;
 
 impl Register {
@@ -423,6 +455,8 @@ impl BitFlags {
     const INTCN      : u8 = 0b0000_0100;
     const OSC_STOP   : u8 = 0b1000_0000;
     const BB32KHZ    : u8 = 0b0100_0000;
+    const CRATE1     : u8 = 0b0010_0000;
+    const CRATE0     : u8 = 0b0001_0000;
     const EN32KHZ    : u8 = 0b0000_1000;
     const BUSY       : u8 = 0b0000_0100;
     const ALARM2F    : u8 = 0b0000_0010;
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
index 43c876d..f82d05c 100644
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -40,6 +40,8 @@ impl BitFlags {
     pub const INTCN      : u8 = 0b0000_0100;
     pub const OSC_STOP   : u8 = 0b1000_0000;
     pub const BB32KHZ    : u8 = 0b0100_0000;
+    pub const CRATE1     : u8 = 0b0010_0000;
+    pub const CRATE0     : u8 = 0b0001_0000;
     pub const EN32KHZ    : u8 = 0b0000_1000;
     pub const BUSY       : u8 = 0b0000_0100;
     pub const ALARM2F    : u8 = 0b0000_0010;
diff --git a/tests/ds3232_4.rs b/tests/ds3232_4.rs
index 0f2b170..4da3273 100644
--- a/tests/ds3232_4.rs
+++ b/tests/ds3232_4.rs
@@ -4,6 +4,9 @@ extern crate embedded_hal_mock as hal;
 use hal::i2c::Transaction as I2cTrans;
 use hal::spi::Transaction as SpiTrans;
 
+extern crate ds323x;
+use ds323x::TempConvRate;
+
 #[allow(unused)]
 mod common;
 use common::{ DEVICE_ADDRESS as DEV_ADDR, Register,
@@ -22,6 +25,26 @@ macro_rules! call_method_status_test {
     };
 }
 
+#[macro_export]
+macro_rules! _set_param_test_2_4 {
+    ($name:ident, $method:ident, $value:expr, $i2c_transactions:expr, $spi_transactions:expr) => {
+        mod $name {
+            use super::*;
+            set_test!(can_set_ds3232, $method, new_ds3232, destroy_ds3232, $value, $i2c_transactions);
+            set_test!(can_set_ds3234, $method, new_ds3234, destroy_ds3234, $value, $spi_transactions);
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! set_param_test_2_4 {
+    ($name:ident, $method:ident, $register:ident, $value:expr, $binary_value:expr) => {
+        _set_param_test_2_4!($name, $method, $value,
+            [ I2cTrans::write(DEV_ADDR, vec![Register::$register, $binary_value]) ],
+            [ SpiTrans::write(vec![Register::$register + 0x80, $binary_value]) ]);
+    };
+}
+
 #[test]
 fn can_create_and_destroy_ds3232() {
     let dev = new_ds3232(&[]);
@@ -41,3 +64,12 @@ call_method_status_test!(can_en_32khz_bat, enable_32khz_output_on_battery,
 call_method_status_test!(can_dis_32khz_bat, disable_32khz_output_on_battery,
     DEFAULT_WRITE_STATUS & !BF::BB32KHZ);
 
+set_param_test_2_4!(can_set_cr_64s, set_temperature_conversion_rate, STATUS, TempConvRate::_64s,
+    DEFAULT_WRITE_STATUS & !BF::CRATE1 & !BF::CRATE0);
+set_param_test_2_4!(can_set_cr_128s, set_temperature_conversion_rate, STATUS, TempConvRate::_128s,
+    DEFAULT_WRITE_STATUS & !BF::CRATE1 |  BF::CRATE0);
+set_param_test_2_4!(can_set_cr_256s, set_temperature_conversion_rate, STATUS, TempConvRate::_256s,
+    DEFAULT_WRITE_STATUS |  BF::CRATE1 & !BF::CRATE0);
+set_param_test_2_4!(can_set_cr_512s, set_temperature_conversion_rate, STATUS, TempConvRate::_512s,
+    DEFAULT_WRITE_STATUS |  BF::CRATE1 |  BF::CRATE0);
+