diff options
| author | xoviat <[email protected]> | 2023-08-24 19:29:11 -0500 |
|---|---|---|
| committer | xoviat <[email protected]> | 2023-08-24 19:29:11 -0500 |
| commit | cda404731093015f84bad96675fdbfc712bc0215 (patch) | |
| tree | ca57bafa357628131a46384645ae738cc78b8123 | |
| parent | ecc305bbfe1007f9daa4d6a585dfc66f6ca69218 (diff) | |
stm32: flesh out lp executor
| -rw-r--r-- | embassy-stm32/src/lib.rs | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/low_power.rs | 45 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/v2.rs | 25 | ||||
| -rw-r--r-- | embassy-stm32/src/time_driver.rs | 76 |
5 files changed, 133 insertions, 20 deletions
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 8c87ea7d5..ec8648ee4 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -197,6 +197,11 @@ pub fn init(config: Config) -> Peripherals { | |||
| 197 | // must be after rcc init | 197 | // must be after rcc init |
| 198 | #[cfg(feature = "_time-driver")] | 198 | #[cfg(feature = "_time-driver")] |
| 199 | time_driver::init(); | 199 | time_driver::init(); |
| 200 | |||
| 201 | #[cfg(feature = "low-power")] | ||
| 202 | while !crate::rcc::low_power_ready() { | ||
| 203 | crate::rcc::clock_refcount_sub(); | ||
| 204 | } | ||
| 200 | } | 205 | } |
| 201 | 206 | ||
| 202 | p | 207 | p |
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 7814fa384..0d9506aaa 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -9,6 +9,7 @@ use crate::interrupt; | |||
| 9 | use crate::interrupt::typelevel::Interrupt; | 9 | use crate::interrupt::typelevel::Interrupt; |
| 10 | use crate::pac::EXTI; | 10 | use crate::pac::EXTI; |
| 11 | use crate::rcc::low_power_ready; | 11 | use crate::rcc::low_power_ready; |
| 12 | use crate::time_driver::{pause_time, resume_time, time_until_next_alarm}; | ||
| 12 | 13 | ||
| 13 | const THREAD_PENDER: usize = usize::MAX; | 14 | const THREAD_PENDER: usize = usize::MAX; |
| 14 | const THRESHOLD: Duration = Duration::from_millis(500); | 15 | const THRESHOLD: Duration = Duration::from_millis(500); |
| @@ -16,6 +17,9 @@ const THRESHOLD: Duration = Duration::from_millis(500); | |||
| 16 | use crate::rtc::{Rtc, RtcInstant}; | 17 | use crate::rtc::{Rtc, RtcInstant}; |
| 17 | 18 | ||
| 18 | static mut RTC: Option<&'static Rtc> = None; | 19 | static mut RTC: Option<&'static Rtc> = None; |
| 20 | static mut STOP_TIME: embassy_time::Duration = Duration::from_ticks(0); | ||
| 21 | static mut NEXT_ALARM: embassy_time::Duration = Duration::from_ticks(u64::MAX); | ||
| 22 | static mut RTC_INSTANT: Option<crate::rtc::RtcInstant> = None; | ||
| 19 | 23 | ||
| 20 | foreach_interrupt! { | 24 | foreach_interrupt! { |
| 21 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { | 25 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { |
| @@ -69,13 +73,25 @@ impl Executor { | |||
| 69 | } | 73 | } |
| 70 | 74 | ||
| 71 | unsafe fn on_wakeup_irq() { | 75 | unsafe fn on_wakeup_irq() { |
| 72 | info!("on wakeup irq"); | 76 | trace!("on wakeup irq"); |
| 73 | 77 | ||
| 74 | cortex_m::asm::bkpt(); | 78 | let elapsed = RTC_INSTANT.take().unwrap() - stop_wakeup_alarm(); |
| 75 | } | 79 | |
| 80 | STOP_TIME += elapsed; | ||
| 81 | // let to_next = NEXT_ALARM - STOP_TIME; | ||
| 82 | let to_next = Duration::from_secs(3); | ||
| 76 | 83 | ||
| 77 | fn time_until_next_alarm(&self) -> Duration { | 84 | trace!("on wakeup irq: to next: {}", to_next); |
| 78 | Duration::from_secs(3) | 85 | if to_next > THRESHOLD { |
| 86 | trace!("start wakeup alarm"); | ||
| 87 | RTC_INSTANT.replace(start_wakeup_alarm(to_next)); | ||
| 88 | |||
| 89 | trace!("set sleeponexit"); | ||
| 90 | Self::get_scb().set_sleeponexit(); | ||
| 91 | } else { | ||
| 92 | Self::get_scb().clear_sleeponexit(); | ||
| 93 | Self::get_scb().clear_sleepdeep(); | ||
| 94 | } | ||
| 79 | } | 95 | } |
| 80 | 96 | ||
| 81 | fn get_scb() -> SCB { | 97 | fn get_scb() -> SCB { |
| @@ -86,25 +102,28 @@ impl Executor { | |||
| 86 | trace!("configure_pwr"); | 102 | trace!("configure_pwr"); |
| 87 | 103 | ||
| 88 | if !low_power_ready() { | 104 | if !low_power_ready() { |
| 105 | trace!("configure_pwr: low power not ready"); | ||
| 89 | return; | 106 | return; |
| 90 | } | 107 | } |
| 91 | 108 | ||
| 92 | let time_until_next_alarm = self.time_until_next_alarm(); | 109 | let time_until_next_alarm = time_until_next_alarm(); |
| 93 | if time_until_next_alarm < THRESHOLD { | 110 | if time_until_next_alarm < THRESHOLD { |
| 111 | trace!("configure_pwr: not enough time until next alarm"); | ||
| 94 | return; | 112 | return; |
| 95 | } | 113 | } |
| 96 | 114 | ||
| 97 | trace!("low power stop required"); | 115 | unsafe { |
| 116 | NEXT_ALARM = time_until_next_alarm; | ||
| 117 | RTC_INSTANT = Some(start_wakeup_alarm(time_until_next_alarm)) | ||
| 118 | }; | ||
| 98 | 119 | ||
| 99 | critical_section::with(|_| { | 120 | // return; |
| 100 | trace!("executor: set wakeup alarm..."); | ||
| 101 | 121 | ||
| 102 | start_wakeup_alarm(time_until_next_alarm); | 122 | pause_time(); |
| 103 | 123 | ||
| 104 | trace!("low power wait for rtc ready..."); | 124 | trace!("enter stop..."); |
| 105 | 125 | ||
| 106 | Self::get_scb().set_sleepdeep(); | 126 | Self::get_scb().set_sleepdeep(); |
| 107 | }); | ||
| 108 | } | 127 | } |
| 109 | 128 | ||
| 110 | /// Run the executor. | 129 | /// Run the executor. |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 3c75923e5..45a4d880d 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -86,6 +86,8 @@ static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0); | |||
| 86 | 86 | ||
| 87 | #[cfg(feature = "low-power")] | 87 | #[cfg(feature = "low-power")] |
| 88 | pub fn low_power_ready() -> bool { | 88 | pub fn low_power_ready() -> bool { |
| 89 | trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst)); | ||
| 90 | |||
| 89 | CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 | 91 | CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 |
| 90 | } | 92 | } |
| 91 | 93 | ||
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index bcb127ecb..197c3b8f9 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -30,9 +30,6 @@ impl RtcInstant { | |||
| 30 | 30 | ||
| 31 | let _ = RTC::regs().dr().read(); | 31 | let _ = RTC::regs().dr().read(); |
| 32 | 32 | ||
| 33 | trace!("ssr: {}", ssr); | ||
| 34 | trace!("st: {}", st); | ||
| 35 | |||
| 36 | Self { ssr, st } | 33 | Self { ssr, st } |
| 37 | } | 34 | } |
| 38 | } | 35 | } |
| @@ -52,7 +49,12 @@ impl core::ops::Sub for RtcInstant { | |||
| 52 | let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); | 49 | let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); |
| 53 | let rtc_ticks = self_ticks - other_ticks; | 50 | let rtc_ticks = self_ticks - other_ticks; |
| 54 | 51 | ||
| 55 | trace!("self, other, rtc ticks: {}, {}, {}", self_ticks, other_ticks, rtc_ticks); | 52 | trace!( |
| 53 | "rtc: instant sub: self, other, rtc ticks: {}, {}, {}", | ||
| 54 | self_ticks, | ||
| 55 | other_ticks, | ||
| 56 | rtc_ticks | ||
| 57 | ); | ||
| 56 | 58 | ||
| 57 | Duration::from_ticks( | 59 | Duration::from_ticks( |
| 58 | ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) | 60 | ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) |
| @@ -174,10 +176,10 @@ impl super::Rtc { | |||
| 174 | rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz, | 176 | rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz, |
| 175 | ); | 177 | ); |
| 176 | 178 | ||
| 177 | trace!("set wakeup timer for {} ms", duration.as_millis()); | 179 | trace!("rtc: set wakeup timer for {} ms", duration.as_millis()); |
| 178 | 180 | ||
| 179 | self.write(false, |regs| { | 181 | self.write(false, |regs| { |
| 180 | regs.cr().modify(|w| w.set_wutie(true)); | 182 | // regs.cr().modify(|w| w.set_wutie(true)); |
| 181 | 183 | ||
| 182 | regs.cr().modify(|w| w.set_wute(false)); | 184 | regs.cr().modify(|w| w.set_wute(false)); |
| 183 | regs.isr().modify(|w| w.set_wutf(false)); | 185 | regs.isr().modify(|w| w.set_wutf(false)); |
| @@ -187,6 +189,15 @@ impl super::Rtc { | |||
| 187 | regs.cr().modify(|w| w.set_wute(true)); | 189 | regs.cr().modify(|w| w.set_wute(true)); |
| 188 | }); | 190 | }); |
| 189 | 191 | ||
| 192 | self.write(false, |regs| { | ||
| 193 | regs.cr().modify(|w| w.set_wutie(false)); | ||
| 194 | |||
| 195 | regs.isr().modify(|w| w.set_wutf(false)); | ||
| 196 | crate::pac::PWR.cr1().modify(|w| w.set_cwuf(false)); | ||
| 197 | |||
| 198 | regs.cr().modify(|w| w.set_wutie(true)); | ||
| 199 | }); | ||
| 200 | |||
| 190 | RtcInstant::now() | 201 | RtcInstant::now() |
| 191 | } | 202 | } |
| 192 | 203 | ||
| @@ -197,7 +208,7 @@ impl super::Rtc { | |||
| 197 | /// note: this api is exposed for testing purposes until low power is implemented. | 208 | /// note: this api is exposed for testing purposes until low power is implemented. |
| 198 | /// it is not intended to be public | 209 | /// it is not intended to be public |
| 199 | pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { | 210 | pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { |
| 200 | trace!("disable wakeup timer..."); | 211 | trace!("rtc: stop wakeup alarm..."); |
| 201 | 212 | ||
| 202 | self.write(false, |regs| { | 213 | self.write(false, |regs| { |
| 203 | regs.cr().modify(|w| w.set_wute(false)); | 214 | regs.cr().modify(|w| w.set_wute(false)); |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 2622442f4..8e05346ad 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -259,6 +259,64 @@ impl RtcDriver { | |||
| 259 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; | 259 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; |
| 260 | f(alarm.ctx.get()); | 260 | f(alarm.ctx.get()); |
| 261 | } | 261 | } |
| 262 | |||
| 263 | #[cfg(feature = "low-power")] | ||
| 264 | /// Compute the approximate amount of time until the next alarm | ||
| 265 | pub(crate) fn time_until_next_alarm(&self) -> embassy_time::Duration { | ||
| 266 | critical_section::with(|cs| { | ||
| 267 | let now = self.now() + 32; | ||
| 268 | |||
| 269 | embassy_time::Duration::from_ticks( | ||
| 270 | self.alarms | ||
| 271 | .borrow(cs) | ||
| 272 | .iter() | ||
| 273 | .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) | ||
| 274 | .min() | ||
| 275 | .unwrap_or(u64::MAX), | ||
| 276 | ) | ||
| 277 | }) | ||
| 278 | } | ||
| 279 | |||
| 280 | #[cfg(feature = "low-power")] | ||
| 281 | /// Pause the timer | ||
| 282 | pub(crate) fn pause_time(&self) { | ||
| 283 | T::regs_gp16().cr1().modify(|w| w.set_cen(false)); | ||
| 284 | } | ||
| 285 | |||
| 286 | #[cfg(feature = "low-power")] | ||
| 287 | /// Resume the timer with the given offset | ||
| 288 | pub(crate) fn resume_time(&self, offset: embassy_time::Duration) { | ||
| 289 | let offset = offset.as_ticks(); | ||
| 290 | let cnt = T::regs_gp16().cnt().read().cnt() as u32; | ||
| 291 | let period = self.period.load(Ordering::SeqCst); | ||
| 292 | |||
| 293 | // Correct the race, if it exists | ||
| 294 | let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { | ||
| 295 | period + 1 | ||
| 296 | } else { | ||
| 297 | period | ||
| 298 | }; | ||
| 299 | |||
| 300 | // Normalize to the full overflow | ||
| 301 | let period = (period / 2) * 2; | ||
| 302 | |||
| 303 | // Add the offset | ||
| 304 | let period = period + 2 * (offset / u16::MAX as u64) as u32; | ||
| 305 | let cnt = cnt + (offset % u16::MAX as u64) as u32; | ||
| 306 | |||
| 307 | let (cnt, period) = if cnt > u16::MAX as u32 { | ||
| 308 | (cnt - u16::MAX as u32, period + 2) | ||
| 309 | } else { | ||
| 310 | (cnt, period) | ||
| 311 | }; | ||
| 312 | |||
| 313 | let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; | ||
| 314 | |||
| 315 | self.period.store(period, Ordering::SeqCst); | ||
| 316 | T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); | ||
| 317 | |||
| 318 | T::regs_gp16().cr1().modify(|w| w.set_cen(true)); | ||
| 319 | } | ||
| 262 | } | 320 | } |
| 263 | 321 | ||
| 264 | impl Driver for RtcDriver { | 322 | impl Driver for RtcDriver { |
| @@ -329,6 +387,24 @@ impl Driver for RtcDriver { | |||
| 329 | } | 387 | } |
| 330 | } | 388 | } |
| 331 | 389 | ||
| 390 | #[cfg(feature = "low-power")] | ||
| 391 | /// Compute the approximate amount of time until the next alarm | ||
| 392 | pub(crate) fn time_until_next_alarm() -> embassy_time::Duration { | ||
| 393 | DRIVER.time_until_next_alarm() | ||
| 394 | } | ||
| 395 | |||
| 396 | #[cfg(feature = "low-power")] | ||
| 397 | /// Pause the timer | ||
| 398 | pub(crate) fn pause_time() { | ||
| 399 | DRIVER.pause_time(); | ||
| 400 | } | ||
| 401 | |||
| 402 | #[cfg(feature = "low-power")] | ||
| 403 | /// Resume the timer with the given offset | ||
| 404 | pub(crate) fn resume_time(offset: embassy_time::Duration) { | ||
| 405 | DRIVER.resume_time(offset); | ||
| 406 | } | ||
| 407 | |||
| 332 | pub(crate) fn init() { | 408 | pub(crate) fn init() { |
| 333 | DRIVER.init() | 409 | DRIVER.init() |
| 334 | } | 410 | } |
