Skip to content

Commit f1e387f

Browse files
committed
Simplify watchdog interval calculation
In addition to the changes, test them
1 parent 8388b57 commit f1e387f

File tree

2 files changed

+98
-70
lines changed

2 files changed

+98
-70
lines changed

src/watchdog.rs

Lines changed: 60 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,37 @@
66
//!
77
//! [examples/can.rs]: https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.6.1/examples/can.rs
88
9+
use embedded_time::fixed_point::FixedPoint;
10+
911
use crate::hal::watchdog::{Watchdog, WatchdogEnable};
1012

11-
use crate::pac::{DBGMCU, IWDG};
12-
use crate::time::duration::*;
13+
use crate::pac::{iwdg::pr::PR_A, DBGMCU, IWDG};
14+
use crate::time::duration::Milliseconds;
15+
use crate::time::rate::Kilohertz;
1316

14-
const LSI_KHZ: u32 = 40;
15-
const MAX_PR: u8 = 8;
16-
const MAX_RL: u16 = 0x1000;
17+
/// Frequency of the watchdog peripheral clock
18+
const LSI: Kilohertz = Kilohertz(40);
19+
// const MAX_PRESCALER: u8 = 0b0111;
20+
const MAX_PRESCALER: PR_A = PR_A::DIVIDEBY256;
21+
const MAX_RELOAD: u32 = 0x0FFF;
1722

1823
/// Independent Watchdog Peripheral
1924
pub struct IndependentWatchDog {
2025
iwdg: IWDG,
2126
}
2227

