| 1 | //! Watchdog Timer (IWDG, WWDG) |
| 2 | use core::marker::PhantomData; |
| 3 | |
| 4 | use embassy_hal_internal::{into_ref, Peripheral}; |
| 5 | use stm32_metapac::iwdg::vals::{Key, Pr}; |
| 6 | |
| 7 | use crate::rcc::LSI_FREQ; |
| 8 | |
| 9 | /// Independent watchdog (IWDG) driver. |
| 10 | pub struct IndependentWatchdog<'d, T: Instance> { |
| 11 | wdg: PhantomData<&'d mut T>, |
| 12 | } |
| 13 | |
| 14 | // 12-bit counter |
| 15 | const MAX_RL: u16 = 0xFFF; |
| 16 | |
| 17 | /// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler |
| 18 | const fn get_timeout_us(prescaler: u16, reload_value: u16) -> u32 { |
| 19 | 1_000_000 * (reload_value + 1) as u32 / (LSI_FREQ.0 / prescaler as u32) |
| 20 | } |
| 21 | |
| 22 | /// Calculates watchdog reload value for the given prescaler and desired timeout |
| 23 | const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 { |
| 24 | (timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16 - 1 |
| 25 | } |
| 26 | |
| 27 | impl<'d, T: Instance> IndependentWatchdog<'d, T> { |
| 28 | /// Creates an IWDG (Independent Watchdog) instance with a given timeout value in microseconds. |
| 29 | /// |
| 30 | /// [Self] has to be started with [Self::unleash()]. |
| 31 | /// Once timer expires, MCU will be reset. To prevent this, timer must be reloaded by repeatedly calling [Self::pet()] within timeout interval. |
| 32 | pub fn new(_instance: impl Peripheral<P = T> + 'd, timeout_us: u32) -> Self { |
| 33 | into_ref!(_instance); |
| 34 | |
| 35 | // Find lowest prescaler value, which makes watchdog period longer or equal to timeout. |
| 36 | // This iterates from 4 (2^2) to 256 (2^8). |
| 37 | let psc_power = unwrap!((2..=8).find(|psc_power| { |
| 38 | let psc = 2u16.pow(*psc_power); |
| 39 | timeout_us <= get_timeout_us(psc, MAX_RL) |
| 40 | })); |
| 41 | |
| 42 | // Prescaler value |
| 43 | let psc = 2u16.pow(psc_power); |
| 44 | |
| 45 | #[cfg (not(iwdg_v3))] |
| 46 | assert!(psc <= 256, "IWDG prescaler should be no more than 256" ); |
| 47 | #[cfg (iwdg_v3)] // H5, U5, WBA |
| 48 | assert!(psc <= 1024, "IWDG prescaler should be no more than 1024" ); |
| 49 | |
| 50 | // Convert prescaler power to PR register value |
| 51 | let pr = psc_power as u8 - 2; |
| 52 | |
| 53 | // Reload value |
| 54 | let rl = reload_value(psc, timeout_us); |
| 55 | |
| 56 | let wdg = T::regs(); |
| 57 | wdg.kr().write(|w| w.set_key(Key::ENABLE)); |
| 58 | wdg.pr().write(|w| w.set_pr(Pr::from_bits(pr))); |
| 59 | wdg.rlr().write(|w| w.set_rl(rl)); |
| 60 | |
| 61 | trace!( |
| 62 | "Watchdog configured with {}us timeout, desired was {}us (PR={}, RL={})" , |
| 63 | get_timeout_us(psc, rl), |
| 64 | timeout_us, |
| 65 | pr, |
| 66 | rl |
| 67 | ); |
| 68 | |
| 69 | IndependentWatchdog { wdg: PhantomData } |
| 70 | } |
| 71 | |
| 72 | /// Unleash (start) the watchdog. |
| 73 | pub fn unleash(&mut self) { |
| 74 | T::regs().kr().write(|w| w.set_key(Key::START)); |
| 75 | } |
| 76 | |
| 77 | /// Pet (reload, refresh) the watchdog. |
| 78 | pub fn pet(&mut self) { |
| 79 | T::regs().kr().write(|w| w.set_key(Key::RESET)); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | trait SealedInstance { |
| 84 | fn regs() -> crate::pac::iwdg::Iwdg; |
| 85 | } |
| 86 | |
| 87 | /// IWDG instance trait. |
| 88 | #[allow (private_bounds)] |
| 89 | pub trait Instance: SealedInstance {} |
| 90 | |
| 91 | foreach_peripheral!( |
| 92 | (iwdg, $inst:ident) => { |
| 93 | impl SealedInstance for crate::peripherals::$inst { |
| 94 | fn regs() -> crate::pac::iwdg::Iwdg { |
| 95 | crate::pac::$inst |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | impl Instance for crate::peripherals::$inst {} |
| 100 | }; |
| 101 | ); |
| 102 | |
| 103 | #[cfg (test)] |
| 104 | mod tests { |
| 105 | use super::*; |
| 106 | |
| 107 | #[test ] |
| 108 | fn can_compute_timeout_us() { |
| 109 | assert_eq!(125, get_timeout_us(4, 0)); |
| 110 | assert_eq!(512_000, get_timeout_us(4, MAX_RL)); |
| 111 | |
| 112 | assert_eq!(8_000, get_timeout_us(256, 0)); |
| 113 | assert_eq!(32_768_000, get_timeout_us(256, MAX_RL)); |
| 114 | |
| 115 | assert_eq!(8_000_000, get_timeout_us(64, 3999)); |
| 116 | } |
| 117 | |
| 118 | #[test ] |
| 119 | fn can_compute_reload_value() { |
| 120 | assert_eq!(0xFFF, reload_value(4, 512_000)); |
| 121 | assert_eq!(0xFFF, reload_value(256, 32_768_000)); |
| 122 | |
| 123 | assert_eq!(3999, reload_value(64, 8_000_000)); |
| 124 | } |
| 125 | } |
| 126 | |