Skip to content

Commit 24286fa

Browse files
bors[bot]h2obrain
andauthored
Merge #150
150: Dwt: Delay and stopwatch r=therealprof a=h2obrain Implements "dwt cycle counter"-based `use embedded_hal::blocking::delay::{DelayMs, DelayUs};` Additionally you can use it to measure execution times of code fragments. ### UPDATE2 Feel free to comment on which function names should change! ```rust use crate::hal::{ dwt::{ClockDiff, DelayS, DwtExt, StopWatch}, prelude::*, stm32, }; // Constrain DWT and DCB let dwt = cp.DWT.constrain(cp.DCB, clocks); // Create a (copy-/cloneable) delay instance let delay = dwt.delay(); // DelayMs/DelayUs from hal delay.delay_us(1000); delay.delay_ms(1); // DelayS from dwt.rs delay.delay_s(0.0000005f32); delay.delay_s(0.0000005f64); // Measure some closures timing let t = dwt.measure(|| delay.delay_ms(10)); let _t: f64 = t.as_secs_f64(); let _t: f32 = t.as_secs_f32(); let _t: u64 = t.as_nanos(); let _t: u32 = t.as_ticks(); // General stopwatch let mut times = [0u32; 5]; // max 4 laps let mut sw = self.stopwatch(&mut times, clocks); sw.lap(); delay.delay_ms(10); sw.lap(); sw.lap(); // Get all the lap times { let mut lap = 1; while let Some(lap_time) = sw.lap_time(lap) { let _t = lap_time.as_secs_f64(); lap += 1; } } ``` ### Iterators A draft for iterators, which seems to need more code than I would have guessed, you can find here ([dwt.rs](https://github.com/h2obrain/stm32f4xx-hal/blob/dwt-stopwatch-iter/src/dwt.rs), [dwt-blinky.rs](https://github.com/h2obrain/stm32f4xx-hal/blob/dwt-stopwatch-iter/examples/dwt-blinky.rs)) ### ClockDuration with from-functions A ClockDuration with interchangeable types would be cool to do stuff like `delay.delay(ClockDuration::from_secs_f64(0.1));` along with this `let cd = dwt.measure(do_stuff); .. ; delay.delay(cd);` A "the are no stupid solutions" implementation of this can found here ([dwt.rs](https://github.com/h2obrain/stm32f4xx-hal/blob/dwt-lazy-duration/src/dwt.rs), [dwt-blinky.rs](https://github.com/h2obrain/stm32f4xx-hal/blob/dwt-lazy-duration/examples/dwt-blinky.rs)) Co-authored-by: Oliver Meier <h2obrain@gmail.com>
2 parents 160572d + 065b66a commit 24286fa

File tree

5 files changed

+278
-0
lines changed

5 files changed

+278
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
- Implement `timer::Cancel` trait for `Timer<TIM>`.
13+
- Added DWT cycle counter based delay and stopwatch, including an example.
1314

1415
## [v0.8.0] - 2020-04-30
1516

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ required-features = ["rt", "stm32f401", "usb_fs"]
101101
name = "delay-blinky"
102102
required-features = ["rt", "stm32f411"]
103103

104+
[[example]]
105+
name = "dwt-blinky"
106+
required-features = ["rt", "stm32f429"]
107+
104108
[[example]]
105109
name = "ssd1306-image"
106110
required-features = ["rt", "stm32f411"]

examples/dwt-blinky.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#![deny(unsafe_code)]
2+
#![no_main]
3+
#![no_std]
4+
5+
// Halt on panic
6+
use crate::hal::{
7+
dwt::{ClockDuration, DwtExt},
8+
prelude::*,
9+
stm32,
10+
};
11+
use cortex_m;
12+
use cortex_m_rt::entry;
13+
use panic_halt as _;
14+
use stm32f4xx_hal as hal;
15+
16+
#[entry]
17+
fn main() -> ! {
18+
if let (Some(dp), Some(cp)) = (
19+
stm32::Peripherals::take(),
20+
cortex_m::peripheral::Peripherals::take(),
21+
) {
22+
// Set up the LEDs. On the STM32F429I-DISC[O1] they are connected to pin PG13/14.
23+
let gpiog = dp.GPIOG.split();
24+
let mut led1 = gpiog.pg13.into_push_pull_output();
25+
let mut led2 = gpiog.pg14.into_push_pull_output();
26+
27+
// Set up the system clock. We want to run at 48MHz for this one.
28+
let rcc = dp.RCC.constrain();
29+
let clocks = rcc.cfgr.sysclk(48.mhz()).freeze();
30+
31+
// Create a delay abstraction based on DWT cycle counter
32+
let dwt = cp.DWT.constrain(cp.DCB, clocks);
33+
let mut delay = dwt.delay();
34+
35+
// Create a stopwatch for maximum 9 laps
36+
// Note: it starts immediately
37+
let mut lap_times = [0u32; 10];
38+
let mut sw = dwt.stopwatch(&mut lap_times);
39+
loop {
40+
// On for 1s, off for 1s.
41+
led1.set_high().unwrap();
42+
led2.set_low().unwrap();
43+
delay.delay_ms(1000_u32);
44+
sw.lap();
45+
led1.set_low().unwrap();
46+
led2.set_high().unwrap();
47+
delay.delay_ms(900_u32);
48+
// Also you can measure with almost clock precision
49+
let cd: ClockDuration = dwt.measure(|| delay.delay_ms(100_u32));
50+
let _t: u32 = cd.as_ticks(); // Should return 48MHz * 0.1s as u32
51+
let _t: f32 = cd.as_secs_f32(); // Should return ~0.1s as a f32
52+
let _t: f64 = cd.as_secs_f64(); // Should return ~0.1s as a f64
53+
let _t: u64 = cd.as_nanos(); // Should return 100000000ns as a u64
54+
sw.lap();
55+
56+
// Get all the lap times
57+
{
58+
let mut lap = 1;
59+
while let Some(lap_time) = sw.lap_time(lap) {
60+
let _t = lap_time.as_secs_f64();
61+
lap += 1;
62+
}
63+
}
64+
65+
// Reset stopwatch
66+
sw.reset();
67+
}
68+
}
69+
70+
loop {}
71+
}

