diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index d43d76012144..43033e74e6e6 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -6,6 +6,7 @@ use super::{ #[cfg(feature = "async")] use super::{FutureAdc, async_api}; +use crate::dac::DacWriteHandle; use crate::{calibration, pac}; use pac::Peripherals; use pac::Sysctrl; @@ -238,6 +239,15 @@ impl Adc { } impl Adc { + #[inline] + /// Reads the output of DAC0 + pub async fn read_dac0_output( + &mut self, + _channel: &DacWriteHandle<'_>, + ) -> u16 { + self.read_channel(0x1C) + } + #[inline] /// Returns the CPU temperature in degrees C /// @@ -283,6 +293,15 @@ impl FutureAdc where F: crate::async_hal::interrupts::Binding>, { + + /// Reads the output of DAC0 + pub async fn read_dac0_output( + &mut self, + _channel: &DacWriteHandle<'_>, + ) -> u16 { + self.read_channel(0x1C).await + } + /// Reads the CPU temperature. Value returned is in Celcius pub async fn read_cpu_temperature(&mut self, sysctrl: &mut Sysctrl) -> f32 { let old_state = sysctrl.vref().read().tsen().bit(); diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 9a8d87cbf993..729f34e9b7df 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -3,13 +3,17 @@ pub mod pin; use pac::Supc; #[cfg(feature = "async")] -use super::{FutureAdc, async_api}; +use super::{async_api, FutureAdc}; use super::{ - ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource, - Error, Flags, PrimaryAdc, SampleCount, + Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource, Error, Flags, PrimaryAdc, + SampleCount, ADC_SETTINGS_INTERNAL_READ, +}; +use crate::{ + calibration, + dac::{Dac0, Dac1, DacWriteHandle, Differential, Single}, + pac, }; -use crate::{calibration, pac}; /// ADC instance 0 pub struct Adc0 { @@ -154,6 +158,21 @@ impl Adc { } impl Adc { + #[inline] + /// Reads the output of DAC0 when in the DAC is in single mode + pub fn read_dac0_single_output(&mut self, _channel: &DacWriteHandle<'_, Single>) -> u16 { + self.read_channel(0x1E) + } + + #[inline] + /// Reads the output of DAC0 when in the DAC is in differential mode + pub fn read_dac0_differential_output( + &mut self, + _channel: &DacWriteHandle<'_, Differential>, + ) -> u16 { + self.read_channel(0x1E) + } + #[inline] /// Reads the CPU temperature in degrees C. /// @@ -235,7 +254,7 @@ impl Adc { Flags::from_bits_truncate(bits) } - #[cfg(feature="async")] + #[cfg(feature = "async")] /// Clear the specified interrupt flags #[inline] pub(super) fn clear_flags(&mut self, flags: &Flags) { @@ -297,6 +316,24 @@ impl FutureAdc where F: crate::async_hal::interrupts::Binding>, { + #[inline] + /// Reads the output of DAC0 when in the DAC is in single mode + pub async fn read_dac0_single_output( + &mut self, + _channel: &DacWriteHandle<'_, Single>, + ) -> u16 { + self.read_channel(0x1E).await + } + + #[inline] + /// Reads the output of DAC0 when in the DAC is in differential mode + pub async fn read_dac0_differential_output( + &mut self, + _channel: &DacWriteHandle<'_, Differential>, + ) -> u16 { + self.read_channel(0x1E).await + } + /// Reads the CPU temperature in degrees C. /// /// n.b. Microchip's errata document for SAM D5x/E5x states: diff --git a/hal/src/peripherals/dac/d11.rs b/hal/src/peripherals/dac/d11.rs new file mode 100644 index 000000000000..ce06476e8a76 --- /dev/null +++ b/hal/src/peripherals/dac/d11.rs @@ -0,0 +1,243 @@ +//! Digital-to-Analog Converter + +use pac::Pm; + +use crate::{ + clock::DacClock, + gpio::{AlternateB, Pin, PA02}, + pac, +}; + +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// The DAC clock exceeds the maximum + /// of 350Khz + ClockTooFast, +} + +pub struct Dac { + inner: pac::Dac, +} + +pub struct DacWriteHandle<'a> { + pin: Pin, + reg: &'a pac::Dac, +} + +impl<'a> DacWriteHandle<'a> { + pub fn new(dac: &'a pac::Dac, pin: Pin) -> Self { + Self { reg: dac, pin } + } +} + +impl Dac { + /// Construct a new DAC. The DAC VREF is set to 3.3V + /// (VDDANA) to allow for maximum output voltage of 3.3V + /// + /// This function also checks the clock frequency provided + /// to the DAC, erroring out if the Clock exceeds the maximum + /// DAC clock frequency: + /// + /// * **SAMD/E5x** - 12Mhz + /// * **SAMC/D21** - 350Khz + /// * **SAMD11** - 350Khz + pub fn new(dac: pac::Dac, clk: DacClock, pm: &mut Pm) -> Result { + if clk.freq().to_Hz() > 350_000 { + return Err(Error::ClockTooFast); + } + pm.apbcmask().modify(|_, w| w.dac_().set_bit()); + dac.ctrla().write(|w| w.swrst().set_bit()); + let s = Self { inner: dac }; + s.sync(); + s.with_disable(|dac| { + // Set VREF + dac.ctrlb().modify(|_, w| w.refsel().avcc()); + }); + Ok(s) + } + + pub(crate) fn with_disable R>(&self, f: F) -> R { + self.inner.ctrla().write(|w| w.enable().clear_bit()); + self.sync(); + let ret = f(&self.inner); + self.inner.ctrla().write(|w| w.enable().set_bit()); + self.sync(); + ret + } + + pub fn sync(&self) { + while self.inner.status().read().syncbusy().bit_is_set() { + core::hint::spin_loop(); + } + } + + /// Converts input voltage in millivolts to DAC output value (RAW) + /// The input voltage is clamped between 0 and 3300mv, + /// resulting in an output between 0 and 1024 + /// + /// Use this function for single channel mode DAC + pub fn voltage_to_raw(mv: u16) -> u16 { + // Up to 4096 output + const RATIO: f32 = 1024.0 / 3300.0; + let targ = core::cmp::min(3300, mv) as f32; + (targ * RATIO) as u16 + } + + /// Get a handle to DAC 0 output. This consumes PA02, since + /// the DAC is now enabled and using this pin + pub fn dac0(&self, pin: Pin) -> DacWriteHandle<'_> { + self.inner.ctrla().modify(|_, w| w.enable().set_bit()); + self.sync(); + DacWriteHandle::new(&self.inner, pin) + } + + /// Destroy the DAC peripheral, returning resources. + /// The DAC will be disabled and reset before being released + pub fn release(self) -> pac::Dac { + self.inner.ctrla().modify(|_, w| w.enable().clear_bit()); + self.sync(); + self.inner.ctrla().write(|w| w.swrst().set_bit()); + self.sync(); + self.inner + } +} + +impl DacWriteHandle<'_> { + pub fn sync(&self) { + while self.reg.status().read().syncbusy().bit_is_set() { + core::hint::spin_loop(); + } + } + + pub(crate) fn with_dac_disable R>(&self, f: F) -> R { + self.reg.ctrla().write(|w| w.enable().clear_bit()); + self.sync(); + let ret = f(self.reg); + self.reg.ctrla().write(|w| w.enable().set_bit()); + self.sync(); + ret + } + + pub fn write_val(&mut self, val: u16) { + unsafe { + self.reg.data().write(|w| w.bits(val)); + } + } + + /// Writes a voltage to the DAC. + #[inline] + pub fn write_voltage(&mut self, mv: u16) { + let raw = Dac::voltage_to_raw(mv); + self.write_val(raw); + } + + /// Stop the DAC write handle, releasing the pin + pub fn stop(self) -> Pin { + self.with_dac_disable(|dac| { + dac.ctrla().modify(|_, w| w.enable().clear_bit()); + }); + self.pin + } +} + +#[cfg(feature = "dma")] +mod dma { + use pac::dmac::chctrlb::Trigactselect as TriggerAction; + use pac::dmac::chctrlb::Trigsrcselect as TriggerSource; + + #[cfg(feature = "async")] + use crate::dmac::ReadyFuture; + use crate::{ + dmac::{self, AnyChannel, Buffer, Ready}, + sercom::dma::SharedSliceBuffer, + }; + + use super::*; + + struct DacDmaPtr(pub *mut u16); + + impl DacDmaPtr { + pub fn new(reg: &pac::Dac) -> Self { + Self(reg.data().as_ptr()) + } + } + + unsafe impl Buffer for DacDmaPtr { + type Beat = u16; + + fn dma_ptr(&mut self) -> *mut Self::Beat { + self.0 + } + + fn incrementing(&self) -> bool { + false + } + + fn buffer_len(&self) -> usize { + 1 + } + } + + impl DacWriteHandle<'_> { + /// Writes a buffer to the DAC using DMA. Each buffer value is DAC + /// RAW output (0-1024). Use [Dac::voltage_to_raw] to convert + /// between target voltage output of the DAC and the value to write + /// to the DAC + /// + /// The samples are consumed at the sample rate of the DAC + pub fn write_buffer_blocking( + &mut self, + buf: &[u16], + channel: &mut CH, + ) -> Result<(), dmac::Error> + where + CH: AnyChannel, + { + let mut bytes = SharedSliceBuffer::from_slice(buf); + let trigger_action = TriggerAction::Beat; + + let mut dest = DacDmaPtr::new(self.reg); + + unsafe { + channel.as_mut().transfer( + &mut bytes, + &mut dest, + TriggerSource::DacEmpty, + trigger_action, + None, + )?; + } + while channel.as_mut().xfer_complete() { + core::hint::spin_loop(); + } + channel.as_mut().xfer_success() + } + + /// Writes a buffer to the DAC using DMA (Async version). + /// Each buffer value is DAC RAW output (0-4096). + /// Use [Dac::voltage_to_raw] to convert between target + /// voltage output of the DAC and the value to write + /// to the DAC + /// + /// The samples are consumed at the sample rate of the DAC + #[cfg(feature = "async")] + pub async fn write_buffer( + &mut self, + buf: &[u16], + channel: &mut CH, + ) -> Result<(), dmac::Error> + where + CH: AnyChannel, + { + let bytes = SharedSliceBuffer::from_slice(buf); + let trigger_action = TriggerAction::Beat; + let dest = DacDmaPtr::new(self.reg); + + channel + .as_mut() + .transfer_future(bytes, dest, TriggerSource::DacEmpty, trigger_action) + .await + } + } +} diff --git a/hal/src/peripherals/dac/d5x.rs b/hal/src/peripherals/dac/d5x.rs new file mode 100644 index 000000000000..834c1f286986 --- /dev/null +++ b/hal/src/peripherals/dac/d5x.rs @@ -0,0 +1,537 @@ +//! Digital-to-Analog Converter + +use crate::clock::v2::{ + apb::ApbClk, + pclk::{Pclk, PclkSourceId}, + types, +}; +use num_traits::clamp; +use pac::dac; + +#[cfg(feature = "dma")] +use pac::dmac::channel::chctrla::Trigsrcselect as TriggerSource; + +use crate::{ + gpio::{self, AlternateB, Pin, PA02, PA05}, + pac, +}; + +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// The DAC clock exceeds the maximum + /// of 12Mhz + ClockTooFast, +} + +pub struct Dac { + inner: pac::Dac, + clk: ApbClk, + pclk: Pclk, +} + +pub trait DacInstance { + type PinId: gpio::AnyPin; + const IDX: usize; + + #[cfg(feature = "dma")] + const DMA_TX_TRIGGER: TriggerSource; + + fn data_ready(reg: &pac::Dac) -> bool; + fn ready(reg: &pac::Dac) -> bool; + fn eoc(reg: &pac::Dac) -> bool; + + #[cfg(feature = "dma")] + fn dma_data_ptr(reg: &pac::Dac) -> *mut u16; +} + +pub struct Single { + pin: D::PinId, +} +pub struct Differential { + pin_0: D0::PinId, + pin_1: D1::PinId, +} + +pub trait DacMode {} + +impl DacMode for Single {} +impl DacMode for Differential {} + +pub struct DacWriteHandle<'a, M: DacMode> { + reg: &'a pac::Dac, + mode: M, +} + +impl<'a, M: DacMode> DacWriteHandle<'a, M> { + pub fn new(dac: &'a pac::Dac, mode: M) -> Self { + Self { reg: dac, mode } + } +} + +pub struct Dac0; + +pub struct Dac1; + +impl DacInstance for Dac0 { + type PinId = Pin; + const IDX: usize = 0; + + #[cfg(feature = "dma")] + const DMA_TX_TRIGGER: TriggerSource = TriggerSource::DacEmpty0; + + fn ready(reg: &pac::Dac) -> bool { + reg.status().read().ready0().bit_is_set() + } + + fn data_ready(reg: &pac::Dac) -> bool { + reg.syncbusy().read().data0().bit_is_set() + } + + fn eoc(reg: &pac::Dac) -> bool { + reg.status().read().eoc0().bit_is_set() + } + + #[cfg(feature = "dma")] + fn dma_data_ptr(reg: &pac::Dac) -> *mut u16 { + reg.data(0).as_ptr() + } +} + +impl DacInstance for Dac1 { + type PinId = Pin; + const IDX: usize = 1; + + #[cfg(feature = "dma")] + const DMA_TX_TRIGGER: TriggerSource = TriggerSource::DacEmpty1; + + fn ready(reg: &pac::Dac) -> bool { + reg.status().read().ready1().bit_is_set() + } + + fn data_ready(reg: &pac::Dac) -> bool { + reg.syncbusy().read().data1().bit_is_set() + } + + fn eoc(reg: &pac::Dac) -> bool { + reg.status().read().eoc1().bit_is_set() + } + + #[cfg(feature = "dma")] + fn dma_data_ptr(reg: &pac::Dac) -> *mut u16 { + reg.data(1).as_ptr() + } +} + +/// Dac voltage converter helper +pub struct DacHelper; + +impl DacHelper { + /// Converts input voltage in millivolts to DAC output value (RAW) + /// The input voltage is clamped between 0 and 3300mv, + /// resulting in an output between 0 and 4096 + /// + /// Use this function for single channel mode DAC + pub fn voltage_to_raw(mv: u16) -> u16 { + // Up to 4096 output + const RATIO: f32 = 4096.0 / 3300.0; + let targ = core::cmp::min(3300, mv) as f32; + (targ * RATIO) as u16 + } + + /// Converts input voltage in millivolts to DAC output value (RAW) + /// The input voltage is clamped between -1650mv and +1650mv, + /// resulting in an output between -2048 and +2048 + /// + /// Use this function for differential mode DAC + pub fn voltage_to_raw_signed(mv: i16) -> i16 { + // Up to +/-2048 output + const RATIO: f32 = 4096.0 / 3300.0; + let targ = clamp(mv, -1650, 1650) as f32; + (targ * RATIO) as i16 + } +} + +impl Dac { + /// Construct a new DAC. The DAC VREF is set to 3.3V + /// (VDDANA) to allow for maximum output voltage of 3.3V + /// + /// This function also checks the clock frequency provided + /// to the DAC, erroring out if the Clock exceeds the maximum + /// DAC clock frequency: + /// + /// * **SAMD/E5x** - 12Mhz + /// * **SAMC/D21** - 350Khz + /// * **SAMD11** - 350Khz + pub fn new( + dac: pac::Dac, + clk: ApbClk, + pclk: Pclk, + ) -> Result { + // TODO: Ideally, the DAC struct would take ownership of the Pclk type here. + // However, since clock::v2 is not implemented for all chips yet, the + // generics for the Adc type would be different between chip families, + // leading to massive and unnecessary code duplication. In the meantime, + // we use a "lite" variation of the typelevel guarantees laid out by the + // clock::v2 module, meaning that we can guarantee that the clocks are enabled + // at the time of creation of the Adc struct; however we can't guarantee + // that the clock will stay enabled for the duration of its lifetime. + let pclk_freq = pclk.freq().to_Hz(); + if pclk_freq > 12_000_000 { + return Err(Error::ClockTooFast); + } + dac.ctrla().write(|w| w.swrst().set_bit()); + + let s = Self { + inner: dac, + clk, + pclk, + }; + s.sync(); + s.with_disable(|dac| { + // Set VREF + dac.ctrlb().modify(|_, w| w.refsel().vddana()); + + // Setup CC size based on GCLK Freq + let cc = match pclk_freq { + ..1_200_000 => dac::dacctrl::Cctrlselect::Cc100k, + 1_200_000..=6_000_000 => dac::dacctrl::Cctrlselect::Cc1m, + _ => dac::dacctrl::Cctrlselect::Cc12m, + }; + dac.dacctrl(0).modify(|_, w| w.cctrl().variant(cc)); + dac.dacctrl(1).modify(|_, w| w.cctrl().variant(cc)); + }); + Ok(s) + } + + pub(crate) fn with_disable R>(&self, f: F) -> R { + self.inner.ctrla().write(|w| w.enable().clear_bit()); + self.sync(); + let ret = f(&self.inner); + self.inner.ctrla().write(|w| w.enable().set_bit()); + self.sync(); + ret + } + + pub fn sync(&self) { + while self.inner.syncbusy().read().bits() != 0 { + core::hint::spin_loop(); + } + } + + /// Sets the DAC up in differential mode. + /// + /// In this mode, Dac0 is output D0, and Dac1 is output D1. + /// + /// When writing to the Differential pair, The baseline voltage + /// is 1.65V, with D0 writing `1.65+x`, and D1 writing `1.65-x`. + /// This is why in differential mode, the write value is signed. + pub fn differential( + &self, + d0: Pin, + d1: Pin, + ) -> DacWriteHandle<'_, Differential> { + self.with_disable(|dac| { + dac.ctrlb().modify(|_, w| w.diff().set_bit()); + dac.dacctrl(0).modify(|_, w| w.enable().set_bit()); + }); + while !Dac0::ready(&self.inner) { + core::hint::spin_loop(); + } + DacWriteHandle::new( + &self.inner, + Differential { + pin_0: d0, + pin_1: d1, + }, + ) + } + + pub fn dac0(&self, pin: Pin) -> DacWriteHandle<'_, Single> { + self.with_disable(|dac| { + dac.ctrlb().modify(|_, w| w.diff().clear_bit()); + dac.dacctrl(Dac0::IDX).modify(|_, w| w.enable().set_bit()) + }); + while !Dac0::ready(&self.inner) { + core::hint::spin_loop(); + } + DacWriteHandle::new(&self.inner, Single { pin }) + } + + pub fn dac1(&self, pin: Pin) -> DacWriteHandle<'_, Single> { + self.with_disable(|dac| { + dac.ctrlb().modify(|_, w| w.diff().clear_bit()); + dac.dacctrl(Dac1::IDX).modify(|_, w| w.enable().set_bit()) + }); + while !Dac1::ready(&self.inner) { + core::hint::spin_loop(); + } + DacWriteHandle::new(&self.inner, Single { pin }) + } + + /// Destroy the DAC peripheral, returning resources. + /// The DAC will be disabled and reset before being released + pub fn free(self) -> (pac::Dac, ApbClk, Pclk) { + self.inner.ctrla().modify(|_, w| w.enable().clear_bit()); + self.sync(); + self.inner.ctrla().write(|w| w.swrst().set_bit()); + self.sync(); + (self.inner, self.clk, self.pclk) + } +} + +// For all modes +impl DacWriteHandle<'_, M> { + pub fn sync(&self) { + while self.reg.syncbusy().read().bits() != 0 { + core::hint::spin_loop(); + } + } + + pub(crate) fn with_dac_disable R>(&self, f: F) -> R { + self.reg.ctrla().write(|w| w.enable().clear_bit()); + self.sync(); + let ret = f(self.reg); + self.reg.ctrla().write(|w| w.enable().set_bit()); + self.sync(); + ret + } +} + +// For single DAC modes (Dac0/Dac1) +impl DacWriteHandle<'_, Single> { + /// Writes a raw value to the DAC. Input value is between + /// 0 and 4096. If you want the DAC to output a specific + /// voltage, use [Self::write_voltage] instead. + pub fn write_val(&mut self, val: u16) { + unsafe { + self.reg.data(DAC::IDX).write(|w| w.bits(val)); + } + while DAC::data_ready(self.reg) { + core::hint::spin_loop(); + } + while !DAC::eoc(self.reg) { + core::hint::spin_loop(); + } + } + + /// Writes a voltage to the DAC. + #[inline] + pub fn write_voltage(&mut self, mv: u16) { + let raw = DacHelper::voltage_to_raw(mv); + self.write_val(raw); + } + + /// Stop the DAC in single mode, releasing the pin + pub fn stop(self) -> DAC::PinId { + self.with_dac_disable(|dac| { + dac.dacctrl(DAC::IDX).modify(|_, w| w.enable().clear_bit()); + }); + self.mode.pin + } +} + +// For differential mode +impl DacWriteHandle<'_, Differential> { + /// Writes a raw value to the DAC. Input value is between + /// -2048 and +2048. If you want the DAC to output a specific + /// voltage, use [Self::write_voltage] instead. + pub fn write_val(&mut self, val: i16) { + // Manipulation for differential mode is done via DAC0 channel info + unsafe { + self.reg.data(0).write(|w| w.bits(val as u16)); + } + while Dac0::data_ready(self.reg) { + core::hint::spin_loop(); + } + while !Dac0::eoc(self.reg) { + core::hint::spin_loop(); + } + } + + /// Writes a voltage to the DAC. + #[inline] + pub fn write_voltage(&mut self, mv: i16) { + let raw = DacHelper::voltage_to_raw_signed(mv); + self.write_val(raw); + } + + /// Stop the DAC in differential mode, releasing both pins + pub fn stop(self) -> (D0::PinId, D1::PinId) { + self.with_dac_disable(|dac| { + dac.dacctrl(0).modify(|_, w| w.enable().clear_bit()); + }); + (self.mode.pin_0, self.mode.pin_1) + } +} + +#[cfg(feature = "dma")] +mod dma { + use pac::dmac::channel::chctrla::Trigactselect as TriggerAction; + + #[cfg(feature = "async")] + use crate::dmac::ReadyFuture; + use crate::{ + dmac::{self, AnyChannel, Buffer, Ready}, + sercom::dma::SharedSliceBuffer, + }; + + use super::*; + + struct DacDmaPtr(pub *mut u16); + + impl DacDmaPtr { + pub fn new(reg: &pac::Dac) -> Self { + Self(D::dma_data_ptr(reg)) + } + } + + unsafe impl Buffer for DacDmaPtr { + type Beat = u16; + + fn dma_ptr(&mut self) -> *mut Self::Beat { + self.0 + } + + fn incrementing(&self) -> bool { + false + } + + fn buffer_len(&self) -> usize { + 1 + } + } + + impl DacWriteHandle<'_, Single> { + /// Writes a buffer to the DAC using DMA. Each buffer value is DAC + /// RAW output (0-4096). Use [DacHelper::voltage_to_raw] to convert + /// between target voltage output of the DAC and the value to write + /// to the DAC + /// + /// The samples are consumed at the sample rate of the DAC + pub fn write_buffer_blocking( + &mut self, + buf: &[u16], + channel: &mut CH, + ) -> Result<(), dmac::Error> + where + CH: AnyChannel, + { + let mut bytes = SharedSliceBuffer::from_slice(buf); + let trigger_action = TriggerAction::Burst; + let mut dest = DacDmaPtr::new::(self.reg); + + unsafe { + channel.as_mut().transfer( + &mut bytes, + &mut dest, + DAC::DMA_TX_TRIGGER, + trigger_action, + None, + ) + } + } + + /// Writes a buffer to the DAC using DMA (Async version). + /// Each buffer value is DAC RAW output (0-4096). + /// Use [DacHelper::voltage_to_raw] to convert between target + /// voltage output of the DAC and the value to write + /// to the DAC + /// + /// The samples are consumed at the sample rate of the DAC + #[cfg(feature = "async")] + pub async fn write_buffer( + &mut self, + buf: &[u16], + channel: &mut CH, + ) -> Result<(), dmac::Error> + where + CH: AnyChannel, + { + let bytes = SharedSliceBuffer::from_slice(buf); + let trigger_action = TriggerAction::Burst; + let dest = DacDmaPtr::new::(self.reg); + + channel + .as_mut() + .transfer_future(bytes, dest, DAC::DMA_TX_TRIGGER, trigger_action) + .await + } + } + + impl DacWriteHandle<'_, Differential> { + /// Writes a buffer to the DAC using DMA + /// Each buffer value is DAC Differential output (-2048-+2048). + /// Use `[DacHelper::voltage_to_raw_signed]` to convert between target + /// voltage output of the DAC and the value to write + /// to the DAC. + /// + /// The samples are consumed at the sample rate of the DAC + pub fn write_buffer_blocking( + &mut self, + buf: &[i16], + channel: &mut CH, + ) -> Result<(), dmac::Error> + where + CH: AnyChannel, + { + // SAFETY - This case is safe, since it just allows + // us to not have a new DacDmaPtr type with i16. + // The DAC interprets the data as i16 for differential mode + let mut bytes = SharedSliceBuffer::from_slice(unsafe { + core::mem::transmute::<&[i16], &[u16]>(buf) + }); + + let trigger_action = TriggerAction::Burst; + + let mut dest = DacDmaPtr::new::(self.reg); + + unsafe { + channel.as_mut().transfer( + &mut bytes, + &mut dest, + D0::DMA_TX_TRIGGER, + trigger_action, + None, + )?; + } + while channel.as_mut().xfer_complete() { + core::hint::spin_loop(); + } + channel.as_mut().xfer_success() + } + + /// Writes a buffer to the DAC using DMA (Async version). + /// Each buffer value is DAC Differential output (-2048-+2048). + /// Use `[DacHelper::voltage_to_raw_signed]` to convert between target + /// voltage output of the DAC and the value to write + /// to the DAC. + /// + /// The samples are consumed at the sample rate of the DAC + #[cfg(feature = "async")] + pub async fn write_buffer( + &mut self, + buf: &[i16], + channel: &mut CH, + ) -> Result<(), dmac::Error> + where + CH: AnyChannel, + { + // SAFETY - This case is safe, since it just allows + // us to not have a new DacDmaPtr type with i16. + // The DAC interprets the data as i16 for differential mode + let bytes = SharedSliceBuffer::from_slice(unsafe { + core::mem::transmute::<&[i16], &[u16]>(buf) + }); + + let trigger_action = TriggerAction::Burst; + + let dest = DacDmaPtr::new::(self.reg); + + channel + .as_mut() + .transfer_future(bytes, dest, D0::DMA_TX_TRIGGER, trigger_action) + .await + } + } +} diff --git a/hal/src/peripherals/mod.rs b/hal/src/peripherals/mod.rs index 78eb4048a08c..8ad536abef66 100644 --- a/hal/src/peripherals/mod.rs +++ b/hal/src/peripherals/mod.rs @@ -3,6 +3,12 @@ use atsamd_hal_macros::{hal_cfg, hal_module}; #[cfg(feature = "device")] pub mod adc; +#[hal_module( + any("dac-d11", "dac-d21") => "dac/d11.rs", + "dac-d5x" => "dac/d5x.rs", +)] +pub mod dac {} + #[hal_module( any("nvmctrl-d11", "nvmctrl-d21") => "calibration/d11.rs", "nvmctrl-d5x" => "calibration/d5x.rs",