Skip to content

Commit 40a1f76

Browse files
dbrgnburrbull
authored andcommitted
Add delay impls for TIM2/TIM5
Sometimes SYST cannot be used for delays, for example in the case of RTIC where SYST is already used by the scheduler. For such cases, this commit adds `DelayMs` and `DelayUs` trait impls (both for u32 and u16) based on the 32-bit general purpose TIM2/TIM5 timers. The implementation uses one-pulse mode, so that the timer doesn't need to be explicitly stopped: The CEN flag is automatically cleared once the auto-reload value in the ARR is reached. For backwards compatibility, the old `Delay` struct was _not_ renamed to `SystickDelay`, but that might be something to consider.
1 parent 844170a commit 40a1f76

File tree

8 files changed

+290
-3
lines changed

8 files changed

+290
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2525
- Added support for I2S communication using SPI peripherals, and two examples [#265]
2626
- Added support for some LCD controllers using the Flexible Static Memory
2727
Controller / Flexible Memory Controller [#297]
28+
- Added `DelayMs` / `DelayUs` impls for TIM2/TIM5 [#309]
2829
- Added an example for using the new FSMC interface with the provided
2930
`display-interface` driver and the `st7789` driver on a F413Discovery board [#302]
3031
- Derive `Eq`, `PartialEq`, `Copy` and `Clone` for error types
@@ -34,6 +35,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3435
[#265]: https://github.com/stm32-rs/stm32f4xx-hal/pull/265
3536
[#297]: https://github.com/stm32-rs/stm32f4xx-hal/pull/297
3637
[#302]: https://github.com/stm32-rs/stm32f4xx-hal/pull/302
38+
[#309]: https://github.com/stm32-rs/stm32f4xx-hal/pull/309
3739
[#313]: https://github.com/stm32-rs/stm32f4xx-hal/pull/313
3840
[#318]: https://github.com/stm32-rs/stm32f4xx-hal/pull/318
3941

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,11 @@ name = "sd"
336336
required-features = ["rt", "stm32f405", "sdio-host"]
337337

338338
[[example]]
339-
name = "delay-blinky"
339+
name = "delay-syst-blinky"
340+
required-features = ["rt", "stm32f411"]
341+
342+
[[example]]
343+
name = "delay-timer-blinky"
340344
required-features = ["rt", "stm32f411"]
341345

342346
[[example]]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use the bsp as BSP for your project.
5555

5656
Otherwise, create a new Rust project as you usually do with `cargo init`. The
5757
"hello world" of embedded development is usually to blink a LED. The code to do
58-
so is available in [examples/delay-blinky.rs](examples/delay-blinky.rs).
58+
so is available in [examples/delay-syst-blinky.rs](examples/delay-syst-blinky.rs).
5959
Copy that file to the `main.rs` of your project.
6060

6161
You also need to add some dependencies to your `Cargo.toml`:

examples/delay-blinky.rs renamed to examples/delay-syst-blinky.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//! Demonstrate the use of a blocking `Delay` using the SYST (sysclock) timer.
2+
13
#![deny(unsafe_code)]
24
#![no_main]
35
#![no_std]

examples/delay-timer-blinky.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//! Demonstrate the use of a blocking `Delay` using TIM5 general-purpose timer.
2+
3+
#![deny(unsafe_code)]
4+
#![no_main]
5+
#![no_std]
6+
7+
// Halt on panic
8+
use panic_halt as _; // panic handler
9+
10+
use cortex_m;
11+
use cortex_m_rt::entry;
12+
use stm32f4xx_hal as hal;
13+
14+
use crate::hal::{prelude::*, stm32};
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 LED. On the Mini-F4 it's connected to pin PC13.
23+
let gpioc = dp.GPIOC.split();
24+
let mut led = gpioc.pc13.into_push_pull_output();
25+
26+
// Set up the system clock. We want to run at 48MHz for this one.
27+
let rcc = dp.RCC.constrain();
28+
let clocks = rcc.cfgr.sysclk(48.mhz()).freeze();
29+
30+
// Create a delay abstraction based on general-pupose 32-bit timer TIM5
31+
let mut delay = hal::delay::Tim5Delay::new(dp.TIM5, clocks);
32+
33+
loop {
34+
// On for 1s, off for 1s.
35+
led.set_high().unwrap();
36+
delay.delay_ms(1_000_u32);
37+
led.set_low().unwrap();
38+
delay.delay_us(1_000_000_u32);
39+
}
40+
}
41+
42+
loop {}
43+
}

src/delay/mod.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,46 @@
33
mod syst;
44

55
pub use syst::SystickDelay as Delay;
6+
7+
mod timer;
8+
9+
#[cfg(any(
10+
feature = "stm32f401",
11+
feature = "stm32f405",
12+
feature = "stm32f407",
13+
feature = "stm32f411",
14+
feature = "stm32f412",
15+
feature = "stm32f413",
16+
feature = "stm32f415",
17+
feature = "stm32f417",
18+
feature = "stm32f423",
19+
feature = "stm32f427",
20+
feature = "stm32f429",
21+
feature = "stm32f437",
22+
feature = "stm32f439",
23+
feature = "stm32f446",
24+
feature = "stm32f469",
25+
feature = "stm32f479"
26+
))]
27+
pub use timer::Tim2Delay;
28+
29+
#[cfg(any(
30+
feature = "stm32f401",
31+
feature = "stm32f405",
32+
feature = "stm32f407",
33+
feature = "stm32f410",
34+
feature = "stm32f411",
35+
feature = "stm32f412",
36+
feature = "stm32f413",
37+
feature = "stm32f415",
38+
feature = "stm32f417",
39+
feature = "stm32f423",
40+
feature = "stm32f427",
41+
feature = "stm32f429",
42+
feature = "stm32f437",
43+
feature = "stm32f439",
44+
feature = "stm32f446",
45+
feature = "stm32f469",
46+
feature = "stm32f479"
47+
))]
48+
pub use timer::Tim5Delay;

src/delay/syst.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/// System timer (SysTick) as a delay provider
1+
//! System timer (SysTick) as a delay provider.
22
33
use cast::u32;
44
use cortex_m::peripheral::syst::SystClkSource;

src/delay/timer.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
//! Delay implementation based on general-purpose 32 bit timers.
2+
//!
3+
//! TIM2 and TIM5 are a general purpose 32-bit auto-reload up/downcounter with
4+
//! a 16-bit prescaler.
5+
6+
use core::cmp::max;
7+
8+
use cast::{u16, u32};
9+
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
10+
11+
use crate::{
12+
bb,
13+
pac::{self, RCC},
14+
rcc::Clocks,
15+
};
16+
17+
macro_rules! hal {
18+
($($TIM:ident: ($struct:ident, $waitfn:ident, $en_bit:expr, $apbenr:ident, $apbrstr:ident, $pclk:ident, $ppre:ident),)+) => {
19+
$(
20+
/// General purpose timer as delay provider
21+
pub struct $struct {
22+
clocks: Clocks,
23+
tim: pac::$TIM,
24+
}
25+
26+
fn $waitfn(tim: &mut pac::$TIM, prescaler: u16, auto_reload_register: u32) {
27+
// Write Prescaler (PSC)
28+
tim.psc.write(|w| w.psc().bits(prescaler));
29+
30+
// Write Auto-Reload Register (ARR)
31+
// Note: Make it impossible to set the ARR value to 0, since this
32+
// would cause an infinite loop.
33+
tim.arr.write(|w| unsafe { w.bits(max(1, auto_reload_register)) });
34+
35+
// Trigger update event (UEV) in the event generation register (EGR)
36+
// in order to immediately apply the config
37+
tim.cr1.modify(|_, w| w.urs().set_bit());
38+
tim.egr.write(|w| w.ug().set_bit());
39+
tim.cr1.modify(|_, w| w.urs().clear_bit());
40+
41+
// Configure the counter in one-pulse mode (counter stops counting at
42+
// the next updateevent, clearing the CEN bit) and enable the counter.
43+
tim.cr1.write(|w| w.opm().set_bit().cen().set_bit());
44+
45+
// Wait for CEN bit to clear
46+
while tim.cr1.read().cen().is_enabled() { /* wait */ }
47+
}
48+
49+
impl $struct {
50+
/// Configures the timer as a delay provider
51+
pub fn new(tim: pac::$TIM, clocks: Clocks) -> Self {
52+
unsafe {
53+
//NOTE(unsafe) this reference will only be used for atomic writes with no side effects
54+
let rcc = &(*RCC::ptr());
55+
56+
// Enable timer peripheral in RCC
57+
bb::set(&rcc.$apbenr, $en_bit);
58+
59+
// Stall the pipeline to work around erratum 2.1.13 (DM00037591)
60+
cortex_m::asm::dsb();
61+
62+
// Reset timer
63+
bb::set(&rcc.$apbrstr, $en_bit);
64+
bb::clear(&rcc.$apbrstr, $en_bit);
65+
}
66+
67+
// Enable one-pulse mode (counter stops counting at the next update
68+
// event, clearing the CEN bit)
69+
tim.cr1.modify(|_, w| w.opm().enabled());
70+
71+
Self { tim, clocks }
72+
}
73+
74+
/// Releases the timer resource
75+
pub fn free(self) -> pac::$TIM {
76+
self.tim
77+
}
78+
}
79+
80+
impl DelayUs<u32> for $struct {
81+
/// Sleep for up to 2^32-1 microseconds (~71 minutes).
82+
fn delay_us(&mut self, us: u32) {
83+
// Set up prescaler so that a tick takes exactly 1 µs.
84+
//
85+
// For example, if the clock is set to 48 MHz, with a prescaler of 48
86+
// we'll get ticks that are 1 µs long. This means that we can write the
87+
// delay value directly to the auto-reload register (ARR).
88+
let psc = u16(self.clocks.pclk1().0 / 1_000_000)
89+
.expect("Prescaler does not fit in u16");
90+
let arr = us;
91+
$waitfn(&mut self.tim, psc, arr);
92+
}
93+
}
94+
95+
impl DelayUs<u16> for $struct {
96+
/// Sleep for up to 2^16-1 microseconds (~65 milliseconds).
97+
fn delay_us(&mut self, us: u16) {
98+
// See DelayUs<u32> for explanations.
99+
let psc = u16(self.clocks.pclk1().0 / 1_000_000)
100+
.expect("Prescaler does not fit in u16");
101+
let arr = u32(us);
102+
$waitfn(&mut self.tim, psc, arr);
103+
}
104+
}
105+
106+
impl DelayMs<u32> for $struct {
107+
/// Sleep for up to (2^32)/2-1 milliseconds (~24 days).
108+
/// If the `ms` value is larger than 2147483647, the code will panic.
109+
fn delay_ms(&mut self, ms: u32) {
110+
// See next section for explanation why the usable range is reduced.
111+
assert!(ms <= 2_147_483_647); // (2^32)/2-1
112+
113+
// Set up prescaler so that a tick takes exactly 0.5 ms.
114+
//
115+
// For example, if the clock is set to 48 MHz, with a prescaler of 24'000
116+
// we'll get ticks that are 0.5 ms long. This means that we can write the
117+
// delay value multipled by two to the auto-reload register (ARR).
118+
//
119+
// Note that we cannot simply use a prescaler value where the tick corresponds
120+
// to 1 ms, because then a clock of 100 MHz would correspond to a prescaler
121+
// value of 100'000, which doesn't fit in the 16-bit PSC register.
122+
//
123+
// Unfortunately this means that only one half of the full 32-bit range
124+
// can be used, but 24 days should be plenty of usable delay time.
125+
let psc = u16(self.clocks.pclk1().0 / 1000 / 2)
126+
.expect("Prescaler does not fit in u16");
127+
128+
// Since PSC = 0.5 ms, double the value for the ARR
129+
let arr = ms << 1;
130+
131+
$waitfn(&mut self.tim, psc, arr);
132+
}
133+
}
134+
135+
impl DelayMs<u16> for $struct {
136+
/// Sleep for up to (2^16)-1 milliseconds (~65 seconds).
137+
fn delay_ms(&mut self, ms: u16) {
138+
// See DelayMs<u32> for explanations. Since the value range is only 16 bit,
139+
// we don't need an assert here.
140+
let psc = u16(self.clocks.pclk1().0 / 1000 / 2)
141+
.expect("Prescaler does not fit in u16");
142+
let arr = u32(ms) << 1;
143+
$waitfn(&mut self.tim, psc, arr);
144+
}
145+
}
146+
)+
147+
}
148+
}
149+
150+
#[cfg(any(
151+
feature = "stm32f401",
152+
feature = "stm32f405",
153+
feature = "stm32f407",
154+
feature = "stm32f410",
155+
feature = "stm32f411",
156+
feature = "stm32f412",
157+
feature = "stm32f413",
158+
feature = "stm32f415",
159+
feature = "stm32f417",
160+
feature = "stm32f423",
161+
feature = "stm32f427",
162+
feature = "stm32f429",
163+
feature = "stm32f437",
164+
feature = "stm32f439",
165+
feature = "stm32f446",
166+
feature = "stm32f469",
167+
feature = "stm32f479"
168+
))]
169+
hal! {
170+
TIM5: (Tim5Delay, wait_tim5, 3, apb1enr, apb1rstr, pclk1, ppre1),
171+
}
172+
173+
#[cfg(any(
174+
feature = "stm32f401",
175+
feature = "stm32f405",
176+
feature = "stm32f407",
177+
feature = "stm32f411",
178+
feature = "stm32f412",
179+
feature = "stm32f413",
180+
feature = "stm32f415",
181+
feature = "stm32f417",
182+
feature = "stm32f423",
183+
feature = "stm32f427",
184+
feature = "stm32f429",
185+
feature = "stm32f437",
186+
feature = "stm32f439",
187+
feature = "stm32f446",
188+
feature = "stm32f469",
189+
feature = "stm32f479"
190+
))]
191+
hal! {
192+
TIM2: (Tim2Delay, wait_tim2, 0, apb1enr, apb1rstr, pclk1, ppre1),
193+
}

0 commit comments

Comments
 (0)