diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index d43d76012144..55eb14e2bf37 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -6,6 +6,9 @@ use super::{ #[cfg(feature = "async")] use super::{FutureAdc, async_api}; +#[cfg(feature = "dma")] +use pac::dmac::chctrlb::Trigsrcselect as TriggerSelect; + use crate::{calibration, pac}; use pac::Peripherals; use pac::Sysctrl; @@ -22,6 +25,8 @@ impl PrimaryAdc for Adc0 {} impl AdcInstance for Adc0 { type Instance = pac::Adc; + const DMA_TRIGGER: TriggerSelect = TriggerSelect::AdcResrdy; + #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC; diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 9a8d87cbf993..7fc2ead2c3ea 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -3,11 +3,14 @@ pub mod pin; use pac::Supc; #[cfg(feature = "async")] -use super::{FutureAdc, async_api}; +use super::{async_api, FutureAdc}; + +#[cfg(feature = "dma")] +use pac::dmac::channel::chctrla::Trigsrcselect as TriggerSelect; 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, pac}; @@ -21,6 +24,9 @@ impl PrimaryAdc for Adc0 {} impl AdcInstance for Adc0 { type Instance = pac::Adc0; + #[cfg(feature = "dma")] + const DMA_TRIGGER: TriggerSelect = TriggerSelect::Adc0Resrdy; + type ClockId = crate::clock::v2::pclk::ids::Adc0; #[cfg(feature = "async")] @@ -72,6 +78,9 @@ impl AdcInstance for Adc1 { type ClockId = crate::clock::v2::pclk::ids::Adc1; + #[cfg(feature = "dma")] + const DMA_TRIGGER: TriggerSelect = TriggerSelect::Adc1Resrdy; + #[cfg(feature = "async")] type Interrupt = crate::async_hal::interrupts::ADC1; @@ -235,7 +244,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) { diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index c34b560ad113..b49bd01b45a5 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -22,12 +22,13 @@ //! adc.read_buffer(&mut adc_pin, &mut _buffer).unwrap(); //! ``` -use core::ops::Deref; - +use crate::{gpio::AnyPin, pac, typelevel::Sealed}; use atsamd_hal_macros::{hal_cfg, hal_module}; +use core::ops::Deref; use pac::Peripherals; -use crate::{gpio::AnyPin, pac, typelevel::Sealed}; +#[cfg(feature = "dma")] +use pac::dmac; #[hal_module( any("adc-d11", "adc-d21") => "d11/mod.rs", @@ -50,6 +51,14 @@ use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] use crate::pac::adc0; +#[cfg(feature = "dma")] +#[hal_cfg("adc-d5x")] +use dmac::channel::chctrla::Trigsrcselect as TriggerSelect; + +#[cfg(feature = "dma")] +#[hal_cfg(any("adc-d11", "adc-d21"))] +use dmac::chctrlb::Trigsrcselect as TriggerSelect; + pub use adc0::refctrl::Refselselect as Reference; /// ADC Settings when reading Internal sensors (Like VREF and Temperatures) @@ -88,6 +97,16 @@ pub enum Error { ClockTooFast, /// Buffer overflowed BufferOverrun, + /// DMA Error + #[cfg(feature = "dma")] + DmaError(crate::dmac::Error), +} + +#[cfg(feature = "dma")] +impl From for Error { + fn from(value: crate::dmac::Error) -> Self { + Self::DmaError(value) + } } /// Voltage source to use when using the ADC to measure the CPU voltage @@ -140,6 +159,9 @@ pub trait AdcInstance { // The Adc0 and Adc1 PAC types implement Deref type Instance: Deref; + #[cfg(feature = "dma")] + const DMA_TRIGGER: TriggerSelect; + #[hal_cfg("adc-d5x")] type ClockId: crate::clock::v2::apb::ApbId + crate::clock::v2::pclk::PclkId; @@ -362,56 +384,6 @@ impl Adc { } } - /// Read into a buffer from the provided ADC pin, in a blocking fashion - #[inline] - pub fn read_buffer>( - &mut self, - _pin: &mut P, - dst: &mut [u16], - ) -> Result<(), Error> { - self.read_buffer_channel(P::CHANNEL, dst) - } - - /// Read into a buffer from the provided channel, in a blocking fashion - #[inline] - fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { - // Clear overrun errors that might've occured before we try to read anything - self.clear_all_flags(); - self.disable_interrupts(Flags::all()); - self.mux(ch); - self.enable_freerunning(); - self.start_conversion(); - if self.discard { - // Discard first result - while !self.read_flags().contains(Flags::RESRDY) { - core::hint::spin_loop(); - } - self.clear_all_flags(); - self.discard = false; - } - - for result in dst.iter_mut() { - while !self.read_flags().contains(Flags::RESRDY) { - core::hint::spin_loop(); - } - - let flags = self.read_flags(); - self.clear_all_flags(); - if let Err(e) = self.check_overrun(&flags) { - //self.power_down(); - self.disable_freerunning(); - - return Err(e); - } - - *result = self.conversion_result(); - } - //self.power_down(); - self.disable_freerunning(); - - Ok(()) - } - /// Return the underlying ADC PAC object. #[hal_cfg(any("adc-d11", "adc-d21"))] #[inline] @@ -479,47 +451,160 @@ where self.inner.sync(); res } +} - /// Read into a buffer from the provided ADC pin - #[inline] - pub async fn read_buffer>( - &mut self, - _pin: &mut P, - dst: &mut [u16], - ) -> Result<(), Error> { - self.read_buffer_channel(P::CHANNEL, dst).await - } +#[cfg(feature = "dma")] +mod dma { + use super::*; + #[cfg(feature = "async")] + use crate::dmac::ReadyFuture; + use crate::dmac::{AnyChannel, Buffer, Ready}; - /// Read into a buffer from the provided channel ID - #[inline] - async fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { - // Clear overrun errors that might've occured before we try to read anything - self.inner.clear_all_flags(); - self.inner.mux(ch); - self.inner.enable_freerunning(); + use atsamd_hal_macros::hal_macro_helper; + #[hal_cfg("adc-d5x")] + use pac::dmac::channel::chctrla::Trigactselect as TriggerAction; - if self.inner.discard { - // Discard first result - let _ = self.wait_flags(Flags::RESRDY).await; - let _ = self.inner.conversion_result(); - self.inner.discard = false; - self.inner.clear_all_flags(); + #[hal_cfg(any("adc-d11", "adc-d21"))] + use pac::dmac::chctrlb::Trigactselect as TriggerAction; + + pub struct AdcDmaPtr(pub *mut u16); + + unsafe impl Buffer for AdcDmaPtr { + type Beat = u16; + + fn dma_ptr(&mut self) -> *mut Self::Beat { + self.0 + } + + fn incrementing(&self) -> bool { + false + } + + fn buffer_len(&self) -> usize { + 1 } + } - // Don't re-trigger start conversion now, its already enabled in free running - for result in dst.iter_mut() { - if let Err(e) = self.wait_flags(Flags::RESRDY).await { - //self.inner.power_down(); - self.inner.disable_freerunning(); + impl Adc { + /// Read into a buffer from the provided ADC pin using DMA + /// in a blocking fashion + #[inline] + pub fn read_buffer>( + &mut self, + _pin: &mut P, + dst: &mut [u16], + channel: &mut CH, + ) -> Result<(), Error> + where + CH: AnyChannel, + { + self.read_buffer_channel(P::CHANNEL, dst, channel) + } - return Err(e); + /// Read into a buffer from the provided channel, in a blocking fashion + #[inline] + #[hal_macro_helper] + fn read_buffer_channel( + &mut self, + ch: u8, + mut dst: &mut [u16], + channel: &mut CH, + ) -> Result<(), Error> + where + CH: AnyChannel, + { + // Clear overrun errors that might've occured before we try to read anything + self.clear_all_flags(); + self.disable_interrupts(Flags::all()); + self.mux(ch); + self.enable_freerunning(); + self.start_conversion(); + if self.discard { + // Discard first result + while !self.read_flags().contains(Flags::RESRDY) { + core::hint::spin_loop(); + } + self.clear_all_flags(); + self.discard = false; + } + // Now read via DMA + let mut src = AdcDmaPtr(self.adc.result().as_ptr()); + + #[hal_cfg(any("adc-d5x"))] + let action = TriggerAction::Burst; + #[hal_cfg(any("adc-d11", "adc-d21"))] + let action = TriggerAction::Beat; + // SAFETY: We must make sure that any DMA transfer is complete or stopped before + // returning. + unsafe { + channel + .as_mut() + .transfer(&mut src, &mut dst, I::DMA_TRIGGER, action, None)?; } - *result = self.inner.conversion_result(); + while !channel.as_mut().xfer_complete() { + core::hint::spin_loop(); + } + channel.as_mut().stop(); + channel.as_mut().xfer_success()?; + self.disable_freerunning(); + Ok(()) } + } - //self.inner.power_down(); - self.inner.disable_freerunning(); + #[cfg(feature = "async")] + impl FutureAdc + where + F: crate::async_hal::interrupts::Binding>, + { + /// Read into a buffer from the provided ADC pin using DMA + #[inline] + pub async fn read_buffer>( + &mut self, + _pin: &mut P, + dst: &mut [u16], + channel: &mut CH, + ) -> Result<(), Error> + where + CH: AnyChannel, + { + self.read_buffer_channel(P::CHANNEL, dst, channel).await + } - Ok(()) + #[inline] + #[hal_macro_helper] + async fn read_buffer_channel( + &mut self, + ch: u8, + dst: &mut [u16], + channel: &mut CH, + ) -> Result<(), Error> + where + CH: AnyChannel, + { + // Clear overrun errors that might've occured before we try to read anything + self.inner.clear_all_flags(); + self.inner.mux(ch); + self.inner.enable_freerunning(); + + if self.inner.discard { + // Discard first result + let _ = self.wait_flags(Flags::RESRDY).await; + let _ = self.inner.conversion_result(); + self.inner.discard = false; + self.inner.clear_all_flags(); + } + let src = AdcDmaPtr(self.inner.adc.result().as_ptr()); + #[hal_cfg(any("adc-d5x"))] + let action = TriggerAction::Burst; + #[hal_cfg(any("adc-d11", "adc-d21"))] + let action = TriggerAction::Beat; + channel + .as_mut() + .transfer_future(src, dst, I::DMA_TRIGGER, action) + .await?; + + self.inner.disable_freerunning(); + Ok(()) + } } }