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 | |