From 6e477fa5bf5c748942e95d1dc60c5acd345f41ff Mon Sep 17 00:00:00 2001
From: Diego Barrios Romero <eldruin@gmail.com>
Date: Sun, 28 Oct 2018 20:17:36 +0100
Subject: [PATCH] Add function to enable/disable the oscillator

---
 README.md                   | 10 +++-----
 src/ds323x/configuration.rs | 32 ++++++++++++++++++++++++
 src/ds323x/mod.rs           |  1 +
 src/lib.rs                  | 12 ++++-----
 tests/common/mod.rs         | 21 ++++++++++++++++
 tests/configuration.rs      | 50 +++++++++++++++++++++++++++++++++++++
 6 files changed, 112 insertions(+), 14 deletions(-)
 create mode 100644 src/ds323x/configuration.rs
 create mode 100644 tests/configuration.rs

diff --git a/README.md b/README.md
index 84ff483..774d85f 100644
--- a/README.md
+++ b/README.md
@@ -6,13 +6,9 @@ extremely accurate real-time clocks, based on the [`embedded-hal`] traits.
 [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
 
 This driver allows you to:
-- Read/write the seconds.
-- Read/write the minutes.
-- Read/write the hours in 24h or AM/PM format.
-- Read/write the weekday.
-- Read/write the day.
-- Read/write the month.
-- Read/write the year.
+- Read and set date and time in 12-hour and 24-hour format. See: `get_datetime`.
+- Read and set date and time elements. For example, see: `get_year`.
+- Enable and disable the real-time clock. See: `enable`.
 
 ## The devices
 
diff --git a/src/ds323x/configuration.rs b/src/ds323x/configuration.rs
new file mode 100644
index 0000000..8d53048
--- /dev/null
+++ b/src/ds323x/configuration.rs
@@ -0,0 +1,32 @@
+//! Device configuration
+
+extern crate embedded_hal as hal;
+use super::super::{ Ds323x, Register, BitFlags, Error };
+use interface::{ ReadData, WriteData };
+
+impl<DI, IC, E> Ds323x<DI, IC>
+where
+    DI: ReadData<Error = E> + WriteData<Error = E>
+{
+    /// Enable the oscillator (set the clock running).
+    ///
+    /// (Does not alter the device register if already running).
+    pub fn enable(&mut self) -> Result<(), Error<E>> {
+        let control = self.iface.read_register(Register::CONTROL)?;
+        if (control & BitFlags::EOSC) != 0 {
+            self.iface.write_register(Register::CONTROL, control & !BitFlags::EOSC)?;
+        }
+        Ok(())
+    }
+
+    /// Disable the oscillator (stops the clock).
+    ///
+    /// (Does not alter the device register if already stopped).
+    pub fn disable(&mut self) -> Result<(), Error<E>> {
+        let control = self.iface.read_register(Register::CONTROL)?;
+        if (control & BitFlags::EOSC) == 0 {
+            self.iface.write_register(Register::CONTROL, control | BitFlags::EOSC)?;
+        }
+        Ok(())
+    }
+}
diff --git a/src/ds323x/mod.rs b/src/ds323x/mod.rs
index aae4efd..4a7a4bb 100644
--- a/src/ds323x/mod.rs
+++ b/src/ds323x/mod.rs
@@ -1,2 +1,3 @@
+mod configuration;
 mod datetime;
 pub use self::datetime::{ Hours, DateTime };
diff --git a/src/lib.rs b/src/lib.rs
index 9fe6c5e..6d1d095 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,13 +4,9 @@
 //! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
 //!
 //! This driver allows you to:
-//! - Read/write the seconds.
-//! - Read/write the minutes.
-//! - Read/write the hours in 24h or AM/PM format.
-//! - Read/write the weekday.
-//! - Read/write the day.
-//! - Read/write the month.
-//! - Read/write the year.
+//! - Read and set date and time in 12-hour and 24-hour format. See: `get_datetime`.
+//! - Read and set date and time elements. For example, see: `get_year`.
+//! - Enable and disable the real-time clock. See: `enable`.
 //!
 //! ## The devices
 //!
@@ -189,6 +185,7 @@ impl Register {
     const DOM       : u8 = 0x04;
     const MONTH     : u8 = 0x05;
     const YEAR      : u8 = 0x06;
+    const CONTROL   : u8 = 0x0E;
 }
 
 struct BitFlags;
@@ -197,6 +194,7 @@ impl BitFlags {
     const H24_H12    : u8 = 0b0100_0000;
     const AM_PM      : u8 = 0b0010_0000;
     const CENTURY    : u8 = 0b1000_0000;
+    const EOSC       : u8 = 0b1000_0000;
 }
 
 const DEVICE_ADDRESS: u8 = 0b110_1000;
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
index e9d8be4..a7d1c68 100644
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -8,6 +8,7 @@ pub const DEVICE_ADDRESS: u8 = 0b110_1000;
 
 pub struct Register;
 
+#[allow(unused)]
 impl Register {
     pub const SECONDS   : u8 = 0x00;
     pub const MINUTES   : u8 = 0x01;
@@ -15,6 +16,14 @@ impl Register {
     pub const DOW       : u8 = 0x03;
     pub const DOM       : u8 = 0x04;
     pub const MONTH     : u8 = 0x05;
+    pub const CONTROL   : u8 = 0x0E;
+}
+
+pub struct BitFlags;
+
+#[allow(unused)]
+impl BitFlags {
+    pub const EOSC       : u8 = 0b1000_0000;
 }
 
 pub struct DummyOutputPin;
@@ -75,3 +84,15 @@ macro_rules! set_invalid_test {
         }
     };
 }
