mirror of https://github.com/eldruin/ds323x-rs
301 lines
9.9 KiB
Rust
301 lines
9.9 KiB
Rust
//! Common implementation
|
|
|
|
use super::{
|
|
decimal_to_packed_bcd, hours_to_register, packed_bcd_to_decimal, some_or_invalid_error,
|
|
};
|
|
use crate::{
|
|
interface::{ReadData, WriteData},
|
|
BitFlags, DateTimeAccess, Datelike, Ds323x, Error, Hours, NaiveDate, NaiveDateTime, NaiveTime,
|
|
Register, Rtcc, Timelike,
|
|
};
|
|
|
|
impl<DI, IC, E> DateTimeAccess for Ds323x<DI, IC>
|
|
where
|
|
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
|
|
{
|
|
type Error = Error<E>;
|
|
|
|
fn datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
|
|
let mut data = [0; 8];
|
|
self.iface.read_data(&mut data)?;
|
|
|
|
let year = year_from_registers(
|
|
data[Register::MONTH as usize + 1],
|
|
data[Register::YEAR as usize + 1],
|
|
);
|
|
let month = packed_bcd_to_decimal(data[Register::MONTH as usize + 1] & !BitFlags::CENTURY);
|
|
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1]);
|
|
let hour = hours_from_register(data[Register::HOURS as usize + 1]);
|
|
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
|
|
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
|
|
|
|
let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
|
|
let date = some_or_invalid_error(date)?;
|
|
let datetime = date.and_hms_opt(get_h24(hour).into(), minute.into(), second.into());
|
|
some_or_invalid_error(datetime)
|
|
}
|
|
|
|
fn set_datetime(&mut self, datetime: &NaiveDateTime) -> Result<(), Self::Error> {
|
|
if datetime.year() < 2000 || datetime.year() > 2100 {
|
|
return Err(Error::InvalidInputData);
|
|
}
|
|
let (month, year) = month_year_to_registers(datetime.month() as u8, datetime.year() as u16);
|
|
let mut payload = [
|
|
Register::SECONDS,
|
|
decimal_to_packed_bcd(datetime.second() as u8),
|
|
decimal_to_packed_bcd(datetime.minute() as u8),
|
|
hours_to_register(Hours::H24(datetime.hour() as u8))?,
|
|
datetime.weekday().number_from_sunday() as u8,
|
|
decimal_to_packed_bcd(datetime.day() as u8),
|
|
month,
|
|
year,
|
|
];
|
|
self.iface.write_data(&mut payload)
|
|
}
|
|
}
|
|
|
|
impl<DI, IC, E> Rtcc for Ds323x<DI, IC>
|
|
where
|
|
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
|
|
{
|
|
fn seconds(&mut self) -> Result<u8, Self::Error> {
|
|
self.read_register_decimal(Register::SECONDS)
|
|
}
|
|
|
|
fn minutes(&mut self) -> Result<u8, Self::Error> {
|
|
self.read_register_decimal(Register::MINUTES)
|
|
}
|
|
|
|
fn hours(&mut self) -> Result<Hours, Self::Error> {
|
|
let data = self.iface.read_register(Register::HOURS)?;
|
|
Ok(hours_from_register(data))
|
|
}
|
|
|
|
fn time(&mut self) -> Result<NaiveTime, Self::Error> {
|
|
let mut data = [0; 4];
|
|
self.iface.read_data(&mut data)?;
|
|
let hour = hours_from_register(data[Register::HOURS as usize + 1]);
|
|
let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
|
|
let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
|
|
|
|
let time = NaiveTime::from_hms_opt(get_h24(hour).into(), minute.into(), second.into());
|
|
some_or_invalid_error(time)
|
|
}
|
|
|
|
fn weekday(&mut self) -> Result<u8, Self::Error> {
|
|
self.read_register_decimal(Register::DOW)
|
|
}
|
|
|
|
fn day(&mut self) -> Result<u8, Self::Error> {
|
|
self.read_register_decimal(Register::DOM)
|
|
}
|
|
|
|
fn month(&mut self) -> Result<u8, Self::Error> {
|
|
let data = self.iface.read_register(Register::MONTH)?;
|
|
let value = data & !BitFlags::CENTURY;
|
|
Ok(packed_bcd_to_decimal(value))
|
|
}
|
|
|
|
fn year(&mut self) -> Result<u16, Self::Error> {
|
|
let mut data = [0; 3];
|
|
data[0] = Register::MONTH;
|
|
self.iface.read_data(&mut data)?;
|
|
Ok(year_from_registers(data[1], data[2]))
|
|
}
|
|
|
|
fn date(&mut self) -> Result<NaiveDate, Self::Error> {
|
|
let mut data = [0; 4];
|
|
data[0] = Register::DOM;
|
|
self.iface.read_data(&mut data)?;
|
|
|
|
let offset = Register::DOM as usize;
|
|
let year = year_from_registers(
|
|
data[Register::MONTH as usize + 1 - offset],
|
|
data[Register::YEAR as usize + 1 - offset],
|
|
);
|
|
let month =
|
|
packed_bcd_to_decimal(data[Register::MONTH as usize + 1 - offset] & !BitFlags::CENTURY);
|
|
let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1 - offset]);
|
|
let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
|
|
some_or_invalid_error(date)
|
|
}
|
|
|
|
fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> {
|
|
if seconds > 59 {
|
|
return Err(Error::InvalidInputData);
|
|
}
|
|
self.write_register_decimal(Register::SECONDS, seconds)
|
|
}
|
|
|
|
fn set_minutes(&mut self, minutes: u8) -> Result<(), Self::Error> {
|
|
if minutes > 59 {
|
|
return Err(Error::InvalidInputData);
|
|
}
|
|
self.write_register_decimal(Register::MINUTES, minutes)
|
|
}
|
|
|
|
fn set_hours(&mut self, hours: Hours) -> Result<(), Self::Error> {
|
|
let value = hours_to_register(hours)?;
|
|
self.iface.write_register(Register::HOURS, value)
|
|
}
|
|
|
|
fn set_time(&mut self, time: &NaiveTime) -> Result<(), Self::Error> {
|
|
let mut payload = [
|
|
Register::SECONDS,
|
|
decimal_to_packed_bcd(time.second() as u8),
|
|
decimal_to_packed_bcd(time.minute() as u8),
|
|
hours_to_register(Hours::H24(time.hour() as u8))?,
|
|
];
|
|
self.iface.write_data(&mut payload)
|
|
}
|
|
|
|
fn set_weekday(&mut self, weekday: u8) -> Result<(), Self::Error> {
|
|
if !(1..=7).contains(&weekday) {
|
|
return Err(Error::InvalidInputData);
|
|
}
|
|
self.iface.write_register(Register::DOW, weekday)
|
|
}
|
|
|
|
fn set_day(&mut self, day: u8) -> Result<(), Self::Error> {
|
|
if !(1..=31).contains(&day) {
|
|
return Err(Error::InvalidInputData);
|
|
}
|
|
self.write_register_decimal(Register::DOM, day)
|
|
}
|
|
|
|
fn set_month(&mut self, month: u8) -> Result<(), Self::Error> {
|
|
if !(1..=12).contains(&month) {
|
|
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)
|
|
}
|
|
|
|
fn set_year(&mut self, year: u16) -> Result<(), Self::Error> {
|
|
if !(2000..=2100).contains(&year) {
|
|
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)
|
|
}
|
|
}
|
|
|
|
fn set_date(&mut self, date: &rtcc::NaiveDate) -> Result<(), Self::Error> {
|
|
if date.year() < 2000 || date.year() > 2100 {
|
|
return Err(Error::InvalidInputData);
|
|
}
|
|
let (month, year) = month_year_to_registers(date.month() as u8, date.year() as u16);
|
|
let mut payload = [
|
|
Register::DOW,
|
|
date.weekday().number_from_sunday() as u8,
|
|
decimal_to_packed_bcd(date.day() as u8),
|
|
month,
|
|
year,
|
|
];
|
|
self.iface.write_data(&mut payload)
|
|
}
|
|
}
|
|
|
|
impl<DI, IC, E> Ds323x<DI, IC>
|
|
where
|
|
DI: ReadData<Error = Error<E>> + WriteData<Error = Error<E>>,
|
|
{
|
|
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))
|
|
}
|
|
|
|
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 + u16::from(year)
|
|
} else {
|
|
2000 + u16::from(year)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
fn get_h24(hour: Hours) -> u8 {
|
|
match hour {
|
|
Hours::H24(h) => h,
|
|
Hours::AM(h) => h,
|
|
Hours::PM(h) => h + 12,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn can_convert_to_h24() {
|
|
assert_eq!(0, get_h24(Hours::H24(0)));
|
|
assert_eq!(0, get_h24(Hours::AM(0)));
|
|
assert_eq!(12, get_h24(Hours::PM(0)));
|
|
|
|
assert_eq!(1, get_h24(Hours::H24(1)));
|
|
assert_eq!(1, get_h24(Hours::AM(1)));
|
|
assert_eq!(13, get_h24(Hours::PM(1)));
|
|
|
|
assert_eq!(23, get_h24(Hours::H24(23)));
|
|
assert_eq!(12, get_h24(Hours::AM(12)));
|
|
assert_eq!(23, get_h24(Hours::PM(11)));
|
|
}
|
|
}
|