Skip to content

Commit 87ba9d0

Browse files
committed
Fix timer panic on high frequencies
Make the calculation more robust against overflows and wrong settings
1 parent 880e02f commit 87ba9d0

File tree

3 files changed

+22
-12
lines changed

3 files changed

+22
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3838
- Missing `MosiPin` impl for `PB5` ([#322])
3939
- Read valid data from data register even if reception of next character
4040
has started ([#317])
41+
- Fix wrong timer frequency calculation and unexpected panics ([#338])
4142

4243
## [v0.9.0] - 2022-03-06
4344

@@ -577,6 +578,7 @@ let clocks = rcc
577578
[defmt]: https://github.com/knurling-rs/defmt
578579
[filter]: https://defmt.ferrous-systems.com/filtering.html
579580

581+
[#338]: https://github.com/stm32-rs/stm32f3xx-hal/pull/338
580582
[#337]: https://github.com/stm32-rs/stm32f3xx-hal/pull/337
581583
[#335]: https://github.com/stm32-rs/stm32f3xx-hal/pull/335
582584
[#334]: https://github.com/stm32-rs/stm32f3xx-hal/pull/334

src/timer.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
//!
1111
//! [examples/adc.rs]: https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.9.1/examples/adc.rs
1212
13-
use core::convert::{From, TryFrom};
14-
1513
use crate::pac::{DCB, DWT};
1614
#[cfg(feature = "enumset")]
1715
use enumset::{EnumSet, EnumSetType};
@@ -279,13 +277,19 @@ where
279277
let timeout: Self::Time = timeout.into();
280278
let clock = TIM::clock(&self.clocks);
281279

282-
let ticks = clock.integer() * *timeout.scaling_factor() * timeout.integer();
280+
let ticks = clock.integer().saturating_mul(timeout.integer()) * *timeout.scaling_factor();
283281

284-
let psc = crate::unwrap!(u16::try_from((ticks - 1) / (1 << 16)).ok());
285-
self.tim.set_psc(psc);
282+
let psc: u32 = (ticks.saturating_sub(1)) / (1 << 16);
283+
self.tim.set_psc(crate::unwrap!(u16::try_from(psc).ok()));
286284

287-
let arr = crate::unwrap!(u16::try_from(ticks / u32::from(psc + 1)).ok());
288-
self.tim.set_arr(arr);
285+
let mut arr = ticks / psc.saturating_add(1);
286+
// If the set frequency is to high to get a meaningful timer resolution,
287+
// set arr to one, so the timer can at least do something and code waiting
288+
// on it is not stuck.
289+
if psc == 0 && arr == 0 {
290+
arr = 1;
291+
}
292+
self.tim.set_arr(crate::unwrap!(u16::try_from(arr).ok()));
289293

290294
// Ensure that the below procedure does not create an unexpected interrupt.
291295
let is_update_interrupt_active = self.is_interrupt_configured(Event::Update);

testsuite/tests/timer.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ mod tests {
7777
defmt::trace!("{}", state.mono_timer);
7878
let freqcyc = state.mono_timer.frequency().integer();
7979

80-
let durations: [duration::Generic<u32>; 5] = [
80+
let durations: [duration::Generic<u32>; 6] = [
81+
100.nanoseconds().into(),
8182
100.microseconds().into(),
8283
1.milliseconds().into(),
8384
100.milliseconds().into(),
@@ -86,11 +87,12 @@ mod tests {
8687
// 100.seconds().into(),
8788
];
8889

89-
for dur in durations {
90+
for (i, dur) in durations.into_iter().enumerate() {
9091
defmt::trace!("Duration: {}", defmt::Debug2Format(&dur));
9192

9293
timer.start(dur);
93-
assert!(!timer.is_event_triggered(Event::Update));
94+
// Wait one cycle, so the start function overhead is not that big.
95+
unwrap!(nb::block!(timer.wait()).ok());
9496
// call instant after start, because starting the timer is the last thing done in the
9597
// start function, and therefor no overhead is added to the timing.
9698
let instant = state.mono_timer.now();
@@ -106,8 +108,10 @@ mod tests {
106108
let deviation = (ratio - 1.).abs();
107109

108110
// Deviation is high for smaller timer duration. Higher duration are pretty accurate.
109-
// TODO: Maybe the allowed deviation should changed depending on the duration?
110-
defmt::assert!(deviation < 11e-02);
111+
defmt::trace!("deviation: {}", deviation);
112+
if i > 0 {
113+
defmt::assert!(deviation < 1e-02);
114+
}
111115
}
112116
state.timer = Some(timer);
113117
}

0 commit comments

Comments
 (0)