diff --git a/README.md b/README.md index b2c7910..60803b4 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ extremely accurate real-time clocks, based on the [`embedded-hal`] traits. This driver allows you to: - Read/write the seconds. - Read/write the minutes. +- Read/write the hours in 24h or AM/PM format. ## The devices diff --git a/src/ds323x/datetime.rs b/src/ds323x/datetime.rs index 16c5a60..0e25d23 100644 --- a/src/ds323x/datetime.rs +++ b/src/ds323x/datetime.rs @@ -1,9 +1,20 @@ //! Common implementation extern crate embedded_hal as hal; -use super::super::{ Ds323x, Register, Error }; +use super::super::{ Ds323x, Register, BitFlags, Error }; use interface::{ ReadRegister, WriteRegister }; +/// Hours in either 12-hour (AM/PM) or 24-hour format +#[derive(Debug, Clone, PartialEq)] +pub enum Hours { + /// AM [1-12] + AM(u8), + /// PM [1-12] + PM(u8), + /// 24H format [0-23] + H24(u8), +} + impl Ds323x where DI: ReadRegister @@ -18,6 +29,24 @@ where self.read_register_decimal(Register::MINUTES) } + /// Read the hours. + pub fn get_hours(&mut self) -> Result> { + let data = self.iface.read_register(Register::HOURS)?; + self.get_hours_from_register(data) + } + + fn get_hours_from_register(&self, data: u8) -> Result> { + if is_24h_format(data) { + Ok(Hours::H24(packed_bcd_to_decimal(data & !BitFlags::H24_H12))) + } + else if is_am(data) { + Ok(Hours::AM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM)))) + } + else { + Ok(Hours::PM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM)))) + } + } + fn read_register_decimal(&mut self, register: u8) -> Result> { let data = self.iface.read_register(register)?; Ok(packed_bcd_to_decimal(data)) @@ -48,11 +77,40 @@ where self.write_register_decimal(Register::MINUTES, minutes) } + /// Set the hours. + /// + /// Changes the operating mode to 12h/24h depending on the parameter. + /// + /// Will return an `Error::InvalidInputData` if the hours are out of range. + pub fn set_hours(&mut self, hours: Hours) -> Result<(), Error> { + let value = self.get_hours_register_value(&hours)?; + self.iface.write_register(Register::HOURS, value) + } + + fn get_hours_register_value(&mut self, hours: &Hours) -> Result> { + match *hours { + Hours::H24(h) if h > 23 => Err(Error::InvalidInputData), + Hours::H24(h) => Ok(decimal_to_packed_bcd(h)), + Hours::AM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData), + Hours::AM(h) => Ok(BitFlags::H24_H12 | decimal_to_packed_bcd(h)), + Hours::PM(h) if h < 1 || h > 12 => Err(Error::InvalidInputData), + Hours::PM(h) => Ok(BitFlags::H24_H12 | BitFlags::AM_PM | decimal_to_packed_bcd(h)), + } + } + fn write_register_decimal(&mut self, register: u8, decimal_number: u8) -> Result<(), Error> { self.iface.write_register(register, decimal_to_packed_bcd(decimal_number)) } } +fn is_24h_format(hours_data: u8) -> bool { + hours_data & BitFlags::H24_H12 == 0 +} + +fn is_am(hours_data: u8) -> bool { + hours_data & BitFlags::AM_PM == 0 +} + // Transforms a decimal number to packed BCD format fn decimal_to_packed_bcd(dec: u8) -> u8 { ((dec / 10) << 4) | (dec % 10) diff --git a/src/ds323x/mod.rs b/src/ds323x/mod.rs index cb3db97..6de57ea 100644 --- a/src/ds323x/mod.rs +++ b/src/ds323x/mod.rs @@ -1 +1,2 @@ mod datetime; +pub use self::datetime::Hours; diff --git a/src/lib.rs b/src/lib.rs index 693c6c9..e8b4578 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ //! This driver allows you to: //! - Read/write the seconds. //! - Read/write the minutes. +//! - Read/write the hours in 24h or AM/PM format. //! //! ## The devices //! @@ -179,6 +180,14 @@ struct Register; impl Register { const SECONDS : u8 = 0x00; const MINUTES : u8 = 0x01; + const HOURS : u8 = 0x02; +} + +struct BitFlags; + +impl BitFlags { + const H24_H12 : u8 = 0b0100_0000; + const AM_PM : u8 = 0b0010_0000; } const DEVICE_ADDRESS: u8 = 0b110_1000; @@ -265,3 +274,4 @@ where } mod ds323x; +pub use ds323x::Hours; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index cf10d14..6ff0efc 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -11,6 +11,7 @@ pub struct Register; impl Register { pub const SECONDS : u8 = 0x00; pub const MINUTES : u8 = 0x01; + pub const HOURS : u8 = 0x02; } pub struct DummyOutputPin; diff --git a/tests/ds323x.rs b/tests/ds323x.rs index ba0da06..71883ad 100644 --- a/tests/ds323x.rs +++ b/tests/ds323x.rs @@ -6,6 +6,8 @@ use hal::spi::Transaction as SpiTrans; mod common; use common::{ DEVICE_ADDRESS, Register, new_ds3231, new_ds3232, new_ds3234 }; +extern crate ds323x; +use ds323x::{ Hours }; mod seconds { use super::*; @@ -49,4 +51,25 @@ mod minutes { set_test!(can_set_ds3234, set_minutes, new_ds3234, 1, SpiTrans::write(vec![Register::MINUTES + 0x80, 1])); -} \ No newline at end of file + +mod hours { + use super::*; + get_test!(can_get_ds3231, get_hours, new_ds3231, Hours::H24(21), + I2cTrans::write_read(DEVICE_ADDRESS, vec![Register::HOURS], vec![0b0010_0001])); + + get_test!(can_get_ds3232, get_hours, new_ds3232, Hours::H24(21), + I2cTrans::write_read(DEVICE_ADDRESS, vec![Register::HOURS], vec![0b0010_0001])); + + get_test!(can_get_ds3234, get_hours, new_ds3234, Hours::H24(21), + SpiTrans::transfer(vec![Register::HOURS, 0], vec![Register::HOURS, 0b0010_0001])); + + + set_test!(can_set_ds3231, set_hours, new_ds3231, Hours::H24(21), + I2cTrans::write(DEVICE_ADDRESS, vec![Register::HOURS, 0b0010_0001])); + + set_test!(can_set_ds3232, set_hours, new_ds3232, Hours::H24(21), + I2cTrans::write(DEVICE_ADDRESS, vec![Register::HOURS, 0b0010_0001])); + + set_test!(can_set_ds3234, set_hours, new_ds3234, Hours::H24(21), + SpiTrans::write(vec![Register::HOURS + 0x80, 0b0010_0001])); +}