+
+#[macro_export]
+macro_rules! call_test {
+    ($name:ident, $method:ident, $create_method:ident, $transactions:expr) => {
+        #[test]
+        fn $name() {
+            let trans = $transactions;
+            let mut dev = $create_method(&trans);
+            dev.$method().unwrap();
+        }
+    };
+}
diff --git a/tests/configuration.rs b/tests/configuration.rs
new file mode 100644
index 0000000..1023ffd
--- /dev/null
+++ b/tests/configuration.rs
@@ -0,0 +1,50 @@
+#[deny(warnings)]
+
+extern crate embedded_hal_mock as hal;
+use hal::i2c::Transaction as I2cTrans;
+use hal::spi::Transaction as SpiTrans;
+mod common;
+use common::{ DEVICE_ADDRESS as DEV_ADDR, Register as Reg, new_ds3231,
+              new_ds3232, new_ds3234, BitFlags as BF };
+
+macro_rules! call_method_test {
+    ($method:ident, $i2c_transactions:expr, $spi_transactions:expr) => {
+        call_test!(can_call_ds3231, $method, new_ds3231, $i2c_transactions);
+        call_test!(can_call_ds3232, $method, new_ds3232, $i2c_transactions);
+        call_test!(can_call_ds3234, $method, new_ds3234, $spi_transactions);
+    };
+}
+
+mod do_nothing_if_already_enabled {
+    use super::*;
+    call_method_test!(enable,
+        [ I2cTrans::write_read(DEV_ADDR, vec![Reg::CONTROL], vec![0]) ],
+        [ SpiTrans::transfer(vec![Reg::CONTROL, 0], vec![Reg::CONTROL, 0]) ]);
+}
+
+mod enable {
+    use super::*;
+    call_method_test!(enable,
+        [ I2cTrans::write_read(DEV_ADDR, vec![Reg::CONTROL], vec![BF::EOSC | 0b0101_1010]),
+          I2cTrans::write(DEV_ADDR, vec![Reg::CONTROL, 0b0101_1010]) ],
+
+        [ SpiTrans::transfer(vec![Reg::CONTROL, 0], vec![Reg::CONTROL, BF::EOSC | 0b0101_1010]),
+          SpiTrans::write(vec![Reg::CONTROL + 0x80, 0b0101_1010]) ]);
+}
+
+mod do_nothing_if_already_disabled {
+    use super::*;
+    call_method_test!(disable,
+        [ I2cTrans::write_read(DEV_ADDR, vec![Reg::CONTROL], vec![BF::EOSC]) ],
+        [ SpiTrans::transfer(vec![Reg::CONTROL, 0], vec![Reg::CONTROL, BF::EOSC]) ]);
+}
+
+mod disable {
+    use super::*;
+    call_method_test!(disable,
+        [ I2cTrans::write_read(DEV_ADDR, vec![Reg::CONTROL], vec![0b0101_1010]),
+          I2cTrans::write(DEV_ADDR, vec![Reg::CONTROL, BF::EOSC | 0b0101_1010]) ],
+
+        [ SpiTrans::transfer(vec![Reg::CONTROL, 0], vec![Reg::CONTROL, 0b0101_1010]),
+          SpiTrans::write(vec![Reg::CONTROL + 0x80, BF::EOSC | 0b0101_1010]) ]);
+}