src/dwt.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
//! Debug and trace and stuff
2+
3+
use crate::rcc::Clocks;
4+
use crate::time::Hertz;
5+
use cortex_m::peripheral::{DCB, DWT};
6+
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
7+
8+
pub trait DwtExt {
9+
fn constrain(self, dcb: DCB, clocks: Clocks) -> Dwt;
10+
}
11+
impl DwtExt for DWT {
12+
/// Enable trace unit and cycle counter
13+
fn constrain(mut self, mut dcb: DCB, clocks: Clocks) -> Dwt {
14+
dcb.enable_trace();
15+
self.enable_cycle_counter();
16+
Dwt {
17+
dwt: self,
18+
dcb,
19+
clocks,
20+
}
21+
}
22+
}
23+
24+
pub struct Dwt {
25+
dwt: DWT,
26+
dcb: DCB,
27+
clocks: Clocks,
28+
}
29+
impl Dwt {
30+
/// Release the dwt and dcb control
31+
/// # Safety
32+
/// All instances of Delay and StopWatch become invalid after this
33+
pub unsafe fn release(self) -> (DWT, DCB) {
34+
(self.dwt, self.dcb)
35+
}
36+
/// Create a delay instance
37+
pub fn delay(&self) -> Delay {
38+
Delay {
39+
clock: self.clocks.hclk(),
40+
}
41+
}
42+
/// Create a stopwatch instance
43+
/// # Arguments
44+
/// * `times` - Array which will be holding the timings in ticks (max laps == times.len()-1)
45+
pub fn stopwatch<'i>(&self, times: &'i mut [u32]) -> StopWatch<'i> {
46+
StopWatch::new(times, self.clocks.hclk())
47+
}
48+
/// Measure cycles it takes to execute f
49+
pub fn measure<F: FnOnce()>(&self, f: F) -> ClockDuration {
50+
let mut times: [u32; 2] = [0; 2];
51+
let mut sw = self.stopwatch(&mut times);
52+
f();
53+
sw.lap().lap_time(1).unwrap()
54+
}
55+
}
56+
57+
#[derive(Clone, Copy)]
58+
pub struct Delay {
59+
clock: Hertz,
60+
}
61+
impl Delay {
62+
/// Delay for ClockDuration::ticks
63+
pub fn delay(duration: ClockDuration) {
64+
let ticks = duration.ticks as u64;
65+
Delay::delay_ticks(DWT::get_cycle_count(), ticks);
66+
}
67+
/// Delay ticks
68+
/// NOTE DCB and DWT need to be set up for this to work, so it is private
69+
fn delay_ticks(mut start: u32, ticks: u64) {
70+
if ticks < (u32::MAX / 2) as u64 {
71+
// Simple delay
72+
let ticks = ticks as u32;
73+
while (DWT::get_cycle_count().wrapping_sub(start)) < ticks {}
74+
} else if ticks <= u32::MAX as u64 {
75+
// Try to avoid race conditions by limiting delay to u32::MAX / 2
76+
let mut ticks = ticks as u32;
77+
ticks -= u32::MAX / 2;
78+
while (DWT::get_cycle_count().wrapping_sub(start)) < u32::MAX / 2 {}
79+
start -= u32::MAX / 2;
80+
while (DWT::get_cycle_count().wrapping_sub(start)) < ticks {}
81+
} else {
82+
// Delay for ticks, then delay for rest * u32::MAX
83+
let mut rest = (ticks >> 32) as u32;
84+
let ticks = (ticks & u32::MAX as u64) as u32;
85+
loop {
86+
while (DWT::get_cycle_count().wrapping_sub(start)) < ticks {}
87+
if rest == 0 {
88+
break;
89+
}
90+
rest -= 1;
91+
while (DWT::get_cycle_count().wrapping_sub(start)) > ticks {}
92+
}
93+
}
94+
}
95+
}
96+
97+
// Implement DelayUs/DelayMs for various integer types
98+
impl<T: Into<u64>> DelayUs<T> for Delay {
99+
fn delay_us(&mut self, us: T) {
100+
// Convert us to ticks
101+
let start = DWT::get_cycle_count();
102+
let ticks = (us.into() * self.clock.0 as u64) / 1_000_000;
103+
Delay::delay_ticks(start, ticks);
104+
}
105+
}
106+
impl<T: Into<u64>> DelayMs<T> for Delay {
107+
fn delay_ms(&mut self, ms: T) {
108+
// Convert ms to ticks
109+
let start = DWT::get_cycle_count();
110+
let ticks = (ms.into() * self.clock.0 as u64) / 1_000;
111+
Delay::delay_ticks(start, ticks);
112+
}
113+
}
114+
115+
/// Very simple stopwatch
116+
pub struct StopWatch<'l> {
117+
times: &'l mut [u32],
118+
timei: usize,
119+
clock: Hertz,
120+
}
121+
impl<'l> StopWatch<'l> {
122+
/// Create a new instance (Private because dwt/dcb should be set up)
123+
/// # Arguments
124+
/// * `times` - Array which will be holding the timings (max laps == times.len()-1)
125+
/// * `clock` - The DWT cycle counters clock
126+
fn new(times: &'l mut [u32], clock: Hertz) -> Self {
127+
assert!(times.len() >= 2);
128+
let mut sw = StopWatch {
129+
times,
130+
timei: 0,
131+
clock,
132+
};
133+
sw.reset();
134+
sw
135+
}
136+
/// Returns the numbers of laps recorded
137+
pub fn lap_count(&self) -> usize {
138+
self.timei
139+
}
140+
/// Resets recorded laps to 0 and sets 0 offset
141+
pub fn reset(&mut self) {
142+
self.timei = 0;
143+
self.times[0] = DWT::get_cycle_count();
144+
}
145+
/// Record a new lap
146+
/// NOTE If lap count exceeds maximum, the last lap is updated
147+
pub fn lap(&mut self) -> &mut Self {
148+
let c = DWT::get_cycle_count();
149+
if self.timei < self.times.len() {
150+
self.timei += 1;
151+
}
152+
self.times[self.timei] = c;
153+
self
154+
}
155+
/// Calculate the time of lap n (n starting with 1)
156+
/// NOTE Returns None if 'n' is out of range
157+
pub fn lap_time(&self, n: usize) -> Option<ClockDuration> {
158+
if (n < 1) || (self.timei < n) {
159+
None
160+
} else {
161+
Some(ClockDuration {
162+
ticks: self.times[n].wrapping_sub(self.times[n - 1]),
163+
clock: self.clock,
164+
})
165+
}
166+
}
167+
}
168+
169+
/// Clock difference with capability to calculate SI units (s)
170+
#[derive(Clone, Copy)]
171+
pub struct ClockDuration {
172+
ticks: u32,
173+
clock: Hertz,
174+
}
175+
impl ClockDuration {
176+
/// Returns ticks
177+
pub fn as_ticks(self) -> u32 {
178+
self.ticks
179+
}
180+
/// Returns calculated milliseconds as integer
181+
pub fn as_millis(self) -> u64 {
182+
self.ticks as u64 * 1_000 / self.clock.0 as u64
183+
}
184+
/// Returns calculated microseconds as integer
185+
pub fn as_micros(self) -> u64 {
186+
self.ticks as u64 * 1_000_000 / self.clock.0 as u64
187+
}
188+
/// Returns calculated nanoseconds as integer
189+
pub fn as_nanos(self) -> u64 {
190+
self.ticks as u64 * 1_000_000_000 / self.clock.0 as u64
191+
}
192+
/// Return calculated seconds as 32-bit float
193+
pub fn as_secs_f32(self) -> f32 {
194+
self.ticks as f32 / self.clock.0 as f32
195+
}
196+
/// Return calculated seconds as 64-bit float
197+
pub fn as_secs_f64(self) -> f64 {
198+
self.ticks as f64 / self.clock.0 as f64
199+
}
200+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ pub mod otg_hs;
135135
))]
136136
pub mod rng;
137137

138+
#[cfg(feature = "device-selected")]
139+
pub mod dwt;
138140
#[cfg(feature = "device-selected")]
139141
pub mod prelude;
140142
#[cfg(feature = "device-selected")]

0 commit comments

Comments
 (0)