STM32 DAC: Rework DAC driver, support all families.
This commit is contained in:
		
				
					committed by
					
						
						Dario Nieuwenhuis
					
				
			
			
				
	
			
			
			
						parent
						
							267cbaebe6
						
					
				
				
					commit
					09d7950313
				
			@@ -996,8 +996,8 @@ fn main() {
 | 
				
			|||||||
        // SDMMCv1 uses the same channel for both directions, so just implement for RX
 | 
					        // SDMMCv1 uses the same channel for both directions, so just implement for RX
 | 
				
			||||||
        (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
 | 
					        (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
 | 
				
			||||||
        (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
 | 
					        (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
 | 
				
			||||||
        (("dac", "CH1"), quote!(crate::dac::DmaCh1)),
 | 
					        (("dac", "CH1"), quote!(crate::dac::DacDma1)),
 | 
				
			||||||
        (("dac", "CH2"), quote!(crate::dac::DmaCh2)),
 | 
					        (("dac", "CH2"), quote!(crate::dac::DacDma2)),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    .into();
 | 
					    .into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,11 @@
 | 
				
			|||||||
 | 
					//! Provide access to the STM32 digital-to-analog converter (DAC).
 | 
				
			||||||
#![macro_use]
 | 
					#![macro_use]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//! Provide access to the STM32 digital-to-analog converter (DAC).
 | 
					 | 
				
			||||||
use core::marker::PhantomData;
 | 
					use core::marker::PhantomData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
 | 
					use embassy_hal_internal::{into_ref, PeripheralRef};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::dma::NoDma;
 | 
				
			||||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
					#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
				
			||||||
use crate::pac::dac;
 | 
					use crate::pac::dac;
 | 
				
			||||||
use crate::rcc::RccPeripheral;
 | 
					use crate::rcc::RccPeripheral;
 | 
				
			||||||
@@ -13,6 +14,7 @@ use crate::{peripherals, Peripheral};
 | 
				
			|||||||
mod tsel;
 | 
					mod tsel;
 | 
				
			||||||
pub use tsel::TriggerSel;
 | 
					pub use tsel::TriggerSel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Operating mode for DAC channel
 | 
				
			||||||
#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
					#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
				
			||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
					#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
				
			||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
@@ -56,32 +58,9 @@ impl Mode {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
					#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
				
			||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
/// Custom Errors
 | 
					/// Single 8 or 12 bit value that can be output by the DAC.
 | 
				
			||||||
pub enum Error {
 | 
					///
 | 
				
			||||||
    UnconfiguredChannel,
 | 
					/// 12-bit values outside the permitted range are silently truncated.
 | 
				
			||||||
    InvalidValue,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
					 | 
				
			||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
					 | 
				
			||||||
/// DAC Channels
 | 
					 | 
				
			||||||
pub enum Channel {
 | 
					 | 
				
			||||||
    Ch1,
 | 
					 | 
				
			||||||
    Ch2,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Channel {
 | 
					 | 
				
			||||||
    const fn index(&self) -> usize {
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Channel::Ch1 => 0,
 | 
					 | 
				
			||||||
            Channel::Ch2 => 1,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
					 | 
				
			||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
					 | 
				
			||||||
/// Single 8 or 12 bit value that can be output by the DAC
 | 
					 | 
				
			||||||
pub enum Value {
 | 
					pub enum Value {
 | 
				
			||||||
    // 8 bit value
 | 
					    // 8 bit value
 | 
				
			||||||
    Bit8(u8),
 | 
					    Bit8(u8),
 | 
				
			||||||
@@ -93,7 +72,21 @@ pub enum Value {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
					#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
				
			||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
/// Array variant of [`Value`]
 | 
					/// Dual 8 or 12 bit values that can be output by the DAC channels 1 and 2 simultaneously.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// 12-bit values outside the permitted range are silently truncated.
 | 
				
			||||||
 | 
					pub enum DualValue {
 | 
				
			||||||
 | 
					    // 8 bit value
 | 
				
			||||||
 | 
					    Bit8(u8, u8),
 | 
				
			||||||
 | 
					    // 12 bit value stored in a u16, left-aligned
 | 
				
			||||||
 | 
					    Bit12Left(u16, u16),
 | 
				
			||||||
 | 
					    // 12 bit value stored in a u16, right-aligned
 | 
				
			||||||
 | 
					    Bit12Right(u16, u16),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
 | 
					/// Array variant of [`Value`].
 | 
				
			||||||
pub enum ValueArray<'a> {
 | 
					pub enum ValueArray<'a> {
 | 
				
			||||||
    // 8 bit values
 | 
					    // 8 bit values
 | 
				
			||||||
    Bit8(&'a [u8]),
 | 
					    Bit8(&'a [u8]),
 | 
				
			||||||
@@ -102,266 +95,206 @@ pub enum ValueArray<'a> {
 | 
				
			|||||||
    // 12 bit values stored in a u16, right-aligned
 | 
					    // 12 bit values stored in a u16, right-aligned
 | 
				
			||||||
    Bit12Right(&'a [u16]),
 | 
					    Bit12Right(&'a [u16]),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/// Provide common functions for DAC channels
 | 
					 | 
				
			||||||
pub trait DacChannel<T: Instance, Tx> {
 | 
					 | 
				
			||||||
    const CHANNEL: Channel;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Enable trigger of the given channel
 | 
					/// Driver for a single DAC channel.
 | 
				
			||||||
    fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        T::regs().cr().modify(|reg| {
 | 
					 | 
				
			||||||
            reg.set_ten(Self::CHANNEL.index(), on);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Set mode register of the given channel
 | 
					 | 
				
			||||||
    #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
					 | 
				
			||||||
    fn set_channel_mode(&mut self, mode: Mode) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        T::regs().mcr().modify(|reg| {
 | 
					 | 
				
			||||||
            reg.set_mode(Self::CHANNEL.index(), mode.mode());
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Set enable register of the given channel
 | 
					 | 
				
			||||||
    fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        T::regs().cr().modify(|reg| {
 | 
					 | 
				
			||||||
            reg.set_en(Self::CHANNEL.index(), on);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Enable the DAC channel `ch`
 | 
					 | 
				
			||||||
    fn enable_channel(&mut self) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        self.set_channel_enable(true)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Disable the DAC channel `ch`
 | 
					 | 
				
			||||||
    fn disable_channel(&mut self) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        self.set_channel_enable(false)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Perform a software trigger on `ch`
 | 
					 | 
				
			||||||
    fn trigger(&mut self) {
 | 
					 | 
				
			||||||
        T::regs().swtrigr().write(|reg| {
 | 
					 | 
				
			||||||
            reg.set_swtrig(Self::CHANNEL.index(), true);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Set a value to be output by the DAC on trigger.
 | 
					 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
    /// The `value` is written to the corresponding "data holding register".
 | 
					/// If you want to use both channels, either together or independently,
 | 
				
			||||||
    fn set(&mut self, value: Value) -> Result<(), Error> {
 | 
					/// create a [`Dac`] first and use it to access each channel.
 | 
				
			||||||
        match value {
 | 
					pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> {
 | 
				
			||||||
            Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
 | 
					 | 
				
			||||||
            Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
 | 
					 | 
				
			||||||
            Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Hold two DAC channels
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// # Example for obtaining both DAC channels
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// ```ignore
 | 
					 | 
				
			||||||
/// // DMA channels and pins may need to be changed for your controller
 | 
					 | 
				
			||||||
/// let (dac_ch1, dac_ch2) =
 | 
					 | 
				
			||||||
///     embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
 | 
					 | 
				
			||||||
/// ```
 | 
					 | 
				
			||||||
pub struct Dac<'d, T: Instance, TxCh1, TxCh2> {
 | 
					 | 
				
			||||||
    ch1: DacCh1<'d, T, TxCh1>,
 | 
					 | 
				
			||||||
    ch2: DacCh2<'d, T, TxCh2>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// DAC CH1
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
 | 
					 | 
				
			||||||
pub struct DacCh1<'d, T: Instance, Tx> {
 | 
					 | 
				
			||||||
    /// To consume T
 | 
					 | 
				
			||||||
    _peri: PeripheralRef<'d, T>,
 | 
					 | 
				
			||||||
    #[allow(unused)] // For chips whose DMA is not (yet) supported
 | 
					 | 
				
			||||||
    dma: PeripheralRef<'d, Tx>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// DAC CH2
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
 | 
					 | 
				
			||||||
pub struct DacCh2<'d, T: Instance, Tx> {
 | 
					 | 
				
			||||||
    /// Instead of PeripheralRef to consume T
 | 
					 | 
				
			||||||
    phantom: PhantomData<&'d mut T>,
 | 
					    phantom: PhantomData<&'d mut T>,
 | 
				
			||||||
    #[allow(unused)] // For chips whose DMA is not (yet) supported
 | 
					    #[allow(unused)]
 | 
				
			||||||
    dma: PeripheralRef<'d, Tx>,
 | 
					    dma: PeripheralRef<'d, DMA>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> {
 | 
					pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>;
 | 
				
			||||||
    /// Obtain DAC CH1
 | 
					pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>;
 | 
				
			||||||
    pub fn new(
 | 
					 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					 | 
				
			||||||
        dma: impl Peripheral<P = Tx> + 'd,
 | 
					 | 
				
			||||||
        pin: impl Peripheral<P = impl DacPin<T, 1>> + crate::gpio::sealed::Pin + 'd,
 | 
					 | 
				
			||||||
    ) -> Self {
 | 
					 | 
				
			||||||
        pin.set_as_analog();
 | 
					 | 
				
			||||||
        into_ref!(peri, dma);
 | 
					 | 
				
			||||||
        T::enable_and_reset();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut dac = Self { _peri: peri, dma };
 | 
					impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
 | 
				
			||||||
 | 
					    const IDX: usize = (N - 1) as usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Configure each activated channel. All results can be `unwrap`ed since they
 | 
					    /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral.
 | 
				
			||||||
        // will only error if the channel is not configured (i.e. ch1, ch2 are false)
 | 
					 | 
				
			||||||
        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
					 | 
				
			||||||
        dac.set_channel_mode(Mode::NormalExternalBuffered).unwrap();
 | 
					 | 
				
			||||||
        dac.enable_channel().unwrap();
 | 
					 | 
				
			||||||
        dac.set_trigger_enable(true).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        dac
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Select a new trigger for this channel
 | 
					 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// **Important**: This disables the channel!
 | 
					    /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
 | 
				
			||||||
    pub fn select_trigger(&mut self, trigger: TriggerSel) -> Result<(), Error> {
 | 
					 | 
				
			||||||
        unwrap!(self.disable_channel());
 | 
					 | 
				
			||||||
        T::regs().cr().modify(|reg| {
 | 
					 | 
				
			||||||
            reg.set_tsel(0, trigger.tsel());
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Write `data` to the DAC CH1 via DMA.
 | 
					 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
 | 
					    /// The channel is enabled on creation and begins to drive the output pin.
 | 
				
			||||||
    /// This will configure a circular DMA transfer that periodically outputs the `data`.
 | 
					    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will
 | 
				
			||||||
    /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
 | 
					    /// disable the channel; you must re-enable it with `enable()`.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// **Important:** Channel 1 has to be configured for the DAC instance!
 | 
					    /// By default, triggering is disabled, but it can be enabled using
 | 
				
			||||||
    #[cfg(not(gpdma))]
 | 
					    /// [`DacChannel::set_trigger()`].
 | 
				
			||||||
    pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        Tx: DmaCh1<T>,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let channel = Channel::Ch1.index();
 | 
					 | 
				
			||||||
        debug!("Writing to channel {}", channel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Enable DAC and DMA
 | 
					 | 
				
			||||||
        T::regs().cr().modify(|w| {
 | 
					 | 
				
			||||||
            w.set_en(channel, true);
 | 
					 | 
				
			||||||
            w.set_dmaen(channel, true);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx_request = self.dma.request();
 | 
					 | 
				
			||||||
        let dma_channel = &mut self.dma;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx_options = crate::dma::TransferOptions {
 | 
					 | 
				
			||||||
            circular,
 | 
					 | 
				
			||||||
            half_transfer_ir: false,
 | 
					 | 
				
			||||||
            complete_transfer_ir: !circular,
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Initiate the correct type of DMA transfer depending on what data is passed
 | 
					 | 
				
			||||||
        let tx_f = match data {
 | 
					 | 
				
			||||||
            ValueArray::Bit8(buf) => unsafe {
 | 
					 | 
				
			||||||
                crate::dma::Transfer::new_write(
 | 
					 | 
				
			||||||
                    dma_channel,
 | 
					 | 
				
			||||||
                    tx_request,
 | 
					 | 
				
			||||||
                    buf,
 | 
					 | 
				
			||||||
                    T::regs().dhr8r(channel).as_ptr() as *mut u8,
 | 
					 | 
				
			||||||
                    tx_options,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            ValueArray::Bit12Left(buf) => unsafe {
 | 
					 | 
				
			||||||
                crate::dma::Transfer::new_write(
 | 
					 | 
				
			||||||
                    dma_channel,
 | 
					 | 
				
			||||||
                    tx_request,
 | 
					 | 
				
			||||||
                    buf,
 | 
					 | 
				
			||||||
                    T::regs().dhr12l(channel).as_ptr() as *mut u16,
 | 
					 | 
				
			||||||
                    tx_options,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            ValueArray::Bit12Right(buf) => unsafe {
 | 
					 | 
				
			||||||
                crate::dma::Transfer::new_write(
 | 
					 | 
				
			||||||
                    dma_channel,
 | 
					 | 
				
			||||||
                    tx_request,
 | 
					 | 
				
			||||||
                    buf,
 | 
					 | 
				
			||||||
                    T::regs().dhr12r(channel).as_ptr() as *mut u16,
 | 
					 | 
				
			||||||
                    tx_options,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        tx_f.await;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // finish dma
 | 
					 | 
				
			||||||
        // TODO: Do we need to check any status registers here?
 | 
					 | 
				
			||||||
        T::regs().cr().modify(|w| {
 | 
					 | 
				
			||||||
            // Disable the DAC peripheral
 | 
					 | 
				
			||||||
            w.set_en(channel, false);
 | 
					 | 
				
			||||||
            // Disable the DMA. TODO: Is this necessary?
 | 
					 | 
				
			||||||
            w.set_dmaen(channel, false);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
 | 
					 | 
				
			||||||
    /// Obtain DAC CH2
 | 
					 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(
 | 
				
			||||||
        _peri: impl Peripheral<P = T> + 'd,
 | 
					        _peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
        dma: impl Peripheral<P = Tx> + 'd,
 | 
					        dma: impl Peripheral<P = DMA> + 'd,
 | 
				
			||||||
        pin: impl Peripheral<P = impl DacPin<T, 2>> + crate::gpio::sealed::Pin + 'd,
 | 
					        pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::sealed::Pin> + 'd,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        into_ref!(dma, pin);
 | 
				
			||||||
        pin.set_as_analog();
 | 
					        pin.set_as_analog();
 | 
				
			||||||
        into_ref!(_peri, dma);
 | 
					 | 
				
			||||||
        T::enable_and_reset();
 | 
					        T::enable_and_reset();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut dac = Self {
 | 
					        let mut dac = Self {
 | 
				
			||||||
            phantom: PhantomData,
 | 
					            phantom: PhantomData,
 | 
				
			||||||
            dma,
 | 
					            dma,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					        #[cfg(any(dac_v5, dac_v6, dac_v7))]
 | 
				
			||||||
        // Configure each activated channel. All results can be `unwrap`ed since they
 | 
					        dac.set_hfsel();
 | 
				
			||||||
        // will only error if the channel is not configured (i.e. ch1, ch2 are false)
 | 
					        dac.enable();
 | 
				
			||||||
        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
					 | 
				
			||||||
        dac.set_channel_mode(Mode::NormalExternalBuffered).unwrap();
 | 
					 | 
				
			||||||
        dac.enable_channel().unwrap();
 | 
					 | 
				
			||||||
        dac.set_trigger_enable(true).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        dac
 | 
					        dac
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Select a new trigger for this channel
 | 
					    /// Create a new `DacChannel` instance where the external output pin is not used,
 | 
				
			||||||
    pub fn select_trigger(&mut self, trigger: TriggerSel) -> Result<(), Error> {
 | 
					    /// so the DAC can only be used to generate internal signals.
 | 
				
			||||||
        unwrap!(self.disable_channel());
 | 
					    /// The GPIO pin is therefore available to be used for other functions.
 | 
				
			||||||
        T::regs().cr().modify(|reg| {
 | 
					    ///
 | 
				
			||||||
            reg.set_tsel(1, trigger.tsel());
 | 
					    /// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.
 | 
				
			||||||
        });
 | 
					    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the
 | 
				
			||||||
        Ok(())
 | 
					    /// channel; you must re-enable it with `enable()`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// By default, triggering is disabled, but it can be enabled using
 | 
				
			||||||
 | 
					    /// [`DacChannel::set_trigger()`].
 | 
				
			||||||
 | 
					    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]
 | 
				
			||||||
 | 
					    pub fn new_internal(_peri: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = DMA> + 'd) -> Self {
 | 
				
			||||||
 | 
					        into_ref!(dma);
 | 
				
			||||||
 | 
					        T::enable_and_reset();
 | 
				
			||||||
 | 
					        let mut dac = Self {
 | 
				
			||||||
 | 
					            phantom: PhantomData,
 | 
				
			||||||
 | 
					            dma,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        #[cfg(any(dac_v5, dac_v6, dac_v7))]
 | 
				
			||||||
 | 
					        dac.set_hfsel();
 | 
				
			||||||
 | 
					        dac.set_mode(Mode::NormalInternalUnbuffered);
 | 
				
			||||||
 | 
					        dac.enable();
 | 
				
			||||||
 | 
					        dac
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Write `data` to the DAC CH2 via DMA.
 | 
					    /// Enable or disable this channel.
 | 
				
			||||||
    ///
 | 
					    pub fn set_enable(&mut self, on: bool) {
 | 
				
			||||||
    /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
 | 
					        critical_section::with(|_| {
 | 
				
			||||||
    /// This will configure a circular DMA transfer that periodically outputs the `data`.
 | 
					            T::regs().cr().modify(|reg| {
 | 
				
			||||||
    /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
 | 
					                reg.set_en(Self::IDX, on);
 | 
				
			||||||
    ///
 | 
					            });
 | 
				
			||||||
    /// **Important:** Channel 2 has to be configured for the DAC instance!
 | 
					        });
 | 
				
			||||||
    #[cfg(not(gpdma))]
 | 
					    }
 | 
				
			||||||
    pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        Tx: DmaCh2<T>,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let channel = Channel::Ch2.index();
 | 
					 | 
				
			||||||
        debug!("Writing to channel {}", channel);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Enable this channel.
 | 
				
			||||||
 | 
					    pub fn enable(&mut self) {
 | 
				
			||||||
 | 
					        self.set_enable(true)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Disable this channel.
 | 
				
			||||||
 | 
					    pub fn disable(&mut self) {
 | 
				
			||||||
 | 
					        self.set_enable(false)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Set the trigger source for this channel.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This method disables the channel, so you may need to re-enable afterwards.
 | 
				
			||||||
 | 
					    pub fn set_trigger(&mut self, source: TriggerSel) {
 | 
				
			||||||
 | 
					        critical_section::with(|_| {
 | 
				
			||||||
 | 
					            T::regs().cr().modify(|reg| {
 | 
				
			||||||
 | 
					                reg.set_en(Self::IDX, false);
 | 
				
			||||||
 | 
					                reg.set_tsel(Self::IDX, source as u8);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Enable or disable triggering for this channel.
 | 
				
			||||||
 | 
					    pub fn set_triggering(&mut self, on: bool) {
 | 
				
			||||||
 | 
					        critical_section::with(|_| {
 | 
				
			||||||
 | 
					            T::regs().cr().modify(|reg| {
 | 
				
			||||||
 | 
					                reg.set_ten(Self::IDX, on);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Software trigger this channel.
 | 
				
			||||||
 | 
					    pub fn trigger(&mut self) {
 | 
				
			||||||
 | 
					        T::regs().swtrigr().write(|reg| {
 | 
				
			||||||
 | 
					            reg.set_swtrig(Self::IDX, true);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Set mode of this channel.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This method disables the channel, so you may need to re-enable afterwards.
 | 
				
			||||||
 | 
					    #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
				
			||||||
 | 
					    pub fn set_mode(&mut self, mode: Mode) {
 | 
				
			||||||
 | 
					        critical_section::with(|_| {
 | 
				
			||||||
 | 
					            T::regs().cr().modify(|reg| {
 | 
				
			||||||
 | 
					                reg.set_en(Self::IDX, false);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            T::regs().mcr().modify(|reg| {
 | 
				
			||||||
 | 
					                reg.set_mode(Self::IDX, mode.mode());
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write a new value to this channel.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// If triggering is not enabled, the new value is immediately output; otherwise,
 | 
				
			||||||
 | 
					    /// it will be output after the next trigger.
 | 
				
			||||||
 | 
					    pub fn set(&mut self, value: Value) {
 | 
				
			||||||
 | 
					        match value {
 | 
				
			||||||
 | 
					            Value::Bit8(v) => T::regs().dhr8r(Self::IDX).write(|reg| reg.set_dhr(v)),
 | 
				
			||||||
 | 
					            Value::Bit12Left(v) => T::regs().dhr12l(Self::IDX).write(|reg| reg.set_dhr(v)),
 | 
				
			||||||
 | 
					            Value::Bit12Right(v) => T::regs().dhr12r(Self::IDX).write(|reg| reg.set_dhr(v)),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Read the current output value of the DAC.
 | 
				
			||||||
 | 
					    pub fn read(&self) -> u16 {
 | 
				
			||||||
 | 
					        T::regs().dor(Self::IDX).read().dor()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Set HFSEL as appropriate for the current peripheral clock frequency.
 | 
				
			||||||
 | 
					    #[cfg(dac_v5)]
 | 
				
			||||||
 | 
					    fn set_hfsel(&mut self) {
 | 
				
			||||||
 | 
					        if T::frequency() >= crate::time::mhz(80) {
 | 
				
			||||||
 | 
					            critical_section::with(|_| {
 | 
				
			||||||
 | 
					                T::regs().cr().modify(|reg| {
 | 
				
			||||||
 | 
					                    reg.set_hfsel(true);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Set HFSEL as appropriate for the current peripheral clock frequency.
 | 
				
			||||||
 | 
					    #[cfg(any(dac_v6, dac_v7))]
 | 
				
			||||||
 | 
					    fn set_hfsel(&mut self) {
 | 
				
			||||||
 | 
					        if T::frequency() >= crate::time::mhz(160) {
 | 
				
			||||||
 | 
					            critical_section::with(|_| {
 | 
				
			||||||
 | 
					                T::regs().mcr().modify(|reg| {
 | 
				
			||||||
 | 
					                    reg.set_hfsel(0b10);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else if T::frequency() >= crate::time::mhz(80) {
 | 
				
			||||||
 | 
					            critical_section::with(|_| {
 | 
				
			||||||
 | 
					                T::regs().mcr().modify(|reg| {
 | 
				
			||||||
 | 
					                    reg.set_hfsel(0b01);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					macro_rules! impl_dma_methods {
 | 
				
			||||||
 | 
					    ($n:literal, $trait:ident) => {
 | 
				
			||||||
 | 
					        impl<'d, T: Instance, DMA> DacChannel<'d, T, $n, DMA>
 | 
				
			||||||
 | 
					        where
 | 
				
			||||||
 | 
					            DMA: $trait<T>,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            /// Write `data` to this channel via DMA.
 | 
				
			||||||
 | 
					            ///
 | 
				
			||||||
 | 
					            /// To prevent delays or glitches when outputing a periodic waveform, the `circular`
 | 
				
			||||||
 | 
					            /// flag can be set. This configures a circular DMA transfer that continually outputs
 | 
				
			||||||
 | 
					            /// `data`. Note that for performance reasons in circular mode the transfer-complete
 | 
				
			||||||
 | 
					            /// interrupt is disabled.
 | 
				
			||||||
 | 
					            #[cfg(not(gpdma))]
 | 
				
			||||||
 | 
					            pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) {
 | 
				
			||||||
                // Enable DAC and DMA
 | 
					                // Enable DAC and DMA
 | 
				
			||||||
                T::regs().cr().modify(|w| {
 | 
					                T::regs().cr().modify(|w| {
 | 
				
			||||||
            w.set_en(channel, true);
 | 
					                    w.set_en(Self::IDX, true);
 | 
				
			||||||
            w.set_dmaen(channel, true);
 | 
					                    w.set_dmaen(Self::IDX, true);
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let tx_request = self.dma.request();
 | 
					                let tx_request = self.dma.request();
 | 
				
			||||||
@@ -381,7 +314,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
 | 
				
			|||||||
                            dma_channel,
 | 
					                            dma_channel,
 | 
				
			||||||
                            tx_request,
 | 
					                            tx_request,
 | 
				
			||||||
                            buf,
 | 
					                            buf,
 | 
				
			||||||
                    T::regs().dhr8r(channel).as_ptr() as *mut u8,
 | 
					                            T::regs().dhr8r(Self::IDX).as_ptr() as *mut u8,
 | 
				
			||||||
                            tx_options,
 | 
					                            tx_options,
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
@@ -390,7 +323,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
 | 
				
			|||||||
                            dma_channel,
 | 
					                            dma_channel,
 | 
				
			||||||
                            tx_request,
 | 
					                            tx_request,
 | 
				
			||||||
                            buf,
 | 
					                            buf,
 | 
				
			||||||
                    T::regs().dhr12l(channel).as_ptr() as *mut u16,
 | 
					                            T::regs().dhr12l(Self::IDX).as_ptr() as *mut u16,
 | 
				
			||||||
                            tx_options,
 | 
					                            tx_options,
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
@@ -399,7 +332,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
 | 
				
			|||||||
                            dma_channel,
 | 
					                            dma_channel,
 | 
				
			||||||
                            tx_request,
 | 
					                            tx_request,
 | 
				
			||||||
                            buf,
 | 
					                            buf,
 | 
				
			||||||
                    T::regs().dhr12r(channel).as_ptr() as *mut u16,
 | 
					                            T::regs().dhr12r(Self::IDX).as_ptr() as *mut u16,
 | 
				
			||||||
                            tx_options,
 | 
					                            tx_options,
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
@@ -407,95 +340,150 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                tx_f.await;
 | 
					                tx_f.await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // finish dma
 | 
					 | 
				
			||||||
        // TODO: Do we need to check any status registers here?
 | 
					 | 
				
			||||||
                T::regs().cr().modify(|w| {
 | 
					                T::regs().cr().modify(|w| {
 | 
				
			||||||
            // Disable the DAC peripheral
 | 
					                    w.set_en(Self::IDX, false);
 | 
				
			||||||
            w.set_en(channel, false);
 | 
					                    w.set_dmaen(Self::IDX, false);
 | 
				
			||||||
            // Disable the DMA. TODO: Is this necessary?
 | 
					 | 
				
			||||||
            w.set_dmaen(channel, false);
 | 
					 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					impl_dma_methods!(1, DacDma1);
 | 
				
			||||||
 | 
					impl_dma_methods!(2, DacDma1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> {
 | 
				
			||||||
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
 | 
					        T::disable();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> {
 | 
					/// DAC driver.
 | 
				
			||||||
    /// Create a new DAC instance with both channels.
 | 
					 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
    /// This is used to obtain two independent channels via `split()` for use e.g. with DMA.
 | 
					/// Use this struct when you want to use both channels, either together or independently.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Example
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ```ignore
 | 
				
			||||||
 | 
					/// // Pins may need to be changed for your specific device.
 | 
				
			||||||
 | 
					/// let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC, NoDma, NoDma, p.PA4, p.PA5).split();
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					pub struct Dac<'d, T: Instance, DMACh1 = NoDma, DMACh2 = NoDma> {
 | 
				
			||||||
 | 
					    ch1: DacChannel<'d, T, 1, DMACh1>,
 | 
				
			||||||
 | 
					    ch2: DacChannel<'d, T, 2, DMACh2>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> {
 | 
				
			||||||
 | 
					    /// Create a new `Dac` instance, consuming the underlying DAC peripheral.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This struct allows you to access both channels of the DAC, where available. You can either
 | 
				
			||||||
 | 
					    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use
 | 
				
			||||||
 | 
					    /// the two channels together.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The channels are enabled on creation and begins to drive their output pins.
 | 
				
			||||||
 | 
					    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will
 | 
				
			||||||
 | 
					    /// disable the channel; you must re-enable them with `enable()`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
 | 
				
			||||||
 | 
					    /// method on the underlying channels.
 | 
				
			||||||
    pub fn new(
 | 
					    pub fn new(
 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					        _peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
        dma_ch1: impl Peripheral<P = TxCh1> + 'd,
 | 
					        dma_ch1: impl Peripheral<P = DMACh1> + 'd,
 | 
				
			||||||
        dma_ch2: impl Peripheral<P = TxCh2> + 'd,
 | 
					        dma_ch2: impl Peripheral<P = DMACh2> + 'd,
 | 
				
			||||||
        pin_ch1: impl Peripheral<P = impl DacPin<T, 1>> + crate::gpio::sealed::Pin + 'd,
 | 
					        pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::sealed::Pin> + 'd,
 | 
				
			||||||
        pin_ch2: impl Peripheral<P = impl DacPin<T, 2>> + crate::gpio::sealed::Pin + 'd,
 | 
					        pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::sealed::Pin> + 'd,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2);
 | 
				
			||||||
        pin_ch1.set_as_analog();
 | 
					        pin_ch1.set_as_analog();
 | 
				
			||||||
        pin_ch2.set_as_analog();
 | 
					        pin_ch2.set_as_analog();
 | 
				
			||||||
        into_ref!(peri, dma_ch1, dma_ch2);
 | 
					        // Enable twice to increment the DAC refcount for each channel.
 | 
				
			||||||
        T::enable_and_reset();
 | 
					        T::enable_and_reset();
 | 
				
			||||||
 | 
					        T::enable_and_reset();
 | 
				
			||||||
        let mut dac_ch1 = DacCh1 {
 | 
					        Self {
 | 
				
			||||||
            _peri: peri,
 | 
					            ch1: DacCh1 {
 | 
				
			||||||
 | 
					                phantom: PhantomData,
 | 
				
			||||||
                dma: dma_ch1,
 | 
					                dma: dma_ch1,
 | 
				
			||||||
        };
 | 
					            },
 | 
				
			||||||
 | 
					            ch2: DacCh2 {
 | 
				
			||||||
        let mut dac_ch2 = DacCh2 {
 | 
					 | 
				
			||||||
                phantom: PhantomData,
 | 
					                phantom: PhantomData,
 | 
				
			||||||
                dma: dma_ch2,
 | 
					                dma: dma_ch2,
 | 
				
			||||||
        };
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        // Configure each activated channel. All results can be `unwrap`ed since they
 | 
					    }
 | 
				
			||||||
        // will only error if the channel is not configured (i.e. ch1, ch2 are false)
 | 
					 | 
				
			||||||
        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v73))]
 | 
					 | 
				
			||||||
        dac_ch1.set_channel_mode(Mode::NormalExternalBuffered).unwrap();
 | 
					 | 
				
			||||||
        dac_ch1.enable_channel().unwrap();
 | 
					 | 
				
			||||||
        dac_ch1.set_trigger_enable(true).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]
 | 
					 | 
				
			||||||
        dac_ch2.set_channel_mode(Mode::NormalExternalBuffered).unwrap();
 | 
					 | 
				
			||||||
        dac_ch2.enable_channel().unwrap();
 | 
					 | 
				
			||||||
        dac_ch2.set_trigger_enable(true).unwrap();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Create a new `Dac` instance where the external output pins are not used,
 | 
				
			||||||
 | 
					    /// so the DAC can only be used to generate internal signals but the GPIO
 | 
				
			||||||
 | 
					    /// pins remain available for other functions.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This struct allows you to access both channels of the DAC, where available. You can either
 | 
				
			||||||
 | 
					    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two
 | 
				
			||||||
 | 
					    /// channels together.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.
 | 
				
			||||||
 | 
					    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the
 | 
				
			||||||
 | 
					    /// channel; you must re-enable them with `enable()`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`
 | 
				
			||||||
 | 
					    /// method on the underlying channels.
 | 
				
			||||||
 | 
					    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]
 | 
				
			||||||
 | 
					    pub fn new_internal(
 | 
				
			||||||
 | 
					        _peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        dma_ch1: impl Peripheral<P = DMACh1> + 'd,
 | 
				
			||||||
 | 
					        dma_ch2: impl Peripheral<P = DMACh2> + 'd,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        into_ref!(dma_ch1, dma_ch2);
 | 
				
			||||||
 | 
					        // Enable twice to increment the DAC refcount for each channel.
 | 
				
			||||||
 | 
					        T::enable_and_reset();
 | 
				
			||||||
 | 
					        T::enable_and_reset();
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            ch1: dac_ch1,
 | 
					            ch1: DacCh1 {
 | 
				
			||||||
            ch2: dac_ch2,
 | 
					                phantom: PhantomData,
 | 
				
			||||||
 | 
					                dma: dma_ch1,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            ch2: DacCh2 {
 | 
				
			||||||
 | 
					                phantom: PhantomData,
 | 
				
			||||||
 | 
					                dma: dma_ch2,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Split the DAC into CH1 and CH2 for independent use.
 | 
					    /// Split this `Dac` into separate channels.
 | 
				
			||||||
    pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) {
 | 
					    ///
 | 
				
			||||||
 | 
					    /// You can access and move the channels around separately after splitting.
 | 
				
			||||||
 | 
					    pub fn split(self) -> (DacCh1<'d, T, DMACh1>, DacCh2<'d, T, DMACh2>) {
 | 
				
			||||||
        (self.ch1, self.ch2)
 | 
					        (self.ch1, self.ch2)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get mutable reference to CH1
 | 
					    /// Temporarily access channel 1.
 | 
				
			||||||
    pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> {
 | 
					    pub fn ch1(&mut self) -> &mut DacCh1<'d, T, DMACh1> {
 | 
				
			||||||
        &mut self.ch1
 | 
					        &mut self.ch1
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get mutable reference to CH2
 | 
					    /// Temporarily access channel 2.
 | 
				
			||||||
    pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> {
 | 
					    pub fn ch2(&mut self) -> &mut DacCh2<'d, T, DMACh2> {
 | 
				
			||||||
        &mut self.ch2
 | 
					        &mut self.ch2
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get reference to CH1
 | 
					    /// Simultaneously update channels 1 and 2 with a new value.
 | 
				
			||||||
    pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> {
 | 
					    ///
 | 
				
			||||||
        &self.ch1
 | 
					    /// If triggering is not enabled, the new values are immediately output;
 | 
				
			||||||
    }
 | 
					    /// otherwise, they will be output after the next trigger.
 | 
				
			||||||
 | 
					    pub fn set(&mut self, values: DualValue) {
 | 
				
			||||||
    /// Get reference to CH2
 | 
					        match values {
 | 
				
			||||||
    pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> {
 | 
					            DualValue::Bit8(v1, v2) => T::regs().dhr8rd().write(|reg| {
 | 
				
			||||||
        &self.ch2
 | 
					                reg.set_dhr(0, v1);
 | 
				
			||||||
 | 
					                reg.set_dhr(1, v2);
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            DualValue::Bit12Left(v1, v2) => T::regs().dhr12ld().write(|reg| {
 | 
				
			||||||
 | 
					                reg.set_dhr(0, v1);
 | 
				
			||||||
 | 
					                reg.set_dhr(1, v2);
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            DualValue::Bit12Right(v1, v2) => T::regs().dhr12rd().write(|reg| {
 | 
				
			||||||
 | 
					                reg.set_dhr(0, v1);
 | 
				
			||||||
 | 
					                reg.set_dhr(1, v2);
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> {
 | 
					 | 
				
			||||||
    const CHANNEL: Channel = Channel::Ch1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> {
 | 
					 | 
				
			||||||
    const CHANNEL: Channel = Channel::Ch2;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub(crate) mod sealed {
 | 
					pub(crate) mod sealed {
 | 
				
			||||||
@@ -505,8 +493,8 @@ pub(crate) mod sealed {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
 | 
					pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
 | 
				
			||||||
dma_trait!(DmaCh1, Instance);
 | 
					dma_trait!(DacDma1, Instance);
 | 
				
			||||||
dma_trait!(DmaCh2, Instance);
 | 
					dma_trait!(DacDma2, Instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Marks a pin that can be used with the DAC
 | 
					/// Marks a pin that can be used with the DAC
 | 
				
			||||||
pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
 | 
					pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
 | 
				
			||||||
@@ -521,12 +509,14 @@ foreach_peripheral!(
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
 | 
					            fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
 | 
				
			||||||
 | 
					                // TODO: Increment refcount?
 | 
				
			||||||
                crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
 | 
					                crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
 | 
				
			||||||
                crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
 | 
					                crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
 | 
				
			||||||
                crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
 | 
					                crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            fn disable_with_cs(_cs: critical_section::CriticalSection) {
 | 
					            fn disable_with_cs(_cs: critical_section::CriticalSection) {
 | 
				
			||||||
 | 
					                // TODO: Decrement refcount?
 | 
				
			||||||
                crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
 | 
					                crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
 | 
					use embassy_stm32::dac::{DacCh1, Value};
 | 
				
			||||||
use embassy_stm32::dma::NoDma;
 | 
					use embassy_stm32::dma::NoDma;
 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,11 +14,10 @@ async fn main(_spawner: Spawner) -> ! {
 | 
				
			|||||||
    info!("Hello World, dude!");
 | 
					    info!("Hello World, dude!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4);
 | 
					    let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4);
 | 
				
			||||||
    unwrap!(dac.set_trigger_enable(false));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        for v in 0..=255 {
 | 
					        for v in 0..=255 {
 | 
				
			||||||
            unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
 | 
					            dac.set(Value::Bit8(to_sine_wave(v)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use cortex_m_rt::entry;
 | 
					use cortex_m_rt::entry;
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
 | 
					use embassy_stm32::dac::{DacCh1, Value};
 | 
				
			||||||
use embassy_stm32::dma::NoDma;
 | 
					use embassy_stm32::dma::NoDma;
 | 
				
			||||||
use embassy_stm32::Config;
 | 
					use embassy_stm32::Config;
 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
@@ -46,11 +46,10 @@ fn main() -> ! {
 | 
				
			|||||||
    let p = embassy_stm32::init(config);
 | 
					    let p = embassy_stm32::init(config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
 | 
					    let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
 | 
				
			||||||
    unwrap!(dac.set_trigger_enable(false));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        for v in 0..=255 {
 | 
					        for v in 0..=255 {
 | 
				
			||||||
            unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
 | 
					            dac.set(Value::Bit8(to_sine_wave(v)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,21 +4,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::dac::{DacChannel, ValueArray};
 | 
					use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
 | 
				
			||||||
use embassy_stm32::pac::timer::vals::{Mms, Opm};
 | 
					use embassy_stm32::pac::timer::vals::{Mms, Opm};
 | 
				
			||||||
use embassy_stm32::peripherals::{TIM6, TIM7};
 | 
					use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
 | 
				
			||||||
use embassy_stm32::rcc::low_level::RccPeripheral;
 | 
					use embassy_stm32::rcc::low_level::RccPeripheral;
 | 
				
			||||||
use embassy_stm32::time::Hertz;
 | 
					use embassy_stm32::time::Hertz;
 | 
				
			||||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
 | 
					use embassy_stm32::timer::low_level::Basic16bitInstance;
 | 
				
			||||||
use micromath::F32Ext;
 | 
					use micromath::F32Ext;
 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub type Dac1Type =
 | 
					 | 
				
			||||||
    embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub type Dac2Type =
 | 
					 | 
				
			||||||
    embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[embassy_executor::main]
 | 
					#[embassy_executor::main]
 | 
				
			||||||
async fn main(spawner: Spawner) {
 | 
					async fn main(spawner: Spawner) {
 | 
				
			||||||
    let mut config = embassy_stm32::Config::default();
 | 
					    let mut config = embassy_stm32::Config::default();
 | 
				
			||||||
@@ -63,7 +57,7 @@ async fn main(spawner: Spawner) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[embassy_executor::task]
 | 
					#[embassy_executor::task]
 | 
				
			||||||
async fn dac_task1(mut dac: Dac1Type) {
 | 
					async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
 | 
				
			||||||
    let data: &[u8; 256] = &calculate_array::<256>();
 | 
					    let data: &[u8; 256] = &calculate_array::<256>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info!("TIM6 frequency is {}", TIM6::frequency());
 | 
					    info!("TIM6 frequency is {}", TIM6::frequency());
 | 
				
			||||||
@@ -77,8 +71,9 @@ async fn dac_task1(mut dac: Dac1Type) {
 | 
				
			|||||||
        error!("Reload value {} below threshold!", reload);
 | 
					        error!("Reload value {} below threshold!", reload);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dac.select_trigger(embassy_stm32::dac::TriggerSel::Tim6).unwrap();
 | 
					    dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6);
 | 
				
			||||||
    dac.enable_channel().unwrap();
 | 
					    dac.set_triggering(true);
 | 
				
			||||||
 | 
					    dac.enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TIM6::enable_and_reset();
 | 
					    TIM6::enable_and_reset();
 | 
				
			||||||
    TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
 | 
					    TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
 | 
				
			||||||
@@ -100,14 +95,12 @@ async fn dac_task1(mut dac: Dac1Type) {
 | 
				
			|||||||
    // Loop technically not necessary if DMA circular mode is enabled
 | 
					    // Loop technically not necessary if DMA circular mode is enabled
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        info!("Loop DAC1");
 | 
					        info!("Loop DAC1");
 | 
				
			||||||
        if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
 | 
					        dac.write(ValueArray::Bit8(data), true).await;
 | 
				
			||||||
            error!("Could not write to dac: {}", e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[embassy_executor::task]
 | 
					#[embassy_executor::task]
 | 
				
			||||||
async fn dac_task2(mut dac: Dac2Type) {
 | 
					async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
 | 
				
			||||||
    let data: &[u8; 256] = &calculate_array::<256>();
 | 
					    let data: &[u8; 256] = &calculate_array::<256>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info!("TIM7 frequency is {}", TIM7::frequency());
 | 
					    info!("TIM7 frequency is {}", TIM7::frequency());
 | 
				
			||||||
@@ -127,7 +120,9 @@ async fn dac_task2(mut dac: Dac2Type) {
 | 
				
			|||||||
        w.set_cen(true);
 | 
					        w.set_cen(true);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dac.select_trigger(embassy_stm32::dac::TriggerSel::Tim7).unwrap();
 | 
					    dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7);
 | 
				
			||||||
 | 
					    dac.set_triggering(true);
 | 
				
			||||||
 | 
					    dac.enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    debug!(
 | 
					    debug!(
 | 
				
			||||||
        "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
 | 
					        "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
 | 
				
			||||||
@@ -138,9 +133,7 @@ async fn dac_task2(mut dac: Dac2Type) {
 | 
				
			|||||||
        data.len()
 | 
					        data.len()
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
 | 
					    dac.write(ValueArray::Bit8(data), true).await;
 | 
				
			||||||
        error!("Could not write to dac: {}", e);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn to_sine_wave(v: u8) -> u8 {
 | 
					fn to_sine_wave(v: u8) -> u8 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
#![feature(type_alias_impl_trait)]
 | 
					#![feature(type_alias_impl_trait)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
 | 
					use embassy_stm32::dac::{DacCh1, Value};
 | 
				
			||||||
use embassy_stm32::dma::NoDma;
 | 
					use embassy_stm32::dma::NoDma;
 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -13,11 +13,10 @@ fn main() -> ! {
 | 
				
			|||||||
    info!("Hello World!");
 | 
					    info!("Hello World!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
 | 
					    let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
 | 
				
			||||||
    unwrap!(dac.set_trigger_enable(false));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        for v in 0..=255 {
 | 
					        for v in 0..=255 {
 | 
				
			||||||
            unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
 | 
					            dac.set(Value::Bit8(to_sine_wave(v)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,21 +4,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::dac::{DacChannel, ValueArray};
 | 
					use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray};
 | 
				
			||||||
use embassy_stm32::pac::timer::vals::{Mms, Opm};
 | 
					use embassy_stm32::pac::timer::vals::{Mms, Opm};
 | 
				
			||||||
use embassy_stm32::peripherals::{TIM6, TIM7};
 | 
					use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
 | 
				
			||||||
use embassy_stm32::rcc::low_level::RccPeripheral;
 | 
					use embassy_stm32::rcc::low_level::RccPeripheral;
 | 
				
			||||||
use embassy_stm32::time::Hertz;
 | 
					use embassy_stm32::time::Hertz;
 | 
				
			||||||
use embassy_stm32::timer::low_level::Basic16bitInstance;
 | 
					use embassy_stm32::timer::low_level::Basic16bitInstance;
 | 
				
			||||||
use micromath::F32Ext;
 | 
					use micromath::F32Ext;
 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub type Dac1Type =
 | 
					 | 
				
			||||||
    embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub type Dac2Type =
 | 
					 | 
				
			||||||
    embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[embassy_executor::main]
 | 
					#[embassy_executor::main]
 | 
				
			||||||
async fn main(spawner: Spawner) {
 | 
					async fn main(spawner: Spawner) {
 | 
				
			||||||
    let config = embassy_stm32::Config::default();
 | 
					    let config = embassy_stm32::Config::default();
 | 
				
			||||||
@@ -34,7 +28,7 @@ async fn main(spawner: Spawner) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[embassy_executor::task]
 | 
					#[embassy_executor::task]
 | 
				
			||||||
async fn dac_task1(mut dac: Dac1Type) {
 | 
					async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
 | 
				
			||||||
    let data: &[u8; 256] = &calculate_array::<256>();
 | 
					    let data: &[u8; 256] = &calculate_array::<256>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info!("TIM6 frequency is {}", TIM6::frequency());
 | 
					    info!("TIM6 frequency is {}", TIM6::frequency());
 | 
				
			||||||
@@ -48,8 +42,9 @@ async fn dac_task1(mut dac: Dac1Type) {
 | 
				
			|||||||
        error!("Reload value {} below threshold!", reload);
 | 
					        error!("Reload value {} below threshold!", reload);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dac.select_trigger(embassy_stm32::dac::TriggerSel::Tim6).unwrap();
 | 
					    dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6);
 | 
				
			||||||
    dac.enable_channel().unwrap();
 | 
					    dac.set_triggering(true);
 | 
				
			||||||
 | 
					    dac.enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TIM6::enable_and_reset();
 | 
					    TIM6::enable_and_reset();
 | 
				
			||||||
    TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
 | 
					    TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
 | 
				
			||||||
@@ -71,14 +66,12 @@ async fn dac_task1(mut dac: Dac1Type) {
 | 
				
			|||||||
    // Loop technically not necessary if DMA circular mode is enabled
 | 
					    // Loop technically not necessary if DMA circular mode is enabled
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        info!("Loop DAC1");
 | 
					        info!("Loop DAC1");
 | 
				
			||||||
        if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
 | 
					        dac.write(ValueArray::Bit8(data), true).await;
 | 
				
			||||||
            error!("Could not write to dac: {}", e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[embassy_executor::task]
 | 
					#[embassy_executor::task]
 | 
				
			||||||
async fn dac_task2(mut dac: Dac2Type) {
 | 
					async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
 | 
				
			||||||
    let data: &[u8; 256] = &calculate_array::<256>();
 | 
					    let data: &[u8; 256] = &calculate_array::<256>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    info!("TIM7 frequency is {}", TIM7::frequency());
 | 
					    info!("TIM7 frequency is {}", TIM7::frequency());
 | 
				
			||||||
@@ -98,7 +91,9 @@ async fn dac_task2(mut dac: Dac2Type) {
 | 
				
			|||||||
        w.set_cen(true);
 | 
					        w.set_cen(true);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dac.select_trigger(embassy_stm32::dac::TriggerSel::Tim7).unwrap();
 | 
					    dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7);
 | 
				
			||||||
 | 
					    dac.set_triggering(true);
 | 
				
			||||||
 | 
					    dac.enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    debug!(
 | 
					    debug!(
 | 
				
			||||||
        "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
 | 
					        "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
 | 
				
			||||||
@@ -109,9 +104,7 @@ async fn dac_task2(mut dac: Dac2Type) {
 | 
				
			|||||||
        data.len()
 | 
					        data.len()
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
 | 
					    dac.write(ValueArray::Bit8(data), true).await;
 | 
				
			||||||
        error!("Could not write to dac: {}", e);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn to_sine_wave(v: u8) -> u8 {
 | 
					fn to_sine_wave(v: u8) -> u8 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ use common::*;
 | 
				
			|||||||
use defmt::assert;
 | 
					use defmt::assert;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::adc::Adc;
 | 
					use embassy_stm32::adc::Adc;
 | 
				
			||||||
use embassy_stm32::dac::{DacCh1, DacChannel, Value};
 | 
					use embassy_stm32::dac::{DacCh1, Value};
 | 
				
			||||||
use embassy_stm32::dma::NoDma;
 | 
					use embassy_stm32::dma::NoDma;
 | 
				
			||||||
use embassy_time::{Delay, Timer};
 | 
					use embassy_time::{Delay, Timer};
 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
@@ -26,9 +26,7 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    #[cfg(any(feature = "stm32h755zi", feature = "stm32g071rb"))]
 | 
					    #[cfg(any(feature = "stm32h755zi", feature = "stm32g071rb"))]
 | 
				
			||||||
    let dac_peripheral = p.DAC1;
 | 
					    let dac_peripheral = p.DAC1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut dac: DacCh1<'_, _, NoDma> = DacCh1::new(dac_peripheral, NoDma, p.PA4);
 | 
					    let mut dac = DacCh1::new(dac_peripheral, NoDma, p.PA4);
 | 
				
			||||||
    unwrap!(dac.set_trigger_enable(false));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let mut adc = Adc::new(p.ADC1, &mut Delay);
 | 
					    let mut adc = Adc::new(p.ADC1, &mut Delay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(feature = "stm32h755zi")]
 | 
					    #[cfg(feature = "stm32h755zi")]
 | 
				
			||||||
@@ -36,7 +34,7 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    #[cfg(any(feature = "stm32f429zi", feature = "stm32g071rb"))]
 | 
					    #[cfg(any(feature = "stm32f429zi", feature = "stm32g071rb"))]
 | 
				
			||||||
    let normalization_factor: i32 = 16;
 | 
					    let normalization_factor: i32 = 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unwrap!(dac.set(Value::Bit8(0)));
 | 
					    dac.set(Value::Bit8(0));
 | 
				
			||||||
    // Now wait a little to obtain a stable value
 | 
					    // Now wait a little to obtain a stable value
 | 
				
			||||||
    Timer::after_millis(30).await;
 | 
					    Timer::after_millis(30).await;
 | 
				
			||||||
    let offset = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4);
 | 
					    let offset = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4);
 | 
				
			||||||
@@ -44,7 +42,7 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    for v in 0..=255 {
 | 
					    for v in 0..=255 {
 | 
				
			||||||
        // First set the DAC output value
 | 
					        // First set the DAC output value
 | 
				
			||||||
        let dac_output_val = to_sine_wave(v);
 | 
					        let dac_output_val = to_sine_wave(v);
 | 
				
			||||||
        unwrap!(dac.set(Value::Bit8(dac_output_val)));
 | 
					        dac.set(Value::Bit8(dac_output_val));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Now wait a little to obtain a stable value
 | 
					        // Now wait a little to obtain a stable value
 | 
				
			||||||
        Timer::after_millis(30).await;
 | 
					        Timer::after_millis(30).await;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user