aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThales Fragoso <[email protected]>2021-02-15 21:38:36 -0300
committerThales Fragoso <[email protected]>2021-02-17 19:40:27 -0300
commit9d895a63832336590ce6780ea38825d2e0183573 (patch)
tree096f268f002f29bdda72af85ad606a15762f1052
parente45496900009c0049617fc4930cc5130980c0e4a (diff)
Add RTC timer for stm32f4
-rw-r--r--embassy-stm32f4-examples/.cargo/config2
-rw-r--r--embassy-stm32f4-examples/Cargo.toml4
-rw-r--r--embassy-stm32f4-examples/memory.x2
-rw-r--r--embassy-stm32f4-examples/src/bin/rtc_async.rs65
-rw-r--r--embassy-stm32f4/src/interrupt.rs60
-rw-r--r--embassy-stm32f4/src/lib.rs1
-rw-r--r--embassy-stm32f4/src/rtc.rs364
7 files changed, 494 insertions, 4 deletions
diff --git a/embassy-stm32f4-examples/.cargo/config b/embassy-stm32f4-examples/.cargo/config
index 707494d68..836853988 100644
--- a/embassy-stm32f4-examples/.cargo/config
+++ b/embassy-stm32f4-examples/.cargo/config
@@ -1,5 +1,5 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2runner = "probe-run --chip STM32F411CEUx --defmt" 2runner = "probe-run --chip STM32F401CCUx --defmt"
3 3
4rustflags = [ 4rustflags = [
5 # LLD (shipped with the Rust toolchain) is used as the default linker 5 # LLD (shipped with the Rust toolchain) is used as the default linker
diff --git a/embassy-stm32f4-examples/Cargo.toml b/embassy-stm32f4-examples/Cargo.toml
index e81f85ff1..216964374 100644
--- a/embassy-stm32f4-examples/Cargo.toml
+++ b/embassy-stm32f4-examples/Cargo.toml
@@ -18,7 +18,7 @@ defmt-error = []
18 18
19[dependencies] 19[dependencies]
20embassy = { version = "0.1.0", path = "../embassy", features = ["defmt", "defmt-trace"] } 20embassy = { version = "0.1.0", path = "../embassy", features = ["defmt", "defmt-trace"] }
21embassy-stm32f4 = { version = "*", path = "../embassy-stm32f4", features = ["stm32f405"] } 21embassy-stm32f4 = { version = "*", path = "../embassy-stm32f4", features = ["stm32f401"] }
22 22
23defmt = "0.1.3" 23defmt = "0.1.3"
24defmt-rtt = "0.1.0" 24defmt-rtt = "0.1.0"
@@ -27,6 +27,6 @@ cortex-m = "0.7.1"
27cortex-m-rt = "0.6.13" 27cortex-m-rt = "0.6.13"
28embedded-hal = { version = "0.2.4" } 28embedded-hal = { version = "0.2.4" }
29panic-probe = "0.1.0" 29panic-probe = "0.1.0"
30stm32f4xx-hal = { version = "0.8.3", features = ["rt", "stm32f405"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} 30stm32f4xx-hal = { version = "0.8.3", features = ["rt", "stm32f401"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"}
31futures = { version = "0.3.8", default-features = false, features = ["async-await"] } 31futures = { version = "0.3.8", default-features = false, features = ["async-await"] }
32rtt-target = { version = "0.3", features = ["cortex-m"] } 32rtt-target = { version = "0.3", features = ["cortex-m"] }
diff --git a/embassy-stm32f4-examples/memory.x b/embassy-stm32f4-examples/memory.x
index ee4d4d59c..efa700b8a 100644
--- a/embassy-stm32f4-examples/memory.x
+++ b/embassy-stm32f4-examples/memory.x
@@ -1,5 +1,5 @@
1MEMORY 1MEMORY
2{ 2{
3 FLASH : ORIGIN = 0x08000000, LENGTH = 64K 3 FLASH : ORIGIN = 0x08000000, LENGTH = 64K
4 RAM : ORIGIN = 0x20000000, LENGTH = 32K 4 RAM : ORIGIN = 0x20000000, LENGTH = 32K
5} 5}
diff --git a/embassy-stm32f4-examples/src/bin/rtc_async.rs b/embassy-stm32f4-examples/src/bin/rtc_async.rs
new file mode 100644
index 000000000..e53212263
--- /dev/null
+++ b/embassy-stm32f4-examples/src/bin/rtc_async.rs
@@ -0,0 +1,65 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7use example_common::*;
8
9use cortex_m_rt::entry;
10use defmt::panic;
11use embassy::executor::{task, Executor};
12use embassy::time::{Duration, Timer};
13use embassy::util::Forever;
14use embassy_stm32f4::{interrupt, pac, rtc};
15use stm32f4xx_hal::prelude::*;
16
17#[task]
18async fn run1() {
19 loop {
20 info!("BIG INFREQUENT TICK");
21 Timer::after(Duration::from_ticks(32768 * 2)).await;
22 }
23}
24
25#[task]
26async fn run2() {
27 loop {
28 info!("tick");
29 Timer::after(Duration::from_ticks(13000)).await;
30 }
31}
32
33static RTC: Forever<rtc::RTC<pac::TIM2>> = Forever::new();
34static ALARM: Forever<rtc::Alarm<pac::TIM2>> = Forever::new();
35static EXECUTOR: Forever<Executor> = Forever::new();
36
37#[entry]
38fn main() -> ! {
39 info!("Hello World!");
40
41 let p = unwrap!(pac::Peripherals::take());
42
43 p.RCC.ahb1enr.modify(|_, w| w.dma1en().enabled());
44 let rcc = p.RCC.constrain();
45 let clocks = rcc.cfgr.freeze();
46
47 p.DBGMCU.cr.modify(|_, w| {
48 w.dbg_sleep().set_bit();
49 w.dbg_standby().set_bit();
50 w.dbg_stop().set_bit()
51 });
52
53 let rtc = RTC.put(rtc::RTC::new(p.TIM2, interrupt::take!(TIM2), clocks));
54 rtc.start();
55
56 unsafe { embassy::time::set_clock(rtc) };
57
58 let alarm = ALARM.put(rtc.alarm1());
59 let executor = EXECUTOR.put(Executor::new());
60 executor.set_alarm(alarm);
61 executor.run(|spawner| {
62 unwrap!(spawner.spawn(run1()));
63 unwrap!(spawner.spawn(run2()));
64 });
65}
diff --git a/embassy-stm32f4/src/interrupt.rs b/embassy-stm32f4/src/interrupt.rs
index c75e4b82d..8336df7d2 100644
--- a/embassy-stm32f4/src/interrupt.rs
+++ b/embassy-stm32f4/src/interrupt.rs
@@ -94,6 +94,66 @@ where
94 } 94 }
95} 95}
96 96
97#[cfg(feature = "stm32f401")]
98mod irqs {
99 use super::*;
100 declare!(PVD);
101 declare!(TAMP_STAMP);
102 declare!(RTC_WKUP);
103 declare!(FLASH);
104 declare!(RCC);
105 declare!(EXTI0);
106 declare!(EXTI1);
107 declare!(EXTI2);
108 declare!(EXTI3);
109 declare!(EXTI4);
110 declare!(DMA1_STREAM0);
111 declare!(DMA1_STREAM1);
112 declare!(DMA1_STREAM2);
113 declare!(DMA1_STREAM3);
114 declare!(DMA1_STREAM4);
115 declare!(DMA1_STREAM5);
116 declare!(DMA1_STREAM6);
117 declare!(ADC);
118 declare!(EXTI9_5);
119 declare!(TIM1_BRK_TIM9);
120 declare!(TIM1_UP_TIM10);
121 declare!(TIM1_TRG_COM_TIM11);
122 declare!(TIM1_CC);
123 declare!(TIM2);
124 declare!(TIM3);
125 declare!(TIM4);
126 declare!(I2C1_EV);
127 declare!(I2C1_ER);
128 declare!(I2C2_EV);
129 declare!(I2C2_ER);
130 declare!(SPI1);
131 declare!(SPI2);
132 declare!(USART1);
133 declare!(USART2);
134 declare!(EXTI15_10);
135 declare!(RTC_ALARM);
136 declare!(OTG_FS_WKUP);
137 declare!(DMA1_STREAM7);
138 declare!(SDIO);
139 declare!(TIM5);
140 declare!(SPI3);
141 declare!(DMA2_STREAM0);
142 declare!(DMA2_STREAM1);
143 declare!(DMA2_STREAM2);
144 declare!(DMA2_STREAM3);
145 declare!(DMA2_STREAM4);
146 declare!(OTG_FS);
147 declare!(DMA2_STREAM5);
148 declare!(DMA2_STREAM6);
149 declare!(DMA2_STREAM7);
150 declare!(USART6);
151 declare!(I2C3_EV);
152 declare!(I2C3_ER);
153 declare!(FPU);
154 declare!(SPI4);
155}
156
97#[cfg(feature = "stm32f405")] 157#[cfg(feature = "stm32f405")]
98mod irqs { 158mod irqs {
99 use super::*; 159 use super::*;
diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs
index 02668b7eb..eb8530ea2 100644
--- a/embassy-stm32f4/src/lib.rs
+++ b/embassy-stm32f4/src/lib.rs
@@ -312,6 +312,7 @@ pub(crate) mod fmt;
312 312
313pub mod exti; 313pub mod exti;
314pub mod interrupt; 314pub mod interrupt;
315pub mod rtc;
315pub mod serial; 316pub mod serial;
316 317
317pub use cortex_m_rt::interrupt; 318pub use cortex_m_rt::interrupt;
diff --git a/embassy-stm32f4/src/rtc.rs b/embassy-stm32f4/src/rtc.rs
new file mode 100644
index 000000000..7dc3a563f
--- /dev/null
+++ b/embassy-stm32f4/src/rtc.rs
@@ -0,0 +1,364 @@
1use core::cell::Cell;
2use core::convert::TryInto;
3use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
4
5use embassy::time::{Clock, TICKS_PER_SECOND};
6use stm32f4xx_hal::bb;
7use stm32f4xx_hal::rcc::Clocks;
8
9use crate::interrupt;
10use crate::interrupt::{CriticalSection, Mutex, OwnedInterrupt};
11
12// RTC timekeeping works with something we call "periods", which are time intervals
13// of 2^15 ticks. The RTC counter value is 16 bits, so one "overflow cycle" is 2 periods.
14//
15// A `period` count is maintained in parallel to the RTC hardware `counter`, like this:
16// - `period` and `counter` start at 0
17// - `period` is incremented on overflow (at counter value 0)
18// - `period` is incremented "midway" between overflows (at counter value 0x8000)
19//
20// Therefore, when `period` is even, counter is in 0..0x7FFF. When odd, counter is in 0x8000..0xFFFF
21// This allows for now() to return the correct value even if it races an overflow.
22//
23// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches
24// the expected range for the `period` parity, we're done. If it doesn't, this means that
25// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value
26// corresponds to the next period.
27//
28// `period` is a 32bit integer, so It overflows on 2^32 * 2^15 / 32768 seconds of uptime, which is 136 years.
29
30fn calc_now(period: u32, counter: u16) -> u64 {
31 ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
32}
33
34struct AlarmState {
35 timestamp: Cell<u64>,
36 callback: Cell<Option<(fn(*mut ()), *mut ())>>,
37}
38
39impl AlarmState {
40 fn new() -> Self {
41 Self {
42 timestamp: Cell::new(u64::MAX),
43 callback: Cell::new(None),
44 }
45 }
46}
47
48// TODO: This is sometimes wasteful, try to find a better way
49const ALARM_COUNT: usize = 3;
50
51pub struct RTC<T: Instance> {
52 rtc: T,
53 irq: T::Interrupt,
54
55 /// Number of 2^23 periods elapsed since boot.
56 period: AtomicU32,
57
58 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
59 alarms: Mutex<[AlarmState; ALARM_COUNT]>,
60
61 clocks: Clocks,
62}
63
64impl<T: Instance> RTC<T> {
65 pub fn new(rtc: T, irq: T::Interrupt, clocks: Clocks) -> Self {
66 Self {
67 rtc,
68 irq,
69 period: AtomicU32::new(0),
70 alarms: Mutex::new([AlarmState::new(), AlarmState::new(), AlarmState::new()]),
71 clocks,
72 }
73 }
74
75 pub fn start(&'static self) {
76 self.rtc.enable_clock();
77 self.rtc.stop_and_reset();
78
79 let multiplier = if T::ppre(&self.clocks) == 1 { 1 } else { 2 };
80 let freq = T::pclk(&self.clocks) * multiplier;
81 let psc = freq / TICKS_PER_SECOND as u32 - 1;
82 let psc: u16 = psc.try_into().unwrap();
83
84 self.rtc.set_psc_arr(psc, u16::MAX);
85 // Mid-way point
86 self.rtc.set_compare(0, 0x8000);
87 self.rtc.set_compare_interrupt(0, true);
88
89 self.irq.set_handler(
90 |ptr| unsafe {
91 let this = &*(ptr as *const () as *const Self);
92 this.on_interrupt();
93 },
94 self as *const _ as *mut _,
95 );
96 self.irq.unpend();
97 self.irq.enable();
98
99 self.rtc.start();
100 }
101
102 fn on_interrupt(&self) {
103 if self.rtc.overflow_interrupt_status() {
104 self.rtc.overflow_clear_flag();
105 self.next_period();
106 }
107
108 // Half overflow
109 if self.rtc.compare_interrupt_status(0) {
110 self.rtc.compare_clear_flag(0);
111 self.next_period();
112 }
113
114 for n in 1..=ALARM_COUNT {
115 if self.rtc.compare_interrupt_status(n) {
116 self.rtc.compare_clear_flag(n);
117 interrupt::free(|cs| self.trigger_alarm(n, cs));
118 }
119 }
120 }
121
122 fn next_period(&self) {
123 interrupt::free(|cs| {
124 let period = self.period.fetch_add(1, Ordering::Relaxed) + 1;
125 let t = (period as u64) << 15;
126
127 for n in 1..=ALARM_COUNT {
128 let alarm = &self.alarms.borrow(cs)[n - 1];
129 let at = alarm.timestamp.get();
130
131 let diff = at - t;
132 if diff < 0xc000 {
133 self.rtc.set_compare(n, at as u16);
134 self.rtc.set_compare_interrupt(n, true);
135 }
136 }
137 })
138 }
139
140 fn trigger_alarm(&self, n: usize, cs: &CriticalSection) {
141 self.rtc.set_compare_interrupt(n, false);
142
143 let alarm = &self.alarms.borrow(cs)[n - 1];
144 alarm.timestamp.set(u64::MAX);
145
146 // Call after clearing alarm, so the callback can set another alarm.
147 if let Some((f, ctx)) = alarm.callback.get() {
148 f(ctx);
149 }
150 }
151
152 fn set_alarm_callback(&self, n: usize, callback: fn(*mut ()), ctx: *mut ()) {
153 interrupt::free(|cs| {
154 let alarm = &self.alarms.borrow(cs)[n - 1];
155 alarm.callback.set(Some((callback, ctx)));
156 })
157 }
158
159 fn set_alarm(&self, n: usize, timestamp: u64) {
160 interrupt::free(|cs| {
161 let alarm = &self.alarms.borrow(cs)[n - 1];
162 alarm.timestamp.set(timestamp);
163
164 let t = self.now();
165 if timestamp <= t {
166 self.trigger_alarm(n, cs);
167 return;
168 }
169
170 let diff = timestamp - t;
171 if diff < 0xc000 {
172 let safe_timestamp = timestamp.max(t + 3);
173 self.rtc.set_compare(n, safe_timestamp as u16);
174 self.rtc.set_compare_interrupt(n, true);
175 } else {
176 self.rtc.set_compare_interrupt(n, false);
177 }
178 })
179 }
180
181 pub fn alarm1(&'static self) -> Alarm<T> {
182 Alarm { n: 1, rtc: self }
183 }
184 pub fn alarm2(&'static self) -> Option<Alarm<T>> {
185 if T::REAL_ALARM_COUNT >= 2 {
186 Some(Alarm { n: 2, rtc: self })
187 } else {
188 None
189 }
190 }
191 pub fn alarm3(&'static self) -> Option<Alarm<T>> {
192 if T::REAL_ALARM_COUNT >= 3 {
193 Some(Alarm { n: 3, rtc: self })
194 } else {
195 None
196 }
197 }
198}
199
200impl<T: Instance> embassy::time::Clock for RTC<T> {
201 fn now(&self) -> u64 {
202 let period = self.period.load(Ordering::Relaxed);
203 compiler_fence(Ordering::Acquire);
204 let counter = self.rtc.counter();
205 calc_now(period, counter)
206 }
207}
208
209pub struct Alarm<T: Instance> {
210 n: usize,
211 rtc: &'static RTC<T>,
212}
213
214impl<T: Instance> embassy::time::Alarm for Alarm<T> {
215 fn set_callback(&self, callback: fn(*mut ()), ctx: *mut ()) {
216 self.rtc.set_alarm_callback(self.n, callback, ctx);
217 }
218
219 fn set(&self, timestamp: u64) {
220 self.rtc.set_alarm(self.n, timestamp);
221 }
222
223 fn clear(&self) {
224 self.rtc.set_alarm(self.n, u64::MAX);
225 }
226}
227
228mod sealed {
229 pub trait Sealed {}
230}
231
232pub trait Instance: sealed::Sealed + Sized + 'static {
233 type Interrupt: OwnedInterrupt;
234 const REAL_ALARM_COUNT: usize;
235
236 fn enable_clock(&self);
237 fn set_compare(&self, n: usize, value: u16);
238 fn set_compare_interrupt(&self, n: usize, enable: bool);
239 fn compare_interrupt_status(&self, n: usize) -> bool;
240 fn compare_clear_flag(&self, n: usize);
241 fn overflow_interrupt_status(&self) -> bool;
242 fn overflow_clear_flag(&self);
243 fn set_psc_arr(&self, psc: u16, arr: u16);
244 fn stop_and_reset(&self);
245 fn start(&self);
246 fn counter(&self) -> u16;
247 fn ppre(clocks: &Clocks) -> u8;
248 fn pclk(clocks: &Clocks) -> u32;
249}
250
251mod tim2 {
252 use super::*;
253 use stm32f4xx_hal::pac::{RCC, TIM2};
254
255 impl sealed::Sealed for TIM2 {}
256
257 impl Instance for TIM2 {
258 type Interrupt = interrupt::TIM2Interrupt;
259 const REAL_ALARM_COUNT: usize = 3;
260
261 fn enable_clock(&self) {
262 // NOTE(unsafe) It will only be used for atomic operations
263 unsafe {
264 let rcc = &*RCC::ptr();
265
266 bb::set(&rcc.apb1enr, 0);
267 bb::set(&rcc.apb1rstr, 0);
268 bb::clear(&rcc.apb1rstr, 0);
269 }
270 }
271
272 fn set_compare(&self, n: usize, value: u16) {
273 // NOTE(unsafe) these registers accept all the range of u16 values
274 match n {
275 0 => self.ccr1.write(|w| unsafe { w.bits(value.into()) }),
276 1 => self.ccr2.write(|w| unsafe { w.bits(value.into()) }),
277 2 => self.ccr3.write(|w| unsafe { w.bits(value.into()) }),
278 3 => self.ccr4.write(|w| unsafe { w.bits(value.into()) }),
279 _ => {}
280 }
281 }
282
283 fn set_compare_interrupt(&self, n: usize, enable: bool) {
284 if n > 3 {
285 return;
286 }
287 let bit = n as u8 + 1;
288 unsafe {
289 if enable {
290 bb::set(&self.dier, bit);
291 } else {
292 bb::clear(&self.dier, bit);
293 }
294 }
295 }
296
297 fn compare_interrupt_status(&self, n: usize) -> bool {
298 let status = self.sr.read();
299 match n {
300 0 => status.cc1if().bit_is_set(),
301 1 => status.cc2if().bit_is_set(),
302 2 => status.cc3if().bit_is_set(),
303 3 => status.cc4if().bit_is_set(),
304 _ => false,
305 }
306 }
307
308 fn compare_clear_flag(&self, n: usize) {
309 if n > 3 {
310 return;
311 }
312 let bit = n as u8 + 1;
313 unsafe {
314 bb::clear(&self.sr, bit);
315 }
316 }
317
318 fn overflow_interrupt_status(&self) -> bool {
319 self.sr.read().uif().bit_is_set()
320 }
321
322 fn overflow_clear_flag(&self) {
323 unsafe {
324 bb::clear(&self.sr, 0);
325 }
326 }
327
328 fn set_psc_arr(&self, psc: u16, arr: u16) {
329 // NOTE(unsafe) All u16 values are valid
330 self.psc.write(|w| unsafe { w.bits(psc.into()) });
331 self.arr.write(|w| unsafe { w.bits(arr.into()) });
332
333 unsafe {
334 // Set URS, generate update, clear URS
335 bb::set(&self.cr1, 2);
336 self.egr.write(|w| w.ug().set_bit());
337 bb::clear(&self.cr1, 2);
338 }
339 }
340
341 fn stop_and_reset(&self) {
342 unsafe {
343 bb::clear(&self.cr1, 0);
344 }
345 self.cnt.reset();
346 }
347
348 fn start(&self) {
349 unsafe { bb::set(&self.cr1, 0) }
350 }
351
352 fn counter(&self) -> u16 {
353 self.cnt.read().bits() as u16
354 }
355
356 fn ppre(clocks: &Clocks) -> u8 {
357 clocks.ppre1()
358 }
359
360 fn pclk(clocks: &Clocks) -> u32 {
361 clocks.pclk1().0
362 }
363 }
364}