28+
fn into_division_value(psc: PR_A) -> u32 {
29+
match psc {
30+
PR_A::DIVIDEBY4 => 4,
31+
PR_A::DIVIDEBY8 => 8,
32+
PR_A::DIVIDEBY16 => 16,
33+
PR_A::DIVIDEBY32 => 32,
34+
PR_A::DIVIDEBY64 => 64,
35+
PR_A::DIVIDEBY128 => 128,
36+
PR_A::DIVIDEBY256 | PR_A::DIVIDEBY256BIS => 256,
37+
}
38+
}
39+
2340
impl IndependentWatchDog {
2441
/// Creates a new [`IndependentWatchDog`] without starting it.
2542
///
@@ -35,52 +52,53 @@ impl IndependentWatchDog {
3552
dbg.apb1_fz.modify(|_, w| w.dbg_iwdg_stop().bit(stop));
3653
}
3754

38-
fn setup(&self, timeout_ms: u32) {
39-
let mut pr = 0;
40-
while pr < MAX_PR && Self::timeout_period(pr, MAX_RL) < timeout_ms {
41-
pr += 1;
55+
/// Find and setup the next best prescaler and reload value for the selected timeout
56+
fn setup(&self, timeout: Milliseconds) {
57+
let mut reload: u32 =
58+
timeout.integer() * LSI.integer() / into_division_value(PR_A::DIVIDEBY4);
59+
60+
// Reload is potentially to high to be stored in the register.
61+
// The goal of this loop is to find the maximum possible reload value,
62+
// which can be stored in the register, while still guaranteeing the wanted timeout.
63+
//
64+
// This is achived by increasing the prescaler value.
65+
let mut psc = 0;
66+
loop {
67+
if psc >= MAX_PRESCALER as u8 {
68+
// ceil the value to the maximum allow reload value
69+
if reload > MAX_RELOAD {
70+
reload = MAX_RELOAD;
71+
}
72+
break;
73+
}
74+
if reload <= MAX_RELOAD {
75+
break;
76+
}
77+
psc += 1;
78+
// When the precaler value incresed, the reload value has to be halfed
79+
// so that the timeout stays the same.
80+
reload /= 2;
4281
}
4382

44-
let max_period = Self::timeout_period(pr, MAX_RL);
45-
let max_rl = u32::from(MAX_RL);
46-
let rl = (timeout_ms * max_rl / max_period).min(max_rl) as u16;
47-
4883
self.access_registers(|iwdg| {
49-
iwdg.pr.modify(|_, w| w.pr().bits(pr));
50-
iwdg.rlr.modify(|_, w| w.rl().bits(rl));
84+
iwdg.pr.modify(|_, w| w.pr().bits(psc));
85+
iwdg.rlr.modify(|_, w| w.rl().bits(reload as u16));
5186
});
52-
}
5387

54-
fn is_pr_updating(&self) -> bool {
55-
self.iwdg.sr.read().pvu().bit()
88+
// NOTE: As the watchdog can not be stopped once started,
89+
// a free method is not provided.
90+
// pub fn free(self) -> IWDG {}
5691
}
5792

58-
/// Returns the interval in ms
93+
/// Returns the currently set interval
5994
pub fn interval(&self) -> Milliseconds {
60-
while self.is_pr_updating() {}
95+
// If the prescaler was changed wait until the change procedure is finished.
96+
while self.iwdg.sr.read().pvu().bit() {}
6197

62-
let pr = self.iwdg.pr.read().pr().bits();
63-
let rl = self.iwdg.rlr.read().rl().bits();
64-
let ms = Self::timeout_period(pr, rl);
65-
Milliseconds(ms)
66-
}
98+
let psc = self.iwdg.pr.read().pr().variant();
99+
let reload = self.iwdg.rlr.read().rl().bits();
67100

68-
/// pr: Prescaler divider bits, rl: reload value
69-
///
70-
/// Returns ms
71-
fn timeout_period(pr: u8, rl: u16) -> u32 {
72-
let divider: u32 = match pr {
73-
0b000 => 4,
74-
0b001 => 8,
75-
0b010 => 16,
76-
0b011 => 32,
77-
0b100 => 64,
78-
0b101 => 128,
79-
0b110 => 256,
80-
0b111 => 256,
81-
_ => crate::panic!("Invalid IWDG prescaler divider"),
82-
};
83-
(u32::from(rl) + 1) * divider / LSI_KHZ
101+
Milliseconds((into_division_value(psc) * u32::from(reload)) / LSI.integer())
84102
}
85103

86104
fn access_registers<A, F: FnMut(&IWDG) -> A>(&self, mut f: F) -> A {
@@ -92,29 +110,13 @@ impl IndependentWatchDog {
92110
self.iwdg.kr.write(|w| w.key().reset());
93111
a
94112
}
95-
96-
/// Stop the watchdog timer
97-
pub fn stop(&mut self) -> Self {
98-
self.access_registers(|iwdg| {
99-
iwdg.pr.reset();
100-
iwdg.rlr.reset();
101-
});
102-
}
103-
104-
/// Release the independent watchdog peripheral.
105-
///
106-
/// Disables the watchdog before releasing it.
107-
pub fn free(mut self) -> IWDG {
108-
self.stop();
109-
self.iwdg
110-
}
111113
}
112114

113115
impl WatchdogEnable for IndependentWatchDog {
114116
type Time = Milliseconds;
115117

116118
fn start<T: Into<Self::Time>>(&mut self, period: T) {
117-
self.setup(period.into().integer());
119+
self.setup(period.into());
118120

119121
self.iwdg.kr.write(|w| w.key().start());
120122
}

testsuite/tests/watchdog.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,18 @@ struct State {
2222
#[defmt_test::tests]
2323
mod tests {
2424
use super::*;
25-
use defmt::{assert_eq, unwrap};
26-
use hal::time::duration::*; // imports all duration-related types and traits
27-
use hal::time::duration::{Duration, Milliseconds, Nanoseconds, Seconds};
28-
use hal::time::rate::Rate;
29-
use hal::time::rate::*; // imports all rate-related types and traits
30-
use hal::time::TimeInt;
25+
use defmt::{assert, assert_eq, unwrap};
26+
use hal::time::duration::{Milliseconds, Nanoseconds};
27+
use hal::time::fixed_point::FixedPoint;
28+
use hal::time::rate::{Kilohertz, Rate};
3129

3230
const INTERVAL: Milliseconds = Milliseconds(100u32);
3331

3432
#[init]
3533
fn init() -> State {
3634
let dp = unwrap!(hal::pac::Peripherals::take());
3735

38-
let mut rcc = dp.RCC.constrain();
36+
let rcc = dp.RCC.constrain();
3937
let mut flash = dp.FLASH.constrain();
4038

4139
// Watchdog makes sure this gets restarted periodically if nothing happens
@@ -62,7 +60,7 @@ mod tests {
6260
fn feed(state: &mut State) {
6361
// Calculate some overhead which is introduced by asm::delay
6462
let interval_wo_overhead = INTERVAL - 35.milliseconds();
65-
let delay: u32 = (u32::try_from(
63+
let delay: u32 = u32::try_from(
6664
Nanoseconds::from(interval_wo_overhead).integer()
6765
/ u64::from(
6866
state
@@ -73,7 +71,7 @@ mod tests {
7371
.integer(),
7472
),
7573
)
76-
.unwrap());
74+
.unwrap();
7775
defmt::info!("Delay = {}", delay);
7876
for _ in 0..5 {
7977
state.iwdg.feed();
@@ -82,10 +80,38 @@ mod tests {
8280
}
8381
}
8482

83+
// TODO:
84+
#[test]
85+
fn test_intervals_around_maximum(state: &mut State) {
86+
const MAX_RELOAD: u32 = 0x0FFF;
87+
const MAX_PRESCALER: u32 = 256;
88+
const LSI: Kilohertz = Kilohertz(40);
89+
let max_period = Milliseconds(MAX_PRESCALER * MAX_RELOAD / LSI.integer());
90+
let mut expected_interval = Milliseconds(15000);
91+
while expected_interval < max_period + 100.milliseconds() {
92+
expected_interval = expected_interval + 100.milliseconds();
93+
94+
state.iwdg.feed();
95+
state.iwdg.start(expected_interval);
96+
let interval = state.iwdg.interval();
97+
if interval < max_period {
98+
// Test if the approximate value is reached (it is slightly lower most of the time)
99+
assert!(
100+
interval.integer()
101+
>= expected_interval
102+
.integer()
103+
.saturating_sub(10.milliseconds().integer())
104+
);
105+
}
106+
}
107+
assert_eq!(state.iwdg.interval().integer(), max_period.integer());
108+
}
109+
85110
// It takes some time, until defmt_test exits.
86-
// In this time, the wathcodg can not be fed. So disable it in the last test.
111+
// In this time, the watchdog can not be fed.
112+
// set the value high enough, so this does not happen.
87113
#[test]
88-
fn disable(state: &mut State) {
89-
state.iwdg.stop();
114+
fn finish(state: &mut State) {
115+
state.iwdg.start(1000.milliseconds());
90116
}
91117
}

0 commit comments

Comments
 (0)