Skip to content

Commit f54423a

Browse files
david-sawatzketherealprof
authored andcommitted
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
1 parent 94eea39 commit f54423a

File tree

2 files changed

+32
-22
lines changed

2 files changed

+32
-22
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212
- Support for STM32F091 - @jessebraham
1313
- Support for HSE as a system clocksource (#25 - breaking change) - @zklapow
1414

15+
### Changed
16+
17+
- Optimize delay implemenation (#42) - @david-sawatzke
18+
1519
### Fixed
1620

1721
- Fixed panic in delay overflow handling for debug builds - @david-sawatzke

src/delay.rs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! API for delays with the systick timer
22
//!
3-
//! Please be aware of potential overflows.
4-
//! For example, the maximum delay with 48MHz is around 89 seconds
3+
//! Please be aware of potential overflows when using `delay_us`.
4+
//! E.g. at 48MHz the maximum delay is 89 seconds.
55
//!
66
//! Consider using the timers api as a more flexible interface
77
//!
@@ -35,27 +35,31 @@ use embedded_hal::blocking::delay::{DelayMs, DelayUs};
3535
/// System timer (SysTick) as a delay provider
3636
#[derive(Clone)]
3737
pub struct Delay {
38-
clocks: Clocks,
38+
scale: u32,
3939
}
4040

41-
const MAX_SYSTICK: u32 = 0x00FF_FFFF;
41+
const SYSTICK_RANGE: u32 = 0x0100_0000;
4242

4343
impl Delay {
4444
/// Configures the system timer (SysTick) as a delay provider
45-
/// As access to the count register is possible without a reference, we can
46-
/// just drop it
4745
pub fn new(mut syst: SYST, clocks: Clocks) -> Delay {
4846
syst.set_clock_source(SystClkSource::Core);
4947

50-
syst.set_reload(MAX_SYSTICK);
48+
syst.set_reload(SYSTICK_RANGE - 1);
5149
syst.clear_current();
5250
syst.enable_counter();
53-
Delay { clocks }
51+
assert!(clocks.hclk().0 >= 1_000_000);
52+
let scale = clocks.hclk().0 / 1_000_000;
53+
54+
Delay { scale }
55+
// As access to the count register is possible without a reference to the systick, we can
56+
// just drop it
5457
}
5558
}
5659

5760
impl DelayMs<u32> for Delay {
58-
// At 48 MHz, calling delay_us with ms * 1_000 directly overflows at 0x15D868 (just over the max u16 value)
61+
// At 48 MHz (the maximum frequency), calling delay_us with ms * 1_000 directly overflows at 0x15D86 (just over the max u16 value)
62+
// So we implement a separate, higher level, delay loop
5963
fn delay_ms(&mut self, mut ms: u32) {
6064
const MAX_MS: u32 = 0x0000_FFFF;
6165
while ms != 0 {
@@ -68,7 +72,9 @@ impl DelayMs<u32> for Delay {
6872

6973
impl DelayMs<u16> for Delay {
7074
fn delay_ms(&mut self, ms: u16) {
71-
self.delay_us(u32::from(ms) * 1_000);
75+
// Call delay_us directly, so we don't have to use the additional
76+
// delay loop the u32 variant uses
77+
self.delay_us(u32(ms) * 1_000);
7278
}
7379
}
7480

@@ -78,28 +84,28 @@ impl DelayMs<u8> for Delay {
7884
}
7985
}
8086

87+
// At 48MHz (the maximum frequency), this overflows at approx. 2^32 / 48 = 89 seconds
8188
impl DelayUs<u32> for Delay {
8289
fn delay_us(&mut self, us: u32) {
8390
// The SysTick Reload Value register supports values between 1 and 0x00FFFFFF.
8491
// Here less than maximum is used so we have some play if there's a long running interrupt.
85-
const MAX_RVR: u32 = 0x007F_FFFF;
92+
const MAX_TICKS: u32 = 0x007F_FFFF;
8693

87-
let mut total_rvr = if self.clocks.sysclk().0 < 1_000_000 {
88-
us / (1_000_000 / self.clocks.sysclk().0)
89-
} else {
90-
us * (self.clocks.sysclk().0 / 1_000_000)
91-
};
94+
let mut total_ticks = us * self.scale;
9295

93-
while total_rvr != 0 {
94-
let current_rvr = if total_rvr <= MAX_RVR {
95-
total_rvr
96+
while total_ticks != 0 {
97+
let current_ticks = if total_ticks <= MAX_TICKS {
98+
total_ticks
9699
} else {
97-
MAX_RVR
100+
MAX_TICKS
98101
};
99102

100103
let start_count = SYST::get_current();
101-
total_rvr -= current_rvr;
102-
while (start_count.wrapping_sub(SYST::get_current()) % MAX_SYSTICK) < current_rvr {}
104+
total_ticks -= current_ticks;
105+
106+
// Use the wrapping substraction and the modulo to deal with the systick wrapping around
107+
// from 0 to 0xFFFF
108+
while (start_count.wrapping_sub(SYST::get_current()) % SYSTICK_RANGE) < current_ticks {}
103109
}
104110
}
105111
}

0 commit comments

Comments
 (0)