diff options
| author | liebman <[email protected]> | 2025-10-22 14:24:01 -0700 |
|---|---|---|
| committer | liebman <[email protected]> | 2025-11-03 12:50:36 -0800 |
| commit | ea94cb58a2016b1ab408aa975192de7aefe52ec5 (patch) | |
| tree | 9f954331a22d65f1565dd54362043a4a674cdfad /embassy-stm32/src | |
| parent | 36aa3e10aaf27bb1bd1109a203b378dc93b90b02 (diff) | |
use DeviceBusy to mark when stop1 or stop2 is unavailable.
Diffstat (limited to 'embassy-stm32/src')
| -rw-r--r-- | embassy-stm32/src/i2c/v2.rs | 10 | ||||
| -rw-r--r-- | embassy-stm32/src/low_power.rs | 78 |
2 files changed, 75 insertions, 13 deletions
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index a2184630f..6f2d03bd1 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -819,6 +819,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 819 | 819 | ||
| 820 | /// Write. | 820 | /// Write. |
| 821 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { | 821 | pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { |
| 822 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 823 | let _device_busy = crate::low_power::DeviceBusy::new(); | ||
| 822 | let timeout = self.timeout(); | 824 | let timeout = self.timeout(); |
| 823 | if write.is_empty() { | 825 | if write.is_empty() { |
| 824 | self.write_internal(address.into(), write, true, timeout) | 826 | self.write_internal(address.into(), write, true, timeout) |
| @@ -833,6 +835,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 833 | /// | 835 | /// |
| 834 | /// The buffers are concatenated in a single write transaction. | 836 | /// The buffers are concatenated in a single write transaction. |
| 835 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { | 837 | pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { |
| 838 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 839 | let _device_busy = crate::low_power::DeviceBusy::new(); | ||
| 836 | let timeout = self.timeout(); | 840 | let timeout = self.timeout(); |
| 837 | 841 | ||
| 838 | if write.is_empty() { | 842 | if write.is_empty() { |
| @@ -856,6 +860,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 856 | 860 | ||
| 857 | /// Read. | 861 | /// Read. |
| 858 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | 862 | pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |
| 863 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 864 | let _device_busy = crate::low_power::DeviceBusy::new(); | ||
| 859 | let timeout = self.timeout(); | 865 | let timeout = self.timeout(); |
| 860 | 866 | ||
| 861 | if buffer.is_empty() { | 867 | if buffer.is_empty() { |
| @@ -868,6 +874,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 868 | 874 | ||
| 869 | /// Write, restart, read. | 875 | /// Write, restart, read. |
| 870 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { | 876 | pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { |
| 877 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 878 | let _device_busy = crate::low_power::DeviceBusy::new(); | ||
| 871 | let timeout = self.timeout(); | 879 | let timeout = self.timeout(); |
| 872 | 880 | ||
| 873 | if write.is_empty() { | 881 | if write.is_empty() { |
| @@ -893,6 +901,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 893 | /// | 901 | /// |
| 894 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction | 902 | /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction |
| 895 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { | 903 | pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { |
| 904 | #[cfg(all(feature = "low-power", stm32wlex))] | ||
| 905 | let _device_busy = crate::low_power::DeviceBusy::new(); | ||
| 896 | let _ = addr; | 906 | let _ = addr; |
| 897 | let _ = operations; | 907 | let _ = operations; |
| 898 | todo!() | 908 | todo!() |
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 4cdaf6a00..0ee81fd18 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -68,6 +68,39 @@ use crate::rtc::Rtc; | |||
| 68 | 68 | ||
| 69 | static mut EXECUTOR: Option<Executor> = None; | 69 | static mut EXECUTOR: Option<Executor> = None; |
| 70 | 70 | ||
| 71 | #[cfg(stm32wlex)] | ||
| 72 | pub(crate) use self::busy::DeviceBusy; | ||
| 73 | #[cfg(stm32wlex)] | ||
| 74 | mod busy { | ||
| 75 | use core::sync::atomic::{AtomicU32, Ordering}; | ||
| 76 | |||
| 77 | // Count of devices blocking STOP | ||
| 78 | static STOP_BLOCKED: AtomicU32 = AtomicU32::new(0); | ||
| 79 | |||
| 80 | /// Check if STOP1 is blocked. | ||
| 81 | pub(crate) fn stop_blocked() -> bool { | ||
| 82 | STOP_BLOCKED.load(Ordering::SeqCst) > 0 | ||
| 83 | } | ||
| 84 | |||
| 85 | /// When ca device goes busy it will construct one of these where it will be dropped when the device goes idle. | ||
| 86 | pub(crate) struct DeviceBusy {} | ||
| 87 | |||
| 88 | impl DeviceBusy { | ||
| 89 | /// Create a new DeviceBusy. | ||
| 90 | pub(crate) fn new() -> Self { | ||
| 91 | STOP_BLOCKED.fetch_add(1, Ordering::SeqCst); | ||
| 92 | trace!("low power: device busy: Stop:{}", STOP_BLOCKED.load(Ordering::SeqCst)); | ||
| 93 | Self {} | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl Drop for DeviceBusy { | ||
| 98 | fn drop(&mut self) { | ||
| 99 | STOP_BLOCKED.fetch_sub(1, Ordering::SeqCst); | ||
| 100 | trace!("low power: device idle: Stop:{}", STOP_BLOCKED.load(Ordering::SeqCst)); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 71 | #[cfg(not(stm32u0))] | 104 | #[cfg(not(stm32u0))] |
| 72 | foreach_interrupt! { | 105 | foreach_interrupt! { |
| 73 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { | 106 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { |
| @@ -92,6 +125,7 @@ foreach_interrupt! { | |||
| 92 | 125 | ||
| 93 | #[allow(dead_code)] | 126 | #[allow(dead_code)] |
| 94 | pub(crate) unsafe fn on_wakeup_irq() { | 127 | pub(crate) unsafe fn on_wakeup_irq() { |
| 128 | info!("low power: on wakeup irq: extscr: {:?}", crate::pac::PWR.extscr().read()); | ||
| 95 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 129 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); |
| 96 | } | 130 | } |
| 97 | 131 | ||
| @@ -180,19 +214,22 @@ impl Executor { | |||
| 180 | } | 214 | } |
| 181 | 215 | ||
| 182 | unsafe fn on_wakeup_irq(&mut self) { | 216 | unsafe fn on_wakeup_irq(&mut self) { |
| 183 | // when we wake from STOP2, we need to re-initialize the rcc and the time driver | ||
| 184 | // to restore the clocks to their last configured state | ||
| 185 | #[cfg(stm32wlex)] | 217 | #[cfg(stm32wlex)] |
| 186 | crate::rcc::apply_resume_config(); | 218 | { |
| 187 | #[cfg(stm32wlex)] | 219 | let extscr = crate::pac::PWR.extscr().read(); |
| 188 | if crate::pac::PWR.extscr().read().c1stop2f() { | 220 | if extscr.c1stop2f() || extscr.c1stopf() { |
| 189 | critical_section::with(|cs| crate::time_driver::init_timer(cs)); | 221 | // when we wake from any stop mode we need to re-initialize the rcc |
| 190 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) | 222 | crate::rcc::apply_resume_config(); |
| 191 | // and given that we just woke from STOP2, we can reset them | 223 | if extscr.c1stop2f() { |
| 192 | crate::rcc::REFCOUNT_STOP2 = 0; | 224 | // when we wake from STOP2, we need to re-initialize the time driver |
| 193 | crate::rcc::REFCOUNT_STOP1 = 0; | 225 | critical_section::with(|cs| crate::time_driver::init_timer(cs)); |
| 226 | // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) | ||
| 227 | // and given that we just woke from STOP2, we can reset them | ||
| 228 | crate::rcc::REFCOUNT_STOP2 = 0; | ||
| 229 | crate::rcc::REFCOUNT_STOP1 = 0; | ||
| 230 | } | ||
| 231 | } | ||
| 194 | } | 232 | } |
| 195 | |||
| 196 | self.time_driver.resume_time(); | 233 | self.time_driver.resume_time(); |
| 197 | trace!("low power: resume"); | 234 | trace!("low power: resume"); |
| 198 | } | 235 | } |
| @@ -208,10 +245,22 @@ impl Executor { | |||
| 208 | self.time_driver.reconfigure_rtc(f); | 245 | self.time_driver.reconfigure_rtc(f); |
| 209 | } | 246 | } |
| 210 | 247 | ||
| 248 | fn stop1_ok_to_enter(&self) -> bool { | ||
| 249 | #[cfg(stm32wlex)] | ||
| 250 | if self::busy::stop_blocked() { | ||
| 251 | return false; | ||
| 252 | } | ||
| 253 | unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } | ||
| 254 | } | ||
| 255 | |||
| 256 | fn stop2_ok_to_enter(&self) -> bool { | ||
| 257 | self.stop1_ok_to_enter() && unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } | ||
| 258 | } | ||
| 259 | |||
| 211 | fn stop_mode(&self) -> Option<StopMode> { | 260 | fn stop_mode(&self) -> Option<StopMode> { |
| 212 | if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 } && unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 261 | if self.stop2_ok_to_enter() { |
| 213 | Some(StopMode::Stop2) | 262 | Some(StopMode::Stop2) |
| 214 | } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { | 263 | } else if self.stop1_ok_to_enter() { |
| 215 | Some(StopMode::Stop1) | 264 | Some(StopMode::Stop1) |
| 216 | } else { | 265 | } else { |
| 217 | None | 266 | None |
| @@ -299,6 +348,9 @@ impl Executor { | |||
| 299 | (true, true) => trace!("low power: wake from STOP1 and STOP2 ???"), | 348 | (true, true) => trace!("low power: wake from STOP1 and STOP2 ???"), |
| 300 | (false, false) => trace!("low power: stop mode not entered"), | 349 | (false, false) => trace!("low power: stop mode not entered"), |
| 301 | }; | 350 | }; |
| 351 | crate::pac::PWR.extscr().modify(|w| { | ||
| 352 | w.set_c1cssf(false); | ||
| 353 | }); | ||
| 302 | } | 354 | } |
| 303 | }; | 355 | }; |
| 304 | } | 356 | } |
