ds323x-rs/src/ds323x/alarms.rs

341 lines
12 KiB
Rust

//! Alarm support
use super::super::{BitFlags, Ds323x, Error, Hours, Register};
use super::{decimal_to_packed_bcd, hours_to_register};
use crate::ds323x::{NaiveTime, Timelike};
use interface::{ReadData, WriteData};
/// Parameters for setting Alarm1 on a day of the month
///
/// Depending on the matching strategy, some fields may not be relevant. In this
/// case, invalid values are ignored and basic values are used instead to
/// configure the alarm:
/// - second, minute and hour : 0
/// - day: 1
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DayAlarm1 {
/// Day of the month [1-31]
pub day: u8,
/// Hour
pub hour: Hours,
/// Minute [0-59]
pub minute: u8,
/// Second [0-59]
pub second: u8,
}
/// Parameters for setting Alarm1 on a weekday
///
/// Depending on the matching strategy, some fields may not be relevant. In this
/// case, invalid values are ignored and basic values are used instead to
/// configure the alarm:
/// - second, minute and hour : 0
/// - weekday: 1
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WeekdayAlarm1 {
/// Weekday [1-7]
pub weekday: u8,
/// Hour
pub hour: Hours,
/// Minute [0-59]
pub minute: u8,
/// Second [0-59]
pub second: u8,
}
/// Alarm1 trigger rate
#[derive(Debug, Clone, Copy, 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,
}
/// Parameters for setting Alarm2 on a day of the month
///
/// Depending on the matching strategy, some fields may not be relevant. In this
/// case, invalid values are ignored and basic values are used instead to
/// configure the alarm:
/// - minute and hour : 0
/// - day: 1
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DayAlarm2 {
/// Day of month [1-31]
pub day: u8,
/// Hour
pub hour: Hours,
/// Minute [0-59]
pub minute: u8,
}
/// Parameters for setting Alarm2 on a weekday
///
/// Depending on the matching strategy, some fields may not be relevant. In this
/// case, invalid values are ignored and basic values are used instead to
/// configure the alarm:
/// - minute and hour : 0
/// - weekday: 1
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WeekdayAlarm2 {
/// Weekday [1-7]
pub weekday: u8,
/// Hour
pub hour: Hours,
/// Minute [0-59]
pub minute: u8,
}
/// Alarm2 trigger rate
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Alarm2Matching {
/// Alarm once per minute. (00 seconds of every minute)
OncePerMinute,
/// Alarm when minutes match.
MinutesMatch,
/// Alarm when hours and minutes match.
HoursAndMinutesMatch,
/// Alarm when date/weekday, hours and minutes 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],
}
}
fn get_matching_mask_alarm2(matching: Alarm2Matching) -> [u8; 3] {
const AM: u8 = BitFlags::ALARM_MATCH;
match matching {
Alarm2Matching::OncePerMinute => [AM, AM, AM],
Alarm2Matching::MinutesMatch => [0, AM, AM],
Alarm2Matching::HoursAndMinutesMatch => [0, 0, AM],
Alarm2Matching::AllMatch => [0, 0, 0],
}
}
/// Test if hours value is valid
///
/// Will return true if valid, false if not
fn is_hour_valid(hours: Hours) -> bool {
match hours {
Hours::H24(h) if h > 23 => true,
Hours::AM(h) if h < 1 || h > 12 => true,
Hours::PM(h) if h < 1 || h > 12 => true,
_ => false,
}
}
/// Relax the hour value by changing an incorrect value by 0
fn relax_hour(hours: Hours) -> Hours {
match hours {
Hours::H24(h) if h > 23 => Hours::H24(0),
Hours::H24(h) => Hours::H24(h),
Hours::AM(h) if h < 1 || h > 12 => Hours::AM(1),
Hours::AM(h) => Hours::AM(h),
Hours::PM(h) if h < 1 || h > 12 => Hours::PM(1),
Hours::PM(h) => Hours::PM(h),
}
}
impl<DI, IC, CommE, PinE> Ds323x<DI, IC>
where
DI: ReadData<Error = Error<CommE, PinE>> + WriteData<Error = Error<CommE, PinE>>,
{
/// Set Alarm1 for day of the month.
///
/// Will return an `Error::InvalidInputData` if any of the used parameters
/// (depending on the matching startegy) is out of range. Any unused
/// parameter is set to basic valid value:
/// - second, minute, hour: 0
/// - day: 1
pub fn set_alarm1_day(
&mut self,
when: DayAlarm1,
matching: Alarm1Matching,
) -> Result<(), Error<CommE, PinE>> {
let day_invalid = when.day < 1 || when.day > 31;
let hour_invalid = is_hour_valid(when.hour);
let minute_invalid = when.minute > 59;
let second_invalid = when.second > 59;
let day = if day_invalid { 1 } else { when.day };
let hour = relax_hour(when.hour);
let minute = if minute_invalid { 0 } else { when.minute };
if (matching == Alarm1Matching::AllMatch && (day_invalid || hour_invalid))
|| (hour_invalid && matching == Alarm1Matching::HoursMinutesAndSecondsMatch)
|| ((matching != Alarm1Matching::SecondsMatch
&& matching != Alarm1Matching::OncePerSecond)
&& minute_invalid)
|| second_invalid
{
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(minute) | match_mask[1],
hours_to_register(hour)? | match_mask[2],
decimal_to_packed_bcd(day) | match_mask[3],
];
self.iface.write_data(&mut data)
}
/// Set Alarm1 for a time (fires when hours, minutes and seconds match).
///
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
/// day is not used by the matching strategy but is set to 1.
pub fn set_alarm1_hms(&mut self, when: NaiveTime) -> Result<(), Error<CommE, PinE>> {
let alarm = DayAlarm1 {
day: 1,
hour: Hours::H24(when.hour() as u8),
minute: when.minute() as u8,
second: when.second() as u8,
};
self.set_alarm1_day(alarm, Alarm1Matching::HoursMinutesAndSecondsMatch)
}
/// Set Alarm1 for weekday.
///
/// Will return an `Error::InvalidInputData` if any of the used parameters
/// (depending on the matching startegy) is out of range. Any unused
/// parameter is set to basic valid value:
/// - second, minute, hour: 0
/// - weekday: 1
pub fn set_alarm1_weekday(
&mut self,
when: WeekdayAlarm1,
matching: Alarm1Matching,
) -> Result<(), Error<CommE, PinE>> {
let weekday_invalid = when.weekday < 1 || when.weekday > 7;
let hour_invalid = is_hour_valid(when.hour);
let minute_invalid = when.minute > 59;
let second_invalid = when.second > 59;
let weekday = if weekday_invalid { 1 } else { when.weekday };
let hour = relax_hour(when.hour);
let minute = if minute_invalid { 0 } else { when.minute };
let second = if second_invalid { 0 } else { when.second };
if ((hour_invalid || weekday_invalid) && matching == Alarm1Matching::AllMatch)
|| (hour_invalid && matching == Alarm1Matching::HoursMinutesAndSecondsMatch)
|| (minute_invalid
&& (matching != Alarm1Matching::OncePerSecond
&& matching != Alarm1Matching::SecondsMatch))
|| (second_invalid && matching != Alarm1Matching::OncePerSecond)
{
return Err(Error::InvalidInputData);
}
let match_mask = get_matching_mask_alarm1(matching);
let mut data = [
Register::ALARM1_SECONDS,
decimal_to_packed_bcd(second) | match_mask[0],
decimal_to_packed_bcd(minute) | match_mask[1],
hours_to_register(hour)? | match_mask[2],
decimal_to_packed_bcd(weekday) | match_mask[3] | BitFlags::WEEKDAY,
];
self.iface.write_data(&mut data)
}
/// Set Alarm2 for date (day of month).
///
/// Will return an `Error::InvalidInputData` if any of the used parameters
/// (depending on the matching startegy) is out of range. Any unused
/// parameter is set to basic valid value:
/// - minute, hour: 0
/// - day: 1
pub fn set_alarm2_day(
&mut self,
when: DayAlarm2,
matching: Alarm2Matching,
) -> Result<(), Error<CommE, PinE>> {
let day_invalid = when.day < 1 || when.day > 31;
let hour_invalid = is_hour_valid(when.hour);
let minute_invalid = when.minute > 59;
let day = if day_invalid { 1 } else { when.day };
let hour = relax_hour(when.hour);
let minute = if minute_invalid { 0 } else { when.minute };
if ((day_invalid || hour_invalid) && matching == Alarm2Matching::AllMatch)
|| (hour_invalid && matching == Alarm2Matching::HoursAndMinutesMatch)
|| (matching != Alarm2Matching::OncePerMinute && minute_invalid)
{
return Err(Error::InvalidInputData);
}
let match_mask = get_matching_mask_alarm2(matching);
let mut data = [
Register::ALARM2_MINUTES,
decimal_to_packed_bcd(minute) | match_mask[0],
hours_to_register(hour)? | match_mask[1],
decimal_to_packed_bcd(day) | match_mask[2],
];
self.iface.write_data(&mut data)
}
/// Set Alarm2 for a time (fires when hours, minutes and seconds match).
///
/// Will return an `Error::InvalidInputData` if any of the parameters is out of range.
/// day is not used by the matching strategy but is set to 1.
pub fn set_alarm2_hm(&mut self, when: NaiveTime) -> Result<(), Error<CommE, PinE>> {
let alarm = DayAlarm2 {
day: 1,
hour: Hours::H24(when.hour() as u8),
minute: when.minute() as u8,
};
self.set_alarm2_day(alarm, Alarm2Matching::HoursAndMinutesMatch)
}
/// Set Alarm2 for weekday.
///
/// Will return an `Error::InvalidInputData` if any of the used parameters
/// (depending on the matching startegy) is out of range. Any unused
/// parameter is set to basic valid value:
/// - minute, hour: 0
/// - weekday: 1
pub fn set_alarm2_weekday(
&mut self,
when: WeekdayAlarm2,
matching: Alarm2Matching,
) -> Result<(), Error<CommE, PinE>> {
let weekday_invalid = when.weekday < 1 || when.weekday > 7;
let hour_invalid = is_hour_valid(when.hour);
let minute_invalid = when.minute > 59;
let weekday = if weekday_invalid { 1 } else { when.weekday };
let hour = relax_hour(when.hour);
let minute = if minute_invalid { 0 } else { when.minute };
if (matching == Alarm2Matching::AllMatch && (weekday_invalid || hour_invalid))
|| (matching == Alarm2Matching::HoursAndMinutesMatch && hour_invalid)
|| (minute_invalid && matching != Alarm2Matching::OncePerMinute)
{
return Err(Error::InvalidInputData);
}
let match_mask = get_matching_mask_alarm2(matching);
let mut data = [
Register::ALARM2_MINUTES,
decimal_to_packed_bcd(minute) | match_mask[0],
hours_to_register(hour)? | match_mask[1],
decimal_to_packed_bcd(weekday) | match_mask[2] | BitFlags::WEEKDAY,
];
self.iface.write_data(&mut data)
}
}