aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-04 20:55:18 +0000
committerGitHub <[email protected]>2025-11-04 20:55:18 +0000
commitd23c027dd1a1c5f7cdf818750dddf6a250658423 (patch)
tree5b81e8e0ca18d95d9852e986fe67f33db17de559
parentda4af0a7ae67d8ec592edf5eff9bbaed2d9e439a (diff)
parenta6412c923280082c506c75e6428e7d87870580eb (diff)
Merge pull request #4838 from embassy-rs/lp2
stm32/low-power: cleanup and improve
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/src/adc/v3.rs2
-rw-r--r--embassy-stm32/src/exti.rs2
-rw-r--r--embassy-stm32/src/i2c/v2.rs12
-rw-r--r--embassy-stm32/src/lib.rs18
-rw-r--r--embassy-stm32/src/low_power.rs164
-rw-r--r--embassy-stm32/src/rtc/mod.rs24
-rw-r--r--embassy-stm32/src/time_driver.rs42
-rw-r--r--embassy-stm32/src/timer/mod.rs4
-rw-r--r--examples/stm32h5/src/bin/stop.rs5
-rw-r--r--examples/stm32l5/src/bin/stop.rs5
-rw-r--r--examples/stm32wle5/src/bin/adc.rs6
-rw-r--r--examples/stm32wle5/src/bin/blinky.rs6
-rw-r--r--examples/stm32wle5/src/bin/button_exti.rs6
-rw-r--r--examples/stm32wle5/src/bin/i2c.rs6
-rw-r--r--tests/stm32/src/bin/rtc.rs18
-rw-r--r--tests/stm32/src/bin/stop.rs11
17 files changed, 164 insertions, 168 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index b7d22da39..ee76a5d1b 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
43- adc: consolidate ringbuffer 43- adc: consolidate ringbuffer
44- feat: Added RTC low-power support for STM32WLEx ([#4716](https://github.com/embassy-rs/embassy/pull/4716)) 44- feat: Added RTC low-power support for STM32WLEx ([#4716](https://github.com/embassy-rs/embassy/pull/4716))
45- fix: Correct STM32WBA VREFBUFTRIM values 45- fix: Correct STM32WBA VREFBUFTRIM values
46- low_power: remove stop_with rtc and initialize in init if low-power feature enabled.
46 47
47## 0.4.0 - 2025-08-26 48## 0.4.0 - 2025-08-26
48 49
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 62c2da557..170b08a25 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -463,7 +463,7 @@ impl<'d, T: Instance> Adc<'d, T> {
463 ); 463 );
464 464
465 #[cfg(all(feature = "low-power", stm32wlex))] 465 #[cfg(all(feature = "low-power", stm32wlex))]
466 let _device_busy = crate::low_power::DeviceBusy::new(); 466 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
467 467
468 // Ensure no conversions are ongoing and ADC is enabled. 468 // Ensure no conversions are ongoing and ADC is enabled.
469 Self::cancel_conversions(); 469 Self::cancel_conversions();
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs
index 12600d4eb..2f5c3406a 100644
--- a/embassy-stm32/src/exti.rs
+++ b/embassy-stm32/src/exti.rs
@@ -71,7 +71,7 @@ unsafe fn on_irq() {
71 } 71 }
72 72
73 #[cfg(feature = "low-power")] 73 #[cfg(feature = "low-power")]
74 crate::low_power::on_wakeup_irq(); 74 crate::low_power::Executor::on_wakeup_irq();
75} 75}
76 76
77struct BitIter(u32); 77struct BitIter(u32);
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 6f2d03bd1..ba9590013 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -73,7 +73,7 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() {
73 // restore the clocks to their last configured state as 73 // restore the clocks to their last configured state as
74 // much is lost in STOP modes 74 // much is lost in STOP modes
75 #[cfg(all(feature = "low-power", stm32wlex))] 75 #[cfg(all(feature = "low-power", stm32wlex))]
76 crate::low_power::on_wakeup_irq(); 76 crate::low_power::Executor::on_wakeup_irq();
77 77
78 let regs = T::info().regs; 78 let regs = T::info().regs;
79 let isr = regs.isr().read(); 79 let isr = regs.isr().read();
@@ -820,7 +820,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
820 /// Write. 820 /// Write.
821 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { 821 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
822 #[cfg(all(feature = "low-power", stm32wlex))] 822 #[cfg(all(feature = "low-power", stm32wlex))]
823 let _device_busy = crate::low_power::DeviceBusy::new(); 823 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
824 let timeout = self.timeout(); 824 let timeout = self.timeout();
825 if write.is_empty() { 825 if write.is_empty() {
826 self.write_internal(address.into(), write, true, timeout) 826 self.write_internal(address.into(), write, true, timeout)
@@ -836,7 +836,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
836 /// The buffers are concatenated in a single write transaction. 836 /// The buffers are concatenated in a single write transaction.
837 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { 837 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {
838 #[cfg(all(feature = "low-power", stm32wlex))] 838 #[cfg(all(feature = "low-power", stm32wlex))]
839 let _device_busy = crate::low_power::DeviceBusy::new(); 839 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
840 let timeout = self.timeout(); 840 let timeout = self.timeout();
841 841
842 if write.is_empty() { 842 if write.is_empty() {
@@ -861,7 +861,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
861 /// Read. 861 /// Read.
862 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { 862 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
863 #[cfg(all(feature = "low-power", stm32wlex))] 863 #[cfg(all(feature = "low-power", stm32wlex))]
864 let _device_busy = crate::low_power::DeviceBusy::new(); 864 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
865 let timeout = self.timeout(); 865 let timeout = self.timeout();
866 866
867 if buffer.is_empty() { 867 if buffer.is_empty() {
@@ -875,7 +875,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
875 /// Write, restart, read. 875 /// Write, restart, read.
876 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { 876 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
877 #[cfg(all(feature = "low-power", stm32wlex))] 877 #[cfg(all(feature = "low-power", stm32wlex))]
878 let _device_busy = crate::low_power::DeviceBusy::new(); 878 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
879 let timeout = self.timeout(); 879 let timeout = self.timeout();
880 880
881 if write.is_empty() { 881 if write.is_empty() {
@@ -902,7 +902,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
902 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 902 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
903 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { 903 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
904 #[cfg(all(feature = "low-power", stm32wlex))] 904 #[cfg(all(feature = "low-power", stm32wlex))]
905 let _device_busy = crate::low_power::DeviceBusy::new(); 905 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
906 let _ = addr; 906 let _ = addr;
907 let _ = operations; 907 let _ = operations;
908 todo!() 908 todo!()
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index dbf0fe620..85606e7de 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -241,6 +241,14 @@ pub struct Config {
241 /// RCC config. 241 /// RCC config.
242 pub rcc: rcc::Config, 242 pub rcc: rcc::Config,
243 243
244 #[cfg(feature = "low-power")]
245 /// RTC config
246 pub rtc: rtc::RtcConfig,
247
248 #[cfg(feature = "low-power")]
249 /// Minimum time to stop
250 pub min_stop_pause: embassy_time::Duration,
251
244 /// Enable debug during sleep and stop. 252 /// Enable debug during sleep and stop.
245 /// 253 ///
246 /// May increase power consumption. Defaults to true. 254 /// May increase power consumption. Defaults to true.
@@ -294,6 +302,10 @@ impl Default for Config {
294 fn default() -> Self { 302 fn default() -> Self {
295 Self { 303 Self {
296 rcc: Default::default(), 304 rcc: Default::default(),
305 #[cfg(feature = "low-power")]
306 rtc: Default::default(),
307 #[cfg(feature = "low-power")]
308 min_stop_pause: embassy_time::Duration::from_millis(250),
297 #[cfg(dbgmcu)] 309 #[cfg(dbgmcu)]
298 enable_debug_during_sleep: true, 310 enable_debug_during_sleep: true,
299 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))] 311 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba))]
@@ -623,6 +635,12 @@ fn init_hw(config: Config) -> Peripherals {
623 exti::init(cs); 635 exti::init(cs);
624 636
625 rcc::init_rcc(cs, config.rcc); 637 rcc::init_rcc(cs, config.rcc);
638
639 #[cfg(feature = "low-power")]
640 crate::rtc::init_rtc(cs, config.rtc);
641
642 #[cfg(feature = "low-power")]
643 crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause);
626 } 644 }
627 645
628 p 646 p
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index cde3153f6..597cedf86 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -41,10 +41,6 @@
41//! config.enable_debug_during_sleep = false; 41//! config.enable_debug_during_sleep = false;
42//! let p = embassy_stm32::init(config); 42//! let p = embassy_stm32::init(config);
43//! 43//!
44//! // give the RTC to the executor...
45//! let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
46//! embassy_stm32::low_power::stop_with_rtc(rtc);
47//!
48//! // your application here... 44//! // your application here...
49//! } 45//! }
50//! ``` 46//! ```
@@ -57,10 +53,11 @@ use core::marker::PhantomData;
57use core::sync::atomic::{Ordering, compiler_fence}; 53use core::sync::atomic::{Ordering, compiler_fence};
58 54
59use cortex_m::peripheral::SCB; 55use cortex_m::peripheral::SCB;
56use critical_section::CriticalSection;
60use embassy_executor::*; 57use embassy_executor::*;
61 58
62use crate::interrupt; 59use crate::interrupt;
63use crate::time_driver::{RtcDriver, get_driver}; 60use crate::time_driver::get_driver;
64 61
65const THREAD_PENDER: usize = usize::MAX; 62const THREAD_PENDER: usize = usize::MAX;
66 63
@@ -68,46 +65,59 @@ use crate::rtc::Rtc;
68 65
69static mut EXECUTOR: Option<Executor> = None; 66static mut EXECUTOR: Option<Executor> = None;
70 67
71#[cfg(stm32wlex)] 68/// Prevent the device from going into the stop mode if held
72pub(crate) use self::busy::DeviceBusy; 69pub struct DeviceBusy(StopMode);
73#[cfg(stm32wlex)]
74mod busy {
75 use core::sync::atomic::{AtomicU32, Ordering};
76 70
77 // Count of devices blocking STOP 71impl DeviceBusy {
78 static STOP_BLOCKED: AtomicU32 = AtomicU32::new(0); 72 /// Create a new DeviceBusy with stop1.
73 pub fn new_stop1() -> Self {
74 Self::new(StopMode::Stop1)
75 }
79 76
80 /// Check if STOP1 is blocked. 77 /// Create a new DeviceBusy with stop2.
81 pub(crate) fn stop_blocked() -> bool { 78 pub fn new_stop2() -> Self {
82 STOP_BLOCKED.load(Ordering::SeqCst) > 0 79 Self::new(StopMode::Stop2)
83 } 80 }
84 81
85 /// When ca device goes busy it will construct one of these where it will be dropped when the device goes idle. 82 /// Create a new DeviceBusy.
86 pub(crate) struct DeviceBusy {} 83 pub fn new(stop_mode: StopMode) -> Self {
84 critical_section::with(|_| unsafe {
85 match stop_mode {
86 StopMode::Stop1 => {
87 crate::rcc::REFCOUNT_STOP1 += 1;
88 }
89 StopMode::Stop2 => {
90 crate::rcc::REFCOUNT_STOP2 += 1;
91 }
92 }
93 });
87 94
88 impl DeviceBusy { 95 Self(stop_mode)
89 /// Create a new DeviceBusy.
90 pub(crate) fn new() -> Self {
91 STOP_BLOCKED.fetch_add(1, Ordering::SeqCst);
92 trace!("low power: device busy: Stop:{}", STOP_BLOCKED.load(Ordering::SeqCst));
93 Self {}
94 }
95 } 96 }
97}
96 98
97 impl Drop for DeviceBusy { 99impl Drop for DeviceBusy {
98 fn drop(&mut self) { 100 fn drop(&mut self) {
99 STOP_BLOCKED.fetch_sub(1, Ordering::SeqCst); 101 critical_section::with(|_| unsafe {
100 trace!("low power: device idle: Stop:{}", STOP_BLOCKED.load(Ordering::SeqCst)); 102 match self.0 {
101 } 103 StopMode::Stop1 => {
104 crate::rcc::REFCOUNT_STOP1 -= 1;
105 }
106 StopMode::Stop2 => {
107 crate::rcc::REFCOUNT_STOP2 -= 1;
108 }
109 }
110 });
102 } 111 }
103} 112}
113
104#[cfg(not(stm32u0))] 114#[cfg(not(stm32u0))]
105foreach_interrupt! { 115foreach_interrupt! {
106 (RTC, rtc, $block:ident, WKUP, $irq:ident) => { 116 (RTC, rtc, $block:ident, WKUP, $irq:ident) => {
107 #[interrupt] 117 #[interrupt]
108 #[allow(non_snake_case)] 118 #[allow(non_snake_case)]
109 unsafe fn $irq() { 119 unsafe fn $irq() {
110 EXECUTOR.as_mut().unwrap().on_wakeup_irq(); 120 Executor::on_wakeup_irq();
111 } 121 }
112 }; 122 };
113} 123}
@@ -118,27 +128,14 @@ foreach_interrupt! {
118 #[interrupt] 128 #[interrupt]
119 #[allow(non_snake_case)] 129 #[allow(non_snake_case)]
120 unsafe fn $irq() { 130 unsafe fn $irq() {
121 EXECUTOR.as_mut().unwrap().on_wakeup_irq(); 131 Executor::on_wakeup_irq();
122 } 132 }
123 }; 133 };
124} 134}
125 135
126#[allow(dead_code)]
127pub(crate) unsafe fn on_wakeup_irq() {
128 if EXECUTOR.is_some() {
129 trace!("low power: wakeup irq");
130 EXECUTOR.as_mut().unwrap().on_wakeup_irq();
131 }
132}
133
134/// Configure STOP mode with RTC.
135pub fn stop_with_rtc(rtc: Rtc) {
136 unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
137}
138
139/// Reconfigure the RTC, if set. 136/// Reconfigure the RTC, if set.
140pub fn reconfigure_rtc(f: impl FnOnce(&mut Rtc)) { 137pub fn reconfigure_rtc<R>(f: impl FnOnce(&mut Rtc) -> R) -> R {
141 unsafe { EXECUTOR.as_mut().unwrap() }.reconfigure_rtc(f); 138 get_driver().reconfigure_rtc(f)
142} 139}
143 140
144/// Get whether the core is ready to enter the given stop mode. 141/// Get whether the core is ready to enter the given stop mode.
@@ -146,11 +143,11 @@ pub fn reconfigure_rtc(f: impl FnOnce(&mut Rtc)) {
146/// This will return false if some peripheral driver is in use that 143/// This will return false if some peripheral driver is in use that
147/// prevents entering the given stop mode. 144/// prevents entering the given stop mode.
148pub fn stop_ready(stop_mode: StopMode) -> bool { 145pub fn stop_ready(stop_mode: StopMode) -> bool {
149 match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() { 146 critical_section::with(|cs| match Executor::stop_mode(cs) {
150 Some(StopMode::Stop2) => true, 147 Some(StopMode::Stop2) => true,
151 Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, 148 Some(StopMode::Stop1) => stop_mode == StopMode::Stop1,
152 None => false, 149 None => false,
153 } 150 })
154} 151}
155 152
156/// Available Stop modes. 153/// Available Stop modes.
@@ -193,7 +190,6 @@ pub struct Executor {
193 inner: raw::Executor, 190 inner: raw::Executor,
194 not_send: PhantomData<*mut ()>, 191 not_send: PhantomData<*mut ()>,
195 scb: SCB, 192 scb: SCB,
196 time_driver: &'static RtcDriver,
197} 193}
198 194
199impl Executor { 195impl Executor {
@@ -206,7 +202,6 @@ impl Executor {
206 inner: raw::Executor::new(THREAD_PENDER as *mut ()), 202 inner: raw::Executor::new(THREAD_PENDER as *mut ()),
207 not_send: PhantomData, 203 not_send: PhantomData,
208 scb: cortex_m::Peripherals::steal().SCB, 204 scb: cortex_m::Peripherals::steal().SCB,
209 time_driver: get_driver(),
210 }); 205 });
211 206
212 let executor = EXECUTOR.as_mut().unwrap(); 207 let executor = EXECUTOR.as_mut().unwrap();
@@ -215,54 +210,33 @@ impl Executor {
215 }) 210 })
216 } 211 }
217 212
218 unsafe fn on_wakeup_irq(&mut self) { 213 pub(crate) unsafe fn on_wakeup_irq() {
219 #[cfg(stm32wlex)] 214 critical_section::with(|cs| {
220 { 215 #[cfg(stm32wlex)]
221 let extscr = crate::pac::PWR.extscr().read(); 216 {
222 if extscr.c1stop2f() || extscr.c1stopf() { 217 let extscr = crate::pac::PWR.extscr().read();
223 // when we wake from any stop mode we need to re-initialize the rcc 218 if extscr.c1stop2f() || extscr.c1stopf() {
224 crate::rcc::apply_resume_config(); 219 // when we wake from any stop mode we need to re-initialize the rcc
225 if extscr.c1stop2f() { 220 crate::rcc::apply_resume_config();
226 // when we wake from STOP2, we need to re-initialize the time driver 221 if extscr.c1stop2f() {
227 critical_section::with(|cs| crate::time_driver::init_timer(cs)); 222 // when we wake from STOP2, we need to re-initialize the time driver
228 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) 223 crate::time_driver::init_timer(cs);
229 // and given that we just woke from STOP2, we can reset them 224 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer)
230 crate::rcc::REFCOUNT_STOP2 = 0; 225 // and given that we just woke from STOP2, we can reset them
231 crate::rcc::REFCOUNT_STOP1 = 0; 226 crate::rcc::REFCOUNT_STOP2 = 0;
227 crate::rcc::REFCOUNT_STOP1 = 0;
228 }
232 } 229 }
233 } 230 }
234 } 231 get_driver().resume_time(cs);
235 self.time_driver.resume_time(); 232 trace!("low power: resume");
236 trace!("low power: resume"); 233 });
237 }
238
239 pub(self) fn stop_with_rtc(&mut self, rtc: Rtc) {
240 self.time_driver.set_rtc(rtc);
241 self.time_driver.reconfigure_rtc(|rtc| rtc.enable_wakeup_line());
242
243 trace!("low power: stop with rtc configured");
244 }
245
246 pub(self) fn reconfigure_rtc(&mut self, f: impl FnOnce(&mut Rtc)) {
247 self.time_driver.reconfigure_rtc(f);
248 }
249
250 fn stop1_ok_to_enter(&self) -> bool {
251 #[cfg(stm32wlex)]
252 if self::busy::stop_blocked() {
253 return false;
254 }
255 unsafe { crate::rcc::REFCOUNT_STOP1 == 0 }
256 }
257
258 fn stop2_ok_to_enter(&self) -> bool {
259 self.stop1_ok_to_enter() && unsafe { crate::rcc::REFCOUNT_STOP2 == 0 }
260 } 234 }
261 235
262 fn stop_mode(&self) -> Option<StopMode> { 236 fn stop_mode(_cs: CriticalSection) -> Option<StopMode> {
263 if self.stop2_ok_to_enter() { 237 if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } {
264 Some(StopMode::Stop2) 238 Some(StopMode::Stop2)
265 } else if self.stop1_ok_to_enter() { 239 } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } {
266 Some(StopMode::Stop1) 240 Some(StopMode::Stop1)
267 } else { 241 } else {
268 None 242 None
@@ -291,14 +265,14 @@ impl Executor {
291 265
292 compiler_fence(Ordering::SeqCst); 266 compiler_fence(Ordering::SeqCst);
293 267
294 let stop_mode = self.stop_mode(); 268 let stop_mode = critical_section::with(|cs| Self::stop_mode(cs));
295 269
296 if stop_mode.is_none() { 270 if stop_mode.is_none() {
297 trace!("low power: not ready to stop"); 271 trace!("low power: not ready to stop");
298 return; 272 return;
299 } 273 }
300 274
301 if self.time_driver.pause_time().is_err() { 275 if get_driver().pause_time().is_err() {
302 trace!("low power: failed to pause time"); 276 trace!("low power: failed to pause time");
303 return; 277 return;
304 } 278 }
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index bc6df528b..fa5b45e3c 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -8,6 +8,8 @@ mod low_power;
8use core::cell::Cell; 8use core::cell::Cell;
9 9
10#[cfg(feature = "low-power")] 10#[cfg(feature = "low-power")]
11use critical_section::CriticalSection;
12#[cfg(feature = "low-power")]
11use embassy_sync::blocking_mutex::Mutex; 13use embassy_sync::blocking_mutex::Mutex;
12#[cfg(feature = "low-power")] 14#[cfg(feature = "low-power")]
13use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 15use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
@@ -44,11 +46,18 @@ pub enum RtcError {
44} 46}
45 47
46/// Provides immutable access to the current time of the RTC. 48/// Provides immutable access to the current time of the RTC.
49#[derive(Clone)]
47pub struct RtcTimeProvider { 50pub struct RtcTimeProvider {
48 _private: (), 51 _private: (),
49} 52}
50 53
51impl RtcTimeProvider { 54impl RtcTimeProvider {
55 #[cfg(feature = "low-power")]
56 /// Create a new RTC time provider instance.
57 pub fn new(_rtc: Peri<'static, RTC>) -> Self {
58 Self { _private: () }
59 }
60
52 /// Return the current datetime. 61 /// Return the current datetime.
53 /// 62 ///
54 /// # Errors 63 /// # Errors
@@ -145,8 +154,13 @@ pub enum RtcCalibrationCyclePeriod {
145} 154}
146 155
147impl Rtc { 156impl Rtc {
157 #[cfg(not(feature = "low-power"))]
148 /// Create a new RTC instance. 158 /// Create a new RTC instance.
149 pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> Self { 159 pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> Self {
160 Self::new_inner(rtc_config)
161 }
162
163 pub(self) fn new_inner(rtc_config: RtcConfig) -> Self {
150 #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] 164 #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))]
151 crate::rcc::enable_and_reset::<RTC>(); 165 crate::rcc::enable_and_reset::<RTC>();
152 166
@@ -169,6 +183,9 @@ impl Rtc {
169 while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} 183 while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {}
170 } 184 }
171 185
186 #[cfg(feature = "low-power")]
187 this.enable_wakeup_line();
188
172 this 189 this
173 } 190 }
174 191
@@ -315,3 +332,10 @@ trait SealedInstance {
315 332
316 // fn apply_config(&mut self, rtc_config: RtcConfig); 333 // fn apply_config(&mut self, rtc_config: RtcConfig);
317} 334}
335
336#[cfg(feature = "low-power")]
337pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) {
338 crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config));
339
340 trace!("low power: stop with rtc configured");
341}
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 4956d1f68..1941788e8 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -216,6 +216,9 @@ pub(crate) struct RtcDriver {
216 alarm: Mutex<CriticalSectionRawMutex, AlarmState>, 216 alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
217 #[cfg(feature = "low-power")] 217 #[cfg(feature = "low-power")]
218 rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>, 218 rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>,
219 #[cfg(feature = "low-power")]
220 /// The minimum pause time beyond which the executor will enter a low-power state.
221 min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>,
219 /// Saved count for the timer (its value is lost when entering STOP2) 222 /// Saved count for the timer (its value is lost when entering STOP2)
220 #[cfg(all(feature = "low-power", stm32wlex))] 223 #[cfg(all(feature = "low-power", stm32wlex))]
221 saved_count: AtomicU16, 224 saved_count: AtomicU16,
@@ -227,6 +230,8 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
227 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), 230 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
228 #[cfg(feature = "low-power")] 231 #[cfg(feature = "low-power")]
229 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)), 232 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)),
233 #[cfg(feature = "low-power")]
234 min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))),
230 #[cfg(all(feature = "low-power", stm32wlex))] 235 #[cfg(all(feature = "low-power", stm32wlex))]
231 saved_count: AtomicU16::new(0), 236 saved_count: AtomicU16::new(0),
232 queue: Mutex::new(RefCell::new(Queue::new())) 237 queue: Mutex::new(RefCell::new(Queue::new()))
@@ -400,27 +405,26 @@ impl RtcDriver {
400 } 405 }
401 406
402 /* 407 /*
403 Low-power public functions: all create a critical section 408 Low-power public functions: all create or require a critical section
404 */ 409 */
405 #[cfg(feature = "low-power")] 410 #[cfg(feature = "low-power")]
406 /// Set the rtc but panic if it's already been set 411 pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) {
407 pub(crate) fn set_rtc(&self, mut rtc: Rtc) { 412 self.min_stop_pause.borrow(cs).replace(min_stop_pause);
408 critical_section::with(|cs| {
409 rtc.stop_wakeup_alarm(cs);
410
411 assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none())
412 });
413 } 413 }
414 414
415 #[cfg(feature = "low-power")] 415 #[cfg(feature = "low-power")]
416 /// Set the rtc but panic if it's already been set 416 /// Set the rtc but panic if it's already been set
417 pub(crate) fn reconfigure_rtc(&self, f: impl FnOnce(&mut Rtc)) { 417 pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) {
418 critical_section::with(|cs| f(self.rtc.borrow(cs).borrow_mut().as_mut().unwrap())); 418 rtc.stop_wakeup_alarm(cs);
419
420 assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none());
419 } 421 }
420 422
421 #[cfg(feature = "low-power")] 423 #[cfg(feature = "low-power")]
422 /// The minimum pause time beyond which the executor will enter a low-power state. 424 /// Reconfigure the rtc
423 pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250); 425 pub(crate) fn reconfigure_rtc<R>(&self, f: impl FnOnce(&mut Rtc) -> R) -> R {
426 critical_section::with(|cs| f(self.rtc.borrow(cs).borrow_mut().as_mut().unwrap()))
427 }
424 428
425 #[cfg(feature = "low-power")] 429 #[cfg(feature = "low-power")]
426 /// Pause the timer if ready; return err if not 430 /// Pause the timer if ready; return err if not
@@ -434,9 +438,9 @@ impl RtcDriver {
434 self.stop_wakeup_alarm(cs); 438 self.stop_wakeup_alarm(cs);
435 439
436 let time_until_next_alarm = self.time_until_next_alarm(cs); 440 let time_until_next_alarm = self.time_until_next_alarm(cs);
437 if time_until_next_alarm < Self::MIN_STOP_PAUSE { 441 if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() {
438 trace!( 442 trace!(
439 "time_until_next_alarm < Self::MIN_STOP_PAUSE ({})", 443 "time_until_next_alarm < self.min_stop_pause ({})",
440 time_until_next_alarm 444 time_until_next_alarm
441 ); 445 );
442 Err(()) 446 Err(())
@@ -460,18 +464,16 @@ impl RtcDriver {
460 464
461 #[cfg(feature = "low-power")] 465 #[cfg(feature = "low-power")]
462 /// Resume the timer with the given offset 466 /// Resume the timer with the given offset
463 pub(crate) fn resume_time(&self) { 467 pub(crate) fn resume_time(&self, cs: CriticalSection) {
464 if regs_gp16().cr1().read().cen() { 468 if regs_gp16().cr1().read().cen() {
465 // Time isn't currently stopped 469 // Time isn't currently stopped
466 470
467 return; 471 return;
468 } 472 }
469 473
470 critical_section::with(|cs| { 474 self.stop_wakeup_alarm(cs);
471 self.stop_wakeup_alarm(cs);
472 475
473 regs_gp16().cr1().modify(|w| w.set_cen(true)); 476 regs_gp16().cr1().modify(|w| w.set_cen(true));
474 })
475 } 477 }
476 478
477 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { 479 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
@@ -543,7 +545,7 @@ impl Driver for RtcDriver {
543} 545}
544 546
545#[cfg(feature = "low-power")] 547#[cfg(feature = "low-power")]
546pub(crate) fn get_driver() -> &'static RtcDriver { 548pub(crate) const fn get_driver() -> &'static RtcDriver {
547 &DRIVER 549 &DRIVER
548} 550}
549 551
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index b09bc7166..804d1ef37 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -399,7 +399,7 @@ pub struct UpdateInterruptHandler<T: CoreInstance> {
399impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> { 399impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> {
400 unsafe fn on_interrupt() { 400 unsafe fn on_interrupt() {
401 #[cfg(feature = "low-power")] 401 #[cfg(feature = "low-power")]
402 crate::low_power::on_wakeup_irq(); 402 crate::low_power::Executor::on_wakeup_irq();
403 403
404 let regs = crate::pac::timer::TimCore::from_ptr(T::regs()); 404 let regs = crate::pac::timer::TimCore::from_ptr(T::regs());
405 405
@@ -429,7 +429,7 @@ impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompare
429{ 429{
430 unsafe fn on_interrupt() { 430 unsafe fn on_interrupt() {
431 #[cfg(feature = "low-power")] 431 #[cfg(feature = "low-power")]
432 crate::low_power::on_wakeup_irq(); 432 crate::low_power::Executor::on_wakeup_irq();
433 433
434 let regs = crate::pac::timer::TimGp16::from_ptr(T::regs()); 434 let regs = crate::pac::timer::TimGp16::from_ptr(T::regs());
435 435
diff --git a/examples/stm32h5/src/bin/stop.rs b/examples/stm32h5/src/bin/stop.rs
index 2026d8f99..caebc9daf 100644
--- a/examples/stm32h5/src/bin/stop.rs
+++ b/examples/stm32h5/src/bin/stop.rs
@@ -9,7 +9,6 @@ use embassy_executor::Spawner;
9use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; 9use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
10use embassy_stm32::low_power::Executor; 10use embassy_stm32::low_power::Executor;
11use embassy_stm32::rcc::{HSIPrescaler, LsConfig}; 11use embassy_stm32::rcc::{HSIPrescaler, LsConfig};
12use embassy_stm32::rtc::{Rtc, RtcConfig};
13use embassy_stm32::{Config, Peri}; 12use embassy_stm32::{Config, Peri};
14use embassy_time::Timer; 13use embassy_time::Timer;
15use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
@@ -36,10 +35,6 @@ async fn async_main(spawner: Spawner) {
36 // config.enable_debug_during_sleep = false; 35 // config.enable_debug_during_sleep = false;
37 let p = embassy_stm32::init(config); 36 let p = embassy_stm32::init(config);
38 37
39 // give the RTC to the executor...
40 let rtc = Rtc::new(p.RTC, RtcConfig::default());
41 embassy_stm32::low_power::stop_with_rtc(rtc);
42
43 spawner.spawn(unwrap!(blinky(p.PB4.into()))); 38 spawner.spawn(unwrap!(blinky(p.PB4.into())));
44 spawner.spawn(unwrap!(timeout())); 39 spawner.spawn(unwrap!(timeout()));
45} 40}
diff --git a/examples/stm32l5/src/bin/stop.rs b/examples/stm32l5/src/bin/stop.rs
index 7662dbfa8..3d119f90f 100644
--- a/examples/stm32l5/src/bin/stop.rs
+++ b/examples/stm32l5/src/bin/stop.rs
@@ -6,7 +6,6 @@ use embassy_executor::Spawner;
6use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; 6use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
7use embassy_stm32::low_power::Executor; 7use embassy_stm32::low_power::Executor;
8use embassy_stm32::rcc::LsConfig; 8use embassy_stm32::rcc::LsConfig;
9use embassy_stm32::rtc::{Rtc, RtcConfig};
10use embassy_stm32::{Config, Peri}; 9use embassy_stm32::{Config, Peri};
11use embassy_time::Timer; 10use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
@@ -27,10 +26,6 @@ async fn async_main(spawner: Spawner) {
27 // config.enable_debug_during_sleep = false; 26 // config.enable_debug_during_sleep = false;
28 let p = embassy_stm32::init(config); 27 let p = embassy_stm32::init(config);
29 28
30 // give the RTC to the executor...
31 let rtc = Rtc::new(p.RTC, RtcConfig::default());
32 embassy_stm32::low_power::stop_with_rtc(rtc);
33
34 spawner.spawn(unwrap!(blinky(p.PC7.into()))); 29 spawner.spawn(unwrap!(blinky(p.PC7.into())));
35 spawner.spawn(unwrap!(timeout())); 30 spawner.spawn(unwrap!(timeout()));
36} 31}
diff --git a/examples/stm32wle5/src/bin/adc.rs b/examples/stm32wle5/src/bin/adc.rs
index ff1a5fa16..8b830a1e6 100644
--- a/examples/stm32wle5/src/bin/adc.rs
+++ b/examples/stm32wle5/src/bin/adc.rs
@@ -7,7 +7,6 @@ use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::adc::{Adc, SampleTime}; 8use embassy_stm32::adc::{Adc, SampleTime};
9use embassy_stm32::low_power::Executor; 9use embassy_stm32::low_power::Executor;
10use embassy_stm32::rtc::{Rtc, RtcConfig};
11use embassy_time::Timer; 10use embassy_time::Timer;
12use panic_probe as _; 11use panic_probe as _;
13use static_cell::StaticCell; 12use static_cell::StaticCell;
@@ -71,11 +70,6 @@ async fn async_main(_spawner: Spawner) {
71 defmt_serial::defmt_serial(SERIAL.init(uart)); 70 defmt_serial::defmt_serial(SERIAL.init(uart));
72 } 71 }
73 72
74 // give the RTC to the low_power executor...
75 let rtc_config = RtcConfig::default();
76 let rtc = Rtc::new(p.RTC, rtc_config);
77 embassy_stm32::low_power::stop_with_rtc(rtc);
78
79 info!("Hello World!"); 73 info!("Hello World!");
80 74
81 let mut adc = Adc::new(p.ADC1); 75 let mut adc = Adc::new(p.ADC1);
diff --git a/examples/stm32wle5/src/bin/blinky.rs b/examples/stm32wle5/src/bin/blinky.rs
index 1191a1157..b2745fdaf 100644
--- a/examples/stm32wle5/src/bin/blinky.rs
+++ b/examples/stm32wle5/src/bin/blinky.rs
@@ -7,7 +7,6 @@ use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::gpio::{Level, Output, Speed}; 8use embassy_stm32::gpio::{Level, Output, Speed};
9use embassy_stm32::low_power::Executor; 9use embassy_stm32::low_power::Executor;
10use embassy_stm32::rtc::{Rtc, RtcConfig};
11use embassy_time::Timer; 10use embassy_time::Timer;
12use panic_probe as _; 11use panic_probe as _;
13use static_cell::StaticCell; 12use static_cell::StaticCell;
@@ -69,11 +68,6 @@ async fn async_main(_spawner: Spawner) {
69 defmt_serial::defmt_serial(SERIAL.init(uart)); 68 defmt_serial::defmt_serial(SERIAL.init(uart));
70 } 69 }
71 70
72 // give the RTC to the low_power executor...
73 let rtc_config = RtcConfig::default();
74 let rtc = Rtc::new(p.RTC, rtc_config);
75 embassy_stm32::low_power::stop_with_rtc(rtc);
76
77 info!("Hello World!"); 71 info!("Hello World!");
78 72
79 let mut led = Output::new(p.PB5, Level::High, Speed::Low); 73 let mut led = Output::new(p.PB5, Level::High, Speed::Low);
diff --git a/examples/stm32wle5/src/bin/button_exti.rs b/examples/stm32wle5/src/bin/button_exti.rs
index f07f9724d..db1bff0be 100644
--- a/examples/stm32wle5/src/bin/button_exti.rs
+++ b/examples/stm32wle5/src/bin/button_exti.rs
@@ -8,7 +8,6 @@ use embassy_executor::Spawner;
8use embassy_stm32::exti::ExtiInput; 8use embassy_stm32::exti::ExtiInput;
9use embassy_stm32::gpio::Pull; 9use embassy_stm32::gpio::Pull;
10use embassy_stm32::low_power::Executor; 10use embassy_stm32::low_power::Executor;
11use embassy_stm32::rtc::{Rtc, RtcConfig};
12use panic_probe as _; 11use panic_probe as _;
13use static_cell::StaticCell; 12use static_cell::StaticCell;
14 13
@@ -71,11 +70,6 @@ async fn async_main(_spawner: Spawner) {
71 defmt_serial::defmt_serial(SERIAL.init(uart)); 70 defmt_serial::defmt_serial(SERIAL.init(uart));
72 } 71 }
73 72
74 // give the RTC to the low_power executor...
75 let rtc_config = RtcConfig::default();
76 let rtc = Rtc::new(p.RTC, rtc_config);
77 embassy_stm32::low_power::stop_with_rtc(rtc);
78
79 info!("Hello World!"); 73 info!("Hello World!");
80 74
81 let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up); 75 let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up);
diff --git a/examples/stm32wle5/src/bin/i2c.rs b/examples/stm32wle5/src/bin/i2c.rs
index af07f911e..c31c673c9 100644
--- a/examples/stm32wle5/src/bin/i2c.rs
+++ b/examples/stm32wle5/src/bin/i2c.rs
@@ -7,7 +7,6 @@ use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::i2c::I2c; 8use embassy_stm32::i2c::I2c;
9use embassy_stm32::low_power::Executor; 9use embassy_stm32::low_power::Executor;
10use embassy_stm32::rtc::{Rtc, RtcConfig};
11use embassy_stm32::time::Hertz; 10use embassy_stm32::time::Hertz;
12use embassy_stm32::{bind_interrupts, i2c, peripherals}; 11use embassy_stm32::{bind_interrupts, i2c, peripherals};
13use embassy_time::{Duration, Timer}; 12use embassy_time::{Duration, Timer};
@@ -78,11 +77,6 @@ async fn async_main(_spawner: Spawner) {
78 defmt_serial::defmt_serial(SERIAL.init(uart)); 77 defmt_serial::defmt_serial(SERIAL.init(uart));
79 } 78 }
80 79
81 // give the RTC to the low_power executor...
82 let rtc_config = RtcConfig::default();
83 let rtc = Rtc::new(p.RTC, rtc_config);
84 embassy_stm32::low_power::stop_with_rtc(rtc);
85
86 info!("Hello World!"); 80 info!("Hello World!");
87 let en3v3 = embassy_stm32::gpio::Output::new( 81 let en3v3 = embassy_stm32::gpio::Output::new(
88 p.PA9, 82 p.PA9,
diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs
index 5fe98d807..5c80eb250 100644
--- a/tests/stm32/src/bin/rtc.rs
+++ b/tests/stm32/src/bin/rtc.rs
@@ -9,10 +9,14 @@ use chrono::{NaiveDate, NaiveDateTime};
9use common::*; 9use common::*;
10use defmt::assert; 10use defmt::assert;
11use embassy_executor::Spawner; 11use embassy_executor::Spawner;
12#[cfg(feature = "stop")]
13use embassy_stm32::low_power::reconfigure_rtc;
12use embassy_stm32::rcc::LsConfig; 14use embassy_stm32::rcc::LsConfig;
15#[cfg(feature = "stop")]
16use embassy_stm32::rtc::RtcTimeProvider;
17#[cfg(not(feature = "stop"))]
13use embassy_stm32::rtc::{Rtc, RtcConfig}; 18use embassy_stm32::rtc::{Rtc, RtcConfig};
14use embassy_time::Timer; 19use embassy_time::Timer;
15
16#[embassy_executor::main] 20#[embassy_executor::main]
17async fn main(_spawner: Spawner) { 21async fn main(_spawner: Spawner) {
18 let mut config = config(); 22 let mut config = config();
@@ -26,14 +30,26 @@ async fn main(_spawner: Spawner) {
26 .and_hms_opt(10, 30, 15) 30 .and_hms_opt(10, 30, 15)
27 .unwrap(); 31 .unwrap();
28 32
33 #[cfg(not(feature = "stop"))]
29 let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); 34 let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
30 35
36 #[cfg(feature = "stop")]
37 let time_provider = RtcTimeProvider::new(p.RTC);
38
39 #[cfg(not(feature = "stop"))]
31 rtc.set_datetime(now.into()).expect("datetime not set"); 40 rtc.set_datetime(now.into()).expect("datetime not set");
32 41
42 #[cfg(feature = "stop")]
43 reconfigure_rtc(|rtc| rtc.set_datetime(now.into()).expect("datetime not set"));
44
33 info!("Waiting 5 seconds"); 45 info!("Waiting 5 seconds");
34 Timer::after_millis(5000).await; 46 Timer::after_millis(5000).await;
35 47
48 #[cfg(not(feature = "stop"))]
36 let then: NaiveDateTime = rtc.now().unwrap().into(); 49 let then: NaiveDateTime = rtc.now().unwrap().into();
50 #[cfg(feature = "stop")]
51 let then: NaiveDateTime = time_provider.now().unwrap().into();
52
37 let seconds = (then - now).num_seconds(); 53 let seconds = (then - now).num_seconds();
38 54
39 info!("measured = {}", seconds); 55 info!("measured = {}", seconds);
diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs
index 73580f0f2..a9dbac676 100644
--- a/tests/stm32/src/bin/stop.rs
+++ b/tests/stm32/src/bin/stop.rs
@@ -10,9 +10,8 @@ use common::*;
10use cortex_m_rt::entry; 10use cortex_m_rt::entry;
11use embassy_executor::Spawner; 11use embassy_executor::Spawner;
12use embassy_stm32::Config; 12use embassy_stm32::Config;
13use embassy_stm32::low_power::{Executor, StopMode, stop_ready, stop_with_rtc}; 13use embassy_stm32::low_power::{Executor, StopMode, reconfigure_rtc, stop_ready};
14use embassy_stm32::rcc::LsConfig; 14use embassy_stm32::rcc::LsConfig;
15use embassy_stm32::rtc::{Rtc, RtcConfig};
16use embassy_time::Timer; 15use embassy_time::Timer;
17 16
18#[entry] 17#[entry]
@@ -57,7 +56,7 @@ async fn async_main(spawner: Spawner) {
57 config.rcc.hsi = Some(HSIPrescaler::DIV4); // 64 MHz HSI will need a /4 56 config.rcc.hsi = Some(HSIPrescaler::DIV4); // 64 MHz HSI will need a /4
58 } 57 }
59 58
60 let p = init_with_config(config); 59 let _p = init_with_config(config);
61 info!("Hello World!"); 60 info!("Hello World!");
62 61
63 let now = NaiveDate::from_ymd_opt(2020, 5, 15) 62 let now = NaiveDate::from_ymd_opt(2020, 5, 15)
@@ -65,11 +64,7 @@ async fn async_main(spawner: Spawner) {
65 .and_hms_opt(10, 30, 15) 64 .and_hms_opt(10, 30, 15)
66 .unwrap(); 65 .unwrap();
67 66
68 let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); 67 reconfigure_rtc(|rtc| rtc.set_datetime(now.into()).expect("datetime not set"));
69
70 rtc.set_datetime(now.into()).expect("datetime not set");
71
72 stop_with_rtc(rtc);
73 68
74 spawner.spawn(task_1().unwrap()); 69 spawner.spawn(task_1().unwrap());
75 spawner.spawn(task_2().unwrap()); 70 spawner.spawn(task_2().unwrap());