#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![cfg_attr(feature = "async", allow(incomplete_features))]
#![cfg_attr(docsrs, feature(doc_cfg), feature(doc_auto_cfg))]
#![feature(error_generic_member_access)]
#![feature(trivial_bounds)]
use crate::bus::{Bus, BusTrait};
pub use embedded_hal_0_2;
use models::{Register, Rv8803, TIME_ARRAY_LENGTH};
pub mod bus;
pub(crate) mod models;
pub(crate) mod error;
pub mod prelude {
pub use crate::bus::Bus;
pub use crate::error::Error;
}
#[allow(dead_code)]
impl<'a, I2C, E> Rv8803<Bus<'a, I2C>>
where
I2C: embedded_hal_0_2::blocking::i2c::WriteRead<Error = E>
+ embedded_hal_0_2::blocking::i2c::Write<Error = E>,
Bus<'a, I2C>: bus::BusTrait<Error = E>,
{
pub fn new(bus: Bus<'a, I2C>) -> Self {
Self { bus }
}
pub fn from_i2c(i2c: I2C, address: u8) -> Self {
let bus = crate::bus::Bus::new(i2c, &address);
Self::new(bus)
}
pub fn from_bus(i2c: I2C, address: u8) -> Self {
let bus = crate::bus::Bus::new(i2c, &address);
Self::new(bus)
}
#[allow(clippy::too_many_arguments)]
pub fn set_time(
&mut self,
sec: u8,
min: u8,
hour: u8,
weekday: u8,
date: u8,
month: u8,
year: u16,
) -> Result<bool, E>
where
E: core::convert::From<core::num::TryFromIntError>,
{
self.bus
.write_register(Register::Seconds, dec_to_bcd(sec))?;
self.bus
.write_register(Register::Minutes, dec_to_bcd(min))?;
self.bus.write_register(Register::Hours, dec_to_bcd(hour))?;
self.bus.write_register(Register::Date, dec_to_bcd(date))?;
self.bus
.write_register(Register::Month, dec_to_bcd(month))?;
self.bus
.write_register(Register::Year, dec_to_bcd(u8::try_from(year - 2000)?))?;
self.bus.write_register(Register::Weekday, weekday)?;
self.write_bit(
Register::Control.address(),
Register::ControlReset.address(),
false,
)?;
defmt::debug!("rv8803::set_time: updated RTC clock");
Ok(true)
}
pub fn update_time(&mut self, dest: &mut [u8]) -> Result<bool, E> {
if !(self.bus.read_multiple_registers(
Register::Hundredths.address(),
dest,
TIME_ARRAY_LENGTH,
)?) {
defmt::warn!("update_time: attempt read - fail 1");
return Ok(false); }
if bcd_to_dec(dest[0]) == 99 || bcd_to_dec(dest[1]) == 59 {
let mut temp_time = [0_u8; TIME_ARRAY_LENGTH];
defmt::debug!("update_time: if hundredths are at 99 or seconds are at 59, read again to make sure we didn't accidentally skip a second/minute / Hundreths: {} / Seconds: {}", bcd_to_dec(dest[0]),bcd_to_dec(dest[1]));
if !(self.bus.read_multiple_registers(
Register::Hundredths.address(),
&mut temp_time,
TIME_ARRAY_LENGTH,
)?) {
defmt::warn!("update_time: attempt read - fail 2");
return Ok(false); };
if bcd_to_dec(dest[0]) > bcd_to_dec(temp_time[0]) {
defmt::debug!("update_time: the reading for hundredths has rolled over, then our new data is correct. / Hundreths: {} / temp_time[0]: {}",
bcd_to_dec(dest[0]),
bcd_to_dec(temp_time[0]));
for (i, el) in temp_time.iter().enumerate() {
dest[i] = *el;
}
}
}
let mut buf = [0_u8; 8];
for (i, el) in dest.iter().enumerate() {
if i == 4 {
buf[i] = *el;
} else {
defmt::info!("Raw: {} / BCD to Dec: {}", *el, bcd_to_dec(*el));
buf[i] = bcd_to_dec(*el);
}
}
dest.copy_from_slice(&buf[..dest.len()]);
Ok(true)
}
pub fn write_bit(&mut self, reg_addr: u8, bit_addr: u8, bit_to_write: bool) -> Result<bool, E> {
let mut value = 0;
if let Ok(reg_value) = self.bus.read_register_by_addr(reg_addr) {
value = reg_value;
}
value &= !(1 << bit_addr);
value |= u8::from(bit_to_write) << bit_addr;
self.bus.write_register_by_addr(reg_addr, value)?;
Ok(true)
}
pub fn read_seconds(&mut self) -> Result<u8, E> {
let secs = self.bus.read_register(Register::Seconds)?;
Ok(bcd_to_dec(secs))
}
pub fn read_year(&mut self) -> Result<u8, E> {
let year = self.bus.read_register(Register::Year)?;
Ok(bcd_to_dec(year))
}
pub fn set_year(&mut self, year: u16) -> Result<u8, E>
where
E: core::convert::From<core::num::TryFromIntError>,
{
let year = dec_to_bcd(u8::try_from(year - 2000)?);
self.bus.write_register(Register::Year, year)?;
self.read_year()
}
}
fn bcd_to_dec(value: u8) -> u8 {
((value / 0x10) * 10) + (value % 0x10)
}
fn dec_to_bcd(value: u8) -> u8 {
((value / 10) * 0x10) + (value % 10)
}
#[allow(unused_imports)]
pub mod rtc {
use crate::Rv8803;
use chrono::{DateTime, Utc};
use core::marker::PhantomData;
use defmt::error;
use heapless::String;
use shared_bus::BusManager;
#[allow(dead_code)]
pub struct RTClock<'a, I2C, I2cErr, M> {
datetime: Option<DateTime<Utc>>,
phantom: PhantomData<&'a I2C>,
bus_err: PhantomData<&'a I2cErr>,
bus: BusManager<M>,
device_address: u8,
}
#[allow(dead_code)]
impl<'a, I2C, I2cErr, SharedBusMutex> RTClock<'a, I2C, I2cErr, SharedBusMutex>
where
I2C: embedded_hal_0_2::blocking::i2c::Write<Error = I2cErr>
+ embedded_hal_0_2::blocking::i2c::WriteRead<Error = I2cErr>,
SharedBusMutex: shared_bus::BusMutex,
<SharedBusMutex as shared_bus::BusMutex>::Bus: embedded_hal_0_2::blocking::i2c::Write<Error = I2cErr>
+ embedded_hal_0_2::blocking::i2c::WriteRead<Error = I2cErr>,
I2cErr: defmt::Format,
{
pub fn new(bus: BusManager<SharedBusMutex>, address: &u8) -> Self {
Self {
datetime: None,
bus,
phantom: PhantomData,
bus_err: PhantomData,
device_address: *address,
}
}
pub fn rtc(
&self,
) -> Rv8803<crate::prelude::Bus<'_, shared_bus::I2cProxy<'_, SharedBusMutex>>> {
let proxy = self.bus.acquire_i2c();
Rv8803::from_i2c(proxy, self.device_address)
}
}
#[allow(dead_code)]
pub struct RTClockDirect<'a, I2C, I2cErr> {
datetime: Option<DateTime<Utc>>,
periph: I2C,
bus_err: PhantomData<&'a I2cErr>,
device_address: u8,
}
#[allow(dead_code)]
impl<'a, I2C, I2cErr> RTClockDirect<'a, I2C, I2cErr>
where
I2C: embedded_hal_0_2::blocking::i2c::Write<Error = I2cErr>
+ embedded_hal_0_2::blocking::i2c::WriteRead<Error = I2cErr>,
{
pub fn new(periph: I2C, address: &u8) -> Self {
Self {
datetime: None,
periph,
bus_err: PhantomData,
device_address: *address,
}
}
pub fn rtc(self) -> Rv8803<crate::prelude::Bus<'static, I2C>> {
Rv8803::from_i2c(self.periph, self.device_address)
}
}
}