aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-20 08:38:36 -0600
committerGitHub <[email protected]>2025-11-20 08:38:36 -0600
commitee2fe27d002302dc981062ec357db56d90370072 (patch)
tree8b0bce5c3e8bff289c8d92c2a5c35c49fef70df3
parent4e808345883ae64099135a8384f774c44feccc5c (diff)
parentb8737b37f1917f938fbb2293aba37cf985a99002 (diff)
Merge branch 'main' into usart
-rw-r--r--docs/pages/bootloader.adoc6
-rw-r--r--embassy-boot/CHANGELOG.md3
-rw-r--r--embassy-boot/Cargo.toml14
-rw-r--r--embassy-boot/src/boot_loader.rs12
-rw-r--r--embassy-boot/src/lib.rs4
-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.rs285
-rw-r--r--embassy-stm32/src/lib.rs3
-rw-r--r--embassy-stm32/src/low_power.rs95
-rw-r--r--examples/stm32h7/src/bin/flash_async.rs84
-rw-r--r--examples/stm32wb/Cargo.toml2
-rw-r--r--tests/stm32/Cargo.toml10
-rw-r--r--tests/stm32/src/bin/hsem.rs50
17 files changed, 677 insertions, 139 deletions
diff --git a/docs/pages/bootloader.adoc b/docs/pages/bootloader.adoc
index b0f0331aa..c010b0622 100644
--- a/docs/pages/bootloader.adoc
+++ b/docs/pages/bootloader.adoc
@@ -43,14 +43,14 @@ Partition Size~dfu~= Partition Size~active~+ Page Size~active~
43+ 43+
44All values are specified in bytes. 44All values are specified in bytes.
45 45
46* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by: 46* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size is given by:
47+ 47+
48Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~) 48Partition Size~state~ = (2 × Write Size~state~) + (4 × Write Size~state~ × Partition Size~active~ / Page Size~active~)
49+ 49+
50All values are specified in bytes. 50All values are specified in bytes.
51 51
52The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. 52The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.
53The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. 53The BOOTLOADER_STATE partition must be big enough to store two words, plus four words per page in the ACTIVE partition.
54 54
55The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. 55The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
56 56
diff --git a/embassy-boot/CHANGELOG.md b/embassy-boot/CHANGELOG.md
index 8d6395357..1d41043cb 100644
--- a/embassy-boot/CHANGELOG.md
+++ b/embassy-boot/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Fixed documentation and assertion of STATE partition size requirements
12- Added documentation for package features
13
11## 0.6.1 - 2025-08-26 14## 0.6.1 - 2025-08-26
12 15
13- First release with changelog. 16- First release with changelog.
diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml
index 8c5c1f633..754c6e5f1 100644
--- a/embassy-boot/Cargo.toml
+++ b/embassy-boot/Cargo.toml
@@ -26,6 +26,7 @@ features = ["defmt"]
26[dependencies] 26[dependencies]
27defmt = { version = "1.0.1", optional = true } 27defmt = { version = "1.0.1", optional = true }
28digest = "0.10" 28digest = "0.10"
29document-features = "0.2.7"
29log = { version = "0.4", optional = true } 30log = { version = "0.4", optional = true }
30ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } 31ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true }
31embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } 32embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" }
@@ -45,11 +46,22 @@ critical-section = { version = "1.1.1", features = ["std"] }
45ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] } 46ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] }
46 47
47[features] 48[features]
49## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging
48defmt = ["dep:defmt"] 50defmt = ["dep:defmt"]
51## Use log for logging
49log = ["dep:log"] 52log = ["dep:log"]
53
54## Enable for devices that set erased flash bytes to `0x00` instead of the usual `0xFF`
55flash-erase-zero = []
56
57#! ## Firmware Signing
58#! Enable one of these features to allow verification of DFU signatures with
59#! `FirmwareUpdater::verify_and_mark_updated`.
60
61## Use the `ed25519-dalek` package to verify DFU signatures.
50ed25519-dalek = ["dep:ed25519-dalek", "_verify"] 62ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
63## Use the `salty` package to verify DFU signatures.
51ed25519-salty = ["dep:salty", "_verify"] 64ed25519-salty = ["dep:salty", "_verify"]
52flash-erase-zero = []
53 65
54#Internal features 66#Internal features
55_verify = [] 67_verify = []
diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs
index c38940d6e..a3a307051 100644
--- a/embassy-boot/src/boot_loader.rs
+++ b/embassy-boot/src/boot_loader.rs
@@ -135,10 +135,12 @@ pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> {
135 dfu: DFU, 135 dfu: DFU,
136 /// The state partition has the following format: 136 /// The state partition has the following format:
137 /// All ranges are in multiples of WRITE_SIZE bytes. 137 /// All ranges are in multiples of WRITE_SIZE bytes.
138 /// | Range | Description | 138 /// N = Active partition size divided by WRITE_SIZE.
139 /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 139 /// | Range | Description |
140 /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | 140 /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
141 /// | 2..2 + N | Progress index used while swapping or reverting 141 /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. |
142 /// | 2..(2 + 2N) | Progress index used while swapping |
143 /// | (2 + 2N)..(2 + 4N) | Progress index used while reverting
142 state: STATE, 144 state: STATE,
143} 145}
144 146
@@ -429,7 +431,7 @@ fn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
429 assert_eq!(dfu.capacity() as u32 % page_size, 0); 431 assert_eq!(dfu.capacity() as u32 % page_size, 0);
430 // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm 432 // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm
431 assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); 433 assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size);
432 assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); 434 assert!(2 + 4 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32);
433} 435}
434 436
435#[cfg(test)] 437#[cfg(test)]
diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs
index 7dc811f66..3e61d6036 100644
--- a/embassy-boot/src/lib.rs
+++ b/embassy-boot/src/lib.rs
@@ -3,6 +3,10 @@
3#![allow(unsafe_op_in_unsafe_fn)] 3#![allow(unsafe_op_in_unsafe_fn)]
4#![warn(missing_docs)] 4#![warn(missing_docs)]
5#![doc = include_str!("../README.md")] 5#![doc = include_str!("../README.md")]
6
7//! ## Feature flags
8#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
9
6mod fmt; 10mod fmt;
7 11
8mod boot_loader; 12mod boot_loader;
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 5fd43744e..416590f88 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- fix: fix incorrect logic for buffered usart transmission complete. 10- fix: fix incorrect logic for buffered usart transmission complete.
11- feat: add poll_for methods to exti
12- feat: implement stop for stm32wb.
13- change: rework hsem and add HIL test for some chips.
11- 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)) 14- 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))
12- feat: allow embassy_executor::main for low power 15- feat: allow embassy_executor::main for low power
13- feat: Add waveform methods to ComplementaryPwm 16- feat: Add waveform methods to ComplementaryPwm
@@ -68,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
68- feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) 71- feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874))
69- fix: fixing channel numbers on vbat and vddcore for adc on adc 72- fix: fixing channel numbers on vbat and vddcore for adc on adc
70- adc: adding disable to vbat 73- adc: adding disable to vbat
74- feat: stm32/flash: add async support for h7 family
71 75
72## 0.4.0 - 2025-08-26 76## 0.4.0 - 2025-08-26
73 77
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 4d3a5d68d..e62de0454 100644
--- a/embassy-stm32/src/hsem/mod.rs
+++ b/embassy-stm32/src/hsem/mod.rs
@@ -1,14 +1,22 @@
1//! Hardware Semaphore (HSEM) 1//! Hardware Semaphore (HSEM)
2 2
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::sync::atomic::{Ordering, compiler_fence};
6use core::task::Poll;
7
8#[cfg(all(stm32wb, feature = "low-power"))]
9use critical_section::CriticalSection;
3use embassy_hal_internal::PeripheralType; 10use embassy_hal_internal::PeripheralType;
11use embassy_sync::waitqueue::AtomicWaker;
4 12
5// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. 13// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs.
6// Those MCUs have a different HSEM implementation (Secure semaphore lock support, 14// Those MCUs have a different HSEM implementation (Secure semaphore lock support,
7// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), 15// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute),
8// which is not yet supported by this code. 16// which is not yet supported by this code.
9use crate::Peri; 17use crate::Peri;
10use crate::pac;
11use crate::rcc::{self, RccPeripheral}; 18use crate::rcc::{self, RccPeripheral};
19use crate::{interrupt, pac};
12 20
13/// HSEM error. 21/// HSEM error.
14#[derive(Debug)] 22#[derive(Debug)]
@@ -41,63 +49,152 @@ pub enum CoreId {
41 Core1 = 0x8, 49 Core1 = 0x8,
42} 50}
43 51
44/// Get the current core id 52impl CoreId {
45/// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. 53 /// Get the current core id
46#[inline(always)] 54 /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core.
47pub fn get_current_coreid() -> CoreId { 55 pub fn current() -> Self {
48 let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; 56 let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() };
49 match (cpuid & 0x000000F0) >> 4 { 57 match (cpuid & 0x000000F0) >> 4 {
50 #[cfg(any(stm32wb, stm32wl))] 58 #[cfg(any(stm32wb, stm32wl))]
51 0x0 => CoreId::Core1, 59 0x0 => CoreId::Core1,
52 60
53 #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] 61 #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))]
54 0x4 => CoreId::Core0, 62 0x4 => CoreId::Core0,
55 63
56 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] 64 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
57 0x4 => CoreId::Core1, 65 0x4 => CoreId::Core1,
58 66
59 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] 67 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]
60 0x7 => CoreId::Core0, 68 0x7 => CoreId::Core0,
61 _ => panic!("Unknown Cortex-M core"), 69 _ => panic!("Unknown Cortex-M core"),
70 }
71 }
72
73 /// Translates the core ID to an index into the interrupt registers.
74 pub fn to_index(&self) -> usize {
75 match &self {
76 CoreId::Core0 => 0,
77 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))]
78 CoreId::Core1 => 1,
79 }
62 } 80 }
63} 81}
64 82
65/// Translates the core ID to an index into the interrupt registers. 83#[cfg(not(all(stm32wb, feature = "low-power")))]
66#[inline(always)] 84const PUB_CHANNELS: usize = 6;
67fn core_id_to_index(core: CoreId) -> usize { 85
68 match core { 86#[cfg(all(stm32wb, feature = "low-power"))]
69 CoreId::Core0 => 0, 87const PUB_CHANNELS: usize = 4;
70 #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] 88
71 CoreId::Core1 => 1, 89/// TX interrupt handler.
90pub struct HardwareSemaphoreInterruptHandler<T: Instance> {
91 _phantom: PhantomData<T>,
92}
93
94impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for HardwareSemaphoreInterruptHandler<T> {
95 unsafe fn on_interrupt() {
96 let core_id = CoreId::current();
97
98 for number in 0..5 {
99 if T::regs().isr(core_id.to_index()).read().isf(number as usize) {
100 T::regs()
101 .icr(core_id.to_index())
102 .write(|w| w.set_isc(number as usize, true));
103
104 T::regs()
105 .ier(core_id.to_index())
106 .modify(|w| w.set_ise(number as usize, false));
107
108 T::state().waker_for(number).wake();
109 }
110 }
72 } 111 }
73} 112}
74 113
75/// HSEM driver 114/// Hardware semaphore mutex
76pub struct HardwareSemaphore<'d, T: Instance> { 115pub struct HardwareSemaphoreMutex<'a, T: Instance> {
77 _peri: Peri<'d, T>, 116 index: u8,
117 process_id: u8,
118 _lifetime: PhantomData<&'a mut T>,
78} 119}
79 120
80impl<'d, T: Instance> HardwareSemaphore<'d, T> { 121impl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> {
81 /// Creates a new HardwareSemaphore instance. 122 fn drop(&mut self) {
82 pub fn new(peripheral: Peri<'d, T>) -> Self { 123 HardwareSemaphoreChannel::<'a, T> {
83 rcc::enable_and_reset::<T>(); 124 index: self.index,
125 _lifetime: PhantomData,
126 }
127 .unlock(self.process_id);
128 }
129}
130
131/// Hardware semaphore channel
132pub struct HardwareSemaphoreChannel<'a, T: Instance> {
133 index: u8,
134 _lifetime: PhantomData<&'a mut T>,
135}
136
137impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> {
138 pub(crate) const fn new(number: u8) -> Self {
139 core::assert!(number > 0 && number <= 6);
140
141 Self {
142 index: number - 1,
143 _lifetime: PhantomData,
144 }
145 }
146
147 /// Locks the semaphore.
148 /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to
149 /// check if the lock has been successful, carried out from the HSEM_Rx register.
150 pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> {
151 let core_id = CoreId::current();
152
153 poll_fn(|cx| {
154 T::state().waker_for(self.index).register(cx.waker());
155
156 compiler_fence(Ordering::SeqCst);
157
158 T::regs()
159 .ier(core_id.to_index())
160 .modify(|w| w.set_ise(self.index as usize, true));
161
162 match self.try_lock(process_id) {
163 Some(mutex) => Poll::Ready(mutex),
164 None => Poll::Pending,
165 }
166 })
167 .await
168 }
84 169
85 HardwareSemaphore { _peri: peripheral } 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 }
86 } 183 }
87 184
88 /// Locks the semaphore. 185 /// Locks the semaphore.
89 /// 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
90 /// 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.
91 pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { 188 pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> {
92 T::regs().r(sem_id as usize).write(|w| { 189 T::regs().r(self.index as usize).write(|w| {
93 w.set_procid(process_id); 190 w.set_procid(process_id);
94 w.set_coreid(get_current_coreid() as u8); 191 w.set_coreid(CoreId::current() as u8);
95 w.set_lock(true); 192 w.set_lock(true);
96 }); 193 });
97 let reg = T::regs().r(sem_id as usize).read(); 194 let reg = T::regs().r(self.index as usize).read();
98 match ( 195 match (
99 reg.lock(), 196 reg.lock(),
100 reg.coreid() == get_current_coreid() as u8, 197 reg.coreid() == CoreId::current() as u8,
101 reg.procid() == process_id, 198 reg.procid() == process_id,
102 ) { 199 ) {
103 (true, true, true) => Ok(()), 200 (true, true, true) => Ok(()),
@@ -108,9 +205,9 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
108 /// Locks the semaphore. 205 /// Locks the semaphore.
109 /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, 206 /// The 1-step procedure consists in a read to lock and check the semaphore in a single step,
110 /// carried out from the HSEM_RLRx register. 207 /// carried out from the HSEM_RLRx register.
111 pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { 208 pub fn one_step_lock(&mut self) -> Result<(), HsemError> {
112 let reg = T::regs().rlr(sem_id as usize).read(); 209 let reg = T::regs().rlr(self.index as usize).read();
113 match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { 210 match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) {
114 (false, true, 0) => Ok(()), 211 (false, true, 0) => Ok(()),
115 _ => Err(HsemError::LockFailed), 212 _ => Err(HsemError::LockFailed),
116 } 213 }
@@ -119,14 +216,60 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
119 /// Unlocks the semaphore. 216 /// Unlocks the semaphore.
120 /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus 217 /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus
121 /// core ID or by a process not having the semaphore lock right. 218 /// core ID or by a process not having the semaphore lock right.
122 pub fn unlock(&mut self, sem_id: u8, process_id: u8) { 219 pub fn unlock(&mut self, process_id: u8) {
123 T::regs().r(sem_id as usize).write(|w| { 220 T::regs().r(self.index as usize).write(|w| {
124 w.set_procid(process_id); 221 w.set_procid(process_id);
125 w.set_coreid(get_current_coreid() as u8); 222 w.set_coreid(CoreId::current() as u8);
126 w.set_lock(false); 223 w.set_lock(false);
127 }); 224 });
128 } 225 }
129 226
227 /// Return the channel number
228 pub const fn channel(&self) -> u8 {
229 self.index + 1
230 }
231}
232
233/// HSEM driver
234pub struct HardwareSemaphore<T: Instance> {
235 _type: PhantomData<T>,
236}
237
238impl<T: Instance> HardwareSemaphore<T> {
239 /// Creates a new HardwareSemaphore instance.
240 pub fn new<'d>(
241 _peripheral: Peri<'d, T>,
242 _irq: impl interrupt::typelevel::Binding<T::Interrupt, HardwareSemaphoreInterruptHandler<T>> + 'd,
243 ) -> Self {
244 rcc::enable_and_reset::<T>();
245
246 HardwareSemaphore { _type: PhantomData }
247 }
248
249 /// Get a single channel, and keep the global struct
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
254 HardwareSemaphoreChannel::new(number)
255 }
256
257 /// Split the global struct into channels
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] {
261 [
262 HardwareSemaphoreChannel::new(1),
263 HardwareSemaphoreChannel::new(2),
264 #[cfg(not(all(stm32wb, feature = "low-power")))]
265 HardwareSemaphoreChannel::new(3),
266 #[cfg(not(all(stm32wb, feature = "low-power")))]
267 HardwareSemaphoreChannel::new(4),
268 HardwareSemaphoreChannel::new(5),
269 HardwareSemaphoreChannel::new(6),
270 ]
271 }
272
130 /// Unlocks all semaphores. 273 /// Unlocks all semaphores.
131 /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR 274 /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR
132 /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a 275 /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a
@@ -138,11 +281,6 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
138 }); 281 });
139 } 282 }
140 283
141 /// Checks if the semaphore is locked.
142 pub fn is_semaphore_locked(&self, sem_id: u8) -> bool {
143 T::regs().r(sem_id as usize).read().lock()
144 }
145
146 /// Sets the clear (unlock) key 284 /// Sets the clear (unlock) key
147 pub fn set_clear_key(&mut self, key: u16) { 285 pub fn set_clear_key(&mut self, key: u16) {
148 T::regs().keyr().modify(|w| w.set_key(key)); 286 T::regs().keyr().modify(|w| w.set_key(key));
@@ -152,38 +290,61 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> {
152 pub fn get_clear_key(&mut self) -> u16 { 290 pub fn get_clear_key(&mut self) -> u16 {
153 T::regs().keyr().read().key() 291 T::regs().keyr().read().key()
154 } 292 }
293}
155 294
156 /// Sets the interrupt enable bit for the semaphore. 295#[cfg(all(stm32wb, feature = "low-power"))]
157 pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { 296pub(crate) fn init_hsem(cs: CriticalSection) {
158 T::regs() 297 rcc::enable_and_reset_with_cs::<crate::peripherals::HSEM>(cs);
159 .ier(core_id_to_index(core_id)) 298
160 .modify(|w| w.set_ise(sem_x, enable)); 299 unsafe {
300 crate::rcc::REFCOUNT_STOP1 = 0;
301 crate::rcc::REFCOUNT_STOP2 = 0;
161 } 302 }
303}
162 304
163 /// Gets the interrupt flag for the semaphore. 305struct State {
164 pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool { 306 wakers: [AtomicWaker; 6],
165 T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x) 307}
308
309impl State {
310 const fn new() -> Self {
311 Self {
312 wakers: [const { AtomicWaker::new() }; 6],
313 }
166 } 314 }
167 315
168 /// Clears the interrupt flag for the semaphore. 316 const fn waker_for(&self, index: u8) -> &AtomicWaker {
169 pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { 317 &self.wakers[index as usize]
170 T::regs()
171 .icr(core_id_to_index(core_id))
172 .write(|w| w.set_isc(sem_x, false));
173 } 318 }
174} 319}
175 320
176trait SealedInstance { 321trait SealedInstance {
177 fn regs() -> pac::hsem::Hsem; 322 fn regs() -> pac::hsem::Hsem;
323 fn state() -> &'static State;
178} 324}
179 325
180/// HSEM instance trait. 326/// HSEM instance trait.
181#[allow(private_bounds)] 327#[allow(private_bounds)]
182pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} 328pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {
329 /// Interrupt for this peripheral.
330 type Interrupt: interrupt::typelevel::Interrupt;
331}
183 332
184impl SealedInstance for crate::peripherals::HSEM { 333impl SealedInstance for crate::peripherals::HSEM {
185 fn regs() -> crate::pac::hsem::Hsem { 334 fn regs() -> crate::pac::hsem::Hsem {
186 crate::pac::HSEM 335 crate::pac::HSEM
187 } 336 }
337
338 fn state() -> &'static State {
339 static STATE: State = State::new();
340 &STATE
341 }
188} 342}
189impl Instance for crate::peripherals::HSEM {} 343
344foreach_interrupt!(
345 ($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => {
346 impl Instance for crate::peripherals::$inst {
347 type Interrupt = crate::interrupt::typelevel::$irq;
348 }
349 };
350);
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/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 b92b47be2..8fcb6b2b4 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -31,9 +31,9 @@ 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"] 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"] 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"]
38stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] 38stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"]
39stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs. 39stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs.
@@ -58,6 +58,7 @@ not-gpdma = []
58dac = [] 58dac = []
59ucpd = [] 59ucpd = []
60cordic = ["dep:num-traits"] 60cordic = ["dep:num-traits"]
61hsem = []
61dual-bank = ["embassy-stm32/dual-bank"] 62dual-bank = ["embassy-stm32/dual-bank"]
62single-bank = ["embassy-stm32/single-bank"] 63single-bank = ["embassy-stm32/single-bank"]
63eeprom = [] 64eeprom = []
@@ -224,6 +225,11 @@ name = "wpan_mac"
224path = "src/bin/wpan_mac.rs" 225path = "src/bin/wpan_mac.rs"
225required-features = [ "mac",] 226required-features = [ "mac",]
226 227
228[[bin]]
229name = "hsem"
230path = "src/bin/hsem.rs"
231required-features = [ "hsem",]
232
227# END TESTS 233# END TESTS
228 234
229[profile.dev] 235[profile.dev]
diff --git a/tests/stm32/src/bin/hsem.rs b/tests/stm32/src/bin/hsem.rs
new file mode 100644
index 000000000..fa69f22b2
--- /dev/null
+++ b/tests/stm32/src/bin/hsem.rs
@@ -0,0 +1,50 @@
1// required-features: hsem
2#![no_std]
3#![no_main]
4
5#[path = "../common.rs"]
6mod common;
7
8use common::*;
9use embassy_executor::Spawner;
10use embassy_stm32::bind_interrupts;
11use embassy_stm32::hsem::{HardwareSemaphore, HardwareSemaphoreInterruptHandler};
12use embassy_stm32::peripherals::HSEM;
13
14bind_interrupts!(struct Irqs{
15 HSEM => HardwareSemaphoreInterruptHandler<HSEM>;
16});
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let p: embassy_stm32::Peripherals = init();
21
22 let hsem = HardwareSemaphore::new(p.HSEM, Irqs);
23
24 // if hsem.channel_for(SemaphoreNumber::Channel5).is_semaphore_locked() {
25 // defmt::panic!("Semaphore 5 already locked!")
26 // }
27 //
28 // hsem.channel_for(SemaphoreNumber::Channel5).one_step_lock().unwrap();
29 // hsem.channel_for(SemaphoreNumber::Channel1).two_step_lock(0).unwrap();
30 //
31 // hsem.channel_for(SemaphoreNumber::Channel5).unlock(0);
32
33 #[cfg(feature = "stm32wb55rg")]
34 let [_channel1, _channel2, mut channel5, _channel6] = hsem.split();
35 #[cfg(not(feature = "stm32wb55rg"))]
36 let [_channel1, _channel2, _channel3, _channel4, mut channel5, _channel6] = hsem.split();
37
38 info!("Locking channel 5");
39
40 let mutex = channel5.lock(0).await;
41
42 info!("Locked channel 5");
43
44 drop(mutex);
45
46 info!("Unlocked channel 5");
47
48 info!("Test OK");
49 cortex_m::asm::bkpt();
50}