//! # Direct Memory Access //! //! ## Overview //! The GDMA (General DMA) module is a part of the DMA (Direct Memory Access) //! driver for ESP chips. Of the Espressif chip range, every chip except of //! `ESP32` and `ESP32-S2` uses the `GDMA` type of direct memory access. //! //! DMA is a hardware feature that allows data transfer between memory and //! peripherals without involving the CPU, resulting in efficient data movement //! and reduced CPU overhead. The `GDMA` module provides multiple DMA channels, //! each capable of managing data transfer for various peripherals. //! //! This module implements DMA channels, such as `channel0`, `channel1` and so //! on. Each channel struct implements the `ChannelTypes` trait, which provides //! associated types for peripheral configuration. //! //! GDMA peripheral can be initializes using the `new` function, which requires //! a DMA peripheral instance and a clock control reference. ```no_run //! let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); //! ``` //! //! PS: Note that the number of DMA channels is chip-specific. use crate::{ dma::*, peripheral::PeripheralRef, system::{Peripheral, PeripheralClockControl}, }; macro_rules! impl_channel { ($num: literal) => { paste::paste! { #[non_exhaustive] pub struct [] {} impl ChannelTypes for [] { type P = []; type Tx<'a> = ChannelTx<'a, [], []>; type Rx<'a> = ChannelRx<'a, [], []>; } impl RegisterAccess for [] { fn init_channel() { // nothing special to be done here } fn set_out_burstmode(burst_mode: bool) { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_,w| { w.out_data_burst_en().bit(burst_mode) .outdscr_burst_en().bit(burst_mode) }); } fn set_out_priority(priority: DmaPriority) { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].write(|w| { w.tx_pri().variant(priority as u8) }); } fn clear_out_interrupts() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] dma.[].write(|w| { w.out_eof() .set_bit() .out_dscr_err() .set_bit() .out_done() .set_bit() .out_total_eof() .set_bit() .outfifo_ovf() .set_bit() .outfifo_udf() .set_bit() }); #[cfg(any(esp32c6, esp32h2))] dma.[].write(|w| { w.out_eof() .set_bit() .out_dscr_err() .set_bit() .out_done() .set_bit() .out_total_eof() .set_bit() .outfifo_ovf() .set_bit() .outfifo_udf() .set_bit() }); #[cfg(esp32s3)] dma.[].write(|w| { w.out_eof() .set_bit() .out_dscr_err() .set_bit() .out_done() .set_bit() .out_total_eof() .set_bit() .outfifo_ovf_l1() .set_bit() .outfifo_ovf_l3() .set_bit() .outfifo_udf_l1() .set_bit() .outfifo_udf_l3() .set_bit() }); } fn reset_out() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_, w| w.out_rst().set_bit()); dma.[].modify(|_, w| w.out_rst().clear_bit()); } fn set_out_descriptors(address: u32) { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_, w| unsafe { w.outlink_addr().bits(address) }); } fn has_out_descriptor_error() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().out_dscr_err().bit(); #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().out_dscr_err().bit(); ret } fn set_out_peripheral(peripheral: u8) { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_, w| w.peri_out_sel().variant(peripheral)); } fn start_out() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_, w| w.outlink_start().set_bit()); } fn is_out_done() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().out_total_eof().bit(); #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().out_total_eof().bit(); ret } fn last_out_dscr_address() -> usize { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].read().out_eof_des_addr().bits() as usize } fn is_out_eof_interrupt_set() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().out_eof().bit(); #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().out_eof().bit(); ret } fn reset_out_eof_interrupt() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] dma.[].write(|w| { w.out_eof() .set_bit() }); #[cfg(any(esp32c6, esp32h2, esp32s3))] dma.[].write(|w| { w.out_eof() .set_bit() }); } fn set_in_burstmode(burst_mode: bool) { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_,w| { w.in_data_burst_en().bit(burst_mode).indscr_burst_en().bit(burst_mode) }); } fn set_in_priority(priority: DmaPriority) { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].write(|w| { w.rx_pri().variant(priority as u8) }); } fn clear_in_interrupts() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] dma.[].write(|w| { w.in_suc_eof() .set_bit() .in_err_eof() .set_bit() .in_dscr_err() .set_bit() .in_dscr_empty() .set_bit() .in_done() .set_bit() .infifo_ovf() .set_bit() .infifo_udf() .set_bit() }); #[cfg(any(esp32c6, esp32h2))] dma.[].write(|w| { w.in_suc_eof() .set_bit() .in_err_eof() .set_bit() .in_dscr_err() .set_bit() .in_dscr_empty() .set_bit() .in_done() .set_bit() .infifo_ovf() .set_bit() .infifo_udf() .set_bit() }); #[cfg(esp32s3)] dma.[].write(|w| { w.in_suc_eof() .set_bit() .in_err_eof() .set_bit() .in_dscr_err() .set_bit() .in_dscr_empty() .set_bit() .in_done() .set_bit() .infifo_ovf_l1() .set_bit() .infifo_ovf_l3() .set_bit() .infifo_udf_l1() .set_bit() .infifo_udf_l3() .set_bit() }); } fn reset_in() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_, w| w.in_rst().set_bit()); dma.[].modify(|_, w| w.in_rst().clear_bit()); } fn set_in_descriptors(address: u32) { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_, w| unsafe { w.inlink_addr().bits(address) }); } fn has_in_descriptor_error() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().in_dscr_err().bit(); #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().in_dscr_err().bit(); ret } fn has_in_descriptor_error_dscr_empty() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().in_dscr_empty().bit(); #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().in_dscr_empty().bit(); ret } fn has_in_descriptor_error_err_eof() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().in_err_eof().bit(); #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().in_err_eof().bit(); ret } fn set_in_peripheral(peripheral: u8) { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_, w| w.peri_in_sel().variant(peripheral)); } fn start_in() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].modify(|_, w| w.inlink_start().set_bit()); } fn is_in_done() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().in_suc_eof().bit(); #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().in_suc_eof().bit(); ret } fn last_in_dscr_address() -> usize { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].read().inlink_dscr_bf0().bits() as usize } fn is_listening_in_eof() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].read().in_suc_eof().bit_is_set() } else { dma.[].read().in_suc_eof().bit_is_set() } } } fn is_listening_out_eof() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].read().out_total_eof().bit_is_set() } else { dma.[].read().out_total_eof().bit_is_set() } } } fn listen_in_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].modify(|_, w| w.in_suc_eof().set_bit()) } else { dma.[].modify(|_, w| w.in_suc_eof().set_bit()) } } } fn listen_out_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].modify(|_, w| w.out_total_eof().set_bit()) } else { dma.[].modify(|_, w| w.out_total_eof().set_bit()) } } } fn unlisten_in_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].modify(|_, w| w.in_suc_eof().clear_bit()) } else { dma.[].modify(|_, w| w.in_suc_eof().clear_bit()) } } } fn unlisten_out_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].modify(|_, w| w.out_total_eof().clear_bit()) } else { dma.[].modify(|_, w| w.out_total_eof().clear_bit()) } } } } #[non_exhaustive] pub struct [] {} impl<'a> TxChannel<[]> for [] { #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER } } #[non_exhaustive] pub struct [] {} impl<'a> RxChannel<[]> for [] { #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER } } #[non_exhaustive] pub struct [] {} impl [] { /// Configure the channel for use /// /// Descriptors should be sized as `((BUFFERSIZE + 4091) / 4092) * 3`. I.e., to /// transfer buffers of size `1..=4092`, you need 3 descriptors. pub fn configure<'a>( self, burst_mode: bool, tx_descriptors: &'a mut [u32], rx_descriptors: &'a mut [u32], priority: DmaPriority, ) -> Channel<'a, []> { let mut tx_impl = [] {}; tx_impl.init(burst_mode, priority); let tx_channel = ChannelTx { descriptors: tx_descriptors, burst_mode, tx_impl: tx_impl, write_offset: 0, write_descr_ptr: core::ptr::null(), available: 0, last_seen_handled_descriptor_ptr: core::ptr::null(), buffer_start: core::ptr::null(), buffer_len: 0, _phantom: PhantomData::default(), }; let mut rx_impl = [] {}; rx_impl.init(burst_mode, priority); let rx_channel = ChannelRx { descriptors: rx_descriptors, burst_mode, rx_impl: rx_impl, read_descr_ptr: core::ptr::null(), available: 0, last_seen_handled_descriptor_ptr: core::ptr::null(), read_buffer_start: core::ptr::null(), _phantom: PhantomData::default(), }; Channel { tx: tx_channel, rx: rx_channel, } } } #[non_exhaustive] pub struct [] {} impl PeripheralMarker for [] {} // with GDMA every channel can be used for any peripheral impl SpiPeripheral for [] {} impl Spi2Peripheral for [] {} #[cfg(esp32s3)] impl Spi3Peripheral for [] {} impl I2sPeripheral for [] {} impl I2s0Peripheral for [] {} impl I2s1Peripheral for [] {} #[cfg(parl_io)] impl ParlIoPeripheral for [] {} } }; } impl_channel!(0); #[cfg(not(esp32c2))] impl_channel!(1); #[cfg(not(esp32c2))] impl_channel!(2); #[cfg(esp32s3)] impl_channel!(3); #[cfg(esp32s3)] impl_channel!(4); /// GDMA Peripheral /// /// This offers the available DMA channels. pub struct Gdma<'d> { _inner: PeripheralRef<'d, crate::peripherals::DMA>, pub channel0: ChannelCreator0, #[cfg(not(esp32c2))] pub channel1: ChannelCreator1, #[cfg(not(esp32c2))] pub channel2: ChannelCreator2, #[cfg(esp32s3)] pub channel3: ChannelCreator3, #[cfg(esp32s3)] pub channel4: ChannelCreator4, } impl<'d> Gdma<'d> { /// Create a DMA instance. pub fn new( dma: impl crate::peripheral::Peripheral

+ 'd, peripheral_clock_control: &mut PeripheralClockControl, ) -> Gdma<'d> { crate::into_ref!(dma); peripheral_clock_control.enable(Peripheral::Gdma); dma.misc_conf.modify(|_, w| w.ahbm_rst_inter().set_bit()); dma.misc_conf.modify(|_, w| w.ahbm_rst_inter().clear_bit()); dma.misc_conf.modify(|_, w| w.clk_en().set_bit()); Gdma { _inner: dma, channel0: ChannelCreator0 {}, #[cfg(not(esp32c2))] channel1: ChannelCreator1 {}, #[cfg(not(esp32c2))] channel2: ChannelCreator2 {}, #[cfg(esp32s3)] channel3: ChannelCreator3 {}, #[cfg(esp32s3)] channel4: ChannelCreator4 {}, } } }