use crate::{
driver::{i2c::I2cDriver, rtc::RtcDriver, Driver},
util,
};
use anyhow::{anyhow, Result};
use chrono::{Datelike, NaiveDate, NaiveDateTime, Timelike};
const HYM8563_ADDR: u16 = 0x51 << 1;
const REG_CTRL_STATUS1: u8 = 0x00;
const REG_CTRL_STATUS2: u8 = 0x01;
const REG_SECONDS: u8 = 0x02;
const STOP_BIT: u8 = 0x20;
const RESET_BIT: u8 = 0x80;
const VL_BIT: u8 = 0x80;
pub struct Hym8563 {
pub bus: &'static mut dyn I2cDriver,
}
impl Hym8563 {
pub const fn new(bus: &'static mut dyn I2cDriver) -> Self {
Self { bus }
}
fn write_register(&mut self, reg: u8, data: u8) -> Result<()> {
let buffer = [reg, data];
self.bus.i2c_write(HYM8563_ADDR, buffer.as_ref())
}
fn burst_write(&mut self, start_reg: u8, data: &[u8]) -> Result<()> {
let mut buffer = [0u8; 8]; if data.len() > 7 {
return Err(anyhow!("HYM8563 EX WSIZE"));
}
buffer[0] = start_reg;
buffer[1..=data.len()].copy_from_slice(data);
self.bus
.i2c_write(HYM8563_ADDR, buffer[..=data.len()].as_ref())?;
Ok(())
}
fn read_register(&mut self, reg: u8) -> Result<u8> {
let reg_buf = [reg];
self.bus.i2c_write(HYM8563_ADDR, reg_buf.as_ref())?;
let mut buffer = [0u8; 1];
self.bus.i2c_read(HYM8563_ADDR, buffer.as_mut())?;
Ok(buffer[0])
}
fn burst_read(&mut self, start_reg: u8, buffer: &mut [u8]) -> Result<()> {
let reg_buf = [start_reg];
self.bus.i2c_write(HYM8563_ADDR, reg_buf.as_ref())?;
self.bus.i2c_read(HYM8563_ADDR, buffer)?;
Ok(())
}
pub fn is_running(&mut self) -> Result<bool> {
let status = self.read_register(REG_CTRL_STATUS1)?;
Ok((status & STOP_BIT) == 0)
}
pub fn reset(&mut self) -> Result<()> {
self.write_register(REG_CTRL_STATUS2, RESET_BIT)
}
fn init(&mut self) -> Result<()> {
self.reset()?;
for _ in 0..10000 {}
self.write_register(REG_CTRL_STATUS1, 0x00)?;
self.write_register(REG_CTRL_STATUS2, 0x00)?;
Ok(())
}
fn deinit(&mut self) -> Result<()> {
let mut status = self.read_register(REG_CTRL_STATUS1)?;
status |= STOP_BIT;
self.write_register(REG_CTRL_STATUS1, status)
}
fn get_datetime(&mut self) -> Result<NaiveDateTime> {
let mut buffer = [0u8; 7];
self.burst_read(REG_SECONDS, &mut buffer)?;
if buffer[0] & VL_BIT != 0 {
return Err(anyhow!("RTC data invalid (VL bit set)"));
}
let second = util::bcd_to_dec(buffer[0] & 0x7F)?;
let minute = util::bcd_to_dec(buffer[1] & 0x7F)?;
let hour = util::bcd_to_dec(buffer[2] & 0x3F)?;
let day = util::bcd_to_dec(buffer[3] & 0x3F)?;
let month = util::bcd_to_dec(buffer[5] & 0x1F)?;
let year = 2000 + util::bcd_to_dec(buffer[6])? as i32;
if second > 59
|| minute > 59
|| hour > 23
|| day == 0
|| day > 31
|| month == 0
|| month > 12
{
return Err(anyhow!("Invalid time"));
}
let naive_date = NaiveDate::from_ymd_opt(year, month as u32, day as u32)
.ok_or(anyhow!("Invalid date"))?;
let naive_time = naive_date
.and_hms_opt(hour as u32, minute as u32, second as u32)
.ok_or(anyhow!("Invalid time"))?;
Ok(naive_time)
}
fn set_datetime(&mut self, naive: &NaiveDateTime) -> Result<()> {
let second = util::dec_to_bcd(naive.second() as u8)?;
let minute = util::dec_to_bcd(naive.minute() as u8)?;
let hour = util::dec_to_bcd(naive.hour() as u8)?;
let day = util::dec_to_bcd(naive.day() as u8)?;
let weekday = naive.weekday().num_days_from_sunday() as u8; let month = util::dec_to_bcd(naive.month() as u8)?;
let year = util::dec_to_bcd((naive.year() % 100) as u8)?;
let buffer = [
second & 0x7F,
minute & 0x7F,
hour & 0x3F,
day & 0x3F,
weekday & 0x07,
month & 0x1F,
year,
];
self.burst_write(REG_SECONDS, &buffer)
}
}
impl Driver for Hym8563 {
fn driver_init(&mut self) -> Result<()> {
self.init()
}
fn driver_deinit(&mut self) -> Result<()> {
self.deinit()
}
}
impl RtcDriver for Hym8563 {
fn rtc_read_datetime(&mut self) -> Result<NaiveDateTime> {
self.get_datetime()
}
fn rtc_write_datetime(&mut self, naive: &NaiveDateTime) -> Result<()> {
self.set_datetime(naive)
}
}