use crate::clock::{Aclk, Smclk};
use core::marker::PhantomData;
use embedded_hal::timer::{Cancel, CountDown, Periodic};
use embedded_hal::watchdog::{Watchdog, WatchdogDisable, WatchdogEnable};
use msp430fr247x as pac;
use pac::wdt_a::wdtctl::WDTSSEL_A;
const PASSWORD: u8 = 0x5A;
pub use pac::wdt_a::wdtctl::WDTIS_A as WdtClkPeriods;
mod sealed {
use super::*;
pub trait SealedWatchdogSelect {}
impl SealedWatchdogSelect for WatchdogMode {}
impl SealedWatchdogSelect for IntervalMode {}
}
pub struct Wdt<MODE> {
_mode: PhantomData<MODE>,
periph: pac::WDT_A,
}
impl Wdt<WatchdogMode> {
pub fn constrain(wdt: pac::WDT_A) -> Self {
wdt.wdtctl.write(|w| {
unsafe { w.wdtpw().bits(PASSWORD) }
.wdthold()
.hold()
.wdtssel()
.variant(WDTSSEL_A::VLOCLK)
});
Wdt {
_mode: PhantomData,
periph: wdt,
}
}
}
pub struct WatchdogMode;
pub struct IntervalMode;
pub trait WatchdogSelect: sealed::SealedWatchdogSelect {
#[doc(hidden)]
fn mode_bit() -> bool;
}
impl WatchdogSelect for WatchdogMode {
#[inline(always)]
fn mode_bit() -> bool {
false
}
}
impl WatchdogSelect for IntervalMode {
#[inline(always)]
fn mode_bit() -> bool {
true
}
}
type WdtWriter = pac::wdt_a::wdtctl::W;
impl<MODE: WatchdogSelect> Wdt<MODE> {
#[inline(always)]
fn prewrite(w: &mut WdtWriter, bits: u16) -> &mut WdtWriter {
unsafe { w.bits(bits).wdtpw().bits(PASSWORD) }
.wdttmsel()
.bit(MODE::mode_bit())
}
#[inline]
fn set_clk(&mut self, clk_src: WDTSSEL_A) -> &mut Self {
self.periph.wdtctl.write(|w| {
Self::prewrite(w, 0)
.wdthold()
.hold()
.wdtcntcl()
.set_bit()
});
self.periph.wdtctl.write(|w| {
Self::prewrite(w, 0)
.wdtssel()
.variant(clk_src)
.wdthold()
.hold()
});
self
}
#[inline]
pub fn set_aclk(&mut self, _clks: &Aclk) -> &mut Self {
self.set_clk(WDTSSEL_A::ACLK)
}
#[inline]
pub fn set_vloclk(&mut self) -> &mut Self {
self.set_clk(WDTSSEL_A::VLOCLK)
}
#[inline]
pub fn set_smclk(&mut self, _clks: &Smclk) -> &mut Self {
self.set_clk(WDTSSEL_A::SMCLK)
}
#[inline]
fn unpause_and_set_time(&mut self, periods: WdtClkPeriods) {
self.periph.wdtctl.modify(|r, w| {
Self::prewrite(w, r.bits())
.wdtcntcl()
.set_bit()
.wdthold()
.unhold()
.wdtis()
.variant(periods)
});
}
#[inline]
fn pause(&mut self) {
self.periph
.wdtctl
.modify(|r, w| Self::prewrite(w, r.bits()).wdthold().hold());
}
}
impl Watchdog for Wdt<WatchdogMode> {
#[inline]
fn feed(&mut self) {
self.periph
.wdtctl
.modify(|r, w| Self::prewrite(w, r.bits()).wdtcntcl().set_bit());
}
}
impl WatchdogEnable for Wdt<WatchdogMode> {
type Time = WdtClkPeriods;
#[inline]
fn start<T>(&mut self, period: T)
where
T: Into<Self::Time>,
{
self.unpause_and_set_time(period.into());
}
}
impl WatchdogDisable for Wdt<WatchdogMode> {
#[inline]
fn disable(&mut self) {
self.pause();
}
}
impl CountDown for Wdt<IntervalMode> {
type Time = WdtClkPeriods;
#[inline]
fn start<T>(&mut self, count: T)
where
T: Into<Self::Time>,
{
self.unpause_and_set_time(count.into());
}
#[inline]
fn wait(&mut self) -> nb::Result<(), void::Void> {
let sfr = unsafe { &*pac::SFR::ptr() };
if sfr.sfrifg1.read().wdtifg().is_wdtifg_1() {
sfr.sfrifg1.write(|w| w.wdtifg().clear_bit());
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}
impl Cancel for Wdt<IntervalMode> {
type Error = void::Void;
#[inline]
fn cancel(&mut self) -> Result<(), Self::Error> {
self.pause();
Ok(())
}
}
impl Periodic for Wdt<IntervalMode> {}
impl Wdt<WatchdogMode> {
#[inline]
pub fn to_interval(self) -> Wdt<IntervalMode> {
let mut wdt = Wdt {
_mode: PhantomData,
periph: self.periph,
};
wdt.pause();
wdt
}
}
impl Wdt<IntervalMode> {
#[inline]
pub fn to_watchdog(self) -> Wdt<WatchdogMode> {
let mut wdt = Wdt {
_mode: PhantomData,
periph: self.periph,
};
wdt.pause();
let sfr = unsafe { &*pac::SFR::ptr() };
sfr.sfrifg1.write(|w| w.wdtifg().clear_bit());
wdt
}
#[inline]
pub fn enable_interrupts(&mut self) -> &mut Self {
let sfr = unsafe { &*pac::SFR::ptr() };
sfr.sfrie1.write(|w| w.wdtie().set_bit());
self
}
#[inline]
pub fn disable_interrupts(&mut self) -> &mut Self {
let sfr = unsafe { &*pac::SFR::ptr() };
sfr.sfrie1.write(|w| w.wdtie().clear_bit());
self
}
}