From f5d084552d9f44d24f020269cc605de0fb4d1041 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 17 Jun 2023 11:48:21 +0200 Subject: [PATCH 01/28] implement mwe of a DMA write() method for DAC --- embassy-stm32/build.rs | 2 + embassy-stm32/src/{dac.rs => dac/mod.rs} | 76 +++++++++++++++++++++--- 2 files changed, 71 insertions(+), 7 deletions(-) rename embassy-stm32/src/{dac.rs => dac/mod.rs} (76%) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 9e597f18..e2bd01d7 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -699,6 +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)), ] .into(); diff --git a/embassy-stm32/src/dac.rs b/embassy-stm32/src/dac/mod.rs similarity index 76% rename from embassy-stm32/src/dac.rs rename to embassy-stm32/src/dac/mod.rs index 60e856c7..348d8bcc 100644 --- a/embassy-stm32/src/dac.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -2,6 +2,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; +use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -97,39 +98,58 @@ pub enum Value { Bit12(u16, Alignment), } -pub struct Dac<'d, T: Instance> { +pub struct Dac<'d, T: Instance, Tx> { channels: u8, + txdma: PeripheralRef<'d, Tx>, _peri: PeripheralRef<'d, T>, } -impl<'d, T: Instance> Dac<'d, T> { - pub fn new_1ch(peri: impl Peripheral

+ 'd, _ch1: impl Peripheral

> + 'd) -> Self { +impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { + pub fn new_1ch( + peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, + _ch1: impl Peripheral

> + 'd, + ) -> Self { into_ref!(peri); - Self::new_inner(peri, 1) + Self::new_inner(peri, 1, txdma) } pub fn new_2ch( peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, _ch1: impl Peripheral

> + 'd, _ch2: impl Peripheral

> + 'd, ) -> Self { into_ref!(peri); - Self::new_inner(peri, 2) + Self::new_inner(peri, 2, txdma) } - fn new_inner(peri: PeripheralRef<'d, T>, channels: u8) -> Self { + fn new_inner(peri: PeripheralRef<'d, T>, channels: u8, txdma: impl Peripheral

+ 'd) -> Self { + into_ref!(txdma); T::enable(); T::reset(); unsafe { + T::regs().mcr().modify(|reg| { + for ch in 0..channels { + reg.set_mode(ch as usize, 0); + reg.set_mode(ch as usize, 0); + } + }); + T::regs().cr().modify(|reg| { for ch in 0..channels { reg.set_en(ch as usize, true); + reg.set_ten(ch as usize, true); } }); } - Self { channels, _peri: peri } + Self { + channels, + txdma, + _peri: peri, + } } /// Check the channel is configured @@ -215,6 +235,47 @@ impl<'d, T: Instance> Dac<'d, T> { } Ok(()) } + + /// TODO: Allow an array of Value instead of only u16, right-aligned + pub async fn write(&mut self, data: &[u16]) -> Result<(), Error> + where + Tx: Dma, + { + // TODO: Make this a parameter or get it from the struct or so... + const CHANNEL: usize = 0; + + //debug!("Starting DAC"); + unsafe { + T::regs().cr().modify(|w| { + w.set_en(CHANNEL, true); + w.set_dmaen(CHANNEL, true); + }); + } + + let tx_request = self.txdma.request(); + + // Use the 12 bit right-aligned register for now. TODO: distinguish values + let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16; + + let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; + + //debug!("Awaiting tx_f"); + + tx_f.await; + + // finish dma + unsafe { + // 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 { @@ -224,6 +285,7 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + RccPeripheral + 'static {} +dma_trait!(Dma, Instance); pub trait DacPin: crate::gpio::Pin + 'static {} From 78a2ca8a0e5af3fe2c76a6cd025b74ea4322f6cf Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 17 Jun 2023 11:51:57 +0200 Subject: [PATCH 02/28] remove unnecessary use, disable DAC and DMA after transfer --- embassy-stm32/src/dac/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 348d8bcc..7b81ec1f 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -2,7 +2,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; -use crate::dma::{slice_ptr_parts, word, Transfer}; +use crate::dma::Transfer; use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -269,9 +269,9 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { T::regs().cr().modify(|w| { // Disable the dac peripheral - //w.set_en(CHANNEL, false); + w.set_en(CHANNEL, false); // Disable the DMA. TODO: Is this necessary? - //w.set_dmaen(CHANNEL, false); + w.set_dmaen(CHANNEL, false); }); } Ok(()) From f8ee33abb9943d6fe57c126eeecb36db9935c4ba Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 18 Jun 2023 18:51:36 +0200 Subject: [PATCH 03/28] add half transfer interrupt and circular dma --- embassy-stm32/src/dac/mod.rs | 17 ++++++++++++++--- embassy-stm32/src/dma/bdma.rs | 26 ++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 7b81ec1f..525d45d7 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -2,7 +2,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; -use crate::dma::Transfer; +use crate::dma::{Transfer, TransferOptions}; use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -237,7 +237,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { } /// TODO: Allow an array of Value instead of only u16, right-aligned - pub async fn write(&mut self, data: &[u16]) -> Result<(), Error> + pub async fn write(&mut self, data: &[u16], circular: bool) -> Result<(), Error> where Tx: Dma, { @@ -257,7 +257,18 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { // Use the 12 bit right-aligned register for now. TODO: distinguish values let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16; - let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; + let tx_f = unsafe { + Transfer::new_write( + &mut self.txdma, + tx_request, + data, + tx_dst, + TransferOptions { + circular, + halt_transfer_ir: false, + }, + ) + }; //debug!("Awaiting tx_f"); diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index c0a503e2..ca18047e 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -1,6 +1,7 @@ #![macro_use] use core::future::Future; +use core::option; use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; @@ -21,11 +22,17 @@ use crate::pac::bdma::{regs, vals}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] -pub struct TransferOptions {} +pub struct TransferOptions { + pub circular: bool, + pub halt_transfer_ir: bool, +} impl Default for TransferOptions { fn default() -> Self { - Self {} + Self { + circular: false, + halt_transfer_ir: false, + } } } @@ -253,7 +260,7 @@ impl<'a, C: Channel> Transfer<'a, C> { mem_len: usize, incr_mem: bool, data_size: WordSize, - _options: TransferOptions, + options: TransferOptions, ) -> Self { let ch = channel.regs().ch(channel.num()); @@ -284,6 +291,14 @@ impl<'a, C: Channel> Transfer<'a, C> { w.set_dir(dir.into()); w.set_teie(true); w.set_tcie(true); + w.set_htie(options.halt_transfer_ir); + if options.circular { + w.set_circ(vals::Circ::ENABLED); + debug!("Setting circular mode"); + } else { + w.set_circ(vals::Circ::DISABLED); + } + w.set_pl(vals::Pl::VERYHIGH); w.set_en(true); }); @@ -314,8 +329,9 @@ impl<'a, C: Channel> Transfer<'a, C> { pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); let en = unsafe { ch.cr().read() }.en(); + let circular = unsafe { ch.cr().read() }.circ() == vals::Circ::ENABLED; let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; - en && !tcif + en && (circular || !tcif) } /// Gets the total remaining transfers for the channel @@ -482,6 +498,8 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let ch = self.channel.regs().ch(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. + // If the channel is enabled and transfer is not completed, we need to perform + // two separate write access to the CR register to disable the channel. unsafe { ch.cr().write(|w| { w.set_teie(true); From e0747e937f06ed280594e17c97d19784410b6e85 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 11:15:09 +0200 Subject: [PATCH 04/28] remove unsafe for circular dma reg access --- embassy-stm32/src/dma/bdma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index be457144..3d315e20 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -325,7 +325,7 @@ impl<'a, C: Channel> Transfer<'a, C> { pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); let en = ch.cr().read().en(); - let circular = unsafe { ch.cr().read() }.circ() == vals::Circ::ENABLED; + let circular = ch.cr().read().circ() == vals::Circ::ENABLED; let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; en && (circular || !tcif) } From fe7b72948ab5d3682f23e305f3eb7186cc308b1b Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:42:25 +0200 Subject: [PATCH 05/28] add ValueArray type and respective write functions --- embassy-stm32/src/dac/mod.rs | 206 +++++++++++++++++++++-------------- 1 file changed, 124 insertions(+), 82 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 525d45d7..4384a7c3 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -98,6 +98,14 @@ pub enum Value { Bit12(u16, Alignment), } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ValueArray<'a> { + Bit8(&'a [u8]), + Bit12Left(&'a [u16]), + Bit12Right(&'a [u16]), +} + pub struct Dac<'d, T: Instance, Tx> { channels: u8, txdma: PeripheralRef<'d, Tx>, @@ -129,21 +137,19 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { T::enable(); T::reset(); - unsafe { - T::regs().mcr().modify(|reg| { - for ch in 0..channels { - reg.set_mode(ch as usize, 0); - reg.set_mode(ch as usize, 0); - } - }); + T::regs().mcr().modify(|reg| { + for ch in 0..channels { + reg.set_mode(ch as usize, 0); + reg.set_mode(ch as usize, 0); + } + }); - T::regs().cr().modify(|reg| { - for ch in 0..channels { - reg.set_en(ch as usize, true); - reg.set_ten(ch as usize, true); - } - }); - } + T::regs().cr().modify(|reg| { + for ch in 0..channels { + reg.set_en(ch as usize, true); + reg.set_ten(ch as usize, true); + } + }); Self { channels, @@ -161,13 +167,12 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { } } + /// Set the enable register of the given channel fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { self.check_channel_exists(ch)?; - unsafe { - T::regs().cr().modify(|reg| { - reg.set_en(ch.index(), on); - }) - } + T::regs().cr().modify(|reg| { + reg.set_en(ch.index(), on); + }); Ok(()) } @@ -179,112 +184,149 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { self.set_channel_enable(ch, false) } + /// Performs all register accesses necessary to select a new trigger for CH1 pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch1)?; unwrap!(self.disable_channel(Channel::Ch1)); - unsafe { - T::regs().cr().modify(|reg| { - reg.set_tsel1(trigger.tsel()); - }) - } + T::regs().cr().modify(|reg| { + reg.set_tsel1(trigger.tsel()); + }); Ok(()) } + /// Performs all register accesses necessary to select a new trigger for CH2 pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch2)?; unwrap!(self.disable_channel(Channel::Ch2)); - unsafe { - T::regs().cr().modify(|reg| { - reg.set_tsel2(trigger.tsel()); - }) - } + T::regs().cr().modify(|reg| { + reg.set_tsel2(trigger.tsel()); + }); Ok(()) } + /// Perform a software trigger on a given channel pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { self.check_channel_exists(ch)?; - unsafe { - T::regs().swtrigr().write(|reg| { - reg.set_swtrig(ch.index(), true); - }); - } + T::regs().swtrigr().write(|reg| { + reg.set_swtrig(ch.index(), true); + }); Ok(()) } + /// Perform a software trigger on all channels pub fn trigger_all(&mut self) { - unsafe { - T::regs().swtrigr().write(|reg| { - reg.set_swtrig(Channel::Ch1.index(), true); - reg.set_swtrig(Channel::Ch2.index(), true); - }) - } + T::regs().swtrigr().write(|reg| { + reg.set_swtrig(Channel::Ch1.index(), true); + reg.set_swtrig(Channel::Ch2.index(), true); + }); } pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { self.check_channel_exists(ch)?; match value { - Value::Bit8(v) => unsafe { - T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)); - }, - Value::Bit12(v, Alignment::Left) => unsafe { - T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)); - }, - Value::Bit12(v, Alignment::Right) => unsafe { - T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)); - }, + Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), } Ok(()) } + /// Write `data` to the DAC 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`. + /// + /// ## Current limitations + /// - Only CH1 Supported + /// /// TODO: Allow an array of Value instead of only u16, right-aligned - pub async fn write(&mut self, data: &[u16], circular: bool) -> Result<(), Error> + pub async fn write_8bit(&mut self, data_ch1: &[u8], circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + self.write_inner(ValueArray::Bit8(data_ch1), circular).await + } + + pub async fn write_12bit_right_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + self.write_inner(ValueArray::Bit12Right(data_ch1), circular).await + } + + pub async fn write_12bit_left_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + self.write_inner(ValueArray::Bit12Left(data_ch1), circular).await + } + + async fn write_inner(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { // TODO: Make this a parameter or get it from the struct or so... const CHANNEL: usize = 0; - //debug!("Starting DAC"); - unsafe { - T::regs().cr().modify(|w| { - w.set_en(CHANNEL, true); - w.set_dmaen(CHANNEL, true); - }); - } + // Enable DAC and DMA + T::regs().cr().modify(|w| { + w.set_en(CHANNEL, true); + w.set_dmaen(CHANNEL, true); + }); let tx_request = self.txdma.request(); - // Use the 12 bit right-aligned register for now. TODO: distinguish values - let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16; - - let tx_f = unsafe { - Transfer::new_write( - &mut self.txdma, - tx_request, - data, - tx_dst, - TransferOptions { - circular, - halt_transfer_ir: false, - }, - ) + // Initiate the correct type of DMA transfer depending on what data is passed + let tx_f = match data_ch1 { + ValueArray::Bit8(buf) => unsafe { + Transfer::new_write( + &mut self.txdma, + tx_request, + buf, + T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8, + TransferOptions { + circular, + halt_transfer_ir: false, + }, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + Transfer::new_write( + &mut self.txdma, + tx_request, + buf, + T::regs().dhr12l(CHANNEL).as_ptr() as *mut u16, + TransferOptions { + circular, + halt_transfer_ir: false, + }, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + Transfer::new_write( + &mut self.txdma, + tx_request, + buf, + T::regs().dhr12r(CHANNEL).as_ptr() as *mut u16, + TransferOptions { + circular, + halt_transfer_ir: false, + }, + ) + }, }; - //debug!("Awaiting tx_f"); - tx_f.await; // finish dma - unsafe { - // TODO: Do we need to check any status registers here? + // 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); + }); - 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(()) } } From 218b102b2840c9786944aa6f613450c876b6110b Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:46:17 +0200 Subject: [PATCH 06/28] remove Alignment and make Value and Value array look the same --- embassy-stm32/src/dac/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 4384a7c3..0fbf6767 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -84,25 +84,25 @@ impl Ch2Trigger { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Alignment { - Left, - Right, -} - #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Value { + // 8 bit value Bit8(u8), - Bit12(u16, Alignment), + // 12 bit value stored in a u16, left-aligned + Bit12Left(u16), + // 12 bit value stored in a u16, right-aligned + Bit12Right(u16), } #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ValueArray<'a> { + // 8 bit values Bit8(&'a [u8]), + // 12 bit value stored in a u16, left-aligned Bit12Left(&'a [u16]), + // 12 bit values stored in a u16, right-aligned Bit12Right(&'a [u16]), } @@ -225,8 +225,8 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { self.check_channel_exists(ch)?; match value { Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), - Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), - Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12Left(v) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12Right(v) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), } Ok(()) } From 88052480b14b8dd38e7c4ba179b7035390f59618 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:50:17 +0200 Subject: [PATCH 07/28] fix typo, minor cleanup --- embassy-stm32/src/dac/mod.rs | 13 +++++++------ embassy-stm32/src/dma/bdma.rs | 7 +++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 0fbf6767..704b77b3 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -275,42 +275,43 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }); let tx_request = self.txdma.request(); + let channel = &mut self.txdma; // Initiate the correct type of DMA transfer depending on what data is passed let tx_f = match data_ch1 { ValueArray::Bit8(buf) => unsafe { Transfer::new_write( - &mut self.txdma, + channel, tx_request, buf, T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8, TransferOptions { circular, - halt_transfer_ir: false, + half_transfer_ir: false, }, ) }, ValueArray::Bit12Left(buf) => unsafe { Transfer::new_write( - &mut self.txdma, + channel, tx_request, buf, T::regs().dhr12l(CHANNEL).as_ptr() as *mut u16, TransferOptions { circular, - halt_transfer_ir: false, + half_transfer_ir: false, }, ) }, ValueArray::Bit12Right(buf) => unsafe { Transfer::new_write( - &mut self.txdma, + channel, tx_request, buf, T::regs().dhr12r(CHANNEL).as_ptr() as *mut u16, TransferOptions { circular, - halt_transfer_ir: false, + half_transfer_ir: false, }, ) }, diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 3d315e20..0ad20579 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -1,7 +1,6 @@ #![macro_use] use core::future::Future; -use core::option; use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; @@ -24,14 +23,14 @@ use crate::pac::bdma::{regs, vals}; #[non_exhaustive] pub struct TransferOptions { pub circular: bool, - pub halt_transfer_ir: bool, + pub half_transfer_ir: bool, } impl Default for TransferOptions { fn default() -> Self { Self { circular: false, - halt_transfer_ir: false, + half_transfer_ir: false, } } } @@ -291,7 +290,7 @@ impl<'a, C: Channel> Transfer<'a, C> { w.set_dir(dir.into()); w.set_teie(true); w.set_tcie(true); - w.set_htie(options.halt_transfer_ir); + w.set_htie(options.half_transfer_ir); if options.circular { w.set_circ(vals::Circ::ENABLED); debug!("Setting circular mode"); From 56ab6d9f143ecc3041ac9726e621eedea729ca4d Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:54:22 +0200 Subject: [PATCH 08/28] remove write_X variants --- embassy-stm32/src/dac/mod.rs | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 704b77b3..f02adeed 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -86,6 +86,7 @@ impl Ch2Trigger { #[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 { // 8 bit value Bit8(u8), @@ -97,6 +98,7 @@ pub enum Value { #[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]), @@ -239,29 +241,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// ## Current limitations /// - Only CH1 Supported /// - /// TODO: Allow an array of Value instead of only u16, right-aligned - pub async fn write_8bit(&mut self, data_ch1: &[u8], circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - self.write_inner(ValueArray::Bit8(data_ch1), circular).await - } - - pub async fn write_12bit_right_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - self.write_inner(ValueArray::Bit12Right(data_ch1), circular).await - } - - pub async fn write_12bit_left_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - self.write_inner(ValueArray::Bit12Left(data_ch1), circular).await - } - - async fn write_inner(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> + pub async fn write(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { From 8d0095c61808b88ad95349c7f24e91797d93dc83 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Thu, 22 Jun 2023 10:43:45 +0200 Subject: [PATCH 09/28] add option to enable/disable complete transfer interrupt --- embassy-stm32/src/dma/bdma.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 162ca9ad..32b75bb6 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -22,8 +22,12 @@ use crate::pac::bdma::{regs, vals}; #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct TransferOptions { + /// 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 { @@ -31,6 +35,7 @@ impl Default for TransferOptions { Self { circular: false, half_transfer_ir: false, + complete_transfer_ir: false, } } } @@ -289,7 +294,7 @@ impl<'a, C: Channel> Transfer<'a, C> { } w.set_dir(dir.into()); w.set_teie(true); - w.set_tcie(true); + w.set_tcie(options.complete_transfer_ir); w.set_htie(options.half_transfer_ir); if options.circular { w.set_circ(vals::Circ::ENABLED); From 78736328a042e7c7f6565ed44ac301be22953f7a Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Thu, 22 Jun 2023 10:44:08 +0200 Subject: [PATCH 10/28] update docs and update to new dma interface --- embassy-stm32/src/dac/mod.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index f02adeed..42646d20 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -115,6 +115,7 @@ pub struct Dac<'d, T: Instance, Tx> { } impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { + /// Create a new instance with one channel pub fn new_1ch( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, @@ -124,6 +125,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Self::new_inner(peri, 1, txdma) } + /// Create a new instance with two channels pub fn new_2ch( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, @@ -134,6 +136,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Self::new_inner(peri, 2, txdma) } + /// Perform initialisation steps for the DAC fn new_inner(peri: PeripheralRef<'d, T>, channels: u8, txdma: impl Peripheral

+ 'd) -> Self { into_ref!(txdma); T::enable(); @@ -178,15 +181,17 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Ok(()) } + /// Enable the DAC channel `ch` pub fn enable_channel(&mut self, ch: Channel) -> Result<(), Error> { self.set_channel_enable(ch, true) } + /// Disable the DAC channel `ch` pub fn disable_channel(&mut self, ch: Channel) -> Result<(), Error> { self.set_channel_enable(ch, false) } - /// Performs all register accesses necessary to select a new trigger for CH1 + /// Select a new trigger for CH1 (disables the channel) pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch1)?; unwrap!(self.disable_channel(Channel::Ch1)); @@ -196,7 +201,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Ok(()) } - /// Performs all register accesses necessary to select a new trigger for CH2 + /// Select a new trigger for CH2 (disables the channel) pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch2)?; unwrap!(self.disable_channel(Channel::Ch2)); @@ -206,7 +211,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Ok(()) } - /// Perform a software trigger on a given channel + /// Perform a software trigger on `ch` pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { self.check_channel_exists(ch)?; T::regs().swtrigr().write(|reg| { @@ -223,6 +228,9 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }); } + /// Set a value to be output by the DAC on trigger. + /// + /// The `value` is written to the corresponding "data holding register" pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { self.check_channel_exists(ch)?; match value { @@ -237,6 +245,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// /// 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. /// /// ## Current limitations /// - Only CH1 Supported @@ -255,7 +264,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }); let tx_request = self.txdma.request(); - let channel = &mut self.txdma; + let channel = &self.txdma; // Initiate the correct type of DMA transfer depending on what data is passed let tx_f = match data_ch1 { @@ -268,6 +277,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { TransferOptions { circular, half_transfer_ir: false, + complete_transfer_ir: !circular, }, ) }, @@ -280,6 +290,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { TransferOptions { circular, half_transfer_ir: false, + complete_transfer_ir: !circular, }, ) }, @@ -292,6 +303,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { TransferOptions { circular, half_transfer_ir: false, + complete_transfer_ir: !circular, }, ) }, From ea04a0277bb19719188e904a86e28c34f9801c96 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:14:26 +0200 Subject: [PATCH 11/28] change dma complete transfer IR default to true --- embassy-stm32/src/dma/bdma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 32b75bb6..5a87888b 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -35,7 +35,7 @@ impl Default for TransferOptions { Self { circular: false, half_transfer_ir: false, - complete_transfer_ir: false, + complete_transfer_ir: true, } } } From 915f79c974ace037e914397b42eb9d2448bd5ca3 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:14:40 +0200 Subject: [PATCH 12/28] allow independent use of ch1 and ch2 on dac --- embassy-stm32/src/dac/mod.rs | 152 +++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 51 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 42646d20..5b39758b 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -22,7 +22,7 @@ pub enum Channel { } impl Channel { - fn index(&self) -> usize { + const fn index(&self) -> usize { match self { Channel::Ch1 => 0, Channel::Ch2 => 1, @@ -109,72 +109,100 @@ pub enum ValueArray<'a> { } pub struct Dac<'d, T: Instance, Tx> { - channels: u8, + ch1: bool, + ch2: bool, txdma: PeripheralRef<'d, Tx>, _peri: PeripheralRef<'d, T>, } impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { - /// Create a new instance with one channel - pub fn new_1ch( + pub fn new_ch1( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, _ch1: impl Peripheral

> + 'd, ) -> Self { into_ref!(peri); - Self::new_inner(peri, 1, txdma) + Self::new_inner(peri, true, false, txdma) } - /// Create a new instance with two channels - pub fn new_2ch( + pub fn new_ch2( + peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, + _ch2: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri); + Self::new_inner(peri, false, true, txdma) + } + + pub fn new_ch1_and_ch2( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, _ch1: impl Peripheral

> + 'd, _ch2: impl Peripheral

> + 'd, ) -> Self { into_ref!(peri); - Self::new_inner(peri, 2, txdma) + Self::new_inner(peri, true, true, txdma) } /// Perform initialisation steps for the DAC - fn new_inner(peri: PeripheralRef<'d, T>, channels: u8, txdma: impl Peripheral

+ 'd) -> Self { + fn new_inner(peri: PeripheralRef<'d, T>, ch1: bool, ch2: bool, txdma: impl Peripheral

+ 'd) -> Self { into_ref!(txdma); T::enable(); T::reset(); - T::regs().mcr().modify(|reg| { - for ch in 0..channels { - reg.set_mode(ch as usize, 0); - reg.set_mode(ch as usize, 0); - } - }); - - T::regs().cr().modify(|reg| { - for ch in 0..channels { - reg.set_en(ch as usize, true); - reg.set_ten(ch as usize, true); - } - }); - - Self { - channels, + let mut dac = Self { + ch1, + ch2, txdma, _peri: peri, + }; + + // 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) + if ch1 { + dac.set_channel_mode(Channel::Ch1, 0).unwrap(); + dac.enable_channel(Channel::Ch1).unwrap(); + dac.set_trigger_enable(Channel::Ch1, true).unwrap(); } + if ch2 { + dac.set_channel_mode(Channel::Ch2, 0).unwrap(); + dac.enable_channel(Channel::Ch2).unwrap(); + dac.set_trigger_enable(Channel::Ch2, true).unwrap(); + } + + dac } /// Check the channel is configured - fn check_channel_exists(&self, ch: Channel) -> Result<(), Error> { - if ch == Channel::Ch2 && self.channels < 2 { + fn check_channel_configured(&self, ch: Channel) -> Result<(), Error> { + if (ch == Channel::Ch1 && !self.ch1) || (ch == Channel::Ch2 && !self.ch2) { Err(Error::UnconfiguredChannel) } else { Ok(()) } } - /// Set the enable register of the given channel + /// Enable trigger of the given channel + fn set_trigger_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { + self.check_channel_configured(ch)?; + T::regs().cr().modify(|reg| { + reg.set_ten(ch.index(), on); + }); + Ok(()) + } + + /// Set mode register of the given channel + fn set_channel_mode(&mut self, ch: Channel, val: u8) -> Result<(), Error> { + self.check_channel_configured(ch)?; + T::regs().mcr().modify(|reg| { + reg.set_mode(ch.index(), val); + }); + Ok(()) + } + + /// Set enable register of the given channel fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { - self.check_channel_exists(ch)?; + self.check_channel_configured(ch)?; T::regs().cr().modify(|reg| { reg.set_en(ch.index(), on); }); @@ -193,7 +221,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// Select a new trigger for CH1 (disables the channel) pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { - self.check_channel_exists(Channel::Ch1)?; + self.check_channel_configured(Channel::Ch1)?; unwrap!(self.disable_channel(Channel::Ch1)); T::regs().cr().modify(|reg| { reg.set_tsel1(trigger.tsel()); @@ -203,7 +231,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// Select a new trigger for CH2 (disables the channel) pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { - self.check_channel_exists(Channel::Ch2)?; + self.check_channel_configured(Channel::Ch2)?; unwrap!(self.disable_channel(Channel::Ch2)); T::regs().cr().modify(|reg| { reg.set_tsel2(trigger.tsel()); @@ -213,7 +241,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// Perform a software trigger on `ch` pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { - self.check_channel_exists(ch)?; + self.check_channel_configured(ch)?; T::regs().swtrigr().write(|reg| { reg.set_swtrig(ch.index(), true); }); @@ -232,7 +260,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// /// The `value` is written to the corresponding "data holding register" pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { - self.check_channel_exists(ch)?; + self.check_channel_configured(ch)?; match value { Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), Value::Bit12Left(v) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), @@ -241,39 +269,61 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Ok(()) } - /// Write `data` to the DAC via DMA. + /// 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. /// - /// ## Current limitations - /// - Only CH1 Supported - /// - pub async fn write(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> + /// **Important:** Channel 1 has to be configured for the DAC instance! + pub async fn write_ch1(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { - // TODO: Make this a parameter or get it from the struct or so... - const CHANNEL: usize = 0; + self.check_channel_configured(Channel::Ch1)?; + self.write_inner(data, circular, Channel::Ch1).await + } + + /// 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_ch2(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + self.check_channel_configured(Channel::Ch2)?; + self.write_inner(data, circular, Channel::Ch2).await + } + + /// Performs the dma write for the given channel. + /// TODO: Should self be &mut? + async fn write_inner(&self, data_ch1: ValueArray<'_>, circular: bool, channel: Channel) -> Result<(), Error> + where + Tx: Dma, + { + let channel = channel.index(); // Enable DAC and DMA T::regs().cr().modify(|w| { - w.set_en(CHANNEL, true); - w.set_dmaen(CHANNEL, true); + w.set_en(channel, true); + w.set_dmaen(channel, true); }); let tx_request = self.txdma.request(); - let channel = &self.txdma; + let dma_channel = &self.txdma; // Initiate the correct type of DMA transfer depending on what data is passed let tx_f = match data_ch1 { ValueArray::Bit8(buf) => unsafe { Transfer::new_write( - channel, + dma_channel, tx_request, buf, - T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8, + T::regs().dhr8r(channel).as_ptr() as *mut u8, TransferOptions { circular, half_transfer_ir: false, @@ -283,10 +333,10 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }, ValueArray::Bit12Left(buf) => unsafe { Transfer::new_write( - channel, + dma_channel, tx_request, buf, - T::regs().dhr12l(CHANNEL).as_ptr() as *mut u16, + T::regs().dhr12l(channel).as_ptr() as *mut u16, TransferOptions { circular, half_transfer_ir: false, @@ -296,10 +346,10 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }, ValueArray::Bit12Right(buf) => unsafe { Transfer::new_write( - channel, + dma_channel, tx_request, buf, - T::regs().dhr12r(CHANNEL).as_ptr() as *mut u16, + T::regs().dhr12r(channel).as_ptr() as *mut u16, TransferOptions { circular, half_transfer_ir: false, @@ -315,9 +365,9 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { // TODO: Do we need to check any status registers here? T::regs().cr().modify(|w| { // Disable the DAC peripheral - w.set_en(CHANNEL, false); + w.set_en(channel, false); // Disable the DMA. TODO: Is this necessary? - w.set_dmaen(CHANNEL, false); + w.set_dmaen(channel, false); }); Ok(()) From 388d3e273d3d003e6a058b4bad9e2517dd33d626 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 24 Jun 2023 13:10:59 +0200 Subject: [PATCH 13/28] first attempt at fixing the 2nd channel problem --- embassy-stm32/src/dac/mod.rs | 386 +++++++++++++++++++++-------------- 1 file changed, 234 insertions(+), 152 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 5b39758b..e87292b8 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -1,5 +1,7 @@ #![macro_use] +use core::marker::PhantomData; + use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::{Transfer, TransferOptions}; @@ -108,166 +110,108 @@ pub enum ValueArray<'a> { Bit12Right(&'a [u16]), } -pub struct Dac<'d, T: Instance, Tx> { - ch1: bool, - ch2: bool, - txdma: PeripheralRef<'d, Tx>, - _peri: PeripheralRef<'d, T>, -} - -impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { - pub fn new_ch1( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - _ch1: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(peri); - Self::new_inner(peri, true, false, txdma) - } - - pub fn new_ch2( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - _ch2: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(peri); - Self::new_inner(peri, false, true, txdma) - } - - pub fn new_ch1_and_ch2( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - _ch1: impl Peripheral

> + 'd, - _ch2: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(peri); - Self::new_inner(peri, true, true, txdma) - } - - /// Perform initialisation steps for the DAC - fn new_inner(peri: PeripheralRef<'d, T>, ch1: bool, ch2: bool, txdma: impl Peripheral

+ 'd) -> Self { - into_ref!(txdma); - T::enable(); - T::reset(); - - let mut dac = Self { - ch1, - ch2, - txdma, - _peri: peri, - }; - - // 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) - if ch1 { - dac.set_channel_mode(Channel::Ch1, 0).unwrap(); - dac.enable_channel(Channel::Ch1).unwrap(); - dac.set_trigger_enable(Channel::Ch1, true).unwrap(); - } - if ch2 { - dac.set_channel_mode(Channel::Ch2, 0).unwrap(); - dac.enable_channel(Channel::Ch2).unwrap(); - dac.set_trigger_enable(Channel::Ch2, true).unwrap(); - } - - dac - } - - /// Check the channel is configured - fn check_channel_configured(&self, ch: Channel) -> Result<(), Error> { - if (ch == Channel::Ch1 && !self.ch1) || (ch == Channel::Ch2 && !self.ch2) { - Err(Error::UnconfiguredChannel) - } else { - Ok(()) - } - } +pub trait DacChannel { + const CHANNEL: Channel; /// Enable trigger of the given channel - fn set_trigger_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> { T::regs().cr().modify(|reg| { - reg.set_ten(ch.index(), on); + reg.set_ten(Self::CHANNEL.index(), on); }); Ok(()) } /// Set mode register of the given channel - fn set_channel_mode(&mut self, ch: Channel, val: u8) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { T::regs().mcr().modify(|reg| { - reg.set_mode(ch.index(), val); + reg.set_mode(Self::CHANNEL.index(), val); }); Ok(()) } /// Set enable register of the given channel - fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> { T::regs().cr().modify(|reg| { - reg.set_en(ch.index(), on); + reg.set_en(Self::CHANNEL.index(), on); }); Ok(()) } /// Enable the DAC channel `ch` - pub fn enable_channel(&mut self, ch: Channel) -> Result<(), Error> { - self.set_channel_enable(ch, true) + fn enable_channel(&mut self) -> Result<(), Error> { + self.set_channel_enable(true) } /// Disable the DAC channel `ch` - pub fn disable_channel(&mut self, ch: Channel) -> Result<(), Error> { - self.set_channel_enable(ch, false) - } - - /// Select a new trigger for CH1 (disables the channel) - pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { - self.check_channel_configured(Channel::Ch1)?; - unwrap!(self.disable_channel(Channel::Ch1)); - T::regs().cr().modify(|reg| { - reg.set_tsel1(trigger.tsel()); - }); - Ok(()) - } - - /// Select a new trigger for CH2 (disables the channel) - pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { - self.check_channel_configured(Channel::Ch2)?; - unwrap!(self.disable_channel(Channel::Ch2)); - T::regs().cr().modify(|reg| { - reg.set_tsel2(trigger.tsel()); - }); - Ok(()) + fn disable_channel(&mut self) -> Result<(), Error> { + self.set_channel_enable(false) } /// Perform a software trigger on `ch` - pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn trigger(&mut self) -> Result<(), Error> { T::regs().swtrigr().write(|reg| { - reg.set_swtrig(ch.index(), true); + reg.set_swtrig(Self::CHANNEL.index(), true); }); Ok(()) } - /// Perform a software trigger on all channels - pub fn trigger_all(&mut self) { - T::regs().swtrigr().write(|reg| { - reg.set_swtrig(Channel::Ch1.index(), true); - reg.set_swtrig(Channel::Ch2.index(), true); - }); - } - /// Set a value to be output by the DAC on trigger. /// /// The `value` is written to the corresponding "data holding register" - pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn set(&mut self, value: Value) -> Result<(), Error> { match value { - Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), - Value::Bit12Left(v) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), - Value::Bit12Right(v) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), + 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(()) } +} + +pub struct Dac<'d, T: Instance, Tx> { + ch1: DacCh1<'d, T, Tx>, + ch2: DacCh2<'d, T, Tx>, +} + +pub struct DacCh1<'d, T: Instance, Tx> { + _peri: PeripheralRef<'d, T>, + dma: PeripheralRef<'d, Tx>, +} + +pub struct DacCh2<'d, T: Instance, Tx> { + phantom: PhantomData<&'d mut T>, + dma: PeripheralRef<'d, Tx>, +} + +impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { + /// Perform initialisation steps for the DAC + pub fn new( + peri: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, + _pin: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, dma); + T::enable(); + T::reset(); + + let mut dac = Self { _peri: peri, 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) + dac.set_channel_mode(0).unwrap(); + dac.enable_channel().unwrap(); + dac.set_trigger_enable(true).unwrap(); + + dac + } + /// Select a new trigger for CH1 (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. /// @@ -276,36 +220,12 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// 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_ch1(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { - self.check_channel_configured(Channel::Ch1)?; - self.write_inner(data, circular, Channel::Ch1).await - } - - /// 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_ch2(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - self.check_channel_configured(Channel::Ch2)?; - self.write_inner(data, circular, Channel::Ch2).await - } - - /// Performs the dma write for the given channel. - /// TODO: Should self be &mut? - async fn write_inner(&self, data_ch1: ValueArray<'_>, circular: bool, channel: Channel) -> Result<(), Error> - where - Tx: Dma, - { - let channel = channel.index(); + let channel = Channel::Ch1.index(); + debug!("Writing to channel {}", channel); // Enable DAC and DMA T::regs().cr().modify(|w| { @@ -313,11 +233,11 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { w.set_dmaen(channel, true); }); - let tx_request = self.txdma.request(); - let dma_channel = &self.txdma; + let tx_request = self.dma.request(); + let dma_channel = &self.dma; // Initiate the correct type of DMA transfer depending on what data is passed - let tx_f = match data_ch1 { + let tx_f = match data { ValueArray::Bit8(buf) => unsafe { Transfer::new_write( dma_channel, @@ -374,6 +294,168 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { } } +impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { + /// Perform initialisation steps for the DAC + pub fn new_ch2( + peri: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, + _pin: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, dma); + T::enable(); + T::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) + dac.set_channel_mode(0).unwrap(); + dac.enable_channel().unwrap(); + dac.set_trigger_enable(true).unwrap(); + + dac + } + + /// Select a new trigger for CH1 (disables the 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 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, + { + 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; + + // 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(()) + } +} + +impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { + pub fn new( + peri: impl Peripheral

+ 'd, + dma_ch1: impl Peripheral

+ 'd, + dma_ch2: impl Peripheral

+ 'd, + _pin_ch1: impl Peripheral

> + 'd, + _pin_ch2: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, dma_ch1, dma_ch2); + T::enable(); + T::reset(); + + let mut dac_ch1 = DacCh1 { + _peri: peri, + dma: dma_ch1, + }; + + let mut dac_ch2 = DacCh2 { + phantom: PhantomData, + 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) + dac_ch1.set_channel_mode(0).unwrap(); + dac_ch1.enable_channel().unwrap(); + dac_ch1.set_trigger_enable(true).unwrap(); + + dac_ch1.set_channel_mode(0).unwrap(); + dac_ch1.enable_channel().unwrap(); + dac_ch1.set_trigger_enable(true).unwrap(); + + Self { + ch1: dac_ch1, + ch2: dac_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 { pub trait Instance { fn regs() -> &'static crate::pac::dac::Dac; From df944edeef738590f481d35ee9e2a1afb09601fa Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 25 Jun 2023 10:53:35 +0200 Subject: [PATCH 14/28] fix minor issues with splitting channels etc --- embassy-stm32/src/dac/mod.rs | 46 ++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index e87292b8..3dcd6b77 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -168,9 +168,9 @@ pub trait DacChannel { } } -pub struct Dac<'d, T: Instance, Tx> { - ch1: DacCh1<'d, T, Tx>, - ch2: DacCh2<'d, T, Tx>, +pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { + ch1: DacCh1<'d, T, TxCh1>, + ch2: DacCh2<'d, T, TxCh2>, } pub struct DacCh1<'d, T: Instance, Tx> { @@ -220,7 +220,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { /// 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> + pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { @@ -297,11 +297,11 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { /// Perform initialisation steps for the DAC pub fn new_ch2( - peri: impl Peripheral

+ 'd, + _peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, _pin: impl Peripheral

> + 'd, ) -> Self { - into_ref!(peri, dma); + into_ref!(_peri, dma); T::enable(); T::reset(); @@ -335,7 +335,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { /// 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> + pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { @@ -409,11 +409,11 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { } } -impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { +impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { pub fn new( peri: impl Peripheral

+ 'd, - dma_ch1: impl Peripheral

+ 'd, - dma_ch2: impl Peripheral

+ 'd, + dma_ch1: impl Peripheral

+ 'd, + dma_ch2: impl Peripheral

+ 'd, _pin_ch1: impl Peripheral

> + 'd, _pin_ch2: impl Peripheral

> + 'd, ) -> Self { @@ -437,15 +437,35 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { dac_ch1.enable_channel().unwrap(); dac_ch1.set_trigger_enable(true).unwrap(); - dac_ch1.set_channel_mode(0).unwrap(); - dac_ch1.enable_channel().unwrap(); - dac_ch1.set_trigger_enable(true).unwrap(); + dac_ch2.set_channel_mode(0).unwrap(); + dac_ch2.enable_channel().unwrap(); + dac_ch2.set_trigger_enable(true).unwrap(); Self { ch1: dac_ch1, ch2: dac_ch2, } } + + pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { + (self.ch1, self.ch2) + } + + pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { + &mut self.ch1 + } + + pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { + &mut self.ch2 + } + + pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { + &self.ch1 + } + + pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { + &self.ch2 + } } impl<'d, T: Instance, Tx> DacChannel for DacCh1<'d, T, Tx> { From 8cafaa1f3c9b75e8dba30a7f37f60d9fee6e65e2 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 25 Jun 2023 11:54:25 +0200 Subject: [PATCH 15/28] add docs, cleanup --- embassy-stm32/src/dac/mod.rs | 322 +++++++++++++++++------------------ 1 file changed, 155 insertions(+), 167 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 3dcd6b77..d95674ff 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -1,5 +1,6 @@ #![macro_use] +//! Provide access to the STM32 digital-to-analog converter (DAC). use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -11,6 +12,7 @@ use crate::{peripherals, Peripheral}; #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Curstom Errors pub enum Error { UnconfiguredChannel, InvalidValue, @@ -18,6 +20,7 @@ pub enum Error { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// DAC Channels pub enum Channel { Ch1, Ch2, @@ -34,6 +37,7 @@ impl Channel { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Trigger sources for CH1 pub enum Ch1Trigger { Tim6, Tim3, @@ -60,6 +64,7 @@ impl Ch1Trigger { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Trigger sources for CH2 pub enum Ch2Trigger { Tim6, Tim8, @@ -109,7 +114,7 @@ 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; @@ -157,7 +162,7 @@ pub trait DacChannel { /// Set a value to be output by the DAC on trigger. /// - /// The `value` is written to the corresponding "data holding register" + /// 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)), @@ -166,25 +171,51 @@ 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 +/// +/// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously. +/// +/// # Example for obtaining both DAC channels +/// +/// ```no_run +/// // 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>, 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>, dma: PeripheralRef<'d, Tx>, } impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { - /// Perform initialisation steps for the DAC + /// Obtain DAC CH1 pub fn new( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, @@ -204,7 +235,8 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { dac } - /// Select a new trigger for CH1 (disables the channel) + + /// Select a new trigger for this channel pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { unwrap!(self.disable_channel()); T::regs().cr().modify(|reg| { @@ -212,91 +244,11 @@ 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: Dma, - { - 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; - - // 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(()) - } } impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { - /// Perform initialisation steps for the DAC - pub fn new_ch2( + /// Obtain DAC CH2 + pub fn new( _peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, _pin: impl Peripheral

> + 'd, @@ -319,7 +271,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { dac } - /// Select a new trigger for CH1 (disables the channel) + /// 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| { @@ -327,89 +279,12 @@ impl<'d, T: Instance, Tx> DacCh2<'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: Dma, - { - 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; - - // 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(()) - } } impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { + /// Create a new DAC instance with both channels. + /// + /// 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, @@ -447,22 +322,27 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { } } + /// Split the DAC into CH1 and CH2 for independent use. pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { (self.ch1, self.ch2) } + /// Get mutable reference to CH1 pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { &mut self.ch1 } + /// Get mutable reference to CH2 pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { &mut self.ch2 } + /// Get reference to CH1 pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { &self.ch1 } + /// Get reference to CH2 pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { &self.ch2 } @@ -470,10 +350,117 @@ 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 { @@ -485,6 +472,7 @@ pub(crate) mod sealed { pub trait Instance: sealed::Instance + RccPeripheral + 'static {} dma_trait!(Dma, Instance); +/// Marks a pin that can be used with the DAC pub trait DacPin: crate::gpio::Pin + 'static {} foreach_peripheral!( From e7bc84dda8cc28835d1b7d3574a94b6142e29864 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 26 Jun 2023 09:42:25 +0200 Subject: [PATCH 16/28] fix issues when DAC2 present, add additional options to DMA (NOT YET WORKING with STM32H7A3ZI) --- embassy-stm32/build.rs | 4 +- embassy-stm32/src/dac/mod.rs | 267 ++++++++++++++++++++--------------- embassy-stm32/src/dma/dma.rs | 26 +++- 3 files changed, 178 insertions(+), 119 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f7a25743..7fa4fae4 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 d95674ff..6ead00e1 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 8abe541d..a5f82894 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 From afec1b439bb40b769c8ccd1c1b19d58edd034c3d Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Tue, 27 Jun 2023 18:17:51 +0200 Subject: [PATCH 17/28] feature-gate dma write, make trigger not return a result --- embassy-stm32/src/dac/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 6ead00e1..3e48d558 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -153,11 +153,10 @@ pub trait DacChannel { } /// Perform a software trigger on `ch` - fn trigger(&mut self) -> Result<(), Error> { + fn trigger(&mut self) { T::regs().swtrigr().write(|reg| { reg.set_swtrig(Self::CHANNEL.index(), true); }); - Ok(()) } /// Set a value to be output by the DAC on trigger. @@ -230,6 +229,8 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { } /// Select a new trigger for this channel + /// + /// **Important**: This disables the channel! pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { unwrap!(self.disable_channel()); T::regs().cr().modify(|reg| { @@ -245,6 +246,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { /// 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! + #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: DmaCh1, @@ -355,6 +357,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { /// 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! + #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: DmaCh2, From 56dd22f0ac49be2b824e88026d38b69843b56972 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:23:47 +0200 Subject: [PATCH 18/28] feature-gate set_channel_mode, undo dma.rs changes --- embassy-stm32/src/dac/mod.rs | 17 ++++++++++------- embassy-stm32/src/dma/dma.rs | 18 +----------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 3e48d558..b5308352 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -127,6 +127,7 @@ pub trait DacChannel { } /// Set mode register of the given channel + #[cfg(dac_v2)] fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { T::regs().mcr().modify(|reg| { reg.set_mode(Self::CHANNEL.index(), val); @@ -221,6 +222,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { // 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(dac_v2)] dac.set_channel_mode(0).unwrap(); dac.enable_channel().unwrap(); dac.set_trigger_enable(true).unwrap(); @@ -334,6 +336,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { // 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(dac_v2)] dac.set_channel_mode(0).unwrap(); dac.enable_channel().unwrap(); dac.set_trigger_enable(true).unwrap(); @@ -454,10 +457,12 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { // 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(dac_v2)] dac_ch1.set_channel_mode(0).unwrap(); dac_ch1.enable_channel().unwrap(); dac_ch1.set_trigger_enable(true).unwrap(); + #[cfg(dac_v2)] dac_ch2.set_channel_mode(0).unwrap(); dac_ch2.enable_channel().unwrap(); dac_ch2.set_trigger_enable(true).unwrap(); @@ -521,27 +526,25 @@ foreach_peripheral!( #[cfg(rcc_h7)] impl crate::rcc::sealed::RccPeripheral for peripherals::$inst { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| unsafe { - crate::rcc::get_freqs().apb1 - }) + critical_section::with(|_| crate::rcc::get_freqs().apb1) } fn reset() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true)); crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false)); }) } fn enable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); }) } fn disable() { - critical_section::with(|_| unsafe { - crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)); + critical_section::with(|_| { + crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)) }) } } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index a5f82894..0b7b6078 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -29,12 +29,6 @@ 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 { @@ -44,9 +38,6 @@ impl Default for TransferOptions { mburst: Burst::Single, flow_ctrl: FlowControl::Dma, fifo_threshold: None, - circular: false, - half_transfer_ir: false, - complete_transfer_ir: true, } } } @@ -375,20 +366,13 @@ impl<'a, C: Channel> Transfer<'a, C> { }); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_tcie(options.complete_transfer_ir); - w.set_htie(options.half_transfer_ir); + w.set_tcie(true); #[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()); From 60c54107ce5cc50f9d9365297d31973d96f00021 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:58:56 +0200 Subject: [PATCH 19/28] fix sdmmc bdma transferconfig fields --- embassy-stm32/src/sdmmc/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 80a336a4..698292bf 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -227,7 +227,11 @@ const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOp fifo_threshold: Some(crate::dma::FifoThreshold::Full), }; #[cfg(all(sdmmc_v1, not(dma)))] -const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {}; +const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { + circular: false, + half_transfer_ir: false, + complete_transfer_ir: true, +}; /// SDMMC configuration /// From 9c81d6315500b236adc7634d2d2d6ef776f984eb Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Tue, 27 Jun 2023 22:33:17 +0200 Subject: [PATCH 20/28] fix warnings --- embassy-stm32/src/dac/mod.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index b5308352..6686a387 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -5,7 +5,6 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -use crate::dma::{Transfer, TransferOptions}; use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -195,6 +194,7 @@ pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { 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>, } @@ -204,6 +204,7 @@ pub struct DacCh1<'d, T: Instance, Tx> { pub struct DacCh2<'d, T: Instance, Tx> { /// Instead of PeripheralRef to consume T phantom: PhantomData<&'d mut T>, + #[allow(unused)] // For chips whose DMA is not (yet) supported dma: PeripheralRef<'d, Tx>, } @@ -265,7 +266,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { let tx_request = self.dma.request(); let dma_channel = &self.dma; - let tx_options = TransferOptions { + let tx_options = crate::dma::TransferOptions { circular, half_transfer_ir: false, complete_transfer_ir: !circular, @@ -275,7 +276,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { // 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( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -284,7 +285,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { ) }, ValueArray::Bit12Left(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -293,7 +294,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { ) }, ValueArray::Bit12Right(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -377,7 +378,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { let tx_request = self.dma.request(); let dma_channel = &self.dma; - let tx_options = TransferOptions { + let tx_options = crate::dma::TransferOptions { circular, half_transfer_ir: false, complete_transfer_ir: !circular, @@ -387,7 +388,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { // 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( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -396,7 +397,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { ) }, ValueArray::Bit12Left(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -405,7 +406,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { ) }, ValueArray::Bit12Right(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -526,7 +527,7 @@ foreach_peripheral!( #[cfg(rcc_h7)] impl crate::rcc::sealed::RccPeripheral for peripherals::$inst { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| crate::rcc::get_freqs().apb1) + critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 }) } fn reset() { From 91c31d5e437b510af3c535f5e597881042563496 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:58:25 +0200 Subject: [PATCH 21/28] Update DAC examples, add DAC + DMA example --- embassy-stm32/src/dac/mod.rs | 2 +- examples/stm32f4/src/bin/dac.rs | 9 +- examples/stm32h7/src/bin/dac.rs | 9 +- examples/stm32l4/Cargo.toml | 2 + examples/stm32l4/src/bin/dac.rs | 16 ++- examples/stm32l4/src/bin/dac_dma.rs | 148 ++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 examples/stm32l4/src/bin/dac_dma.rs diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 6686a387..1dc13949 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -178,7 +178,7 @@ pub trait DacChannel { /// /// # Example for obtaining both DAC channels /// -/// ```no_run +/// ```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(); diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs index d97ae708..3a621671 100644 --- a/examples/stm32f4/src/bin/dac.rs +++ b/examples/stm32f4/src/bin/dac.rs @@ -4,7 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dac::{Channel, Dac, Value}; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,12 +13,12 @@ async fn main(_spawner: Spawner) -> ! { let p = embassy_stm32::init(Default::default()); info!("Hello World, dude!"); - let mut dac = Dac::new_1ch(p.DAC, p.PA4); + let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4); loop { for v in 0..=255 { - unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); - unwrap!(dac.trigger(Channel::Ch1)); + unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.trigger(); } } } diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index f1271637..586b4154 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs @@ -4,7 +4,8 @@ use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::dac::{Channel, Dac, Value}; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; use embassy_stm32::time::mhz; use embassy_stm32::Config; use {defmt_rtt as _, panic_probe as _}; @@ -19,12 +20,12 @@ fn main() -> ! { config.rcc.pll1.q_ck = Some(mhz(100)); let p = embassy_stm32::init(config); - let mut dac = Dac::new_1ch(p.DAC1, p.PA4); + let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); loop { for v in 0..=255 { - unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); - unwrap!(dac.trigger(Channel::Ch1)); + unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.trigger(); } } } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 3bb473ef..d2d22828 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -25,3 +25,5 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" + +static_cell = "1.0.0" diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index a36ed5d9..8aad2764 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs @@ -3,26 +3,22 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy_stm32::dac::{Channel, Dac, Value}; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; use embassy_stm32::pac; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] fn main() -> ! { + let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - pac::RCC.apb1enr1().modify(|w| { - w.set_dac1en(true); - }); - - let p = embassy_stm32::init(Default::default()); - - let mut dac = Dac::new_1ch(p.DAC1, p.PA4); + let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); loop { for v in 0..=255 { - unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); - unwrap!(dac.trigger(Channel::Ch1)); + unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.trigger(); } } } diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs new file mode 100644 index 00000000..81e6a58e --- /dev/null +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -0,0 +1,148 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dac::{DacChannel, ValueArray}; +use embassy_stm32::pac::timer::vals::{Mms, Opm}; +use embassy_stm32::peripherals::{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 static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +pub type Dac1Type<'d> = + embassy_stm32::dac::DacCh1<'d, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; + +pub type Dac2Type<'d> = + embassy_stm32::dac::DacCh2<'d, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let config = embassy_stm32::Config::default(); + + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config); + + // Obtain two independent channels (p.DAC1 can only be consumed once, though!) + let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); + + let dac1 = { + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init(dac_ch1) + }; + + let dac2 = { + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init(dac_ch2) + }; + + spawner.spawn(dac_task1(dac1)).ok(); + spawner.spawn(dac_task2(dac2)).ok(); +} + +#[embassy_executor::task] +async fn dac_task1(dac: &'static mut Dac1Type<'static>) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM6 frequency is {}", TIM6::frequency()); + const FREQUENCY: Hertz = Hertz::hz(200); + let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; + + // Depends on your clock and on the specific chip used, you may need higher or lower values here + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); + dac.enable_channel().unwrap(); + + TIM6::enable(); + TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM6::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + debug!( + "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM6::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + // 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); + } + } +} + +#[embassy_executor::task] +async fn dac_task2(dac: &'static mut Dac2Type<'static>) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM7 frequency is {}", TIM7::frequency()); + + const FREQUENCY: Hertz = Hertz::hz(600); + let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; + + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + TIM7::enable(); + TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM7::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); + + debug!( + "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM7::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } +} + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = 3.14 * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = 3.14 + 3.14 * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} + +fn calculate_array() -> [u8; N] { + let mut res = [0; N]; + let mut i = 0; + while i < N { + res[i] = to_sine_wave(i as u8); + i += 1; + } + res +} From 27a89019adaebfd1916d3b71dde1db3a6a768883 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:21:24 +0200 Subject: [PATCH 22/28] add doc --- examples/stm32l4/src/bin/dac_dma.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index 81e6a58e..aefc8412 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -52,6 +52,8 @@ async fn dac_task1(dac: &'static mut Dac1Type<'static>) { info!("TIM6 frequency is {}", TIM6::frequency()); const FREQUENCY: Hertz = Hertz::hz(200); + + // Compute the reload value such that we obtain the FREQUENCY for the sine let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; // Depends on your clock and on the specific chip used, you may need higher or lower values here From f2e7a23148f0c1f663744bfe47fbbb37d9552080 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:25:57 +0200 Subject: [PATCH 23/28] attempt at fixing ci --- examples/stm32l4/.cargo/config.toml | 3 ++- examples/stm32l4/memory.x | 8 ++++---- examples/stm32l4/src/bin/dac_dma.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index abf55eb2..4ccdf121 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,7 +2,8 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` #runner = "probe-rs-cli run --chip STM32L475VGT6" #runner = "probe-rs-cli run --chip STM32L475VG" -runner = "probe-rs-cli run --chip STM32L4S5VI" +#runner = "probe-rs-cli run --chip STM32L4S5VI" +runner = "probe-run --chip STM32L432KCUx" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l4/memory.x b/examples/stm32l4/memory.x index eb87d1b5..0cef526a 100644 --- a/examples/stm32l4/memory.x +++ b/examples/stm32l4/memory.x @@ -1,7 +1,7 @@ MEMORY { - /* NOTE 1 K = 1 KiBi = 1024 bytes */ - /* These values correspond to the STM32L4S5 */ - FLASH : ORIGIN = 0x08000000, LENGTH = 1024K - RAM : ORIGIN = 0x20000000, LENGTH = 128K + /* NOTE K = KiBi = 1024 bytes */ + /* TODO Adjust these memory regions to match your device memory layout */ + FLASH : ORIGIN = 0x8000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 64K } diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index aefc8412..7c0df835 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -93,7 +93,7 @@ async fn dac_task1(dac: &'static mut Dac1Type<'static>) { #[embassy_executor::task] async fn dac_task2(dac: &'static mut Dac2Type<'static>) { let data: &[u8; 256] = &calculate_array::<256>(); - + info!("TIM7 frequency is {}", TIM7::frequency()); const FREQUENCY: Hertz = Hertz::hz(600); From 02f367f733591c9423731a86ea7726772a88dac0 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:28:10 +0200 Subject: [PATCH 24/28] attempt at fixing ci --- examples/stm32l4/src/bin/dac.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index 8aad2764..ade43eb3 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_stm32::dac::{DacCh1, DacChannel, Value}; use embassy_stm32::dma::NoDma; -use embassy_stm32::pac; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] From bf7e24e9d7abc5d31b75ef97418577920bd4600c Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:37:29 +0200 Subject: [PATCH 25/28] revert to STM32L4S5VI --- examples/stm32l4/.cargo/config.toml | 3 +-- examples/stm32l4/memory.x | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index 4ccdf121..abf55eb2 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,8 +2,7 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` #runner = "probe-rs-cli run --chip STM32L475VGT6" #runner = "probe-rs-cli run --chip STM32L475VG" -#runner = "probe-rs-cli run --chip STM32L4S5VI" -runner = "probe-run --chip STM32L432KCUx" +runner = "probe-rs-cli run --chip STM32L4S5VI" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l4/memory.x b/examples/stm32l4/memory.x index 0cef526a..eb87d1b5 100644 --- a/examples/stm32l4/memory.x +++ b/examples/stm32l4/memory.x @@ -1,7 +1,7 @@ MEMORY { - /* NOTE K = KiBi = 1024 bytes */ - /* TODO Adjust these memory regions to match your device memory layout */ - FLASH : ORIGIN = 0x8000000, LENGTH = 256K - RAM : ORIGIN = 0x20000000, LENGTH = 64K + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the STM32L4S5 */ + FLASH : ORIGIN = 0x08000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 128K } From daedfbbd8756e921cc6343ad531401d309966eaa Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:39:36 +0200 Subject: [PATCH 26/28] add dma is_running change doc --- embassy-stm32/src/dma/dma.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 0b7b6078..9c03599e 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -404,11 +404,9 @@ 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()); let en = ch.cr().read().en(); + // Check if circular mode is enabled, if so it will still be running even if tcif == 1 let circular = ch.cr().read().circ() == vals::Circ::ENABLED; let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; en && (circular || !tcif) From d5898c11ebef63fa0ec6dba8381484f4cfabd65c Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 16:40:50 +0200 Subject: [PATCH 27/28] remove need for StaticCell in dac_dma example for stm32l4 --- examples/stm32l4/Cargo.toml | 2 -- examples/stm32l4/src/bin/dac_dma.rs | 29 ++++++++--------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index d2d22828..3bb473ef 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -25,5 +25,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" - -static_cell = "1.0.0" diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index 7c0df835..c27cc03e 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -11,14 +11,13 @@ use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Basic16bitInstance; use micromath::F32Ext; -use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -pub type Dac1Type<'d> = - embassy_stm32::dac::DacCh1<'d, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; +pub type Dac1Type = + embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; -pub type Dac2Type<'d> = - embassy_stm32::dac::DacCh2<'d, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; +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) { @@ -30,24 +29,12 @@ async fn main(spawner: Spawner) { // Obtain two independent channels (p.DAC1 can only be consumed once, though!) let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); - let dac1 = { - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init(dac_ch1) - }; - - let dac2 = { - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init(dac_ch2) - }; - - spawner.spawn(dac_task1(dac1)).ok(); - spawner.spawn(dac_task2(dac2)).ok(); + spawner.spawn(dac_task1(dac_ch1)).ok(); + spawner.spawn(dac_task2(dac_ch2)).ok(); } #[embassy_executor::task] -async fn dac_task1(dac: &'static mut Dac1Type<'static>) { +async fn dac_task1(mut dac: Dac1Type) { let data: &[u8; 256] = &calculate_array::<256>(); info!("TIM6 frequency is {}", TIM6::frequency()); @@ -91,7 +78,7 @@ async fn dac_task1(dac: &'static mut Dac1Type<'static>) { } #[embassy_executor::task] -async fn dac_task2(dac: &'static mut Dac2Type<'static>) { +async fn dac_task2(mut dac: Dac2Type) { let data: &[u8; 256] = &calculate_array::<256>(); info!("TIM7 frequency is {}", TIM7::frequency()); From 96f1525ffe675b7e3ca26f038bc558488c03af9b Mon Sep 17 00:00:00 2001 From: Julian <20155974+JuliDi@users.noreply.github.com> Date: Thu, 29 Jun 2023 09:20:25 +0200 Subject: [PATCH 28/28] Revert changes to dma.rs --- embassy-stm32/src/dma/dma.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9c03599e..8abe541d 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -405,11 +405,7 @@ impl<'a, C: Channel> Transfer<'a, C> { pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().st(self.channel.num()); - let en = ch.cr().read().en(); - // Check if circular mode is enabled, if so it will still be running even if tcif == 1 - let circular = ch.cr().read().circ() == vals::Circ::ENABLED; - let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; - en && (circular || !tcif) + ch.cr().read().en() } /// Gets the total remaining transfers for the channel