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