use crate::peripherals::Wdt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResetPulseLength {
Cycles2 = 0,
Cycles4 = 1,
Cycles8 = 2,
Cycles16 = 3,
Cycles32 = 4,
Cycles64 = 5,
Cycles128 = 6,
Cycles256 = 7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WdtMode {
SingleInterrupt,
DoubleInterrupt,
}
pub struct Watchdog<'d> {
_wdt: Wdt<'d>,
}
pub const WDT_CLOCK_HZ: u32 = crate::soc::ws63::TCXO_HZ;
pub const WDT_MAX_LOAD: u32 = 0x00FF_FFFF;
const WDT_LOAD_RESEV: u32 = 8;
impl<'d> Watchdog<'d> {
pub fn new(wdt: Wdt<'d>) -> Self {
let wd = Self { _wdt: wdt };
wd.unlock();
wd
}
fn regs(&self) -> &'static ws63_pac::wdt::RegisterBlock {
unsafe { &*Wdt::ptr() }
}
fn unlock(&self) {
unsafe {
self.regs().wdt_lock().write(|w| w.bits(0x5A5A5A5A));
}
}
fn lock(&self) {
unsafe {
self.regs().wdt_lock().write(|w| w.bits(0x0000_0000));
}
}
pub fn configure(&mut self, timeout_ms: u32, mode: WdtMode, reset_enable: bool, reset_pulse: ResetPulseLength) {
let cycles = (timeout_ms as u64 * WDT_CLOCK_HZ as u64) / 1000;
let load = ((cycles >> WDT_LOAD_RESEV) as u32).min(WDT_MAX_LOAD);
self.unlock();
unsafe {
self.regs().wdt_load().write(|w| w.bits(load << WDT_LOAD_RESEV));
}
let mut cr: u32 = 0;
cr |= 0x01; if reset_enable {
cr |= 1 << 2; }
cr |= (reset_pulse as u32) << 3; cr |= 1 << 6; if matches!(mode, WdtMode::DoubleInterrupt) {
cr |= 1 << 7; }
unsafe {
self.regs().wdt_cr().write(|w| w.bits(cr));
}
self.request_counter();
self.lock();
}
pub fn enable(&mut self) {
self.unlock();
let cr = self.regs().wdt_cr().read().bits();
unsafe {
self.regs().wdt_cr().write(|w| w.bits(cr | 0x01));
}
self.lock();
}
pub fn disable(&mut self) {
self.unlock();
let cr = self.regs().wdt_cr().read().bits();
unsafe {
self.regs().wdt_cr().write(|w| w.bits(cr & !0x01));
}
self.lock();
}
pub fn feed(&mut self) {
self.unlock();
unsafe {
self.regs().wdt_restart().write(|w| w.bits(0x0000_0001));
}
self.lock();
}
pub fn counter_value(&self) -> u32 {
self.request_counter();
self.regs().wdt_cnt().read().bits()
}
fn request_counter(&self) {
unsafe {
self.regs().wdt_ccvr_en().write(|w| w.bits(0x01));
}
while self.regs().wdt_ccvr_en().read().bits() & 0x02 == 0 {}
}
pub fn interrupt_pending(&self) -> bool {
self.regs().wdt_raw_intr().read().bits() & 0x01 != 0
}
pub fn interrupt_masked(&self) -> bool {
self.regs().wdt_intr().read().bits() & 0x01 != 0
}
pub fn clear_interrupt(&self) {
let _ = self.regs().wdt_eoi().read().bits();
}
pub fn enable_interrupt(&mut self) {
self.unlock();
let cr = self.regs().wdt_cr().read().bits();
unsafe {
self.regs().wdt_cr().write(|w| w.bits(cr & !(1 << 6)));
}
self.lock();
}
pub fn disable_interrupt(&mut self) {
self.unlock();
let cr = self.regs().wdt_cr().read().bits();
unsafe {
self.regs().wdt_cr().write(|w| w.bits(cr | (1 << 6)));
}
self.lock();
}
pub fn is_busy(&self) -> bool {
self.regs().wdt_status().read().bits() & 0x01 == 0
}
}