//! Common implementation extern crate embedded_hal as hal; use super::super::{ Ds323x, Register, BitFlags, Error }; use interface::{ ReadData, WriteData }; /// Date and time #[derive(Debug, Clone, PartialEq)] pub struct DateTime { /// Year [2000-2099] pub year : u16, /// Month [1-12] pub month : u8, /// Day [1-31] pub day : u8, /// Weekday [1-7] pub weekday : u8, /// Hour in 24h/12h format pub hour : Hours, /// Minute [0-59] pub minute : u8, /// Second [0-59] pub second : u8, } /// 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<DI, IC, E> Ds323x<DI, IC> where DI: ReadData<Error = E> + WriteData<Error = E> { /// Read the seconds. pub fn get_seconds(&mut self) -> Result<u8, Error<E>> { self.read_register_decimal(Register::SECONDS) } /// Read the minutes. pub fn get_minutes(&mut self) -> Result<u8, Error<E>> { self.read_register_decimal(Register::MINUTES) } /// Read the hours. pub fn get_hours(&mut self) -> Result<Hours, Error<E>> { let data = self.iface.read_register(Register::HOURS)?; Ok(hours_from_register(data)) } /// Read the day of the week [1-7]. pub fn get_weekday(&mut self) -> Result<u8, Error<E>> { self.read_register_decimal(Register::DOW) } /// Read the day of the month [1-31]. pub fn get_day(&mut self) -> Result<u8, Error<E>> { self.read_register_decimal(Register::DOM) } /// Read the month [1-12]. pub fn get_month(&mut self) -> Result<u8, Error<E>> { let data = self.iface.read_register(Register::MONTH)?; let value = data & !BitFlags::CENTURY; Ok(packed_bcd_to_decimal(value)) } /// Read the year [2000-2100]. pub fn get_year(&mut self) -> Result<u16, Error<E>> { let mut data = [0; 3]; data[0] = Register::MONTH; self.iface.read_data(&mut data)?; Ok(year_from_registers(data[1], data[2])) } /// Read the date and time. pub fn get_datetime(&mut self) -> Result<DateTime, Error<E>> { let mut data = [0; 8]; self.iface.read_data(&mut data)?; Ok(DateTime { year: year_from_registers(data[Register::MONTH as usize + 1], data[Register::YEAR as usize + 1]), month: packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY), day: packed_bcd_to_decimal(data[Register::DOM as usize + 1]), weekday: packed_bcd_to_decimal(data[Register::DOW as usize + 1]), hour: hours_from_register(data[Register::HOURS as usize + 1]), minute: packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]), second: packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]) }) } fn read_register_decimal(&mut self, register: u8) -> Result<u8, Error<E>> { let data = self.iface.read_register(register)?; Ok(packed_bcd_to_decimal(data)) } /// Set the seconds [0-59]. /// /// Will return an `Error::InvalidInputData` if the seconds are out of range. pub fn set_seconds(&mut self, seconds: u8) -> Result<(), Error<E>> { if seconds > 59 { return Err(Error::InvalidInputData); } self.write_register_decimal(Register::SECONDS, seconds) } /// Set the minutes [0-59]. /// /// Will return an `Error::InvalidInputData` if the minutes are out of range. pub fn set_minutes(&mut self, minutes: u8) -> Result<(), Error<E>> { if minutes > 59 { return Err(Error::InvalidInputData); } 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<E>> { 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<u8, Error<E>> { 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)), } } /// Set the day of week [1-7]. /// /// Will return an `Error::InvalidInputData` if the day is out of range. pub fn set_weekday(&mut self, weekday: u8) -> Result<(), Error<E>> { if weekday < 1 || weekday > 7 { return Err(Error::InvalidInputData); } self.iface.write_register(Register::DOW, weekday) } /// Set the day of month [1-31]. /// /// Will return an `Error::InvalidInputData` if the day is out of range. pub fn set_day(&mut self, day: u8) -> Result<(), Error<E>> { if day < 1 || day > 7 { return Err(Error::InvalidInputData); } self.iface.write_register(Register::DOM, day) } /// Set the month [1-12]. /// /// Will return an `Error::InvalidInputData` if the month is out of range. pub fn set_month(&mut self, month: u8) -> Result<(), Error<E>> { if month < 1 || month > 12 { return Err(Error::InvalidInputData); } // keep the century bit let data = self.iface.read_register(Register::MONTH)?; let value = (data & BitFlags::CENTURY) | decimal_to_packed_bcd(month); self.iface.write_register(Register::MONTH, value) } /// Set the year [2000-2100]. /// /// Will return an `Error::InvalidInputData` if the year is out of range. pub fn set_year(&mut self, year: u16) -> Result<(), Error<E>> { if year < 2000 || year > 2100 { return Err(Error::InvalidInputData); } let data = self.iface.read_register(Register::MONTH)?; let month_bcd = data & !BitFlags::CENTURY; if year > 2099 { let mut data = [ Register::MONTH, BitFlags::CENTURY | month_bcd, decimal_to_packed_bcd((year - 2100) as u8) ]; self.iface.write_data(&mut data) } else { let mut data = [ Register::MONTH, month_bcd, decimal_to_packed_bcd((year - 2000) as u8) ]; self.iface.write_data(&mut data) } } /// Set the date and time. /// /// Will return an `Error::InvalidInputData` if any of the parameters is out of range. pub fn set_datetime(&mut self, datetime: &DateTime) -> Result<(), Error<E>> { if datetime.year < 2000 || datetime.year > 2100 || datetime.month < 1 || datetime.month > 12 || datetime.day < 1 || datetime.day > 31 || datetime.weekday < 1 || datetime.weekday > 7 || datetime.minute > 59 || datetime.second > 59 { return Err(Error::InvalidInputData); } let (month, year) = month_year_to_registers(datetime.month, datetime.year); let mut payload = [Register::SECONDS, decimal_to_packed_bcd(datetime.second), decimal_to_packed_bcd(datetime.minute), self.get_hours_register_value(&datetime.hour)?, decimal_to_packed_bcd(datetime.weekday), decimal_to_packed_bcd(datetime.day), month, year]; self.iface.write_data(&mut payload) } fn write_register_decimal(&mut self, register: u8, decimal_number: u8) -> Result<(), Error<E>> { self.iface.write_register(register, decimal_to_packed_bcd(decimal_number)) } } fn hours_from_register(data: u8) -> Hours { if is_24h_format(data) { Hours::H24(packed_bcd_to_decimal(data & !BitFlags::H24_H12)) } else if is_am(data) { Hours::AM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM))) } else { Hours::PM(packed_bcd_to_decimal(data & !(BitFlags::H24_H12 | BitFlags::AM_PM))) } } fn year_from_registers(month: u8, year: u8) -> u16 { let century = month & BitFlags::CENTURY; let year = packed_bcd_to_decimal(year); if century != 0 { 2100 + (year as u16) } else { 2000 + (year as u16) } } fn month_year_to_registers(month: u8, year: u16) -> (u8, u8) { if year > 2099 { let month = BitFlags::CENTURY | decimal_to_packed_bcd(month); (month, decimal_to_packed_bcd((year - 2100) as u8)) } else { (decimal_to_packed_bcd(month), decimal_to_packed_bcd((year - 2000) as u8)) } } 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) } // Transforms a number in packed BCD format to decimal fn packed_bcd_to_decimal(bcd: u8) -> u8 { (bcd >> 4) * 10 + (bcd & 0xF) } #[cfg(test)] mod tests { use super::*; #[test] fn can_convert_packed_bcd_to_decimal() { assert_eq!(0, packed_bcd_to_decimal(0b0000_0000)); assert_eq!(1, packed_bcd_to_decimal(0b0000_0001)); assert_eq!(9, packed_bcd_to_decimal(0b0000_1001)); assert_eq!(10, packed_bcd_to_decimal(0b0001_0000)); assert_eq!(11, packed_bcd_to_decimal(0b0001_0001)); assert_eq!(19, packed_bcd_to_decimal(0b0001_1001)); assert_eq!(20, packed_bcd_to_decimal(0b0010_0000)); assert_eq!(21, packed_bcd_to_decimal(0b0010_0001)); assert_eq!(59, packed_bcd_to_decimal(0b0101_1001)); } #[test] fn can_convert_decimal_to_packed_bcd() { assert_eq!(0b0000_0000, decimal_to_packed_bcd( 0)); assert_eq!(0b0000_0001, decimal_to_packed_bcd( 1)); assert_eq!(0b0000_1001, decimal_to_packed_bcd( 9)); assert_eq!(0b0001_0000, decimal_to_packed_bcd(10)); assert_eq!(0b0001_0001, decimal_to_packed_bcd(11)); assert_eq!(0b0001_1001, decimal_to_packed_bcd(19)); assert_eq!(0b0010_0000, decimal_to_packed_bcd(20)); assert_eq!(0b0010_0001, decimal_to_packed_bcd(21)); assert_eq!(0b0101_1001, decimal_to_packed_bcd(59)); } }