#![no_implicit_prelude]
extern crate core;
extern crate rpsp;
use core::cmp;
use core::convert::Into;
use core::marker::Send;
use core::option::Option::{self, None, Some};
use core::result::Result::{self, Err, Ok};
use rpsp::clock::{AlarmConfig, RtcError, TimeSource};
use rpsp::i2c::mode::Controller;
use rpsp::i2c::{I2c, I2cAddress, I2cBus};
use rpsp::int::Acknowledge;
use rpsp::time::Time;
const CMD_SAVED: u8 = 0x03u8;
const CMD_CTRL_1: u8 = 0x00u8;
const CMD_CTRL_2: u8 = 0x01u8;
const CMD_ALARM2: u8 = 0x0Bu8;
const CMD_STATUS: u8 = 0x04u8;
const CMD_TIMER_MODE: u8 = 0x11u8;
const CMD_TIMER_VALUE: u8 = 0x10u8;
const ADDR: I2cAddress = I2cAddress::new_7bit(0x51u8);
#[repr(u8)]
pub enum PcfTick {
Speed4kHz = 0u8,
Speed64Hz = 1u8,
Speed1Hz = 2u8,
Slow = 3u8,
}
#[repr(u8)]
pub enum PcfOutput {
Rate32kHz = 0u8,
Rate16kHz = 1u8,
Rate8kHz = 2u8,
Rate4kHz = 3u8,
Rate2kHz = 4u8,
Rate1kHz = 5u8,
Rate1Hz = 6u8,
Off = 7u8,
}
pub struct PcfRtc<'a> {
i2c: I2cBus<'a, Controller>,
}
impl<'a> PcfRtc<'a> {
#[inline(always)]
pub fn new(i2c: impl Into<I2cBus<'a, Controller>>) -> PcfRtc<'a> {
PcfRtc { i2c: i2c.into() }
}
#[inline(always)]
pub fn i2c_bus(&self) -> &I2c<Controller> {
&self.i2c
}
#[inline]
pub fn reset(&mut self) -> Result<(), RtcError> {
self.i2c.write(ADDR, &[CMD_CTRL_1, 0x58u8])?;
loop {
self.i2c.write(ADDR, &[CMD_STATUS, 0u8])?;
if self.is_stable()? {
break;
}
}
Ok(())
}
#[inline(always)]
pub fn now(&mut self) -> Result<Time, RtcError> {
self.now_inner()
}
#[inline(always)]
pub fn get_byte(&mut self) -> Result<u8, RtcError> {
self.read_reg(CMD_SAVED)
}
#[inline(always)]
pub fn is_24hr(&mut self) -> Result<bool, RtcError> {
Ok(self.read_reg(CMD_CTRL_1)? & 0x2 == 0)
}
#[inline(always)]
pub fn is_stable(&mut self) -> Result<bool, RtcError> {
Ok(self.read_reg(CMD_STATUS)? & 0x80 == 0)
}
#[inline]
pub fn alarm_disable(&mut self) -> Result<(), RtcError> {
self.i2c
.write(ADDR, &[CMD_ALARM2, 0x80u8, 0x80u8, 0x80u8, 0x80u8, 0x80u8])?;
Ok(())
}
#[inline]
pub fn timer_disable(&mut self) -> Result<(), RtcError> {
let v = self.read_reg(CMD_TIMER_MODE)?;
self.i2c.write(ADDR, &[CMD_TIMER_MODE, v & 0xF8])?;
Ok(())
}
#[inline(always)]
pub fn alarm_state(&mut self) -> Result<bool, RtcError> {
Ok(self.read_reg(CMD_CTRL_2)? & 0x40 != 0)
}
#[inline(always)]
pub fn timer_state(&mut self) -> Result<bool, RtcError> {
Ok(self.read_reg(CMD_CTRL_2)? & 0x8 != 0)
}
#[inline(always)]
pub fn set_byte(&mut self, v: u8) -> Result<(), RtcError> {
self.i2c.write(ADDR, &[CMD_SAVED, v])?;
Ok(())
}
pub fn set_time(&mut self, v: Time) -> Result<(), RtcError> {
if !v.is_valid() {
return Err(RtcError::InvalidTime);
}
let r = self.read_reg(CMD_CTRL_1)?;
self.i2c.write(ADDR, &[CMD_CTRL_1, r & 0xFD])?;
self.i2c.write(ADDR, &[
CMD_STATUS,
encode(v.secs),
encode(v.mins),
encode(v.hours),
encode(v.day),
encode(v.weekday as u8),
encode(v.month as u8),
encode(v.year.saturating_sub(0x7D0) as u8),
])?;
Ok(())
}
#[inline]
pub fn set_24hr(&mut self, en: bool) -> Result<(), RtcError> {
let v = self.read_reg(CMD_CTRL_1)?;
self.i2c.write(ADDR, &[CMD_CTRL_1, if en { v & 0xFD } else { v | 0x2 }])?;
Ok(())
}
#[inline]
pub fn alarm_clear_state(&mut self) -> Result<bool, RtcError> {
let v = self.read_reg(CMD_CTRL_2)?;
self.i2c.write(ADDR, &[CMD_CTRL_2, v & 0xBF])?;
Ok(v & 0x40 != 0)
}
#[inline]
pub fn timer_clear_state(&mut self) -> Result<bool, RtcError> {
let v = self.read_reg(CMD_CTRL_2)?;
self.i2c.write(ADDR, &[CMD_CTRL_2, v & 0xF7])?;
Ok(v & 0x8 != 0)
}
#[inline(always)]
pub fn set_timer_ms(&mut self, ms: u32) -> Result<(), RtcError> {
let (t, s) = ms_to_ticks(ms).ok_or(RtcError::ValueTooLarge)?;
self.set_timer(t, s)
}
pub fn set_alarm(&mut self, v: AlarmConfig) -> Result<(), RtcError> {
if v.is_empty() {
return self.alarm_disable();
}
if !v.is_valid() {
return Err(RtcError::InvalidTime);
}
let r = self.read_reg(CMD_CTRL_1)?;
self.i2c.write(ADDR, &[CMD_CTRL_1, r & 0xFD])?;
self.i2c.write(ADDR, &[
CMD_ALARM2,
v.secs.map_or(0x80u8, encode),
v.mins.map_or(0x80u8, encode),
v.hours.map_or(0x80u8, encode),
v.day.map_or(0x80u8, |i| encode(i.get())),
v.weekday.map_or(0x80u8, |i| encode(i as u8)),
])?;
Ok(())
}
#[inline]
pub fn set_alarm_interrupt(&mut self, en: bool) -> Result<(), RtcError> {
let v = self.read_reg(CMD_CTRL_2)?;
self.i2c.write(ADDR, &[
CMD_CTRL_2,
(if en { v | 0x80 } else { v & 0x7F }) & 0xBF,
])?;
Ok(())
}
#[inline]
pub fn set_timer(&mut self, ticks: u8, speed: PcfTick) -> Result<(), RtcError> {
let v = self.read_reg(CMD_TIMER_MODE)?;
self.i2c.write(ADDR, &[
CMD_TIMER_VALUE,
ticks,
(v & 0xE7) | (((speed as u8) & 0x3) << 3) | 0x4,
])?;
Ok(())
}
#[inline(always)]
pub fn set_time_from(&mut self, mut v: impl TimeSource) -> Result<(), RtcError> {
self.set_time(v.now().map_err(|e| e.into())?)
}
#[inline]
pub fn set_timer_interrupt(&mut self, en: bool, pulse: bool) -> Result<(), RtcError> {
self.timer_clear_state()?; let v = self.read_reg(CMD_TIMER_MODE)?;
self.i2c.write(ADDR, &[
CMD_TIMER_MODE,
(v & 0xFC) | if en { 0x2 } else { 0 } | if pulse { 0x1 } else { 0 },
])?;
Ok(())
}
#[inline]
fn now_inner(&mut self) -> Result<Time, RtcError> {
let mut b: [u8; 7] = [0u8; 7];
self.i2c.write_single_then_read(ADDR, CMD_STATUS, &mut b)?;
let mut d = Time::new(
decode(b[6]) as u16 + 0x7D0,
decode(b[5]).into(),
decode(b[3]),
decode(b[2]),
decode(b[1]),
decode(b[0] & 0x7F),
decode(b[4]).into(),
);
if d.hours >= 24 {
d.hours = cmp::max(decode(b[2] & 0x7), 9) + if b[2] & 0x10 != 0 { 10 } else { 0 } + if b[2] & 0x20 != 0 { 12 } else { 0 }
}
if !d.is_valid() { Err(RtcError::InvalidTime) } else { Ok(d) }
}
#[inline]
fn read_reg(&mut self, r: u8) -> Result<u8, RtcError> {
self.i2c.write_single(ADDR, r)?;
Ok(self.i2c.read_single(ADDR)?)
}
}
impl TimeSource for PcfRtc<'_> {
type Error = RtcError;
#[inline(always)]
fn now(&mut self) -> Result<Time, RtcError> {
self.now_inner()
}
}
impl Acknowledge for PcfRtc<'_> {
#[inline(always)]
fn ack_interrupt(&mut self) -> bool {
self.alarm_clear_state().unwrap_or(false) | self.timer_clear_state().unwrap_or(false)
}
}
unsafe impl Send for PcfRtc<'_> {}
#[inline]
fn encode(v: u8) -> u8 {
let i = v / 10;
(v - (i * 10)) | (i << 4)
}
#[inline]
fn decode(v: u8) -> u8 {
(v & 0xF) + ((v >> 4) & 0xF).wrapping_mul(10)
}
#[inline]
fn ms_to_ticks(v: u32) -> Option<(u8, PcfTick)> {
match v {
0..=62 => Some((cmp::min((v * 1_000) / 244, 0xFF) as u8, PcfTick::Speed4kHz)),
0..=3_800 => Some((cmp::min(v / 15, 0xFF) as u8, PcfTick::Speed64Hz)),
0..=255_000 => Some((cmp::min(v / 1_000, 0xFF) as u8, PcfTick::Speed1Hz)),
0..=15_300_000 => Some((cmp::min((v / 1_000) / 60, 0xFF) as u8, PcfTick::Slow)),
_ => None,
}
}