stm32f4xx_hal/
watchdog.rs1use crate::pac::{DBGMCU, IWDG};
4use core::fmt;
5use embedded_hal_02::watchdog::{Watchdog, WatchdogEnable};
6use fugit::MillisDurationU32 as MilliSeconds;
7
8pub struct IndependentWatchdog {
10 iwdg: IWDG,
11}
12
13#[cfg(feature = "defmt")]
14impl defmt::Format for IndependentWatchdog {
15 fn format(&self, f: defmt::Formatter) {
16 defmt::write!(f, "IndependentWatchdog");
17 }
18}
19
20impl fmt::Debug for IndependentWatchdog {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 f.write_str("IndependentWatchdog")
23 }
24}
25
26const LSI_KHZ: u32 = 32;
27const MAX_PR: u32 = 0b110;
28const MAX_RL: u16 = 0xFFF;
29const KR_ACCESS: u16 = 0x5555;
30const KR_RELOAD: u16 = 0xAAAA;
31const KR_START: u16 = 0xCCCC;
32
33impl IndependentWatchdog {
34 pub fn new(iwdg: IWDG) -> Self {
37 IndependentWatchdog { iwdg }
38 }
39
40 pub fn stop_on_debug(&self, dbgmcu: &DBGMCU, stop: bool) {
42 dbgmcu.apb1_fz().modify(|_, w| w.dbg_iwdg_stop().bit(stop));
43 }
44
45 fn setup(&self, timeout_ms: MilliSeconds) {
47 assert!(timeout_ms.ticks() < (1 << 15), "Watchdog timeout to high");
48 let pr = match timeout_ms.ticks() {
49 0 => 0b000, t if t <= (MAX_PR + 1) * 8 / LSI_KHZ => 0b001,
51 t if t <= (MAX_PR + 1) * 16 / LSI_KHZ => 0b010,
52 t if t <= (MAX_PR + 1) * 32 / LSI_KHZ => 0b011,
53 t if t <= (MAX_PR + 1) * 64 / LSI_KHZ => 0b100,
54 t if t <= (MAX_PR + 1) * 128 / LSI_KHZ => 0b101,
55 _ => 0b110,
56 };
57
58 let max_period = Self::timeout_period(pr, MAX_RL);
59 let max_rl = u32::from(MAX_RL);
60 let rl = (timeout_ms.ticks() * max_rl / max_period).min(max_rl) as u16;
61
62 self.access_registers(|iwdg| {
63 iwdg.pr().modify(|_, w| unsafe { w.pr().bits(pr) });
64 iwdg.rlr().modify(|_, w| w.rl().set(rl));
65 });
66 }
67
68 fn is_pr_updating(&self) -> bool {
69 self.iwdg.sr().read().pvu().bit()
70 }
71
72 pub fn interval(&self) -> MilliSeconds {
74 while self.is_pr_updating() {}
75
76 let pr = self.iwdg.pr().read().pr().bits();
77 let rl = self.iwdg.rlr().read().rl().bits();
78 let ms = Self::timeout_period(pr, rl);
79 MilliSeconds::from_ticks(ms)
80 }
81
82 fn timeout_period(pr: u8, rl: u16) -> u32 {
86 let divider: u32 = match pr {
87 0b000 => 4,
88 0b001 => 8,
89 0b010 => 16,
90 0b011 => 32,
91 0b100 => 64,
92 0b101 => 128,
93 0b110 => 256,
94 0b111 => 256,
95 _ => unreachable!(),
96 };
97 (u32::from(rl) + 1) * divider / LSI_KHZ
98 }
99
100 fn access_registers<A, F: FnMut(&IWDG) -> A>(&self, mut f: F) -> A {
101 self.iwdg.kr().write(|w| unsafe { w.key().bits(KR_ACCESS) });
103 let a = f(&self.iwdg);
104
105 self.iwdg.kr().write(|w| unsafe { w.key().bits(KR_RELOAD) });
107 a
108 }
109
110 pub fn start(&mut self, period: MilliSeconds) {
111 self.setup(period);
112
113 self.iwdg.kr().write(|w| unsafe { w.key().bits(KR_START) });
114 }
115
116 pub fn feed(&mut self) {
117 self.iwdg.kr().write(|w| unsafe { w.key().bits(KR_RELOAD) });
118 }
119}
120
121impl WatchdogEnable for IndependentWatchdog {
122 type Time = MilliSeconds;
123
124 fn start<T: Into<Self::Time>>(&mut self, period: T) {
125 self.start(period.into())
126 }
127}
128
129impl Watchdog for IndependentWatchdog {
130 fn feed(&mut self) {
131 self.feed()
132 }
133}