From d2d00b57c8bf5b6879c5df5021f44652d1fd52ee Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 11:47:44 -0600 Subject: stm32: allow granular stop for uart --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/rcc/mod.rs | 39 ++++++++++++++++++++++++++++----- embassy-stm32/src/usart/mod.rs | 20 +++++++++++++---- embassy-stm32/src/usart/ringbuffered.rs | 3 +++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index d3e5ba48d..5c31b5a11 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 ## Unreleased - ReleaseDate +- feat: allow granular stop for regular usart - feat: Add continuous waveform method to SimplePWM - change: remove waveform timer method - change: low power: store stop mode for dma channels diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 85434fa83..f38d9078d 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -234,9 +234,6 @@ impl RccInfo { } } - #[cfg(feature = "low-power")] - increment_stop_refcount(_cs, self.stop_mode); - // set the xxxRST bit let reset_ptr = self.reset_ptr(); if let Some(reset_ptr) = reset_ptr { @@ -292,9 +289,6 @@ impl RccInfo { } } - #[cfg(feature = "low-power")] - decrement_stop_refcount(_cs, self.stop_mode); - // clear the xxxEN bit let enable_ptr = self.enable_ptr(); unsafe { @@ -303,13 +297,46 @@ impl RccInfo { } } + pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) { + #[cfg(feature = "low-power")] + increment_stop_refcount(_cs, self.stop_mode); + } + + pub(crate) fn increment_stop_refcount(&self) { + critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs)) + } + + pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) { + #[cfg(feature = "low-power")] + decrement_stop_refcount(_cs, self.stop_mode); + } + + pub(crate) fn decrement_stop_refcount(&self) { + critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs)) + } + // TODO: should this be `unsafe`? pub(crate) fn enable_and_reset(&self) { + critical_section::with(|cs| { + self.enable_and_reset_with_cs(cs); + self.increment_stop_refcount_with_cs(cs); + }) + } + + pub(crate) fn enable_and_reset_without_stop(&self) { critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) } // TODO: should this be `unsafe`? pub(crate) fn disable(&self) { + critical_section::with(|cs| { + self.disable_with_cs(cs); + self.decrement_stop_refcount_with_cs(cs); + }) + } + + // TODO: should this be `unsafe`? + pub(crate) fn disable_without_stop(&self) { critical_section::with(|cs| self.disable_with_cs(cs)) } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0e7da634d..1af78b358 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -491,6 +491,9 @@ impl<'d> UartTx<'d, Async> { /// Initiate an asynchronous UART write pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.info.rcc.increment_stop_refcount(); + let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + let r = self.info.regs; half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback)); @@ -508,6 +511,9 @@ impl<'d> UartTx<'d, Async> { /// Wait until transmission complete pub async fn flush(&mut self) -> Result<(), Error> { + self.info.rcc.increment_stop_refcount(); + let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + flush(&self.info, &self.state).await } } @@ -569,7 +575,7 @@ impl<'d, M: Mode> UartTx<'d, M> { let state = self.state; state.tx_rx_refcount.store(1, Ordering::Relaxed); - info.rcc.enable_and_reset(); + info.rcc.enable_and_reset_without_stop(); info.regs.cr3().modify(|w| { w.set_ctse(self.cts.is_some()); @@ -726,6 +732,9 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous UART read pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.info.rcc.increment_stop_refcount(); + let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + self.inner_read(buffer, false).await?; Ok(()) @@ -733,6 +742,9 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous read with idle line detection enabled pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + self.info.rcc.increment_stop_refcount(); + let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + self.inner_read(buffer, true).await } @@ -1004,7 +1016,7 @@ impl<'d, M: Mode> UartRx<'d, M> { .eager_reads .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); - info.rcc.enable_and_reset(); + info.rcc.enable_and_reset_without_stop(); info.regs.cr3().write(|w| { w.set_rtse(self.rts.is_some()); @@ -1143,7 +1155,7 @@ fn drop_tx_rx(info: &Info, state: &State) { refcount == 1 }); if is_last_drop { - info.rcc.disable(); + info.rcc.disable_without_stop(); } } @@ -1506,7 +1518,7 @@ impl<'d, M: Mode> Uart<'d, M> { .eager_reads .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); - info.rcc.enable_and_reset(); + info.rcc.enable_and_reset_without_stop(); info.regs.cr3().write(|w| { w.set_rtse(self.rx.rts.is_some()); diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index bac570d27..cc5224b69 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -117,6 +117,8 @@ impl<'d> UartRx<'d, Async> { let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) }; let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) }; + info.rcc.increment_stop_refcount(); + // Don't disable the clock mem::forget(self); @@ -324,6 +326,7 @@ impl<'d> RingBufferedUartRx<'d> { impl Drop for RingBufferedUartRx<'_> { fn drop(&mut self) { + self.info.rcc.decrement_stop_refcount(); self.stop_uart(); self.rx.as_ref().map(|x| x.set_as_disconnected()); self.rts.as_ref().map(|x| x.set_as_disconnected()); -- cgit