From b9559c7713bc7f773cdef0df14f1158840d06d06 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 4 Nov 2025 16:35:07 -0600 Subject: rtc: use consistent api between stop and non-stop --- embassy-stm32/src/low_power.rs | 7 ---- embassy-stm32/src/rtc/low_power.rs | 4 +- embassy-stm32/src/rtc/mod.rs | 79 ++++++++++++++++++++++++++++---------- embassy-stm32/src/time_driver.rs | 8 +--- examples/stm32c0/src/bin/rtc.rs | 4 +- examples/stm32f4/src/bin/rtc.rs | 4 +- examples/stm32g0/src/bin/rtc.rs | 4 +- examples/stm32h7/src/bin/rtc.rs | 4 +- examples/stm32h7rs/src/bin/rtc.rs | 4 +- examples/stm32l4/src/bin/rtc.rs | 4 +- examples/stm32u0/src/bin/rtc.rs | 4 +- examples/stm32wl/src/bin/rtc.rs | 4 +- tests/stm32/Cargo.toml | 1 + tests/stm32/src/bin/rtc.rs | 15 +++----- tests/stm32/src/bin/stop.rs | 11 ++++-- 15 files changed, 92 insertions(+), 65 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 597cedf86..696dfe83f 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -61,8 +61,6 @@ use crate::time_driver::get_driver; const THREAD_PENDER: usize = usize::MAX; -use crate::rtc::Rtc; - static mut EXECUTOR: Option = None; /// Prevent the device from going into the stop mode if held @@ -133,11 +131,6 @@ foreach_interrupt! { }; } -/// Reconfigure the RTC, if set. -pub fn reconfigure_rtc(f: impl FnOnce(&mut Rtc) -> R) -> R { - get_driver().reconfigure_rtc(f) -} - /// Get whether the core is ready to enter the given stop mode. /// /// This will return false if some peripheral driver is in use that diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index e09d5afb0..e5bf30927 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -4,7 +4,7 @@ use embassy_time::{Duration, TICK_HZ}; use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::RTC; -use crate::rtc::SealedInstance; +use crate::rtc::{RtcTimeProvider, SealedInstance}; /// Represents an instant in time that can be substracted to compute a duration pub(super) struct RtcInstant { @@ -117,7 +117,7 @@ impl WakeupPrescaler { impl Rtc { /// Return the current instant. fn instant(&self) -> Result { - self.time_provider().read(|_, tr, ss| { + RtcTimeProvider::new().read(|_, tr, ss| { let second = bcd2_to_byte((tr.st(), tr.su())); RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index fa5b45e3c..cbb904fd3 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -5,7 +5,9 @@ mod datetime; mod low_power; #[cfg(feature = "low-power")] -use core::cell::Cell; +use core::cell::{Cell, RefCell, RefMut}; +#[cfg(feature = "low-power")] +use core::ops; #[cfg(feature = "low-power")] use critical_section::CriticalSection; @@ -52,9 +54,8 @@ pub struct RtcTimeProvider { } impl RtcTimeProvider { - #[cfg(feature = "low-power")] /// Create a new RTC time provider instance. - pub fn new(_rtc: Peri<'static, RTC>) -> Self { + pub(self) const fn new() -> Self { Self { _private: () } } @@ -115,6 +116,50 @@ impl RtcTimeProvider { } } +#[cfg(feature = "low-power")] +/// Contains an RTC driver. +pub struct RtcContainer { + pub(self) mutex: &'static Mutex>>, +} + +#[cfg(feature = "low-power")] +impl RtcContainer { + pub(self) const fn new() -> Self { + Self { + mutex: &crate::time_driver::get_driver().rtc, + } + } + + /// Acquire an RTC borrow. + pub fn borrow_mut<'a>(&self, cs: CriticalSection<'a>) -> RtcBorrow<'a> { + RtcBorrow { + ref_mut: self.mutex.borrow(cs).borrow_mut(), + } + } +} + +#[cfg(feature = "low-power")] +/// Contains an RTC borrow. +pub struct RtcBorrow<'a> { + pub(self) ref_mut: RefMut<'a, Option>, +} + +#[cfg(feature = "low-power")] +impl<'a> ops::Deref for RtcBorrow<'a> { + type Target = Rtc; + + fn deref(&self) -> &Self::Target { + self.ref_mut.as_ref().unwrap() + } +} + +#[cfg(feature = "low-power")] +impl<'a> ops::DerefMut for RtcBorrow<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.ref_mut.as_mut().unwrap() + } +} + /// RTC driver. pub struct Rtc { #[cfg(feature = "low-power")] @@ -156,8 +201,14 @@ pub enum RtcCalibrationCyclePeriod { impl Rtc { #[cfg(not(feature = "low-power"))] /// Create a new RTC instance. - pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> Self { - Self::new_inner(rtc_config) + pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> (Self, RtcTimeProvider) { + (Self::new_inner(rtc_config), RtcTimeProvider::new()) + } + + #[cfg(feature = "low-power")] + /// Create a new RTC instance. + pub fn new(_rtc: Peri<'static, RTC>) -> (RtcContainer, RtcTimeProvider) { + (RtcContainer::new(), RtcTimeProvider::new()) } pub(self) fn new_inner(rtc_config: RtcConfig) -> Self { @@ -179,8 +230,8 @@ impl Rtc { // Wait for the clock to update after initialization #[cfg(not(rtc_v2_f2))] { - let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); - while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} + let now = RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap(); + while now == RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap() {} } #[cfg(feature = "low-power")] @@ -194,11 +245,6 @@ impl Rtc { freqs.rtc.to_hertz().unwrap() } - /// Acquire a [`RtcTimeProvider`] instance. - pub const fn time_provider(&self) -> RtcTimeProvider { - RtcTimeProvider { _private: () } - } - /// Set the datetime to a new value. /// /// # Errors @@ -242,15 +288,6 @@ impl Rtc { Ok(()) } - /// Return the current datetime. - /// - /// # Errors - /// - /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. - pub fn now(&self) -> Result { - self.time_provider().now() - } - /// Check if daylight savings time is active. pub fn get_daylight_savings(&self) -> bool { let cr = RTC::regs().cr().read(); diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 1941788e8..7db51d72e 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -215,7 +215,7 @@ pub(crate) struct RtcDriver { period: AtomicU32, alarm: Mutex, #[cfg(feature = "low-power")] - rtc: Mutex>>, + pub(crate) rtc: Mutex>>, #[cfg(feature = "low-power")] /// The minimum pause time beyond which the executor will enter a low-power state. min_stop_pause: Mutex>, @@ -420,12 +420,6 @@ impl RtcDriver { assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()); } - #[cfg(feature = "low-power")] - /// Reconfigure the rtc - pub(crate) fn reconfigure_rtc(&self, f: impl FnOnce(&mut Rtc) -> R) -> R { - critical_section::with(|cs| f(self.rtc.borrow(cs).borrow_mut().as_mut().unwrap())) - } - #[cfg(feature = "low-power")] /// Pause the timer if ready; return err if not pub(crate) fn pause_time(&self) -> Result<(), ()> { diff --git a/examples/stm32c0/src/bin/rtc.rs b/examples/stm32c0/src/bin/rtc.rs index feb27f6d9..5ff705ca2 100644 --- a/examples/stm32c0/src/bin/rtc.rs +++ b/examples/stm32c0/src/bin/rtc.rs @@ -21,12 +21,12 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); rtc.set_datetime(now.into()).expect("datetime not set"); loop { - let now: NaiveDateTime = rtc.now().unwrap().into(); + let now: NaiveDateTime = time_provider.now().unwrap().into(); info!("{}", now.and_utc().timestamp()); diff --git a/examples/stm32f4/src/bin/rtc.rs b/examples/stm32f4/src/bin/rtc.rs index feb27f6d9..5ff705ca2 100644 --- a/examples/stm32f4/src/bin/rtc.rs +++ b/examples/stm32f4/src/bin/rtc.rs @@ -21,12 +21,12 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); rtc.set_datetime(now.into()).expect("datetime not set"); loop { - let now: NaiveDateTime = rtc.now().unwrap().into(); + let now: NaiveDateTime = time_provider.now().unwrap().into(); info!("{}", now.and_utc().timestamp()); diff --git a/examples/stm32g0/src/bin/rtc.rs b/examples/stm32g0/src/bin/rtc.rs index 21da204cc..d8b58de22 100644 --- a/examples/stm32g0/src/bin/rtc.rs +++ b/examples/stm32g0/src/bin/rtc.rs @@ -17,12 +17,12 @@ async fn main(_spawner: Spawner) { let now = DateTime::from(2023, 6, 14, DayOfWeek::Friday, 15, 59, 10, 0); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); rtc.set_datetime(now.unwrap()).expect("datetime not set"); loop { - let now: DateTime = rtc.now().unwrap().into(); + let now: DateTime = time_provider.now().unwrap().into(); info!("{}:{}:{}", now.hour(), now.minute(), now.second()); diff --git a/examples/stm32h7/src/bin/rtc.rs b/examples/stm32h7/src/bin/rtc.rs index 1bd71637b..965716d23 100644 --- a/examples/stm32h7/src/bin/rtc.rs +++ b/examples/stm32h7/src/bin/rtc.rs @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.and_utc().timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); @@ -31,6 +31,6 @@ async fn main(_spawner: Spawner) { // In reality the delay would be much longer Timer::after_millis(20000).await; - let then: NaiveDateTime = rtc.now().unwrap().into(); + let then: NaiveDateTime = time_provider.now().unwrap().into(); info!("Got RTC! {:?}", then.and_utc().timestamp()); } diff --git a/examples/stm32h7rs/src/bin/rtc.rs b/examples/stm32h7rs/src/bin/rtc.rs index 1bd71637b..965716d23 100644 --- a/examples/stm32h7rs/src/bin/rtc.rs +++ b/examples/stm32h7rs/src/bin/rtc.rs @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.and_utc().timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); @@ -31,6 +31,6 @@ async fn main(_spawner: Spawner) { // In reality the delay would be much longer Timer::after_millis(20000).await; - let then: NaiveDateTime = rtc.now().unwrap().into(); + let then: NaiveDateTime = time_provider.now().unwrap().into(); info!("Got RTC! {:?}", then.and_utc().timestamp()); } diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index 1d26cd008..8b92075cc 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.and_utc().timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); @@ -47,6 +47,6 @@ async fn main(_spawner: Spawner) { // In reality the delay would be much longer Timer::after_millis(20000).await; - let then: NaiveDateTime = rtc.now().unwrap().into(); + let then: NaiveDateTime = time_provider.now().unwrap().into(); info!("Got RTC! {:?}", then.and_utc().timestamp()); } diff --git a/examples/stm32u0/src/bin/rtc.rs b/examples/stm32u0/src/bin/rtc.rs index d071cfbc7..56d16ccf7 100644 --- a/examples/stm32u0/src/bin/rtc.rs +++ b/examples/stm32u0/src/bin/rtc.rs @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.and_utc().timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); @@ -44,6 +44,6 @@ async fn main(_spawner: Spawner) { // In reality the delay would be much longer Timer::after_millis(20000).await; - let then: NaiveDateTime = rtc.now().unwrap().into(); + let then: NaiveDateTime = time_provider.now().unwrap().into(); info!("Got RTC! {:?}", then.and_utc().timestamp()); } diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs index d3709120f..2185142c9 100644 --- a/examples/stm32wl/src/bin/rtc.rs +++ b/examples/stm32wl/src/bin/rtc.rs @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.and_utc().timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); @@ -52,6 +52,6 @@ async fn main(_spawner: Spawner) { // In reality the delay would be much longer Timer::after_millis(20000).await; - let then: NaiveDateTime = rtc.now().unwrap().into(); + let then: NaiveDateTime = time_provider.now().unwrap().into(); info!("Got RTC! {:?}", then.and_utc().timestamp()); } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 1161e827b..e75a4ebb1 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -94,6 +94,7 @@ rand_core = { version = "0.9.1", default-features = false } rand_chacha = { version = "0.9.0", default-features = false } static_cell = "2" portable-atomic = { version = "1.5", features = [] } +critical-section = "1.1" chrono = { version = "^0.4", default-features = false, optional = true} sha2 = { version = "0.10.8", default-features = false } diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index 5c80eb250..43cf4a411 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -9,11 +9,9 @@ use chrono::{NaiveDate, NaiveDateTime}; use common::*; use defmt::assert; use embassy_executor::Spawner; -#[cfg(feature = "stop")] -use embassy_stm32::low_power::reconfigure_rtc; use embassy_stm32::rcc::LsConfig; #[cfg(feature = "stop")] -use embassy_stm32::rtc::RtcTimeProvider; +use embassy_stm32::rtc::Rtc; #[cfg(not(feature = "stop"))] use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_time::Timer; @@ -31,23 +29,22 @@ async fn main(_spawner: Spawner) { .unwrap(); #[cfg(not(feature = "stop"))] - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default()); #[cfg(feature = "stop")] - let time_provider = RtcTimeProvider::new(p.RTC); + let (rtc, time_provider) = Rtc::new(p.RTC); #[cfg(not(feature = "stop"))] rtc.set_datetime(now.into()).expect("datetime not set"); #[cfg(feature = "stop")] - reconfigure_rtc(|rtc| rtc.set_datetime(now.into()).expect("datetime not set")); + critical_section::with(|cs| { + rtc.borrow_mut(cs).set_datetime(now.into()).expect("datetime not set"); + }); info!("Waiting 5 seconds"); Timer::after_millis(5000).await; - #[cfg(not(feature = "stop"))] - let then: NaiveDateTime = rtc.now().unwrap().into(); - #[cfg(feature = "stop")] let then: NaiveDateTime = time_provider.now().unwrap().into(); let seconds = (then - now).num_seconds(); diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs index a9dbac676..4b3a775bb 100644 --- a/tests/stm32/src/bin/stop.rs +++ b/tests/stm32/src/bin/stop.rs @@ -10,8 +10,9 @@ use common::*; use cortex_m_rt::entry; use embassy_executor::Spawner; use embassy_stm32::Config; -use embassy_stm32::low_power::{Executor, StopMode, reconfigure_rtc, stop_ready}; +use embassy_stm32::low_power::{Executor, StopMode, stop_ready}; use embassy_stm32::rcc::LsConfig; +use embassy_stm32::rtc::Rtc; use embassy_time::Timer; #[entry] @@ -56,7 +57,7 @@ async fn async_main(spawner: Spawner) { config.rcc.hsi = Some(HSIPrescaler::DIV4); // 64 MHz HSI will need a /4 } - let _p = init_with_config(config); + let p = init_with_config(config); info!("Hello World!"); let now = NaiveDate::from_ymd_opt(2020, 5, 15) @@ -64,7 +65,11 @@ async fn async_main(spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - reconfigure_rtc(|rtc| rtc.set_datetime(now.into()).expect("datetime not set")); + let (rtc, _time_provider) = Rtc::new(p.RTC); + + critical_section::with(|cs| { + rtc.borrow_mut(cs).set_datetime(now.into()).expect("datetime not set"); + }); spawner.spawn(task_1().unwrap()); spawner.spawn(task_2().unwrap()); -- cgit