aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/lib.rs5
-rw-r--r--embassy-stm32/src/low_power.rs110
-rw-r--r--embassy-stm32/src/rcc/mod.rs2
-rw-r--r--embassy-stm32/src/rtc/mod.rs79
-rw-r--r--embassy-stm32/src/rtc/v2.rs108
-rw-r--r--embassy-stm32/src/time_driver.rs124
-rw-r--r--tests/stm32/Cargo.toml9
-rw-r--r--tests/stm32/src/bin/stop.rs53
8 files changed, 360 insertions, 130 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..65b93f8a4 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -3,46 +3,52 @@ use core::marker::PhantomData;
3 3
4use cortex_m::peripheral::SCB; 4use cortex_m::peripheral::SCB;
5use embassy_executor::*; 5use embassy_executor::*;
6use embassy_time::Duration;
7 6
8use crate::interrupt; 7use crate::interrupt;
9use crate::interrupt::typelevel::Interrupt; 8use crate::interrupt::typelevel::Interrupt;
10use crate::pac::EXTI; 9use crate::pac::EXTI;
11use crate::rcc::low_power_ready; 10use crate::rcc::low_power_ready;
11use crate::time_driver::{get_driver, RtcDriver};
12 12
13const THREAD_PENDER: usize = usize::MAX; 13const THREAD_PENDER: usize = usize::MAX;
14const THRESHOLD: Duration = Duration::from_millis(500);
15 14
16use crate::rtc::{Rtc, RtcInstant}; 15use crate::rtc::Rtc;
17 16
18static mut RTC: Option<&'static Rtc> = None; 17static mut EXECUTOR: Option<Executor> = None;
19 18
20foreach_interrupt! { 19foreach_interrupt! {
21 (RTC, rtc, $block:ident, WKUP, $irq:ident) => { 20 (RTC, rtc, $block:ident, WKUP, $irq:ident) => {
22 #[interrupt] 21 #[interrupt]
23 unsafe fn $irq() { 22 unsafe fn $irq() {
24 Executor::on_wakeup_irq(); 23 unsafe { EXECUTOR.as_mut().unwrap() }.on_wakeup_irq();
25 } 24 }
26 }; 25 };
27} 26}
28 27
29pub fn stop_with_rtc(rtc: &'static Rtc) { 28// pub fn timer_driver_pause_time() {
30 crate::interrupt::typelevel::RTC_WKUP::unpend(); 29// pause_time();
31 unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; 30// }
32
33 EXTI.rtsr(0).modify(|w| w.set_line(22, true));
34 EXTI.imr(0).modify(|w| w.set_line(22, true));
35
36 unsafe { RTC = Some(rtc) };
37}
38 31
39pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { 32pub fn stop_with_rtc(rtc: &'static Rtc) {
40 unsafe { RTC }.unwrap().start_wakeup_alarm(requested_duration) 33 unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
41} 34}
42 35
43pub fn stop_wakeup_alarm() -> RtcInstant { 36// pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) {
44 unsafe { RTC }.unwrap().stop_wakeup_alarm() 37// let rtc_instant = unsafe { EXECUTOR.as_mut().unwrap() }
45} 38// .rtc
39// .unwrap()
40// .start_wakeup_alarm(requested_duration);
41//
42// unsafe { EXECUTOR.as_mut().unwrap() }.last_stop = Some(rtc_instant);
43// }
44//
45// pub fn set_sleepdeep() {
46// unsafe { EXECUTOR.as_mut().unwrap() }.scb.set_sleepdeep();
47// }
48//
49// pub fn stop_wakeup_alarm() -> RtcInstant {
50// unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm()
51// }
46 52
47/// Thread mode executor, using WFE/SEV. 53/// Thread mode executor, using WFE/SEV.
48/// 54///
@@ -57,54 +63,62 @@ pub fn stop_wakeup_alarm() -> RtcInstant {
57pub struct Executor { 63pub struct Executor {
58 inner: raw::Executor, 64 inner: raw::Executor,
59 not_send: PhantomData<*mut ()>, 65 not_send: PhantomData<*mut ()>,
66 scb: SCB,
67 time_driver: &'static RtcDriver,
60} 68}
61 69
62impl Executor { 70impl Executor {
63 /// Create a new Executor. 71 /// Create a new Executor.
64 pub fn new() -> Self { 72 pub fn take() -> &'static mut Self {
65 Self { 73 unsafe {
66 inner: raw::Executor::new(THREAD_PENDER as *mut ()), 74 assert!(EXECUTOR.is_none());
67 not_send: PhantomData, 75
76 EXECUTOR = Some(Self {
77 inner: raw::Executor::new(THREAD_PENDER as *mut ()),
78 not_send: PhantomData,
79 scb: cortex_m::Peripherals::steal().SCB,
80 time_driver: get_driver(),
81 });
82
83 EXECUTOR.as_mut().unwrap()
68 } 84 }
69 } 85 }
70 86
71 unsafe fn on_wakeup_irq() { 87 unsafe fn on_wakeup_irq(&mut self) {
72 info!("on wakeup irq"); 88 trace!("low power: on wakeup irq");
73 89
74 cortex_m::asm::bkpt(); 90 self.time_driver.resume_time();
91 trace!("low power: resume time");
75 } 92 }
76 93
77 fn time_until_next_alarm(&self) -> Duration { 94 pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) {
78 Duration::from_secs(3) 95 trace!("low power: stop with rtc configured");
79 }
80 96
81 fn get_scb() -> SCB { 97 self.time_driver.set_rtc(rtc);
82 unsafe { cortex_m::Peripherals::steal() }.SCB 98
99 crate::interrupt::typelevel::RTC_WKUP::unpend();
100 unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
101
102 EXTI.rtsr(0).modify(|w| w.set_line(22, true));
103 EXTI.imr(0).modify(|w| w.set_line(22, true));
83 } 104 }
84 105
85 fn configure_pwr(&self) { 106 fn configure_pwr(&mut self) {
86 trace!("configure_pwr"); 107 trace!("low power: configure_pwr");
87 108
109 self.scb.clear_sleepdeep();
88 if !low_power_ready() { 110 if !low_power_ready() {
111 trace!("low power: configure_pwr: low power not ready");
89 return; 112 return;
90 } 113 }
91 114
92 let time_until_next_alarm = self.time_until_next_alarm(); 115 if self.time_driver.pause_time().is_err() {
93 if time_until_next_alarm < THRESHOLD { 116 trace!("low power: configure_pwr: time driver failed to pause");
94 return; 117 return;
95 } 118 }
96 119
97 trace!("low power stop required"); 120 trace!("low power: enter stop...");
98 121 self.scb.set_sleepdeep();
99 critical_section::with(|_| {
100 trace!("executor: set wakeup alarm...");
101
102 start_wakeup_alarm(time_until_next_alarm);
103
104 trace!("low power wait for rtc ready...");
105
106 Self::get_scb().set_sleepdeep();
107 });
108 } 122 }
109 123
110 /// Run the executor. 124 /// Run the executor.
@@ -126,11 +140,11 @@ impl Executor {
126 /// 140 ///
127 /// This function never returns. 141 /// This function never returns.
128 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 142 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
129 init(self.inner.spawner()); 143 init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner());
130 144
131 loop { 145 loop {
132 unsafe { 146 unsafe {
133 self.inner.poll(); 147 EXECUTOR.as_mut().unwrap().inner.poll();
134 self.configure_pwr(); 148 self.configure_pwr();
135 asm!("wfe"); 149 asm!("wfe");
136 }; 150 };
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/mod.rs b/embassy-stm32/src/rtc/mod.rs
index a6102077a..1f1abb78d 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -1,6 +1,14 @@
1//! RTC peripheral abstraction 1//! RTC peripheral abstraction
2mod datetime; 2mod datetime;
3 3
4#[cfg(feature = "low-power")]
5use core::cell::Cell;
6
7#[cfg(feature = "low-power")]
8use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
9#[cfg(feature = "low-power")]
10use embassy_sync::blocking_mutex::Mutex;
11
4pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; 12pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
5 13
6/// refer to AN4759 to compare features of RTC2 and RTC3 14/// refer to AN4759 to compare features of RTC2 and RTC3
@@ -30,9 +38,73 @@ pub enum RtcError {
30 NotRunning, 38 NotRunning,
31} 39}
32 40
41#[cfg(feature = "low-power")]
42/// Represents an instant in time that can be substracted to compute a duration
43struct RtcInstant {
44 second: u8,
45 subsecond: u16,
46}
47
48#[cfg(feature = "low-power")]
49impl RtcInstant {
50 pub fn now() -> Self {
51 let tr = RTC::regs().tr().read();
52 let tr2 = RTC::regs().tr().read();
53 let ssr = RTC::regs().ssr().read().ss();
54 let ssr2 = RTC::regs().ssr().read().ss();
55
56 let st = bcd2_to_byte((tr.st(), tr.su()));
57 let st2 = bcd2_to_byte((tr2.st(), tr2.su()));
58
59 assert!(st == st2);
60 assert!(ssr == ssr2);
61
62 let _ = RTC::regs().dr().read();
63
64 let subsecond = ssr;
65 let second = st;
66
67 // trace!("rtc: instant now: st, ssr: {}, {}", st, ssr);
68
69 Self { second, subsecond }
70 }
71}
72
73#[cfg(feature = "low-power")]
74impl core::ops::Sub for RtcInstant {
75 type Output = embassy_time::Duration;
76
77 fn sub(self, rhs: Self) -> Self::Output {
78 use embassy_time::{Duration, TICK_HZ};
79
80 let second = if self.second < rhs.second {
81 self.second + 60
82 } else {
83 self.second
84 };
85
86 // TODO: read prescaler
87
88 let self_ticks = second as u32 * 256 + (255 - self.subsecond as u32);
89 let other_ticks = rhs.second as u32 * 256 + (255 - rhs.subsecond as u32);
90 let rtc_ticks = self_ticks - other_ticks;
91
92 // trace!(
93 // "rtc: instant sub: self, other, rtc ticks: {}, {}, {}",
94 // self_ticks,
95 // other_ticks,
96 // rtc_ticks
97 // );
98
99 Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / 256u32) as u64)
100 }
101}
102
33/// RTC Abstraction 103/// RTC Abstraction
34pub struct Rtc { 104pub struct Rtc {
35 rtc_config: RtcConfig, 105 rtc_config: RtcConfig,
106 #[cfg(feature = "low-power")]
107 stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<RtcInstant>>>,
36} 108}
37 109
38#[derive(Copy, Clone, Debug, PartialEq)] 110#[derive(Copy, Clone, Debug, PartialEq)]
@@ -108,8 +180,15 @@ impl Rtc {
108 pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self { 180 pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
109 RTC::enable_peripheral_clk(); 181 RTC::enable_peripheral_clk();
110 182
183 #[cfg(not(feature = "low-power"))]
111 let mut rtc_struct = Self { rtc_config }; 184 let mut rtc_struct = Self { rtc_config };
112 185
186 #[cfg(feature = "low-power")]
187 let mut rtc_struct = Self {
188 rtc_config,
189 stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
190 };
191
113 Self::enable(); 192 Self::enable();
114 193
115 rtc_struct.configure(rtc_config); 194 rtc_struct.configure(rtc_config);
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs
index bcb127ecb..bf926f986 100644
--- a/embassy-stm32/src/rtc/v2.rs
+++ b/embassy-stm32/src/rtc/v2.rs
@@ -1,67 +1,12 @@
1use stm32_metapac::rtc::vals::{Init, Osel, Pol}; 1use stm32_metapac::rtc::vals::{Init, Osel, Pol};
2 2
3#[cfg(feature = "low-power")]
4use super::RtcInstant;
3use super::{sealed, RtcClockSource, RtcConfig}; 5use super::{sealed, RtcClockSource, RtcConfig};
4use crate::pac::rtc::Rtc; 6use crate::pac::rtc::Rtc;
5use crate::peripherals::RTC; 7use crate::peripherals::RTC;
6use crate::rtc::sealed::Instance; 8use crate::rtc::sealed::Instance;
7 9
8#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
9pub struct RtcInstant {
10 ssr: u16,
11 st: u8,
12}
13
14#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
15impl RtcInstant {
16 pub fn now() -> Self {
17 // TODO: read value twice
18 use crate::rtc::bcd2_to_byte;
19
20 let tr = RTC::regs().tr().read();
21 let tr2 = RTC::regs().tr().read();
22 let ssr = RTC::regs().ssr().read().ss();
23 let ssr2 = RTC::regs().ssr().read().ss();
24
25 let st = bcd2_to_byte((tr.st(), tr.su()));
26 let st2 = bcd2_to_byte((tr2.st(), tr2.su()));
27
28 assert!(st == st2);
29 assert!(ssr == ssr2);
30
31 let _ = RTC::regs().dr().read();
32
33 trace!("ssr: {}", ssr);
34 trace!("st: {}", st);
35
36 Self { ssr, st }
37 }
38}
39
40#[cfg(all(feature = "time", any(stm32wb, stm32f4)))]
41impl core::ops::Sub for RtcInstant {
42 type Output = embassy_time::Duration;
43
44 fn sub(self, rhs: Self) -> Self::Output {
45 use embassy_time::{Duration, TICK_HZ};
46
47 let st = if self.st < rhs.st { self.st + 60 } else { self.st };
48
49 // TODO: read prescaler
50
51 let self_ticks = st as u32 * 256 + (255 - self.ssr as u32);
52 let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32);
53 let rtc_ticks = self_ticks - other_ticks;
54
55 trace!("self, other, rtc ticks: {}, {}, {}", self_ticks, other_ticks, rtc_ticks);
56
57 Duration::from_ticks(
58 ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32)))
59 * TICK_HZ as u32) as u32
60 / 256u32) as u64,
61 )
62 }
63}
64
65#[allow(dead_code)] 10#[allow(dead_code)]
66#[derive(Clone, Copy, Debug)] 11#[derive(Clone, Copy, Debug)]
67pub(crate) enum WakeupPrescaler { 12pub(crate) enum WakeupPrescaler {
@@ -144,15 +89,10 @@ impl super::Rtc {
144 } 89 }
145 } 90 }
146 91
147 #[allow(dead_code)] 92 #[cfg(feature = "low-power")]
148 #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] 93 /// start the wakeup alarm and wtih a duration that is as close to but less than
149 /// start the wakeup alarm and return the actual duration of the alarm 94 /// the requested duration, and record the instant the wakeup alarm was started
150 /// the actual duration will be the closest value possible that is less 95 pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) {
151 /// than the requested duration.
152 ///
153 /// note: this api is exposed for testing purposes until low power is implemented.
154 /// it is not intended to be public
155 pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant {
156 use embassy_time::{Duration, TICK_HZ}; 96 use embassy_time::{Duration, TICK_HZ};
157 97
158 use crate::rcc::get_freqs; 98 use crate::rcc::get_freqs;
@@ -174,37 +114,45 @@ impl super::Rtc {
174 rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz, 114 rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz,
175 ); 115 );
176 116
177 trace!("set wakeup timer for {} ms", duration.as_millis());
178
179 self.write(false, |regs| { 117 self.write(false, |regs| {
180 regs.cr().modify(|w| w.set_wutie(true));
181
182 regs.cr().modify(|w| w.set_wute(false)); 118 regs.cr().modify(|w| w.set_wute(false));
183 regs.isr().modify(|w| w.set_wutf(false)); 119 regs.isr().modify(|w| w.set_wutf(false));
184 while !regs.isr().read().wutwf() {} 120 while !regs.isr().read().wutwf() {}
185 121
186 regs.cr().modify(|w| w.set_wucksel(prescaler.into())); 122 regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
187 regs.cr().modify(|w| w.set_wute(true)); 123 regs.cr().modify(|w| w.set_wute(true));
124 regs.cr().modify(|w| w.set_wutie(true));
188 }); 125 });
189 126
190 RtcInstant::now() 127 trace!("rtc: start wakeup alarm for {} ms", duration.as_millis());
128
129 critical_section::with(|cs| assert!(self.stop_time.borrow(cs).replace(Some(RtcInstant::now())).is_none()))
191 } 130 }
192 131
193 #[allow(dead_code)] 132 #[cfg(feature = "low-power")]
194 #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] 133 /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
195 /// stop the wakeup alarm and return the time remaining 134 /// was called, otherwise none
196 /// 135 pub(crate) fn stop_wakeup_alarm(&self) -> Option<embassy_time::Duration> {
197 /// note: this api is exposed for testing purposes until low power is implemented. 136 use crate::interrupt::typelevel::Interrupt;
198 /// it is not intended to be public 137
199 pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { 138 trace!("rtc: stop wakeup alarm...");
200 trace!("disable wakeup timer...");
201 139
202 self.write(false, |regs| { 140 self.write(false, |regs| {
141 regs.cr().modify(|w| w.set_wutie(false));
203 regs.cr().modify(|w| w.set_wute(false)); 142 regs.cr().modify(|w| w.set_wute(false));
204 regs.isr().modify(|w| w.set_wutf(false)); 143 regs.isr().modify(|w| w.set_wutf(false));
144
145 crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, true));
146 crate::interrupt::typelevel::RTC_WKUP::unpend();
205 }); 147 });
206 148
207 RtcInstant::now() 149 critical_section::with(|cs| {
150 if let Some(stop_time) = self.stop_time.borrow(cs).take() {
151 Some(RtcInstant::now() - stop_time)
152 } else {
153 None
154 }
155 })
208 } 156 }
209 157
210 #[allow(dead_code)] 158 #[allow(dead_code)]
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 2622442f4..99d423d08 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;
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,14 @@ 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>>>,
139} 143}
140 144
141const ALARM_STATE_NEW: AlarmState = AlarmState::new(); 145const ALARM_STATE_NEW: AlarmState = AlarmState::new();
@@ -144,6 +148,8 @@ embassy_time::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
144 period: AtomicU32::new(0), 148 period: AtomicU32::new(0),
145 alarm_count: AtomicU8::new(0), 149 alarm_count: AtomicU8::new(0),
146 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), 150 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]),
151 #[cfg(feature = "low-power")]
152 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
147}); 153});
148 154
149impl RtcDriver { 155impl RtcDriver {
@@ -259,6 +265,117 @@ impl RtcDriver {
259 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; 265 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
260 f(alarm.ctx.get()); 266 f(alarm.ctx.get());
261 } 267 }
268
269 #[cfg(feature = "low-power")]
270 /// Set the rtc but panic if it's already been set
271 pub(crate) fn set_rtc(&self, rtc: &'static Rtc) {
272 critical_section::with(|cs| assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()));
273 }
274
275 #[cfg(feature = "low-power")]
276 /// Compute the approximate amount of time until the next alarm
277 fn time_until_next_alarm(&self) -> embassy_time::Duration {
278 critical_section::with(|cs| {
279 let now = self.now() + 32;
280
281 embassy_time::Duration::from_ticks(
282 self.alarms
283 .borrow(cs)
284 .iter()
285 .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now))
286 .min()
287 .unwrap_or(u64::MAX),
288 )
289 })
290 }
291
292 #[cfg(feature = "low-power")]
293 /// Add the given offset to the current time
294 fn add_time(&self, offset: embassy_time::Duration) {
295 let offset = offset.as_ticks();
296 let cnt = T::regs_gp16().cnt().read().cnt() as u32;
297 let period = self.period.load(Ordering::SeqCst);
298
299 // Correct the race, if it exists
300 let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 {
301 period + 1
302 } else {
303 period
304 };
305
306 // Normalize to the full overflow
307 let period = (period / 2) * 2;
308
309 // Add the offset
310 let period = period + 2 * (offset / u16::MAX as u64) as u32;
311 let cnt = cnt + (offset % u16::MAX as u64) as u32;
312
313 let (cnt, period) = if cnt > u16::MAX as u32 {
314 (cnt - u16::MAX as u32, period + 2)
315 } else {
316 (cnt, period)
317 };
318
319 let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period };
320
321 self.period.store(period, Ordering::SeqCst);
322 T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
323
324 // Now, recompute all alarms
325 critical_section::with(|cs| {
326 for i in 0..ALARM_COUNT {
327 let alarm_handle = unsafe { AlarmHandle::new(i as u8) };
328 let alarm = self.get_alarm(cs, alarm_handle);
329
330 self.set_alarm(alarm_handle, alarm.timestamp.get());
331 }
332 })
333 }
334
335 #[cfg(feature = "low-power")]
336 /// Stop the wakeup alarm, if enabled, and add the appropriate offset
337 fn stop_wakeup_alarm(&self) {
338 critical_section::with(|cs| {
339 if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm() {
340 self.add_time(offset);
341 }
342 });
343 }
344
345 #[cfg(feature = "low-power")]
346 /// Pause the timer if ready; return err if not
347 pub(crate) fn pause_time(&self) -> Result<(), ()> {
348 /*
349 If the wakeup timer is currently running, then we need to stop it and
350 add the elapsed time to the current time
351 */
352 self.stop_wakeup_alarm();
353
354 let time_until_next_alarm = self.time_until_next_alarm();
355 if time_until_next_alarm < embassy_time::Duration::from_millis(250) {
356 Err(())
357 } else {
358 critical_section::with(|cs| {
359 self.rtc
360 .borrow(cs)
361 .get()
362 .unwrap()
363 .start_wakeup_alarm(time_until_next_alarm);
364 });
365
366 T::regs_gp16().cr1().modify(|w| w.set_cen(false));
367
368 Ok(())
369 }
370 }
371
372 #[cfg(feature = "low-power")]
373 /// Resume the timer with the given offset
374 pub(crate) fn resume_time(&self) {
375 self.stop_wakeup_alarm();
376
377 T::regs_gp16().cr1().modify(|w| w.set_cen(true));
378 }
262} 379}
263 380
264impl Driver for RtcDriver { 381impl Driver for RtcDriver {
@@ -329,6 +446,11 @@ impl Driver for RtcDriver {
329 } 446 }
330} 447}
331 448
449#[cfg(feature = "low-power")]
450pub(crate) fn get_driver() -> &'static RtcDriver {
451 &DRIVER
452}
453
332pub(crate) fn init() { 454pub(crate) fn init() {
333 DRIVER.init() 455 DRIVER.init()
334} 456}
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index 754356cb5..1f8c7373c 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -7,7 +7,7 @@ autobins = false
7 7
8[features] 8[features]
9stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill 9stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill
10stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "can", "not-gpdma", "dac-adc-pin"] # Nucleo "sdmmc" 10stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "stop", "can", "not-gpdma", "dac-adc-pin"] # Nucleo "sdmmc"
11stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma", "dac-adc-pin"] # Nucleo 11stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma", "dac-adc-pin"] # Nucleo
12stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo 12stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo
13stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo 13stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo
@@ -17,6 +17,7 @@ stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo
17stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board 17stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board
18 18
19sdmmc = [] 19sdmmc = []
20stop = ["embassy-stm32/low-power"]
20chrono = ["embassy-stm32/chrono", "dep:chrono"] 21chrono = ["embassy-stm32/chrono", "dep:chrono"]
21can = [] 22can = []
22ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] 23ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"]
@@ -47,6 +48,7 @@ micromath = "2.0.0"
47panic-probe = { version = "0.3.0", features = ["print-defmt"] } 48panic-probe = { version = "0.3.0", features = ["print-defmt"] }
48rand_core = { version = "0.6", default-features = false } 49rand_core = { version = "0.6", default-features = false }
49rand_chacha = { version = "0.3", default-features = false } 50rand_chacha = { version = "0.3", default-features = false }
51static_cell = {version = "1.1", features = ["nightly"] }
50 52
51chrono = { version = "^0.4", default-features = false, optional = true} 53chrono = { version = "^0.4", default-features = false, optional = true}
52 54
@@ -88,6 +90,11 @@ path = "src/bin/spi_dma.rs"
88required-features = [] 90required-features = []
89 91
90[[bin]] 92[[bin]]
93name = "stop"
94path = "src/bin/stop.rs"
95required-features = [ "stop", "chrono",]
96
97[[bin]]
91name = "timer" 98name = "timer"
92path = "src/bin/timer.rs" 99path = "src/bin/timer.rs"
93required-features = [] 100required-features = []
diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs
new file mode 100644
index 000000000..4a49bde9d
--- /dev/null
+++ b/tests/stm32/src/bin/stop.rs
@@ -0,0 +1,53 @@
1// required-features: stop,chrono
2
3#![no_std]
4#![no_main]
5#![feature(type_alias_impl_trait)]
6#[path = "../common.rs"]
7mod common;
8
9use chrono::NaiveDate;
10use common::*;
11use cortex_m_rt::entry;
12use embassy_executor::Spawner;
13use embassy_stm32::low_power::{stop_with_rtc, Executor};
14use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig};
15use embassy_time::{Duration, Timer};
16use static_cell::make_static;
17
18#[entry]
19fn main() -> ! {
20 let executor = Executor::take();
21 executor.run(|spawner| {
22 unwrap!(spawner.spawn(async_main(spawner)));
23 });
24}
25
26#[embassy_executor::task]
27async fn async_main(_spawner: Spawner) {
28 let mut config = config();
29
30 config.rcc.rtc = Some(RtcClockSource::LSI);
31
32 let p = embassy_stm32::init(config);
33 info!("Hello World!");
34
35 let now = NaiveDate::from_ymd_opt(2020, 5, 15)
36 .unwrap()
37 .and_hms_opt(10, 30, 15)
38 .unwrap();
39
40 let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
41
42 rtc.set_datetime(now.into()).expect("datetime not set");
43
44 let rtc = make_static!(rtc);
45
46 stop_with_rtc(rtc);
47
48 info!("Waiting 5 seconds");
49 Timer::after(Duration::from_secs(5)).await;
50
51 info!("Test OK");
52 cortex_m::asm::bkpt();
53}