From cda404731093015f84bad96675fdbfc712bc0215 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 24 Aug 2023 19:29:11 -0500 Subject: [PATCH 1/6] stm32: flesh out lp executor --- embassy-stm32/src/lib.rs | 5 +++ embassy-stm32/src/low_power.rs | 45 +++++++++++++------ embassy-stm32/src/rcc/mod.rs | 2 + embassy-stm32/src/rtc/v2.rs | 25 ++++++++--- 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 { // must be after rcc init #[cfg(feature = "_time-driver")] time_driver::init(); + + #[cfg(feature = "low-power")] + while !crate::rcc::low_power_ready() { + crate::rcc::clock_refcount_sub(); + } } 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; use crate::interrupt::typelevel::Interrupt; use crate::pac::EXTI; use crate::rcc::low_power_ready; +use crate::time_driver::{pause_time, resume_time, time_until_next_alarm}; const THREAD_PENDER: usize = usize::MAX; const THRESHOLD: Duration = Duration::from_millis(500); @@ -16,6 +17,9 @@ const THRESHOLD: Duration = Duration::from_millis(500); use crate::rtc::{Rtc, RtcInstant}; static mut RTC: Option<&'static Rtc> = None; +static mut STOP_TIME: embassy_time::Duration = Duration::from_ticks(0); +static mut NEXT_ALARM: embassy_time::Duration = Duration::from_ticks(u64::MAX); +static mut RTC_INSTANT: Option = None; foreach_interrupt! { (RTC, rtc, $block:ident, WKUP, $irq:ident) => { @@ -69,13 +73,25 @@ impl Executor { } unsafe fn on_wakeup_irq() { - info!("on wakeup irq"); + trace!("on wakeup irq"); - cortex_m::asm::bkpt(); - } + let elapsed = RTC_INSTANT.take().unwrap() - stop_wakeup_alarm(); - fn time_until_next_alarm(&self) -> Duration { - Duration::from_secs(3) + STOP_TIME += elapsed; + // let to_next = NEXT_ALARM - STOP_TIME; + let to_next = Duration::from_secs(3); + + trace!("on wakeup irq: to next: {}", to_next); + if to_next > THRESHOLD { + trace!("start wakeup alarm"); + RTC_INSTANT.replace(start_wakeup_alarm(to_next)); + + trace!("set sleeponexit"); + Self::get_scb().set_sleeponexit(); + } else { + Self::get_scb().clear_sleeponexit(); + Self::get_scb().clear_sleepdeep(); + } } fn get_scb() -> SCB { @@ -86,25 +102,28 @@ impl Executor { trace!("configure_pwr"); if !low_power_ready() { + trace!("configure_pwr: low power not ready"); return; } - let time_until_next_alarm = self.time_until_next_alarm(); + let time_until_next_alarm = time_until_next_alarm(); if time_until_next_alarm < THRESHOLD { + trace!("configure_pwr: not enough time until next alarm"); return; } - trace!("low power stop required"); + unsafe { + NEXT_ALARM = time_until_next_alarm; + RTC_INSTANT = Some(start_wakeup_alarm(time_until_next_alarm)) + }; - critical_section::with(|_| { - trace!("executor: set wakeup alarm..."); + // return; - start_wakeup_alarm(time_until_next_alarm); + pause_time(); - trace!("low power wait for rtc ready..."); + trace!("enter stop..."); - Self::get_scb().set_sleepdeep(); - }); + Self::get_scb().set_sleepdeep(); } /// 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); #[cfg(feature = "low-power")] pub fn low_power_ready() -> bool { + trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst)); + CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 } 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 { let _ = RTC::regs().dr().read(); - trace!("ssr: {}", ssr); - trace!("st: {}", st); - Self { ssr, st } } } @@ -52,7 +49,12 @@ impl core::ops::Sub for RtcInstant { let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); let rtc_ticks = self_ticks - other_ticks; - trace!("self, other, rtc ticks: {}, {}, {}", self_ticks, other_ticks, rtc_ticks); + trace!( + "rtc: instant sub: self, other, rtc ticks: {}, {}, {}", + self_ticks, + other_ticks, + rtc_ticks + ); Duration::from_ticks( ((((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 { rtc_ticks as u64 * TICK_HZ * (>::into(prescaler) as u64) / rtc_hz, ); - trace!("set wakeup timer for {} ms", duration.as_millis()); + trace!("rtc: set wakeup timer for {} ms", duration.as_millis()); self.write(false, |regs| { - regs.cr().modify(|w| w.set_wutie(true)); + // regs.cr().modify(|w| w.set_wutie(true)); regs.cr().modify(|w| w.set_wute(false)); regs.isr().modify(|w| w.set_wutf(false)); @@ -187,6 +189,15 @@ impl super::Rtc { regs.cr().modify(|w| w.set_wute(true)); }); + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wutie(false)); + + regs.isr().modify(|w| w.set_wutf(false)); + crate::pac::PWR.cr1().modify(|w| w.set_cwuf(false)); + + regs.cr().modify(|w| w.set_wutie(true)); + }); + RtcInstant::now() } @@ -197,7 +208,7 @@ impl super::Rtc { /// note: this api is exposed for testing purposes until low power is implemented. /// it is not intended to be public pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { - trace!("disable wakeup timer..."); + trace!("rtc: stop wakeup alarm..."); self.write(false, |regs| { 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 { let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; f(alarm.ctx.get()); } + + #[cfg(feature = "low-power")] + /// Compute the approximate amount of time until the next alarm + pub(crate) fn time_until_next_alarm(&self) -> embassy_time::Duration { + critical_section::with(|cs| { + let now = self.now() + 32; + + embassy_time::Duration::from_ticks( + self.alarms + .borrow(cs) + .iter() + .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) + .min() + .unwrap_or(u64::MAX), + ) + }) + } + + #[cfg(feature = "low-power")] + /// Pause the timer + pub(crate) fn pause_time(&self) { + T::regs_gp16().cr1().modify(|w| w.set_cen(false)); + } + + #[cfg(feature = "low-power")] + /// Resume the timer with the given offset + pub(crate) fn resume_time(&self, offset: embassy_time::Duration) { + let offset = offset.as_ticks(); + let cnt = T::regs_gp16().cnt().read().cnt() as u32; + let period = self.period.load(Ordering::SeqCst); + + // Correct the race, if it exists + let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { + period + 1 + } else { + period + }; + + // Normalize to the full overflow + let period = (period / 2) * 2; + + // Add the offset + let period = period + 2 * (offset / u16::MAX as u64) as u32; + let cnt = cnt + (offset % u16::MAX as u64) as u32; + + let (cnt, period) = if cnt > u16::MAX as u32 { + (cnt - u16::MAX as u32, period + 2) + } else { + (cnt, period) + }; + + let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; + + self.period.store(period, Ordering::SeqCst); + T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); + + T::regs_gp16().cr1().modify(|w| w.set_cen(true)); + } } impl Driver for RtcDriver { @@ -329,6 +387,24 @@ impl Driver for RtcDriver { } } +#[cfg(feature = "low-power")] +/// Compute the approximate amount of time until the next alarm +pub(crate) fn time_until_next_alarm() -> embassy_time::Duration { + DRIVER.time_until_next_alarm() +} + +#[cfg(feature = "low-power")] +/// Pause the timer +pub(crate) fn pause_time() { + DRIVER.pause_time(); +} + +#[cfg(feature = "low-power")] +/// Resume the timer with the given offset +pub(crate) fn resume_time(offset: embassy_time::Duration) { + DRIVER.resume_time(offset); +} + pub(crate) fn init() { DRIVER.init() } From 3023e70ccf14157c6a9c1315b987951cb6138e0d Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 25 Aug 2023 18:41:51 -0500 Subject: [PATCH 2/6] stm32: clenaup lp executor --- embassy-stm32/src/low_power.rs | 154 +++++++++++++++++++-------------- 1 file changed, 91 insertions(+), 63 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 0d9506aaa..cf12a1ea5 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -16,36 +16,30 @@ const THRESHOLD: Duration = Duration::from_millis(500); use crate::rtc::{Rtc, RtcInstant}; -static mut RTC: Option<&'static Rtc> = None; -static mut STOP_TIME: embassy_time::Duration = Duration::from_ticks(0); -static mut NEXT_ALARM: embassy_time::Duration = Duration::from_ticks(u64::MAX); -static mut RTC_INSTANT: Option = None; +static mut EXECUTOR: Option = None; foreach_interrupt! { (RTC, rtc, $block:ident, WKUP, $irq:ident) => { #[interrupt] unsafe fn $irq() { - Executor::on_wakeup_irq(); + unsafe { EXECUTOR.as_mut().unwrap() }.on_wakeup_irq(); } }; } pub fn stop_with_rtc(rtc: &'static Rtc) { - crate::interrupt::typelevel::RTC_WKUP::unpend(); - unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; - - EXTI.rtsr(0).modify(|w| w.set_line(22, true)); - EXTI.imr(0).modify(|w| w.set_line(22, true)); - - unsafe { RTC = Some(rtc) }; + unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) } pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { - unsafe { RTC }.unwrap().start_wakeup_alarm(requested_duration) + unsafe { EXECUTOR.as_mut().unwrap() } + .rtc + .unwrap() + .start_wakeup_alarm(requested_duration) } pub fn stop_wakeup_alarm() -> RtcInstant { - unsafe { RTC }.unwrap().stop_wakeup_alarm() + unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm() } /// Thread mode executor, using WFE/SEV. @@ -61,69 +55,103 @@ pub fn stop_wakeup_alarm() -> RtcInstant { pub struct Executor { inner: raw::Executor, not_send: PhantomData<*mut ()>, + scb: SCB, + pub(self) rtc: Option<&'static Rtc>, + stop_time: embassy_time::Duration, + next_alarm: embassy_time::Duration, + last_stop: Option, } impl Executor { /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(THREAD_PENDER as *mut ()), - not_send: PhantomData, + pub fn new() -> &'static mut Self { + unsafe { + assert!(EXECUTOR.is_none()); + + EXECUTOR = Some(Self { + inner: raw::Executor::new(THREAD_PENDER as *mut ()), + not_send: PhantomData, + scb: cortex_m::Peripherals::steal().SCB, + rtc: None, + stop_time: Duration::from_ticks(0), + next_alarm: Duration::from_ticks(u64::MAX), + last_stop: None, + }); + + EXECUTOR.as_mut().unwrap() } } - unsafe fn on_wakeup_irq() { - trace!("on wakeup irq"); + unsafe fn on_wakeup_irq(&mut self) { + trace!("low power: one wakekup irq"); - let elapsed = RTC_INSTANT.take().unwrap() - stop_wakeup_alarm(); - - STOP_TIME += elapsed; - // let to_next = NEXT_ALARM - STOP_TIME; - let to_next = Duration::from_secs(3); - - trace!("on wakeup irq: to next: {}", to_next); - if to_next > THRESHOLD { - trace!("start wakeup alarm"); - RTC_INSTANT.replace(start_wakeup_alarm(to_next)); - - trace!("set sleeponexit"); - Self::get_scb().set_sleeponexit(); - } else { - Self::get_scb().clear_sleeponexit(); - Self::get_scb().clear_sleepdeep(); - } + self.rtc.unwrap().clear_wakeup_alarm(); + // Self::get_scb().set_sleeponexit(); + // + // return; + // + // let elapsed = RTC_INSTANT.take().unwrap() - stop_wakeup_alarm(); + // + // STOP_TIME += elapsed; + // // let to_next = NEXT_ALARM - STOP_TIME; + // let to_next = Duration::from_secs(3); + // + // trace!("on wakeup irq: to next: {}", to_next); + // if to_next > THRESHOLD { + // trace!("start wakeup alarm"); + // RTC_INSTANT.replace(start_wakeup_alarm(to_next)); + // + // trace!("set sleeponexit"); + // Self::get_scb().set_sleeponexit(); + // } else { + // Self::get_scb().clear_sleeponexit(); + // Self::get_scb().clear_sleepdeep(); + // } } - fn get_scb() -> SCB { - unsafe { cortex_m::Peripherals::steal() }.SCB + pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { + assert!(self.rtc.is_none()); + + crate::interrupt::typelevel::RTC_WKUP::unpend(); + unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; + + EXTI.rtsr(0).modify(|w| w.set_line(22, true)); + EXTI.imr(0).modify(|w| w.set_line(22, true)); + + self.rtc = Some(rtc); } fn configure_pwr(&self) { - trace!("configure_pwr"); + // defeat the borrow checker + let s = unsafe { EXECUTOR.as_mut().unwrap() }; - if !low_power_ready() { - trace!("configure_pwr: low power not ready"); - return; - } - - let time_until_next_alarm = time_until_next_alarm(); - if time_until_next_alarm < THRESHOLD { - trace!("configure_pwr: not enough time until next alarm"); - return; - } - - unsafe { - NEXT_ALARM = time_until_next_alarm; - RTC_INSTANT = Some(start_wakeup_alarm(time_until_next_alarm)) - }; - - // return; - - pause_time(); - - trace!("enter stop..."); - - Self::get_scb().set_sleepdeep(); + // trace!("configure_pwr"); + // + // if !low_power_ready() { + // trace!("configure_pwr: low power not ready"); + // return; + // } + // + // let time_until_next_alarm = time_until_next_alarm(); + // if time_until_next_alarm < THRESHOLD { + // trace!("configure_pwr: not enough time until next alarm"); + // return; + // } + // + // unsafe { + // NEXT_ALARM = time_until_next_alarm; + // if RTC_INSTANT.is_none() { + // RTC_INSTANT = Some(start_wakeup_alarm(time_until_next_alarm)) + // } + // }; + // + // // return; + // + // pause_time(); + // + // trace!("enter stop..."); + // + // Self::get_scb().set_sleepdeep(); } /// Run the executor. From 2897670f2438525bc128cf016ee8f8a948edfbb7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 26 Aug 2023 19:23:25 -0500 Subject: [PATCH 3/6] stm32: get the basic lp working --- embassy-stm32/src/low_power.rs | 126 +++++++++++++++++-------------- embassy-stm32/src/rtc/v2.rs | 55 +++++++++++--- embassy-stm32/src/time_driver.rs | 107 ++++++++++++++++++++------ 3 files changed, 196 insertions(+), 92 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index cf12a1ea5..964819abb 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -9,12 +9,11 @@ use crate::interrupt; use crate::interrupt::typelevel::Interrupt; use crate::pac::EXTI; use crate::rcc::low_power_ready; -use crate::time_driver::{pause_time, resume_time, time_until_next_alarm}; +use crate::time_driver::{get_driver, RtcDriver}; const THREAD_PENDER: usize = usize::MAX; -const THRESHOLD: Duration = Duration::from_millis(500); -use crate::rtc::{Rtc, RtcInstant}; +use crate::rtc::Rtc; static mut EXECUTOR: Option = None; @@ -27,20 +26,30 @@ foreach_interrupt! { }; } +// pub fn timer_driver_pause_time() { +// pause_time(); +// } + pub fn stop_with_rtc(rtc: &'static Rtc) { unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) } -pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { - unsafe { EXECUTOR.as_mut().unwrap() } - .rtc - .unwrap() - .start_wakeup_alarm(requested_duration) -} - -pub fn stop_wakeup_alarm() -> RtcInstant { - unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm() -} +// pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) { +// let rtc_instant = unsafe { EXECUTOR.as_mut().unwrap() } +// .rtc +// .unwrap() +// .start_wakeup_alarm(requested_duration); +// +// unsafe { EXECUTOR.as_mut().unwrap() }.last_stop = Some(rtc_instant); +// } +// +// pub fn set_sleepdeep() { +// unsafe { EXECUTOR.as_mut().unwrap() }.scb.set_sleepdeep(); +// } +// +// pub fn stop_wakeup_alarm() -> RtcInstant { +// unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm() +// } /// Thread mode executor, using WFE/SEV. /// @@ -56,15 +65,15 @@ pub struct Executor { inner: raw::Executor, not_send: PhantomData<*mut ()>, scb: SCB, - pub(self) rtc: Option<&'static Rtc>, + time_driver: &'static RtcDriver, stop_time: embassy_time::Duration, next_alarm: embassy_time::Duration, - last_stop: Option, + wfe: u8, } impl Executor { /// Create a new Executor. - pub fn new() -> &'static mut Self { + pub fn take() -> &'static mut Self { unsafe { assert!(EXECUTOR.is_none()); @@ -72,10 +81,10 @@ impl Executor { inner: raw::Executor::new(THREAD_PENDER as *mut ()), not_send: PhantomData, scb: cortex_m::Peripherals::steal().SCB, - rtc: None, + time_driver: get_driver(), stop_time: Duration::from_ticks(0), next_alarm: Duration::from_ticks(u64::MAX), - last_stop: None, + wfe: 0, }); EXECUTOR.as_mut().unwrap() @@ -83,9 +92,31 @@ impl Executor { } unsafe fn on_wakeup_irq(&mut self) { - trace!("low power: one wakekup irq"); + trace!("low power: on wakeup irq"); - self.rtc.unwrap().clear_wakeup_alarm(); + if crate::pac::RTC.isr().read().wutf() { + trace!("low power: wutf set"); + } else { + trace!("low power: wutf not set"); + } + + self.time_driver.resume_time(); + trace!("low power: resume time"); + + crate::interrupt::typelevel::RTC_WKUP::disable(); + + // cortex_m::asm::bkpt(); + + // let time_elasped = self.rtc.unwrap().stop_wakeup_alarm() - self.last_stop.take().unwrap(); + // + // trace!("low power: {} ms elapsed", time_elasped.as_millis()); + // + // resume_time(time_elasped); + // trace!("low power: resume time"); + // + // self.scb.clear_sleepdeep(); + + // cortex_m::asm::bkpt(); // Self::get_scb().set_sleeponexit(); // // return; @@ -110,48 +141,33 @@ impl Executor { } pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { - assert!(self.rtc.is_none()); + trace!("low power: stop with rtc configured"); + + self.time_driver.set_rtc(rtc); crate::interrupt::typelevel::RTC_WKUP::unpend(); unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; EXTI.rtsr(0).modify(|w| w.set_line(22, true)); EXTI.imr(0).modify(|w| w.set_line(22, true)); - - self.rtc = Some(rtc); } - fn configure_pwr(&self) { - // defeat the borrow checker - let s = unsafe { EXECUTOR.as_mut().unwrap() }; + fn configure_pwr(&mut self) { + trace!("low power: configure_pwr"); - // trace!("configure_pwr"); - // - // if !low_power_ready() { - // trace!("configure_pwr: low power not ready"); - // return; - // } - // - // let time_until_next_alarm = time_until_next_alarm(); - // if time_until_next_alarm < THRESHOLD { - // trace!("configure_pwr: not enough time until next alarm"); - // return; - // } - // - // unsafe { - // NEXT_ALARM = time_until_next_alarm; - // if RTC_INSTANT.is_none() { - // RTC_INSTANT = Some(start_wakeup_alarm(time_until_next_alarm)) - // } - // }; - // - // // return; - // - // pause_time(); - // - // trace!("enter stop..."); - // - // Self::get_scb().set_sleepdeep(); + self.scb.clear_sleepdeep(); + if !low_power_ready() { + trace!("low power: configure_pwr: low power not ready"); + return; + } + + if self.time_driver.pause_time().is_err() { + trace!("low power: configure_pwr: time driver failed to pause"); + return; + } + + trace!("low power: enter stop..."); + self.scb.set_sleepdeep(); } /// Run the executor. @@ -173,11 +189,11 @@ impl Executor { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner()); loop { unsafe { - self.inner.poll(); + EXECUTOR.as_mut().unwrap().inner.poll(); self.configure_pwr(); asm!("wfe"); }; diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 197c3b8f9..525dc45a2 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -30,6 +30,8 @@ impl RtcInstant { let _ = RTC::regs().dr().read(); + trace!("rtc: instant now: st, ssr: {}, {}", st, ssr); + Self { ssr, st } } } @@ -146,6 +148,21 @@ impl super::Rtc { } } + // pub(crate) fn clear_wakeup_alarm(&self) { + // use crate::interrupt::typelevel::Interrupt; + // + // self.write(false, |regs| { + // regs.cr().modify(|w| w.set_wutie(false)); + // + // regs.isr().modify(|w| w.set_wutf(false)); + // crate::pac::PWR.cr1().modify(|w| w.set_cwuf(false)); + // crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, false)); + // crate::interrupt::typelevel::RTC_WKUP::unpend(); + // + // regs.cr().modify(|w| w.set_wutie(true)); + // }); + // } + #[allow(dead_code)] #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] /// start the wakeup alarm and return the actual duration of the alarm @@ -157,6 +174,7 @@ impl super::Rtc { pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant { use embassy_time::{Duration, TICK_HZ}; + use crate::interrupt::typelevel::Interrupt; use crate::rcc::get_freqs; let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; @@ -176,28 +194,40 @@ impl super::Rtc { rtc_ticks as u64 * TICK_HZ * (>::into(prescaler) as u64) / rtc_hz, ); - trace!("rtc: set wakeup timer for {} ms", duration.as_millis()); - self.write(false, |regs| { - // regs.cr().modify(|w| w.set_wutie(true)); - regs.cr().modify(|w| w.set_wute(false)); regs.isr().modify(|w| w.set_wutf(false)); while !regs.isr().read().wutwf() {} regs.cr().modify(|w| w.set_wucksel(prescaler.into())); regs.cr().modify(|w| w.set_wute(true)); - }); - - self.write(false, |regs| { - regs.cr().modify(|w| w.set_wutie(false)); - - regs.isr().modify(|w| w.set_wutf(false)); - crate::pac::PWR.cr1().modify(|w| w.set_cwuf(false)); - regs.cr().modify(|w| w.set_wutie(true)); }); + trace!("rtc: start wakeup alarm for {} ms", duration.as_millis()); + + // self.write(false, |regs| { + // regs.cr().modify(|w| w.set_wutie(false)); + // + // regs.isr().modify(|w| w.set_wutf(false)); + // + // regs.cr().modify(|w| w.set_wutie(true)); + // }); + // + // trace!("rtc: clear wuf..."); + // crate::pac::PWR.cr1().modify(|w| w.set_cwuf(true)); + // while crate::pac::PWR.csr1().read().wuf() {} + // trace!("rtc: clear wuf...done"); + // + // crate::pac::PWR + // .cr1() + // .modify(|w| w.set_pdds(crate::pac::pwr::vals::Pdds::STANDBY_MODE)); + // + // // crate::pac::PWR.cr1().modify(|w| w.set_lpds(true)); + // + // // crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, true)); + // crate::interrupt::typelevel::RTC_WKUP::unpend(); + RtcInstant::now() } @@ -211,6 +241,7 @@ impl super::Rtc { trace!("rtc: stop wakeup alarm..."); self.write(false, |regs| { + regs.cr().modify(|w| w.set_wutie(false)); regs.cr().modify(|w| w.set_wute(false)); regs.isr().modify(|w| w.set_wutf(false)); }); diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 8e05346ad..56f42aa96 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -14,6 +14,8 @@ use stm32_metapac::timer::regs; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; use crate::rcc::sealed::RccPeripheral; +#[cfg(feature = "low-power")] +use crate::rtc::{Rtc, RtcInstant}; use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance}; use crate::{interrupt, peripherals}; @@ -130,12 +132,16 @@ impl AlarmState { } } -struct RtcDriver { +pub(crate) struct RtcDriver { /// Number of 2^15 periods elapsed since boot. period: AtomicU32, alarm_count: AtomicU8, /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. alarms: Mutex, + #[cfg(feature = "low-power")] + rtc: Mutex>>, + #[cfg(feature = "low-power")] + stop_time: Mutex>>, } const ALARM_STATE_NEW: AlarmState = AlarmState::new(); @@ -144,6 +150,10 @@ embassy_time::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), alarm_count: AtomicU8::new(0), alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), + #[cfg(feature = "low-power")] + rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + #[cfg(feature = "low-power")] + stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), }); impl RtcDriver { @@ -260,9 +270,15 @@ impl RtcDriver { f(alarm.ctx.get()); } + #[cfg(feature = "low-power")] + /// Set the rtc but panic if it's already been set + pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { + critical_section::with(|cs| assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none())); + } + #[cfg(feature = "low-power")] /// Compute the approximate amount of time until the next alarm - pub(crate) fn time_until_next_alarm(&self) -> embassy_time::Duration { + fn time_until_next_alarm(&self) -> embassy_time::Duration { critical_section::with(|cs| { let now = self.now() + 32; @@ -278,14 +294,8 @@ impl RtcDriver { } #[cfg(feature = "low-power")] - /// Pause the timer - pub(crate) fn pause_time(&self) { - T::regs_gp16().cr1().modify(|w| w.set_cen(false)); - } - - #[cfg(feature = "low-power")] - /// Resume the timer with the given offset - pub(crate) fn resume_time(&self, offset: embassy_time::Duration) { + /// Add the given offset to the current time + fn add_time(&self, offset: embassy_time::Duration) { let offset = offset.as_ticks(); let cnt = T::regs_gp16().cnt().read().cnt() as u32; let period = self.period.load(Ordering::SeqCst); @@ -315,6 +325,66 @@ impl RtcDriver { self.period.store(period, Ordering::SeqCst); T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); + // Now, recompute all alarms + critical_section::with(|cs| { + for i in 0..ALARM_COUNT { + let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; + let alarm = self.get_alarm(cs, alarm_handle); + + self.set_alarm(alarm_handle, alarm.timestamp.get()); + } + }) + } + + #[cfg(feature = "low-power")] + /// Stop the wakeup alarm, if enabled, and add the appropriate offset + fn stop_wakeup_alarm(&self) { + critical_section::with(|cs| { + if let Some(stop_time) = self.stop_time.borrow(cs).take() { + let current_time = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(); + self.add_time(current_time - stop_time); + } + }); + } + + #[cfg(feature = "low-power")] + /// Pause the timer if ready; return err if not + pub(crate) fn pause_time(&self) -> Result<(), ()> { + /* + If the wakeup timer is currently running, then we need to stop it and + add the elapsed time to the current time + */ + self.stop_wakeup_alarm(); + + let time_until_next_alarm = self.time_until_next_alarm(); + if time_until_next_alarm < embassy_time::Duration::from_millis(250) { + Err(()) + } else { + critical_section::with(|cs| { + assert!(self + .stop_time + .borrow(cs) + .replace(Some( + self.rtc + .borrow(cs) + .get() + .unwrap() + .start_wakeup_alarm(time_until_next_alarm) + )) + .is_none()); + }); + + T::regs_gp16().cr1().modify(|w| w.set_cen(false)); + + Ok(()) + } + } + + #[cfg(feature = "low-power")] + /// Resume the timer with the given offset + pub(crate) fn resume_time(&self) { + self.stop_wakeup_alarm(); + T::regs_gp16().cr1().modify(|w| w.set_cen(true)); } } @@ -388,21 +458,8 @@ impl Driver for RtcDriver { } #[cfg(feature = "low-power")] -/// Compute the approximate amount of time until the next alarm -pub(crate) fn time_until_next_alarm() -> embassy_time::Duration { - DRIVER.time_until_next_alarm() -} - -#[cfg(feature = "low-power")] -/// Pause the timer -pub(crate) fn pause_time() { - DRIVER.pause_time(); -} - -#[cfg(feature = "low-power")] -/// Resume the timer with the given offset -pub(crate) fn resume_time(offset: embassy_time::Duration) { - DRIVER.resume_time(offset); +pub(crate) fn get_driver() -> &'static RtcDriver { + &DRIVER } pub(crate) fn init() { From 1e430f74133acaeb31cb689ded3da77cb7b1c0ca Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 26 Aug 2023 20:31:12 -0500 Subject: [PATCH 4/6] stm32: complete stop impl. --- embassy-stm32/src/low_power.rs | 48 ------------- embassy-stm32/src/rtc/mod.rs | 79 ++++++++++++++++++++ embassy-stm32/src/rtc/v2.rs | 119 ++++++------------------------- embassy-stm32/src/time_driver.rs | 25 ++----- 4 files changed, 107 insertions(+), 164 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 964819abb..425362162 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -66,9 +66,6 @@ pub struct Executor { not_send: PhantomData<*mut ()>, scb: SCB, time_driver: &'static RtcDriver, - stop_time: embassy_time::Duration, - next_alarm: embassy_time::Duration, - wfe: u8, } impl Executor { @@ -82,9 +79,6 @@ impl Executor { not_send: PhantomData, scb: cortex_m::Peripherals::steal().SCB, time_driver: get_driver(), - stop_time: Duration::from_ticks(0), - next_alarm: Duration::from_ticks(u64::MAX), - wfe: 0, }); EXECUTOR.as_mut().unwrap() @@ -94,50 +88,8 @@ impl Executor { unsafe fn on_wakeup_irq(&mut self) { trace!("low power: on wakeup irq"); - if crate::pac::RTC.isr().read().wutf() { - trace!("low power: wutf set"); - } else { - trace!("low power: wutf not set"); - } - self.time_driver.resume_time(); trace!("low power: resume time"); - - crate::interrupt::typelevel::RTC_WKUP::disable(); - - // cortex_m::asm::bkpt(); - - // let time_elasped = self.rtc.unwrap().stop_wakeup_alarm() - self.last_stop.take().unwrap(); - // - // trace!("low power: {} ms elapsed", time_elasped.as_millis()); - // - // resume_time(time_elasped); - // trace!("low power: resume time"); - // - // self.scb.clear_sleepdeep(); - - // cortex_m::asm::bkpt(); - // Self::get_scb().set_sleeponexit(); - // - // return; - // - // let elapsed = RTC_INSTANT.take().unwrap() - stop_wakeup_alarm(); - // - // STOP_TIME += elapsed; - // // let to_next = NEXT_ALARM - STOP_TIME; - // let to_next = Duration::from_secs(3); - // - // trace!("on wakeup irq: to next: {}", to_next); - // if to_next > THRESHOLD { - // trace!("start wakeup alarm"); - // RTC_INSTANT.replace(start_wakeup_alarm(to_next)); - // - // trace!("set sleeponexit"); - // Self::get_scb().set_sleeponexit(); - // } else { - // Self::get_scb().clear_sleeponexit(); - // Self::get_scb().clear_sleepdeep(); - // } } pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index a6102077a..1f1abb78d 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -1,6 +1,14 @@ //! RTC peripheral abstraction mod datetime; +#[cfg(feature = "low-power")] +use core::cell::Cell; + +#[cfg(feature = "low-power")] +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +#[cfg(feature = "low-power")] +use embassy_sync::blocking_mutex::Mutex; + pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; /// refer to AN4759 to compare features of RTC2 and RTC3 @@ -30,9 +38,73 @@ pub enum RtcError { NotRunning, } +#[cfg(feature = "low-power")] +/// Represents an instant in time that can be substracted to compute a duration +struct RtcInstant { + second: u8, + subsecond: u16, +} + +#[cfg(feature = "low-power")] +impl RtcInstant { + pub fn now() -> Self { + let tr = RTC::regs().tr().read(); + let tr2 = RTC::regs().tr().read(); + let ssr = RTC::regs().ssr().read().ss(); + let ssr2 = RTC::regs().ssr().read().ss(); + + let st = bcd2_to_byte((tr.st(), tr.su())); + let st2 = bcd2_to_byte((tr2.st(), tr2.su())); + + assert!(st == st2); + assert!(ssr == ssr2); + + let _ = RTC::regs().dr().read(); + + let subsecond = ssr; + let second = st; + + // trace!("rtc: instant now: st, ssr: {}, {}", st, ssr); + + Self { second, subsecond } + } +} + +#[cfg(feature = "low-power")] +impl core::ops::Sub for RtcInstant { + type Output = embassy_time::Duration; + + fn sub(self, rhs: Self) -> Self::Output { + use embassy_time::{Duration, TICK_HZ}; + + let second = if self.second < rhs.second { + self.second + 60 + } else { + self.second + }; + + // TODO: read prescaler + + let self_ticks = second as u32 * 256 + (255 - self.subsecond as u32); + let other_ticks = rhs.second as u32 * 256 + (255 - rhs.subsecond as u32); + let rtc_ticks = self_ticks - other_ticks; + + // trace!( + // "rtc: instant sub: self, other, rtc ticks: {}, {}, {}", + // self_ticks, + // other_ticks, + // rtc_ticks + // ); + + Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / 256u32) as u64) + } +} + /// RTC Abstraction pub struct Rtc { rtc_config: RtcConfig, + #[cfg(feature = "low-power")] + stop_time: Mutex>>, } #[derive(Copy, Clone, Debug, PartialEq)] @@ -108,8 +180,15 @@ impl Rtc { pub fn new(_rtc: impl Peripheral

, rtc_config: RtcConfig) -> Self { RTC::enable_peripheral_clk(); + #[cfg(not(feature = "low-power"))] let mut rtc_struct = Self { rtc_config }; + #[cfg(feature = "low-power")] + let mut rtc_struct = Self { + rtc_config, + stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + }; + Self::enable(); rtc_struct.configure(rtc_config); diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 525dc45a2..d73e7083c 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,71 +1,12 @@ use stm32_metapac::rtc::vals::{Init, Osel, Pol}; +#[cfg(feature = "low-power")] +use super::RtcInstant; use super::{sealed, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; use crate::peripherals::RTC; use crate::rtc::sealed::Instance; -#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] -pub struct RtcInstant { - ssr: u16, - st: u8, -} - -#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] -impl RtcInstant { - pub fn now() -> Self { - // TODO: read value twice - use crate::rtc::bcd2_to_byte; - - let tr = RTC::regs().tr().read(); - let tr2 = RTC::regs().tr().read(); - let ssr = RTC::regs().ssr().read().ss(); - let ssr2 = RTC::regs().ssr().read().ss(); - - let st = bcd2_to_byte((tr.st(), tr.su())); - let st2 = bcd2_to_byte((tr2.st(), tr2.su())); - - assert!(st == st2); - assert!(ssr == ssr2); - - let _ = RTC::regs().dr().read(); - - trace!("rtc: instant now: st, ssr: {}, {}", st, ssr); - - Self { ssr, st } - } -} - -#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] -impl core::ops::Sub for RtcInstant { - type Output = embassy_time::Duration; - - fn sub(self, rhs: Self) -> Self::Output { - use embassy_time::{Duration, TICK_HZ}; - - let st = if self.st < rhs.st { self.st + 60 } else { self.st }; - - // TODO: read prescaler - - let self_ticks = st as u32 * 256 + (255 - self.ssr as u32); - let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); - let rtc_ticks = self_ticks - other_ticks; - - trace!( - "rtc: instant sub: self, other, rtc ticks: {}, {}, {}", - self_ticks, - other_ticks, - rtc_ticks - ); - - Duration::from_ticks( - ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) - * TICK_HZ as u32) as u32 - / 256u32) as u64, - ) - } -} - #[allow(dead_code)] #[derive(Clone, Copy, Debug)] pub(crate) enum WakeupPrescaler { @@ -165,16 +106,11 @@ impl super::Rtc { #[allow(dead_code)] #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] - /// start the wakeup alarm and return the actual duration of the alarm - /// the actual duration will be the closest value possible that is less - /// than the requested duration. - /// - /// note: this api is exposed for testing purposes until low power is implemented. - /// it is not intended to be public - pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant { + /// start the wakeup alarm and wtih a duration that is as close to but less than + /// the requested duration, and record the instant the wakeup alarm was started + pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) { use embassy_time::{Duration, TICK_HZ}; - use crate::interrupt::typelevel::Interrupt; use crate::rcc::get_freqs; let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; @@ -206,47 +142,34 @@ impl super::Rtc { trace!("rtc: start wakeup alarm for {} ms", duration.as_millis()); - // self.write(false, |regs| { - // regs.cr().modify(|w| w.set_wutie(false)); - // - // regs.isr().modify(|w| w.set_wutf(false)); - // - // regs.cr().modify(|w| w.set_wutie(true)); - // }); - // - // trace!("rtc: clear wuf..."); - // crate::pac::PWR.cr1().modify(|w| w.set_cwuf(true)); - // while crate::pac::PWR.csr1().read().wuf() {} - // trace!("rtc: clear wuf...done"); - // - // crate::pac::PWR - // .cr1() - // .modify(|w| w.set_pdds(crate::pac::pwr::vals::Pdds::STANDBY_MODE)); - // - // // crate::pac::PWR.cr1().modify(|w| w.set_lpds(true)); - // - // // crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, true)); - // crate::interrupt::typelevel::RTC_WKUP::unpend(); - - RtcInstant::now() + critical_section::with(|cs| assert!(self.stop_time.borrow(cs).replace(Some(RtcInstant::now())).is_none())) } #[allow(dead_code)] #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] - /// stop the wakeup alarm and return the time remaining - /// - /// note: this api is exposed for testing purposes until low power is implemented. - /// it is not intended to be public - pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { + /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` + /// was called, otherwise none + pub(crate) fn stop_wakeup_alarm(&self) -> Option { + use crate::interrupt::typelevel::Interrupt; + trace!("rtc: stop wakeup alarm..."); self.write(false, |regs| { regs.cr().modify(|w| w.set_wutie(false)); regs.cr().modify(|w| w.set_wute(false)); regs.isr().modify(|w| w.set_wutf(false)); + + crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, true)); + crate::interrupt::typelevel::RTC_WKUP::unpend(); }); - RtcInstant::now() + critical_section::with(|cs| { + if let Some(stop_time) = self.stop_time.borrow(cs).take() { + Some(RtcInstant::now() - stop_time) + } else { + None + } + }) } #[allow(dead_code)] diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 56f42aa96..99d423d08 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -15,7 +15,7 @@ use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; use crate::rcc::sealed::RccPeripheral; #[cfg(feature = "low-power")] -use crate::rtc::{Rtc, RtcInstant}; +use crate::rtc::Rtc; use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance}; use crate::{interrupt, peripherals}; @@ -140,8 +140,6 @@ pub(crate) struct RtcDriver { alarms: Mutex, #[cfg(feature = "low-power")] rtc: Mutex>>, - #[cfg(feature = "low-power")] - stop_time: Mutex>>, } const ALARM_STATE_NEW: AlarmState = AlarmState::new(); @@ -152,8 +150,6 @@ embassy_time::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), #[cfg(feature = "low-power")] rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), - #[cfg(feature = "low-power")] - stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), }); impl RtcDriver { @@ -340,9 +336,8 @@ impl RtcDriver { /// Stop the wakeup alarm, if enabled, and add the appropriate offset fn stop_wakeup_alarm(&self) { critical_section::with(|cs| { - if let Some(stop_time) = self.stop_time.borrow(cs).take() { - let current_time = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(); - self.add_time(current_time - stop_time); + if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm() { + self.add_time(offset); } }); } @@ -361,17 +356,11 @@ impl RtcDriver { Err(()) } else { critical_section::with(|cs| { - assert!(self - .stop_time + self.rtc .borrow(cs) - .replace(Some( - self.rtc - .borrow(cs) - .get() - .unwrap() - .start_wakeup_alarm(time_until_next_alarm) - )) - .is_none()); + .get() + .unwrap() + .start_wakeup_alarm(time_until_next_alarm); }); T::regs_gp16().cr1().modify(|w| w.set_cen(false)); From db71887817e665c5c226e8775ad2d5814babac6e Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 26 Aug 2023 20:37:01 -0500 Subject: [PATCH 5/6] tests/stm32: add stop and cleanpu --- embassy-stm32/src/low_power.rs | 1 - tests/stm32/Cargo.toml | 9 +++++- tests/stm32/src/bin/stop.rs | 53 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/stm32/src/bin/stop.rs diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 425362162..65b93f8a4 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -3,7 +3,6 @@ use core::marker::PhantomData; use cortex_m::peripheral::SCB; use embassy_executor::*; -use embassy_time::Duration; use crate::interrupt; use crate::interrupt::typelevel::Interrupt; diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 754356cb5..1f8c7373c 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,7 +7,7 @@ autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "can", "not-gpdma", "dac-adc-pin"] # Nucleo "sdmmc" +stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "stop", "can", "not-gpdma", "dac-adc-pin"] # Nucleo "sdmmc" stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma", "dac-adc-pin"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo @@ -17,6 +17,7 @@ stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] +stop = ["embassy-stm32/low-power"] chrono = ["embassy-stm32/chrono", "dep:chrono"] can = [] ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] @@ -47,6 +48,7 @@ micromath = "2.0.0" panic-probe = { version = "0.3.0", features = ["print-defmt"] } rand_core = { version = "0.6", default-features = false } rand_chacha = { version = "0.3", default-features = false } +static_cell = {version = "1.1", features = ["nightly"] } chrono = { version = "^0.4", default-features = false, optional = true} @@ -87,6 +89,11 @@ name = "spi_dma" path = "src/bin/spi_dma.rs" required-features = [] +[[bin]] +name = "stop" +path = "src/bin/stop.rs" +required-features = [ "stop", "chrono",] + [[bin]] name = "timer" path = "src/bin/timer.rs" diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs new file mode 100644 index 000000000..4a49bde9d --- /dev/null +++ b/tests/stm32/src/bin/stop.rs @@ -0,0 +1,53 @@ +// required-features: stop,chrono + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use chrono::NaiveDate; +use common::*; +use cortex_m_rt::entry; +use embassy_executor::Spawner; +use embassy_stm32::low_power::{stop_with_rtc, Executor}; +use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig}; +use embassy_time::{Duration, Timer}; +use static_cell::make_static; + +#[entry] +fn main() -> ! { + let executor = Executor::take(); + executor.run(|spawner| { + unwrap!(spawner.spawn(async_main(spawner))); + }); +} + +#[embassy_executor::task] +async fn async_main(_spawner: Spawner) { + let mut config = config(); + + config.rcc.rtc = Some(RtcClockSource::LSI); + + let p = embassy_stm32::init(config); + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + let rtc = make_static!(rtc); + + stop_with_rtc(rtc); + + info!("Waiting 5 seconds"); + Timer::after(Duration::from_secs(5)).await; + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 94de1a53530d58cf6db38dffcf434ba19a576d40 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 26 Aug 2023 20:40:21 -0500 Subject: [PATCH 6/6] stm32: feature-gate wakeup alarm --- embassy-stm32/src/rtc/v2.rs | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index d73e7083c..bf926f986 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -89,23 +89,7 @@ impl super::Rtc { } } - // pub(crate) fn clear_wakeup_alarm(&self) { - // use crate::interrupt::typelevel::Interrupt; - // - // self.write(false, |regs| { - // regs.cr().modify(|w| w.set_wutie(false)); - // - // regs.isr().modify(|w| w.set_wutf(false)); - // crate::pac::PWR.cr1().modify(|w| w.set_cwuf(false)); - // crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, false)); - // crate::interrupt::typelevel::RTC_WKUP::unpend(); - // - // regs.cr().modify(|w| w.set_wutie(true)); - // }); - // } - - #[allow(dead_code)] - #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] + #[cfg(feature = "low-power")] /// start the wakeup alarm and wtih a duration that is as close to but less than /// the requested duration, and record the instant the wakeup alarm was started pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) { @@ -145,8 +129,7 @@ impl super::Rtc { critical_section::with(|cs| assert!(self.stop_time.borrow(cs).replace(Some(RtcInstant::now())).is_none())) } - #[allow(dead_code)] - #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] + #[cfg(feature = "low-power")] /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` /// was called, otherwise none pub(crate) fn stop_wakeup_alarm(&self) -> Option {