Merge pull request #4599 from universalhandle/4577-dropped-channel-disables-dac

fix(embassy-stm32): Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope
This commit is contained in:
Dario Nieuwenhuis 2025-09-05 16:28:53 +02:00 committed by GitHub
commit 4ac4452c16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 57 additions and 1 deletions

View File

@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received
- feat: stm32/timer: add set_polarity functions for main and complementary outputs in complementary_pwm
- Add I2S support for STM32F1, STM32C0, STM32F0, STM32F3, STM32F7, STM32G0, STM32WL, STM32H5, STM32H7RS
### Fixed
- STM32: Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope ([#4577](https://github.com/embassy-rs/embassy/pull/4577))
## 0.4.0 - 2025-08-26

View File

@ -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<CriticalSectionRawMutex, core::cell::RefCell<InnerState>>;
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::<T>();
}
}
/// 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::<T>();
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.