From a886e97a33690cf9724dedd272d3073a577f9fa4 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 18 Dec 2025 09:13:05 -0600 Subject: stm32: use datemath to resume time --- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/rtc/low_power.rs | 94 ++++++-------------------------------- embassy-stm32/src/rtc/mod.rs | 16 +++++-- embassy-stm32/src/time_driver.rs | 23 ++++++---- 4 files changed, 43 insertions(+), 92 deletions(-) (limited to 'embassy-stm32') diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index e96933b78..66a731e5f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -241,7 +241,7 @@ log = ["dep:log"] chrono = ["dep:chrono"] exti = [] -low-power = [ "dep:embassy-executor", "time" ] +low-power = [ "dep:embassy-executor", "time", "chrono"] low-power-pender = [ ] low-power-debug-with-sleep = [] diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index f049d6b12..cd5cea081 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -1,64 +1,12 @@ -#[cfg(feature = "time")] -use embassy_time::{Duration, TICK_HZ}; +use chrono::{DateTime, NaiveDateTime, TimeDelta, Utc}; +use embassy_time::{Duration, Instant, TICK_HZ}; -use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; +use super::Rtc; use crate::interrupt::typelevel::Interrupt; use crate::pac::rtc::vals::Wucksel; use crate::peripherals::RTC; use crate::rtc::{RtcTimeProvider, SealedInstance}; -/// Represents an instant in time that can be substracted to compute a duration -pub(super) struct RtcInstant { - /// 0..59 - second: u8, - /// 0..256 - subsecond: u16, -} - -impl RtcInstant { - #[cfg(not(rtc_v2_f2))] - const fn from(second: u8, subsecond: u16) -> Result { - if second > 59 { - Err(DateTimeError::InvalidSecond) - } else { - Ok(Self { second, subsecond }) - } - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for RtcInstant { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!( - fmt, - "{}:{}", - self.second, - RTC::regs().prer().read().prediv_s() - self.subsecond, - ) - } -} - -#[cfg(feature = "time")] -impl core::ops::Sub for RtcInstant { - type Output = embassy_time::Duration; - - fn sub(self, rhs: Self) -> Self::Output { - let second = if self.second < rhs.second { - self.second + 60 - } else { - self.second - }; - - let psc = RTC::regs().prer().read().prediv_s() as u32; - - let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32); - let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32); - let rtc_ticks = self_ticks - other_ticks; - - Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64) - } -} - fn wucksel_compute_min(val: u32) -> (Wucksel, u32) { *[ (Wucksel::DIV2, 2), @@ -72,22 +20,15 @@ fn wucksel_compute_min(val: u32) -> (Wucksel, u32) { } impl Rtc { - /// Return the current instant. - fn instant(&self) -> Result { - RtcTimeProvider::new().read(|_, tr, ss| { - let second = bcd2_to_byte((tr.st(), tr.su())); + pub(super) fn calc_epoch(&self) -> DateTime { + let now: NaiveDateTime = RtcTimeProvider::new().now().unwrap().into(); - RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) - }) + now.and_utc() - TimeDelta::microseconds(Instant::now().as_micros().try_into().unwrap()) } /// start the wakeup alarm and with 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( - &mut self, - requested_duration: embassy_time::Duration, - cs: critical_section::CriticalSection, - ) { + pub(crate) fn start_wakeup_alarm(&mut self, requested_duration: embassy_time::Duration) { // Panic if the rcc mod knows we're not using low-power rtc #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap(); @@ -122,27 +63,19 @@ impl Rtc { regs.cr().modify(|w| w.set_wutie(true)); }); - let instant = self.instant().unwrap(); trace!( - "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", + "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {})", Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), prescaler as u32, rtc_ticks, - instant, ); - - assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none()) } /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` /// was called, otherwise none - pub(crate) fn stop_wakeup_alarm( - &mut self, - cs: critical_section::CriticalSection, - ) -> Option { - let instant = self.instant().unwrap(); + pub(crate) fn stop_wakeup_alarm(&mut self) -> embassy_time::Instant { if RTC::regs().cr().read().wute() { - trace!("rtc: stop wakeup alarm at {}", instant); + trace!("rtc: stop wakeup alarm"); self.write(false, |regs| { regs.cr().modify(|w| w.set_wutie(false)); @@ -166,10 +99,13 @@ impl Rtc { }); } - self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time) + let datetime: NaiveDateTime = RtcTimeProvider::new().now().expect("failed to read now").into(); + let offset = datetime.and_utc() - self.epoch; + + Instant::from_micros(offset.num_microseconds().unwrap().try_into().unwrap()) } - pub(crate) fn enable_wakeup_line(&self) { + pub(super) fn enable_wakeup_line(&mut self) { ::WakeupInterrupt::unpend(); unsafe { ::WakeupInterrupt::enable() }; diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index e88bd7ab2..94ba13ae1 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -5,7 +5,7 @@ mod datetime; mod low_power; #[cfg(feature = "low-power")] -use core::cell::{Cell, RefCell, RefMut}; +use core::cell::{RefCell, RefMut}; #[cfg(feature = "low-power")] use core::ops; @@ -163,7 +163,7 @@ impl<'a> ops::DerefMut for RtcBorrow<'a> { /// RTC driver. pub struct Rtc { #[cfg(feature = "low-power")] - stop_time: Mutex>>, + epoch: chrono::DateTime, _private: (), } @@ -225,7 +225,7 @@ impl Rtc { let mut this = Self { #[cfg(feature = "low-power")] - stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + epoch: chrono::DateTime::from_timestamp_secs(0).unwrap(), _private: (), }; @@ -243,7 +243,10 @@ impl Rtc { } #[cfg(feature = "low-power")] - this.enable_wakeup_line(); + { + this.enable_wakeup_line(); + this.epoch = this.calc_epoch(); + } this } @@ -293,6 +296,11 @@ impl Rtc { }); }); + #[cfg(feature = "low-power")] + { + self.epoch = self.calc_epoch(); + } + Ok(()) } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index ed5d902bd..59ec58575 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -251,8 +251,8 @@ impl RtcDriver { #[cfg(feature = "low-power")] /// Add the given offset to the current time - fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { - let (period, counter) = calc_period_counter(self.now() + offset.as_ticks()); + fn set_time(&self, instant: u64, cs: CriticalSection) { + let (period, counter) = calc_period_counter(core::cmp::max(self.now(), instant)); self.period.store(period, Ordering::SeqCst); regs_gp16().cnt().write(|w| w.set_cnt(counter)); @@ -269,10 +269,17 @@ impl RtcDriver { #[cfg(feature = "low-power")] /// Stop the wakeup alarm, if enabled, and add the appropriate offset fn stop_wakeup_alarm(&self, cs: CriticalSection) { - if !regs_gp16().cr1().read().cen() - && let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) - { - self.add_time(offset, cs); + if !regs_gp16().cr1().read().cen() { + self.set_time( + self.rtc + .borrow(cs) + .borrow_mut() + .as_mut() + .unwrap() + .stop_wakeup_alarm() + .as_ticks(), + cs, + ); } } @@ -287,7 +294,7 @@ impl RtcDriver { #[cfg(feature = "low-power")] /// Set the rtc but panic if it's already been set pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) { - rtc.stop_wakeup_alarm(cs); + rtc.stop_wakeup_alarm(); assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()); } @@ -310,7 +317,7 @@ impl RtcDriver { .borrow_mut() .as_mut() .unwrap() - .start_wakeup_alarm(time_until_next_alarm, cs); + .start_wakeup_alarm(time_until_next_alarm); regs_gp16().cr1().modify(|w| w.set_cen(false)); // save the count for the timer as its lost in STOP2 for stm32wlex -- cgit