use core::marker::PhantomData;
use embassy_hal_internal::PeripheralType;
use stm32_metapac::iwdg::vals::{Key, Pr};
use crate::Peri;
use crate::rcc::LSI_FREQ;
pub struct IndependentWatchdog<'d, T: Instance> {
wdg: PhantomData<&'d mut T>,
}
const MAX_RL: u16 = 0xFFF;
const fn get_timeout_us(prescaler: u16, reload_value: u16) -> u32 {
1_000_000 * (reload_value + 1) as u32 / (LSI_FREQ.0 / prescaler as u32)
}
const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 {
(timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16 - 1
}
impl<'d, T: Instance> IndependentWatchdog<'d, T> {
pub fn new(_instance: Peri<'d, T>, timeout_us: u32) -> Self {
let psc_power = unwrap!((2..=8).find(|psc_power| {
let psc = 2u16.pow(*psc_power);
timeout_us <= get_timeout_us(psc, MAX_RL)
}));
let psc = 2u16.pow(psc_power);
#[cfg(not(iwdg_v3))]
assert!(psc <= 256, "IWDG prescaler should be no more than 256");
#[cfg(iwdg_v3)] assert!(psc <= 1024, "IWDG prescaler should be no more than 1024");
let pr = psc_power as u8 - 2;
let rl = reload_value(psc, timeout_us);
let wdg = T::regs();
wdg.kr().write(|w| w.set_key(Key::ENABLE));
wdg.pr().write(|w| w.set_pr(Pr::from_bits(pr)));
wdg.rlr().write(|w| w.set_rl(rl));
trace!(
"Watchdog configured with {}us timeout, desired was {}us (PR={}, RL={})",
get_timeout_us(psc, rl),
timeout_us,
pr,
rl
);
IndependentWatchdog { wdg: PhantomData }
}
pub fn unleash(&mut self) {
T::regs().kr().write(|w| w.set_key(Key::START));
}
pub fn pet(&mut self) {
T::regs().kr().write(|w| w.set_key(Key::RESET));
}
}
trait SealedInstance {
fn regs() -> crate::pac::iwdg::Iwdg;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType {}
foreach_peripheral!(
(iwdg, $inst:ident) => {
impl SealedInstance for crate::peripherals::$inst {
fn regs() -> crate::pac::iwdg::Iwdg {
crate::pac::$inst
}
}
impl Instance for crate::peripherals::$inst {}
};
);
#[cfg(any(wwdg, test))]
fn wwdg_ticks(duration_us: u32, pclk1_hz: u32, prescaler_mul: u32) -> u64 {
let num = duration_us as u64 * pclk1_hz as u64;
let den = prescaler_mul as u64 * 4096 * 1_000_000;
(num + den - 1) / den
}
#[cfg(wwdg)]
use stm32_metapac::wwdg::vals::Wdgtb;
#[cfg(wwdg)]
pub struct WindowWatchdog<'d, T: WwdgInstance> {
wdg: PhantomData<&'d mut T>,
counter: u8,
}
#[cfg(wwdg)]
impl<'d, T: WwdgInstance> WindowWatchdog<'d, T> {
pub fn new(_instance: Peri<'d, T>, timeout_us: u32, window_us: u32) -> Self {
assert!(window_us < timeout_us, "window_us must be less than timeout_us");
crate::rcc::enable_and_reset::<T>();
let pclk1 = T::frequency().0;
#[cfg(wwdg_v2)]
const PRESCALER_MULS: &[u32] = &[1, 2, 4, 8, 16, 32, 64, 128];
#[cfg(not(wwdg_v2))]
const PRESCALER_MULS: &[u32] = &[1, 2, 4, 8];
let (prescaler_mul, ticks) = unwrap!(
PRESCALER_MULS.iter().find_map(|&mul| {
let t = wwdg_ticks(timeout_us, pclk1, mul);
if (1..=64).contains(&t) { Some((mul, t)) } else { None }
}),
"WWDG: timeout_us is out of range for all prescalers"
);
let t_val = 63u8 + ticks as u8;
let den = prescaler_mul as u64 * 4096 * 1_000_000;
let closed_ticks = (window_us as u64 * pclk1 as u64) / den;
let w_val = t_val - closed_ticks as u8;
let wdgtb = Wdgtb::from_bits(prescaler_mul.trailing_zeros() as u8);
let regs = T::regs();
regs.cfr().write(|cfr| {
cfr.set_wdgtb(wdgtb);
cfr.set_w(w_val);
});
regs.cr().write(|cr| {
cr.set_t(t_val);
cr.set_wdga(true);
});
trace!(
"WWDG configured: timeout={}us window={}us pclk1={} prescaler=x{} T={} W={}",
timeout_us, window_us, pclk1, prescaler_mul, t_val, w_val,
);
WindowWatchdog {
wdg: PhantomData,
counter: t_val,
}
}
pub fn pet(&mut self) {
T::regs().cr().write(|cr| {
cr.set_t(self.counter);
cr.set_wdga(true);
});
}
}
#[cfg(wwdg)]
trait WwdgSealedInstance {
fn regs() -> crate::pac::wwdg::Wwdg;
}
#[cfg(wwdg)]
#[allow(private_bounds)]
pub trait WwdgInstance: WwdgSealedInstance + PeripheralType + crate::rcc::RccPeripheral {}
#[cfg(wwdg)]
foreach_peripheral!(
(wwdg, $inst:ident) => {
impl WwdgSealedInstance for crate::peripherals::$inst {
fn regs() -> crate::pac::wwdg::Wwdg {
crate::pac::$inst
}
}
impl WwdgInstance for crate::peripherals::$inst {}
};
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_compute_timeout_us() {
assert_eq!(125, get_timeout_us(4, 0));
assert_eq!(512_000, get_timeout_us(4, MAX_RL));
assert_eq!(8_000, get_timeout_us(256, 0));
assert_eq!(32_768_000, get_timeout_us(256, MAX_RL));
assert_eq!(8_000_000, get_timeout_us(64, 3999));
}
#[test]
fn can_compute_reload_value() {
assert_eq!(0xFFF, reload_value(4, 512_000));
assert_eq!(0xFFF, reload_value(256, 32_768_000));
assert_eq!(3999, reload_value(64, 8_000_000));
}
}
#[cfg(test)]
mod wwdg_tests {
use super::wwdg_ticks;
const PCLK1: u32 = 64_000_000;
#[test]
fn ticks_rounded_up() {
assert_eq!(16, wwdg_ticks(1_000, PCLK1, 1));
}
#[test]
fn ticks_exact() {
assert_eq!(16, wwdg_ticks(1_024, PCLK1, 1));
}
#[test]
fn ticks_just_above_exact() {
assert_eq!(17, wwdg_ticks(1_025, PCLK1, 1));
}
#[test]
fn ticks_large_prescaler() {
assert_eq!(13, wwdg_ticks(100_000, PCLK1, 128));
}
#[test]
fn ticks_minimum_duration() {
assert_eq!(1, wwdg_ticks(1, PCLK1, 1));
}
}