use crate::pac::{FWDGT, WWDGT};
use crate::unit::MicroSeconds;
use embedded_hal::watchdog::{Watchdog, Enable};
use core::convert::Infallible;
const FWDGT_CMD_ENABLE: u16 = 0xCCCC;
const FWDGT_CMD_RELOAD: u16 = 0xAAAA;
const FWDGT_CMD_WRITE_ACCESS_ENABLE: u16 = 0x5555;
const FWDGT_CMD_WRITE_ACCESS_DISABLE: u16 = 0x0000;
const FWDGT_MAX_PSC: u8 = 0b110;
const FWDGT_MAX_RLD: u16 = 0xFFF;
pub struct Disabled;
pub struct Enabled;
pub struct Free<STATE> {
fwdgt: FWDGT,
state: STATE,
}
impl<STATE> Free<STATE> {
pub fn new(fwdgt: FWDGT) -> Free<Disabled> {
Free { fwdgt, state: Disabled }
}
pub fn interval(&self) -> MicroSeconds {
while self.rud_or_pud_updating() {}
let psc: u32 = self.fwdgt.psc.read().psc().bits().into();
let rld: u32 = self.fwdgt.rld.read().rld().bits().into();
let time_us = (rld + 1) * (1 << psc) * 100;
MicroSeconds(time_us)
}
}
impl<STATE> Free<STATE> {
#[inline]
fn rud_or_pud_updating(&self) -> bool {
let stat = self.fwdgt.stat.read();
stat.rud().bits() || stat.pud().bits()
}
#[inline]
fn calc_psc_rld(time_us: u32) -> (u8, u16) {
let mut psc = 0;
let mut max_time = 409_600; while psc < FWDGT_MAX_PSC && max_time < time_us {
psc += 1;
max_time *= 2;
}
let mut rld = u32::saturating_sub(time_us, 1) / (100 * (1 << psc));
if rld > FWDGT_MAX_RLD as u32 {
rld = FWDGT_MAX_RLD as u32;
}
(psc, rld as u16)
}
fn access_protected<A, F: FnMut(&FWDGT) -> A>(&self, mut f: F) -> A {
self.fwdgt
.ctl
.write(|w| unsafe { w.cmd().bits(FWDGT_CMD_WRITE_ACCESS_ENABLE) });
let ans = f(&self.fwdgt);
self.fwdgt
.ctl
.write(|w| unsafe { w.cmd().bits(FWDGT_CMD_WRITE_ACCESS_DISABLE) });
ans
}
}
impl Free<Enabled> {
pub fn set_period(&mut self, period: impl Into<MicroSeconds>) {
let (psc, rld) = Self::calc_psc_rld(period.into().0);
while self.rud_or_pud_updating() {}
riscv::interrupt::free(|_| {
self.access_protected(|fwdgt| {
fwdgt.psc.modify(|_, w| unsafe { w.psc().bits(psc) });
fwdgt.rld.modify(|_, w| unsafe { w.rld().bits(rld) });
});
self.fwdgt
.ctl
.write(|w| unsafe { w.cmd().bits(FWDGT_CMD_RELOAD) });
});
}
}
impl Enable for Free<Disabled> {
type Error = Infallible;
type Time = MicroSeconds;
type Target = Free<Enabled>;
fn try_start<T>(self, period: T) -> Result<Free<Enabled>, Self::Error>
where
T: Into<Self::Time>,
{
let (psc, rld) = Self::calc_psc_rld(period.into().0);
while self.rud_or_pud_updating() {}
riscv::interrupt::free(|_| {
self.access_protected(|fwdgt| {
fwdgt.psc.modify(|_, w| unsafe { w.psc().bits(psc) });
fwdgt.rld.modify(|_, w| unsafe { w.rld().bits(rld) });
});
self.fwdgt
.ctl
.write(|w| unsafe { w.cmd().bits(FWDGT_CMD_RELOAD) });
self.fwdgt
.ctl
.write(|w| unsafe { w.cmd().bits(FWDGT_CMD_ENABLE) });
});
Ok(Free { fwdgt: self.fwdgt, state: Enabled })
}
}
impl Watchdog for Free<Enabled> {
type Error = Infallible;
fn try_feed(&mut self) -> Result<(), Self::Error> {
self.fwdgt
.ctl
.write(|w| unsafe { w.cmd().bits(FWDGT_CMD_RELOAD) });
Ok(())
}
}
pub struct Window {
wwdgt: WWDGT,
}