diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f7a25743c..7fa4fae45 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -699,8 +699,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::Dma)), - (("dac", "CH2"), quote!(crate::dac::Dma)), + (("dac", "CH1"), quote!(crate::dac::DmaCh1)), + (("dac", "CH2"), quote!(crate::dac::DmaCh2)), ] .into(); diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index d95674ff0..6ead00e15 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -171,13 +171,6 @@ pub trait DacChannel { } Ok(()) } - - /// Write `data` to the DAC channel via DMA. - /// - /// `circular` sets the DMA to circular mode. - async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma; } /// Hold two DAC channels @@ -244,6 +237,81 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { }); Ok(()) } + + /// Write `data` to the DAC CH1 via DMA. + /// + /// 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. + /// + /// **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 = &self.dma; + + let tx_options = 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 { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr8r(channel).as_ptr() as *mut u8, + tx_options, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12l(channel).as_ptr() as *mut u16, + tx_options, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + 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> { @@ -279,6 +347,81 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { }); Ok(()) } + + /// Write `data` to the DAC CH2 via DMA. + /// + /// 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. + /// + /// **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 = &self.dma; + + let tx_options = 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 { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr8r(channel).as_ptr() as *mut u8, + tx_options, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12l(channel).as_ptr() as *mut u16, + tx_options, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + 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> { @@ -350,117 +493,10 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { impl<'d, T: Instance, Tx> DacChannel for DacCh1<'d, T, Tx> { const CHANNEL: Channel = Channel::Ch1; - - /// Write `data` to the DAC CH1 via DMA. - /// - /// 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. - /// - /// **Important:** Channel 1 has to be configured for the DAC instance! - async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - write_inner(Self::CHANNEL, &self.dma, data, circular).await - } } impl<'d, T: Instance, Tx> DacChannel for DacCh2<'d, T, Tx> { const CHANNEL: Channel = Channel::Ch2; - - /// Write `data` to the DAC CH2 via DMA. - /// - /// 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. - /// - /// **Important:** Channel 2 has to be configured for the DAC instance! - async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - write_inner(Self::CHANNEL, &self.dma, data, circular).await - } -} - -/// Shared utility function to perform the actual DMA config and write. -async fn write_inner( - ch: Channel, - dma: &PeripheralRef<'_, Tx>, - data: ValueArray<'_>, - circular: bool, -) -> Result<(), Error> -where - Tx: Dma, -{ - let channel = ch.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 = dma.request(); - let dma_channel = dma; - - // Initiate the correct type of DMA transfer depending on what data is passed - let tx_f = match data { - ValueArray::Bit8(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr8r(channel).as_ptr() as *mut u8, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - ValueArray::Bit12Left(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr12l(channel).as_ptr() as *mut u16, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - ValueArray::Bit12Right(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr12r(channel).as_ptr() as *mut u16, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - }; - - 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(()) } pub(crate) mod sealed { @@ -470,7 +506,8 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + RccPeripheral + 'static {} -dma_trait!(Dma, Instance); +dma_trait!(DmaCh1, Instance); +dma_trait!(DmaCh2, Instance); /// Marks a pin that can be used with the DAC pub trait DacPin: crate::gpio::Pin + 'static {} diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 8abe541d3..a5f828948 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -29,6 +29,12 @@ pub struct TransferOptions { pub flow_ctrl: FlowControl, /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. pub fifo_threshold: Option, + /// Enable circular DMA + pub circular: bool, + /// Enable half transfer interrupt + pub half_transfer_ir: bool, + /// Enable transfer complete interrupt + pub complete_transfer_ir: bool, } impl Default for TransferOptions { @@ -38,6 +44,9 @@ impl Default for TransferOptions { mburst: Burst::Single, flow_ctrl: FlowControl::Dma, fifo_threshold: None, + circular: false, + half_transfer_ir: false, + complete_transfer_ir: true, } } } @@ -366,13 +375,20 @@ impl<'a, C: Channel> Transfer<'a, C> { }); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_tcie(true); + w.set_tcie(options.complete_transfer_ir); + w.set_htie(options.half_transfer_ir); #[cfg(dma_v1)] w.set_trbuff(true); #[cfg(dma_v2)] w.set_chsel(_request); + if options.circular { + w.set_circ(vals::Circ::ENABLED); + debug!("Setting circular mode"); + } else { + w.set_circ(vals::Circ::DISABLED); + } w.set_pburst(options.pburst.into()); w.set_mburst(options.mburst.into()); w.set_pfctrl(options.flow_ctrl.into()); @@ -404,8 +420,14 @@ impl<'a, C: Channel> Transfer<'a, C> { } pub fn is_running(&mut self) -> bool { + //let ch = self.channel.regs().st(self.channel.num()); + //ch.cr().read().en() + let ch = self.channel.regs().st(self.channel.num()); - ch.cr().read().en() + let en = ch.cr().read().en(); + let circular = ch.cr().read().circ() == vals::Circ::ENABLED; + let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; + en && (circular || !tcif) } /// Gets the total remaining transfers for the channel