use crate::bits::{encode_bcd, get_bits, set_bits};
use crate::datetime::Pcf8523DateTime;
use crate::driver::Pcf8523Error::{Internal, InvalidArgument, InvalidState, InvalidTimerCountdown};
use crate::registers::*;
use crate::typedefs::ClkOut::Frequency0Hz;
use crate::typedefs::TimerMode::{Countdown, Watchdog};
use crate::typedefs::*;
#[cfg(feature = "sync")]
use embedded_hal::i2c::{I2c, Operation};
#[cfg(not(feature = "sync"))]
use embedded_hal_async::i2c::{I2c, Operation};
pub const PCF8523_I2C_ADDRESS: u8 = 0x68;
#[derive(Debug, PartialEq)]
pub enum Pcf8523Error<E> {
I2C(E),
InconsistentTimerCounter,
Internal,
InvalidArgument,
InvalidState,
InvalidTimerCountdown,
}
impl<E> From<E> for Pcf8523Error<E> {
fn from(e: E) -> Self {
Pcf8523Error::I2C(e)
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct Pcf8523<I2C, V> {
i2c: I2C,
variant: V,
}
impl<I2C: I2c, V: Variant> Pcf8523<I2C, V> {
#[maybe_async::maybe_async]
pub async fn new(i2c: I2C, variant: V) -> Result<Self, Pcf8523Error<I2C::Error>> {
let mut peri = Self { i2c, variant };
peri.i2c.read(PCF8523_I2C_ADDRESS, &mut [0u8]).await?;
Ok(peri)
}
#[maybe_async::maybe_async]
pub async fn clear_alarm_interrupt(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_2).await?;
set_bits(&mut reg_val, 0b0_1110, 3, 0b1111_1000);
self.write_reg(PCF8523_CONTROL_2, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn clear_battery_switch_over_interrupt(
&mut self,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_3).await?;
set_bits(&mut reg_val, 0, 3, 0b1000);
self.write_reg(PCF8523_CONTROL_3, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn clear_second_interrupt(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_2).await?;
set_bits(&mut reg_val, 0b0_1101, 3, 0b1111_1000);
self.write_reg(PCF8523_CONTROL_2, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn clear_timer_a_interrupt(
&mut self,
timer: &TimerA,
) -> Result<(), Pcf8523Error<I2C::Error>> {
match timer.mode {
Countdown => {
let mut reg_val = self.read_reg(PCF8523_CONTROL_2).await?;
set_bits(&mut reg_val, 0b0_0111, 3, 0b1111_1000);
self.write_reg(PCF8523_CONTROL_2, reg_val).await
}
Watchdog => {
self.read_reg(PCF8523_CONTROL_2).await?;
Ok(())
}
}
}
#[maybe_async::maybe_async]
pub async fn disable_alarm_interrupt(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_1).await?;
set_bits(&mut reg_val, 0, 1, 0b10);
self.write_reg(PCF8523_CONTROL_1, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn disable_battery_low_detection_interrupt(
&mut self,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_3).await?;
set_bits(&mut reg_val, 0, 0, 0b1);
self.write_reg(PCF8523_CONTROL_3, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn disable_battery_switch_over_interrupt(
&mut self,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_3).await?;
set_bits(&mut reg_val, 0, 1, 0b10);
self.write_reg(PCF8523_CONTROL_3, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn disable_correction_interrupt(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_1).await?;
set_bits(&mut reg_val, 0, 0, 0b1);
self.write_reg(PCF8523_CONTROL_1, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn disable_day_alarm(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_DAY_ALARM).await?;
set_bits(&mut reg_val, 1, 7, 0b1000_0000);
self.write_reg(PCF8523_DAY_ALARM, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn disable_hour_alarm(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_HOUR_ALARM).await?;
set_bits(&mut reg_val, 1, 7, 0b1000_0000);
self.write_reg(PCF8523_HOUR_ALARM, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn disable_minute_alarm(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_MINUTE_ALARM).await?;
set_bits(&mut reg_val, 1, 7, 0b1000_0000);
self.write_reg(PCF8523_MINUTE_ALARM, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn disable_weekday_alarm(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_WEEKDAY_ALARM).await?;
set_bits(&mut reg_val, 1, 7, 0b1000_0000);
self.write_reg(PCF8523_WEEKDAY_ALARM, reg_val).await
}
#[maybe_async::maybe_async]
async fn enable_alarm_interrupt(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_1).await?;
set_bits(&mut reg_val, 1, 1, 0b10);
self.write_reg(PCF8523_CONTROL_1, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn enable_battery_low_detection_interrupt(
&mut self,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_3).await?;
let pm_bits = get_bits(reg_val, 3, 5);
match PowerManagement::try_from(pm_bits) {
Ok(pm) => match pm {
PowerManagement::SwitchOverStandardOnLowDetectionOn
| PowerManagement::SwitchOverDirectOnLowDetectionOn
| PowerManagement::SwitchOverOffLowDetectionOn => {
self.set_clkout(ClkOut::Frequency0Hz).await?;
set_bits(&mut reg_val, 1, 0, 0b1);
self.write_reg(PCF8523_CONTROL_3, reg_val).await
}
_ => Err(InvalidState),
},
Err(_) => Err(Internal),
}
}
#[maybe_async::maybe_async]
pub async fn enable_battery_switch_over_interrupt(
&mut self,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_3).await?;
let pm_bits = get_bits(reg_val, 3, 5);
match PowerManagement::try_from(pm_bits) {
Ok(pm) => match pm {
PowerManagement::SwitchOverStandardOnLowDetectionOn
| PowerManagement::SwitchOverDirectOnLowDetectionOn
| PowerManagement::SwitchOverStandardOnLowDetectionOff
| PowerManagement::SwitchOverDirectOnLowDetectionOff => {
self.set_clkout(ClkOut::Frequency0Hz).await?;
set_bits(&mut reg_val, 1, 1, 0b10);
self.write_reg(PCF8523_CONTROL_3, reg_val).await
}
_ => Err(InvalidState),
},
Err(_) => Err(Internal),
}
}
#[maybe_async::maybe_async]
pub async fn enable_correction_interrupt(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
if self.read_reg(PCF8523_OFFSET).await? != 0 {
let mut reg_val = self.read_reg(PCF8523_CONTROL_1).await?;
set_bits(&mut reg_val, 1, 0, 0b1);
self.set_clkout(ClkOut::Frequency0Hz).await?;
self.write_reg(PCF8523_CONTROL_1, reg_val).await
} else {
Err(InvalidState)
}
}
#[maybe_async::maybe_async]
pub async fn enable_day_alarm(&mut self, day: u8) -> Result<(), Pcf8523Error<I2C::Error>> {
if day == 0 || day > 31 {
return Err(InvalidArgument);
}
self.set_clkout(ClkOut::Frequency0Hz).await?;
self.write_reg(PCF8523_DAY_ALARM, (0 << 7) | encode_bcd(day))
.await?;
self.enable_alarm_interrupt().await
}
#[maybe_async::maybe_async]
pub async fn enable_hour_alarm(&mut self, hour: u8) -> Result<(), Pcf8523Error<I2C::Error>> {
if hour > 23 {
return Err(InvalidArgument);
}
self.set_clkout(ClkOut::Frequency0Hz).await?;
self.write_reg(PCF8523_HOUR_ALARM, (0 << 7) | encode_bcd(hour))
.await?;
self.enable_alarm_interrupt().await
}
#[maybe_async::maybe_async]
pub async fn enable_minute_alarm(
&mut self,
minute: u8,
) -> Result<(), Pcf8523Error<I2C::Error>> {
if minute > 59 {
return Err(InvalidArgument);
}
self.set_clkout(ClkOut::Frequency0Hz).await?;
self.write_reg(PCF8523_MINUTE_ALARM, (0 << 7) | encode_bcd(minute))
.await?;
self.enable_alarm_interrupt().await
}
#[maybe_async::maybe_async]
pub async fn enable_weekday_alarm(
&mut self,
weekday: u8,
) -> Result<(), Pcf8523Error<I2C::Error>> {
if weekday > 6 {
return Err(InvalidArgument);
}
self.set_clkout(ClkOut::Frequency0Hz).await?;
self.write_reg(PCF8523_WEEKDAY_ALARM, (0 << 7) | weekday)
.await?;
self.enable_alarm_interrupt().await
}
#[maybe_async::maybe_async]
pub async fn lost_power(&mut self) -> Result<bool, Pcf8523Error<I2C::Error>> {
let reg_val = self.read_reg(PCF8523_SECONDS).await?;
Ok((reg_val >> 7) == 1)
}
#[maybe_async::maybe_async]
pub async fn now(&mut self) -> Result<Pcf8523DateTime, Pcf8523Error<I2C::Error>> {
let mut second = [0u8];
let mut minute = [0u8];
let mut hour = [0u8];
let mut day = [0u8];
let mut month = [0u8];
let mut year = [0u8];
self.i2c
.transaction(
PCF8523_I2C_ADDRESS,
&mut [
Operation::Write(&[PCF8523_SECONDS]),
Operation::Read(&mut second),
Operation::Write(&[PCF8523_MINUTES]),
Operation::Read(&mut minute),
Operation::Write(&[PCF8523_HOURS]),
Operation::Read(&mut hour),
Operation::Write(&[PCF8523_DAYS]),
Operation::Read(&mut day),
Operation::Write(&[PCF8523_MONTHS]),
Operation::Read(&mut month),
Operation::Write(&[PCF8523_YEARS]),
Operation::Read(&mut year),
],
)
.await?;
Ok(Pcf8523DateTime {
second: second[0],
minute: minute[0],
hour: hour[0],
day: day[0],
month: month[0],
year: year[0],
}
.bcd_decode())
}
#[maybe_async::maybe_async]
pub async fn read_reg(&mut self, addr: u8) -> Result<u8, Pcf8523Error<I2C::Error>> {
let mut buffer = [0u8];
self.i2c
.write_read(PCF8523_I2C_ADDRESS, &[addr], &mut buffer)
.await?;
Ok(buffer[0])
}
#[maybe_async::maybe_async]
pub async fn reload_timer_a_watchdog_countdown(
&mut self,
timer: &TimerA,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let tmr_clkout_ctrl = self.read_reg(PCF8523_TMR_CLKOUT_CTRL).await?;
if timer.mode == Countdown || get_bits(tmr_clkout_ctrl, 2, 1) != 0b10 {
return Err(InvalidArgument);
}
self.write_reg(PCF8523_TMR_A_REG, timer.countdown).await
}
#[maybe_async::maybe_async]
pub async fn reset(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
self.write_reg(PCF8523_CONTROL_1, 0b101_1000).await
}
#[maybe_async::maybe_async]
pub async fn running(&mut self) -> Result<bool, Pcf8523Error<I2C::Error>> {
let reg_val = self.read_reg(PCF8523_CONTROL_1).await?;
Ok(get_bits(reg_val, 1, 5) == 0)
}
#[maybe_async::maybe_async]
pub async fn set_clkout(&mut self, frequency: ClkOut) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut tmr_clkout_ctrl = self.read_reg(PCF8523_TMR_CLKOUT_CTRL).await?;
set_bits(&mut tmr_clkout_ctrl, frequency as u8, 3, 0b11_1000);
self.write_reg(PCF8523_TMR_CLKOUT_CTRL, tmr_clkout_ctrl)
.await
}
#[maybe_async::maybe_async]
pub async fn set_datetime(
&mut self,
datetime: Pcf8523DateTime,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let dt = datetime.encode_bcd();
self.i2c
.transaction(
PCF8523_I2C_ADDRESS,
&mut [
Operation::Write(&[PCF8523_SECONDS, dt.second]),
Operation::Write(&[PCF8523_MINUTES, dt.minute]),
Operation::Write(&[PCF8523_HOURS, dt.hour]),
Operation::Write(&[PCF8523_DAYS, dt.day]),
Operation::Write(&[PCF8523_MONTHS, dt.month]),
Operation::Write(&[PCF8523_YEARS, dt.year]),
],
)
.await?;
Ok(())
}
#[maybe_async::maybe_async]
pub async fn set_power_management(
&mut self,
power_management: PowerManagement,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_3).await?;
set_bits(&mut reg_val, power_management as u8, 5, 0b1110_0000);
self.write_reg(PCF8523_CONTROL_3, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn set_offset(
&mut self,
mode: CorrectionMode,
offset: i8,
) -> Result<(), Pcf8523Error<I2C::Error>> {
if offset < -64 || offset > 63 {
return Err(InvalidArgument);
}
let mut reg_val = (mode as u8) << 7;
set_bits(&mut reg_val, offset as u8, 0, 0b111_1111);
self.write_reg(PCF8523_OFFSET, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn start(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_1).await?;
set_bits(&mut reg_val, 0, 5, 0b10_0000);
self.write_reg(PCF8523_CONTROL_1, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn start_second_timer(
&mut self,
interrupt_mode: TimerInterruptMode,
) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut clkout_ctrl = self.read_reg(PCF8523_TMR_CLKOUT_CTRL).await?;
set_bits(&mut clkout_ctrl, interrupt_mode as u8, 7, 0b1000_0000);
set_bits(&mut clkout_ctrl, 0b111, 3, 0b11_1000);
self.write_reg(PCF8523_TMR_CLKOUT_CTRL, clkout_ctrl).await?;
let mut control_1 = self.read_reg(PCF8523_CONTROL_1).await?;
set_bits(&mut control_1, 1, 2, 0b100); self.write_reg(PCF8523_CONTROL_1, control_1).await
}
#[maybe_async::maybe_async]
pub async fn start_timer_a(&mut self, timer: &TimerA) -> Result<(), Pcf8523Error<I2C::Error>> {
if timer.countdown == 0 {
return Err(InvalidTimerCountdown);
}
self.stop_timer_a().await?;
self.write_reg(PCF8523_TMR_A_FREQ_CTRL, timer.source_clock as u8)
.await?;
let mut tmr_clkout_ctrl = self.read_reg(PCF8523_TMR_CLKOUT_CTRL).await?;
set_bits(&mut tmr_clkout_ctrl, Frequency0Hz as u8, 3, 0b11_1000);
set_bits(
&mut tmr_clkout_ctrl,
timer.interrupt_mode.into(),
7,
0b1000_0000,
);
set_bits(&mut tmr_clkout_ctrl, timer.mode.into(), 1, 0b110);
self.write_reg(PCF8523_TMR_CLKOUT_CTRL, tmr_clkout_ctrl)
.await?;
let mut control_2 = self.read_reg(PCF8523_CONTROL_2).await?;
match timer.mode {
Countdown => set_bits(&mut control_2, 0b01, 1, 0b110),
Watchdog => set_bits(&mut control_2, 0b10, 1, 0b110),
}
self.write_reg(PCF8523_CONTROL_2, control_2).await?;
self.write_reg(PCF8523_TMR_A_REG, timer.countdown).await
}
#[maybe_async::maybe_async]
pub async fn stop(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_1).await?;
set_bits(&mut reg_val, 1, 5, 0b10_0000);
self.write_reg(PCF8523_CONTROL_1, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn stop_clkout(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_TMR_CLKOUT_CTRL).await?;
set_bits(&mut reg_val, 0b111, 3, 0b11_1000); self.write_reg(PCF8523_TMR_CLKOUT_CTRL, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn stop_second_timer(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut control_1 = self.read_reg(PCF8523_CONTROL_1).await?;
set_bits(&mut control_1, 0, 2, 0b100); self.write_reg(PCF8523_CONTROL_1, control_1).await
}
#[maybe_async::maybe_async]
pub async fn stop_timer_a(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut tmr_clkout_ctrl = self.read_reg(PCF8523_TMR_CLKOUT_CTRL).await?;
set_bits(&mut tmr_clkout_ctrl, 00, 1, 0b110);
self.write_reg(PCF8523_TMR_CLKOUT_CTRL, tmr_clkout_ctrl)
.await
}
#[maybe_async::maybe_async]
pub async fn timer_a_counter(&mut self) -> Result<u8, Pcf8523Error<I2C::Error>> {
self.timer_counter(PCF8523_TMR_A_REG).await
}
#[maybe_async::maybe_async]
async fn timer_counter(&mut self, reg_addr: u8) -> Result<u8, Pcf8523Error<I2C::Error>> {
let a = self.read_reg(reg_addr).await?;
let b = self.read_reg(reg_addr).await?;
if a == b {
Ok(a)
} else {
Err(Pcf8523Error::InconsistentTimerCounter)
}
}
#[maybe_async::maybe_async]
pub async fn write_reg(&mut self, reg: u8, val: u8) -> Result<(), Pcf8523Error<I2C::Error>> {
Ok(self.i2c.write(PCF8523_I2C_ADDRESS, &[reg, val]).await?)
}
}
impl<I2C: I2c, V: Variant + Int2Pin> Pcf8523<I2C, V> {
#[maybe_async::maybe_async]
pub async fn clear_timer_b_interrupt(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_CONTROL_2).await?;
set_bits(&mut reg_val, 0b0_1011, 3, 0b1111_1000);
self.write_reg(PCF8523_CONTROL_2, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn start_timer_b(&mut self, timer: &TimerB) -> Result<(), Pcf8523Error<I2C::Error>> {
self.stop_timer_b().await?;
let mut tmr_b_freq_ctrl = self.read_reg(PCF8523_TMR_B_FREQ_CTRL).await?;
set_bits(&mut tmr_b_freq_ctrl, timer.source_clock as u8, 0, 0b111);
match timer.interrupt_mode {
TimerBInterruptMode::Pulsed(width) => {
set_bits(&mut tmr_b_freq_ctrl, width as u8, 4, 0b111_0000);
}
_ => {}
}
self.write_reg(PCF8523_TMR_B_FREQ_CTRL, tmr_b_freq_ctrl)
.await?;
let mut control_2 = self.read_reg(PCF8523_CONTROL_2).await?;
set_bits(&mut control_2, 1, 0, 0b1);
self.write_reg(PCF8523_CONTROL_2, control_2).await?;
let mut tmr_clkout_ctrl = self.read_reg(PCF8523_TMR_CLKOUT_CTRL).await?;
set_bits(&mut tmr_clkout_ctrl, Frequency0Hz as u8, 3, 0b11_1000);
set_bits(
&mut tmr_clkout_ctrl,
timer.interrupt_mode.into(),
6,
0b100_0000,
);
set_bits(&mut tmr_clkout_ctrl, 1, 0, 0b1);
self.write_reg(PCF8523_TMR_CLKOUT_CTRL, tmr_clkout_ctrl)
.await?;
self.write_reg(PCF8523_TMR_B_REG, timer.countdown).await
}
#[maybe_async::maybe_async]
pub async fn stop_timer_b(&mut self) -> Result<(), Pcf8523Error<I2C::Error>> {
let mut reg_val = self.read_reg(PCF8523_TMR_CLKOUT_CTRL).await?;
set_bits(&mut reg_val, 0, 0, 0b1);
self.write_reg(PCF8523_TMR_CLKOUT_CTRL, reg_val).await
}
#[maybe_async::maybe_async]
pub async fn timer_b_counter(&mut self) -> Result<u8, Pcf8523Error<I2C::Error>> {
self.timer_counter(PCF8523_TMR_B_REG).await
}
}