aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2023-08-24 01:30:17 +0000
committerGitHub <[email protected]>2023-08-24 01:30:17 +0000
commit2a6b743b9e8dbeef6b02320b2485f3f1efe4a337 (patch)
tree6ecb235b98bb67c9b064fca2b47e035fbeeb234e
parentbed1f07c15455f7c88364a06b8c94b3c781c2f68 (diff)
parentecc305bbfe1007f9daa4d6a585dfc66f6ca69218 (diff)
Merge pull request #1814 from xoviat/rtc-lp
stm32: add low-power mod
-rw-r--r--embassy-stm32/Cargo.toml5
-rw-r--r--embassy-stm32/build.rs4
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/low_power.rs139
-rw-r--r--embassy-stm32/src/rcc/mod.rs21
-rw-r--r--embassy-stm32/src/rtc/v2.rs65
6 files changed, 186 insertions, 50 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index fe5dc443a..150014afe 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" 8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" 9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/"
10 10
11features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] 11features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time", "low-power"]
12flavors = [ 12flavors = [
13 { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, 13 { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" },
14 { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, 14 { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
@@ -38,6 +38,7 @@ embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", fea
38embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } 38embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
39embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } 39embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
40embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } 40embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
41embassy-executor = { version = "0.3.0", path = "../embassy-executor", optional = true }
41 42
42embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 43embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
43embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} 44embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
@@ -88,6 +89,8 @@ rt = ["stm32-metapac/rt"]
88defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] 89defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"]
89 90
90exti = [] 91exti = []
92low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ]
93embassy-executor = []
91 94
92## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) 95## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/)
93memory-x = ["stm32-metapac/memory-x"] 96memory-x = ["stm32-metapac/memory-x"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 8a731620f..6c364f7bb 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -356,6 +356,8 @@ fn main() {
356 } 356 }
357 fn enable() { 357 fn enable() {
358 critical_section::with(|_| { 358 critical_section::with(|_| {
359 #[cfg(feature = "low-power")]
360 crate::rcc::clock_refcount_add();
359 crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); 361 crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
360 #after_enable 362 #after_enable
361 }) 363 })
@@ -363,6 +365,8 @@ fn main() {
363 fn disable() { 365 fn disable() {
364 critical_section::with(|_| { 366 critical_section::with(|_| {
365 crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); 367 crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
368 #[cfg(feature = "low-power")]
369 crate::rcc::clock_refcount_sub();
366 }) 370 })
367 } 371 }
368 fn reset() { 372 fn reset() {
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index adb3054d8..8c87ea7d5 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -47,6 +47,8 @@ pub mod i2c;
47pub mod i2s; 47pub mod i2s;
48#[cfg(stm32wb)] 48#[cfg(stm32wb)]
49pub mod ipcc; 49pub mod ipcc;
50#[cfg(feature = "low-power")]
51pub mod low_power;
50#[cfg(quadspi)] 52#[cfg(quadspi)]
51pub mod qspi; 53pub mod qspi;
52#[cfg(rng)] 54#[cfg(rng)]
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
new file mode 100644
index 000000000..7814fa384
--- /dev/null
+++ b/embassy-stm32/src/low_power.rs
@@ -0,0 +1,139 @@
1use core::arch::asm;
2use core::marker::PhantomData;
3
4use cortex_m::peripheral::SCB;
5use embassy_executor::*;
6use embassy_time::Duration;
7
8use crate::interrupt;
9use crate::interrupt::typelevel::Interrupt;
10use crate::pac::EXTI;
11use crate::rcc::low_power_ready;
12
13const THREAD_PENDER: usize = usize::MAX;
14const THRESHOLD: Duration = Duration::from_millis(500);
15
16use crate::rtc::{Rtc, RtcInstant};
17
18static mut RTC: Option<&'static Rtc> = None;
19
20foreach_interrupt! {
21 (RTC, rtc, $block:ident, WKUP, $irq:ident) => {
22 #[interrupt]
23 unsafe fn $irq() {
24 Executor::on_wakeup_irq();
25 }
26 };
27}
28
29pub fn stop_with_rtc(rtc: &'static Rtc) {
30 crate::interrupt::typelevel::RTC_WKUP::unpend();
31 unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
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
39pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant {
40 unsafe { RTC }.unwrap().start_wakeup_alarm(requested_duration)
41}
42
43pub fn stop_wakeup_alarm() -> RtcInstant {
44 unsafe { RTC }.unwrap().stop_wakeup_alarm()
45}
46
47/// Thread mode executor, using WFE/SEV.
48///
49/// This is the simplest and most common kind of executor. It runs on
50/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
51/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
52/// is executed, to make the `WFE` exit from sleep and poll the task.
53///
54/// This executor allows for ultra low power consumption for chips where `WFE`
55/// triggers low-power sleep without extra steps. If your chip requires extra steps,
56/// you may use [`raw::Executor`] directly to program custom behavior.
57pub struct Executor {
58 inner: raw::Executor,
59 not_send: PhantomData<*mut ()>,
60}
61
62impl Executor {
63 /// Create a new Executor.
64 pub fn new() -> Self {
65 Self {
66 inner: raw::Executor::new(THREAD_PENDER as *mut ()),
67 not_send: PhantomData,
68 }
69 }
70
71 unsafe fn on_wakeup_irq() {
72 info!("on wakeup irq");
73
74 cortex_m::asm::bkpt();
75 }
76
77 fn time_until_next_alarm(&self) -> Duration {
78 Duration::from_secs(3)
79 }
80
81 fn get_scb() -> SCB {
82 unsafe { cortex_m::Peripherals::steal() }.SCB
83 }
84
85 fn configure_pwr(&self) {
86 trace!("configure_pwr");
87
88 if !low_power_ready() {
89 return;
90 }
91
92 let time_until_next_alarm = self.time_until_next_alarm();
93 if time_until_next_alarm < THRESHOLD {
94 return;
95 }
96
97 trace!("low power stop required");
98
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 }
109
110 /// Run the executor.
111 ///
112 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
113 /// this executor. Use it to spawn the initial task(s). After `init` returns,
114 /// the executor starts running the tasks.
115 ///
116 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
117 /// for example by passing it as an argument to the initial tasks.
118 ///
119 /// This function requires `&'static mut self`. This means you have to store the
120 /// Executor instance in a place where it'll live forever and grants you mutable
121 /// access. There's a few ways to do this:
122 ///
123 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
124 /// - a `static mut` (unsafe)
125 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
126 ///
127 /// This function never returns.
128 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
129 init(self.inner.spawner());
130
131 loop {
132 unsafe {
133 self.inner.poll();
134 self.configure_pwr();
135 asm!("wfe");
136 };
137 }
138 }
139}
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index ac9ae9c6a..3c75923e5 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -26,6 +26,8 @@ use crate::time::Hertz;
26#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] 26#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")]
27mod _version; 27mod _version;
28pub use _version::*; 28pub use _version::*;
29#[cfg(feature = "low-power")]
30use atomic_polyfill::{AtomicU32, Ordering};
29 31
30#[derive(Clone, Copy, Debug)] 32#[derive(Clone, Copy, Debug)]
31#[cfg_attr(feature = "defmt", derive(defmt::Format))] 33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -79,6 +81,25 @@ pub struct Clocks {
79 pub rtc: Option<Hertz>, 81 pub rtc: Option<Hertz>,
80} 82}
81 83
84#[cfg(feature = "low-power")]
85static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0);
86
87#[cfg(feature = "low-power")]
88pub fn low_power_ready() -> bool {
89 CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0
90}
91
92#[cfg(feature = "low-power")]
93pub(crate) fn clock_refcount_add() {
94 // We don't check for overflow because constructing more than u32 peripherals is unlikely
95 CLOCK_REFCOUNT.fetch_add(1, Ordering::Relaxed);
96}
97
98#[cfg(feature = "low-power")]
99pub(crate) fn clock_refcount_sub() {
100 assert!(CLOCK_REFCOUNT.fetch_sub(1, Ordering::Relaxed) != 0);
101}
102
82/// Frozen clock frequencies 103/// Frozen clock frequencies
83/// 104///
84/// The existence of this value indicates that the clock configuration can no longer be changed 105/// The existence of this value indicates that the clock configuration can no longer be changed
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs
index 53d0161d6..bcb127ecb 100644
--- a/embassy-stm32/src/rtc/v2.rs
+++ b/embassy-stm32/src/rtc/v2.rs
@@ -44,25 +44,15 @@ impl core::ops::Sub for RtcInstant {
44 fn sub(self, rhs: Self) -> Self::Output { 44 fn sub(self, rhs: Self) -> Self::Output {
45 use embassy_time::{Duration, TICK_HZ}; 45 use embassy_time::{Duration, TICK_HZ};
46 46
47 trace!("self st: {}", self.st);
48 trace!("other st: {}", rhs.st);
49
50 trace!("self ssr: {}", self.ssr);
51 trace!("other ssr: {}", rhs.ssr);
52
53 let st = if self.st < rhs.st { self.st + 60 } else { self.st }; 47 let st = if self.st < rhs.st { self.st + 60 } else { self.st };
54 48
55 trace!("self st: {}", st); 49 // TODO: read prescaler
56 50
57 let self_ticks = st as u32 * 256 + (255 - self.ssr as u32); 51 let self_ticks = st as u32 * 256 + (255 - self.ssr as u32);
58 let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); 52 let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32);
59 let rtc_ticks = self_ticks - other_ticks; 53 let rtc_ticks = self_ticks - other_ticks;
60 54
61 trace!("self ticks: {}", self_ticks); 55 trace!("self, other, rtc ticks: {}, {}, {}", self_ticks, other_ticks, rtc_ticks);
62 trace!("other ticks: {}", other_ticks);
63 trace!("rtc ticks: {}", rtc_ticks);
64
65 // TODO: read prescaler
66 56
67 Duration::from_ticks( 57 Duration::from_ticks(
68 ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) 58 ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32)))
@@ -73,7 +63,7 @@ impl core::ops::Sub for RtcInstant {
73} 63}
74 64
75#[allow(dead_code)] 65#[allow(dead_code)]
76#[derive(Clone, Copy)] 66#[derive(Clone, Copy, Debug)]
77pub(crate) enum WakeupPrescaler { 67pub(crate) enum WakeupPrescaler {
78 Div2, 68 Div2,
79 Div4, 69 Div4,
@@ -162,10 +152,9 @@ impl super::Rtc {
162 /// 152 ///
163 /// note: this api is exposed for testing purposes until low power is implemented. 153 /// note: this api is exposed for testing purposes until low power is implemented.
164 /// it is not intended to be public 154 /// it is not intended to be public
165 pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { 155 pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant {
166 use embassy_time::{Duration, TICK_HZ}; 156 use embassy_time::{Duration, TICK_HZ};
167 157
168 use crate::interrupt::typelevel::Interrupt;
169 use crate::rcc::get_freqs; 158 use crate::rcc::get_freqs;
170 159
171 let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; 160 let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64;
@@ -187,27 +176,17 @@ impl super::Rtc {
187 176
188 trace!("set wakeup timer for {} ms", duration.as_millis()); 177 trace!("set wakeup timer for {} ms", duration.as_millis());
189 178
190 RTC::regs().wpr().write(|w| w.set_key(0xca)); 179 self.write(false, |regs| {
191 RTC::regs().wpr().write(|w| w.set_key(0x53)); 180 regs.cr().modify(|w| w.set_wutie(true));
192
193 RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks));
194 181
195 RTC::regs().cr().modify(|w| { 182 regs.cr().modify(|w| w.set_wute(false));
196 w.set_wucksel(prescaler.into()); 183 regs.isr().modify(|w| w.set_wutf(false));
184 while !regs.isr().read().wutwf() {}
197 185
198 w.set_wutie(true); 186 regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
199 w.set_wute(true); 187 regs.cr().modify(|w| w.set_wute(true));
200 }); 188 });
201 189
202 if !RTC::regs().cr().read().wute() {
203 trace!("wakeup timer not enabled");
204 } else {
205 trace!("wakeup timer enabled");
206 }
207
208 crate::interrupt::typelevel::RTC_WKUP::unpend();
209 unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
210
211 RtcInstant::now() 190 RtcInstant::now()
212 } 191 }
213 192
@@ -217,26 +196,14 @@ impl super::Rtc {
217 /// 196 ///
218 /// note: this api is exposed for testing purposes until low power is implemented. 197 /// note: this api is exposed for testing purposes until low power is implemented.
219 /// it is not intended to be public 198 /// it is not intended to be public
220 pub fn stop_wakeup_alarm() -> RtcInstant { 199 pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant {
221 use crate::interrupt::typelevel::Interrupt;
222
223 crate::interrupt::typelevel::RTC_WKUP::disable();
224
225 trace!("disable wakeup timer..."); 200 trace!("disable wakeup timer...");
226 201
227 RTC::regs().cr().modify(|w| { 202 self.write(false, |regs| {
228 w.set_wute(false); 203 regs.cr().modify(|w| w.set_wute(false));
204 regs.isr().modify(|w| w.set_wutf(false));
229 }); 205 });
230 206
231 trace!("wait for wakeup timer stop...");
232
233 // Wait for the wakeup timer to stop
234 // while !RTC::regs().isr().read().wutf() {}
235 //
236 // RTC::regs().isr().modify(|w| w.set_wutf(false));
237
238 trace!("wait for wakeup timer stop...done");
239
240 RtcInstant::now() 207 RtcInstant::now()
241 } 208 }
242 209
@@ -388,7 +355,7 @@ impl super::Rtc {
388 }) 355 })
389 } 356 }
390 357
391 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R 358 pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R
392 where 359 where
393 F: FnOnce(&crate::pac::rtc::Rtc) -> R, 360 F: FnOnce(&crate::pac::rtc::Rtc) -> R,
394 { 361 {