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