Browse Source

Optimize delay implementation (#42)

* Optimize delay implementation
* Configure delay with hclk & only for 1MHz+
* Clean up delay implementation
* Add more reasoning and fix documentation
* Add changelog entry for delay optimization
trying.tmp
david-sawatzke 4 years ago committed by Daniel Egger
parent
commit
f54423aa9c
  1. 4
      CHANGELOG.md
  2. 50
      src/delay.rs

4
CHANGELOG.md

@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Support for STM32F091 - @jessebraham
- Support for HSE as a system clocksource (#25 - breaking change) - @zklapow
### Changed
- Optimize delay implemenation (#42) - @david-sawatzke
### Fixed
- Fixed panic in delay overflow handling for debug builds - @david-sawatzke

50
src/delay.rs

@ -1,7 +1,7 @@
//! API for delays with the systick timer
//!
//! Please be aware of potential overflows.
//! For example, the maximum delay with 48MHz is around 89 seconds
//! Please be aware of potential overflows when using `delay_us`.
//! E.g. at 48MHz the maximum delay is 89 seconds.
//!
//! Consider using the timers api as a more flexible interface
//!
@ -35,27 +35,31 @@ use embedded_hal::blocking::delay::{DelayMs, DelayUs};
/// System timer (SysTick) as a delay provider
#[derive(Clone)]
pub struct Delay {
clocks: Clocks,
scale: u32,
}
const MAX_SYSTICK: u32 = 0x00FF_FFFF;
const SYSTICK_RANGE: u32 = 0x0100_0000;
impl Delay {
/// Configures the system timer (SysTick) as a delay provider
/// As access to the count register is possible without a reference, we can
/// just drop it
pub fn new(mut syst: SYST, clocks: Clocks) -> Delay {
syst.set_clock_source(SystClkSource::Core);
syst.set_reload(MAX_SYSTICK);
syst.set_reload(SYSTICK_RANGE - 1);
syst.clear_current();
syst.enable_counter();
Delay { clocks }
assert!(clocks.hclk().0 >= 1_000_000);
let scale = clocks.hclk().0 / 1_000_000;
Delay { scale }
// As access to the count register is possible without a reference to the systick, we can
// just drop it
}
}
impl DelayMs<u32> for Delay {
// At 48 MHz, calling delay_us with ms * 1_000 directly overflows at 0x15D868 (just over the max u16 value)
// At 48 MHz (the maximum frequency), calling delay_us with ms * 1_000 directly overflows at 0x15D86 (just over the max u16 value)
// So we implement a separate, higher level, delay loop
fn delay_ms(&mut self, mut ms: u32) {
const MAX_MS: u32 = 0x0000_FFFF;
while ms != 0 {
@ -68,7 +72,9 @@ impl DelayMs<u32> for Delay {
impl DelayMs<u16> for Delay {
fn delay_ms(&mut self, ms: u16) {
self.delay_us(u32::from(ms) * 1_000);
// Call delay_us directly, so we don't have to use the additional
// delay loop the u32 variant uses
self.delay_us(u32(ms) * 1_000);
}
}
@ -78,28 +84,28 @@ impl DelayMs<u8> for Delay {
}
}
// At 48MHz (the maximum frequency), this overflows at approx. 2^32 / 48 = 89 seconds
impl DelayUs<u32> for Delay {
fn delay_us(&mut self, us: u32) {
// The SysTick Reload Value register supports values between 1 and 0x00FFFFFF.
// Here less than maximum is used so we have some play if there's a long running interrupt.
const MAX_RVR: u32 = 0x007F_FFFF;
const MAX_TICKS: u32 = 0x007F_FFFF;
let mut total_rvr = if self.clocks.sysclk().0 < 1_000_000 {
us / (1_000_000 / self.clocks.sysclk().0)
} else {
us * (self.clocks.sysclk().0 / 1_000_000)
};
let mut total_ticks = us * self.scale;
while total_rvr != 0 {
let current_rvr = if total_rvr <= MAX_RVR {
total_rvr
while total_ticks != 0 {
let current_ticks = if total_ticks <= MAX_TICKS {
total_ticks
} else {
MAX_RVR
MAX_TICKS
};
let start_count = SYST::get_current();
total_rvr -= current_rvr;
while (start_count.wrapping_sub(SYST::get_current()) % MAX_SYSTICK) < current_rvr {}
total_ticks -= current_ticks;
// Use the wrapping substraction and the modulo to deal with the systick wrapping around
// from 0 to 0xFFFF
while (start_count.wrapping_sub(SYST::get_current()) % SYSTICK_RANGE) < current_ticks {}
}
}
}

Loading…
Cancel
Save