#![no_implicit_prelude]
extern crate core;
extern crate rpsp;
use core::cmp::Ord;
use core::convert::{From, 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::{Month, Time, Weekday};
use crate::Slice;
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]
pub fn new(i2c: impl Into<I2cBus<'a, Controller>>) -> PcfRtc<'a> {
PcfRtc { i2c: i2c.into() }
}
#[inline]
pub fn i2c_bus(&self) -> &I2c<Controller> {
&self.i2c
}
#[inline]
pub fn reset(&mut self) -> Result<(), RtcError> {
self.i2c.write(ADDR, &[CMD_CTRL_1, 0x58])?;
loop {
self.i2c.write(ADDR, &[CMD_STATUS, 0])?;
if self.is_stable()? {
break;
}
}
Ok(())
}
#[inline]
pub fn now(&mut self) -> Result<Time, RtcError> {
self.now_inner()
}
#[inline]
pub fn get_byte(&mut self) -> Result<u8, RtcError> {
self.read_reg(CMD_SAVED)
}
#[inline]
pub fn is_24hr(&mut self) -> Result<bool, RtcError> {
Ok(self.read_reg(CMD_CTRL_1)? & 0x2 == 0)
}
#[inline]
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, 0x80, 0x80, 0x80, 0x80, 0x80])?;
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]
pub fn alarm_state(&mut self) -> Result<bool, RtcError> {
Ok(self.read_reg(CMD_CTRL_2)? & 0x40 != 0)
}
#[inline]
pub fn timer_state(&mut self) -> Result<bool, RtcError> {
Ok(self.read_reg(CMD_CTRL_2)? & 0x8 != 0)
}
#[inline]
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]
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(0x80, encode),
v.mins.map_or(0x80, encode),
v.hours.map_or(0x80, encode),
v.day.map_or(0x80, |i| encode(i.get())),
v.weekday.map_or(0x80, |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, unsafe {
(v & 0xE7) | ((speed as u8) & 0x3).unchecked_shl(3) | 0x4
}])?;
Ok(())
}
#[inline]
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.read_u8(6)) as u16 + 0x7D0,
Month::from(decode(b.read_u8(5))),
decode(b.read_u8(3)),
decode(b.read_u8(2)),
decode(b.read_u8(1)),
decode(b.read_u8(0) & 0x7F),
Weekday::from(decode(b.read_u8(4))),
);
if d.hours >= 24 {
let h = b.read_u8(2);
d.hours = decode(h & 0x7).max(9) + if h & 0x10 != 0 { 10 } else { 0 } + if h & 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]
fn now(&mut self) -> Result<Time, RtcError> {
self.now_inner()
}
}
impl Acknowledge for PcfRtc<'_> {
#[inline]
fn ack_interrupt(&mut self) -> bool {
self.alarm_clear_state()
.or_else(|_| self.timer_clear_state())
.unwrap_or(false)
}
}
unsafe impl Send for PcfRtc<'_> {}
#[inline]
fn encode(v: u8) -> u8 {
let i = v / 10;
unsafe { (v - (i * 10)) | i.unchecked_shl(4) }
}
#[inline]
fn decode(v: u8) -> u8 {
unsafe { (v & 0xF) + (v.unchecked_shr(4) & 0xF).wrapping_mul(10) }
}
#[inline]
fn ms_to_ticks(v: u32) -> Option<(u8, PcfTick)> {
match v {
0..=62 => Some(((v * 1_000 / 244) as u8, PcfTick::Speed4kHz)),
0..=3_800 => Some(((v / 15) as u8, PcfTick::Speed64Hz)),
0..=255_000 => Some(((v / 1_000) as u8, PcfTick::Speed1Hz)),
0..=15_300_000 => Some((((v / 1_000) / 60) as u8, PcfTick::Slow)),
_ => None,
}
}