aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/CHANGELOG.md4
-rw-r--r--embassy-stm32/src/adc/ringbuffered.rs4
-rw-r--r--embassy-stm32/src/exti.rs46
-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.rs65
-rw-r--r--embassy-stm32/src/lib.rs3
-rw-r--r--embassy-stm32/src/low_power.rs95
-rw-r--r--embassy-stm32/src/usart/buffered.rs2
-rw-r--r--examples/stm32h7/src/bin/flash_async.rs84
-rw-r--r--examples/stm32wb/Cargo.toml2
-rw-r--r--tests/stm32/Cargo.toml2
-rw-r--r--tests/stm32/src/bin/hsem.rs3
13 files changed, 421 insertions, 83 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index c547fa443..f4d9dcf09 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- feat: allow use of anyadcchannel for adc4 10- feat: allow use of anyadcchannel for adc4
11- fix: fix incorrect logic for buffered usart transmission complete.
12- feat: add poll_for methods to exti
13- feat: implement stop for stm32wb.
11- change: rework hsem and add HIL test for some chips. 14- 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)) 15- 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 16- feat: allow embassy_executor::main for low power
@@ -69,6 +72,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)) 72- 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 73- fix: fixing channel numbers on vbat and vddcore for adc on adc
71- adc: adding disable to vbat 74- adc: adding disable to vbat
75- feat: stm32/flash: add async support for h7 family
72 76
73## 0.4.0 - 2025-08-26 77## 0.4.0 - 2025-08-26
74 78
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs
index a56f8ca0b..5437866d3 100644
--- a/embassy-stm32/src/adc/ringbuffered.rs
+++ b/embassy-stm32/src/adc/ringbuffered.rs
@@ -49,8 +49,6 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> {
49 } 49 }
50 50
51 pub fn stop(&mut self) { 51 pub fn stop(&mut self) {
52 T::stop();
53
54 self.ring_buf.request_pause(); 52 self.ring_buf.request_pause();
55 53
56 compiler_fence(Ordering::SeqCst); 54 compiler_fence(Ordering::SeqCst);
@@ -161,7 +159,7 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> {
161 return Ok(len); 159 return Ok(len);
162 } 160 }
163 Err(_) => { 161 Err(_) => {
164 self.stop(); 162 self.ring_buf.request_pause();
165 163
166 return Err(OverrunError); 164 return Err(OverrunError);
167 } 165 }
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs
index cb46d362c..899d5e677 100644
--- a/embassy-stm32/src/exti.rs
+++ b/embassy-stm32/src/exti.rs
@@ -7,6 +7,7 @@ use core::task::{Context, Poll};
7 7
8use embassy_hal_internal::{PeripheralType, impl_peripheral}; 8use embassy_hal_internal::{PeripheralType, impl_peripheral};
9use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
10use futures_util::FutureExt;
10 11
11use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; 12use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull};
12use crate::pac::EXTI; 13use crate::pac::EXTI;
@@ -133,7 +134,7 @@ impl<'d> ExtiInput<'d> {
133 /// 134 ///
134 /// This returns immediately if the pin is already high. 135 /// This returns immediately if the pin is already high.
135 pub async fn wait_for_high(&mut self) { 136 pub async fn wait_for_high(&mut self) {
136 let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); 137 let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true);
137 if self.is_high() { 138 if self.is_high() {
138 return; 139 return;
139 } 140 }
@@ -144,7 +145,7 @@ impl<'d> ExtiInput<'d> {
144 /// 145 ///
145 /// This returns immediately if the pin is already low. 146 /// This returns immediately if the pin is already low.
146 pub async fn wait_for_low(&mut self) { 147 pub async fn wait_for_low(&mut self) {
147 let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); 148 let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true);
148 if self.is_low() { 149 if self.is_low() {
149 return; 150 return;
150 } 151 }
@@ -155,19 +156,40 @@ impl<'d> ExtiInput<'d> {
155 /// 156 ///
156 /// If the pin is already high, it will wait for it to go low then back high. 157 /// If the pin is already high, it will wait for it to go low then back high.
157 pub async fn wait_for_rising_edge(&mut self) { 158 pub async fn wait_for_rising_edge(&mut self) {
158 ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await 159 ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true).await
160 }
161
162 /// Asynchronously wait until the pin sees a rising edge.
163 ///
164 /// If the pin is already high, it will wait for it to go low then back high.
165 pub fn poll_for_rising_edge<'a>(&mut self, cx: &mut Context<'a>) {
166 let _ =
167 ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, false).poll_unpin(cx);
159 } 168 }
160 169
161 /// Asynchronously wait until the pin sees a falling edge. 170 /// Asynchronously wait until the pin sees a falling edge.
162 /// 171 ///
163 /// If the pin is already low, it will wait for it to go high then back low. 172 /// If the pin is already low, it will wait for it to go high then back low.
164 pub async fn wait_for_falling_edge(&mut self) { 173 pub async fn wait_for_falling_edge(&mut self) {
165 ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await 174 ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true).await
175 }
176
177 /// Asynchronously wait until the pin sees a falling edge.
178 ///
179 /// If the pin is already low, it will wait for it to go high then back low.
180 pub fn poll_for_falling_edge<'a>(&mut self, cx: &mut Context<'a>) {
181 let _ =
182 ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, false).poll_unpin(cx);
166 } 183 }
167 184
168 /// Asynchronously wait until the pin sees any edge (either rising or falling). 185 /// Asynchronously wait until the pin sees any edge (either rising or falling).
169 pub async fn wait_for_any_edge(&mut self) { 186 pub async fn wait_for_any_edge(&mut self) {
170 ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await 187 ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, true).await
188 }
189
190 /// Asynchronously wait until the pin sees any edge (either rising or falling).
191 pub fn poll_for_any_edge<'a>(&mut self, cx: &mut Context<'a>) {
192 let _ = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, false).poll_unpin(cx);
171 } 193 }
172} 194}
173 195
@@ -227,11 +249,12 @@ impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> {
227#[must_use = "futures do nothing unless you `.await` or poll them"] 249#[must_use = "futures do nothing unless you `.await` or poll them"]
228struct ExtiInputFuture<'a> { 250struct ExtiInputFuture<'a> {
229 pin: PinNumber, 251 pin: PinNumber,
252 drop: bool,
230 phantom: PhantomData<&'a mut AnyPin>, 253 phantom: PhantomData<&'a mut AnyPin>,
231} 254}
232 255
233impl<'a> ExtiInputFuture<'a> { 256impl<'a> ExtiInputFuture<'a> {
234 fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool) -> Self { 257 fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool, drop: bool) -> Self {
235 critical_section::with(|_| { 258 critical_section::with(|_| {
236 let pin = pin as usize; 259 let pin = pin as usize;
237 exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); 260 exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port));
@@ -252,6 +275,7 @@ impl<'a> ExtiInputFuture<'a> {
252 275
253 Self { 276 Self {
254 pin, 277 pin,
278 drop,
255 phantom: PhantomData, 279 phantom: PhantomData,
256 } 280 }
257 } 281 }
@@ -259,10 +283,12 @@ impl<'a> ExtiInputFuture<'a> {
259 283
260impl<'a> Drop for ExtiInputFuture<'a> { 284impl<'a> Drop for ExtiInputFuture<'a> {
261 fn drop(&mut self) { 285 fn drop(&mut self) {
262 critical_section::with(|_| { 286 if self.drop {
263 let pin = self.pin as _; 287 critical_section::with(|_| {
264 cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); 288 let pin = self.pin as _;
265 }); 289 cpu_regs().imr(0).modify(|w| w.set_line(pin, false));
290 });
291 }
266 } 292 }
267} 293}
268 294
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..e62de0454 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,16 @@ 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_with_cs::<crate::peripherals::HSEM>(cs);
298
299 unsafe {
300 crate::rcc::REFCOUNT_STOP1 = 0;
301 crate::rcc::REFCOUNT_STOP2 = 0;
302 }
303}
304
270struct State { 305struct State {
271 wakers: [AtomicWaker; 6], 306 wakers: [AtomicWaker; 6],
272} 307}
@@ -278,8 +313,8 @@ impl State {
278 } 313 }
279 } 314 }
280 315
281 const fn waker_for(&self, number: u8) -> &AtomicWaker { 316 const fn waker_for(&self, index: u8) -> &AtomicWaker {
282 &self.wakers[number as usize] 317 &self.wakers[index as usize]
283 } 318 }
284} 319}
285 320
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..9de49c867 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -50,7 +50,7 @@ use critical_section::CriticalSection;
50use embassy_executor::*; 50use embassy_executor::*;
51 51
52use crate::interrupt; 52use crate::interrupt;
53use crate::rcc::{REFCOUNT_STOP1, REFCOUNT_STOP2}; 53use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2};
54use crate::time_driver::get_driver; 54use crate::time_driver::get_driver;
55 55
56const THREAD_PENDER: usize = usize::MAX; 56const THREAD_PENDER: usize = usize::MAX;
@@ -147,17 +147,17 @@ pub enum StopMode {
147 Stop2, 147 Stop2,
148} 148}
149 149
150#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] 150#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, 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, stm32wb, stm32wlex, stm32u0))]
154impl Into<Lpms> for StopMode { 154impl Into<Lpms> for StopMode {
155 fn into(self) -> Lpms { 155 fn into(self) -> Lpms {
156 match self { 156 match self {
157 StopMode::Stop1 => Lpms::STOP1, 157 StopMode::Stop1 => Lpms::STOP1,
158 #[cfg(not(stm32wba))] 158 #[cfg(not(any(stm32wb, stm32wba)))]
159 StopMode::Stop2 => Lpms::STOP2, 159 StopMode::Stop2 => Lpms::STOP2,
160 #[cfg(stm32wba)] 160 #[cfg(any(stm32wb, stm32wba))]
161 StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? 161 StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2?
162 } 162 }
163 } 163 }
@@ -201,7 +201,7 @@ impl Executor {
201 { 201 {
202 use crate::pac::rcc::vals::Sw; 202 use crate::pac::rcc::vals::Sw;
203 use crate::pac::{PWR, RCC}; 203 use crate::pac::{PWR, RCC};
204 use crate::rcc::{RCC_CONFIG, init as init_rcc}; 204 use crate::rcc::init as init_rcc;
205 205
206 let extscr = PWR.extscr().read(); 206 let extscr = PWR.extscr().read();
207 if extscr.c1stop2f() || extscr.c1stopf() { 207 if extscr.c1stop2f() || extscr.c1stopf() {
@@ -237,14 +237,77 @@ impl Executor {
237 trace!("low power: stop 1"); 237 trace!("low power: stop 1");
238 Some(StopMode::Stop1) 238 Some(StopMode::Stop1)
239 } else { 239 } else {
240 trace!("low power: not ready to stop"); 240 trace!("low power: not ready to stop (refcount_stop1: {})", unsafe {
241 REFCOUNT_STOP1
242 });
241 None 243 None
242 } 244 }
243 } 245 }
244 246
247 #[cfg(all(stm32wb, feature = "low-power"))]
248 fn configure_stop_stm32wb(&self, _cs: CriticalSection) -> Result<(), ()> {
249 use core::task::Poll;
250
251 use embassy_futures::poll_once;
252
253 use crate::hsem::HardwareSemaphoreChannel;
254 use crate::pac::rcc::vals::{Smps, Sw};
255 use crate::pac::{PWR, RCC};
256
257 trace!("low power: trying to get sem3");
258
259 let sem3_mutex = match poll_once(HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(3).lock(0)) {
260 Poll::Pending => None,
261 Poll::Ready(mutex) => Some(mutex),
262 }
263 .ok_or(())?;
264
265 trace!("low power: got sem3");
266
267 let sem4_mutex = HardwareSemaphoreChannel::<crate::peripherals::HSEM>::new(4).try_lock(0);
268 if let Some(sem4_mutex) = sem4_mutex {
269 trace!("low power: got sem4");
270
271 if PWR.extscr().read().c2ds() {
272 drop(sem4_mutex);
273 } else {
274 return Ok(());
275 }
276 }
277
278 // Sem4 not granted
279 // Set HSION
280 RCC.cr().modify(|w| {
281 w.set_hsion(true);
282 });
283
284 // Wait for HSIRDY
285 while !RCC.cr().read().hsirdy() {}
286
287 // Set SW to HSI
288 RCC.cfgr().modify(|w| {
289 w.set_sw(Sw::HSI);
290 });
291
292 // Wait for SWS to report HSI
293 while !RCC.cfgr().read().sws().eq(&Sw::HSI) {}
294
295 // Set SMPSSEL to HSI
296 RCC.smpscr().modify(|w| {
297 w.set_smpssel(Smps::HSI);
298 });
299
300 drop(sem3_mutex);
301
302 Ok(())
303 }
304
245 #[allow(unused_variables)] 305 #[allow(unused_variables)]
246 fn configure_stop(&self, stop_mode: StopMode) { 306 fn configure_stop(&self, _cs: CriticalSection, stop_mode: StopMode) -> Result<(), ()> {
247 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] 307 #[cfg(all(stm32wb, feature = "low-power"))]
308 self.configure_stop_stm32wb(_cs)?;
309
310 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb, stm32wba, stm32wlex))]
248 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); 311 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
249 #[cfg(stm32h5)] 312 #[cfg(stm32h5)]
250 crate::pac::PWR.pmcr().modify(|v| { 313 crate::pac::PWR.pmcr().modify(|v| {
@@ -252,6 +315,8 @@ impl Executor {
252 v.set_lpms(vals::Lpms::STOP); 315 v.set_lpms(vals::Lpms::STOP);
253 v.set_svos(vals::Svos::SCALE3); 316 v.set_svos(vals::Svos::SCALE3);
254 }); 317 });
318
319 Ok(())
255 } 320 }
256 321
257 fn configure_pwr(&self) { 322 fn configure_pwr(&self) {
@@ -265,14 +330,14 @@ impl Executor {
265 compiler_fence(Ordering::SeqCst); 330 compiler_fence(Ordering::SeqCst);
266 331
267 critical_section::with(|cs| { 332 critical_section::with(|cs| {
333 let _ = unsafe { RCC_CONFIG }?;
268 let stop_mode = Self::stop_mode(cs)?; 334 let stop_mode = Self::stop_mode(cs)?;
269 let _ = get_driver().pause_time(cs).ok()?; 335 get_driver().pause_time(cs).ok()?;
336 self.configure_stop(cs, stop_mode).ok()?;
270 337
271 Some(stop_mode) 338 Some(())
272 }) 339 })
273 .map(|stop_mode| { 340 .map(|_| {
274 self.configure_stop(stop_mode);
275
276 #[cfg(not(feature = "low-power-debug-with-sleep"))] 341 #[cfg(not(feature = "low-power-debug-with-sleep"))]
277 Self::get_scb().set_sleepdeep(); 342 Self::get_scb().set_sleepdeep();
278 }); 343 });
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index 69c3a740f..26d2b8991 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -87,7 +87,7 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) {
87 // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) 87 // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC)
88 // indicates that all bytes are pushed out from the FIFO. 88 // indicates that all bytes are pushed out from the FIFO.
89 // For other usart variants it shows that last byte from the buffer was just sent. 89 // For other usart variants it shows that last byte from the buffer was just sent.
90 if sr_val.tc() { 90 if sr_val.tc() && r.cr1().read().tcie() {
91 // For others it is cleared above with `clear_interrupt_flags`. 91 // For others it is cleared above with `clear_interrupt_flags`.
92 #[cfg(any(usart_v1, usart_v2))] 92 #[cfg(any(usart_v1, usart_v2))]
93 sr(r).modify(|w| w.set_tc(false)); 93 sr(r).modify(|w| w.set_tc(false));
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"] }
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index f5684d4df..8fcb6b2b4 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -31,7 +31,7 @@ stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual-
31stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"] 31stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"]
32stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] 32stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"]
33stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash 33stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash
34stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem"] 34stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem", "stop"]
35stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] 35stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"]
36stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono", "hsem"] 36stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono", "hsem"]
37stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] 37stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"]
diff --git a/tests/stm32/src/bin/hsem.rs b/tests/stm32/src/bin/hsem.rs
index 19648997c..fa69f22b2 100644
--- a/tests/stm32/src/bin/hsem.rs
+++ b/tests/stm32/src/bin/hsem.rs
@@ -30,6 +30,9 @@ async fn main(_spawner: Spawner) {
30 // 30 //
31 // hsem.channel_for(SemaphoreNumber::Channel5).unlock(0); 31 // hsem.channel_for(SemaphoreNumber::Channel5).unlock(0);
32 32
33 #[cfg(feature = "stm32wb55rg")]
34 let [_channel1, _channel2, mut channel5, _channel6] = hsem.split();
35 #[cfg(not(feature = "stm32wb55rg"))]
33 let [_channel1, _channel2, _channel3, _channel4, mut channel5, _channel6] = hsem.split(); 36 let [_channel1, _channel2, _channel3, _channel4, mut channel5, _channel6] = hsem.split();
34 37
35 info!("Locking channel 5"); 38 info!("Locking channel 5");