Skip to content

Commit

Permalink
Fix timer panic on high frequencies
Browse files Browse the repository at this point in the history
Make the calculation more robust against overflows and wrong settings
  • Loading branch information
Sh3Rm4n committed Feb 19, 2023
1 parent 880e02f commit 0e8d503
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 12 deletions.
18 changes: 11 additions & 7 deletions src/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
//!
//! [examples/adc.rs]: https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.9.1/examples/adc.rs

use core::convert::{From, TryFrom};

use crate::pac::{DCB, DWT};
#[cfg(feature = "enumset")]
use enumset::{EnumSet, EnumSetType};
Expand Down Expand Up @@ -279,13 +277,19 @@ where
let timeout: Self::Time = timeout.into();
let clock = TIM::clock(&self.clocks);

let ticks = clock.integer() * *timeout.scaling_factor() * timeout.integer();
let ticks = clock.integer() * timeout.integer() * *timeout.scaling_factor();

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

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

// Ensure that the below procedure does not create an unexpected interrupt.
let is_update_interrupt_active = self.is_interrupt_configured(Event::Update);
Expand Down
14 changes: 9 additions & 5 deletions testsuite/tests/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ mod tests {
defmt::trace!("{}", state.mono_timer);
let freqcyc = state.mono_timer.frequency().integer();

let durations: [duration::Generic<u32>; 5] = [
let durations: [duration::Generic<u32>; 6] = [
100.nanoseconds().into(),
100.microseconds().into(),
1.milliseconds().into(),
100.milliseconds().into(),
Expand All @@ -86,11 +87,12 @@ mod tests {
// 100.seconds().into(),
];

for dur in durations {
for (i, dur) in durations.into_iter().enumerate() {
defmt::trace!("Duration: {}", defmt::Debug2Format(&dur));

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

// Deviation is high for smaller timer duration. Higher duration are pretty accurate.
// TODO: Maybe the allowed deviation should changed depending on the duration?
defmt::assert!(deviation < 11e-02);
defmt::trace!("deviation: {}", deviation);
if i > 0 {
defmt::assert!(deviation < 1e-02);
}
}
state.timer = Some(timer);
}
Expand Down

0 comments on commit 0e8d503

Please sign in to comment.