aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-19 15:46:34 -0600
committerGitHub <[email protected]>2025-11-19 15:46:34 -0600
commite5a95221ba2ab05f3e7000c206049654fe28af92 (patch)
tree82cf5e8a35a69e617e19b15b52c26eddffe310ef
parente8a187f758e0d6d2b8f9080d399baa1073dc6a74 (diff)
parent4d040403f56da169337b10008027df28ecb921e7 (diff)
Merge branch 'main' into exti
-rw-r--r--embassy-stm32/CHANGELOG.md2
-rw-r--r--embassy-stm32/src/flash/h7.rs190
-rw-r--r--embassy-stm32/src/flash/mod.rs4
-rw-r--r--embassy-stm32/src/hsem/mod.rs60
-rw-r--r--embassy-stm32/src/lib.rs3
-rw-r--r--embassy-stm32/src/low_power.rs68
-rw-r--r--examples/stm32h7/src/bin/flash_async.rs84
-rw-r--r--examples/stm32wb/Cargo.toml2
8 files changed, 354 insertions, 59 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 9479ea7b1..d2f675dbc 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- feat: add poll_for methods to exti 10- feat: add poll_for methods to exti
11- feat: implement stop for stm32wb.
11- change: rework hsem and add HIL test for some chips. 12- change: rework hsem and add HIL test for some chips.
12- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) 13- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871))
13- feat: allow embassy_executor::main for low power 14- feat: allow embassy_executor::main for low power
@@ -69,6 +70,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
69- feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) 70- feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874))
70- fix: fixing channel numbers on vbat and vddcore for adc on adc 71- fix: fixing channel numbers on vbat and vddcore for adc on adc
71- adc: adding disable to vbat 72- adc: adding disable to vbat
73- feat: stm32/flash: add async support for h7 family
72 74
73## 0.4.0 - 2025-08-26 75## 0.4.0 - 2025-08-26
74 76
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs
index 8a43cce3f..b342f4a83 100644
--- a/embassy-stm32/src/flash/h7.rs
+++ b/embassy-stm32/src/flash/h7.rs
@@ -1,10 +1,31 @@
1use core::ptr::write_volatile; 1use core::ptr::write_volatile;
2use core::sync::atomic::{Ordering, fence}; 2use core::sync::atomic::{Ordering, fence};
3 3
4use embassy_sync::waitqueue::AtomicWaker;
5use pac::flash::regs::Sr;
6
4use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; 7use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE};
5use crate::flash::Error; 8use crate::flash::Error;
6use crate::pac; 9use crate::pac;
7 10
11static WAKER: AtomicWaker = AtomicWaker::new();
12
13pub(crate) unsafe fn on_interrupt() {
14 // Clear IRQ flags
15 pac::FLASH.bank(0).ccr().write(|w| {
16 w.set_clr_eop(true);
17 w.set_clr_operr(true);
18 });
19 if is_dual_bank() {
20 pac::FLASH.bank(1).ccr().write(|w| {
21 w.set_clr_eop(true);
22 w.set_clr_operr(true);
23 });
24 }
25
26 WAKER.wake();
27}
28
8const fn is_dual_bank() -> bool { 29const fn is_dual_bank() -> bool {
9 FLASH_REGIONS.len() >= 2 30 FLASH_REGIONS.len() >= 2
10} 31}
@@ -29,12 +50,68 @@ pub(crate) unsafe fn unlock() {
29 } 50 }
30} 51}
31 52
53pub(crate) unsafe fn enable_write() {
54 enable_blocking_write();
55}
56
57pub(crate) unsafe fn disable_write() {
58 disable_blocking_write();
59}
60
32pub(crate) unsafe fn enable_blocking_write() { 61pub(crate) unsafe fn enable_blocking_write() {
33 assert_eq!(0, WRITE_SIZE % 4); 62 assert_eq!(0, WRITE_SIZE % 4);
34} 63}
35 64
36pub(crate) unsafe fn disable_blocking_write() {} 65pub(crate) unsafe fn disable_blocking_write() {}
37 66
67pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
68 // We cannot have the write setup sequence in begin_write as it depends on the address
69 let bank = if start_address < BANK1_REGION.end() {
70 pac::FLASH.bank(0)
71 } else {
72 pac::FLASH.bank(1)
73 };
74 bank.cr().write(|w| {
75 w.set_pg(true);
76 #[cfg(flash_h7)]
77 w.set_psize(2); // 32 bits at once
78 w.set_eopie(true);
79 w.set_operrie(true);
80 });
81 cortex_m::asm::isb();
82 cortex_m::asm::dsb();
83 fence(Ordering::SeqCst);
84
85 let mut res = None;
86 let mut address = start_address;
87 for val in buf.chunks(4) {
88 write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));
89 address += val.len() as u32;
90
91 res = Some(wait_ready(bank).await);
92 bank.sr().modify(|w| {
93 if w.eop() {
94 w.set_eop(true);
95 }
96 });
97 if unwrap!(res).is_err() {
98 break;
99 }
100 }
101
102 cortex_m::asm::isb();
103 cortex_m::asm::dsb();
104 fence(Ordering::SeqCst);
105
106 bank.cr().write(|w| {
107 w.set_pg(false);
108 w.set_eopie(false);
109 w.set_operrie(false);
110 });
111
112 unwrap!(res)
113}
114
38pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { 115pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
39 // We cannot have the write setup sequence in begin_write as it depends on the address 116 // We cannot have the write setup sequence in begin_write as it depends on the address
40 let bank = if start_address < BANK1_REGION.end() { 117 let bank = if start_address < BANK1_REGION.end() {
@@ -77,6 +154,36 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE])
77 unwrap!(res) 154 unwrap!(res)
78} 155}
79 156
157pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> {
158 let bank = pac::FLASH.bank(sector.bank as usize);
159 bank.cr().modify(|w| {
160 w.set_ser(true);
161 #[cfg(flash_h7)]
162 w.set_snb(sector.index_in_bank);
163 #[cfg(flash_h7ab)]
164 w.set_ssn(sector.index_in_bank);
165 w.set_eopie(true);
166 w.set_operrie(true);
167 });
168
169 bank.cr().modify(|w| {
170 w.set_start(true);
171 });
172
173 cortex_m::asm::isb();
174 cortex_m::asm::dsb();
175 fence(Ordering::SeqCst);
176
177 let ret: Result<(), Error> = wait_ready(bank).await;
178 bank.cr().modify(|w| {
179 w.set_ser(false);
180 w.set_eopie(false);
181 w.set_operrie(false);
182 });
183 bank_clear_all_err(bank);
184 ret
185}
186
80pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { 187pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
81 let bank = pac::FLASH.bank(sector.bank as usize); 188 let bank = pac::FLASH.bank(sector.bank as usize);
82 bank.cr().modify(|w| { 189 bank.cr().modify(|w| {
@@ -112,46 +219,59 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
112 bank.sr().modify(|_| {}); 219 bank.sr().modify(|_| {});
113} 220}
114 221
222async fn wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
223 use core::future::poll_fn;
224 use core::task::Poll;
225
226 poll_fn(|cx| {
227 WAKER.register(cx.waker());
228
229 let sr = bank.sr().read();
230 if !sr.bsy() && !sr.qw() {
231 Poll::Ready(get_result(sr))
232 } else {
233 return Poll::Pending;
234 }
235 })
236 .await
237}
238
115unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { 239unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
116 loop { 240 loop {
117 let sr = bank.sr().read(); 241 let sr = bank.sr().read();
118 242
119 if !sr.bsy() && !sr.qw() { 243 if !sr.bsy() && !sr.qw() {
120 if sr.wrperr() { 244 return get_result(sr);
121 return Err(Error::Protected);
122 }
123 if sr.pgserr() {
124 error!("pgserr");
125 return Err(Error::Seq);
126 }
127 if sr.incerr() {
128 // writing to a different address when programming 256 bit word was not finished
129 error!("incerr");
130 return Err(Error::Seq);
131 }
132 if sr.crcrderr() {
133 error!("crcrderr");
134 return Err(Error::Seq);
135 }
136 if sr.operr() {
137 return Err(Error::Prog);
138 }
139 if sr.sneccerr1() {
140 // single ECC error
141 return Err(Error::Prog);
142 }
143 if sr.dbeccerr() {
144 // double ECC error
145 return Err(Error::Prog);
146 }
147 if sr.rdperr() {
148 return Err(Error::Protected);
149 }
150 if sr.rdserr() {
151 return Err(Error::Protected);
152 }
153
154 return Ok(());
155 } 245 }
156 } 246 }
157} 247}
248
249fn get_result(sr: Sr) -> Result<(), Error> {
250 if sr.wrperr() {
251 Err(Error::Protected)
252 } else if sr.pgserr() {
253 error!("pgserr");
254 Err(Error::Seq)
255 } else if sr.incerr() {
256 // writing to a different address when programming 256 bit word was not finished
257 error!("incerr");
258 Err(Error::Seq)
259 } else if sr.crcrderr() {
260 error!("crcrderr");
261 Err(Error::Seq)
262 } else if sr.operr() {
263 Err(Error::Prog)
264 } else if sr.sneccerr1() {
265 // single ECC error
266 Err(Error::Prog)
267 } else if sr.dbeccerr() {
268 // double ECC error
269 Err(Error::Prog)
270 } else if sr.rdperr() {
271 Err(Error::Protected)
272 } else if sr.rdserr() {
273 Err(Error::Protected)
274 } else {
275 Ok(())
276 }
277}
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index 39cd9b3a9..6211a37b7 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -1,14 +1,14 @@
1//! Flash memory (FLASH) 1//! Flash memory (FLASH)
2use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; 2use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
3 3
4#[cfg(flash_f4)] 4#[cfg(any(flash_f4, flash_h7, flash_h7ab))]
5mod asynch; 5mod asynch;
6#[cfg(flash)] 6#[cfg(flash)]
7mod common; 7mod common;
8#[cfg(eeprom)] 8#[cfg(eeprom)]
9mod eeprom; 9mod eeprom;
10 10
11#[cfg(flash_f4)] 11#[cfg(any(flash_f4, flash_h7, flash_h7ab))]
12pub use asynch::InterruptHandler; 12pub use asynch::InterruptHandler;
13#[cfg(flash)] 13#[cfg(flash)]
14pub use common::*; 14pub use common::*;
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs
index a6b910a53..5f1ed9b09 100644
--- a/embassy-stm32/src/hsem/mod.rs
+++ b/embassy-stm32/src/hsem/mod.rs
@@ -5,6 +5,8 @@ use core::marker::PhantomData;
5use core::sync::atomic::{Ordering, compiler_fence}; 5use core::sync::atomic::{Ordering, compiler_fence};
6use core::task::Poll; 6use core::task::Poll;
7 7
8#[cfg(all(stm32wb, feature = "low-power"))]
9use critical_section::CriticalSection;
8use embassy_hal_internal::PeripheralType; 10use embassy_hal_internal::PeripheralType;
9use embassy_sync::waitqueue::AtomicWaker; 11use embassy_sync::waitqueue::AtomicWaker;
10 12
@@ -78,6 +80,12 @@ impl CoreId {
78 } 80 }
79} 81}
80 82
83#[cfg(not(all(stm32wb, feature = "low-power")))]
84const PUB_CHANNELS: usize = 6;
85
86#[cfg(all(stm32wb, feature = "low-power"))]
87const PUB_CHANNELS: usize = 4;
88
81/// TX interrupt handler. 89/// TX interrupt handler.
82pub struct HardwareSemaphoreInterruptHandler<T: Instance> { 90pub struct HardwareSemaphoreInterruptHandler<T: Instance> {
83 _phantom: PhantomData<T>, 91 _phantom: PhantomData<T>,
@@ -127,7 +135,7 @@ pub struct HardwareSemaphoreChannel<'a, T: Instance> {
127} 135}
128 136
129impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { 137impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> {
130 pub(self) const fn new(number: u8) -> Self { 138 pub(crate) const fn new(number: u8) -> Self {
131 core::assert!(number > 0 && number <= 6); 139 core::assert!(number > 0 && number <= 6);
132 140
133 Self { 141 Self {
@@ -151,19 +159,29 @@ impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> {
151 .ier(core_id.to_index()) 159 .ier(core_id.to_index())
152 .modify(|w| w.set_ise(self.index as usize, true)); 160 .modify(|w| w.set_ise(self.index as usize, true));
153 161
154 if self.two_step_lock(process_id).is_ok() { 162 match self.try_lock(process_id) {
155 Poll::Ready(HardwareSemaphoreMutex { 163 Some(mutex) => Poll::Ready(mutex),
156 index: self.index, 164 None => Poll::Pending,
157 process_id: process_id,
158 _lifetime: PhantomData,
159 })
160 } else {
161 Poll::Pending
162 } 165 }
163 }) 166 })
164 .await 167 .await
165 } 168 }
166 169
170 /// Try to lock the semaphor
171 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to
172 /// check if the lock has been successful, carried out from the HSEM_Rx register.
173 pub fn try_lock(&mut self, process_id: u8) -> Option<HardwareSemaphoreMutex<'a, T>> {
174 if self.two_step_lock(process_id).is_ok() {
175 Some(HardwareSemaphoreMutex {
176 index: self.index,
177 process_id: process_id,
178 _lifetime: PhantomData,
179 })
180 } else {
181 None
182 }
183 }
184
167 /// Locks the semaphore. 185 /// Locks the semaphore.
168 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to 186 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to
169 /// check if the lock has been successful, carried out from the HSEM_Rx register. 187 /// check if the lock has been successful, carried out from the HSEM_Rx register.
@@ -206,9 +224,9 @@ impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> {
206 }); 224 });
207 } 225 }
208 226
209 /// Checks if the semaphore is locked. 227 /// Return the channel number
210 pub fn is_semaphore_locked(&self) -> bool { 228 pub const fn channel(&self) -> u8 {
211 T::regs().r(self.index as usize).read().lock() 229 self.index + 1
212 } 230 }
213} 231}
214 232
@@ -230,15 +248,22 @@ impl<T: Instance> HardwareSemaphore<T> {
230 248
231 /// Get a single channel, and keep the global struct 249 /// Get a single channel, and keep the global struct
232 pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> { 250 pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> {
251 #[cfg(all(stm32wb, feature = "low-power"))]
252 core::assert!(number != 3 && number != 4);
253
233 HardwareSemaphoreChannel::new(number) 254 HardwareSemaphoreChannel::new(number)
234 } 255 }
235 256
236 /// Split the global struct into channels 257 /// Split the global struct into channels
237 pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; 6] { 258 ///
259 /// If using low-power mode, channels 3 and 4 will not be returned
260 pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; PUB_CHANNELS] {
238 [ 261 [
239 HardwareSemaphoreChannel::new(1), 262 HardwareSemaphoreChannel::new(1),
240 HardwareSemaphoreChannel::new(2), 263 HardwareSemaphoreChannel::new(2),
264 #[cfg(not(all(stm32wb, feature = "low-power")))]
241 HardwareSemaphoreChannel::new(3), 265 HardwareSemaphoreChannel::new(3),
266 #[cfg(not(all(stm32wb, feature = "low-power")))]
242 HardwareSemaphoreChannel::new(4), 267 HardwareSemaphoreChannel::new(4),
243 HardwareSemaphoreChannel::new(5), 268 HardwareSemaphoreChannel::new(5),
244 HardwareSemaphoreChannel::new(6), 269 HardwareSemaphoreChannel::new(6),
@@ -267,6 +292,11 @@ impl<T: Instance> HardwareSemaphore<T> {
267 } 292 }
268} 293}
269 294
295#[cfg(all(stm32wb, feature = "low-power"))]
296pub(crate) fn init_hsem(_cs: CriticalSection) {
297 rcc::enable_and_reset::<crate::peripherals::HSEM>();
298}
299
270struct State { 300struct State {
271 wakers: [AtomicWaker; 6], 301 wakers: [AtomicWaker; 6],
272} 302}
@@ -278,8 +308,8 @@ impl State {
278 } 308 }
279 } 309 }
280 310
281 const fn waker_for(&self, number: u8) -> &AtomicWaker { 311 const fn waker_for(&self, index: u8) -> &AtomicWaker {
282 &self.wakers[number as usize] 312 &self.wakers[index as usize]
283 } 313 }
284} 314}
285 315
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 7c3770643..ef6f1d6dc 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -650,6 +650,9 @@ fn init_hw(config: Config) -> Peripherals {
650 650
651 #[cfg(feature = "low-power")] 651 #[cfg(feature = "low-power")]
652 rtc::init_rtc(cs, config.rtc, config.min_stop_pause); 652 rtc::init_rtc(cs, config.rtc, config.min_stop_pause);
653
654 #[cfg(all(stm32wb, feature = "low-power"))]
655 hsem::init_hsem(cs);
653 } 656 }
654 657
655 p 658 p
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index cf8f2b393..15478eed4 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -148,7 +148,7 @@ pub enum StopMode {
148} 148}
149 149
150#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] 150#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))]
151use stm32_metapac::pwr::vals::Lpms; 151use crate::pac::pwr::vals::Lpms;
152 152
153#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] 153#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))]
154impl Into<Lpms> for StopMode { 154impl Into<Lpms> for StopMode {
@@ -242,8 +242,63 @@ impl Executor {
242 } 242 }
243 } 243 }
244 244
245 #[cfg(all(stm32wb, feature = "low-power"))]
246 fn configure_stop_stm32wb(&self, _stop_mode: StopMode) -> Result<(), ()> {
247 use core::task::Poll;
248
249 use embassy_futures::poll_once;
250
251 use crate::hsem::HardwareSemaphoreChannel;
252 use crate::pac::rcc::vals::{Smps, Sw};
253 use crate::pac::{PWR, RCC};
254
255 let sem3_mutex = match poll_once(HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(3).lock(0)) {
256 Poll::Pending => None,
257 Poll::Ready(mutex) => Some(mutex),
258 }
259 .ok_or(())?;
260
261 let sem4_mutex = HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(4).try_lock(0);
262 if let Some(sem4_mutex) = sem4_mutex {
263 if PWR.extscr().read().c2ds() {
264 drop(sem4_mutex);
265 } else {
266 return Ok(());
267 }
268 }
269
270 // Sem4 not granted
271 // Set HSION
272 RCC.cr().modify(|w| {
273 w.set_hsion(true);
274 });
275
276 // Wait for HSIRDY
277 while !RCC.cr().read().hsirdy() {}
278
279 // Set SW to HSI
280 RCC.cfgr().modify(|w| {
281 w.set_sw(Sw::HSI);
282 });
283
284 // Wait for SWS to report HSI
285 while !RCC.cfgr().read().sws().eq(&Sw::HSI) {}
286
287 // Set SMPSSEL to HSI
288 RCC.smpscr().modify(|w| {
289 w.set_smpssel(Smps::HSI);
290 });
291
292 drop(sem3_mutex);
293
294 Ok(())
295 }
296
245 #[allow(unused_variables)] 297 #[allow(unused_variables)]
246 fn configure_stop(&self, stop_mode: StopMode) { 298 fn configure_stop(&self, stop_mode: StopMode) -> Result<(), ()> {
299 #[cfg(all(stm32wb, feature = "low-power"))]
300 self.configure_stop_stm32wb(stop_mode)?;
301
247 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] 302 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))]
248 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); 303 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
249 #[cfg(stm32h5)] 304 #[cfg(stm32h5)]
@@ -252,6 +307,8 @@ impl Executor {
252 v.set_lpms(vals::Lpms::STOP); 307 v.set_lpms(vals::Lpms::STOP);
253 v.set_svos(vals::Svos::SCALE3); 308 v.set_svos(vals::Svos::SCALE3);
254 }); 309 });
310
311 Ok(())
255 } 312 }
256 313
257 fn configure_pwr(&self) { 314 fn configure_pwr(&self) {
@@ -267,12 +324,11 @@ impl Executor {
267 critical_section::with(|cs| { 324 critical_section::with(|cs| {
268 let stop_mode = Self::stop_mode(cs)?; 325 let stop_mode = Self::stop_mode(cs)?;
269 let _ = get_driver().pause_time(cs).ok()?; 326 let _ = get_driver().pause_time(cs).ok()?;
327 self.configure_stop(stop_mode).ok()?;
270 328
271 Some(stop_mode) 329 Some(())
272 }) 330 })
273 .map(|stop_mode| { 331 .map(|_| {
274 self.configure_stop(stop_mode);
275
276 #[cfg(not(feature = "low-power-debug-with-sleep"))] 332 #[cfg(not(feature = "low-power-debug-with-sleep"))]
277 Self::get_scb().set_sleepdeep(); 333 Self::get_scb().set_sleepdeep();
278 }); 334 });
diff --git a/examples/stm32h7/src/bin/flash_async.rs b/examples/stm32h7/src/bin/flash_async.rs
new file mode 100644
index 000000000..96d1936f3
--- /dev/null
+++ b/examples/stm32h7/src/bin/flash_async.rs
@@ -0,0 +1,84 @@
1#![no_std]
2#![no_main]
3
4use defmt::{info, unwrap};
5use embassy_executor::Spawner;
6use embassy_stm32::flash::{Flash, InterruptHandler};
7use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
8use embassy_stm32::{Peri, bind_interrupts};
9use embassy_time::Timer;
10use {defmt_rtt as _, panic_probe as _};
11
12bind_interrupts!(struct Irqs {
13 FLASH => InterruptHandler;
14});
15
16#[embassy_executor::main]
17async fn main(spawner: Spawner) {
18 let p = embassy_stm32::init(Default::default());
19 info!("Hello Flash!");
20
21 let mut f = Flash::new(p.FLASH, Irqs);
22
23 // Led should blink uninterrupted during ~2sec erase operation
24 spawner.spawn(blinky(p.PB14.into()).unwrap());
25
26 // Test on bank 2 in order not to stall CPU.
27 test_flash(&mut f, 1024 * 1024, 128 * 1024).await;
28}
29
30#[embassy_executor::task]
31async fn blinky(p: Peri<'static, AnyPin>) {
32 let mut led = Output::new(p, Level::High, Speed::Low);
33
34 loop {
35 info!("high");
36 led.set_high();
37 Timer::after_millis(300).await;
38
39 info!("low");
40 led.set_low();
41 Timer::after_millis(300).await;
42 }
43}
44
45async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {
46 info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size);
47
48 info!("Reading...");
49 let mut buf = [0u8; 32];
50 unwrap!(f.blocking_read(offset, &mut buf));
51 info!("Read: {=[u8]:x}", buf);
52
53 info!("Erasing...");
54 unwrap!(f.erase(offset, offset + size).await);
55
56 info!("Reading...");
57 let mut buf = [0u8; 32];
58 unwrap!(f.blocking_read(offset, &mut buf));
59 info!("Read after erase: {=[u8]:x}", buf);
60
61 info!("Writing...");
62 unwrap!(
63 f.write(
64 offset,
65 &[
66 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
67 29, 30, 31, 32
68 ]
69 )
70 .await
71 );
72
73 info!("Reading...");
74 let mut buf = [0u8; 32];
75 unwrap!(f.blocking_read(offset, &mut buf));
76 info!("Read: {=[u8]:x}", buf);
77 assert_eq!(
78 &buf[..],
79 &[
80 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
81 30, 31, 32
82 ]
83 );
84}
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index 783690c11..83119e3a0 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -7,7 +7,7 @@ publish = false
7 7
8[dependencies] 8[dependencies]
9# Change stm32wb55rg to your chip name in both dependencies, if necessary. 9# Change stm32wb55rg to your chip name in both dependencies, if necessary.
10embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } 10embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti", "low-power"] }
11embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } 11embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] }
12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
13embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 13embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }