diff options
| author | xoviat <[email protected]> | 2025-11-19 15:24:05 -0600 |
|---|---|---|
| committer | xoviat <[email protected]> | 2025-11-19 15:24:05 -0600 |
| commit | 4104d114955be79cf419cb4cfb5c0e72b0abd261 (patch) | |
| tree | e67e5a10072606f3be2c326870ce3790696c4653 | |
| parent | a68b5d5d29514dcaf0a6ed08207d3a1f1fa9b464 (diff) | |
stm32: impl. stop for stm32wb
| -rw-r--r-- | embassy-stm32/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/hsem/mod.rs | 60 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 3 | ||||
| -rw-r--r-- | embassy-stm32/src/low_power.rs | 68 | ||||
| -rw-r--r-- | examples/stm32wb/Cargo.toml | 2 |
5 files changed, 112 insertions, 22 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 597cbb192..7d8e7c617 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | ## Unreleased - ReleaseDate | 8 | ## Unreleased - ReleaseDate |
| 9 | 9 | ||
| 10 | - feat: implement stop for stm32wb. | ||
| 10 | - change: rework hsem and add HIL test for some chips. | 11 | - 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)) | 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)) |
| 12 | - feat: allow embassy_executor::main for low power | 13 | - feat: allow embassy_executor::main for low power |
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; | |||
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | 5 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | 7 | ||
| 8 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 9 | use critical_section::CriticalSection; | ||
| 8 | use embassy_hal_internal::PeripheralType; | 10 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 11 | use 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")))] | ||
| 84 | const PUB_CHANNELS: usize = 6; | ||
| 85 | |||
| 86 | #[cfg(all(stm32wb, feature = "low-power"))] | ||
| 87 | const PUB_CHANNELS: usize = 4; | ||
| 88 | |||
| 81 | /// TX interrupt handler. | 89 | /// TX interrupt handler. |
| 82 | pub struct HardwareSemaphoreInterruptHandler<T: Instance> { | 90 | pub 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 | ||
| 129 | impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { | 137 | impl<'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"))] | ||
| 296 | pub(crate) fn init_hsem(_cs: CriticalSection) { | ||
| 297 | rcc::enable_and_reset::<crate::peripherals::HSEM>(); | ||
| 298 | } | ||
| 299 | |||
| 270 | struct State { | 300 | struct 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))] |
| 151 | use stm32_metapac::pwr::vals::Lpms; | 151 | use 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))] |
| 154 | impl Into<Lpms> for StopMode { | 154 | impl 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/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. |
| 10 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } | 10 | embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti", "low-power"] } |
| 11 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } | 11 | embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } |
| 12 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } | 12 | embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } |
| 13 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 13 | embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
