diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d04000a1..292902ac 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f6d1ffc1a25f208b5cd6b1024bff246592da1949" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f6d1ffc1a25f208b5cd6b1024bff246592da1949", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 4aae5822..7bfd290d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -65,7 +65,6 @@ fn main() { match r.kind { // Generate singletons per pin, not per port "gpio" => { - println!("{}", p.name); let port_letter = p.name.strip_prefix("GPIO").unwrap(); for pin_num in 0..16 { singletons.push(format!("P{}{}", port_letter, pin_num)); @@ -997,8 +996,8 @@ fn main() { // SDMMCv1 uses the same channel for both directions, so just implement for RX (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), - (("dac", "CH1"), quote!(crate::dac::DmaCh1)), - (("dac", "CH2"), quote!(crate::dac::DmaCh2)), + (("dac", "CH1"), quote!(crate::dac::DacDma1)), + (("dac", "CH2"), quote!(crate::dac::DacDma2)), ] .into(); @@ -1352,15 +1351,6 @@ fn main() { if let Some(core) = core_name { println!("cargo:rustc-cfg={}_{}", &chip_name[..chip_name.len() - 2], core); - } else { - println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]); - } - - // ======== - // stm32f3 wildcard features used in RCC - - if chip_name.starts_with("stm32f3") { - println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]); } // ======= @@ -1375,16 +1365,25 @@ fn main() { if &chip_name[..8] == "stm32wba" { println!("cargo:rustc-cfg={}", &chip_name[..8]); // stm32wba println!("cargo:rustc-cfg={}", &chip_name[..10]); // stm32wba52 + println!("cargo:rustc-cfg=package_{}", &chip_name[10..11]); + println!("cargo:rustc-cfg=flashsize_{}", &chip_name[11..12]); } else { println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4 println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429 println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9 + println!("cargo:rustc-cfg=package_{}", &chip_name[9..10]); + println!("cargo:rustc-cfg=flashsize_{}", &chip_name[10..11]); } - // Handle time-driver-XXXX features. - if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {} - println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]); + // Mark the L4+ chips as they have many differences to regular L4. + if &chip_name[..7] == "stm32l4" { + if "pqrs".contains(&chip_name[7..8]) { + println!("cargo:rustc-cfg=stm32l4_plus"); + } else { + println!("cargo:rustc-cfg=stm32l4_nonplus"); + } + } println!("cargo:rerun-if-changed=build.rs"); } diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 3d1a820e..500eac4c 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -1,136 +1,66 @@ +//! Provide access to the STM32 digital-to-analog converter (DAC). #![macro_use] -//! Provide access to the STM32 digital-to-analog converter (DAC). use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; +use crate::dma::NoDma; +#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; +mod tsel; +pub use tsel::TriggerSel; + +/// Operating mode for DAC channel +#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Custom Errors -pub enum Error { - UnconfiguredChannel, - InvalidValue, +pub enum Mode { + /// Normal mode, channel is connected to external pin with buffer enabled. + NormalExternalBuffered, + /// Normal mode, channel is connected to external pin and internal peripherals + /// with buffer enabled. + NormalBothBuffered, + /// Normal mode, channel is connected to external pin with buffer disabled. + NormalExternalUnbuffered, + /// Normal mode, channel is connected to internal peripherals with buffer disabled. + NormalInternalUnbuffered, + /// Sample-and-hold mode, channel is connected to external pin with buffer enabled. + SampleHoldExternalBuffered, + /// Sample-and-hold mode, channel is connected to external pin and internal peripherals + /// with buffer enabled. + SampleHoldBothBuffered, + /// Sample-and-hold mode, channel is connected to external pin and internal peripherals + /// with buffer disabled. + SampleHoldBothUnbuffered, + /// Sample-and-hold mode, channel is connected to internal peripherals with buffer disabled. + SampleHoldInternalUnbuffered, } -#[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 { +#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] +impl Mode { + fn mode(&self) -> dac::vals::Mode { match self { - Channel::Ch1 => 0, - Channel::Ch2 => 1, + Mode::NormalExternalBuffered => dac::vals::Mode::NORMAL_EXT_BUFEN, + Mode::NormalBothBuffered => dac::vals::Mode::NORMAL_EXT_INT_BUFEN, + Mode::NormalExternalUnbuffered => dac::vals::Mode::NORMAL_EXT_BUFDIS, + Mode::NormalInternalUnbuffered => dac::vals::Mode::NORMAL_INT_BUFDIS, + Mode::SampleHoldExternalBuffered => dac::vals::Mode::SAMPHOLD_EXT_BUFEN, + Mode::SampleHoldBothBuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFEN, + Mode::SampleHoldBothUnbuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFDIS, + Mode::SampleHoldInternalUnbuffered => dac::vals::Mode::SAMPHOLD_INT_BUFDIS, } } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Trigger sources for CH1 -pub enum Ch1Trigger { - #[cfg(dac_v3)] - Tim1, - Tim2, - #[cfg(not(dac_v3))] - Tim3, - #[cfg(dac_v3)] - Tim4, - #[cfg(dac_v3)] - Tim5, - Tim6, - Tim7, - #[cfg(dac_v3)] - Tim8, - Tim15, - #[cfg(dac_v3)] - Hrtim1Dactrg1, - #[cfg(dac_v3)] - Hrtim1Dactrg2, - #[cfg(dac_v3)] - Lptim1, - #[cfg(dac_v3)] - Lptim2, - #[cfg(dac_v3)] - Lptim3, - Exti9, - Software, -} - -impl Ch1Trigger { - fn tsel(&self) -> dac::vals::Tsel1 { - match self { - #[cfg(dac_v3)] - Ch1Trigger::Tim1 => dac::vals::Tsel1::TIM1_TRGO, - Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO, - #[cfg(not(dac_v3))] - Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, - #[cfg(dac_v3)] - Ch1Trigger::Tim4 => dac::vals::Tsel1::TIM4_TRGO, - #[cfg(dac_v3)] - Ch1Trigger::Tim5 => dac::vals::Tsel1::TIM5_TRGO, - Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, - Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, - #[cfg(dac_v3)] - Ch1Trigger::Tim8 => dac::vals::Tsel1::TIM8_TRGO, - Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, - #[cfg(dac_v3)] - Ch1Trigger::Hrtim1Dactrg1 => dac::vals::Tsel1::HRTIM1_DACTRG1, - #[cfg(dac_v3)] - Ch1Trigger::Hrtim1Dactrg2 => dac::vals::Tsel1::HRTIM1_DACTRG2, - #[cfg(dac_v3)] - Ch1Trigger::Lptim1 => dac::vals::Tsel1::LPTIM1_OUT, - #[cfg(dac_v3)] - Ch1Trigger::Lptim2 => dac::vals::Tsel1::LPTIM2_OUT, - #[cfg(dac_v3)] - Ch1Trigger::Lptim3 => dac::vals::Tsel1::LPTIM3_OUT, - Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9, - Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE, - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Trigger sources for CH2 -pub enum Ch2Trigger { - Tim6, - Tim8, - Tim7, - Tim5, - Tim2, - Tim4, - Exti9, - Software, -} - -impl Ch2Trigger { - fn tsel(&self) -> dac::vals::Tsel2 { - match self { - Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO, - Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO, - Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO, - Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO, - Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO, - Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO, - Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9, - Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE, - } - } -} - -#[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 +/// Single 8 or 12 bit value that can be output by the DAC. +/// +/// 12-bit values outside the permitted range are silently truncated. pub enum Value { // 8 bit value Bit8(u8), @@ -142,7 +72,21 @@ pub enum Value { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[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> { // 8 bit values Bit8(&'a [u8]), @@ -151,398 +95,395 @@ pub enum ValueArray<'a> { // 12 bit values stored in a u16, right-aligned Bit12Right(&'a [u16]), } -/// Provide common functions for DAC channels -pub trait DacChannel { - const CHANNEL: Channel; - /// Enable trigger of the given 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_v2, dac_v3))] - fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { - T::regs().mcr().modify(|reg| { - reg.set_mode(Self::CHANNEL.index(), val); - }); - 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". - fn set(&mut self, value: Value) -> Result<(), Error> { - match value { - 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 +/// Driver for a single DAC channel. /// -/// 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 +/// If you want to use both channels, either together or independently, +/// create a [`Dac`] first and use it to access each channel. +pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> { phantom: PhantomData<&'d mut T>, - #[allow(unused)] // For chips whose DMA is not (yet) supported - dma: PeripheralRef<'d, Tx>, + #[allow(unused)] + dma: PeripheralRef<'d, DMA>, } -impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { - /// Obtain DAC CH1 - pub fn new( - peri: impl Peripheral

+ 'd, - dma: impl Peripheral

+ 'd, - pin: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, - ) -> Self { - pin.set_as_analog(); - into_ref!(peri, dma); - T::enable_and_reset(); +pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>; +pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>; - 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 - // will only error if the channel is not configured (i.e. ch1, ch2 are false) - #[cfg(any(dac_v2, dac_v3))] - dac.set_channel_mode(0).unwrap(); - dac.enable_channel().unwrap(); - dac.set_trigger_enable(true).unwrap(); - - dac - } - - /// Select a new trigger for this channel + /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral. /// - /// **Important**: This disables the channel! - pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { - unwrap!(self.disable_channel()); - T::regs().cr().modify(|reg| { - reg.set_tsel1(trigger.tsel()); - }); - Ok(()) - } - - /// Write `data` to the DAC CH1 via DMA. + /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument. /// - /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. - /// This will configure a circular DMA transfer that periodically outputs the `data`. - /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. + /// The channel is enabled on creation and begins to drive the output pin. + /// Note that some methods, such as `set_trigger()` and `set_mode()`, will + /// disable the channel; you must re-enable it with `enable()`. /// - /// **Important:** Channel 1 has to be configured for the DAC instance! - pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: DmaCh1, - { - 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 + /// By default, triggering is disabled, but it can be enabled using + /// [`DacChannel::set_trigger()`]. pub fn new( _peri: impl Peripheral

+ 'd, - dma: impl Peripheral

+ 'd, - pin: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, + dma: impl Peripheral

+ 'd, + pin: impl Peripheral

+ crate::gpio::sealed::Pin> + 'd, ) -> Self { + into_ref!(dma, pin); pin.set_as_analog(); - into_ref!(_peri, dma); T::enable_and_reset(); - let mut dac = Self { phantom: PhantomData, dma, }; - - // 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_v2, dac_v3))] - dac.set_channel_mode(0).unwrap(); - dac.enable_channel().unwrap(); - dac.set_trigger_enable(true).unwrap(); - + #[cfg(any(dac_v5, dac_v6, dac_v7))] + dac.set_hfsel(); + dac.enable(); dac } - /// Select a new trigger for this channel - pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { - unwrap!(self.disable_channel()); - T::regs().cr().modify(|reg| { - reg.set_tsel2(trigger.tsel()); - }); - Ok(()) - } - - /// Write `data` to the DAC CH2 via DMA. + /// Create a new `DacChannel` instance where the external output pin is not used, + /// so the DAC can only be used to generate internal signals. + /// The GPIO pin is therefore available to be used for other functions. /// - /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. - /// This will configure a circular DMA transfer that periodically outputs the `data`. - /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. + /// 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 + /// channel; you must re-enable it with `enable()`. /// - /// **Important:** Channel 2 has to be configured for the DAC instance! - pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: DmaCh2, - { - let channel = Channel::Ch2.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, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { - /// Create a new DAC instance with both channels. + /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument. /// - /// This is used to obtain two independent channels via `split()` for use e.g. with DMA. - pub fn new( - peri: impl Peripheral

+ 'd, - dma_ch1: impl Peripheral

+ 'd, - dma_ch2: impl Peripheral

+ 'd, - pin_ch1: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, - pin_ch2: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, - ) -> Self { - pin_ch1.set_as_analog(); - pin_ch2.set_as_analog(); - into_ref!(peri, dma_ch1, dma_ch2); + /// 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

+ 'd, dma: impl Peripheral

+ 'd) -> Self { + into_ref!(dma); T::enable_and_reset(); - - let mut dac_ch1 = DacCh1 { - _peri: peri, - dma: dma_ch1, - }; - - let mut dac_ch2 = DacCh2 { + let mut dac = Self { phantom: PhantomData, - dma: dma_ch2, + dma, }; + #[cfg(any(dac_v5, dac_v6, dac_v7))] + dac.set_hfsel(); + dac.set_mode(Mode::NormalInternalUnbuffered); + dac.enable(); + dac + } - // 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_v2, dac_v3))] - dac_ch1.set_channel_mode(0).unwrap(); - dac_ch1.enable_channel().unwrap(); - dac_ch1.set_trigger_enable(true).unwrap(); + /// Enable or disable this channel. + pub fn set_enable(&mut self, on: bool) { + critical_section::with(|_| { + T::regs().cr().modify(|reg| { + reg.set_en(Self::IDX, on); + }); + }); + } - #[cfg(any(dac_v2, dac_v3))] - dac_ch2.set_channel_mode(0).unwrap(); - dac_ch2.enable_channel().unwrap(); - dac_ch2.set_trigger_enable(true).unwrap(); + /// Enable this channel. + pub fn enable(&mut self) { + self.set_enable(true) + } - Self { - ch1: dac_ch1, - ch2: dac_ch2, + /// 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)), } } - /// Split the DAC into CH1 and CH2 for independent use. - pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { + /// 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, + { + /// 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 + T::regs().cr().modify(|w| { + w.set_en(Self::IDX, true); + w.set_dmaen(Self::IDX, 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(Self::IDX).as_ptr() as *mut u8, + tx_options, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + crate::dma::Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12l(Self::IDX).as_ptr() as *mut u16, + tx_options, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + crate::dma::Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12r(Self::IDX).as_ptr() as *mut u16, + tx_options, + ) + }, + }; + + tx_f.await; + + T::regs().cr().modify(|w| { + w.set_en(Self::IDX, false); + w.set_dmaen(Self::IDX, false); + }); + } + } + }; +} + +impl_dma_methods!(1, DacDma1); +impl_dma_methods!(2, DacDma2); + +impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> { + fn drop(&mut self) { + T::disable(); + } +} + +/// DAC driver. +/// +/// 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( + _peri: impl Peripheral

+ 'd, + dma_ch1: impl Peripheral

+ 'd, + dma_ch2: impl Peripheral

+ 'd, + pin_ch1: impl Peripheral

+ crate::gpio::sealed::Pin> + 'd, + pin_ch2: impl Peripheral

+ crate::gpio::sealed::Pin> + 'd, + ) -> Self { + into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2); + pin_ch1.set_as_analog(); + pin_ch2.set_as_analog(); + // Enable twice to increment the DAC refcount for each channel. + T::enable_and_reset(); + T::enable_and_reset(); + Self { + ch1: DacCh1 { + phantom: PhantomData, + dma: dma_ch1, + }, + ch2: DacCh2 { + phantom: PhantomData, + dma: dma_ch2, + }, + } + } + + /// 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

+ 'd, + dma_ch1: impl Peripheral

+ 'd, + dma_ch2: impl Peripheral

+ '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 { + ch1: DacCh1 { + phantom: PhantomData, + dma: dma_ch1, + }, + ch2: DacCh2 { + phantom: PhantomData, + dma: dma_ch2, + }, + } + } + + /// Split this `Dac` into separate channels. + /// + /// 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) } - /// Get mutable reference to CH1 - pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { + /// Temporarily access channel 1. + pub fn ch1(&mut self) -> &mut DacCh1<'d, T, DMACh1> { &mut self.ch1 } - /// Get mutable reference to CH2 - pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { + /// Temporarily access channel 2. + pub fn ch2(&mut self) -> &mut DacCh2<'d, T, DMACh2> { &mut self.ch2 } - /// Get reference to CH1 - pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { - &self.ch1 + /// Simultaneously update channels 1 and 2 with a new value. + /// + /// 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) { + match values { + DualValue::Bit8(v1, v2) => T::regs().dhr8rd().write(|reg| { + 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); + }), + } } - - /// Get reference to CH2 - pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { - &self.ch2 - } -} - -impl<'d, T: Instance, Tx> DacChannel for DacCh1<'d, T, Tx> { - const CHANNEL: Channel = Channel::Ch1; -} - -impl<'d, T: Instance, Tx> DacChannel for DacCh2<'d, T, Tx> { - const CHANNEL: Channel = Channel::Ch2; } pub(crate) mod sealed { @@ -552,34 +493,36 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + RccPeripheral + 'static {} -dma_trait!(DmaCh1, Instance); -dma_trait!(DmaCh2, Instance); +dma_trait!(DacDma1, Instance); +dma_trait!(DacDma2, Instance); /// Marks a pin that can be used with the DAC pub trait DacPin: crate::gpio::Pin + 'static {} foreach_peripheral!( (dac, $inst:ident) => { - // H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented - #[cfg(any(rcc_h7, rcc_h7rm0433))] - impl crate::rcc::sealed::RccPeripheral for peripherals::$inst { - fn frequency() -> crate::time::Hertz { - critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 }) - } + // H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented + #[cfg(any(rcc_h7, rcc_h7rm0433))] + impl crate::rcc::sealed::RccPeripheral for peripherals::$inst { + fn frequency() -> crate::time::Hertz { + critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 }) + } - fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) { - crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true)); - crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false)); - crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); - } + 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(false)); + crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); + } - fn disable_with_cs(_cs: critical_section::CriticalSection) { - crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)) - } - } + fn disable_with_cs(_cs: critical_section::CriticalSection) { + // TODO: Decrement refcount? + crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)) + } + } - #[cfg(any(rcc_h7, rcc_h7rm0433))] - impl crate::rcc::RccPeripheral for peripherals::$inst {} + #[cfg(any(rcc_h7, rcc_h7rm0433))] + impl crate::rcc::RccPeripheral for peripherals::$inst {} impl crate::dac::sealed::Instance for peripherals::$inst { fn regs() -> &'static crate::pac::dac::Dac { diff --git a/embassy-stm32/src/dac/tsel.rs b/embassy-stm32/src/dac/tsel.rs new file mode 100644 index 00000000..f38dd8fd --- /dev/null +++ b/embassy-stm32/src/dac/tsel.rs @@ -0,0 +1,282 @@ +/// Trigger selection for STM32F0. +#[cfg(stm32f0)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + Tim3 = 1, + Tim7 = 2, + Tim15 = 3, + Tim2 = 4, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for STM32F1. +#[cfg(stm32f1)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + #[cfg(any(stm32f100, stm32f105, stm32f107))] + Tim3 = 1, + #[cfg(any(stm32f101, stm32f103))] + Tim8 = 1, + Tim7 = 2, + #[cfg(any(stm32f101, stm32f103, stm32f105, stm32f107))] + Tim5 = 3, + #[cfg(all(stm32f100, any(flashsize_4, flashsize_6, flashsize_8, flashsize_b)))] + Tim15 = 3, + #[cfg(all(stm32f100, any(flashsize_c, flashsize_d, flashsize_e)))] + /// Can be remapped to TIM15 with MISC_REMAP in AFIO_MAPR2. + Tim5Or15 = 3, + Tim2 = 4, + Tim4 = 5, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for STM32F2/F4/F7/L4, except F410 or L4+. +#[cfg(all(any(stm32f2, stm32f4, stm32f7, stm32l4_nonplus), not(stm32f410)))] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + Tim8 = 1, + #[cfg(not(any(stm32l45x, stm32l46x)))] + Tim7 = 2, + Tim5 = 3, + Tim2 = 4, + Tim4 = 5, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for STM32F410. +#[cfg(stm32f410)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim5 = 3, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for STM32F301/2 and 318. +#[cfg(any(stm32f301, stm32f302, stm32f318))] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + #[cfg(stm32f302)] + /// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1. + Tim3 = 1, + Tim15 = 3, + Tim2 = 4, + #[cfg(all(stm32f302, any(flashsize_6, flashsize_8)))] + Tim4 = 5, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for STM32F303/3x8 (excluding 318 which is like 301, and 378 which is 37x). +#[cfg(any(stm32f303, stm32f328, stm32f358, stm32f398))] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + /// * DAC1: defaults to TIM8 but can be remapped to TIM3 with DAC_TRIG_RMP in SYSCFG_CFGR1 + /// * DAC2: always TIM3 + Tim8Or3 = 1, + Tim7 = 2, + Tim15 = 3, + Tim2 = 4, + Tim4 = 5, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for STM32F37x. +#[cfg(any(stm32f373, stm32f378))] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + Tim3 = 1, + Tim7 = 2, + /// TIM5 on DAC1, TIM18 on DAC2 + Dac1Tim5Dac2Tim18 = 3, + Tim2 = 4, + Tim4 = 5, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for STM32F334. +#[cfg(stm32f334)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + /// Requires DAC_TRIG_RMP set in SYSCFG_CFGR1. + Tim3 = 1, + Tim7 = 2, + /// Can be remapped to HRTIM_DACTRG1 using DAC1_TRIG3_RMP in SYSCFG_CFGR3. + Tim15OrHrtimDacTrg1 = 3, + Tim2 = 4, + /// Requires DAC_TRIG5_RMP set in SYSCFG_CFGR3. + HrtimDacTrg2 = 5, +} + +/// Trigger selection for STM32L0. +#[cfg(stm32l0)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + Tim3 = 1, + Tim3Ch3 = 2, + Tim21 = 3, + Tim2 = 4, + Tim7 = 5, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for STM32L1. +#[cfg(stm32l1)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Tim6 = 0, + Tim7 = 2, + Tim9 = 3, + Tim2 = 4, + Tim4 = 5, + Exti9 = 6, + Software = 7, +} + +/// Trigger selection for L4+, L5, U5, H7. +#[cfg(any(stm32l4_plus, stm32l5, stm32u5, stm32h7))] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Software = 0, + Tim1 = 1, + Tim2 = 2, + Tim4 = 3, + Tim5 = 4, + Tim6 = 5, + Tim7 = 6, + Tim8 = 7, + Tim15 = 8, + #[cfg(all(stm32h7, hrtim))] + Hrtim1DacTrg1 = 9, + #[cfg(all(stm32h7, hrtim))] + Hrtim1DacTrg2 = 10, + Lptim1 = 11, + #[cfg(not(stm32u5))] + Lptim2 = 12, + #[cfg(stm32u5)] + Lptim3 = 12, + Exti9 = 13, + #[cfg(any(stm32h7ax, stm32h7bx))] + /// RM0455 suggests this might be LPTIM2 on DAC1 and LPTIM3 on DAC2, + /// but it's probably wrong. Please let us know if you find out. + Lptim3 = 14, + #[cfg(any(stm32h72x, stm32h73x))] + Tim23 = 14, + #[cfg(any(stm32h72x, stm32h73x))] + Tim24 = 15, +} + +/// Trigger selection for H5. +#[cfg(stm32h5)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Software = 0, + Tim1 = 1, + Tim2 = 2, + #[cfg(any(stm32h56x, stm32h57x))] + Tim4 = 3, + #[cfg(stm32h503)] + Tim3 = 3, + #[cfg(any(stm32h56x, stm32h57x))] + Tim5 = 4, + Tim6 = 5, + Tim7 = 6, + #[cfg(any(stm32h56x, stm32h57x))] + Tim8 = 7, + #[cfg(any(stm32h56x, stm32h57x))] + Tim15 = 8, + Lptim1 = 11, + Lptim2 = 12, + Exti9 = 13, +} + +/// Trigger selection for G0. +#[cfg(stm32g0)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Software = 0, + Tim1 = 1, + Tim2 = 2, + Tim3 = 3, + Tim6 = 5, + Tim7 = 6, + Tim15 = 8, + Lptim1 = 11, + Lptim2 = 12, + Exti9 = 13, +} + +/// Trigger selection for G4. +#[cfg(stm32g4)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Software = 0, + /// * DAC1, DAC2, DAC4: TIM8 + /// * DAC3: TIM1 + Dac124Tim8Dac3Tim1 = 1, + Tim7 = 2, + Tim15 = 3, + Tim2 = 4, + Tim4 = 5, + Exti9 = 6, + Tim6 = 7, + Tim3 = 8, + HrtimDacRstTrg1 = 9, + HrtimDacRstTrg2 = 10, + HrtimDacRstTrg3 = 11, + HrtimDacRstTrg4 = 12, + HrtimDacRstTrg5 = 13, + HrtimDacRstTrg6 = 14, + /// * DAC1, DAC4: HRTIM_DAC_TRG1 + /// * DAC2: HRTIM_DAC_TRG2 + /// * DAC3: HRTIM_DAC_TRG3 + HrtimDacTrg123 = 15, +} + +/// Trigger selection for WL. +#[cfg(stm32wl)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Software = 0, + Tim1 = 1, + Tim2 = 2, + Lptim1 = 11, + Lptim2 = 12, + Lptim3 = 13, + Exti9 = 14, +} + +impl TriggerSel { + pub fn tsel(&self) -> u8 { + *self as u8 + } +} diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs index 9dcd50df..bf035fd2 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -346,10 +346,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { None => { cfg_if::cfg_if! { // For some chips PREDIV is always two, and cannot be changed - if #[cfg(any( - stm32f302xd, stm32f302xe, stm32f303xd, - stm32f303xe, stm32f398xe - ))] { + if #[cfg(any(flashsize_d, flashsize_e))] { let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0); ( Hertz((HSI_FREQ.0 / divisor) * multiplier), diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs index aaedcfec..8f14d607 100644 --- a/examples/stm32f4/src/bin/dac.rs +++ b/examples/stm32f4/src/bin/dac.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dac::{DacCh1, Value}; use embassy_stm32::dma::NoDma; use {defmt_rtt as _, panic_probe as _}; @@ -14,11 +14,10 @@ async fn main(_spawner: Spawner) -> ! { info!("Hello World, dude!"); let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4); - unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { - unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.set(Value::Bit8(to_sine_wave(v))); } } } diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index 35fd6550..f6626815 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs @@ -4,7 +4,7 @@ use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dac::{DacCh1, Value}; use embassy_stm32::dma::NoDma; use embassy_stm32::Config; use {defmt_rtt as _, panic_probe as _}; @@ -46,11 +46,10 @@ fn main() -> ! { let p = embassy_stm32::init(config); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); - unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { - unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.set(Value::Bit8(to_sine_wave(v))); } } } diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index e141fc48..c19fdd62 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -4,21 +4,15 @@ use defmt::*; 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::peripherals::{TIM6, TIM7}; +use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Basic16bitInstance; use micromath::F32Ext; 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] async fn main(spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -63,7 +57,7 @@ async fn main(spawner: Spawner) { } #[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>(); info!("TIM6 frequency is {}", TIM6::frequency()); @@ -77,8 +71,9 @@ async fn dac_task1(mut dac: Dac1Type) { error!("Reload value {} below threshold!", reload); } - dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); - dac.enable_channel().unwrap(); + dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6); + dac.set_triggering(true); + dac.enable(); TIM6::enable_and_reset(); 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 { info!("Loop DAC1"); - if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { - error!("Could not write to dac: {}", e); - } + dac.write(ValueArray::Bit8(data), true).await; } } #[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>(); info!("TIM7 frequency is {}", TIM7::frequency()); @@ -127,7 +120,9 @@ async fn dac_task2(mut dac: Dac2Type) { w.set_cen(true); }); - dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); + dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7); + dac.set_triggering(true); + dac.enable(); debug!( "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", @@ -138,9 +133,7 @@ async fn dac_task2(mut dac: Dac2Type) { data.len() ); - if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { - error!("Could not write to dac: {}", e); - } + dac.write(ValueArray::Bit8(data), true).await; } fn to_sine_wave(v: u8) -> u8 { diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index 0193a248..d6a7ff62 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dac::{DacCh1, Value}; use embassy_stm32::dma::NoDma; use {defmt_rtt as _, panic_probe as _}; @@ -13,11 +13,10 @@ fn main() -> ! { info!("Hello World!"); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); - unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { - unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.set(Value::Bit8(to_sine_wave(v))); } } } diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index 98f37f90..dc86dbf4 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -4,21 +4,15 @@ use defmt::*; 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::peripherals::{TIM6, TIM7}; +use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Basic16bitInstance; use micromath::F32Ext; 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] async fn main(spawner: Spawner) { let config = embassy_stm32::Config::default(); @@ -34,7 +28,7 @@ async fn main(spawner: Spawner) { } #[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>(); info!("TIM6 frequency is {}", TIM6::frequency()); @@ -48,8 +42,9 @@ async fn dac_task1(mut dac: Dac1Type) { error!("Reload value {} below threshold!", reload); } - dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); - dac.enable_channel().unwrap(); + dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim6); + dac.set_triggering(true); + dac.enable(); TIM6::enable_and_reset(); 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 { info!("Loop DAC1"); - if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { - error!("Could not write to dac: {}", e); - } + dac.write(ValueArray::Bit8(data), true).await; } } #[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>(); info!("TIM7 frequency is {}", TIM7::frequency()); @@ -98,7 +91,9 @@ async fn dac_task2(mut dac: Dac2Type) { w.set_cen(true); }); - dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); + dac.set_trigger(embassy_stm32::dac::TriggerSel::Tim7); + dac.set_triggering(true); + dac.enable(); debug!( "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", @@ -109,9 +104,7 @@ async fn dac_task2(mut dac: Dac2Type) { data.len() ); - if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { - error!("Could not write to dac: {}", e); - } + dac.write(ValueArray::Bit8(data), true).await; } fn to_sine_wave(v: u8) -> u8 { diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs index 10e3c3e8..824eb880 100644 --- a/tests/stm32/src/bin/dac.rs +++ b/tests/stm32/src/bin/dac.rs @@ -10,7 +10,7 @@ use common::*; use defmt::assert; use embassy_executor::Spawner; 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_time::{Delay, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -26,9 +26,7 @@ async fn main(_spawner: Spawner) { #[cfg(any(feature = "stm32h755zi", feature = "stm32g071rb"))] let dac_peripheral = p.DAC1; - let mut dac: DacCh1<'_, _, NoDma> = DacCh1::new(dac_peripheral, NoDma, p.PA4); - unwrap!(dac.set_trigger_enable(false)); - + let mut dac = DacCh1::new(dac_peripheral, NoDma, p.PA4); let mut adc = Adc::new(p.ADC1, &mut Delay); #[cfg(feature = "stm32h755zi")] @@ -36,7 +34,7 @@ async fn main(_spawner: Spawner) { #[cfg(any(feature = "stm32f429zi", feature = "stm32g071rb"))] 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 Timer::after_millis(30).await; 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 { // First set the DAC output value 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 Timer::after_millis(30).await;