From 46d6656009430630b15086608d4579734cf28d02 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Sat, 3 Nov 2018 08:06:18 +0100 Subject: [PATCH] Add function to set the alarm1 to a date --- src/ds323x/alarms.rs | 71 +++++++++++++++++++++++++++++++ src/ds323x/mod.rs | 2 + src/lib.rs | 4 +- tests/alarms.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++ tests/common/mod.rs | 2 + 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/ds323x/alarms.rs create mode 100644 tests/alarms.rs diff --git a/src/ds323x/alarms.rs b/src/ds323x/alarms.rs new file mode 100644 index 0000000..6507275 --- /dev/null +++ b/src/ds323x/alarms.rs @@ -0,0 +1,71 @@ +//! Alarm support + +extern crate embedded_hal as hal; +use super::super::{ Ds323x, Hours, Register, BitFlags, Error }; +use interface::{ ReadData, WriteData }; +use super::{ decimal_to_packed_bcd, hours_to_register }; + +/// Parameters for setting Alarm1 on a date +#[derive(Debug, Clone, PartialEq)] +pub struct DateAlarm1 { + /// Date (day of month) [1-31] + pub date: u8, + /// Hour + pub hour: Hours, + /// Minute [0-59] + pub minute: u8, + /// Second [0-59] + pub second: u8 +} + +/// Alarm1 trigger rate +#[derive(Debug, Clone, PartialEq)] +pub enum Alarm1Matching { + /// Alarm once per second. + OncePerSecond, + /// Alarm when seconds match. + SecondsMatch, + /// Alarm when minutes and seconds match. + MinutesAndSecondsMatch, + /// Alarm when hours, minutes and seconds match. + HoursMinutesAndSecondsMatch, + /// Alarm when date/weekday, hours, minutes and seconds match. + AllMatch +} + + +fn get_matching_mask_alarm1(matching: Alarm1Matching) -> [u8; 4] { + const AM : u8 = BitFlags::ALARM_MATCH; + match matching { + Alarm1Matching::OncePerSecond => [AM, AM, AM, AM], + Alarm1Matching::SecondsMatch => [ 0, AM, AM, AM], + Alarm1Matching::MinutesAndSecondsMatch => [ 0, 0, AM, AM], + Alarm1Matching::HoursMinutesAndSecondsMatch => [ 0, 0, 0, AM], + Alarm1Matching::AllMatch => [ 0, 0, 0, 0], + } +} + + +impl Ds323x +where + DI: ReadData + WriteData +{ + /// Set Alarm1 for date (day of month). + /// + /// Will return an `Error::InvalidInputData` if any of the parameters is out of range. + pub fn set_alarm1_date(&mut self, when: DateAlarm1, matching: Alarm1Matching) -> Result<(), Error> { + if when.date < 1 || when.date > 31 || + when.minute > 59 || + when.second > 59 { + return Err(Error::InvalidInputData); + } + let match_mask = get_matching_mask_alarm1(matching); + let mut data = [ Register::ALARM1_SECONDS, + decimal_to_packed_bcd(when.second) | match_mask[0], + decimal_to_packed_bcd(when.minute) | match_mask[1], + hours_to_register(&when.hour)? | match_mask[2], + decimal_to_packed_bcd(when.date) | match_mask[3]]; + self.iface.write_data(&mut data) + } + +} diff --git a/src/ds323x/mod.rs b/src/ds323x/mod.rs index e1a33ff..62a7570 100644 --- a/src/ds323x/mod.rs +++ b/src/ds323x/mod.rs @@ -1,5 +1,7 @@ mod configuration; mod status; +mod alarms; +pub use self::alarms::{ DateAlarm1, Alarm1Matching }; mod datetime; pub use self::datetime::{ Hours, DateTime }; use super::{ BitFlags, Error }; diff --git a/src/lib.rs b/src/lib.rs index 120917b..9df2fe0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -447,6 +447,7 @@ impl Register { const AGING_OFFSET : u8 = 0x10; const TEMP_MSB : u8 = 0x11; const TEMP_CONV : u8 = 0x13; + const ALARM1_SECONDS : u8 = 0x07; } struct BitFlags; @@ -472,6 +473,7 @@ impl BitFlags { const ALARM2F : u8 = 0b0000_0010; const ALARM1F : u8 = 0b0000_0001; const TEMP_CONV_BAT : u8 = 0b0000_0001; + const ALARM_MATCH : u8 = 0b1000_0000; } const DEVICE_ADDRESS : u8 = 0b110_1000; @@ -498,7 +500,7 @@ pub struct Ds323x { pub mod interface; mod ds323x; -pub use ds323x::{ Hours, DateTime }; +pub use ds323x::{ Hours, DateTime, DateAlarm1, Alarm1Matching }; mod ds3231; mod ds3232; mod ds3234; diff --git a/tests/alarms.rs b/tests/alarms.rs new file mode 100644 index 0000000..f5e344b --- /dev/null +++ b/tests/alarms.rs @@ -0,0 +1,99 @@ +#[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, new_ds3231, + new_ds3232, new_ds3234, destroy_ds3231, destroy_ds3232, + destroy_ds3234, BitFlags as BF }; +extern crate ds323x; +use ds323x::{ DateAlarm1, Alarm1Matching as A1M, Hours, Error }; + +#[macro_export] +macro_rules! _set_invalid_alarm_test { + ($name:ident, $method:ident, $create_method:ident, $destroy_method:ident, $alarm:expr, $matching:expr) => { + #[test] + fn $name() { + let mut dev = $create_method(&[]); + assert_invalid_input_data!(dev.$method($alarm, $matching)); + $destroy_method(dev); + } + }; +} + +macro_rules! set_invalid_alarm_test { + ($name:ident, $method:ident, $alarm:expr, $matching:expr) => { + mod $name { + use super::*; + _set_invalid_alarm_test!(cannot_set_invalid_ds3231, $method, new_ds3231, destroy_ds3231, $alarm, $matching); + _set_invalid_alarm_test!(cannot_set_invalid_ds3232, $method, new_ds3232, destroy_ds3232, $alarm, $matching); + _set_invalid_alarm_test!(cannot_set_invalid_ds3234, $method, new_ds3234, destroy_ds3234, $alarm, $matching); + } + }; +} + +mod alarm1 { + use super::*; + set_invalid_alarm_test!(date_invalid_s, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::H24(1), minute: 1, second: 60 }, A1M::AllMatch); + set_invalid_alarm_test!(date_invalid_min, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::H24(1), minute: 60, second: 1 }, A1M::AllMatch); + set_invalid_alarm_test!(date_invalid_h, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::H24(24), minute: 1, second: 1 }, A1M::AllMatch); + set_invalid_alarm_test!(date_invalid_am1, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::AM(0), minute: 1, second: 1 }, A1M::AllMatch); + set_invalid_alarm_test!(date_invalid_am2, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::AM(13), minute: 1, second: 1 }, A1M::AllMatch); + set_invalid_alarm_test!(date_invalid_pm1, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::PM(0), minute: 1, second: 1 }, A1M::AllMatch); + set_invalid_alarm_test!(date_invalid_pm2, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::PM(13), minute: 1, second: 1 }, A1M::AllMatch); + set_invalid_alarm_test!(date_invalid_d1, set_alarm1_date, DateAlarm1{ date: 0, hour: Hours::H24(1), minute: 1, second: 1 }, A1M::AllMatch); + set_invalid_alarm_test!(date_invalid_d2, set_alarm1_date, DateAlarm1{ date: 32, hour: Hours::H24(1), minute: 1, second: 1 }, A1M::AllMatch); +} + +macro_rules! _set_values_test { + ($name:ident, $method:ident, $create_method:ident, $destroy_method:ident, $value1:expr, $value2:expr, $transactions:expr) => { + #[test] + fn $name() { + let trans = $transactions; + let mut dev = $create_method(&trans); + dev.$method($value1, $value2).unwrap(); + $destroy_method(dev); + } + }; +} + +macro_rules! set_values_test { + ($name:ident, $method:ident, $value1:expr, $value2:expr, $i2c_transactions:expr, $spi_transactions:expr) => { + mod $name { + use super::*; + _set_values_test!(can_set_ds3231, $method, new_ds3231, destroy_ds3231, $value1, $value2, $i2c_transactions); + _set_values_test!(can_set_ds3232, $method, new_ds3232, destroy_ds3232, $value1, $value2, $i2c_transactions); + _set_values_test!(can_set_ds3234, $method, new_ds3234, destroy_ds3234, $value1, $value2, $spi_transactions); + } + }; +} + +macro_rules! set_alarm_test { + ($name:ident, $method:ident, $alarm:expr, $matching:expr, $register:ident, [ $( $registers:expr ),+ ]) => { + set_values_test!($name, $method, $alarm, $matching, + [ I2cTrans::write(DEV_ADDR, vec![Register::$register, $( $registers ),*]) ], + [ SpiTrans::write(vec![Register::$register + 0x80, $( $registers ),*]) ]); + }; +} + +const AM : u8 = BF::ALARM_MATCH; + +mod alarm1_date { + use super::*; + set_alarm_test!(h24, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::H24(2), minute: 3, second: 4 }, A1M::AllMatch, + ALARM1_SECONDS, [4, 3, 2, 1]); + set_alarm_test!(am, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::AM(2), minute: 3, second: 4 }, A1M::AllMatch, + ALARM1_SECONDS, [4, 3, 0b0100_0010, 1]); + set_alarm_test!(pm, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::PM(2), minute: 3, second: 4 }, A1M::AllMatch, + ALARM1_SECONDS, [4, 3, 0b0110_0010, 1]); + + set_alarm_test!(match_hms, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::H24(2), minute: 3, second: 4 }, A1M::HoursMinutesAndSecondsMatch, + ALARM1_SECONDS, [ 4, 3, 2, AM | 1]); + set_alarm_test!(match_ms, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::H24(2), minute: 3, second: 4 }, A1M::MinutesAndSecondsMatch, + ALARM1_SECONDS, [ 4, 3, AM | 2, AM | 1]); + set_alarm_test!(match_s, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::H24(2), minute: 3, second: 4 }, A1M::SecondsMatch, + ALARM1_SECONDS, [ 4, AM | 3, AM | 2, AM | 1]); + set_alarm_test!(match_ops, set_alarm1_date, DateAlarm1{ date: 1, hour: Hours::H24(2), minute: 3, second: 4 }, A1M::OncePerSecond, + ALARM1_SECONDS, [AM | 4, AM | 3, AM | 2, AM | 1]); +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 3311c4a..7ec452c 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -27,6 +27,7 @@ impl Register { pub const AGING_OFFSET : u8 = 0x10; pub const TEMP_MSB : u8 = 0x11; pub const TEMP_CONV : u8 = 0x13; + pub const ALARM1_SECONDS : u8 = 0x07; } pub struct BitFlags; @@ -50,6 +51,7 @@ impl BitFlags { pub const ALARM2F : u8 = 0b0000_0010; pub const ALARM1F : u8 = 0b0000_0001; pub const TEMP_CONV_BAT : u8 = 0b0000_0001; + pub const ALARM_MATCH : u8 = 0b1000_0000; } pub struct DummyOutputPin;