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