aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-04 07:16:27 -0600
committerGitHub <[email protected]>2025-11-04 07:16:27 -0600
commitf8f7820ab98cc1f49d7b2a623566f836354916bc (patch)
tree8a26ba0e703d82ec70c0cc3487a8ebc56273d1b6
parentf85d709f140838cf0161206df01dd77abbac9c5d (diff)
parent51f8aeaa0dd2359a669a3c38d194a8a70f26441f (diff)
Merge branch 'main' into i2c
-rw-r--r--embassy-executor/CHANGELOG.md3
-rw-r--r--embassy-executor/Cargo.toml9
-rw-r--r--embassy-executor/build.rs5
-rw-r--r--embassy-executor/src/arch/cortex_ar.rs7
-rw-r--r--embassy-rp/CHANGELOG.md2
-rw-r--r--embassy-rp/src/pio_programs/onewire.rs18
-rw-r--r--embassy-rp/src/pio_programs/ws2812.rs123
-rw-r--r--embassy-stm32/CHANGELOG.md12
-rw-r--r--embassy-stm32/src/adc/mod.rs20
-rw-r--r--embassy-stm32/src/adc/ringbuffered.rs179
-rw-r--r--embassy-stm32/src/adc/ringbuffered_v2.rs432
-rw-r--r--embassy-stm32/src/adc/v2.rs134
-rw-r--r--embassy-stm32/src/adc/v3.rs173
-rw-r--r--embassy-stm32/src/can/fd/config.rs13
-rw-r--r--embassy-stm32/src/can/fdcan.rs15
-rw-r--r--embassy-stm32/src/i2c/v2.rs15
-rw-r--r--embassy-stm32/src/low_power.rs112
-rw-r--r--embassy-stm32/src/rcc/l.rs43
-rw-r--r--embassy-stm32/src/rcc/mod.rs2
-rw-r--r--embassy-stm32/src/rtc/low_power.rs11
-rw-r--r--embassy-stm32/src/rtc/v2.rs2
-rw-r--r--embassy-stm32/src/rtc/v3.rs6
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs10
-rw-r--r--embassy-stm32/src/time_driver.rs50
-rw-r--r--embassy-stm32/src/vrefbuf/mod.rs8
-rw-r--r--examples/rp/src/bin/pio_onewire.rs2
-rw-r--r--examples/rp/src/bin/pio_onewire_parasite.rs2
-rw-r--r--examples/rp235x/src/bin/pio_onewire.rs2
-rw-r--r--examples/rp235x/src/bin/pio_onewire_parasite.rs2
-rw-r--r--examples/stm32f4/src/bin/adc_dma.rs29
-rw-r--r--examples/stm32g0/src/bin/onewire_ds18b20.rs2
-rw-r--r--examples/stm32h5/src/bin/stop.rs3
-rw-r--r--examples/stm32l4/src/bin/adc_dma.rs51
-rw-r--r--examples/stm32l5/src/bin/stop.rs3
-rw-r--r--examples/stm32wle5/.cargo/config.toml9
-rw-r--r--examples/stm32wle5/Cargo.toml38
-rw-r--r--examples/stm32wle5/README.md52
-rw-r--r--examples/stm32wle5/build.rs5
-rw-r--r--examples/stm32wle5/src/bin/adc.rs100
-rw-r--r--examples/stm32wle5/src/bin/blinky.rs90
-rw-r--r--examples/stm32wle5/src/bin/button_exti.rs91
-rw-r--r--examples/stm32wle5/src/bin/i2c.rs110
-rw-r--r--examples/stm32wle5/stm32wle5.code-workspace13
-rw-r--r--tests/stm32/src/bin/stop.rs4
44 files changed, 1478 insertions, 534 deletions
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md
index 47a8ae995..5fbb8cf13 100644
--- a/embassy-executor/CHANGELOG.md
+++ b/embassy-executor/CHANGELOG.md
@@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13- Upgraded rtos-trace 13- Upgraded rtos-trace
14- Added optional "highest priority" scheduling 14- Added optional "highest priority" scheduling
15- Added optional "earliest deadline first" EDF scheduling 15- Added optional "earliest deadline first" EDF scheduling
16- Bump `cortex-ar` to v0.3 16- Migrate `cortex-ar` to `aarch32-cpu`. The feature name `arch-cortex-ar` remains the same and
17 legacy ARM architectures are not supported.
17 18
18## 0.9.1 - 2025-08-31 19## 0.9.1 - 2025-08-31
19 20
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index e500833c0..d3e5b241a 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -102,7 +102,7 @@ portable-atomic = { version = "1.5", optional = true }
102cortex-m = { version = "0.7.6", optional = true } 102cortex-m = { version = "0.7.6", optional = true }
103 103
104# arch-cortex-ar dependencies 104# arch-cortex-ar dependencies
105cortex-ar = { version = "0.3", optional = true } 105aarch32-cpu = { version = "0.1", optional = true }
106 106
107# arch-wasm dependencies 107# arch-wasm dependencies
108wasm-bindgen = { version = "0.2.82", optional = true } 108wasm-bindgen = { version = "0.2.82", optional = true }
@@ -130,7 +130,7 @@ nightly = ["embassy-executor-macros/nightly"]
130## Enable defmt logging 130## Enable defmt logging
131defmt = ["dep:defmt"] 131defmt = ["dep:defmt"]
132 132
133## Enable log logging 133## Enable log logging
134log = ["dep:log"] 134log = ["dep:log"]
135 135
136# Enables turbo wakers, which requires patching core. Not surfaced in the docs by default due to 136# Enables turbo wakers, which requires patching core. Not surfaced in the docs by default due to
@@ -145,7 +145,7 @@ arch-std = ["_arch"]
145## Cortex-M 145## Cortex-M
146arch-cortex-m = ["_arch", "dep:cortex-m"] 146arch-cortex-m = ["_arch", "dep:cortex-m"]
147## Cortex-A/R 147## Cortex-A/R
148arch-cortex-ar = ["_arch", "dep:cortex-ar"] 148arch-cortex-ar = ["_arch", "dep:aarch32-cpu", "dep:arm-targets"]
149## RISC-V 32 149## RISC-V 32
150arch-riscv32 = ["_arch"] 150arch-riscv32 = ["_arch"]
151## WASM 151## WASM
@@ -182,3 +182,6 @@ scheduler-priority = []
182## Enable the embassy_time_driver dependency. 182## Enable the embassy_time_driver dependency.
183## This can unlock extra APIs, for example for the `sheduler-deadline` 183## This can unlock extra APIs, for example for the `sheduler-deadline`
184embassy-time-driver = ["dep:embassy-time-driver"] 184embassy-time-driver = ["dep:embassy-time-driver"]
185
186[build-dependencies]
187arm-targets = { version = "0.4", optional = true }
diff --git a/embassy-executor/build.rs b/embassy-executor/build.rs
index 37becde3e..36e23a632 100644
--- a/embassy-executor/build.rs
+++ b/embassy-executor/build.rs
@@ -4,4 +4,9 @@ mod common;
4fn main() { 4fn main() {
5 let mut rustc_cfgs = common::CfgSet::new(); 5 let mut rustc_cfgs = common::CfgSet::new();
6 common::set_target_cfgs(&mut rustc_cfgs); 6 common::set_target_cfgs(&mut rustc_cfgs);
7
8 // This is used to exclude legacy architecture support. The raw executor needs to be used for
9 // those architectures because SEV/WFE are not supported.
10 #[cfg(feature = "arch-cortex-ar")]
11 arm_targets::process();
7} 12}
diff --git a/embassy-executor/src/arch/cortex_ar.rs b/embassy-executor/src/arch/cortex_ar.rs
index a9be3d323..ce572738a 100644
--- a/embassy-executor/src/arch/cortex_ar.rs
+++ b/embassy-executor/src/arch/cortex_ar.rs
@@ -1,3 +1,6 @@
1#[cfg(arm_profile = "legacy")]
2compile_error!("`arch-cortex-ar` does not support the legacy ARM profile, WFE/SEV are not available.");
3
1#[cfg(feature = "executor-interrupt")] 4#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-cortex-ar`."); 5compile_error!("`executor-interrupt` is not supported with `arch-cortex-ar`.");
3 6
@@ -10,7 +13,7 @@ fn __pender(context: *mut ()) {
10 #[cfg(feature = "executor-thread")] 13 #[cfg(feature = "executor-thread")]
11 // Try to make Rust optimize the branching away if we only use thread mode. 14 // Try to make Rust optimize the branching away if we only use thread mode.
12 if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER { 15 if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER {
13 cortex_ar::asm::sev(); 16 aarch32_cpu::asm::sev();
14 return; 17 return;
15 } 18 }
16} 19}
@@ -23,7 +26,7 @@ mod thread {
23 26
24 use core::marker::PhantomData; 27 use core::marker::PhantomData;
25 28
26 use cortex_ar::asm::wfe; 29 use aarch32_cpu::asm::wfe;
27 pub use embassy_executor_macros::main_cortex_ar as main; 30 pub use embassy_executor_macros::main_cortex_ar as main;
28 31
29 use crate::{Spawner, raw}; 32 use crate::{Spawner, raw};
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index 57ec13658..3b3cb5351 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15- add `wait_for_alarm` and `alarm_scheduled` methods to rtc module ([#4216](https://github.com/embassy-rs/embassy/pull/4216)) 15- add `wait_for_alarm` and `alarm_scheduled` methods to rtc module ([#4216](https://github.com/embassy-rs/embassy/pull/4216))
16- rp235x: use msplim for stack guard instead of MPU 16- rp235x: use msplim for stack guard instead of MPU
17- Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705)) 17- Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705))
18- Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824))
19- Add PIO::Ws2812 color order support
18 20
19## 0.8.0 - 2025-08-26 21## 0.8.0 - 2025-08-26
20 22
diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs
index 09babc229..3adab3b79 100644
--- a/embassy-rp/src/pio_programs/onewire.rs
+++ b/embassy-rp/src/pio_programs/onewire.rs
@@ -163,7 +163,11 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
163 163
164 /// Write bytes to the onewire bus 164 /// Write bytes to the onewire bus
165 pub async fn write_bytes(&mut self, data: &[u8]) { 165 pub async fn write_bytes(&mut self, data: &[u8]) {
166 unsafe { self.sm.set_y(u32::MAX as u32) }; 166 unsafe {
167 self.sm.set_enable(false);
168 self.sm.set_y(u32::MAX as u32);
169 self.sm.set_enable(true);
170 }
167 let (rx, tx) = self.sm.rx_tx(); 171 let (rx, tx) = self.sm.rx_tx();
168 for b in data { 172 for b in data {
169 tx.wait_push(*b as u32).await; 173 tx.wait_push(*b as u32).await;
@@ -175,7 +179,11 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
175 179
176 /// Write bytes to the onewire bus, then apply a strong pullup 180 /// Write bytes to the onewire bus, then apply a strong pullup
177 pub async fn write_bytes_pullup(&mut self, data: &[u8], pullup_time: embassy_time::Duration) { 181 pub async fn write_bytes_pullup(&mut self, data: &[u8], pullup_time: embassy_time::Duration) {
178 unsafe { self.sm.set_y(data.len() as u32 * 8 - 1) }; 182 unsafe {
183 self.sm.set_enable(false);
184 self.sm.set_y(data.len() as u32 * 8 - 1);
185 self.sm.set_enable(true);
186 };
179 let (rx, tx) = self.sm.rx_tx(); 187 let (rx, tx) = self.sm.rx_tx();
180 for b in data { 188 for b in data {
181 tx.wait_push(*b as u32).await; 189 tx.wait_push(*b as u32).await;
@@ -195,7 +203,11 @@ impl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
195 203
196 /// Read bytes from the onewire bus 204 /// Read bytes from the onewire bus
197 pub async fn read_bytes(&mut self, data: &mut [u8]) { 205 pub async fn read_bytes(&mut self, data: &mut [u8]) {
198 unsafe { self.sm.set_y(u32::MAX as u32) }; 206 unsafe {
207 self.sm.set_enable(false);
208 self.sm.set_y(u32::MAX as u32);
209 self.sm.set_enable(true);
210 };
199 let (rx, tx) = self.sm.rx_tx(); 211 let (rx, tx) = self.sm.rx_tx();
200 for b in data { 212 for b in data {
201 // Write all 1's so that we can read what the device responds 213 // Write all 1's so that we can read what the device responds
diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs
index e6851b1a6..0b6035316 100644
--- a/embassy-rp/src/pio_programs/ws2812.rs
+++ b/embassy-rp/src/pio_programs/ws2812.rs
@@ -1,4 +1,4 @@
1//! [ws2812](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) 1//! [ws2812](https://www.sparkfun.com/categories/tags/ws2812)
2 2
3use embassy_time::Timer; 3use embassy_time::Timer;
4use fixed::types::U24F8; 4use fixed::types::U24F8;
@@ -16,6 +16,54 @@ const T2: u8 = 5; // data bit
16const T3: u8 = 3; // stop bit 16const T3: u8 = 3; // stop bit
17const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; 17const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
18 18
19/// Color orders for WS2812B, type RGB8
20pub trait RgbColorOrder {
21 /// Pack an 8-bit RGB color into a u32
22 fn pack(color: RGB8) -> u32;
23}
24
25/// Green, Red, Blue order is the common default for WS2812B
26pub struct Grb;
27impl RgbColorOrder for Grb {
28 /// Pack an 8-bit RGB color into a u32 in GRB order
29 fn pack(color: RGB8) -> u32 {
30 (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8)
31 }
32}
33
34/// Red, Green, Blue is used by some WS2812B implementations
35pub struct Rgb;
36impl RgbColorOrder for Rgb {
37 /// Pack an 8-bit RGB color into a u32 in RGB order
38 fn pack(color: RGB8) -> u32 {
39 (u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8)
40 }
41}
42
43/// Color orders RGBW strips
44pub trait RgbwColorOrder {
45 /// Pack an RGB+W color into a u32
46 fn pack(color: RGBW<u8>) -> u32;
47}
48
49/// Green, Red, Blue, White order is the common default for RGBW strips
50pub struct Grbw;
51impl RgbwColorOrder for Grbw {
52 /// Pack an RGB+W color into a u32 in GRBW order
53 fn pack(color: RGBW<u8>) -> u32 {
54 (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8) | u32::from(color.a.0)
55 }
56}
57
58/// Red, Green, Blue, White order
59pub struct Rgbw;
60impl RgbwColorOrder for Rgbw {
61 /// Pack an RGB+W color into a u32 in RGBW order
62 fn pack(color: RGBW<u8>) -> u32 {
63 (u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8) | u32::from(color.a.0)
64 }
65}
66
19/// This struct represents a ws2812 program loaded into pio instruction memory. 67/// This struct represents a ws2812 program loaded into pio instruction memory.
20pub struct PioWs2812Program<'a, PIO: Instance> { 68pub struct PioWs2812Program<'a, PIO: Instance> {
21 prg: LoadedProgram<'a, PIO>, 69 prg: LoadedProgram<'a, PIO>,
@@ -52,15 +100,37 @@ impl<'a, PIO: Instance> PioWs2812Program<'a, PIO> {
52 100
53/// Pio backed RGB ws2812 driver 101/// Pio backed RGB ws2812 driver
54/// Const N is the number of ws2812 leds attached to this pin 102/// Const N is the number of ws2812 leds attached to this pin
55pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize> { 103pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER>
104where
105 ORDER: RgbColorOrder,
106{
56 dma: Peri<'d, AnyChannel>, 107 dma: Peri<'d, AnyChannel>,
57 sm: StateMachine<'d, P, S>, 108 sm: StateMachine<'d, P, S>,
109 _order: core::marker::PhantomData<ORDER>,
58} 110}
59 111
60impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> { 112impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N, Grb> {
61 /// Configure a pio state machine to use the loaded ws2812 program. 113 /// Configure a pio state machine to use the loaded ws2812 program.
114 /// Uses the default GRB order.
62 pub fn new( 115 pub fn new(
63 pio: &mut Common<'d, P>, 116 pio: &mut Common<'d, P>,
117 sm: StateMachine<'d, P, S>,
118 dma: Peri<'d, impl Channel>,
119 pin: Peri<'d, impl PioPin>,
120 program: &PioWs2812Program<'d, P>,
121 ) -> Self {
122 Self::with_color_order(pio, sm, dma, pin, program)
123 }
124}
125
126impl<'d, P: Instance, const S: usize, const N: usize, ORDER> PioWs2812<'d, P, S, N, ORDER>
127where
128 ORDER: RgbColorOrder,
129{
130 /// Configure a pio state machine to use the loaded ws2812 program.
131 /// Uses the specified color order.
132 pub fn with_color_order(
133 pio: &mut Common<'d, P>,
64 mut sm: StateMachine<'d, P, S>, 134 mut sm: StateMachine<'d, P, S>,
65 dma: Peri<'d, impl Channel>, 135 dma: Peri<'d, impl Channel>,
66 pin: Peri<'d, impl PioPin>, 136 pin: Peri<'d, impl PioPin>,
@@ -93,7 +163,11 @@ impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> {
93 sm.set_config(&cfg); 163 sm.set_config(&cfg);
94 sm.set_enable(true); 164 sm.set_enable(true);
95 165
96 Self { dma: dma.into(), sm } 166 Self {
167 dma: dma.into(),
168 sm,
169 _order: core::marker::PhantomData,
170 }
97 } 171 }
98 172
99 /// Write a buffer of [smart_leds::RGB8] to the ws2812 string 173 /// Write a buffer of [smart_leds::RGB8] to the ws2812 string
@@ -101,8 +175,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> {
101 // Precompute the word bytes from the colors 175 // Precompute the word bytes from the colors
102 let mut words = [0u32; N]; 176 let mut words = [0u32; N];
103 for i in 0..N { 177 for i in 0..N {
104 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); 178 words[i] = ORDER::pack(colors[i]);
105 words[i] = word;
106 } 179 }
107 180
108 // DMA transfer 181 // DMA transfer
@@ -115,15 +188,37 @@ impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> {
115/// Pio backed RGBW ws2812 driver 188/// Pio backed RGBW ws2812 driver
116/// This version is intended for ws2812 leds with 4 addressable lights 189/// This version is intended for ws2812 leds with 4 addressable lights
117/// Const N is the number of ws2812 leds attached to this pin 190/// Const N is the number of ws2812 leds attached to this pin
118pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize> { 191pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER>
192where
193 ORDER: RgbwColorOrder,
194{
119 dma: Peri<'d, AnyChannel>, 195 dma: Peri<'d, AnyChannel>,
120 sm: StateMachine<'d, P, S>, 196 sm: StateMachine<'d, P, S>,
197 _order: core::marker::PhantomData<ORDER>,
121} 198}
122 199
123impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N> { 200impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N, Grbw> {
124 /// Configure a pio state machine to use the loaded ws2812 program. 201 /// Configure a pio state machine to use the loaded ws2812 program.
202 /// Uses the default GRBW color order
125 pub fn new( 203 pub fn new(
126 pio: &mut Common<'d, P>, 204 pio: &mut Common<'d, P>,
205 sm: StateMachine<'d, P, S>,
206 dma: Peri<'d, impl Channel>,
207 pin: Peri<'d, impl PioPin>,
208 program: &PioWs2812Program<'d, P>,
209 ) -> Self {
210 Self::with_color_order(pio, sm, dma, pin, program)
211 }
212}
213
214impl<'d, P: Instance, const S: usize, const N: usize, ORDER> RgbwPioWs2812<'d, P, S, N, ORDER>
215where
216 ORDER: RgbwColorOrder,
217{
218 /// Configure a pio state machine to use the loaded ws2812 program.
219 /// Uses the specified color order
220 pub fn with_color_order(
221 pio: &mut Common<'d, P>,
127 mut sm: StateMachine<'d, P, S>, 222 mut sm: StateMachine<'d, P, S>,
128 dma: Peri<'d, impl Channel>, 223 dma: Peri<'d, impl Channel>,
129 pin: Peri<'d, impl PioPin>, 224 pin: Peri<'d, impl PioPin>,
@@ -156,7 +251,11 @@ impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N>
156 sm.set_config(&cfg); 251 sm.set_config(&cfg);
157 sm.set_enable(true); 252 sm.set_enable(true);
158 253
159 Self { dma: dma.into(), sm } 254 Self {
255 dma: dma.into(),
256 sm,
257 _order: core::marker::PhantomData,
258 }
160 } 259 }
161 260
162 /// Write a buffer of [smart_leds::RGBW] to the ws2812 string 261 /// Write a buffer of [smart_leds::RGBW] to the ws2812 string
@@ -164,11 +263,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N>
164 // Precompute the word bytes from the colors 263 // Precompute the word bytes from the colors
165 let mut words = [0u32; N]; 264 let mut words = [0u32; N];
166 for i in 0..N { 265 for i in 0..N {
167 let word = (u32::from(colors[i].g) << 24) 266 words[i] = ORDER::pack(colors[i]);
168 | (u32::from(colors[i].r) << 16)
169 | (u32::from(colors[i].b) << 8)
170 | u32::from(colors[i].a.0);
171 words[i] = word;
172 } 267 }
173 268
174 // DMA transfer 269 // DMA transfer
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 8b215f13b..3d6a41f14 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -5,10 +5,6 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8<!-- next-header -->
9### [Unreleased]
10
11* **Fix(stm32h5):** Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696)
12## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
13 9
14- fix flash erase on L4 & L5 10- fix flash erase on L4 & L5
@@ -35,10 +31,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
35- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) 31- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668))
36- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options 32- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options
37- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer 33- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer
34- fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696)
38- change: timer: added output compare values 35- change: timer: added output compare values
39- feat: timer: add ability to set master mode 36- feat: timer: add ability to set master mode
37- fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723)
40- fix: usart: fix race condition in ringbuffered usart 38- fix: usart: fix race condition in ringbuffered usart
41- feat: Add I2C MultiMaster (Slave) support for I2C v1 39- feat: Add I2C MultiMaster (Slave) support for I2C v1
40- feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821))
41- low-power: update rtc api to allow reconfig
42- adc: consolidate ringbuffer
43- feat: Added RTC low-power support for STM32WLEx ([#4716](https://github.com/embassy-rs/embassy/pull/4716))
44- fix: Correct STM32WBA VREFBUFTRIM values
42 45
43## 0.4.0 - 2025-08-26 46## 0.4.0 - 2025-08-26
44 47
@@ -51,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
51- chore: Updated stm32-metapac and stm32-data dependencies 54- chore: Updated stm32-metapac and stm32-data dependencies
52- feat: stm32/adc/v3: allow DMA reads to loop through enable channels 55- feat: stm32/adc/v3: allow DMA reads to loop through enable channels
53- fix: Fix XSPI not disabling alternate bytes when they were previously enabled 56- fix: Fix XSPI not disabling alternate bytes when they were previously enabled
57- feat: stm32/adc/v3: added support for Continuous DMA configuration
54- fix: Fix stm32h7rs init when using external flash via XSPI 58- fix: Fix stm32h7rs init when using external flash via XSPI
55- feat: Add Adc::new_with_clock() to configure analog clock 59- feat: Add Adc::new_with_clock() to configure analog clock
56- feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) 60- feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923))
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 22ed8295f..ea7341f75 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -87,14 +87,18 @@ pub(crate) trait SealedAdcChannel<T> {
87/// Performs a busy-wait delay for a specified number of microseconds. 87/// Performs a busy-wait delay for a specified number of microseconds.
88#[allow(unused)] 88#[allow(unused)]
89pub(crate) fn blocking_delay_us(us: u32) { 89pub(crate) fn blocking_delay_us(us: u32) {
90 #[cfg(feature = "time")] 90 cfg_if::cfg_if! {
91 embassy_time::block_for(embassy_time::Duration::from_micros(us as u64)); 91 // this does strange things on stm32wlx in low power mode depending on exactly when it's called
92 #[cfg(not(feature = "time"))] 92 // as in sometimes 15 us (1 tick) would take > 20 seconds.
93 { 93 if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] {
94 let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; 94 let duration = embassy_time::Duration::from_micros(us as u64);
95 let us = us as u64; 95 embassy_time::block_for(duration);
96 let cycles = freq * us / 1_000_000; 96 } else {
97 cortex_m::asm::delay(cycles as u32); 97 let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64;
98 let us = us as u64;
99 let cycles = freq * us / 1_000_000;
100 cortex_m::asm::delay(cycles as u32);
101 }
98 } 102 }
99} 103}
100 104
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs
new file mode 100644
index 000000000..1f384efb5
--- /dev/null
+++ b/embassy-stm32/src/adc/ringbuffered.rs
@@ -0,0 +1,179 @@
1use core::marker::PhantomData;
2use core::sync::atomic::{Ordering, compiler_fence};
3
4#[allow(unused_imports)]
5use embassy_hal_internal::Peri;
6
7use crate::adc::Adc;
8#[allow(unused_imports)]
9use crate::adc::{Instance, RxDma};
10#[allow(unused_imports)]
11use crate::dma::{ReadableRingBuffer, TransferOptions};
12use crate::rcc;
13
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub struct OverrunError;
16
17pub struct RingBufferedAdc<'d, T: Instance> {
18 _phantom: PhantomData<T>,
19 ring_buf: ReadableRingBuffer<'d, u16>,
20}
21
22impl<'d, T: Instance> RingBufferedAdc<'d, T> {
23 pub(crate) fn new(dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> Self {
24 //dma side setup
25 let opts = TransferOptions {
26 half_transfer_ir: true,
27 circular: true,
28 ..Default::default()
29 };
30
31 // Safety: we forget the struct before this function returns.
32 let request = dma.request();
33
34 let ring_buf =
35 unsafe { ReadableRingBuffer::new(dma, request, T::regs().dr().as_ptr() as *mut u16, dma_buf, opts) };
36
37 Self {
38 _phantom: PhantomData,
39 ring_buf,
40 }
41 }
42
43 /// Turns on ADC if it is not already turned on and starts continuous DMA transfer.
44 pub fn start(&mut self) {
45 compiler_fence(Ordering::SeqCst);
46 self.ring_buf.start();
47
48 Adc::<T>::start();
49 }
50
51 pub fn stop(&mut self) {
52 Adc::<T>::stop();
53
54 self.ring_buf.request_pause();
55
56 compiler_fence(Ordering::SeqCst);
57 }
58
59 pub fn clear(&mut self) {
60 self.ring_buf.clear();
61 }
62
63 /// Reads measurements from the DMA ring buffer.
64 ///
65 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
66 /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes.
67 ///
68 /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `sequence`.
69 /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled.
70 /// For example if 2 channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`.
71 ///
72 /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly running tasks
73 /// Otherwise, you'll see constant Overrun errors occuring, this means that you're sampling too quickly for the task to handle, and you may need to increase the buffer size.
74 /// Example:
75 /// ```rust,ignore
76 /// const DMA_BUF_LEN: usize = 120;
77 /// use embassy_stm32::adc::{Adc, AdcChannel}
78 ///
79 /// let mut adc = Adc::new(p.ADC1);
80 /// let mut adc_pin0 = p.PA0.degrade_adc();
81 /// let mut adc_pin1 = p.PA1.degrade_adc();
82 /// let adc_dma_buf = [0u16; DMA_BUF_LEN];
83 ///
84 /// let mut ring_buffered_adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(
85 /// p.DMA2_CH0,
86 /// adc_dma_buf, [
87 /// (&mut *adc_pin0, SampleTime::CYCLES160_5),
88 /// (&mut *adc_pin1, SampleTime::CYCLES160_5),
89 /// ].into_iter());
90 ///
91 ///
92 /// let mut measurements = [0u16; DMA_BUF_LEN / 2];
93 /// loop {
94 /// match ring_buffered_adc.read(&mut measurements).await {
95 /// Ok(_) => {
96 /// defmt::info!("adc1: {}", measurements);
97 /// }
98 /// Err(e) => {
99 /// defmt::warn!("Error: {:?}", e);
100 /// }
101 /// }
102 /// }
103 /// ```
104 ///
105 ///
106 /// [`teardown_adc`]: #method.teardown_adc
107 /// [`start_continuous_sampling`]: #method.start_continuous_sampling
108 pub async fn read(&mut self, measurements: &mut [u16]) -> Result<usize, OverrunError> {
109 assert_eq!(
110 self.ring_buf.capacity() / 2,
111 measurements.len(),
112 "Buffer size must be half the size of the ring buffer"
113 );
114
115 if !self.ring_buf.is_running() {
116 self.start();
117 }
118
119 #[cfg(adc_v2)]
120 {
121 // Clear overrun flag if set.
122 if T::regs().sr().read().ovr() {
123 self.stop();
124
125 return Err(OverrunError);
126 }
127 }
128
129 self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError)
130 }
131
132 /// Read bytes that are readily available in the ring buffer.
133 /// If no bytes are currently available in the buffer the call waits until the some
134 /// bytes are available (at least one byte and at most half the buffer size)
135 ///
136 /// Background receive is started if `start_continuous_sampling()` has not been previously called.
137 ///
138 /// Receive in the background is terminated if an error is returned.
139 /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`.
140 pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> {
141 if !self.ring_buf.is_running() {
142 self.start();
143 }
144
145 #[cfg(adc_v2)]
146 {
147 // Clear overrun flag if set.
148 if T::regs().sr().read().ovr() {
149 self.stop();
150
151 return Err(OverrunError);
152 }
153 }
154 loop {
155 match self.ring_buf.read(buf) {
156 Ok((0, _)) => {}
157 Ok((len, _)) => {
158 return Ok(len);
159 }
160 Err(_) => {
161 self.stop();
162
163 return Err(OverrunError);
164 }
165 }
166 }
167 }
168}
169
170impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
171 fn drop(&mut self) {
172 Adc::<T>::teardown_adc();
173
174 compiler_fence(Ordering::SeqCst);
175
176 self.ring_buf.request_pause();
177 rcc::disable::<T>();
178 }
179}
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs
deleted file mode 100644
index 9b2e5b8fe..000000000
--- a/embassy-stm32/src/adc/ringbuffered_v2.rs
+++ /dev/null
@@ -1,432 +0,0 @@
1use core::marker::PhantomData;
2use core::mem;
3use core::sync::atomic::{Ordering, compiler_fence};
4
5use stm32_metapac::adc::vals::SampleTime;
6
7use crate::adc::{Adc, AdcChannel, Instance, RxDma};
8use crate::dma::{Priority, ReadableRingBuffer, TransferOptions};
9use crate::pac::adc::vals;
10use crate::{Peri, rcc};
11
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct OverrunError;
14
15fn clear_interrupt_flags(r: crate::pac::adc::Adc) {
16 r.sr().modify(|regs| {
17 regs.set_eoc(false);
18 regs.set_ovr(false);
19 });
20}
21
22#[derive(PartialOrd, PartialEq, Debug, Clone, Copy)]
23pub enum Sequence {
24 One,
25 Two,
26 Three,
27 Four,
28 Five,
29 Six,
30 Seven,
31 Eight,
32 Nine,
33 Ten,
34 Eleven,
35 Twelve,
36 Thirteen,
37 Fourteen,
38 Fifteen,
39 Sixteen,
40}
41
42impl From<Sequence> for u8 {
43 fn from(s: Sequence) -> u8 {
44 match s {
45 Sequence::One => 0,
46 Sequence::Two => 1,
47 Sequence::Three => 2,
48 Sequence::Four => 3,
49 Sequence::Five => 4,
50 Sequence::Six => 5,
51 Sequence::Seven => 6,
52 Sequence::Eight => 7,
53 Sequence::Nine => 8,
54 Sequence::Ten => 9,
55 Sequence::Eleven => 10,
56 Sequence::Twelve => 11,
57 Sequence::Thirteen => 12,
58 Sequence::Fourteen => 13,
59 Sequence::Fifteen => 14,
60 Sequence::Sixteen => 15,
61 }
62 }
63}
64
65impl From<u8> for Sequence {
66 fn from(val: u8) -> Self {
67 match val {
68 0 => Sequence::One,
69 1 => Sequence::Two,
70 2 => Sequence::Three,
71 3 => Sequence::Four,
72 4 => Sequence::Five,
73 5 => Sequence::Six,
74 6 => Sequence::Seven,
75 7 => Sequence::Eight,
76 8 => Sequence::Nine,
77 9 => Sequence::Ten,
78 10 => Sequence::Eleven,
79 11 => Sequence::Twelve,
80 12 => Sequence::Thirteen,
81 13 => Sequence::Fourteen,
82 14 => Sequence::Fifteen,
83 15 => Sequence::Sixteen,
84 _ => panic!("Invalid sequence number"),
85 }
86 }
87}
88
89pub struct RingBufferedAdc<'d, T: Instance> {
90 _phantom: PhantomData<T>,
91 ring_buf: ReadableRingBuffer<'d, u16>,
92}
93
94impl<'d, T: Instance> Adc<'d, T> {
95 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
96 ///
97 /// The `dma_buf` should be large enough to prevent DMA buffer overrun.
98 /// The length of the `dma_buf` should be a multiple of the ADC channel count.
99 /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
100 ///
101 /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
102 /// It is critical to call `read` frequently to prevent DMA buffer overrun.
103 ///
104 /// [`read`]: #method.read
105 pub fn into_ring_buffered(self, dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> RingBufferedAdc<'d, T> {
106 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
107
108 let opts: crate::dma::TransferOptions = TransferOptions {
109 half_transfer_ir: true,
110 priority: Priority::VeryHigh,
111 ..Default::default()
112 };
113
114 // Safety: we forget the struct before this function returns.
115 let rx_src = T::regs().dr().as_ptr() as *mut u16;
116 let request = dma.request();
117
118 let ring_buf = unsafe { ReadableRingBuffer::new(dma, request, rx_src, dma_buf, opts) };
119
120 // Don't disable the clock
121 mem::forget(self);
122
123 RingBufferedAdc {
124 _phantom: PhantomData,
125 ring_buf,
126 }
127 }
128}
129
130impl<'d, T: Instance> RingBufferedAdc<'d, T> {
131 fn is_on() -> bool {
132 T::regs().cr2().read().adon()
133 }
134
135 fn stop_adc() {
136 T::regs().cr2().modify(|reg| {
137 reg.set_adon(false);
138 });
139 }
140
141 fn start_adc() {
142 T::regs().cr2().modify(|reg| {
143 reg.set_adon(true);
144 });
145 }
146
147 /// Sets the channel sample time
148 ///
149 /// ## SAFETY:
150 /// - ADON == 0 i.e ADC must not be enabled when this is called.
151 unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
152 if ch <= 9 {
153 T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
154 } else {
155 T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
156 }
157 }
158
159 fn set_channels_sample_time(&mut self, ch: &[u8], sample_time: SampleTime) {
160 let ch_iter = ch.iter();
161 for idx in ch_iter {
162 unsafe {
163 Self::set_channel_sample_time(*idx, sample_time);
164 }
165 }
166 }
167
168 pub fn set_sample_sequence(
169 &mut self,
170 sequence: Sequence,
171 channel: &mut impl AdcChannel<T>,
172 sample_time: SampleTime,
173 ) {
174 let was_on = Self::is_on();
175 if !was_on {
176 Self::start_adc();
177 }
178
179 // Check the sequence is long enough
180 T::regs().sqr1().modify(|r| {
181 let prev: Sequence = r.l().into();
182 if prev < sequence {
183 let new_l: Sequence = sequence;
184 trace!("Setting sequence length from {:?} to {:?}", prev as u8, new_l as u8);
185 r.set_l(sequence.into())
186 } else {
187 r.set_l(prev.into())
188 }
189 });
190
191 // Set this GPIO as an analog input.
192 channel.setup();
193
194 // Set the channel in the right sequence field.
195 match sequence {
196 Sequence::One => T::regs().sqr3().modify(|w| w.set_sq(0, channel.channel())),
197 Sequence::Two => T::regs().sqr3().modify(|w| w.set_sq(1, channel.channel())),
198 Sequence::Three => T::regs().sqr3().modify(|w| w.set_sq(2, channel.channel())),
199 Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())),
200 Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())),
201 Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())),
202 Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(0, channel.channel())),
203 Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(1, channel.channel())),
204 Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(2, channel.channel())),
205 Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(3, channel.channel())),
206 Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(4, channel.channel())),
207 Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(5, channel.channel())),
208 Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(0, channel.channel())),
209 Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(1, channel.channel())),
210 Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(2, channel.channel())),
211 Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(3, channel.channel())),
212 };
213
214 if !was_on {
215 Self::stop_adc();
216 }
217
218 self.set_channels_sample_time(&[channel.channel()], sample_time);
219
220 Self::start_adc();
221 }
222
223 /// Turns on ADC if it is not already turned on and starts continuous DMA transfer.
224 pub fn start(&mut self) -> Result<(), OverrunError> {
225 self.setup_adc();
226 self.ring_buf.clear();
227
228 Ok(())
229 }
230
231 fn stop(&mut self, err: OverrunError) -> Result<usize, OverrunError> {
232 self.teardown_adc();
233 Err(err)
234 }
235
236 /// Stops DMA transfer.
237 /// It does not turn off ADC.
238 /// Calling `start` restarts continuous DMA transfer.
239 ///
240 /// [`start`]: #method.start
241 pub fn teardown_adc(&mut self) {
242 // Stop the DMA transfer
243 self.ring_buf.request_pause();
244
245 let r = T::regs();
246
247 // Stop ADC
248 r.cr2().modify(|reg| {
249 // Stop ADC
250 reg.set_swstart(false);
251 // Stop DMA
252 reg.set_dma(false);
253 });
254
255 r.cr1().modify(|w| {
256 // Disable interrupt for end of conversion
257 w.set_eocie(false);
258 // Disable interrupt for overrun
259 w.set_ovrie(false);
260 });
261
262 clear_interrupt_flags(r);
263
264 compiler_fence(Ordering::SeqCst);
265 }
266
267 fn setup_adc(&mut self) {
268 compiler_fence(Ordering::SeqCst);
269
270 self.ring_buf.start();
271
272 let r = T::regs();
273
274 // Enable ADC
275 let was_on = Self::is_on();
276 if !was_on {
277 r.cr2().modify(|reg| {
278 reg.set_adon(false);
279 reg.set_swstart(false);
280 });
281 }
282
283 // Clear all interrupts
284 r.sr().modify(|regs| {
285 regs.set_eoc(false);
286 regs.set_ovr(false);
287 regs.set_strt(false);
288 });
289
290 r.cr1().modify(|w| {
291 // Enable interrupt for end of conversion
292 w.set_eocie(true);
293 // Enable interrupt for overrun
294 w.set_ovrie(true);
295 // Scanning converisons of multiple channels
296 w.set_scan(true);
297 // Continuous conversion mode
298 w.set_discen(false);
299 });
300
301 r.cr2().modify(|w| {
302 // Enable DMA mode
303 w.set_dma(true);
304 // Enable continuous conversions
305 w.set_cont(true);
306 // DMA requests are issues as long as DMA=1 and data are converted.
307 w.set_dds(vals::Dds::CONTINUOUS);
308 // EOC flag is set at the end of each conversion.
309 w.set_eocs(vals::Eocs::EACH_CONVERSION);
310 });
311
312 // Begin ADC conversions
313 T::regs().cr2().modify(|reg| {
314 reg.set_adon(true);
315 reg.set_swstart(true);
316 });
317
318 super::blocking_delay_us(3);
319 }
320
321 /// Read bytes that are readily available in the ring buffer.
322 /// If no bytes are currently available in the buffer the call waits until the some
323 /// bytes are available (at least one byte and at most half the buffer size)
324 ///
325 /// Background receive is started if `start()` has not been previously called.
326 ///
327 /// Receive in the background is terminated if an error is returned.
328 /// It must then manually be started again by calling `start()` or by re-calling `read()`.
329 pub fn blocking_read<const N: usize>(&mut self, buf: &mut [u16; N]) -> Result<usize, OverrunError> {
330 let r = T::regs();
331
332 // Start background receive if it was not already started
333 if !r.cr2().read().dma() {
334 self.start()?;
335 }
336
337 // Clear overrun flag if set.
338 if r.sr().read().ovr() {
339 return self.stop(OverrunError);
340 }
341
342 loop {
343 match self.ring_buf.read(buf) {
344 Ok((0, _)) => {}
345 Ok((len, _)) => {
346 return Ok(len);
347 }
348 Err(_) => {
349 return self.stop(OverrunError);
350 }
351 }
352 }
353 }
354
355 /// Reads measurements from the DMA ring buffer.
356 ///
357 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
358 /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes.
359 ///
360 /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `set_sample_sequence`.
361 /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled.
362 /// For example if 3 channels are sampled `measurements` contain: `[sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3 sq0 sq1 sq3..]`.
363 ///
364 /// If an error is returned, it indicates a DMA overrun, and the process must be restarted by calling `start` or `read` again.
365 ///
366 /// By default, the ADC fills the DMA buffer as quickly as possible. To control the sample rate, call `teardown_adc` after each readout, and then start the DMA again at the desired interval.
367 /// Note that even if using `teardown_adc` to control the sample rate, with each call to `read`, measurements equivalent to half the size of the DMA buffer are still collected.
368 ///
369 /// Example:
370 /// ```rust,ignore
371 /// const DMA_BUF_LEN: usize = 120;
372 /// let adc_dma_buf = [0u16; DMA_BUF_LEN];
373 /// let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_dma_buf);
374 ///
375 /// adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112);
376 /// adc.set_sample_sequence(Sequence::Two, &mut p.PA1, SampleTime::CYCLES112);
377 /// adc.set_sample_sequence(Sequence::Three, &mut p.PA2, SampleTime::CYCLES112);
378 ///
379 /// let mut measurements = [0u16; DMA_BUF_LEN / 2];
380 /// loop {
381 /// match adc.read(&mut measurements).await {
382 /// Ok(_) => {
383 /// defmt::info!("adc1: {}", measurements);
384 /// // Only needed to manually control sample rate.
385 /// adc.teardown_adc();
386 /// }
387 /// Err(e) => {
388 /// defmt::warn!("Error: {:?}", e);
389 /// // DMA overrun, next call to `read` restarts ADC.
390 /// }
391 /// }
392 ///
393 /// // Manually control sample rate.
394 /// Timer::after_millis(100).await;
395 /// }
396 /// ```
397 ///
398 ///
399 /// [`set_sample_sequence`]: #method.set_sample_sequence
400 /// [`teardown_adc`]: #method.teardown_adc
401 /// [`start`]: #method.start
402 pub async fn read<const N: usize>(&mut self, measurements: &mut [u16; N]) -> Result<usize, OverrunError> {
403 assert_eq!(
404 self.ring_buf.capacity() / 2,
405 N,
406 "Buffer size must be half the size of the ring buffer"
407 );
408
409 let r = T::regs();
410
411 // Start background receive if it was not already started
412 if !r.cr2().read().dma() {
413 self.start()?;
414 }
415
416 // Clear overrun flag if set.
417 if r.sr().read().ovr() {
418 return self.stop(OverrunError);
419 }
420 match self.ring_buf.read_exact(measurements).await {
421 Ok(len) => Ok(len),
422 Err(_) => self.stop(OverrunError),
423 }
424 }
425}
426
427impl<T: Instance> Drop for RingBufferedAdc<'_, T> {
428 fn drop(&mut self) {
429 self.teardown_adc();
430 rcc::disable::<T>();
431 }
432}
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index 93ec78548..90c6294d2 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -1,11 +1,22 @@
1use core::mem;
2use core::sync::atomic::{Ordering, compiler_fence};
3
1use super::blocking_delay_us; 4use super::blocking_delay_us;
2use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; 5use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel};
6use crate::pac::adc::vals;
3use crate::peripherals::ADC1; 7use crate::peripherals::ADC1;
4use crate::time::Hertz; 8use crate::time::Hertz;
5use crate::{Peri, rcc}; 9use crate::{Peri, rcc};
6 10
7mod ringbuffered_v2; 11mod ringbuffered;
8pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; 12pub use ringbuffered::RingBufferedAdc;
13
14fn clear_interrupt_flags(r: crate::pac::adc::Adc) {
15 r.sr().modify(|regs| {
16 regs.set_eoc(false);
17 regs.set_ovr(false);
18 });
19}
9 20
10/// Default VREF voltage used for sample conversion to millivolts. 21/// Default VREF voltage used for sample conversion to millivolts.
11pub const VREF_DEFAULT_MV: u32 = 3300; 22pub const VREF_DEFAULT_MV: u32 = 3300;
@@ -112,6 +123,98 @@ where
112 } 123 }
113 } 124 }
114 125
126 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
127 ///
128 /// The `dma_buf` should be large enough to prevent DMA buffer overrun.
129 /// The length of the `dma_buf` should be a multiple of the ADC channel count.
130 /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
131 ///
132 /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
133 /// It is critical to call `read` frequently to prevent DMA buffer overrun.
134 ///
135 /// [`read`]: #method.read
136 pub fn into_ring_buffered<'a>(
137 self,
138 dma: Peri<'d, impl RxDma<T>>,
139 dma_buf: &'d mut [u16],
140 sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>,
141 ) -> RingBufferedAdc<'d, T> {
142 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
143
144 T::regs().cr2().modify(|reg| {
145 reg.set_adon(true);
146 });
147
148 // Check the sequence is long enough
149 T::regs().sqr1().modify(|r| {
150 r.set_l((sequence.len() - 1).try_into().unwrap());
151 });
152
153 for (i, (channel, sample_time)) in sequence.enumerate() {
154 // Set this GPIO as an analog input.
155 channel.setup();
156
157 // Set the channel in the right sequence field.
158 T::regs().sqr3().modify(|w| w.set_sq(i, channel.channel()));
159
160 Self::set_channel_sample_time(channel.channel(), sample_time);
161 }
162
163 compiler_fence(Ordering::SeqCst);
164
165 let r = T::regs();
166
167 // Clear all interrupts
168 r.sr().modify(|regs| {
169 regs.set_eoc(false);
170 regs.set_ovr(false);
171 regs.set_strt(false);
172 });
173
174 r.cr1().modify(|w| {
175 // Enable interrupt for end of conversion
176 w.set_eocie(true);
177 // Enable interrupt for overrun
178 w.set_ovrie(true);
179 // Scanning converisons of multiple channels
180 w.set_scan(true);
181 // Continuous conversion mode
182 w.set_discen(false);
183 });
184
185 r.cr2().modify(|w| {
186 // Enable DMA mode
187 w.set_dma(true);
188 // Enable continuous conversions
189 w.set_cont(true);
190 // DMA requests are issues as long as DMA=1 and data are converted.
191 w.set_dds(vals::Dds::CONTINUOUS);
192 // EOC flag is set at the end of each conversion.
193 w.set_eocs(vals::Eocs::EACH_CONVERSION);
194 });
195
196 // Don't disable the clock
197 mem::forget(self);
198
199 RingBufferedAdc::new(dma, dma_buf)
200 }
201
202 pub(super) fn start() {
203 // Begin ADC conversions
204 T::regs().cr2().modify(|reg| {
205 reg.set_adon(true);
206 reg.set_swstart(true);
207 });
208 }
209
210 pub(super) fn stop() {
211 // Stop ADC
212 T::regs().cr2().modify(|reg| {
213 // Stop ADC
214 reg.set_swstart(false);
215 });
216 }
217
115 pub fn set_sample_time(&mut self, sample_time: SampleTime) { 218 pub fn set_sample_time(&mut self, sample_time: SampleTime) {
116 self.sample_time = sample_time; 219 self.sample_time = sample_time;
117 } 220 }
@@ -198,6 +301,31 @@ where
198 T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); 301 T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
199 } 302 }
200 } 303 }
304
305 pub(super) fn teardown_adc() {
306 let r = T::regs();
307
308 // Stop ADC
309 r.cr2().modify(|reg| {
310 // Stop ADC
311 reg.set_swstart(false);
312 // Stop ADC
313 reg.set_adon(false);
314 // Stop DMA
315 reg.set_dma(false);
316 });
317
318 r.cr1().modify(|w| {
319 // Disable interrupt for end of conversion
320 w.set_eocie(false);
321 // Disable interrupt for overrun
322 w.set_ovrie(false);
323 });
324
325 clear_interrupt_flags(r);
326
327 compiler_fence(Ordering::SeqCst);
328 }
201} 329}
202 330
203impl<'d, T: Instance> Drop for Adc<'d, T> { 331impl<'d, T: Instance> Drop for Adc<'d, T> {
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 47632263b..62c2da557 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -12,6 +12,13 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc};
12use super::{ 12use super::{
13 Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, 13 Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us,
14}; 14};
15
16#[cfg(any(adc_v3, adc_g0, adc_u0))]
17mod ringbuffered;
18
19#[cfg(any(adc_v3, adc_g0, adc_u0))]
20use ringbuffered::RingBufferedAdc;
21
15use crate::dma::Transfer; 22use crate::dma::Transfer;
16use crate::{Peri, pac, rcc}; 23use crate::{Peri, pac, rcc};
17 24
@@ -174,6 +181,38 @@ impl<'d, T: Instance> Adc<'d, T> {
174 blocking_delay_us(1); 181 blocking_delay_us(1);
175 } 182 }
176 183
184 #[cfg(any(adc_v3, adc_g0, adc_u0))]
185 pub(super) fn start() {
186 // Start adc conversion
187 T::regs().cr().modify(|reg| {
188 reg.set_adstart(true);
189 });
190 }
191
192 #[cfg(any(adc_v3, adc_g0, adc_u0))]
193 pub(super) fn stop() {
194 // Stop adc conversion
195 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
196 T::regs().cr().modify(|reg| {
197 reg.set_adstp(true);
198 });
199 while T::regs().cr().read().adstart() {}
200 }
201 }
202
203 #[cfg(any(adc_v3, adc_g0, adc_u0))]
204 pub(super) fn teardown_adc() {
205 //disable dma control
206 #[cfg(not(any(adc_g0, adc_u0)))]
207 T::regs().cfgr().modify(|reg| {
208 reg.set_dmaen(false);
209 });
210 #[cfg(any(adc_g0, adc_u0))]
211 T::regs().cfgr1().modify(|reg| {
212 reg.set_dmaen(false);
213 });
214 }
215
177 /// Initialize the ADC leaving any analog clock at reset value. 216 /// Initialize the ADC leaving any analog clock at reset value.
178 /// For G0 and WL, this is the async clock without prescaler. 217 /// For G0 and WL, this is the async clock without prescaler.
179 pub fn new(adc: Peri<'d, T>) -> Self { 218 pub fn new(adc: Peri<'d, T>) -> Self {
@@ -423,6 +462,9 @@ impl<'d, T: Instance> Adc<'d, T> {
423 "Asynchronous read sequence cannot be more than 16 in length" 462 "Asynchronous read sequence cannot be more than 16 in length"
424 ); 463 );
425 464
465 #[cfg(all(feature = "low-power", stm32wlex))]
466 let _device_busy = crate::low_power::DeviceBusy::new();
467
426 // Ensure no conversions are ongoing and ADC is enabled. 468 // Ensure no conversions are ongoing and ADC is enabled.
427 Self::cancel_conversions(); 469 Self::cancel_conversions();
428 self.enable(); 470 self.enable();
@@ -559,6 +601,137 @@ impl<'d, T: Instance> Adc<'d, T> {
559 }); 601 });
560 } 602 }
561 603
604 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
605 ///
606 /// The `dma_buf` should be large enough to prevent DMA buffer overrun.
607 /// The length of the `dma_buf` should be a multiple of the ADC channel count.
608 /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
609 ///
610 /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
611 /// It is critical to call `read` frequently to prevent DMA buffer overrun.
612 ///
613 /// [`read`]: #method.read
614 #[cfg(any(adc_v3, adc_g0, adc_u0))]
615 pub fn into_ring_buffered<'a>(
616 &mut self,
617 dma: Peri<'a, impl RxDma<T>>,
618 dma_buf: &'a mut [u16],
619 sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>,
620 ) -> RingBufferedAdc<'a, T> {
621 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
622 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
623 assert!(
624 sequence.len() <= 16,
625 "Asynchronous read sequence cannot be more than 16 in length"
626 );
627 // reset conversions and enable the adc
628 Self::cancel_conversions();
629 self.enable();
630
631 //adc side setup
632
633 // Set sequence length
634 #[cfg(not(any(adc_g0, adc_u0)))]
635 T::regs().sqr1().modify(|w| {
636 w.set_l(sequence.len() as u8 - 1);
637 });
638
639 #[cfg(adc_g0)]
640 {
641 let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new();
642
643 T::regs().chselr().write(|chselr| {
644 T::regs().smpr().write(|smpr| {
645 for (channel, sample_time) in sequence {
646 chselr.set_chsel(channel.channel.into(), true);
647 if let Some(i) = sample_times.iter().position(|&t| t == sample_time) {
648 smpr.set_smpsel(channel.channel.into(), (i as u8).into());
649 } else {
650 smpr.set_sample_time(sample_times.len(), sample_time);
651 if let Err(_) = sample_times.push(sample_time) {
652 panic!(
653 "Implementation is limited to {} unique sample times among all channels.",
654 SAMPLE_TIMES_CAPACITY
655 );
656 }
657 }
658 }
659 })
660 });
661 }
662 #[cfg(not(adc_g0))]
663 {
664 #[cfg(adc_u0)]
665 let mut channel_mask = 0;
666
667 // Configure channels and ranks
668 for (_i, (channel, sample_time)) in sequence.enumerate() {
669 Self::configure_channel(channel, sample_time);
670
671 // Each channel is sampled according to sequence
672 #[cfg(not(any(adc_g0, adc_u0)))]
673 match _i {
674 0..=3 => {
675 T::regs().sqr1().modify(|w| {
676 w.set_sq(_i, channel.channel());
677 });
678 }
679 4..=8 => {
680 T::regs().sqr2().modify(|w| {
681 w.set_sq(_i - 4, channel.channel());
682 });
683 }
684 9..=13 => {
685 T::regs().sqr3().modify(|w| {
686 w.set_sq(_i - 9, channel.channel());
687 });
688 }
689 14..=15 => {
690 T::regs().sqr4().modify(|w| {
691 w.set_sq(_i - 14, channel.channel());
692 });
693 }
694 _ => unreachable!(),
695 }
696
697 #[cfg(adc_u0)]
698 {
699 channel_mask |= 1 << channel.channel();
700 }
701 }
702
703 // On G0 and U0 enabled channels are sampled from 0 to last channel.
704 // It is possible to add up to 8 sequences if CHSELRMOD = 1.
705 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used.
706 #[cfg(adc_u0)]
707 T::regs().chselr().modify(|reg| {
708 reg.set_chsel(channel_mask);
709 });
710 }
711 // Set continuous mode with Circular dma.
712 // Clear overrun flag before starting transfer.
713 T::regs().isr().modify(|reg| {
714 reg.set_ovr(true);
715 });
716
717 #[cfg(not(any(adc_g0, adc_u0)))]
718 T::regs().cfgr().modify(|reg| {
719 reg.set_discen(false);
720 reg.set_cont(true);
721 reg.set_dmacfg(Dmacfg::CIRCULAR);
722 reg.set_dmaen(true);
723 });
724 #[cfg(any(adc_g0, adc_u0))]
725 T::regs().cfgr1().modify(|reg| {
726 reg.set_discen(false);
727 reg.set_cont(true);
728 reg.set_dmacfg(Dmacfg::CIRCULAR);
729 reg.set_dmaen(true);
730 });
731
732 RingBufferedAdc::new(dma, dma_buf)
733 }
734
562 #[cfg(not(adc_g0))] 735 #[cfg(not(adc_g0))]
563 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { 736 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
564 // RM0492, RM0481, etc. 737 // RM0492, RM0481, etc.
diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config.rs
index e08349f02..4fe634ce4 100644
--- a/embassy-stm32/src/can/fd/config.rs
+++ b/embassy-stm32/src/can/fd/config.rs
@@ -360,6 +360,8 @@ pub struct FdCanConfig {
360 pub global_filter: GlobalFilter, 360 pub global_filter: GlobalFilter,
361 /// TX buffer mode (FIFO or priority queue) 361 /// TX buffer mode (FIFO or priority queue)
362 pub tx_buffer_mode: TxBufferMode, 362 pub tx_buffer_mode: TxBufferMode,
363 /// Automatic recovery from bus off state
364 pub automatic_bus_off_recovery: bool,
363} 365}
364 366
365impl FdCanConfig { 367impl FdCanConfig {
@@ -456,6 +458,16 @@ impl FdCanConfig {
456 self.tx_buffer_mode = txbm; 458 self.tx_buffer_mode = txbm;
457 self 459 self
458 } 460 }
461
462 /// Enables or disables automatic recovery from bus off state
463 ///
464 /// Automatic recovery is performed by clearing the INIT bit in the CCCR register if
465 /// the BO bit is active in the IR register in the IT0 interrupt.
466 #[inline]
467 pub const fn set_automatic_bus_off_recovery(mut self, enabled: bool) -> Self {
468 self.automatic_bus_off_recovery = enabled;
469 self
470 }
459} 471}
460 472
461impl Default for FdCanConfig { 473impl Default for FdCanConfig {
@@ -474,6 +486,7 @@ impl Default for FdCanConfig {
474 timestamp_source: TimestampSource::None, 486 timestamp_source: TimestampSource::None,
475 global_filter: GlobalFilter::default(), 487 global_filter: GlobalFilter::default(),
476 tx_buffer_mode: TxBufferMode::Priority, 488 tx_buffer_mode: TxBufferMode::Priority,
489 automatic_bus_off_recovery: true,
477 } 490 }
478 } 491 }
479} 492}
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs
index a142a6d63..9883aff57 100644
--- a/embassy-stm32/src/can/fdcan.rs
+++ b/embassy-stm32/src/can/fdcan.rs
@@ -53,7 +53,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
53 regs.ir().write(|w| w.set_tefn(true)); 53 regs.ir().write(|w| w.set_tefn(true));
54 } 54 }
55 55
56 T::info().state.lock(|s| { 56 let recover_from_bo = T::info().state.lock(|s| {
57 let state = s.borrow_mut(); 57 let state = s.borrow_mut();
58 match &state.tx_mode { 58 match &state.tx_mode {
59 TxMode::NonBuffered(waker) => waker.wake(), 59 TxMode::NonBuffered(waker) => waker.wake(),
@@ -85,11 +85,15 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
85 if ir.rfn(1) { 85 if ir.rfn(1) {
86 state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick); 86 state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick);
87 } 87 }
88
89 state.automatic_bus_off_recovery
88 }); 90 });
89 91
90 if ir.bo() { 92 if ir.bo() {
91 regs.ir().write(|w| w.set_bo(true)); 93 regs.ir().write(|w| w.set_bo(true));
92 if regs.psr().read().bo() { 94 if let Some(true) = recover_from_bo
95 && regs.psr().read().bo()
96 {
93 // Initiate bus-off recovery sequence by resetting CCCR.INIT 97 // Initiate bus-off recovery sequence by resetting CCCR.INIT
94 regs.cccr().modify(|w| w.set_init(false)); 98 regs.cccr().modify(|w| w.set_init(false));
95 } 99 }
@@ -263,7 +267,9 @@ impl<'d> CanConfigurator<'d> {
263 pub fn start(self, mode: OperatingMode) -> Can<'d> { 267 pub fn start(self, mode: OperatingMode) -> Can<'d> {
264 let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit); 268 let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit);
265 self.info.state.lock(|s| { 269 self.info.state.lock(|s| {
266 s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; 270 let mut state = s.borrow_mut();
271 state.ns_per_timer_tick = ns_per_timer_tick;
272 state.automatic_bus_off_recovery = Some(self.config.automatic_bus_off_recovery);
267 }); 273 });
268 self.info.regs.into_mode(self.config, mode); 274 self.info.regs.into_mode(self.config, mode);
269 Can { 275 Can {
@@ -861,7 +867,7 @@ struct State {
861 sender_instance_count: usize, 867 sender_instance_count: usize,
862 tx_pin_port: Option<u8>, 868 tx_pin_port: Option<u8>,
863 rx_pin_port: Option<u8>, 869 rx_pin_port: Option<u8>,
864 870 automatic_bus_off_recovery: Option<bool>, // controlled by CanConfigurator::start()
865 pub err_waker: AtomicWaker, 871 pub err_waker: AtomicWaker,
866} 872}
867 873
@@ -876,6 +882,7 @@ impl State {
876 sender_instance_count: 0, 882 sender_instance_count: 0,
877 tx_pin_port: None, 883 tx_pin_port: None,
878 rx_pin_port: None, 884 rx_pin_port: None,
885 automatic_bus_off_recovery: None,
879 } 886 }
880 } 887 }
881} 888}
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 01b6b8800..6f2d03bd1 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -70,6 +70,11 @@ fn debug_print_interrupts(isr: stm32_metapac::i2c::regs::Isr) {
70} 70}
71 71
72pub(crate) unsafe fn on_interrupt<T: Instance>() { 72pub(crate) unsafe fn on_interrupt<T: Instance>() {
73 // restore the clocks to their last configured state as
74 // much is lost in STOP modes
75 #[cfg(all(feature = "low-power", stm32wlex))]
76 crate::low_power::on_wakeup_irq();
77
73 let regs = T::info().regs; 78 let regs = T::info().regs;
74 let isr = regs.isr().read(); 79 let isr = regs.isr().read();
75 80
@@ -814,6 +819,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
814 819
815 /// Write. 820 /// Write.
816 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))]
823 let _device_busy = crate::low_power::DeviceBusy::new();
817 let timeout = self.timeout(); 824 let timeout = self.timeout();
818 if write.is_empty() { 825 if write.is_empty() {
819 self.write_internal(address.into(), write, true, timeout) 826 self.write_internal(address.into(), write, true, timeout)
@@ -828,6 +835,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
828 /// 835 ///
829 /// The buffers are concatenated in a single write transaction. 836 /// The buffers are concatenated in a single write transaction.
830 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))]
839 let _device_busy = crate::low_power::DeviceBusy::new();
831 let timeout = self.timeout(); 840 let timeout = self.timeout();
832 841
833 if write.is_empty() { 842 if write.is_empty() {
@@ -851,6 +860,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
851 860
852 /// Read. 861 /// Read.
853 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))]
864 let _device_busy = crate::low_power::DeviceBusy::new();
854 let timeout = self.timeout(); 865 let timeout = self.timeout();
855 866
856 if buffer.is_empty() { 867 if buffer.is_empty() {
@@ -863,6 +874,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
863 874
864 /// Write, restart, read. 875 /// Write, restart, read.
865 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))]
878 let _device_busy = crate::low_power::DeviceBusy::new();
866 let timeout = self.timeout(); 879 let timeout = self.timeout();
867 880
868 if write.is_empty() { 881 if write.is_empty() {
@@ -888,6 +901,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
888 /// 901 ///
889 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 902 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
890 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))]
905 let _device_busy = crate::low_power::DeviceBusy::new();
891 let _ = addr; 906 let _ = addr;
892 let _ = operations; 907 let _ = operations;
893 todo!() 908 todo!()
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index 1b66ca680..cde3153f6 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -43,8 +43,6 @@
43//! 43//!
44//! // give the RTC to the executor... 44//! // give the RTC to the executor...
45//! let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); 45//! let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
46//! static RTC: StaticCell<Rtc> = StaticCell::new();
47//! let rtc = RTC.init(rtc);
48//! embassy_stm32::low_power::stop_with_rtc(rtc); 46//! embassy_stm32::low_power::stop_with_rtc(rtc);
49//! 47//!
50//! // your application here... 48//! // your application here...
@@ -70,6 +68,39 @@ use crate::rtc::Rtc;
70 68
71static mut EXECUTOR: Option<Executor> = None; 69static mut EXECUTOR: Option<Executor> = None;
72 70
71#[cfg(stm32wlex)]
72pub(crate) use self::busy::DeviceBusy;
73#[cfg(stm32wlex)]
74mod busy {
75 use core::sync::atomic::{AtomicU32, Ordering};
76
77 // Count of devices blocking STOP
78 static STOP_BLOCKED: AtomicU32 = AtomicU32::new(0);
79
80 /// Check if STOP1 is blocked.
81 pub(crate) fn stop_blocked() -> bool {
82 STOP_BLOCKED.load(Ordering::SeqCst) > 0
83 }
84
85 /// When ca device goes busy it will construct one of these where it will be dropped when the device goes idle.
86 pub(crate) struct DeviceBusy {}
87
88 impl DeviceBusy {
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 impl Drop for DeviceBusy {
98 fn drop(&mut self) {
99 STOP_BLOCKED.fetch_sub(1, Ordering::SeqCst);
100 trace!("low power: device idle: Stop:{}", STOP_BLOCKED.load(Ordering::SeqCst));
101 }
102 }
103}
73#[cfg(not(stm32u0))] 104#[cfg(not(stm32u0))]
74foreach_interrupt! { 105foreach_interrupt! {
75 (RTC, rtc, $block:ident, WKUP, $irq:ident) => { 106 (RTC, rtc, $block:ident, WKUP, $irq:ident) => {
@@ -94,14 +125,22 @@ foreach_interrupt! {
94 125
95#[allow(dead_code)] 126#[allow(dead_code)]
96pub(crate) unsafe fn on_wakeup_irq() { 127pub(crate) unsafe fn on_wakeup_irq() {
97 EXECUTOR.as_mut().unwrap().on_wakeup_irq(); 128 if EXECUTOR.is_some() {
129 trace!("low power: wakeup irq");
130 EXECUTOR.as_mut().unwrap().on_wakeup_irq();
131 }
98} 132}
99 133
100/// Configure STOP mode with RTC. 134/// Configure STOP mode with RTC.
101pub fn stop_with_rtc(rtc: &'static Rtc) { 135pub fn stop_with_rtc(rtc: Rtc) {
102 unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) 136 unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
103} 137}
104 138
139/// Reconfigure the RTC, if set.
140pub fn reconfigure_rtc(f: impl FnOnce(&mut Rtc)) {
141 unsafe { EXECUTOR.as_mut().unwrap() }.reconfigure_rtc(f);
142}
143
105/// Get whether the core is ready to enter the given stop mode. 144/// Get whether the core is ready to enter the given stop mode.
106/// 145///
107/// This will return false if some peripheral driver is in use that 146/// This will return false if some peripheral driver is in use that
@@ -124,10 +163,10 @@ pub enum StopMode {
124 Stop2, 163 Stop2,
125} 164}
126 165
127#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] 166#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))]
128use stm32_metapac::pwr::vals::Lpms; 167use stm32_metapac::pwr::vals::Lpms;
129 168
130#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32u0))] 169#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))]
131impl Into<Lpms> for StopMode { 170impl Into<Lpms> for StopMode {
132 fn into(self) -> Lpms { 171 fn into(self) -> Lpms {
133 match self { 172 match self {
@@ -177,22 +216,53 @@ impl Executor {
177 } 216 }
178 217
179 unsafe fn on_wakeup_irq(&mut self) { 218 unsafe fn on_wakeup_irq(&mut self) {
219 #[cfg(stm32wlex)]
220 {
221 let extscr = crate::pac::PWR.extscr().read();
222 if extscr.c1stop2f() || extscr.c1stopf() {
223 // when we wake from any stop mode we need to re-initialize the rcc
224 crate::rcc::apply_resume_config();
225 if extscr.c1stop2f() {
226 // when we wake from STOP2, we need to re-initialize the time driver
227 critical_section::with(|cs| crate::time_driver::init_timer(cs));
228 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer)
229 // and given that we just woke from STOP2, we can reset them
230 crate::rcc::REFCOUNT_STOP2 = 0;
231 crate::rcc::REFCOUNT_STOP1 = 0;
232 }
233 }
234 }
180 self.time_driver.resume_time(); 235 self.time_driver.resume_time();
181 trace!("low power: resume"); 236 trace!("low power: resume");
182 } 237 }
183 238
184 pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) { 239 pub(self) fn stop_with_rtc(&mut self, rtc: Rtc) {
185 self.time_driver.set_rtc(rtc); 240 self.time_driver.set_rtc(rtc);
186 241 self.time_driver.reconfigure_rtc(|rtc| rtc.enable_wakeup_line());
187 rtc.enable_wakeup_line();
188 242
189 trace!("low power: stop with rtc configured"); 243 trace!("low power: stop with rtc configured");
190 } 244 }
191 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 }
261
192 fn stop_mode(&self) -> Option<StopMode> { 262 fn stop_mode(&self) -> Option<StopMode> {
193 if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } && unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { 263 if self.stop2_ok_to_enter() {
194 Some(StopMode::Stop2) 264 Some(StopMode::Stop2)
195 } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { 265 } else if self.stop1_ok_to_enter() {
196 Some(StopMode::Stop1) 266 Some(StopMode::Stop1)
197 } else { 267 } else {
198 None 268 None
@@ -201,7 +271,7 @@ impl Executor {
201 271
202 #[allow(unused_variables)] 272 #[allow(unused_variables)]
203 fn configure_stop(&mut self, stop_mode: StopMode) { 273 fn configure_stop(&mut self, stop_mode: StopMode) {
204 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba))] 274 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))]
205 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); 275 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
206 #[cfg(stm32h5)] 276 #[cfg(stm32h5)]
207 crate::pac::PWR.pmcr().modify(|v| { 277 crate::pac::PWR.pmcr().modify(|v| {
@@ -213,6 +283,11 @@ impl Executor {
213 283
214 fn configure_pwr(&mut self) { 284 fn configure_pwr(&mut self) {
215 self.scb.clear_sleepdeep(); 285 self.scb.clear_sleepdeep();
286 // Clear any previous stop flags
287 #[cfg(stm32wlex)]
288 crate::pac::PWR.extscr().modify(|w| {
289 w.set_c1cssf(true);
290 });
216 291
217 compiler_fence(Ordering::SeqCst); 292 compiler_fence(Ordering::SeqCst);
218 293
@@ -266,6 +341,19 @@ impl Executor {
266 executor.inner.poll(); 341 executor.inner.poll();
267 self.configure_pwr(); 342 self.configure_pwr();
268 asm!("wfe"); 343 asm!("wfe");
344 #[cfg(stm32wlex)]
345 {
346 let es = crate::pac::PWR.extscr().read();
347 match (es.c1stopf(), es.c1stop2f()) {
348 (true, false) => debug!("low power: wake from STOP1"),
349 (false, true) => debug!("low power: wake from STOP2"),
350 (true, true) => debug!("low power: wake from STOP1 and STOP2 ???"),
351 (false, false) => trace!("low power: stop mode not entered"),
352 };
353 crate::pac::PWR.extscr().modify(|w| {
354 w.set_c1cssf(false);
355 });
356 }
269 }; 357 };
270 } 358 }
271 } 359 }
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs
index 2e1cbd702..584957c6d 100644
--- a/embassy-stm32/src/rcc/l.rs
+++ b/embassy-stm32/src/rcc/l.rs
@@ -1,3 +1,6 @@
1#[cfg(all(feature = "low-power", stm32wlex))]
2use core::mem::MaybeUninit;
3
1#[cfg(any(stm32l0, stm32l1))] 4#[cfg(any(stm32l0, stm32l1))]
2pub use crate::pac::pwr::vals::Vos as VoltageScale; 5pub use crate::pac::pwr::vals::Vos as VoltageScale;
3use crate::pac::rcc::regs::Cfgr; 6use crate::pac::rcc::regs::Cfgr;
@@ -11,6 +14,42 @@ use crate::time::Hertz;
11/// HSI speed 14/// HSI speed
12pub const HSI_FREQ: Hertz = Hertz(16_000_000); 15pub const HSI_FREQ: Hertz = Hertz(16_000_000);
13 16
17/// Saved RCC Config
18///
19/// Used when exiting STOP2 to re-enable clocks to their last configured state
20/// for chips that need it.
21#[cfg(all(feature = "low-power", stm32wlex))]
22static mut RESUME_RCC_CONFIG: MaybeUninit<Config> = MaybeUninit::uninit();
23
24/// Set the rcc config to be restored when exiting STOP2
25///
26/// Safety: Sets a mutable global.
27#[cfg(all(feature = "low-power", stm32wlex))]
28pub(crate) unsafe fn set_resume_config(config: Config) {
29 trace!("rcc set_resume_config()");
30 RESUME_RCC_CONFIG = MaybeUninit::new(config);
31}
32
33/// Get the rcc config to be restored when exiting STOP2
34///
35/// Safety: Reads a mutable global.
36#[cfg(all(feature = "low-power", stm32wlex))]
37pub(crate) unsafe fn get_resume_config() -> Config {
38 *(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref()
39}
40
41#[cfg(all(feature = "low-power", stm32wlex))]
42/// Safety: should only be called from low power executable just after resuming from STOP2
43pub(crate) unsafe fn apply_resume_config() {
44 trace!("rcc apply_resume_config()");
45
46 while RCC.cfgr().read().sws() != Sysclk::MSI {}
47
48 let config = get_resume_config();
49
50 init(config);
51}
52
14#[derive(Clone, Copy, Eq, PartialEq)] 53#[derive(Clone, Copy, Eq, PartialEq)]
15pub enum HseMode { 54pub enum HseMode {
16 /// crystal/ceramic oscillator (HSEBYP=0) 55 /// crystal/ceramic oscillator (HSEBYP=0)
@@ -154,6 +193,10 @@ fn msi_enable(range: MSIRange) {
154} 193}
155 194
156pub(crate) unsafe fn init(config: Config) { 195pub(crate) unsafe fn init(config: Config) {
196 // save the rcc config because if we enter stop 2 we need to re-apply it on wakeup
197 #[cfg(all(feature = "low-power", stm32wlex))]
198 set_resume_config(config);
199
157 // Switch to MSI to prevent problems with PLL configuration. 200 // Switch to MSI to prevent problems with PLL configuration.
158 if !RCC.cr().read().msion() { 201 if !RCC.cr().read().msion() {
159 // Turn on MSI and configure it to 4MHz. 202 // Turn on MSI and configure it to 4MHz.
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index addfca3c3..c817dd4b7 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -390,7 +390,7 @@ pub fn disable<T: RccPeripheral>() {
390/// 390///
391/// This should only be called after `init`. 391/// This should only be called after `init`.
392#[cfg(not(feature = "_dual-core"))] 392#[cfg(not(feature = "_dual-core"))]
393pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { 393pub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) {
394 critical_section::with(|cs| init_rcc(cs, config)) 394 critical_section::with(|cs| init_rcc(cs, config))
395} 395}
396 396
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs
index 999f24714..e09d5afb0 100644
--- a/embassy-stm32/src/rtc/low_power.rs
+++ b/embassy-stm32/src/rtc/low_power.rs
@@ -68,7 +68,7 @@ pub(crate) enum WakeupPrescaler {
68} 68}
69 69
70#[cfg(any( 70#[cfg(any(
71 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba 71 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex
72))] 72))]
73impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { 73impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
74 fn from(val: WakeupPrescaler) -> Self { 74 fn from(val: WakeupPrescaler) -> Self {
@@ -84,7 +84,7 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
84} 84}
85 85
86#[cfg(any( 86#[cfg(any(
87 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba 87 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex
88))] 88))]
89impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { 89impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
90 fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { 90 fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
@@ -127,7 +127,7 @@ impl Rtc {
127 /// start the wakeup alarm and with a duration that is as close to but less than 127 /// start the wakeup alarm and with a duration that is as close to but less than
128 /// the requested duration, and record the instant the wakeup alarm was started 128 /// the requested duration, and record the instant the wakeup alarm was started
129 pub(crate) fn start_wakeup_alarm( 129 pub(crate) fn start_wakeup_alarm(
130 &self, 130 &mut self,
131 requested_duration: embassy_time::Duration, 131 requested_duration: embassy_time::Duration,
132 cs: critical_section::CriticalSection, 132 cs: critical_section::CriticalSection,
133 ) { 133 ) {
@@ -179,7 +179,10 @@ impl Rtc {
179 179
180 /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` 180 /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
181 /// was called, otherwise none 181 /// was called, otherwise none
182 pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> { 182 pub(crate) fn stop_wakeup_alarm(
183 &mut self,
184 cs: critical_section::CriticalSection,
185 ) -> Option<embassy_time::Duration> {
183 let instant = self.instant().unwrap(); 186 let instant = self.instant().unwrap();
184 if RTC::regs().cr().read().wute() { 187 if RTC::regs().cr().read().wute() {
185 trace!("rtc: stop wakeup alarm at {}", instant); 188 trace!("rtc: stop wakeup alarm at {}", instant);
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs
index 23f6ccb0c..8ac022536 100644
--- a/embassy-stm32/src/rtc/v2.rs
+++ b/embassy-stm32/src/rtc/v2.rs
@@ -93,7 +93,7 @@ impl super::Rtc {
93 }) 93 })
94 } 94 }
95 95
96 pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R 96 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
97 where 97 where
98 F: FnOnce(crate::pac::rtc::Rtc) -> R, 98 F: FnOnce(crate::pac::rtc::Rtc) -> R,
99 { 99 {
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index 01da5d70a..f7ebea73e 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -95,7 +95,7 @@ impl super::Rtc {
95 }) 95 })
96 } 96 }
97 97
98 pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R 98 pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
99 where 99 where
100 F: FnOnce(crate::pac::rtc::Rtc) -> R, 100 F: FnOnce(crate::pac::rtc::Rtc) -> R,
101 { 101 {
@@ -131,7 +131,7 @@ impl SealedInstance for crate::peripherals::RTC {
131 131
132 #[cfg(feature = "low-power")] 132 #[cfg(feature = "low-power")]
133 cfg_if::cfg_if!( 133 cfg_if::cfg_if!(
134 if #[cfg(stm32g4)] { 134 if #[cfg(any(stm32g4, stm32wlex))] {
135 const EXTI_WAKEUP_LINE: usize = 20; 135 const EXTI_WAKEUP_LINE: usize = 20;
136 } else if #[cfg(stm32g0)] { 136 } else if #[cfg(stm32g0)] {
137 const EXTI_WAKEUP_LINE: usize = 19; 137 const EXTI_WAKEUP_LINE: usize = 19;
@@ -142,7 +142,7 @@ impl SealedInstance for crate::peripherals::RTC {
142 142
143 #[cfg(feature = "low-power")] 143 #[cfg(feature = "low-power")]
144 cfg_if::cfg_if!( 144 cfg_if::cfg_if!(
145 if #[cfg(stm32g4)] { 145 if #[cfg(any(stm32g4, stm32wlex))] {
146 type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; 146 type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;
147 } else if #[cfg(any(stm32g0, stm32u0))] { 147 } else if #[cfg(any(stm32g0, stm32u0))] {
148 type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; 148 type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP;
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 408d1b764..e05131040 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -1032,12 +1032,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1032 1032
1033 /// Wait for a previously started datapath transfer to complete from an interrupt. 1033 /// Wait for a previously started datapath transfer to complete from an interrupt.
1034 #[inline] 1034 #[inline]
1035 #[allow(unused)]
1035 async fn complete_datapath_transfer(block: bool) -> Result<(), Error> { 1036 async fn complete_datapath_transfer(block: bool) -> Result<(), Error> {
1036 let regs = T::regs();
1037
1038 let res = poll_fn(|cx| { 1037 let res = poll_fn(|cx| {
1038 // Compiler might not be sufficiently constrained here
1039 // https://github.com/embassy-rs/embassy/issues/4723
1039 T::state().register(cx.waker()); 1040 T::state().register(cx.waker());
1040 let status = regs.star().read(); 1041 let status = T::regs().star().read();
1041 1042
1042 if status.dcrcfail() { 1043 if status.dcrcfail() {
1043 return Poll::Ready(Err(Error::Crc)); 1044 return Poll::Ready(Err(Error::Crc));
@@ -1052,10 +1053,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
1052 if status.stbiterr() { 1053 if status.stbiterr() {
1053 return Poll::Ready(Err(Error::StBitErr)); 1054 return Poll::Ready(Err(Error::StBitErr));
1054 } 1055 }
1056 #[cfg(sdmmc_v1)]
1055 let done = match block { 1057 let done = match block {
1056 true => status.dbckend(), 1058 true => status.dbckend(),
1057 false => status.dataend(), 1059 false => status.dataend(),
1058 }; 1060 };
1061 #[cfg(sdmmc_v2)]
1062 let done = status.dataend();
1059 if done { 1063 if done {
1060 return Poll::Ready(Ok(())); 1064 return Poll::Ready(Ok(()));
1061 } 1065 }
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 74b10a183..4956d1f68 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -1,6 +1,8 @@
1#![allow(non_snake_case)] 1#![allow(non_snake_case)]
2 2
3use core::cell::{Cell, RefCell}; 3use core::cell::{Cell, RefCell};
4#[cfg(all(feature = "low-power", stm32wlex))]
5use core::sync::atomic::AtomicU16;
4use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; 6use core::sync::atomic::{AtomicU32, Ordering, compiler_fence};
5 7
6use critical_section::CriticalSection; 8use critical_section::CriticalSection;
@@ -213,7 +215,10 @@ pub(crate) struct RtcDriver {
213 period: AtomicU32, 215 period: AtomicU32,
214 alarm: Mutex<CriticalSectionRawMutex, AlarmState>, 216 alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
215 #[cfg(feature = "low-power")] 217 #[cfg(feature = "low-power")]
216 rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, 218 rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>,
219 /// Saved count for the timer (its value is lost when entering STOP2)
220 #[cfg(all(feature = "low-power", stm32wlex))]
221 saved_count: AtomicU16,
217 queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, 222 queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
218} 223}
219 224
@@ -221,12 +226,16 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
221 period: AtomicU32::new(0), 226 period: AtomicU32::new(0),
222 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), 227 alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
223 #[cfg(feature = "low-power")] 228 #[cfg(feature = "low-power")]
224 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), 229 rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)),
230 #[cfg(all(feature = "low-power", stm32wlex))]
231 saved_count: AtomicU16::new(0),
225 queue: Mutex::new(RefCell::new(Queue::new())) 232 queue: Mutex::new(RefCell::new(Queue::new()))
226}); 233});
227 234
228impl RtcDriver { 235impl RtcDriver {
229 fn init(&'static self, cs: critical_section::CriticalSection) { 236 /// initialize the timer, but don't start it. Used for chips like stm32wle5
237 /// for low power where the timer config is lost in STOP2.
238 fn init_timer(&'static self, cs: critical_section::CriticalSection) {
230 let r = regs_gp16(); 239 let r = regs_gp16();
231 240
232 rcc::enable_and_reset_with_cs::<T>(cs); 241 rcc::enable_and_reset_with_cs::<T>(cs);
@@ -259,10 +268,16 @@ impl RtcDriver {
259 w.set_ccie(0, true); 268 w.set_ccie(0, true);
260 }); 269 });
261 270
271 #[cfg(all(feature = "low-power", stm32wlex))]
272 r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst)));
273
262 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); 274 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend();
263 unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; 275 unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() };
276 }
264 277
265 r.cr1().modify(|w| w.set_cen(true)); 278 fn init(&'static self, cs: CriticalSection) {
279 self.init_timer(cs);
280 regs_gp16().cr1().modify(|w| w.set_cen(true));
266 } 281 }
267 282
268 fn on_interrupt(&self) { 283 fn on_interrupt(&self) {
@@ -379,7 +394,7 @@ impl RtcDriver {
379 #[cfg(feature = "low-power")] 394 #[cfg(feature = "low-power")]
380 /// Stop the wakeup alarm, if enabled, and add the appropriate offset 395 /// Stop the wakeup alarm, if enabled, and add the appropriate offset
381 fn stop_wakeup_alarm(&self, cs: CriticalSection) { 396 fn stop_wakeup_alarm(&self, cs: CriticalSection) {
382 if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm(cs) { 397 if let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) {
383 self.add_time(offset, cs); 398 self.add_time(offset, cs);
384 } 399 }
385 } 400 }
@@ -389,7 +404,7 @@ impl RtcDriver {
389 */ 404 */
390 #[cfg(feature = "low-power")] 405 #[cfg(feature = "low-power")]
391 /// Set the rtc but panic if it's already been set 406 /// Set the rtc but panic if it's already been set
392 pub(crate) fn set_rtc(&self, rtc: &'static Rtc) { 407 pub(crate) fn set_rtc(&self, mut rtc: Rtc) {
393 critical_section::with(|cs| { 408 critical_section::with(|cs| {
394 rtc.stop_wakeup_alarm(cs); 409 rtc.stop_wakeup_alarm(cs);
395 410
@@ -398,6 +413,12 @@ impl RtcDriver {
398 } 413 }
399 414
400 #[cfg(feature = "low-power")] 415 #[cfg(feature = "low-power")]
416 /// Set the rtc but panic if it's already been set
417 pub(crate) fn reconfigure_rtc(&self, f: impl FnOnce(&mut Rtc)) {
418 critical_section::with(|cs| f(self.rtc.borrow(cs).borrow_mut().as_mut().unwrap()));
419 }
420
421 #[cfg(feature = "low-power")]
401 /// The minimum pause time beyond which the executor will enter a low-power state. 422 /// The minimum pause time beyond which the executor will enter a low-power state.
402 pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250); 423 pub(crate) const MIN_STOP_PAUSE: embassy_time::Duration = embassy_time::Duration::from_millis(250);
403 424
@@ -414,16 +435,24 @@ impl RtcDriver {
414 435
415 let time_until_next_alarm = self.time_until_next_alarm(cs); 436 let time_until_next_alarm = self.time_until_next_alarm(cs);
416 if time_until_next_alarm < Self::MIN_STOP_PAUSE { 437 if time_until_next_alarm < Self::MIN_STOP_PAUSE {
438 trace!(
439 "time_until_next_alarm < Self::MIN_STOP_PAUSE ({})",
440 time_until_next_alarm
441 );
417 Err(()) 442 Err(())
418 } else { 443 } else {
419 self.rtc 444 self.rtc
420 .borrow(cs) 445 .borrow(cs)
421 .get() 446 .borrow_mut()
447 .as_mut()
422 .unwrap() 448 .unwrap()
423 .start_wakeup_alarm(time_until_next_alarm, cs); 449 .start_wakeup_alarm(time_until_next_alarm, cs);
424 450
425 regs_gp16().cr1().modify(|w| w.set_cen(false)); 451 regs_gp16().cr1().modify(|w| w.set_cen(false));
426 452 // save the count for the timer as its lost in STOP2 for stm32wlex
453 #[cfg(stm32wlex)]
454 self.saved_count
455 .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst);
427 Ok(()) 456 Ok(())
428 } 457 }
429 }) 458 })
@@ -521,3 +550,8 @@ pub(crate) fn get_driver() -> &'static RtcDriver {
521pub(crate) fn init(cs: CriticalSection) { 550pub(crate) fn init(cs: CriticalSection) {
522 DRIVER.init(cs) 551 DRIVER.init(cs)
523} 552}
553
554#[cfg(all(feature = "low-power", stm32wlex))]
555pub(crate) fn init_timer(cs: CriticalSection) {
556 DRIVER.init_timer(cs)
557}
diff --git a/embassy-stm32/src/vrefbuf/mod.rs b/embassy-stm32/src/vrefbuf/mod.rs
index b061306a0..43dd9c800 100644
--- a/embassy-stm32/src/vrefbuf/mod.rs
+++ b/embassy-stm32/src/vrefbuf/mod.rs
@@ -14,10 +14,10 @@ pub struct VoltageReferenceBuffer<'d, T: Instance> {
14#[cfg(rcc_wba)] 14#[cfg(rcc_wba)]
15fn get_refbuf_trim(voltage_scale: Vrs) -> usize { 15fn get_refbuf_trim(voltage_scale: Vrs) -> usize {
16 match voltage_scale { 16 match voltage_scale {
17 Vrs::VREF0 => 0x0BFA_07A8usize, 17 Vrs::VREF0 => 0x0BFA_07ABusize,
18 Vrs::VREF1 => 0x0BFA_07A9usize, 18 Vrs::VREF1 => 0x0BFA_07AAusize,
19 Vrs::VREF2 => 0x0BFA_07AAusize, 19 Vrs::VREF2 => 0x0BFA_07A9usize,
20 Vrs::VREF3 => 0x0BFA_07ABusize, 20 Vrs::VREF3 => 0x0BFA_07A8usize,
21 _ => panic!("Incorrect Vrs setting!"), 21 _ => panic!("Incorrect Vrs setting!"),
22 } 22 }
23} 23}
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs
index 102f13c45..6432edb8a 100644
--- a/examples/rp/src/bin/pio_onewire.rs
+++ b/examples/rp/src/bin/pio_onewire.rs
@@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) {
61 let mut data = [0; 9]; 61 let mut data = [0; 9];
62 onewire.read_bytes(&mut data).await; 62 onewire.read_bytes(&mut data).await;
63 if crc8(&data) == 0 { 63 if crc8(&data) == 0 {
64 let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; 64 let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;
65 info!("Read device {:x}: {} deg C", device, temp); 65 info!("Read device {:x}: {} deg C", device, temp);
66 } else { 66 } else {
67 warn!("Reading device {:x} failed", device); 67 warn!("Reading device {:x} failed", device);
diff --git a/examples/rp/src/bin/pio_onewire_parasite.rs b/examples/rp/src/bin/pio_onewire_parasite.rs
index fd076dee0..78fb94b18 100644
--- a/examples/rp/src/bin/pio_onewire_parasite.rs
+++ b/examples/rp/src/bin/pio_onewire_parasite.rs
@@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) {
63 let mut data = [0; 9]; 63 let mut data = [0; 9];
64 onewire.read_bytes(&mut data).await; 64 onewire.read_bytes(&mut data).await;
65 if crc8(&data) == 0 { 65 if crc8(&data) == 0 {
66 let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; 66 let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;
67 info!("Read device {:x}: {} deg C", device, temp); 67 info!("Read device {:x}: {} deg C", device, temp);
68 } else { 68 } else {
69 warn!("Reading device {:x} failed. {:02x}", device, data); 69 warn!("Reading device {:x} failed. {:02x}", device, data);
diff --git a/examples/rp235x/src/bin/pio_onewire.rs b/examples/rp235x/src/bin/pio_onewire.rs
index 102f13c45..6432edb8a 100644
--- a/examples/rp235x/src/bin/pio_onewire.rs
+++ b/examples/rp235x/src/bin/pio_onewire.rs
@@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) {
61 let mut data = [0; 9]; 61 let mut data = [0; 9];
62 onewire.read_bytes(&mut data).await; 62 onewire.read_bytes(&mut data).await;
63 if crc8(&data) == 0 { 63 if crc8(&data) == 0 {
64 let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; 64 let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;
65 info!("Read device {:x}: {} deg C", device, temp); 65 info!("Read device {:x}: {} deg C", device, temp);
66 } else { 66 } else {
67 warn!("Reading device {:x} failed", device); 67 warn!("Reading device {:x} failed", device);
diff --git a/examples/rp235x/src/bin/pio_onewire_parasite.rs b/examples/rp235x/src/bin/pio_onewire_parasite.rs
index fd076dee0..78fb94b18 100644
--- a/examples/rp235x/src/bin/pio_onewire_parasite.rs
+++ b/examples/rp235x/src/bin/pio_onewire_parasite.rs
@@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) {
63 let mut data = [0; 9]; 63 let mut data = [0; 9];
64 onewire.read_bytes(&mut data).await; 64 onewire.read_bytes(&mut data).await;
65 if crc8(&data) == 0 { 65 if crc8(&data) == 0 {
66 let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; 66 let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;
67 info!("Read device {:x}: {} deg C", device, temp); 67 info!("Read device {:x}: {} deg C", device, temp);
68 } else { 68 } else {
69 warn!("Reading device {:x} failed. {:02x}", device, data); 69 warn!("Reading device {:x} failed. {:02x}", device, data);
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs
index c24f01753..f8da91336 100644
--- a/examples/stm32f4/src/bin/adc_dma.rs
+++ b/examples/stm32f4/src/bin/adc_dma.rs
@@ -4,7 +4,7 @@ use cortex_m::singleton;
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::Peripherals; 6use embassy_stm32::Peripherals;
7use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence}; 7use embassy_stm32::adc::{Adc, AdcChannel, RingBufferedAdc, SampleTime};
8use embassy_time::Instant; 8use embassy_time::Instant;
9use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
10 10
@@ -15,7 +15,7 @@ async fn main(spawner: Spawner) {
15} 15}
16 16
17#[embassy_executor::task] 17#[embassy_executor::task]
18async fn adc_task(mut p: Peripherals) { 18async fn adc_task(p: Peripherals) {
19 const ADC_BUF_SIZE: usize = 1024; 19 const ADC_BUF_SIZE: usize = 1024;
20 let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); 20 let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
21 let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); 21 let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();
@@ -23,13 +23,24 @@ async fn adc_task(mut p: Peripherals) {
23 let adc = Adc::new(p.ADC1); 23 let adc = Adc::new(p.ADC1);
24 let adc2 = Adc::new(p.ADC2); 24 let adc2 = Adc::new(p.ADC2);
25 25
26 let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_data); 26 let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(
27 let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered(p.DMA2_CH2, adc_data2); 27 p.DMA2_CH0,
28 28 adc_data,
29 adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); 29 [
30 adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112); 30 (&mut p.PA0.degrade_adc(), SampleTime::CYCLES112),
31 adc2.set_sample_sequence(Sequence::One, &mut p.PA1, SampleTime::CYCLES112); 31 (&mut p.PA2.degrade_adc(), SampleTime::CYCLES112),
32 adc2.set_sample_sequence(Sequence::Two, &mut p.PA3, SampleTime::CYCLES112); 32 ]
33 .into_iter(),
34 );
35 let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered(
36 p.DMA2_CH2,
37 adc_data2,
38 [
39 (&mut p.PA1.degrade_adc(), SampleTime::CYCLES112),
40 (&mut p.PA3.degrade_adc(), SampleTime::CYCLES112),
41 ]
42 .into_iter(),
43 );
33 44
34 // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around 45 // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around
35 // to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of 46 // to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of
diff --git a/examples/stm32g0/src/bin/onewire_ds18b20.rs b/examples/stm32g0/src/bin/onewire_ds18b20.rs
index 62f8711a6..43ecca8c4 100644
--- a/examples/stm32g0/src/bin/onewire_ds18b20.rs
+++ b/examples/stm32g0/src/bin/onewire_ds18b20.rs
@@ -267,7 +267,7 @@ where
267 } 267 }
268 268
269 match Self::crc8(&data) == 0 { 269 match Self::crc8(&data) == 0 {
270 true => Ok(((data[1] as u16) << 8 | data[0] as u16) as f32 / 16.), 270 true => Ok(((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.),
271 false => Err(()), 271 false => Err(()),
272 } 272 }
273 } 273 }
diff --git a/examples/stm32h5/src/bin/stop.rs b/examples/stm32h5/src/bin/stop.rs
index 3c4f49f64..2026d8f99 100644
--- a/examples/stm32h5/src/bin/stop.rs
+++ b/examples/stm32h5/src/bin/stop.rs
@@ -12,7 +12,6 @@ use embassy_stm32::rcc::{HSIPrescaler, LsConfig};
12use embassy_stm32::rtc::{Rtc, RtcConfig}; 12use embassy_stm32::rtc::{Rtc, RtcConfig};
13use embassy_stm32::{Config, Peri}; 13use embassy_stm32::{Config, Peri};
14use embassy_time::Timer; 14use embassy_time::Timer;
15use static_cell::StaticCell;
16use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
17 16
18#[cortex_m_rt::entry] 17#[cortex_m_rt::entry]
@@ -39,8 +38,6 @@ async fn async_main(spawner: Spawner) {
39 38
40 // give the RTC to the executor... 39 // give the RTC to the executor...
41 let rtc = Rtc::new(p.RTC, RtcConfig::default()); 40 let rtc = Rtc::new(p.RTC, RtcConfig::default());
42 static RTC: StaticCell<Rtc> = StaticCell::new();
43 let rtc = RTC.init(rtc);
44 embassy_stm32::low_power::stop_with_rtc(rtc); 41 embassy_stm32::low_power::stop_with_rtc(rtc);
45 42
46 spawner.spawn(unwrap!(blinky(p.PB4.into()))); 43 spawner.spawn(unwrap!(blinky(p.PB4.into())));
diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs
new file mode 100644
index 000000000..7a9200edd
--- /dev/null
+++ b/examples/stm32l4/src/bin/adc_dma.rs
@@ -0,0 +1,51 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::adc::{Adc, AdcChannel, SampleTime};
8use {defmt_rtt as _, panic_probe as _};
9
10const DMA_BUF_LEN: usize = 512;
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 info!("Hello World!");
15
16 let mut config = Config::default();
17 {
18 use embassy_stm32::rcc::*;
19 config.rcc.mux.adcsel = mux::Adcsel::SYS;
20 }
21 let p = embassy_stm32::init(config);
22
23 let mut adc = Adc::new(p.ADC1);
24 let mut adc_pin0 = p.PA0.degrade_adc();
25 let mut adc_pin1 = p.PA1.degrade_adc();
26 let mut adc_dma_buf = [0u16; DMA_BUF_LEN];
27 let mut measurements = [0u16; DMA_BUF_LEN / 2];
28 let mut ring_buffered_adc = adc.into_ring_buffered(
29 p.DMA1_CH1,
30 &mut adc_dma_buf,
31 [
32 (&mut adc_pin0, SampleTime::CYCLES640_5),
33 (&mut adc_pin1, SampleTime::CYCLES640_5),
34 ]
35 .into_iter(),
36 );
37
38 info!("starting measurement loop");
39 loop {
40 match ring_buffered_adc.read(&mut measurements).await {
41 Ok(_) => {
42 //note: originally there was a print here showing all the samples,
43 //but even that takes too much time and would cause adc overruns
44 info!("adc1 first 10 samples: {}", measurements[0..10]);
45 }
46 Err(e) => {
47 warn!("Error: {:?}", e);
48 }
49 }
50 }
51}
diff --git a/examples/stm32l5/src/bin/stop.rs b/examples/stm32l5/src/bin/stop.rs
index c34053190..7662dbfa8 100644
--- a/examples/stm32l5/src/bin/stop.rs
+++ b/examples/stm32l5/src/bin/stop.rs
@@ -9,7 +9,6 @@ use embassy_stm32::rcc::LsConfig;
9use embassy_stm32::rtc::{Rtc, RtcConfig}; 9use embassy_stm32::rtc::{Rtc, RtcConfig};
10use embassy_stm32::{Config, Peri}; 10use embassy_stm32::{Config, Peri};
11use embassy_time::Timer; 11use embassy_time::Timer;
12use static_cell::StaticCell;
13use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
14 13
15#[cortex_m_rt::entry] 14#[cortex_m_rt::entry]
@@ -30,8 +29,6 @@ async fn async_main(spawner: Spawner) {
30 29
31 // give the RTC to the executor... 30 // give the RTC to the executor...
32 let rtc = Rtc::new(p.RTC, RtcConfig::default()); 31 let rtc = Rtc::new(p.RTC, RtcConfig::default());
33 static RTC: StaticCell<Rtc> = StaticCell::new();
34 let rtc = RTC.init(rtc);
35 embassy_stm32::low_power::stop_with_rtc(rtc); 32 embassy_stm32::low_power::stop_with_rtc(rtc);
36 33
37 spawner.spawn(unwrap!(blinky(p.PC7.into()))); 34 spawner.spawn(unwrap!(blinky(p.PC7.into())));
diff --git a/examples/stm32wle5/.cargo/config.toml b/examples/stm32wle5/.cargo/config.toml
new file mode 100644
index 000000000..0178d377c
--- /dev/null
+++ b/examples/stm32wle5/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip STM32WLE5JCIx --connect-under-reset"
4
5[build]
6target = "thumbv7em-none-eabi"
7
8[env]
9DEFMT_LOG = "info"
diff --git a/examples/stm32wle5/Cargo.toml b/examples/stm32wle5/Cargo.toml
new file mode 100644
index 000000000..f2fc4dd3d
--- /dev/null
+++ b/examples/stm32wle5/Cargo.toml
@@ -0,0 +1,38 @@
1[package]
2edition = "2024"
3name = "embassy-stm32wl-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6publish = false
7
8[dependencies]
9# Change stm32wl55jc-cm4 to your chip name, if necessary.
10embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = ["defmt", "stm32wle5jc", "time-driver-any", "memory-x", "unstable-pac", "exti", "low-power"] }
11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-1_000"] }
14embassy-embedded-hal = { version = "0.5.0", path = "../../embassy-embedded-hal" }
15
16defmt = "1.0.1"
17defmt-rtt = { version = "1.1.0", optional = true }
18defmt-serial = { version = "0.10.0", optional = true }
19
20cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
21cortex-m-rt = "0.7.0"
22embedded-hal = "1.0.0"
23embedded-storage = "0.3.1"
24panic-probe = { version = "1.0.0", features = ["print-defmt"] }
25static_cell = { version = "2.1.1", default-features = false }
26
27[profile.release]
28debug = 2
29
30[package.metadata.embassy]
31build = [
32 { target = "thumbv7em-none-eabi", artifact-dir = "out/examples/stm32wl" }
33]
34
35[features]
36default = ["defmt-serial"]
37defmt-rtt = ["dep:defmt-rtt"]
38defmt-serial = ["dep:defmt-serial"]
diff --git a/examples/stm32wle5/README.md b/examples/stm32wle5/README.md
new file mode 100644
index 000000000..18c3b5071
--- /dev/null
+++ b/examples/stm32wle5/README.md
@@ -0,0 +1,52 @@
1# Low Power Examples for STM32WLEx family
2
3Examples in this repo should work with [LoRa-E5 Dev Board](https://www.st.com/en/partner-products-and-services/lora-e5-development-kit.html) board.
4
5## Prerequsits
6
7- Connect a usb serial adapter to LPUart1 (this is where ALL logging will go)
8- Optional: Connect an amp meter that ran measure down to 0.1uA to the power test pins
9- `cargo install defmt-print` so you can print log messahes from LPUart1
10
11## Example Notes
12
13All examples will set all pins to analog mode before configuring pins for the example, if any. This saves about 500uA!!!!
14
15- the `adc` example will sleep in STOP1 betwen samples and the chip will only draw about 13uA while sleeping
16- the `blinky` example will sleep in STOP2 and the chip will only draw 1uA or less while sleeping
17- the `button_exti` example will sleep in STOP2 and the chip will only draw 1uA or less while sleeping
18- the `i2c` examples will sleep in STOP1 between reads and the chip only draw about 10uA while sleeping
19
20For each example you will need to start `defmt-print` with the example binary and the correct serial port in a seperate terminal. Example:
21```
22defmt-print -w -v -e target/thumbv7em-none-eabi/debug/<module-name> serial --path /dev/cu.usbserial-00000000 --baud 115200
23```
24
25Run individual examples with
26```
27cargo flash --chip STM32WLE5JCIx --connect-under-reset --bin <module-name>
28```
29for example
30```
31cargo flash --chip STM32WLE5JCIx --connect-under-reset --bin blinky
32```
33
34You can also run them with with `run`. However in this case expect probe-rs to be disconnected as soon as flashing is done as all IO pins are set to analog input!
35```
36cargo run --bin blinky
37```
38
39## Checklist before running examples
40You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.
41
42* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L432KCU6 it should be `probe-rs run --chip STM32L432KCUx`. (use `probe-rs chip list` to find your chip)
43* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L432KCU6 it should be `stm32l432kc`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.
44* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.
45* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic
46
47If you are unsure, please drop by the Embassy Matrix chat for support, and let us know:
48
49* Which example you are trying to run
50* Which chip and board you are using
51
52Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org
diff --git a/examples/stm32wle5/build.rs b/examples/stm32wle5/build.rs
new file mode 100644
index 000000000..8cd32d7ed
--- /dev/null
+++ b/examples/stm32wle5/build.rs
@@ -0,0 +1,5 @@
1fn main() {
2 println!("cargo:rustc-link-arg-bins=--nmagic");
3 println!("cargo:rustc-link-arg-bins=-Tlink.x");
4 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
5}
diff --git a/examples/stm32wle5/src/bin/adc.rs b/examples/stm32wle5/src/bin/adc.rs
new file mode 100644
index 000000000..ff1a5fa16
--- /dev/null
+++ b/examples/stm32wle5/src/bin/adc.rs
@@ -0,0 +1,100 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt as _;
7use embassy_executor::Spawner;
8use embassy_stm32::adc::{Adc, SampleTime};
9use embassy_stm32::low_power::Executor;
10use embassy_stm32::rtc::{Rtc, RtcConfig};
11use embassy_time::Timer;
12use panic_probe as _;
13use static_cell::StaticCell;
14
15#[cortex_m_rt::entry]
16fn main() -> ! {
17 info!("main: Starting!");
18 Executor::take().run(|spawner| {
19 spawner.spawn(unwrap!(async_main(spawner)));
20 });
21}
22
23#[embassy_executor::task]
24async fn async_main(_spawner: Spawner) {
25 let mut config = embassy_stm32::Config::default();
26 // enable HSI clock
27 config.rcc.hsi = true;
28 // enable LSI clock for RTC
29 config.rcc.ls = embassy_stm32::rcc::LsConfig::default_lsi();
30 config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);
31 config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;
32 // enable ADC with HSI clock
33 config.rcc.mux.adcsel = embassy_stm32::pac::rcc::vals::Adcsel::HSI;
34 #[cfg(feature = "defmt-serial")]
35 {
36 // disable debug during sleep to reduce power consumption since we are
37 // using defmt-serial on LPUART1.
38 config.enable_debug_during_sleep = false;
39 // if we are using defmt-serial on LPUART1, we need to use HSI for the clock
40 // so that its registers are preserved during STOP modes.
41 config.rcc.mux.lpuart1sel = embassy_stm32::pac::rcc::vals::Lpuart1sel::HSI;
42 }
43 // Initialize STM32WL peripherals (use default config like wio-e5-async example)
44 let p = embassy_stm32::init(config);
45
46 // start with all GPIOs as analog to reduce power consumption
47 for r in [
48 embassy_stm32::pac::GPIOA,
49 embassy_stm32::pac::GPIOB,
50 embassy_stm32::pac::GPIOC,
51 embassy_stm32::pac::GPIOH,
52 ] {
53 r.moder().modify(|w| {
54 for i in 0..16 {
55 // don't reset these if probe-rs should stay connected!
56 #[cfg(feature = "defmt-rtt")]
57 if config.enable_debug_during_sleep && r == embassy_stm32::pac::GPIOA && [13, 14].contains(&i) {
58 continue;
59 }
60 w.set_moder(i, embassy_stm32::pac::gpio::vals::Moder::ANALOG);
61 }
62 });
63 }
64 #[cfg(feature = "defmt-serial")]
65 {
66 use embassy_stm32::mode::Blocking;
67 use embassy_stm32::usart::Uart;
68 let config = embassy_stm32::usart::Config::default();
69 let uart = Uart::new_blocking(p.LPUART1, p.PC0, p.PC1, config).expect("failed to configure UART!");
70 static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();
71 defmt_serial::defmt_serial(SERIAL.init(uart));
72 }
73
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!");
80
81 let mut adc = Adc::new(p.ADC1);
82 adc.set_sample_time(SampleTime::CYCLES79_5);
83 let mut pin = p.PA10;
84
85 let mut vrefint = adc.enable_vrefint();
86 let vrefint_sample = adc.blocking_read(&mut vrefint);
87 let convert_to_millivolts = |sample| {
88 // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf
89 // 6.3.3 Embedded internal reference voltage
90 const VREFINT_MV: u32 = 1212; // mV
91
92 (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16
93 };
94
95 loop {
96 let v = adc.blocking_read(&mut pin);
97 info!("--> {} - {} mV", v, convert_to_millivolts(v));
98 Timer::after_secs(1).await;
99 }
100}
diff --git a/examples/stm32wle5/src/bin/blinky.rs b/examples/stm32wle5/src/bin/blinky.rs
new file mode 100644
index 000000000..1191a1157
--- /dev/null
+++ b/examples/stm32wle5/src/bin/blinky.rs
@@ -0,0 +1,90 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt as _;
7use embassy_executor::Spawner;
8use embassy_stm32::gpio::{Level, Output, Speed};
9use embassy_stm32::low_power::Executor;
10use embassy_stm32::rtc::{Rtc, RtcConfig};
11use embassy_time::Timer;
12use panic_probe as _;
13use static_cell::StaticCell;
14
15#[cortex_m_rt::entry]
16fn main() -> ! {
17 info!("main: Starting!");
18 Executor::take().run(|spawner| {
19 spawner.spawn(unwrap!(async_main(spawner)));
20 });
21}
22
23#[embassy_executor::task]
24async fn async_main(_spawner: Spawner) {
25 let mut config = embassy_stm32::Config::default();
26 // enable HSI clock
27 config.rcc.hsi = true;
28 // enable LSI clock for RTC
29 config.rcc.ls = embassy_stm32::rcc::LsConfig::default_lsi();
30 config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);
31 config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;
32 #[cfg(feature = "defmt-serial")]
33 {
34 // disable debug during sleep to reduce power consumption since we are
35 // using defmt-serial on LPUART1.
36 config.enable_debug_during_sleep = false;
37 // if we are using defmt-serial on LPUART1, we need to use HSI for the clock
38 // so that its registers are preserved during STOP modes.
39 config.rcc.mux.lpuart1sel = embassy_stm32::pac::rcc::vals::Lpuart1sel::HSI;
40 }
41 // Initialize STM32WL peripherals (use default config like wio-e5-async example)
42 let p = embassy_stm32::init(config);
43
44 // start with all GPIOs as analog to reduce power consumption
45 for r in [
46 embassy_stm32::pac::GPIOA,
47 embassy_stm32::pac::GPIOB,
48 embassy_stm32::pac::GPIOC,
49 embassy_stm32::pac::GPIOH,
50 ] {
51 r.moder().modify(|w| {
52 for i in 0..16 {
53 // don't reset these if probe-rs should stay connected!
54 #[cfg(feature = "defmt-rtt")]
55 if config.enable_debug_during_sleep && r == embassy_stm32::pac::GPIOA && [13, 14].contains(&i) {
56 continue;
57 }
58 w.set_moder(i, embassy_stm32::pac::gpio::vals::Moder::ANALOG);
59 }
60 });
61 }
62 #[cfg(feature = "defmt-serial")]
63 {
64 use embassy_stm32::mode::Blocking;
65 use embassy_stm32::usart::Uart;
66 let config = embassy_stm32::usart::Config::default();
67 let uart = Uart::new_blocking(p.LPUART1, p.PC0, p.PC1, config).expect("failed to configure UART!");
68 static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();
69 defmt_serial::defmt_serial(SERIAL.init(uart));
70 }
71
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!");
78
79 let mut led = Output::new(p.PB5, Level::High, Speed::Low);
80
81 loop {
82 info!("low");
83 led.set_low();
84 Timer::after_millis(500).await;
85
86 info!("high");
87 led.set_high();
88 Timer::after_millis(500).await;
89 }
90}
diff --git a/examples/stm32wle5/src/bin/button_exti.rs b/examples/stm32wle5/src/bin/button_exti.rs
new file mode 100644
index 000000000..f07f9724d
--- /dev/null
+++ b/examples/stm32wle5/src/bin/button_exti.rs
@@ -0,0 +1,91 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt as _;
7use embassy_executor::Spawner;
8use embassy_stm32::exti::ExtiInput;
9use embassy_stm32::gpio::Pull;
10use embassy_stm32::low_power::Executor;
11use embassy_stm32::rtc::{Rtc, RtcConfig};
12use panic_probe as _;
13use static_cell::StaticCell;
14
15#[cortex_m_rt::entry]
16fn main() -> ! {
17 info!("main: Starting!");
18 Executor::take().run(|spawner| {
19 spawner.spawn(unwrap!(async_main(spawner)));
20 });
21}
22
23#[embassy_executor::task]
24async fn async_main(_spawner: Spawner) {
25 let mut config = embassy_stm32::Config::default();
26 // enable HSI clock
27 config.rcc.hsi = true;
28 // enable LSI clock for RTC
29 config.rcc.ls = embassy_stm32::rcc::LsConfig::default_lsi();
30 config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);
31 config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;
32 // enable ADC with HSI clock
33 config.rcc.mux.adcsel = embassy_stm32::pac::rcc::vals::Adcsel::HSI;
34 #[cfg(feature = "defmt-serial")]
35 {
36 // disable debug during sleep to reduce power consumption since we are
37 // using defmt-serial on LPUART1.
38 config.enable_debug_during_sleep = false;
39 // if we are using defmt-serial on LPUART1, we need to use HSI for the clock
40 // so that its registers are preserved during STOP modes.
41 config.rcc.mux.lpuart1sel = embassy_stm32::pac::rcc::vals::Lpuart1sel::HSI;
42 }
43 // Initialize STM32WL peripherals (use default config like wio-e5-async example)
44 let p = embassy_stm32::init(config);
45
46 // start with all GPIOs as analog to reduce power consumption
47 for r in [
48 embassy_stm32::pac::GPIOA,
49 embassy_stm32::pac::GPIOB,
50 embassy_stm32::pac::GPIOC,
51 embassy_stm32::pac::GPIOH,
52 ] {
53 r.moder().modify(|w| {
54 for i in 0..16 {
55 // don't reset these if probe-rs should stay connected!
56 #[cfg(feature = "defmt-rtt")]
57 if config.enable_debug_during_sleep && r == embassy_stm32::pac::GPIOA && [13, 14].contains(&i) {
58 continue;
59 }
60 w.set_moder(i, embassy_stm32::pac::gpio::vals::Moder::ANALOG);
61 }
62 });
63 }
64 #[cfg(feature = "defmt-serial")]
65 {
66 use embassy_stm32::mode::Blocking;
67 use embassy_stm32::usart::Uart;
68 let config = embassy_stm32::usart::Config::default();
69 let uart = Uart::new_blocking(p.LPUART1, p.PC0, p.PC1, config).expect("failed to configure UART!");
70 static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();
71 defmt_serial::defmt_serial(SERIAL.init(uart));
72 }
73
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!");
80
81 let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up);
82
83 info!("Press the USER button...");
84
85 loop {
86 button.wait_for_falling_edge().await;
87 info!("Pressed!");
88 button.wait_for_rising_edge().await;
89 info!("Released!");
90 }
91}
diff --git a/examples/stm32wle5/src/bin/i2c.rs b/examples/stm32wle5/src/bin/i2c.rs
new file mode 100644
index 000000000..af07f911e
--- /dev/null
+++ b/examples/stm32wle5/src/bin/i2c.rs
@@ -0,0 +1,110 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5#[cfg(feature = "defmt-rtt")]
6use defmt_rtt as _;
7use embassy_executor::Spawner;
8use embassy_stm32::i2c::I2c;
9use embassy_stm32::low_power::Executor;
10use embassy_stm32::rtc::{Rtc, RtcConfig};
11use embassy_stm32::time::Hertz;
12use embassy_stm32::{bind_interrupts, i2c, peripherals};
13use embassy_time::{Duration, Timer};
14use panic_probe as _;
15use static_cell::StaticCell;
16
17bind_interrupts!(struct IrqsI2C{
18 I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
19 I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
20});
21
22#[cortex_m_rt::entry]
23fn main() -> ! {
24 info!("main: Starting!");
25 Executor::take().run(|spawner| {
26 spawner.spawn(unwrap!(async_main(spawner)));
27 });
28}
29
30#[embassy_executor::task]
31async fn async_main(_spawner: Spawner) {
32 let mut config = embassy_stm32::Config::default();
33 // enable HSI clock
34 config.rcc.hsi = true;
35 // enable LSI clock for RTC
36 config.rcc.ls = embassy_stm32::rcc::LsConfig::default_lsi();
37 config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);
38 config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;
39 // enable ADC with HSI clock
40 config.rcc.mux.i2c2sel = embassy_stm32::pac::rcc::vals::I2c2sel::HSI;
41 #[cfg(feature = "defmt-serial")]
42 {
43 // disable debug during sleep to reduce power consumption since we are
44 // using defmt-serial on LPUART1.
45 config.enable_debug_during_sleep = false;
46 // if we are using defmt-serial on LPUART1, we need to use HSI for the clock
47 // so that its registers are preserved during STOP modes.
48 config.rcc.mux.lpuart1sel = embassy_stm32::pac::rcc::vals::Lpuart1sel::HSI;
49 }
50 // Initialize STM32WL peripherals (use default config like wio-e5-async example)
51 let p = embassy_stm32::init(config);
52
53 // start with all GPIOs as analog to reduce power consumption
54 for r in [
55 embassy_stm32::pac::GPIOA,
56 embassy_stm32::pac::GPIOB,
57 embassy_stm32::pac::GPIOC,
58 embassy_stm32::pac::GPIOH,
59 ] {
60 r.moder().modify(|w| {
61 for i in 0..16 {
62 // don't reset these if probe-rs should stay connected!
63 #[cfg(feature = "defmt-rtt")]
64 if config.enable_debug_during_sleep && r == embassy_stm32::pac::GPIOA && [13, 14].contains(&i) {
65 continue;
66 }
67 w.set_moder(i, embassy_stm32::pac::gpio::vals::Moder::ANALOG);
68 }
69 });
70 }
71 #[cfg(feature = "defmt-serial")]
72 {
73 use embassy_stm32::mode::Blocking;
74 use embassy_stm32::usart::Uart;
75 let config = embassy_stm32::usart::Config::default();
76 let uart = Uart::new_blocking(p.LPUART1, p.PC0, p.PC1, config).expect("failed to configure UART!");
77 static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();
78 defmt_serial::defmt_serial(SERIAL.init(uart));
79 }
80
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!");
87 let en3v3 = embassy_stm32::gpio::Output::new(
88 p.PA9,
89 embassy_stm32::gpio::Level::High,
90 embassy_stm32::gpio::Speed::High,
91 );
92 core::mem::forget(en3v3); // keep the output pin enabled
93
94 let mut i2c = I2c::new(p.I2C2, p.PB15, p.PA15, IrqsI2C, p.DMA1_CH6, p.DMA1_CH7, {
95 let mut config = i2c::Config::default();
96 config.frequency = Hertz::khz(100);
97 config.timeout = Duration::from_millis(500);
98 config
99 });
100
101 loop {
102 let mut buffer = [0; 2];
103 // read the temperature register of the onboard lm75
104 match i2c.read(0x48, &mut buffer).await {
105 Ok(_) => info!("--> {:?}", buffer),
106 Err(e) => info!("--> Error: {:?}", e),
107 }
108 Timer::after_secs(5).await;
109 }
110}
diff --git a/examples/stm32wle5/stm32wle5.code-workspace b/examples/stm32wle5/stm32wle5.code-workspace
new file mode 100644
index 000000000..a7c4a0ebd
--- /dev/null
+++ b/examples/stm32wle5/stm32wle5.code-workspace
@@ -0,0 +1,13 @@
1{
2 "folders": [
3 {
4 "path": "."
5 }
6 ],
7 "settings": {
8 "rust-analyzer.cargo.target": "thumbv7em-none-eabi",
9 "rust-analyzer.cargo.allTargets": false,
10 "rust-analyzer.cargo.targetDir": "target/rust-analyzer",
11 "rust-analyzer.checkOnSave": true,
12 }
13}
diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs
index 833ca05d0..73580f0f2 100644
--- a/tests/stm32/src/bin/stop.rs
+++ b/tests/stm32/src/bin/stop.rs
@@ -14,7 +14,6 @@ use embassy_stm32::low_power::{Executor, StopMode, stop_ready, stop_with_rtc};
14use embassy_stm32::rcc::LsConfig; 14use embassy_stm32::rcc::LsConfig;
15use embassy_stm32::rtc::{Rtc, RtcConfig}; 15use embassy_stm32::rtc::{Rtc, RtcConfig};
16use embassy_time::Timer; 16use embassy_time::Timer;
17use static_cell::StaticCell;
18 17
19#[entry] 18#[entry]
20fn main() -> ! { 19fn main() -> ! {
@@ -70,9 +69,6 @@ async fn async_main(spawner: Spawner) {
70 69
71 rtc.set_datetime(now.into()).expect("datetime not set"); 70 rtc.set_datetime(now.into()).expect("datetime not set");
72 71
73 static RTC: StaticCell<Rtc> = StaticCell::new();
74 let rtc = RTC.init(rtc);
75
76 stop_with_rtc(rtc); 72 stop_with_rtc(rtc);
77 73
78 spawner.spawn(task_1().unwrap()); 74 spawner.spawn(task_1().unwrap());