diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index f8437c351d2..e4685941806 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Introduce DMA buffer objects (#1856) +- Introduce traits for the DMA buffer objects (#1976) - Added new `Io::new_no_bind_interrupt` constructor (#1861) - Added touch pad support for esp32 (#1873, #1956) - Allow configuration of period updating method for MCPWM timers (#1898) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 3af076e8e23..372c61606c2 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -1878,6 +1878,29 @@ where } } +/// Holds all the information need to configure a DMA channel for a transfer. +pub struct Preparation { + pub(crate) start: *mut DmaDescriptor, + pub(crate) length: usize, + // burst_mode, alignment, check_owner, etc. +} + +/// [DmaTxBuffer] is a DMA descriptor + memory combo that can be used for transmitting +/// data from a DMA channel to a peripheral's FIFO. +pub trait DmaTxBuffer { + /// Prepares the buffer for an imminent transfer and return + /// information required to use this buffer. + fn prepare(&mut self) -> Preparation; +} + +/// [DmaRxBuffer] is a DMA descriptor + memory combo that can be used for receiving +/// data from a peripheral's FIFO to a DMA channel. +pub trait DmaRxBuffer { + /// Prepares the buffer for an imminent transfer and return + /// information required to use this buffer. + fn prepare(&mut self) -> Preparation; +} + /// Error returned from Dma[Tx|Rx|TxRx]Buf operations. #[derive(Debug)] pub enum DmaBufError { @@ -2016,9 +2039,26 @@ impl DmaTxBuf { pub fn as_slice(&self) -> &[u8] { self.buffer } +} + +impl DmaTxBuffer for DmaTxBuf { + fn prepare(&mut self) -> Preparation { + // Calculate length and setup descriptor flags. + let mut length = 0; + for desc in self.descriptors.iter_mut() { + // Give ownership to the DMA + desc.set_owner(Owner::Dma); + + length += desc.len(); + if desc.next.is_null() { + break; + } + } - pub(crate) fn first(&self) -> *mut DmaDescriptor { - self.descriptors.as_ptr() as _ + Preparation { + start: self.descriptors.as_mut_ptr(), + length, + } } } @@ -2227,9 +2267,34 @@ impl DmaRxBuf { Some(chunk) }) } +} - pub(crate) fn first(&self) -> *mut DmaDescriptor { - self.descriptors.as_ptr() as _ +impl DmaRxBuffer for DmaRxBuf { + fn prepare(&mut self) -> Preparation { + // Calculate length and setup descriptor flags. + let mut length = 0; + for desc in self.descriptors.iter_mut() { + // Give ownership to the DMA + desc.set_owner(Owner::Dma); + + // Clear this to allow hardware to set it when the peripheral returns an EOF + // bit. + desc.set_suc_eof(false); + + // Clear this to allow hardware to set it when it's + // done receiving data for this descriptor. + desc.set_length(0); + + length += desc.size(); + if desc.next.is_null() { + break; + } + } + + Preparation { + start: self.descriptors.as_mut_ptr(), + length, + } } } @@ -2320,7 +2385,7 @@ impl DmaTxRxBuf { } /// Returns the entire buf as a slice than can be written. - pub fn as_slice_mut(&mut self) -> &mut [u8] { + pub fn as_mut_slice(&mut self) -> &mut [u8] { &mut self.buffer[..] } @@ -2391,6 +2456,56 @@ impl DmaTxRxBuf { } } +impl DmaTxBuffer for DmaTxRxBuf { + fn prepare(&mut self) -> Preparation { + // Calculate length and setup descriptor flags. + let mut length = 0; + for desc in self.tx_descriptors.iter_mut() { + // Give ownership to the DMA + desc.set_owner(Owner::Dma); + + length += desc.len(); + if desc.next.is_null() { + break; + } + } + + Preparation { + start: self.tx_descriptors.as_mut_ptr(), + length, + } + } +} + +impl DmaRxBuffer for DmaTxRxBuf { + fn prepare(&mut self) -> Preparation { + // Calculate length and setup descriptor flags. + let mut length = 0; + for desc in self.rx_descriptors.iter_mut() { + // Give ownership to the DMA + desc.set_owner(Owner::Dma); + + // Clear this to allow hardware to set it when the peripheral returns an EOF + // bit. + desc.set_suc_eof(false); + + // Clear this to allow hardware to set it when it's + // done receiving data for this descriptor. + desc.set_length(0); + + length += desc.size(); + if desc.next.is_null() { + break; + } + } + + Preparation { + start: self.rx_descriptors.as_mut_ptr(), + length, + } + } +} + pub(crate) mod dma_private { use super::*; diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index 651542bdc32..f9cb7803c8d 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -866,6 +866,7 @@ pub mod dma { InterruptConfigurable, Mode, }; + use crate::dma::{DmaRxBuffer, DmaTxBuffer}; pub trait WithDmaSpi2<'d, C, M, DmaMode> where @@ -1178,19 +1179,19 @@ pub mod dma { /// bytes. #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn dma_write( + pub fn dma_write( mut self, - buffer: DmaTxBuf, - ) -> Result, (Error, Self, DmaTxBuf)> + mut buffer: TX, + ) -> Result, (Error, Self, TX)> { - let bytes_to_write = buffer.len(); - if bytes_to_write > MAX_DMA_SIZE { + let preparation = buffer.prepare(); + if preparation.length > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } let result = unsafe { self.spi - .start_write_bytes_dma(buffer.first(), bytes_to_write, &mut self.channel.tx) + .start_write_bytes_dma(preparation.start, preparation.length, &mut self.channel.tx) }; if let Err(e) = result { return Err((e, self, buffer)); @@ -1206,19 +1207,19 @@ pub mod dma { /// received is 32736 bytes. #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn dma_read( + pub fn dma_read( mut self, - buffer: DmaRxBuf, - ) -> Result, (Error, Self, DmaRxBuf)> + mut buffer: RX, + ) -> Result, (Error, Self, RX)> { - let bytes_to_read = buffer.len(); - if bytes_to_read > MAX_DMA_SIZE { + let preparation = buffer.prepare(); + if preparation.length > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } let result = unsafe { self.spi - .start_read_bytes_dma(buffer.first(), bytes_to_read, &mut self.channel.rx) + .start_read_bytes_dma(preparation.start, preparation.length, &mut self.channel.rx) }; if let Err(e) = result { return Err((e, self, buffer)); @@ -1233,18 +1234,18 @@ pub mod dma { /// the SPI instance. The maximum amount of data to be /// sent/received is 32736 bytes. #[allow(clippy::type_complexity)] - pub fn dma_transfer( + pub fn dma_transfer( mut self, - tx_buffer: DmaTxBuf, - rx_buffer: DmaRxBuf, + mut tx_buffer: TX, + mut rx_buffer: RX, ) -> Result< - SpiDmaTransfer<'d, T, C, M, DmaMode, (DmaTxBuf, DmaRxBuf)>, - (Error, Self, DmaTxBuf, DmaRxBuf), + SpiDmaTransfer<'d, T, C, M, DmaMode, (TX, RX)>, + (Error, Self, TX, RX), > { - let bytes_to_write = tx_buffer.len(); - let bytes_to_read = rx_buffer.len(); + let tx_preparation = tx_buffer.prepare(); + let rx_preparation = rx_buffer.prepare(); - if bytes_to_write > MAX_DMA_SIZE || bytes_to_read > MAX_DMA_SIZE { + if tx_preparation.length > MAX_DMA_SIZE || rx_preparation.length > MAX_DMA_SIZE { return Err(( Error::MaxDmaTransferSizeExceeded, self, @@ -1255,10 +1256,10 @@ pub mod dma { let result = unsafe { self.spi.start_transfer_dma( - tx_buffer.first(), - rx_buffer.first(), - bytes_to_write, - bytes_to_read, + tx_preparation.start, + rx_preparation.start, + tx_preparation.length, + rx_preparation.length, &mut self.channel.tx, &mut self.channel.rx, ) @@ -1286,17 +1287,17 @@ pub mod dma { { #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn read( + pub fn read( mut self, data_mode: SpiDataMode, cmd: Command, address: Address, dummy: u8, - buffer: DmaRxBuf, - ) -> Result, (Error, Self, DmaRxBuf)> + mut buffer: RX, + ) -> Result, (Error, Self, RX)> { - let bytes_to_read = buffer.len(); - if bytes_to_read > MAX_DMA_SIZE { + let preparation = buffer.prepare(); + if preparation.length > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } @@ -1306,7 +1307,7 @@ pub mod dma { !address.is_none(), false, dummy != 0, - bytes_to_read == 0, + preparation.length == 0, ); self.spi .init_spi_data_mode(cmd.mode(), address.mode(), data_mode); @@ -1352,28 +1353,28 @@ pub mod dma { let result = unsafe { self.spi - .start_read_bytes_dma(buffer.first(), bytes_to_read, &mut self.channel.rx) + .start_read_bytes_dma(preparation.start, preparation.length, &mut self.channel.rx) }; if let Err(e) = result { return Err((e, self, buffer)); } - Ok(SpiDmaTransfer::new(self, buffer, bytes_to_read > 0, false)) + Ok(SpiDmaTransfer::new(self, buffer, preparation.length > 0, false)) } #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn write( + pub fn write( mut self, data_mode: SpiDataMode, cmd: Command, address: Address, dummy: u8, - buffer: DmaTxBuf, - ) -> Result, (Error, Self, DmaTxBuf)> + mut buffer: TX, + ) -> Result, (Error, Self, TX)> { - let bytes_to_write = buffer.len(); - if bytes_to_write > MAX_DMA_SIZE { + let preparation = buffer.prepare(); + if preparation.length > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } @@ -1383,7 +1384,7 @@ pub mod dma { !address.is_none(), false, dummy != 0, - bytes_to_write == 0, + preparation.length == 0, ); self.spi .init_spi_data_mode(cmd.mode(), address.mode(), data_mode); @@ -1429,13 +1430,13 @@ pub mod dma { let result = unsafe { self.spi - .start_write_bytes_dma(buffer.first(), bytes_to_write, &mut self.channel.tx) + .start_write_bytes_dma(preparation.start, preparation.length, &mut self.channel.tx) }; if let Err(e) = result { return Err((e, self, buffer)); } - Ok(SpiDmaTransfer::new(self, buffer, false, bytes_to_write > 0)) + Ok(SpiDmaTransfer::new(self, buffer, false, preparation.length > 0)) } }