aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/low_power.rs126
-rw-r--r--embassy-stm32/src/rtc/v2.rs55
-rw-r--r--embassy-stm32/src/time_driver.rs107
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;
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}; 12use crate::time_driver::{get_driver, RtcDriver};
13 13
14const THREAD_PENDER: usize = usize::MAX; 14const THREAD_PENDER: usize = usize::MAX;
15const THRESHOLD: Duration = Duration::from_millis(500);
16 15
17use crate::rtc::{Rtc, RtcInstant}; 16use crate::rtc::Rtc;
18 17
19static mut EXECUTOR: Option<Executor> = None; 18static mut EXECUTOR: Option<Executor> = None;
20 19
@@ -27,20 +26,30 @@ foreach_interrupt! {
27 }; 26 };
28} 27}
29 28
29// pub fn timer_driver_pause_time() {
30// pause_time();
31// }
32
30pub fn stop_with_rtc(rtc: &'static Rtc) { 33pub fn stop_with_rtc(rtc: &'static Rtc) {
31 unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) 34 unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
32} 35}
33 36
34pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { 37// pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) {
35 unsafe { EXECUTOR.as_mut().unwrap() } 38// let rtc_instant = unsafe { EXECUTOR.as_mut().unwrap() }
36 .rtc 39// .rtc
37 .unwrap() 40// .unwrap()
38 .start_wakeup_alarm(requested_duration) 41// .start_wakeup_alarm(requested_duration);
39} 42//
40 43// unsafe { EXECUTOR.as_mut().unwrap() }.last_stop = Some(rtc_instant);
41pub fn stop_wakeup_alarm() -> RtcInstant { 44// }
42 unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm() 45//
43} 46// pub fn set_sleepdeep() {
47// unsafe { EXECUTOR.as_mut().unwrap() }.scb.set_sleepdeep();
48// }
49//
50// pub fn stop_wakeup_alarm() -> RtcInstant {
51// unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm()
52// }
44 53
45/// Thread mode executor, using WFE/SEV. 54/// Thread mode executor, using WFE/SEV.
46/// 55///
@@ -56,15 +65,15 @@ pub struct Executor {
56 inner: raw::Executor, 65 inner: raw::Executor,
57 not_send: PhantomData<*mut ()>, 66 not_send: PhantomData<*mut ()>,
58 scb: SCB, 67 scb: SCB,
59 pub(self) rtc: Option<&'static Rtc>, 68 time_driver: &'static RtcDriver,
60 stop_time: embassy_time::Duration, 69 stop_time: embassy_time::Duration,
61 next_alarm: embassy_time::Duration, 70 next_alarm: embassy_time::Duration,
62 last_stop: Option<crate::rtc::RtcInstant>, 71 wfe: u8,
63} 72}
64 73
65impl Executor { 74impl Executor {
66 /// Create a new Executor. 75 /// Create a new Executor.
67 pub fn new() -> &'static mut Self { 76 pub fn take() -> &'static mut Self {
68 unsafe { 77 unsafe {
69 assert!(EXECUTOR.is_none()); 78 assert!(EXECUTOR.is_none());
70 79
@@ -72,10 +81,10 @@ impl Executor {
72 inner: raw::Executor::new(THREAD_PENDER as *mut ()), 81 inner: raw::Executor::new(THREAD_PENDER as *mut ()),
73 not_send: PhantomData, 82 not_send: PhantomData,
74 scb: cortex_m::Peripherals::steal().SCB, 83 scb: cortex_m::Peripherals::steal().SCB,
75 rtc: None, 84 time_driver: get_driver(),
76 stop_time: Duration::from_ticks(0), 85 stop_time: Duration::from_ticks(0),
77 next_alarm: Duration::from_ticks(u64::MAX), 86 next_alarm: Duration::from_ticks(u64::MAX),
78 last_stop: None, 87 wfe: 0,
79 }); 88 });
80 89
81 EXECUTOR.as_mut().unwrap() 90 EXECUTOR.as_mut().unwrap()
@@ -83,9 +92,31 @@ impl Executor {
83 } 92 }
84 93
85 unsafe fn on_wakeup_irq(&mut self) { 94 unsafe fn on_wakeup_irq(&mut self) {
86 trace!("low power: one wakekup irq"); 95 trace!("low power: on wakeup irq");
96
97 if crate::pac::RTC.isr().read().wutf() {
98 trace!("low power: wutf set");
99 } else {
100 trace!("low power: wutf not set");
101 }
102
103 self.time_driver.resume_time();
104 trace!("low power: resume time");
87 105
88 self.rtc.unwrap().clear_wakeup_alarm(); 106 crate::interrupt::typelevel::RTC_WKUP::disable();
107
108 // cortex_m::asm::bkpt();
109
110 // let time_elasped = self.rtc.unwrap().stop_wakeup_alarm() - self.last_stop.take().unwrap();
111 //
112 // trace!("low power: {} ms elapsed", time_elasped.as_millis());
113 //
114 // resume_time(time_elasped);
115 // trace!("low power: resume time");
116 //
117 // self.scb.clear_sleepdeep();
118
119 // cortex_m::asm::bkpt();
89 // Self::get_scb().set_sleeponexit(); 120 // Self::get_scb().set_sleeponexit();
90 // 121 //
91 // return; 122 // return;
@@ -110,48 +141,33 @@ impl Executor {
110 } 141 }
111 142
112 pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { 143 pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) {
113 assert!(self.rtc.is_none()); 144 trace!("low power: stop with rtc configured");
145
146 self.time_driver.set_rtc(rtc);
114 147
115 crate::interrupt::typelevel::RTC_WKUP::unpend(); 148 crate::interrupt::typelevel::RTC_WKUP::unpend();
116 unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; 149 unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
117 150
118 EXTI.rtsr(0).modify(|w| w.set_line(22, true)); 151 EXTI.rtsr(0).modify(|w| w.set_line(22, true));
119 EXTI.imr(0).modify(|w| w.set_line(22, true)); 152 EXTI.imr(0).modify(|w| w.set_line(22, true));
120
121 self.rtc = Some(rtc);
122 } 153 }
123 154
124 fn configure_pwr(&self) { 155 fn configure_pwr(&mut self) {
125 // defeat the borrow checker 156 trace!("low power: configure_pwr");
126 let s = unsafe { EXECUTOR.as_mut().unwrap() };
127 157
128 // trace!("configure_pwr"); 158 self.scb.clear_sleepdeep();
129 // 159 if !low_power_ready() {
130 // if !low_power_ready() { 160 trace!("low power: configure_pwr: low power not ready");
131 // trace!("configure_pwr: low power not ready"); 161 return;
132 // return; 162 }
133 // } 163
134 // 164 if self.time_driver.pause_time().is_err() {
135 // let time_until_next_alarm = time_until_next_alarm(); 165 trace!("low power: configure_pwr: time driver failed to pause");
136 // if time_until_next_alarm < THRESHOLD { 166 return;
137 // trace!("configure_pwr: not enough time until next alarm"); 167 }
138 // return; 168
139 // } 169 trace!("low power: enter stop...");
140 // 170 self.scb.set_sleepdeep();
141 // unsafe {
142 // NEXT_ALARM = time_until_next_alarm;
143 // if RTC_INSTANT.is_none() {
144 // RTC_INSTANT = Some(start_wakeup_alarm(time_until_next_alarm))
145 // }
146 // };
147 //
148 // // return;
149 //
150 // pause_time();
151 //
152 // trace!("enter stop...");
153 //
154 // Self::get_scb().set_sleepdeep();
155 } 171 }
156 172
157 /// Run the executor. 173 /// Run the executor.
@@ -173,11 +189,11 @@ impl Executor {
173 /// 189 ///
174 /// This function never returns. 190 /// This function never returns.
175 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 191 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
176 init(self.inner.spawner()); 192 init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner());
177 193
178 loop { 194 loop {
179 unsafe { 195 unsafe {
180 self.inner.poll(); 196 EXECUTOR.as_mut().unwrap().inner.poll();
181 self.configure_pwr(); 197 self.configure_pwr();
182 asm!("wfe"); 198 asm!("wfe");
183 }; 199 };
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 {
30 30
31 let _ = RTC::regs().dr().read(); 31 let _ = RTC::regs().dr().read();
32 32
33 trace!("rtc: instant now: st, ssr: {}, {}", st, ssr);
34
33 Self { ssr, st } 35 Self { ssr, st }
34 } 36 }
35} 37}
@@ -146,6 +148,21 @@ impl super::Rtc {
146 } 148 }
147 } 149 }
148 150
151 // pub(crate) fn clear_wakeup_alarm(&self) {
152 // use crate::interrupt::typelevel::Interrupt;
153 //
154 // self.write(false, |regs| {
155 // regs.cr().modify(|w| w.set_wutie(false));
156 //
157 // regs.isr().modify(|w| w.set_wutf(false));
158 // crate::pac::PWR.cr1().modify(|w| w.set_cwuf(false));
159 // crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, false));
160 // crate::interrupt::typelevel::RTC_WKUP::unpend();
161 //
162 // regs.cr().modify(|w| w.set_wutie(true));
163 // });
164 // }
165
149 #[allow(dead_code)] 166 #[allow(dead_code)]
150 #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] 167 #[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
151 /// start the wakeup alarm and return the actual duration of the alarm 168 /// start the wakeup alarm and return the actual duration of the alarm
@@ -157,6 +174,7 @@ impl super::Rtc {
157 pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant { 174 pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant {
158 use embassy_time::{Duration, TICK_HZ}; 175 use embassy_time::{Duration, TICK_HZ};
159 176
177 use crate::interrupt::typelevel::Interrupt;
160 use crate::rcc::get_freqs; 178 use crate::rcc::get_freqs;
161 179
162 let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; 180 let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64;
@@ -176,28 +194,40 @@ impl super::Rtc {
176 rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz, 194 rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz,
177 ); 195 );
178 196
179 trace!("rtc: set wakeup timer for {} ms", duration.as_millis());
180
181 self.write(false, |regs| { 197 self.write(false, |regs| {
182 // regs.cr().modify(|w| w.set_wutie(true));
183
184 regs.cr().modify(|w| w.set_wute(false)); 198 regs.cr().modify(|w| w.set_wute(false));
185 regs.isr().modify(|w| w.set_wutf(false)); 199 regs.isr().modify(|w| w.set_wutf(false));
186 while !regs.isr().read().wutwf() {} 200 while !regs.isr().read().wutwf() {}
187 201
188 regs.cr().modify(|w| w.set_wucksel(prescaler.into())); 202 regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
189 regs.cr().modify(|w| w.set_wute(true)); 203 regs.cr().modify(|w| w.set_wute(true));
190 });
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)); 204 regs.cr().modify(|w| w.set_wutie(true));
199 }); 205 });
200 206
207 trace!("rtc: start wakeup alarm for {} ms", duration.as_millis());
208
209 // self.write(false, |regs| {
210 // regs.cr().modify(|w| w.set_wutie(false));
211 //
212 // regs.isr().modify(|w| w.set_wutf(false));
213 //
214 // regs.cr().modify(|w| w.set_wutie(true));
215 // });
216 //
217 // trace!("rtc: clear wuf...");
218 // crate::pac::PWR.cr1().modify(|w| w.set_cwuf(true));
219 // while crate::pac::PWR.csr1().read().wuf() {}
220 // trace!("rtc: clear wuf...done");
221 //
222 // crate::pac::PWR
223 // .cr1()
224 // .modify(|w| w.set_pdds(crate::pac::pwr::vals::Pdds::STANDBY_MODE));
225 //
226 // // crate::pac::PWR.cr1().modify(|w| w.set_lpds(true));
227 //
228 // // crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, true));
229 // crate::interrupt::typelevel::RTC_WKUP::unpend();
230
201 RtcInstant::now() 231 RtcInstant::now()
202 } 232 }
203 233
@@ -211,6 +241,7 @@ impl super::Rtc {
211 trace!("rtc: stop wakeup alarm..."); 241 trace!("rtc: stop wakeup alarm...");
212 242
213 self.write(false, |regs| { 243 self.write(false, |regs| {
244 regs.cr().modify(|w| w.set_wutie(false));
214 regs.cr().modify(|w| w.set_wute(false)); 245 regs.cr().modify(|w| w.set_wute(false));
215 regs.isr().modify(|w| w.set_wutf(false)); 246 regs.isr().modify(|w| w.set_wutf(false));
216 }); 247 });
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;
14use crate::interrupt::typelevel::Interrupt; 14use crate::interrupt::typelevel::Interrupt;
15use crate::pac::timer::vals; 15use crate::pac::timer::vals;
16use crate::rcc::sealed::RccPeripheral; 16use crate::rcc::sealed::RccPeripheral;
17#[cfg(feature = "low-power")]
18use crate::rtc::{Rtc, RtcInstant};
17use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance}; 19use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance};
18use crate::{interrupt, peripherals}; 20use crate::{interrupt, peripherals};
19 21
@@ -130,12 +132,16 @@ impl AlarmState {
130 } 132 }
131} 133}
132 134
133struct RtcDriver { 135pub(crate) struct RtcDriver {
134 /// Number of 2^15 periods elapsed since boot. 136 /// Number of 2^15 periods elapsed since boot.
135 period: AtomicU32, 137 period: AtomicU32,
136 alarm_count: AtomicU8, 138 alarm_count: AtomicU8,
137 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. 139 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
138 alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>, 140 alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
141 #[cfg(feature = "low-power")]
142 rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>,
143 #[cfg(feature = "low-power")]
144 stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<RtcInstant>>>,
139} 145}
140 146
141const ALARM_STATE_NEW: AlarmState = AlarmState::new(); 147const ALARM_STATE_NEW: AlarmState = AlarmState::new();
@@ -144,6 +150,10 @@ embassy_time::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
144 period: AtomicU32::new(0), 150 period: AtomicU32::new(0),
145 alarm_count: AtomicU8::new(0), 151 alarm_count: AtomicU8::new(0),
146 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), 152 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]),
153 #[cfg(feature = "low-power")]
154 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
155 #[cfg(feature = "low-power")]
156 stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
147}); 157});
148 158
149impl RtcDriver { 159impl RtcDriver {
@@ -261,8 +271,14 @@ impl RtcDriver {
261 } 271 }
262 272
263 #[cfg(feature = "low-power")] 273 #[cfg(feature = "low-power")]
274 /// Set the rtc but panic if it's already been set
275 pub(crate) fn set_rtc(&self, rtc: &'static Rtc) {
276 critical_section::with(|cs| assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()));
277 }
278
279 #[cfg(feature = "low-power")]
264 /// Compute the approximate amount of time until the next alarm 280 /// Compute the approximate amount of time until the next alarm
265 pub(crate) fn time_until_next_alarm(&self) -> embassy_time::Duration { 281 fn time_until_next_alarm(&self) -> embassy_time::Duration {
266 critical_section::with(|cs| { 282 critical_section::with(|cs| {
267 let now = self.now() + 32; 283 let now = self.now() + 32;
268 284
@@ -278,14 +294,8 @@ impl RtcDriver {
278 } 294 }
279 295
280 #[cfg(feature = "low-power")] 296 #[cfg(feature = "low-power")]
281 /// Pause the timer 297 /// Add the given offset to the current time
282 pub(crate) fn pause_time(&self) { 298 fn add_time(&self, offset: embassy_time::Duration) {
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(); 299 let offset = offset.as_ticks();
290 let cnt = T::regs_gp16().cnt().read().cnt() as u32; 300 let cnt = T::regs_gp16().cnt().read().cnt() as u32;
291 let period = self.period.load(Ordering::SeqCst); 301 let period = self.period.load(Ordering::SeqCst);
@@ -315,6 +325,66 @@ impl RtcDriver {
315 self.period.store(period, Ordering::SeqCst); 325 self.period.store(period, Ordering::SeqCst);
316 T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); 326 T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
317 327
328 // Now, recompute all alarms
329 critical_section::with(|cs| {
330 for i in 0..ALARM_COUNT {
331 let alarm_handle = unsafe { AlarmHandle::new(i as u8) };
332 let alarm = self.get_alarm(cs, alarm_handle);
333
334 self.set_alarm(alarm_handle, alarm.timestamp.get());
335 }
336 })
337 }
338
339 #[cfg(feature = "low-power")]
340 /// Stop the wakeup alarm, if enabled, and add the appropriate offset
341 fn stop_wakeup_alarm(&self) {
342 critical_section::with(|cs| {
343 if let Some(stop_time) = self.stop_time.borrow(cs).take() {
344 let current_time = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm();
345 self.add_time(current_time - stop_time);
346 }
347 });
348 }
349
350 #[cfg(feature = "low-power")]
351 /// Pause the timer if ready; return err if not
352 pub(crate) fn pause_time(&self) -> Result<(), ()> {
353 /*
354 If the wakeup timer is currently running, then we need to stop it and
355 add the elapsed time to the current time
356 */
357 self.stop_wakeup_alarm();
358
359 let time_until_next_alarm = self.time_until_next_alarm();
360 if time_until_next_alarm < embassy_time::Duration::from_millis(250) {
361 Err(())
362 } else {
363 critical_section::with(|cs| {
364 assert!(self
365 .stop_time
366 .borrow(cs)
367 .replace(Some(
368 self.rtc
369 .borrow(cs)
370 .get()
371 .unwrap()
372 .start_wakeup_alarm(time_until_next_alarm)
373 ))
374 .is_none());
375 });
376
377 T::regs_gp16().cr1().modify(|w| w.set_cen(false));
378
379 Ok(())
380 }
381 }
382
383 #[cfg(feature = "low-power")]
384 /// Resume the timer with the given offset
385 pub(crate) fn resume_time(&self) {
386 self.stop_wakeup_alarm();
387
318 T::regs_gp16().cr1().modify(|w| w.set_cen(true)); 388 T::regs_gp16().cr1().modify(|w| w.set_cen(true));
319 } 389 }
320} 390}
@@ -388,21 +458,8 @@ impl Driver for RtcDriver {
388} 458}
389 459
390#[cfg(feature = "low-power")] 460#[cfg(feature = "low-power")]
391/// Compute the approximate amount of time until the next alarm 461pub(crate) fn get_driver() -> &'static RtcDriver {
392pub(crate) fn time_until_next_alarm() -> embassy_time::Duration { 462 &DRIVER
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} 463}
407 464
408pub(crate) fn init() { 465pub(crate) fn init() {