aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/time_driver.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/time_driver.rs')
-rw-r--r--embassy-stm32/src/time_driver.rs285
1 files changed, 87 insertions, 198 deletions
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 7db74bdf6..ed5d902bd 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -1,22 +1,24 @@
1#![allow(non_snake_case)] 1#![allow(non_snake_case)]
2 2
3use core::cell::{Cell, RefCell}; 3use core::cell::{Cell, RefCell};
4use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; 4#[cfg(all(feature = "low-power", stm32wlex))]
5use core::sync::atomic::AtomicU16;
6use core::sync::atomic::{AtomicU32, Ordering, compiler_fence};
5 7
6use critical_section::CriticalSection; 8use critical_section::CriticalSection;
7use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
8use embassy_sync::blocking_mutex::Mutex; 9use embassy_sync::blocking_mutex::Mutex;
10use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
9use embassy_time_driver::{Driver, TICK_HZ}; 11use embassy_time_driver::{Driver, TICK_HZ};
10use embassy_time_queue_utils::Queue; 12use embassy_time_queue_utils::Queue;
11use stm32_metapac::timer::{regs, TimGp16}; 13use stm32_metapac::timer::{TimGp16, regs};
12 14
13use crate::interrupt::typelevel::Interrupt; 15use crate::interrupt::typelevel::Interrupt;
14use crate::pac::timer::vals; 16use crate::pac::timer::vals;
17use crate::peripherals;
15use crate::rcc::{self, SealedRccPeripheral}; 18use crate::rcc::{self, SealedRccPeripheral};
16#[cfg(feature = "low-power")] 19#[cfg(feature = "low-power")]
17use crate::rtc::Rtc; 20use crate::rtc::Rtc;
18use crate::timer::{CoreInstance, GeneralInstance1Channel}; 21use crate::timer::{CoreInstance, GeneralInstance1Channel};
19use crate::{interrupt, peripherals};
20 22
21// NOTE regarding ALARM_COUNT: 23// NOTE regarding ALARM_COUNT:
22// 24//
@@ -54,121 +56,6 @@ type T = peripherals::TIM23;
54#[cfg(time_driver_tim24)] 56#[cfg(time_driver_tim24)]
55type T = peripherals::TIM24; 57type T = peripherals::TIM24;
56 58
57foreach_interrupt! {
58 (TIM1, timer, $block:ident, CC, $irq:ident) => {
59 #[cfg(time_driver_tim1)]
60 #[cfg(feature = "rt")]
61 #[interrupt]
62 fn $irq() {
63 DRIVER.on_interrupt()
64 }
65 };
66 (TIM2, timer, $block:ident, CC, $irq:ident) => {
67 #[cfg(time_driver_tim2)]
68 #[cfg(feature = "rt")]
69 #[interrupt]
70 fn $irq() {
71 DRIVER.on_interrupt()
72 }
73 };
74 (TIM3, timer, $block:ident, CC, $irq:ident) => {
75 #[cfg(time_driver_tim3)]
76 #[cfg(feature = "rt")]
77 #[interrupt]
78 fn $irq() {
79 DRIVER.on_interrupt()
80 }
81 };
82 (TIM4, timer, $block:ident, CC, $irq:ident) => {
83 #[cfg(time_driver_tim4)]
84 #[cfg(feature = "rt")]
85 #[interrupt]
86 fn $irq() {
87 DRIVER.on_interrupt()
88 }
89 };
90 (TIM5, timer, $block:ident, CC, $irq:ident) => {
91 #[cfg(time_driver_tim5)]
92 #[cfg(feature = "rt")]
93 #[interrupt]
94 fn $irq() {
95 DRIVER.on_interrupt()
96 }
97 };
98 (TIM8, timer, $block:ident, CC, $irq:ident) => {
99 #[cfg(time_driver_tim8)]
100 #[cfg(feature = "rt")]
101 #[interrupt]
102 fn $irq() {
103 DRIVER.on_interrupt()
104 }
105 };
106 (TIM9, timer, $block:ident, CC, $irq:ident) => {
107 #[cfg(time_driver_tim9)]
108 #[cfg(feature = "rt")]
109 #[interrupt]
110 fn $irq() {
111 DRIVER.on_interrupt()
112 }
113 };
114 (TIM12, timer, $block:ident, CC, $irq:ident) => {
115 #[cfg(time_driver_tim12)]
116 #[cfg(feature = "rt")]
117 #[interrupt]
118 fn $irq() {
119 DRIVER.on_interrupt()
120 }
121 };
122 (TIM15, timer, $block:ident, CC, $irq:ident) => {
123 #[cfg(time_driver_tim15)]
124 #[cfg(feature = "rt")]
125 #[interrupt]
126 fn $irq() {
127 DRIVER.on_interrupt()
128 }
129 };
130 (TIM20, timer, $block:ident, CC, $irq:ident) => {
131 #[cfg(time_driver_tim20)]
132 #[cfg(feature = "rt")]
133 #[interrupt]
134 fn $irq() {
135 DRIVER.on_interrupt()
136 }
137 };
138 (TIM21, timer, $block:ident, CC, $irq:ident) => {
139 #[cfg(time_driver_tim21)]
140 #[cfg(feature = "rt")]
141 #[interrupt]
142 fn $irq() {
143 DRIVER.on_interrupt()
144 }
145 };
146 (TIM22, timer, $block:ident, CC, $irq:ident) => {
147 #[cfg(time_driver_tim22)]
148 #[cfg(feature = "rt")]
149 #[interrupt]
150 fn $irq() {
151 DRIVER.on_interrupt()
152 }
153 };
154 (TIM23, timer, $block:ident, CC, $irq:ident) => {
155 #[cfg(time_driver_tim23)]
156 #[cfg(feature = "rt")]
157 #[interrupt]
158 fn $irq() {
159 DRIVER.on_interrupt()
160 }
161 };
162 (TIM24, timer, $block:ident, CC, $irq:ident) => {
163 #[cfg(time_driver_tim24)]
164 #[cfg(feature = "rt")]
165 #[interrupt]
166 fn $irq() {
167 DRIVER.on_interrupt()
168 }
169 };
170}
171
172fn regs_gp16() -> TimGp16 { 59fn regs_gp16() -> TimGp16 {
173 unsafe { TimGp16::from_ptr(T::regs()) } 60 unsafe { TimGp16::from_ptr(T::regs()) }
174} 61}
@@ -194,6 +81,11 @@ fn calc_now(period: u32, counter: u16) -> u64 {
194 ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) 81 ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
195} 82}
196 83
84#[cfg(feature = "low-power")]
85fn calc_period_counter(ticks: u64) -> (u32, u16) {
86 (2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16)
87}
88
197struct AlarmState { 89struct AlarmState {
198 timestamp: Cell<u64>, 90 timestamp: Cell<u64>,
199} 91}
@@ -213,7 +105,13 @@ pub(crate) struct RtcDriver {
213 period: AtomicU32, 105 period: AtomicU32,
214 alarm: Mutex<CriticalSectionRawMutex, AlarmState>, 106 alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
215 #[cfg(feature = "low-power")] 107 #[cfg(feature = "low-power")]
216 rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, 108 pub(crate) rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>,
109 #[cfg(feature = "low-power")]
110 /// The minimum pause time beyond which the executor will enter a low-power state.
111 min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>,
112 /// Saved count for the timer (its value is lost when entering STOP2)
113 #[cfg(all(feature = "low-power", stm32wlex))]
114 saved_count: AtomicU16,
217 queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, 115 queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
218} 116}
219 117
@@ -221,12 +119,18 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
221 period: AtomicU32::new(0), 119 period: AtomicU32::new(0),
222 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), 120 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
223 #[cfg(feature = "low-power")] 121 #[cfg(feature = "low-power")]
224 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), 122 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)),
123 #[cfg(feature = "low-power")]
124 min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))),
125 #[cfg(all(feature = "low-power", stm32wlex))]
126 saved_count: AtomicU16::new(0),
225 queue: Mutex::new(RefCell::new(Queue::new())) 127 queue: Mutex::new(RefCell::new(Queue::new()))
226}); 128});
227 129
228impl RtcDriver { 130impl RtcDriver {
229 fn init(&'static self, cs: critical_section::CriticalSection) { 131 /// initialize the timer, but don't start it. Used for chips like stm32wle5
132 /// for low power where the timer config is lost in STOP2.
133 pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) {
230 let r = regs_gp16(); 134 let r = regs_gp16();
231 135
232 rcc::enable_and_reset_with_cs::<T>(cs); 136 rcc::enable_and_reset_with_cs::<T>(cs);
@@ -259,13 +163,23 @@ impl RtcDriver {
259 w.set_ccie(0, true); 163 w.set_ccie(0, true);
260 }); 164 });
261 165
166 #[cfg(all(feature = "low-power", stm32wlex))]
167 r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst)));
168
262 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); 169 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend();
263 unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; 170 <T as CoreInstance>::UpdateInterrupt::unpend();
171 unsafe {
172 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable();
173 <T as CoreInstance>::UpdateInterrupt::enable();
174 }
175 }
264 176
265 r.cr1().modify(|w| w.set_cen(true)); 177 fn init(&'static self, cs: CriticalSection) {
178 self.init_timer(cs);
179 regs_gp16().cr1().modify(|w| w.set_cen(true));
266 } 180 }
267 181
268 fn on_interrupt(&self) { 182 pub(crate) fn on_interrupt(&self) {
269 let r = regs_gp16(); 183 let r = regs_gp16();
270 184
271 critical_section::with(|cs| { 185 critical_section::with(|cs| {
@@ -338,34 +252,10 @@ impl RtcDriver {
338 #[cfg(feature = "low-power")] 252 #[cfg(feature = "low-power")]
339 /// Add the given offset to the current time 253 /// Add the given offset to the current time
340 fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { 254 fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) {
341 let offset = offset.as_ticks(); 255 let (period, counter) = calc_period_counter(self.now() + offset.as_ticks());
342 let cnt = regs_gp16().cnt().read().cnt() as u32;
343 let period = self.period.load(Ordering::SeqCst);
344
345 // Correct the race, if it exists
346 let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 {
347 period + 1
348 } else {
349 period
350 };
351
352 // Normalize to the full overflow
353 let period = (period / 2) * 2;
354
355 // Add the offset
356 let period = period + 2 * (offset / u16::MAX as u64) as u32;
357 let cnt = cnt + (offset % u16::MAX as u64) as u32;
358
359 let (cnt, period) = if cnt > u16::MAX as u32 {
360 (cnt - u16::MAX as u32, period + 2)
361 } else {
362 (cnt, period)
363 };
364
365 let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period };
366 256
367 self.period.store(period, Ordering::SeqCst); 257 self.period.store(period, Ordering::SeqCst);
368 regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); 258 regs_gp16().cnt().write(|w| w.set_cnt(counter));
369 259
370 // Now, recompute alarm 260 // Now, recompute alarm
371 let alarm = self.alarm.borrow(cs); 261 let alarm = self.alarm.borrow(cs);
@@ -379,70 +269,70 @@ impl RtcDriver {
379 #[cfg(feature = "low-power")] 269 #[cfg(feature = "low-power")]
380 /// Stop the wakeup alarm, if enabled, and add the appropriate offset 270 /// Stop the wakeup alarm, if enabled, and add the appropriate offset
381 fn stop_wakeup_alarm(&self, cs: CriticalSection) { 271 fn stop_wakeup_alarm(&self, cs: CriticalSection) {
382 if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { 272 if !regs_gp16().cr1().read().cen()
273 && let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs)
274 {
383 self.add_time(offset, cs); 275 self.add_time(offset, cs);
384 } 276 }
385 } 277 }
386 278
387 /* 279 /*
388 Low-power public functions: all create a critical section 280 Low-power public functions: all require a critical section
389 */ 281 */
390 #[cfg(feature = "low-power")] 282 #[cfg(feature = "low-power")]
391 /// Set the rtc but panic if it's already been set 283 pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) {
392 pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { 284 self.min_stop_pause.borrow(cs).replace(min_stop_pause);
393 critical_section::with(|cs| {
394 rtc.stop_wakeup_alarm(cs);
395
396 assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none())
397 });
398 } 285 }
399 286
400 #[cfg(feature = "low-power")] 287 #[cfg(feature = "low-power")]
401 /// The minimum pause time beyond which the executor will enter a low-power state. 288 /// Set the rtc but panic if it's already been set
402 pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250); 289 pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) {
290 rtc.stop_wakeup_alarm(cs);
291
292 assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none());
293 }
403 294
404 #[cfg(feature = "low-power")] 295 #[cfg(feature = "low-power")]
405 /// Pause the timer if ready; return err if not 296 /// Pause the timer if ready; return err if not
406 pub(crate) fn pause_time(&self) -> Result<(), ()> { 297 pub(crate) fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> {
407 critical_section::with(|cs| { 298 self.stop_wakeup_alarm(cs);
408 /* 299
409 If the wakeup timer is currently running, then we need to stop it and 300 let time_until_next_alarm = self.time_until_next_alarm(cs);
410 add the elapsed time to the current time, as this will impact the result 301 if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() {
411 of `time_until_next_alarm`. 302 trace!(
412 */ 303 "time_until_next_alarm < self.min_stop_pause ({})",
413 self.stop_wakeup_alarm(cs); 304 time_until_next_alarm
414 305 );
415 let time_until_next_alarm = self.time_until_next_alarm(cs); 306 Err(())
416 if time_until_next_alarm < Self::MIN_STOP_PAUSE { 307 } else {
417 Err(()) 308 self.rtc
418 } else { 309 .borrow(cs)
419 self.rtc 310 .borrow_mut()
420 .borrow(cs) 311 .as_mut()
421 .get() 312 .unwrap()
422 .unwrap() 313 .start_wakeup_alarm(time_until_next_alarm, cs);
423 .start_wakeup_alarm(time_until_next_alarm, cs); 314
424 315 regs_gp16().cr1().modify(|w| w.set_cen(false));
425 regs_gp16().cr1().modify(|w| w.set_cen(false)); 316 // save the count for the timer as its lost in STOP2 for stm32wlex
426 317 #[cfg(stm32wlex)]
427 Ok(()) 318 self.saved_count
428 } 319 .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst);
429 }) 320 Ok(())
321 }
430 } 322 }
431 323
432 #[cfg(feature = "low-power")] 324 #[cfg(feature = "low-power")]
433 /// Resume the timer with the given offset 325 /// Resume the timer with the given offset
434 pub(crate) fn resume_time(&self) { 326 pub(crate) fn resume_time(&self, cs: CriticalSection) {
435 if regs_gp16().cr1().read().cen() { 327 self.stop_wakeup_alarm(cs);
436 // Time isn't currently stopped
437 328
438 return; 329 regs_gp16().cr1().modify(|w| w.set_cen(true));
439 } 330 }
440
441 critical_section::with(|cs| {
442 self.stop_wakeup_alarm(cs);
443 331
444 regs_gp16().cr1().modify(|w| w.set_cen(true)); 332 #[cfg(feature = "low-power")]
445 }) 333 /// Returns whether time is currently stopped
334 pub(crate) fn is_stopped(&self) -> bool {
335 !regs_gp16().cr1().read().cen()
446 } 336 }
447 337
448 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { 338 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
@@ -513,8 +403,7 @@ impl Driver for RtcDriver {
513 } 403 }
514} 404}
515 405
516#[cfg(feature = "low-power")] 406pub(crate) const fn get_driver() -> &'static RtcDriver {
517pub(crate) fn get_driver() -> &'static RtcDriver {
518 &DRIVER 407 &DRIVER
519} 408}
520 409