From d264c8ab31d6361a3fb76443ae80630d123d68fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Jos=C3=A9=20G=C3=B3mez?= Date: Thu, 28 Aug 2025 10:36:07 -0400 Subject: [PATCH] fix(embassy-stm32): Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope Fix #4577 by counting references to DacChannel. Modeled after similar code in the `can` module. --- embassy-stm32/src/dac/mod.rs | 55 +++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index d8f1f96f2..bc6c3cd34 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -12,6 +12,7 @@ use crate::{peripherals, Peri}; mod tsel; use embassy_hal_internal::PeripheralType; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; pub use tsel::TriggerSel; /// Operating mode for DAC channel @@ -96,6 +97,41 @@ pub enum ValueArray<'a> { Bit12Right(&'a [u16]), } +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum ChannelEvent { + Enable, + Disable, +} + +struct InnerState { + channel_count: usize, +} + +type SharedState = embassy_sync::blocking_mutex::Mutex>; +struct State { + state: SharedState, +} + +impl State { + /// Adjusts the channel count in response to a `ChannelEvent`, returning the updated value. + pub fn adjust_channel_count(&self, event: ChannelEvent) -> usize { + self.state.lock(|state| { + { + let mut mut_state = state.borrow_mut(); + match event { + ChannelEvent::Enable => { + mut_state.channel_count += 1; + } + ChannelEvent::Disable => { + mut_state.channel_count -= 1; + } + }; + } + state.borrow().channel_count + }) + } +} /// Driver for a single DAC channel. /// /// If you want to use both channels, either together or independently, @@ -249,6 +285,16 @@ impl<'d, T: Instance, C: Channel, M: PeriMode> DacChannel<'d, T, C, M> { reg.set_en(C::IDX, on); }); }); + let event = if on { + ChannelEvent::Enable + } else { + ChannelEvent::Disable + }; + let channel_count = T::state().adjust_channel_count(event); + // Disable the DAC only if no more channels are using it. + if channel_count == 0 { + rcc::disable::(); + } } /// Enable this channel. @@ -354,7 +400,7 @@ impl<'d, T: Instance, C: Channel, M: PeriMode> DacChannel<'d, T, C, M> { impl<'d, T: Instance, C: Channel, M: PeriMode> Drop for DacChannel<'d, T, C, M> { fn drop(&mut self) { - rcc::disable::(); + self.disable(); } } @@ -597,6 +643,13 @@ impl<'d, T: Instance, M: PeriMode> Dac<'d, T, M> { trait SealedInstance { fn regs() -> crate::pac::dac::Dac; + + fn state() -> &'static State { + static STATE: State = State { + state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(InnerState { channel_count: 0 })), + }; + &STATE + } } /// DAC instance.