diff options
| author | xoviat <[email protected]> | 2023-09-22 00:36:21 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-09-22 00:36:21 +0000 |
| commit | f1488864ebee3daebdc2cc6ed3ccdf7014fa67b2 (patch) | |
| tree | c2ab9527d56f0541fa8e7b91cc7b5e001116adce | |
| parent | 02b05231992af4fec3c92d0cc6ce99ae6508bc41 (diff) | |
| parent | 7cf327130e97f2569e1be73054a778ba5bf39d5b (diff) | |
Merge pull request #1937 from xoviat/low-power
stm32/low-power: create one critical-section for all time ops
| -rw-r--r-- | embassy-stm32/src/rtc/v2.rs | 46 | ||||
| -rw-r--r-- | embassy-stm32/src/time_driver.rs | 104 | ||||
| -rw-r--r-- | tests/stm32/src/bin/stop.rs | 33 |
3 files changed, 101 insertions, 82 deletions
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index aa3c31ee1..d139f2f47 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -64,7 +64,11 @@ impl super::Rtc { | |||
| 64 | #[cfg(feature = "low-power")] | 64 | #[cfg(feature = "low-power")] |
| 65 | /// start the wakeup alarm and wtih a duration that is as close to but less than | 65 | /// start the wakeup alarm and wtih a duration that is as close to but less than |
| 66 | /// the requested duration, and record the instant the wakeup alarm was started | 66 | /// the requested duration, and record the instant the wakeup alarm was started |
| 67 | pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) { | 67 | pub(crate) fn start_wakeup_alarm( |
| 68 | &self, | ||
| 69 | requested_duration: embassy_time::Duration, | ||
| 70 | cs: critical_section::CriticalSection, | ||
| 71 | ) { | ||
| 68 | use embassy_time::{Duration, TICK_HZ}; | 72 | use embassy_time::{Duration, TICK_HZ}; |
| 69 | 73 | ||
| 70 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] | 74 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] |
| @@ -102,25 +106,13 @@ impl super::Rtc { | |||
| 102 | self.instant(), | 106 | self.instant(), |
| 103 | ); | 107 | ); |
| 104 | 108 | ||
| 105 | critical_section::with(|cs| assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none())) | 109 | assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none()) |
| 106 | } | ||
| 107 | |||
| 108 | #[cfg(feature = "low-power")] | ||
| 109 | pub(crate) fn enable_wakeup_line(&self) { | ||
| 110 | use crate::interrupt::typelevel::Interrupt; | ||
| 111 | use crate::pac::EXTI; | ||
| 112 | |||
| 113 | <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend(); | ||
| 114 | unsafe { <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::enable() }; | ||
| 115 | |||
| 116 | EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | ||
| 117 | EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | ||
| 118 | } | 110 | } |
| 119 | 111 | ||
| 120 | #[cfg(feature = "low-power")] | 112 | #[cfg(feature = "low-power")] |
| 121 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` | 113 | /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` |
| 122 | /// was called, otherwise none | 114 | /// was called, otherwise none |
| 123 | pub(crate) fn stop_wakeup_alarm(&self) -> Option<embassy_time::Duration> { | 115 | pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { |
| 124 | use crate::interrupt::typelevel::Interrupt; | 116 | use crate::interrupt::typelevel::Interrupt; |
| 125 | 117 | ||
| 126 | trace!("rtc: stop wakeup alarm at {}", self.instant()); | 118 | trace!("rtc: stop wakeup alarm at {}", self.instant()); |
| @@ -137,13 +129,23 @@ impl super::Rtc { | |||
| 137 | <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend(); | 129 | <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend(); |
| 138 | }); | 130 | }); |
| 139 | 131 | ||
| 140 | critical_section::with(|cs| { | 132 | if let Some(stop_time) = self.stop_time.borrow(cs).take() { |
| 141 | if let Some(stop_time) = self.stop_time.borrow(cs).take() { | 133 | Some(self.instant() - stop_time) |
| 142 | Some(self.instant() - stop_time) | 134 | } else { |
| 143 | } else { | 135 | None |
| 144 | None | 136 | } |
| 145 | } | 137 | } |
| 146 | }) | 138 | |
| 139 | #[cfg(feature = "low-power")] | ||
| 140 | pub(crate) fn enable_wakeup_line(&self) { | ||
| 141 | use crate::interrupt::typelevel::Interrupt; | ||
| 142 | use crate::pac::EXTI; | ||
| 143 | |||
| 144 | <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend(); | ||
| 145 | unsafe { <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::enable() }; | ||
| 146 | |||
| 147 | EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | ||
| 148 | EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | ||
| 147 | } | 149 | } |
| 148 | 150 | ||
| 149 | /// Applies the RTC config | 151 | /// Applies the RTC config |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 887e54f65..5b01937f5 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -266,32 +266,28 @@ impl RtcDriver { | |||
| 266 | f(alarm.ctx.get()); | 266 | f(alarm.ctx.get()); |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | #[cfg(feature = "low-power")] | 269 | /* |
| 270 | /// Set the rtc but panic if it's already been set | 270 | Low-power private functions: all operate within a critical seciton |
| 271 | pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { | 271 | */ |
| 272 | critical_section::with(|cs| assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none())); | ||
| 273 | } | ||
| 274 | 272 | ||
| 275 | #[cfg(feature = "low-power")] | 273 | #[cfg(feature = "low-power")] |
| 276 | /// Compute the approximate amount of time until the next alarm | 274 | /// Compute the approximate amount of time until the next alarm |
| 277 | fn time_until_next_alarm(&self) -> embassy_time::Duration { | 275 | fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { |
| 278 | critical_section::with(|cs| { | 276 | let now = self.now() + 32; |
| 279 | let now = self.now() + 32; | 277 | |
| 280 | 278 | embassy_time::Duration::from_ticks( | |
| 281 | embassy_time::Duration::from_ticks( | 279 | self.alarms |
| 282 | self.alarms | 280 | .borrow(cs) |
| 283 | .borrow(cs) | 281 | .iter() |
| 284 | .iter() | 282 | .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) |
| 285 | .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) | 283 | .min() |
| 286 | .min() | 284 | .unwrap_or(u64::MAX), |
| 287 | .unwrap_or(u64::MAX), | 285 | ) |
| 288 | ) | ||
| 289 | }) | ||
| 290 | } | 286 | } |
| 291 | 287 | ||
| 292 | #[cfg(feature = "low-power")] | 288 | #[cfg(feature = "low-power")] |
| 293 | /// Add the given offset to the current time | 289 | /// Add the given offset to the current time |
| 294 | fn add_time(&self, offset: embassy_time::Duration) { | 290 | fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { |
| 295 | let offset = offset.as_ticks(); | 291 | let offset = offset.as_ticks(); |
| 296 | let cnt = T::regs_gp16().cnt().read().cnt() as u32; | 292 | let cnt = T::regs_gp16().cnt().read().cnt() as u32; |
| 297 | let period = self.period.load(Ordering::SeqCst); | 293 | let period = self.period.load(Ordering::SeqCst); |
| @@ -322,51 +318,57 @@ impl RtcDriver { | |||
| 322 | T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); | 318 | T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); |
| 323 | 319 | ||
| 324 | // Now, recompute all alarms | 320 | // Now, recompute all alarms |
| 325 | critical_section::with(|cs| { | 321 | for i in 0..ALARM_COUNT { |
| 326 | for i in 0..ALARM_COUNT { | 322 | let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; |
| 327 | let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; | 323 | let alarm = self.get_alarm(cs, alarm_handle); |
| 328 | let alarm = self.get_alarm(cs, alarm_handle); | ||
| 329 | 324 | ||
| 330 | self.set_alarm(alarm_handle, alarm.timestamp.get()); | 325 | self.set_alarm(alarm_handle, alarm.timestamp.get()); |
| 331 | } | 326 | } |
| 332 | }) | ||
| 333 | } | 327 | } |
| 334 | 328 | ||
| 335 | #[cfg(feature = "low-power")] | 329 | #[cfg(feature = "low-power")] |
| 336 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset | 330 | /// Stop the wakeup alarm, if enabled, and add the appropriate offset |
| 337 | fn stop_wakeup_alarm(&self) { | 331 | fn stop_wakeup_alarm(&self, cs: CriticalSection) { |
| 338 | critical_section::with(|cs| { | 332 | if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { |
| 339 | if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm() { | 333 | self.add_time(offset, cs); |
| 340 | self.add_time(offset); | 334 | } |
| 341 | } | 335 | } |
| 342 | }); | 336 | |
| 337 | /* | ||
| 338 | Low-power public functions: all create a critical section | ||
| 339 | */ | ||
| 340 | #[cfg(feature = "low-power")] | ||
| 341 | /// Set the rtc but panic if it's already been set | ||
| 342 | pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { | ||
| 343 | critical_section::with(|cs| assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none())); | ||
| 343 | } | 344 | } |
| 344 | 345 | ||
| 345 | #[cfg(feature = "low-power")] | 346 | #[cfg(feature = "low-power")] |
| 346 | /// Pause the timer if ready; return err if not | 347 | /// Pause the timer if ready; return err if not |
| 347 | pub(crate) fn pause_time(&self) -> Result<(), ()> { | 348 | pub(crate) fn pause_time(&self) -> Result<(), ()> { |
| 348 | /* | 349 | critical_section::with(|cs| { |
| 349 | If the wakeup timer is currently running, then we need to stop it and | 350 | /* |
| 350 | add the elapsed time to the current time | 351 | If the wakeup timer is currently running, then we need to stop it and |
| 351 | */ | 352 | add the elapsed time to the current time, as this will impact the result |
| 352 | self.stop_wakeup_alarm(); | 353 | of `time_until_next_alarm`. |
| 353 | 354 | */ | |
| 354 | let time_until_next_alarm = self.time_until_next_alarm(); | 355 | self.stop_wakeup_alarm(cs); |
| 355 | if time_until_next_alarm < embassy_time::Duration::from_millis(250) { | 356 | |
| 356 | Err(()) | 357 | let time_until_next_alarm = self.time_until_next_alarm(cs); |
| 357 | } else { | 358 | if time_until_next_alarm < embassy_time::Duration::from_millis(250) { |
| 358 | critical_section::with(|cs| { | 359 | Err(()) |
| 360 | } else { | ||
| 359 | self.rtc | 361 | self.rtc |
| 360 | .borrow(cs) | 362 | .borrow(cs) |
| 361 | .get() | 363 | .get() |
| 362 | .unwrap() | 364 | .unwrap() |
| 363 | .start_wakeup_alarm(time_until_next_alarm); | 365 | .start_wakeup_alarm(time_until_next_alarm, cs); |
| 364 | }); | ||
| 365 | 366 | ||
| 366 | T::regs_gp16().cr1().modify(|w| w.set_cen(false)); | 367 | T::regs_gp16().cr1().modify(|w| w.set_cen(false)); |
| 367 | 368 | ||
| 368 | Ok(()) | 369 | Ok(()) |
| 369 | } | 370 | } |
| 371 | }) | ||
| 370 | } | 372 | } |
| 371 | 373 | ||
| 372 | #[cfg(feature = "low-power")] | 374 | #[cfg(feature = "low-power")] |
| @@ -378,9 +380,11 @@ impl RtcDriver { | |||
| 378 | return; | 380 | return; |
| 379 | } | 381 | } |
| 380 | 382 | ||
| 381 | self.stop_wakeup_alarm(); | 383 | critical_section::with(|cs| { |
| 384 | self.stop_wakeup_alarm(cs); | ||
| 382 | 385 | ||
| 383 | T::regs_gp16().cr1().modify(|w| w.set_cen(true)); | 386 | T::regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| 387 | }) | ||
| 384 | } | 388 | } |
| 385 | } | 389 | } |
| 386 | 390 | ||
diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs index f60ab271a..48d59b794 100644 --- a/tests/stm32/src/bin/stop.rs +++ b/tests/stm32/src/bin/stop.rs | |||
| @@ -19,14 +19,32 @@ use static_cell::make_static; | |||
| 19 | 19 | ||
| 20 | #[entry] | 20 | #[entry] |
| 21 | fn main() -> ! { | 21 | fn main() -> ! { |
| 22 | let executor = Executor::take(); | 22 | Executor::take().run(|spawner| { |
| 23 | executor.run(|spawner| { | ||
| 24 | unwrap!(spawner.spawn(async_main(spawner))); | 23 | unwrap!(spawner.spawn(async_main(spawner))); |
| 25 | }); | 24 | }); |
| 26 | } | 25 | } |
| 27 | 26 | ||
| 28 | #[embassy_executor::task] | 27 | #[embassy_executor::task] |
| 29 | async fn async_main(_spawner: Spawner) { | 28 | async fn task_1() { |
| 29 | for _ in 0..9 { | ||
| 30 | info!("task 1: waiting for 500ms..."); | ||
| 31 | Timer::after(Duration::from_millis(500)).await; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | #[embassy_executor::task] | ||
| 36 | async fn task_2() { | ||
| 37 | for _ in 0..5 { | ||
| 38 | info!("task 2: waiting for 1000ms..."); | ||
| 39 | Timer::after(Duration::from_millis(1000)).await; | ||
| 40 | } | ||
| 41 | |||
| 42 | info!("Test OK"); | ||
| 43 | cortex_m::asm::bkpt(); | ||
| 44 | } | ||
| 45 | |||
| 46 | #[embassy_executor::task] | ||
| 47 | async fn async_main(spawner: Spawner) { | ||
| 30 | let mut config = config(); | 48 | let mut config = config(); |
| 31 | 49 | ||
| 32 | config.rcc.lse = Some(Hertz(32_768)); | 50 | config.rcc.lse = Some(Hertz(32_768)); |
| @@ -48,11 +66,6 @@ async fn async_main(_spawner: Spawner) { | |||
| 48 | 66 | ||
| 49 | stop_with_rtc(rtc); | 67 | stop_with_rtc(rtc); |
| 50 | 68 | ||
| 51 | info!("Waiting..."); | 69 | spawner.spawn(task_1()).unwrap(); |
| 52 | Timer::after(Duration::from_secs(2)).await; | 70 | spawner.spawn(task_2()).unwrap(); |
| 53 | info!("Waiting..."); | ||
| 54 | Timer::after(Duration::from_secs(3)).await; | ||
| 55 | |||
| 56 | info!("Test OK"); | ||
| 57 | cortex_m::asm::bkpt(); | ||
| 58 | } | 71 | } |
