From 9edd098da56be11dde2ba7cd8ecffaaea23cb030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Wed, 15 May 2024 18:42:35 +0200 Subject: [PATCH] De-duplicate DMA transfer implementations (#1550) * De-duplicate DMA transfer implementations * CHANGELOG.md * Renaming * Fix * Get rid of the lambda * Clippy --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/aes/mod.rs | 109 +++++----- esp-hal/src/dma/mod.rs | 268 +++++++++++++++++++++++-- esp-hal/src/i2s.rs | 284 ++++++++------------------- esp-hal/src/lcd_cam/cam.rs | 109 +++------- esp-hal/src/lcd_cam/lcd/i8080.rs | 77 +++----- esp-hal/src/parl_io.rs | 130 ++++++------ esp-hal/src/prelude.rs | 5 - esp-hal/src/spi/master.rs | 182 ++++++----------- esp-hal/src/spi/slave.rs | 226 ++++++--------------- examples/src/bin/spi_loopback_dma.rs | 2 +- 11 files changed, 625 insertions(+), 768 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 4d32b22e1..e7e5831ed 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Software interrupt 3 is now used instead of software interrupt 0 on the thread aware executor on multicore systems (#1485) - Timer abstraction: refactor `systimer` and `timer` modules into a common `timer` module (#1527) - Refactoring of GPIO module, have drivers for Input,Output,OutputOpenDrain, all drivers setup their GPIOs correctly (#1542) +- DMA transactions are now found in the `dma` module (#1550) ### Removed diff --git a/esp-hal/src/aes/mod.rs b/esp-hal/src/aes/mod.rs index f4428b7a9..c4a6e9011 100644 --- a/esp-hal/src/aes/mod.rs +++ b/esp-hal/src/aes/mod.rs @@ -276,12 +276,12 @@ pub mod dma { use crate::{ aes::{Key, Mode}, dma::{ + dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, AesPeripheral, Channel, ChannelTypes, - DmaError, DmaPeripheral, - DmaTransferRxTx, + DmaTransferTxRx, RxPrivate, TxPrivate, }, @@ -336,61 +336,6 @@ pub mod dma { } } - /// An in-progress DMA transfer - #[must_use] - pub struct AesDmaTransferRxTx<'t, 'd, C> - where - C: ChannelTypes, - C::P: AesPeripheral, - { - aes_dma: &'t mut AesDma<'d, C>, - } - - impl<'t, 'd, C> DmaTransferRxTx for AesDmaTransferRxTx<'t, 'd, C> - where - C: ChannelTypes, - C::P: AesPeripheral, - { - /// Wait for the DMA transfer to complete - fn wait(self) -> Result<(), DmaError> { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. - while self.aes_dma.aes.aes.state().read().state().bits() != 2 // DMA status DONE == 2 - && !self.aes_dma.channel.tx.is_done() - { - // wait until done - } - - self.aes_dma.finish_transform(); - - if self.aes_dma.channel.rx.has_error() || self.aes_dma.channel.tx.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - /// Check if the DMA transfer is complete - fn is_done(&self) -> bool { - let ch = &self.aes_dma.channel; - ch.tx.is_done() && ch.rx.is_done() - } - } - - impl<'t, 'd, C> Drop for AesDmaTransferRxTx<'t, 'd, C> - where - C: ChannelTypes, - C::P: AesPeripheral, - { - fn drop(&mut self) { - self.aes_dma - .aes - .aes - .dma_exit() - .write(|w| w.dma_exit().set_bit()); - } - } - impl<'d, C> core::fmt::Debug for AesDma<'d, C> where C: ChannelTypes, @@ -401,6 +346,50 @@ pub mod dma { } } + impl<'d, C> DmaSupport for AesDma<'d, C> + where + C: ChannelTypes, + C::P: AesPeripheral, + { + fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + while self.aes.aes.state().read().state().bits() != 2 // DMA status DONE == 2 + && !self.channel.tx.is_done() + { + // wait until done + } + + self.finish_transform(); + } + + fn peripheral_dma_stop(&mut self) { + unreachable!("unsupported") + } + } + + impl<'d, C> DmaSupportTx for AesDma<'d, C> + where + C: ChannelTypes, + C::P: AesPeripheral, + { + type TX = C::Tx<'d>; + + fn tx(&mut self) -> &mut Self::TX { + &mut self.channel.tx + } + } + + impl<'d, C> DmaSupportRx for AesDma<'d, C> + where + C: ChannelTypes, + C::P: AesPeripheral, + { + type RX = C::Rx<'d>; + + fn rx(&mut self) -> &mut Self::RX { + &mut self.channel.rx + } + } + impl<'d, C> AesDma<'d, C> where C: ChannelTypes, @@ -427,7 +416,7 @@ pub mod dma { /// Perform a DMA transfer. /// - /// This will return a [AesDmaTransferRxTx] owning the buffer(s) and the + /// This will return a [AesDmaTransfer] owning the buffer(s) and the /// AES instance. The maximum amount of data to be sent/received /// is 32736 bytes. pub fn process<'t, K, TXBUF, RXBUF>( @@ -437,7 +426,7 @@ pub mod dma { mode: Mode, cipher_mode: CipherMode, key: K, - ) -> Result, crate::dma::DmaError> + ) -> Result, crate::dma::DmaError> where K: Into, TXBUF: ReadBuffer, @@ -456,7 +445,7 @@ pub mod dma { key.into(), )?; - Ok(AesDmaTransferRxTx { aes_dma: self }) + Ok(DmaTransferTxRx::new(self)) } #[allow(clippy::too_many_arguments)] diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 9214f45b9..9fe918102 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -1494,24 +1494,264 @@ where } } -/// Trait to be implemented for an in progress dma transfer. -#[allow(drop_bounds)] -#[doc(hidden)] -pub trait DmaTransfer: Drop { - /// Wait for the transfer to finish. - fn wait(self) -> Result<(), DmaError>; - /// Check if the transfer is finished. - fn is_done(&self) -> bool; +pub(crate) mod dma_private { + use super::*; + + pub trait DmaSupport { + /// Wait until the transfer is done. + /// + /// Depending on the peripheral this might include checking the DMA + /// channel and/or the peripheral. + /// + /// After this all data should be processed by the peripheral - i.e. the + /// peripheral should have processed it's FIFO(s) + fn peripheral_wait_dma(&mut self, is_tx: bool, is_rx: bool); + + /// Only used by circular DMA transfers + fn peripheral_dma_stop(&mut self); + } + + pub trait DmaSupportTx: DmaSupport { + type TX: Tx; + + fn tx(&mut self) -> &mut Self::TX; + } + + pub trait DmaSupportRx: DmaSupport { + type RX: Rx; + + fn rx(&mut self) -> &mut Self::RX; + } } -/// Trait to be implemented for an in progress dma transfer. -#[allow(clippy::type_complexity, drop_bounds)] -#[doc(hidden)] -pub trait DmaTransferRxTx: Drop { +/// DMA transaction for TX only transfers +#[non_exhaustive] +#[must_use] +pub struct DmaTransferTx<'a, I> +where + I: dma_private::DmaSupportTx, +{ + instance: &'a mut I, +} + +impl<'a, I> DmaTransferTx<'a, I> +where + I: dma_private::DmaSupportTx, +{ + pub(crate) fn new(instance: &'a mut I) -> Self { + Self { instance } + } + /// Wait for the transfer to finish. - fn wait(self) -> Result<(), DmaError>; + pub fn wait(self) -> Result<(), DmaError> { + self.instance.peripheral_wait_dma(true, false); + + if self.instance.tx().has_error() { + Err(DmaError::DescriptorError) + } else { + Ok(()) + } + } + /// Check if the transfer is finished. - fn is_done(&self) -> bool; + pub fn is_done(&mut self) -> bool { + self.instance.tx().is_done() + } +} + +impl<'a, I> Drop for DmaTransferTx<'a, I> +where + I: dma_private::DmaSupportTx, +{ + fn drop(&mut self) { + self.instance.peripheral_wait_dma(true, false); + } +} + +/// DMA transaction for RX only transfers +#[non_exhaustive] +#[must_use] +pub struct DmaTransferRx<'a, I> +where + I: dma_private::DmaSupportRx, +{ + instance: &'a mut I, +} + +impl<'a, I> DmaTransferRx<'a, I> +where + I: dma_private::DmaSupportRx, +{ + pub(crate) fn new(instance: &'a mut I) -> Self { + Self { instance } + } + + /// Wait for the transfer to finish. + pub fn wait(self) -> Result<(), DmaError> { + self.instance.peripheral_wait_dma(false, true); + + if self.instance.rx().has_error() { + Err(DmaError::DescriptorError) + } else { + Ok(()) + } + } + + /// Check if the transfer is finished. + pub fn is_done(&mut self) -> bool { + self.instance.rx().is_done() + } +} + +impl<'a, I> Drop for DmaTransferRx<'a, I> +where + I: dma_private::DmaSupportRx, +{ + fn drop(&mut self) { + self.instance.peripheral_wait_dma(false, true); + } +} + +/// DMA transaction for TX+RX transfers +#[non_exhaustive] +#[must_use] +pub struct DmaTransferTxRx<'a, I> +where + I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, +{ + instance: &'a mut I, +} + +impl<'a, I> DmaTransferTxRx<'a, I> +where + I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, +{ + pub(crate) fn new(instance: &'a mut I) -> Self { + Self { instance } + } + + /// Wait for the transfer to finish. + pub fn wait(self) -> Result<(), DmaError> { + self.instance.peripheral_wait_dma(true, true); + + if self.instance.tx().has_error() || self.instance.rx().has_error() { + Err(DmaError::DescriptorError) + } else { + Ok(()) + } + } + + /// Check if the transfer is finished. + pub fn is_done(&mut self) -> bool { + self.instance.tx().is_done() && self.instance.rx().is_done() + } +} + +impl<'a, I> Drop for DmaTransferTxRx<'a, I> +where + I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, +{ + fn drop(&mut self) { + self.instance.peripheral_wait_dma(true, true); + } +} + +/// DMA transaction for TX only circular transfers +#[non_exhaustive] +#[must_use] +pub struct DmaTransferTxCircular<'a, I> +where + I: dma_private::DmaSupportTx, +{ + instance: &'a mut I, +} + +impl<'a, I> DmaTransferTxCircular<'a, I> +where + I: dma_private::DmaSupportTx, +{ + #[allow(unused)] // currently used by peripherals not available on all chips + pub(crate) fn new(instance: &'a mut I) -> Self { + Self { instance } + } + + /// Amount of bytes which can be pushed. + pub fn available(&mut self) -> usize { + self.instance.tx().available() + } + + /// Push bytes into the DMA buffer. + pub fn push(&mut self, data: &[u8]) -> Result { + self.instance.tx().push(data) + } + + /// Push bytes into the DMA buffer via the given closure. + /// The closure *must* return the actual number of bytes written. + /// The closure *might* get called with a slice which is smaller than the + /// total available buffer. + pub fn push_with(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> Result { + self.instance.tx().push_with(f) + } + + /// Stop the DMA transfer + #[allow(clippy::type_complexity)] + pub fn stop(self) -> Result<(), DmaError> { + self.instance.peripheral_dma_stop(); + + if self.instance.tx().has_error() { + Err(DmaError::DescriptorError) + } else { + Ok(()) + } + } +} + +impl<'a, I> Drop for DmaTransferTxCircular<'a, I> +where + I: dma_private::DmaSupportTx, +{ + fn drop(&mut self) { + self.instance.peripheral_dma_stop(); + } +} + +/// DMA transaction for RX only circular transfers +#[non_exhaustive] +#[must_use] +pub struct DmaTransferRxCircular<'a, I> +where + I: dma_private::DmaSupportRx, +{ + instance: &'a mut I, +} + +impl<'a, I> DmaTransferRxCircular<'a, I> +where + I: dma_private::DmaSupportRx, +{ + #[allow(unused)] // currently used by peripherals not available on all chips + pub(crate) fn new(instance: &'a mut I) -> Self { + Self { instance } + } + + /// Amount of bytes which can be popped + pub fn available(&mut self) -> usize { + self.instance.rx().available() + } + + /// Get available data + pub fn pop(&mut self, data: &mut [u8]) -> Result { + self.instance.rx().pop(data) + } +} + +impl<'a, I> Drop for DmaTransferRxCircular<'a, I> +where + I: dma_private::DmaSupportRx, +{ + fn drop(&mut self) { + self.instance.peripheral_dma_stop(); + } } #[cfg(feature = "async")] diff --git a/esp-hal/src/i2s.rs b/esp-hal/src/i2s.rs index 45aa9ef51..35a4d0226 100644 --- a/esp-hal/src/i2s.rs +++ b/esp-hal/src/i2s.rs @@ -76,10 +76,14 @@ use crate::dma::I2s1Peripheral; use crate::{ clock::Clocks, dma::{ + dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, Channel, ChannelTypes, DmaError, - DmaTransfer, + DmaTransferRx, + DmaTransferRxCircular, + DmaTransferTx, + DmaTransferTxCircular, I2s0Peripheral, I2sPeripheral, RxPrivate, @@ -210,93 +214,6 @@ impl DataFormat { } } -/// An in-progress DMA write transfer. -#[must_use] -pub struct I2sWriteDmaTransfer<'t, 'd, T, CH, DmaMode> -where - T: RegisterAccess, - CH: ChannelTypes, - DmaMode: Mode, -{ - i2s_tx: &'t mut I2sTx<'d, T, CH, DmaMode>, -} - -impl<'t, 'd, T, CH, DmaMode> I2sWriteDmaTransfer<'t, 'd, T, CH, DmaMode> -where - T: RegisterAccess, - CH: ChannelTypes, - DmaMode: Mode, -{ - /// Amount of bytes which can be pushed. - /// Only useful for circular DMA transfers - pub fn available(&mut self) -> usize { - self.i2s_tx.tx_channel.available() - } - - /// Push bytes into the DMA buffer. - /// Only useful for circular DMA transfers - pub fn push(&mut self, data: &[u8]) -> Result { - Ok(self.i2s_tx.tx_channel.push(data)?) - } - - /// Push bytes into the DMA buffer via the given closure. - /// The closure *must* return the actual number of bytes written. - /// The closure *might* get called with a slice which is smaller than the - /// total available buffer. Only useful for circular DMA transfers - pub fn push_with(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> Result { - Ok(self.i2s_tx.tx_channel.push_with(f)?) - } - - /// Stop for the DMA transfer and return the buffer and the - /// I2sTx instance. - #[allow(clippy::type_complexity)] - pub fn stop(self) -> Result<(), DmaError> { - T::tx_stop(); - - if self.i2s_tx.tx_channel.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } -} - -impl<'t, 'd, T, CH, DmaMode> DmaTransfer for I2sWriteDmaTransfer<'t, 'd, T, CH, DmaMode> -where - T: RegisterAccess, - CH: ChannelTypes, - DmaMode: Mode, -{ - /// Wait for the DMA transfer to complete - fn wait(self) -> Result<(), DmaError> { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. - self.i2s_tx.wait_tx_dma_done().ok(); - - if self.i2s_tx.tx_channel.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - /// Check if the DMA transfer is complete - fn is_done(&self) -> bool { - self.i2s_tx.tx_channel.is_done() - } -} - -impl<'t, 'd, T, CH, DmaMode> Drop for I2sWriteDmaTransfer<'t, 'd, T, CH, DmaMode> -where - T: RegisterAccess, - CH: ChannelTypes, - DmaMode: Mode, -{ - fn drop(&mut self) { - self.i2s_tx.wait_tx_dma_done().ok(); - } -} - /// Blocking I2s Write pub trait I2sWrite { fn write(&mut self, words: &[W]) -> Result<(), Error>; @@ -308,14 +225,12 @@ where T: RegisterAccess, CH: ChannelTypes, DmaMode: Mode, + Self: DmaSupportTx + Sized, { /// Write I2S. /// Returns [I2sWriteDmaTransfer] which represents the in-progress DMA /// transfer - fn write_dma<'t>( - &'t mut self, - words: &'t TXBUF, - ) -> Result, Error> + fn write_dma<'t>(&'t mut self, words: &'t TXBUF) -> Result, Error> where TXBUF: ReadBuffer; @@ -324,90 +239,11 @@ where fn write_dma_circular<'t>( &'t mut self, words: &'t TXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer; } -/// An in-progress DMA read transfer. -#[must_use] -pub struct I2sReadDmaTransfer<'t, 'd, T, CH, DmaMode> -where - T: RegisterAccess, - CH: ChannelTypes, - DmaMode: Mode, -{ - i2s_rx: &'t mut I2sRx<'d, T, CH, DmaMode>, -} - -impl<'t, 'd, T, CH, DmaMode> I2sReadDmaTransfer<'t, 'd, T, CH, DmaMode> -where - T: RegisterAccess, - CH: ChannelTypes, - DmaMode: Mode, -{ - /// Amount of bytes which can be popped - pub fn available(&mut self) -> usize { - self.i2s_rx.rx_channel.available() - } - - pub fn pop(&mut self, data: &mut [u8]) -> Result { - Ok(self.i2s_rx.rx_channel.pop(data)?) - } - - /// Wait for the DMA transfer to complete. - /// Length of the received data is returned - #[allow(clippy::type_complexity)] - pub fn wait_receive(self, dst: &mut [u8]) -> Result { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. - self.i2s_rx.wait_rx_dma_done().ok(); - let len = self.i2s_rx.rx_channel.drain_buffer(dst).unwrap(); - - if self.i2s_rx.rx_channel.has_error() { - Err((DmaError::DescriptorError, len)) - } else { - Ok(len) - } - } -} - -impl<'t, 'd, T, CH, DmaMode> DmaTransfer for I2sReadDmaTransfer<'t, 'd, T, CH, DmaMode> -where - T: RegisterAccess, - CH: ChannelTypes, - DmaMode: Mode, -{ - /// Wait for the DMA transfer to complete - fn wait(self) -> Result<(), DmaError> { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. - self.i2s_rx.wait_rx_dma_done().ok(); - - if self.i2s_rx.rx_channel.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - /// Check if the DMA transfer is complete - fn is_done(&self) -> bool { - self.i2s_rx.rx_channel.is_done() - } -} - -impl<'t, T, CH, DmaMode> Drop for I2sReadDmaTransfer<'t, '_, T, CH, DmaMode> -where - T: RegisterAccess, - CH: ChannelTypes, - DmaMode: Mode, -{ - fn drop(&mut self) { - self.i2s_rx.wait_rx_dma_done().ok(); - } -} - /// Blocking I2S Read pub trait I2sRead { fn read(&mut self, words: &mut [W]) -> Result<(), Error>; @@ -419,14 +255,12 @@ where T: RegisterAccess, CH: ChannelTypes, DmaMode: Mode, + Self: DmaSupportRx + Sized, { /// Read I2S. /// Returns [I2sReadDmaTransfer] which represents the in-progress DMA /// transfer - fn read_dma<'t>( - &'t mut self, - words: &'t mut RXBUF, - ) -> Result, Error> + fn read_dma<'t>(&'t mut self, words: &'t mut RXBUF) -> Result, Error> where RXBUF: WriteBuffer; @@ -436,7 +270,7 @@ where fn read_dma_circular<'t>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer; } @@ -606,6 +440,34 @@ where } } +impl<'d, T, CH, DmaMode> DmaSupport for I2sTx<'d, T, CH, DmaMode> +where + T: RegisterAccess, + CH: ChannelTypes, + DmaMode: Mode, +{ + fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + self.wait_tx_dma_done().ok(); + } + + fn peripheral_dma_stop(&mut self) { + T::tx_stop(); + } +} + +impl<'d, T, CH, DmaMode> DmaSupportTx for I2sTx<'d, T, CH, DmaMode> +where + T: RegisterAccess, + CH: ChannelTypes, + DmaMode: Mode, +{ + type TX = CH::Tx<'d>; + + fn tx(&mut self) -> &mut Self::TX { + &mut self.tx_channel + } +} + impl<'d, T, CH, DmaMode> I2sTx<'d, T, CH, DmaMode> where T: RegisterAccess, @@ -648,7 +510,7 @@ where &'t mut self, words: &'t TXBUF, circular: bool, - ) -> Result, Error> + ) -> Result<(), Error> where TXBUF: ReadBuffer, DmaMode: Mode, @@ -670,7 +532,7 @@ where // start: set I2S_TX_START T::tx_start(); - Ok(I2sWriteDmaTransfer { i2s_tx: self }) + Ok(()) } fn wait_tx_dma_done(&self) -> Result<(), Error> { @@ -704,24 +566,23 @@ where CH: ChannelTypes, DmaMode: Mode, { - fn write_dma<'t>( - &'t mut self, - words: &'t TXBUF, - ) -> Result, Error> + fn write_dma<'t>(&'t mut self, words: &'t TXBUF) -> Result, Error> where TXBUF: ReadBuffer, { - self.start_tx_transfer(words, false) + self.start_tx_transfer(words, false)?; + Ok(DmaTransferTx::new(self)) } fn write_dma_circular<'t>( &'t mut self, words: &'t TXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer, { - self.start_tx_transfer(words, true) + self.start_tx_transfer(words, true)?; + Ok(DmaTransferTxCircular::new(self)) } } @@ -748,6 +609,34 @@ where } } +impl<'d, T, CH, DmaMode> DmaSupport for I2sRx<'d, T, CH, DmaMode> +where + T: RegisterAccess, + CH: ChannelTypes, + DmaMode: Mode, +{ + fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + T::wait_for_rx_done(); + } + + fn peripheral_dma_stop(&mut self) { + T::reset_rx(); + } +} + +impl<'d, T, CH, DmaMode> DmaSupportRx for I2sRx<'d, T, CH, DmaMode> +where + T: RegisterAccess, + CH: ChannelTypes, + DmaMode: Mode, +{ + type RX = CH::Rx<'d>; + + fn rx(&mut self) -> &mut Self::RX { + &mut self.rx_channel + } +} + impl<'d, T, CH, DmaMode> I2sRx<'d, T, CH, DmaMode> where T: RegisterAccess, @@ -792,7 +681,7 @@ where &'t mut self, words: &'t mut RXBUF, circular: bool, - ) -> Result, Error> + ) -> Result<(), Error> where RXBUF: WriteBuffer, { @@ -818,13 +707,6 @@ where // start: set I2S_RX_START T::rx_start(len); - - Ok(I2sReadDmaTransfer { i2s_rx: self }) - } - - fn wait_rx_dma_done(&self) -> Result<(), Error> { - T::wait_for_rx_done(); - Ok(()) } } @@ -855,25 +737,25 @@ where T: RegisterAccess, CH: ChannelTypes, DmaMode: Mode, + Self: DmaSupportRx + Sized, { - fn read_dma<'t>( - &'t mut self, - words: &'t mut RXBUF, - ) -> Result, Error> + fn read_dma<'t>(&'t mut self, words: &'t mut RXBUF) -> Result, Error> where RXBUF: WriteBuffer, { - self.start_rx_transfer(words, false) + self.start_rx_transfer(words, false)?; + Ok(DmaTransferRx::new(self)) } fn read_dma_circular<'t>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer, { - self.start_rx_transfer(words, true) + self.start_rx_transfer(words, true)?; + Ok(DmaTransferRxCircular::new(self)) } } diff --git a/esp-hal/src/lcd_cam/cam.rs b/esp-hal/src/lcd_cam/cam.rs index 98da53c8a..110244ba5 100644 --- a/esp-hal/src/lcd_cam/cam.rs +++ b/esp-hal/src/lcd_cam/cam.rs @@ -42,11 +42,13 @@ use fugit::HertzU32; use crate::{ clock::Clocks, dma::{ + dma_private::{DmaSupport, DmaSupportRx}, ChannelRx, ChannelTypes, DmaError, DmaPeripheral, - DmaTransfer, + DmaTransferRx, + DmaTransferRxCircular, LcdCamPeripheral, RegisterAccess, Rx, @@ -54,7 +56,6 @@ use crate::{ RxPrivate, }, gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, - i2s::Error, lcd_cam::{cam::private::RxPins, private::calculate_clkm, BitOrder, ByteOrder}, peripheral::{Peripheral, PeripheralRef}, peripherals::LCD_CAM, @@ -177,6 +178,31 @@ where } } +impl<'d, RX: Rx> DmaSupport for Camera<'d, RX> { + fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + while ! + // Wait for IN_SUC_EOF (i.e. VSYNC) + self.rx_channel.is_done() || + // Or for IN_DSCR_EMPTY (i.e. No more buffer space) + self.rx_channel.has_dscr_empty_error() || + // Or for IN_DSCR_ERR (i.e. bad descriptor) + self.rx_channel.has_error() + {} + } + + fn peripheral_dma_stop(&mut self) { + // TODO: Stop DMA?? self.instance.rx_channel.stop_transfer(); + } +} + +impl<'d, RX: Rx> DmaSupportRx for Camera<'d, RX> { + type RX = RX; + + fn rx(&mut self) -> &mut Self::RX { + &mut self.rx_channel + } +} + impl<'d, RX: Rx> Camera<'d, RX> { pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self { self.lcd_cam @@ -295,96 +321,25 @@ impl<'d, RX: Rx> Camera<'d, RX> { pub fn read_dma<'t, RXBUF: WriteBuffer>( &'t mut self, buf: &'t mut RXBUF, - ) -> Result, DmaError> { + ) -> Result, DmaError> { self.reset_unit_and_fifo(); // Start DMA to receive incoming transfer. self.start_dma(false, buf)?; self.start_unit(); - Ok(Transfer { instance: self }) + Ok(DmaTransferRx::new(self)) } pub fn read_dma_circular<'t, RXBUF: WriteBuffer>( &'t mut self, buf: &'t mut RXBUF, - ) -> Result, DmaError> { + ) -> Result, DmaError> { self.reset_unit_and_fifo(); // Start DMA to receive incoming transfer. self.start_dma(true, buf)?; self.start_unit(); - Ok(Transfer { instance: self }) - } -} - -/// An in-progress transfer -#[must_use] -pub struct Transfer<'t, 'd, RX: Rx> { - instance: &'t mut Camera<'d, RX>, -} - -impl<'t, 'd, RX: Rx> Transfer<'t, 'd, RX> { - /// Amount of bytes which can be popped - pub fn available(&mut self) -> usize { - self.instance.rx_channel.available() - } - - pub fn pop(&mut self, data: &mut [u8]) -> Result { - Ok(self.instance.rx_channel.pop(data)?) - } - - /// Wait for the DMA transfer to complete. - /// Length of the received data is returned - #[allow(clippy::type_complexity)] - pub fn wait_receive(self, dst: &mut [u8]) -> Result { - // Wait for DMA transfer to finish. - while !self.is_done() {} - - let len = self - .instance - .rx_channel - .drain_buffer(dst) - .map_err(|e| (e, 0))?; - - if self.instance.rx_channel.has_error() { - Err((DmaError::DescriptorError, len)) - } else { - Ok(len) - } - } -} - -impl<'t, 'd, RX: Rx> DmaTransfer for Transfer<'t, 'd, RX> { - fn wait(self) -> Result<(), DmaError> { - // Wait for DMA transfer to finish. - while !self.is_done() {} - - let ch = &self.instance.rx_channel; - if ch.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - fn is_done(&self) -> bool { - let ch = &self.instance.rx_channel; - // Wait for IN_SUC_EOF (i.e. VSYNC) - ch.is_done() || - // Or for IN_DSCR_EMPTY (i.e. No more buffer space) - ch.has_dscr_empty_error() || - // Or for IN_DSCR_ERR (i.e. bad descriptor) - ch.has_error() - } -} - -impl<'t, 'd, RX: Rx> Drop for Transfer<'t, 'd, RX> { - fn drop(&mut self) { - self.instance - .lcd_cam - .cam_ctrl1() - .modify(|_, w| w.cam_start().clear_bit()); - // TODO: Stop DMA?? self.instance.rx_channel.stop_transfer(); + Ok(DmaTransferRxCircular::new(self)) } } diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 855fcaab5..2e9645c01 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -43,10 +43,12 @@ use fugit::HertzU32; use crate::{ clock::Clocks, dma::{ + dma_private::{DmaSupport, DmaSupportTx}, ChannelTx, ChannelTypes, DmaError, DmaPeripheral, + DmaTransferTx, LcdCamPeripheral, RegisterAccess, Tx, @@ -229,6 +231,27 @@ where } } +impl<'d, TX: Tx, P: TxPins> DmaSupport for I8080<'d, TX, P> { + fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + let dma_int_raw = self.lcd_cam.lc_dma_int_raw(); + // Wait until LCD_TRANS_DONE is set. + while dma_int_raw.read().lcd_trans_done_int_raw().bit_is_clear() {} + self.tear_down_send(); + } + + fn peripheral_dma_stop(&mut self) { + unreachable!("unsupported") + } +} + +impl<'d, TX: Tx, P: TxPins> DmaSupportTx for I8080<'d, TX, P> { + type TX = TX; + + fn tx(&mut self) -> &mut Self::TX { + &mut self.tx_channel + } +} + impl<'d, TX: Tx, P: TxPins> I8080<'d, TX, P> where P::Word: Into, @@ -301,7 +324,7 @@ where cmd: impl Into>, dummy: u8, data: &'t TXBUF, - ) -> Result, DmaError> + ) -> Result, DmaError> where TXBUF: ReadBuffer, { @@ -311,9 +334,7 @@ where self.start_write_bytes_dma(ptr as _, len * size_of::())?; self.start_send(); - Ok(Transfer { - instance: Some(self), - }) + Ok(DmaTransferTx::new(self)) } } @@ -434,54 +455,6 @@ impl<'d, TX, P> core::fmt::Debug for I8080<'d, TX, P> { } } -/// An in-progress transfer -#[must_use] -pub struct Transfer<'t, 'd, TX: Tx, P> { - instance: Option<&'t mut I8080<'d, TX, P>>, -} - -impl<'t, 'd, TX: Tx, P> Transfer<'t, 'd, TX, P> { - #[allow(clippy::type_complexity)] - pub fn wait(mut self) -> Result<(), DmaError> { - let instance = self - .instance - .take() - .expect("instance must be available throughout object lifetime"); - - { - let dma_int_raw = instance.lcd_cam.lc_dma_int_raw(); - // Wait until LCD_TRANS_DONE is set. - while dma_int_raw.read().lcd_trans_done_int_raw().bit_is_clear() {} - instance.tear_down_send(); - } - - if instance.tx_channel.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - pub fn is_done(&self) -> bool { - let int_raw = self - .instance - .as_ref() - .expect("instance must be available throughout object lifetime") - .lcd_cam - .lc_dma_int_raw(); - int_raw.read().lcd_trans_done_int_raw().bit_is_set() - } -} - -impl<'t, 'd, TX: Tx, P> Drop for Transfer<'t, 'd, TX, P> { - fn drop(&mut self) { - if let Some(instance) = self.instance.as_mut() { - // This will cancel the transfer. - instance.tear_down_send(); - } - } -} - #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { diff --git a/esp-hal/src/parl_io.rs b/esp-hal/src/parl_io.rs index 43526f93c..69433a2ec 100644 --- a/esp-hal/src/parl_io.rs +++ b/esp-hal/src/parl_io.rs @@ -88,7 +88,18 @@ use private::*; use crate::{ clock::Clocks, - dma::{Channel, ChannelTypes, DmaError, DmaPeripheral, ParlIoPeripheral, RxPrivate, TxPrivate}, + dma::{ + dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, + Channel, + ChannelTypes, + DmaError, + DmaPeripheral, + DmaTransferRx, + DmaTransferTx, + ParlIoPeripheral, + RxPrivate, + TxPrivate, + }, gpio::{InputPin, OutputPin}, interrupt::InterruptHandler, peripheral::{self, Peripheral}, @@ -1417,7 +1428,7 @@ where pub fn write_dma<'t, TXBUF>( &'t mut self, words: &'t TXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer, { @@ -1429,7 +1440,7 @@ where self.start_write_bytes_dma(ptr, len)?; - Ok(DmaTransfer { instance: self }) + Ok(DmaTransferTx::new(self)) } fn start_write_bytes_dma(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { @@ -1459,47 +1470,37 @@ where } } -/// An in-progress DMA transfer. -#[must_use] -pub struct DmaTransfer<'t, 'd, C, P, CP, DM> +impl<'d, CH, P, CP, DM> DmaSupport for ParlIoTx<'d, CH, P, CP, DM> where - C: ChannelTypes, - C::P: ParlIoPeripheral, + CH: ChannelTypes, + CH::P: ParlIoPeripheral, P: TxPins + ConfigurePins, CP: TxClkPin, DM: Mode, { - instance: &'t mut ParlIoTx<'d, C, P, CP, DM>, -} - -impl<'t, 'd, C, P, CP, DM> DmaTransfer<'t, 'd, C, P, CP, DM> -where - C: ChannelTypes, - C::P: ParlIoPeripheral, - P: TxPins + ConfigurePins, - CP: TxClkPin, - DM: Mode, -{ - /// Wait for the DMA transfer to complete - #[allow(clippy::type_complexity)] - pub fn wait(self) -> Result<(), DmaError> { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. + fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { while !Instance::is_tx_eof() {} Instance::set_tx_start(false); - - if self.instance.tx_channel.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } } - /// Check if the DMA transfer is complete - pub fn is_done(&self) -> bool { - let ch = &self.instance.tx_channel; - ch.is_done() + fn peripheral_dma_stop(&mut self) { + unreachable!("unsupported") + } +} + +impl<'d, CH, P, CP, DM> DmaSupportTx for ParlIoTx<'d, CH, P, CP, DM> +where + CH: ChannelTypes, + CH::P: ParlIoPeripheral, + P: TxPins + ConfigurePins, + CP: TxClkPin, + DM: Mode, +{ + type TX = CH::Tx<'d>; + + fn tx(&mut self) -> &mut Self::TX { + &mut self.tx_channel } } @@ -1522,7 +1523,7 @@ where pub fn read_dma<'t, RXBUF>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer, { @@ -1534,7 +1535,7 @@ where Self::start_receive_bytes_dma(&mut self.rx_channel, ptr, len)?; - Ok(RxDmaTransfer { instance: self }) + Ok(DmaTransferRx::new(self)) } fn start_receive_bytes_dma( @@ -1564,55 +1565,44 @@ where } } -/// An in-progress DMA transfer. -pub struct RxDmaTransfer<'t, 'd, C, P, CP, DM> +impl<'d, CH, P, CP, DM> DmaSupport for ParlIoRx<'d, CH, P, CP, DM> where - C: ChannelTypes, - C::P: ParlIoPeripheral, + CH: ChannelTypes, + CH::P: ParlIoPeripheral, P: RxPins + ConfigurePins, CP: RxClkPin, DM: Mode, { - instance: &'t mut ParlIoRx<'d, C, P, CP, DM>, -} - -impl<'t, 'd, C, P, CP, DM> RxDmaTransfer<'t, 'd, C, P, CP, DM> -where - C: ChannelTypes, - C::P: ParlIoPeripheral, - P: RxPins + ConfigurePins, - CP: RxClkPin, - DM: Mode, -{ - /// Wait for the DMA transfer to complete - #[allow(clippy::type_complexity)] - pub fn wait(self) -> Result<(), DmaError> { + fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { loop { - if self.is_done() || self.is_eof_error() { + if self.rx_channel.is_done() + || self.rx_channel.has_eof_error() + || self.rx_channel.has_dscr_empty_error() + { break; } } Instance::set_rx_start(false); - - if self.instance.rx_channel.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } } - /// Check if the DMA transfer is complete - pub fn is_done(&self) -> bool { - let ch = &self.instance.rx_channel; - ch.is_done() + fn peripheral_dma_stop(&mut self) { + unreachable!("unsupported") } +} - /// Check if the DMA transfer is completed by buffer full or source EOF - /// error - pub fn is_eof_error(&self) -> bool { - let ch = &self.instance.rx_channel; - ch.has_eof_error() || ch.has_dscr_empty_error() +impl<'d, CH, P, CP, DM> DmaSupportRx for ParlIoRx<'d, CH, P, CP, DM> +where + CH: ChannelTypes, + CH::P: ParlIoPeripheral, + P: RxPins + ConfigurePins, + CP: RxClkPin, + DM: Mode, +{ + type RX = CH::Rx<'d>; + + fn rx(&mut self) -> &mut Self::RX { + &mut self.rx_channel } } diff --git a/esp-hal/src/prelude.rs b/esp-hal/src/prelude.rs index 8be8fb9eb..83d5ba67d 100644 --- a/esp-hal/src/prelude.rs +++ b/esp-hal/src/prelude.rs @@ -15,11 +15,6 @@ pub use nb; #[cfg(any(dport, pcr, system))] pub use crate::clock::Clock as _esp_hal_clock_Clock; -#[cfg(any(gdma, pdma))] -pub use crate::dma::{ - DmaTransfer as _esp_hal_dma_DmaTransfer, - DmaTransferRxTx as _esp_hal_dma_DmaTransferRxTx, -}; #[cfg(gpio)] pub use crate::gpio::{ InputPin as _esp_hal_gpio_InputPin, diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index 844b81913..543268776 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -837,12 +837,12 @@ pub mod dma { use crate::dma::Spi3Peripheral; use crate::{ dma::{ + dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, Channel, ChannelTypes, - DmaError, - DmaTransfer, - DmaTransferRxTx, - RxPrivate, + DmaTransferRx, + DmaTransferTx, + DmaTransferTxRx, Spi2Peripheral, SpiPeripheral, TxPrivate, @@ -919,113 +919,6 @@ pub mod dma { } } } - /// An in-progress DMA transfer - #[must_use] - pub struct SpiDmaTransferRxTx<'t, 'd, T, C, M, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, - { - spi_dma: &'t mut SpiDma<'d, T, C, M, DmaMode>, - } - - impl<'t, 'd, T, C, M, DmaMode> DmaTransferRxTx for SpiDmaTransferRxTx<'t, 'd, T, C, M, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, - { - /// Wait for the DMA transfer to complete - fn wait(self) -> Result<(), DmaError> { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. - self.spi_dma.spi.flush().ok(); - - if self.spi_dma.channel.rx.has_error() || self.spi_dma.channel.tx.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - /// Check if the DMA transfer is complete - fn is_done(&self) -> bool { - let ch = &self.spi_dma.channel; - ch.tx.is_done() && ch.rx.is_done() - } - } - - impl<'t, 'd, T, C, M, DmaMode> Drop for SpiDmaTransferRxTx<'t, 'd, T, C, M, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, - { - fn drop(&mut self) { - self.spi_dma.spi.flush().ok(); - } - } - - /// An in-progress DMA transfer. - #[must_use] - pub struct SpiDmaTransfer<'t, 'd, T, C, M, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, - { - spi_dma: &'t mut SpiDma<'d, T, C, M, DmaMode>, - } - - impl<'t, 'd, T, C, M, DmaMode> DmaTransfer for SpiDmaTransfer<'t, 'd, T, C, M, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, - { - /// Wait for the DMA transfer to complete - fn wait(self) -> Result<(), DmaError> { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. - self.spi_dma.spi.flush().ok(); - - if self.spi_dma.channel.rx.has_error() || self.spi_dma.channel.tx.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - /// Check if the DMA transfer is complete - fn is_done(&self) -> bool { - let ch = &self.spi_dma.channel; - ch.tx.is_done() && ch.rx.is_done() - } - } - - impl<'t, 'd, T, C, M, DmaMode> Drop for SpiDmaTransfer<'t, 'd, T, C, M, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - M: DuplexMode, - DmaMode: Mode, - { - fn drop(&mut self) { - self.spi_dma.spi.flush().ok(); - } - } /// A DMA capable SPI instance. pub struct SpiDma<'d, T, C, M, DmaMode> @@ -1106,6 +999,53 @@ pub mod dma { } } + impl<'d, T, C, M, DmaMode> DmaSupport for SpiDma<'d, T, C, M, DmaMode> + where + T: InstanceDma, C::Rx<'d>>, + C: ChannelTypes, + C::P: SpiPeripheral, + M: DuplexMode, + DmaMode: Mode, + { + fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { + self.spi.flush().ok(); + } + + fn peripheral_dma_stop(&mut self) { + unreachable!("unsupported") + } + } + + impl<'d, T, C, M, DmaMode> DmaSupportTx for SpiDma<'d, T, C, M, DmaMode> + where + T: InstanceDma, C::Rx<'d>>, + C: ChannelTypes, + C::P: SpiPeripheral, + M: DuplexMode, + DmaMode: Mode, + { + type TX = C::Tx<'d>; + + fn tx(&mut self) -> &mut Self::TX { + &mut self.channel.tx + } + } + + impl<'d, T, C, M, DmaMode> DmaSupportRx for SpiDma<'d, T, C, M, DmaMode> + where + T: InstanceDma, C::Rx<'d>>, + C: ChannelTypes, + C::P: SpiPeripheral, + M: DuplexMode, + DmaMode: Mode, + { + type RX = C::Rx<'d>; + + fn rx(&mut self) -> &mut Self::RX { + &mut self.channel.rx + } + } + impl<'d, T, C, M, DmaMode> SpiDma<'d, T, C, M, DmaMode> where T: InstanceDma, C::Rx<'d>>, @@ -1123,7 +1063,7 @@ pub mod dma { pub fn dma_write<'t, TXBUF>( &'t mut self, words: &'t TXBUF, - ) -> Result, super::Error> + ) -> Result, super::Error> where TXBUF: ReadBuffer, { @@ -1135,7 +1075,7 @@ pub mod dma { self.spi .start_write_bytes_dma(ptr, len, &mut self.channel.tx, false)?; - Ok(SpiDmaTransfer { spi_dma: self }) + Ok(DmaTransferTx::new(self)) } /// Perform a DMA read. @@ -1147,7 +1087,7 @@ pub mod dma { pub fn dma_read<'t, RXBUF>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, super::Error> + ) -> Result, super::Error> where RXBUF: WriteBuffer, { @@ -1161,7 +1101,7 @@ pub mod dma { self.spi .start_read_bytes_dma(ptr, len, &mut self.channel.rx, false)?; } - Ok(SpiDmaTransfer { spi_dma: self }) + Ok(DmaTransferRx::new(self)) } /// Perform a DMA transfer. @@ -1173,7 +1113,7 @@ pub mod dma { &'t mut self, words: &'t TXBUF, read_buffer: &'t mut RXBUF, - ) -> Result, super::Error> + ) -> Result, super::Error> where TXBUF: ReadBuffer, RXBUF: WriteBuffer, @@ -1195,7 +1135,7 @@ pub mod dma { &mut self.channel.rx, )?; } - Ok(SpiDmaTransferRxTx { spi_dma: self }) + Ok(DmaTransferTxRx::new(self)) } } @@ -1215,7 +1155,7 @@ pub mod dma { address: Address, dummy: u8, buffer: &'t mut RXBUF, - ) -> Result, super::Error> + ) -> Result, super::Error> where RXBUF: WriteBuffer, { @@ -1279,7 +1219,7 @@ pub mod dma { self.spi .start_read_bytes_dma(ptr, len, &mut self.channel.rx, false)?; } - Ok(SpiDmaTransfer { spi_dma: self }) + Ok(DmaTransferRx::new(self)) } #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] @@ -1290,7 +1230,7 @@ pub mod dma { address: Address, dummy: u8, buffer: &'t TXBUF, - ) -> Result, super::Error> + ) -> Result, super::Error> where TXBUF: ReadBuffer, { @@ -1352,7 +1292,7 @@ pub mod dma { self.spi .start_write_bytes_dma(ptr, len, &mut self.channel.tx, false)?; - Ok(SpiDmaTransfer { spi_dma: self }) + Ok(DmaTransferTx::new(self)) } } diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 94b9b03ec..648184ab9 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -134,11 +134,12 @@ pub mod dma { use crate::dma::Spi3Peripheral; use crate::{ dma::{ + dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, Channel, ChannelTypes, - DmaError, - DmaTransfer, - DmaTransferRxTx, + DmaTransferRx, + DmaTransferTx, + DmaTransferTxRx, RxPrivate, Spi2Peripheral, SpiPeripheral, @@ -228,164 +229,6 @@ pub mod dma { } } } - /// An in-progress DMA transfer - #[must_use] - pub struct SpiDmaTransferRxTx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - spi_dma: &'t mut SpiDma<'d, T, C, DmaMode>, - } - - impl<'t, 'd, T, C, DmaMode> DmaTransferRxTx for SpiDmaTransferRxTx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - /// Wait for the DMA transfer to complete - fn wait(self) -> Result<(), DmaError> { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. - while !self.is_done() {} - self.spi_dma.spi.flush().ok(); - - if self.spi_dma.channel.rx.has_error() || self.spi_dma.channel.tx.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - /// Check if the DMA transfer is complete - fn is_done(&self) -> bool { - let ch = &self.spi_dma.channel; - ch.tx.is_done() && ch.rx.is_done() && !self.spi_dma.spi.is_bus_busy() - } - } - - impl<'t, 'd, T, C, DmaMode> Drop for SpiDmaTransferRxTx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - fn drop(&mut self) { - while !self.is_done() {} - self.spi_dma.spi.flush().ok(); - } - } - - /// An in-progress DMA transfer. - #[must_use] - pub struct SpiDmaTransferRx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - spi_dma: &'t mut SpiDma<'d, T, C, DmaMode>, - } - - impl<'t, 'd, T, C, DmaMode> DmaTransfer for SpiDmaTransferRx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - /// Wait for the DMA transfer to complete - fn wait(self) -> Result<(), DmaError> { - while !self.is_done() {} - self.spi_dma.spi.flush().ok(); // waiting for the DMA transfer is not enough - - if self.spi_dma.channel.rx.has_error() || self.spi_dma.channel.tx.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - /// Check if the DMA transfer is complete - fn is_done(&self) -> bool { - let ch = &self.spi_dma.channel; - ch.rx.is_done() - } - } - - impl<'t, 'd, T, C, DmaMode> Drop for SpiDmaTransferRx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - fn drop(&mut self) { - while !self.is_done() {} - self.spi_dma.spi.flush().ok(); // waiting for the DMA transfer is - // not enough - } - } - - /// An in-progress DMA transfer. - #[must_use] - pub struct SpiDmaTransferTx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - spi_dma: &'t mut SpiDma<'d, T, C, DmaMode>, - } - - impl<'t, 'd, T, C, DmaMode> DmaTransfer for SpiDmaTransferTx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - /// Wait for the DMA transfer to complete - fn wait(self) -> Result<(), DmaError> { - // Waiting for the DMA transfer is not enough. We need to wait for the - // peripheral to finish flushing its buffers, too. - while !self.is_done() {} - self.spi_dma.spi.flush().ok(); - - if self.spi_dma.channel.rx.has_error() || self.spi_dma.channel.tx.has_error() { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - /// Check if the DMA transfer is complete - fn is_done(&self) -> bool { - let ch = &self.spi_dma.channel; - ch.tx.is_done() - } - } - - impl<'t, 'd, T, C, DmaMode> Drop for SpiDmaTransferTx<'t, 'd, T, C, DmaMode> - where - T: InstanceDma, C::Rx<'d>>, - C: ChannelTypes, - C::P: SpiPeripheral, - DmaMode: Mode, - { - fn drop(&mut self) { - while !self.is_done() {} - self.spi_dma.spi.flush().ok(); // waiting for the DMA transfer is - // not enough - } - } /// A DMA capable SPI instance. pub struct SpiDma<'d, T, C, DmaMode> @@ -409,6 +252,55 @@ pub mod dma { } } + impl<'d, T, C, DmaMode> DmaSupport for SpiDma<'d, T, C, DmaMode> + where + T: InstanceDma, C::Rx<'d>>, + C: ChannelTypes, + C::P: SpiPeripheral, + DmaMode: Mode, + { + fn peripheral_wait_dma(&mut self, is_tx: bool, is_rx: bool) { + while !((!is_tx || self.channel.tx.is_done()) + && (!is_rx || self.channel.rx.is_done()) + && !self.spi.is_bus_busy()) + {} + + self.spi.flush().ok(); + } + + fn peripheral_dma_stop(&mut self) { + unreachable!("unsupported") + } + } + + impl<'d, T, C, DmaMode> DmaSupportTx for SpiDma<'d, T, C, DmaMode> + where + T: InstanceDma, C::Rx<'d>>, + C: ChannelTypes, + C::P: SpiPeripheral, + DmaMode: Mode, + { + type TX = C::Tx<'d>; + + fn tx(&mut self) -> &mut Self::TX { + &mut self.channel.tx + } + } + + impl<'d, T, C, DmaMode> DmaSupportRx for SpiDma<'d, T, C, DmaMode> + where + T: InstanceDma, C::Rx<'d>>, + C: ChannelTypes, + C::P: SpiPeripheral, + DmaMode: Mode, + { + type RX = C::Rx<'d>; + + fn rx(&mut self) -> &mut Self::RX { + &mut self.channel.rx + } + } + impl<'d, T, C, DmaMode> SpiDma<'d, T, C, DmaMode> where T: InstanceDma, C::Rx<'d>>, @@ -426,7 +318,7 @@ pub mod dma { pub fn dma_write<'t, TXBUF>( &'t mut self, words: &'t TXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer, { @@ -438,7 +330,7 @@ pub mod dma { self.spi .start_write_bytes_dma(ptr, len, &mut self.channel.tx) - .map(move |_| SpiDmaTransferTx { spi_dma: self }) + .map(move |_| DmaTransferTx::new(self)) } /// Register a buffer for a DMA read. @@ -451,7 +343,7 @@ pub mod dma { pub fn dma_read<'t, RXBUF>( &'t mut self, words: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer, { @@ -464,7 +356,7 @@ pub mod dma { unsafe { self.spi .start_read_bytes_dma(ptr, len, &mut self.channel.rx) - .map(move |_| SpiDmaTransferRx { spi_dma: self }) + .map(move |_| DmaTransferRx::new(self)) } } @@ -480,7 +372,7 @@ pub mod dma { &'t mut self, words: &'t TXBUF, read_buffer: &'t mut RXBUF, - ) -> Result, Error> + ) -> Result, Error> where TXBUF: ReadBuffer, RXBUF: WriteBuffer, @@ -502,7 +394,7 @@ pub mod dma { &mut self.channel.tx, &mut self.channel.rx, ) - .map(move |_| SpiDmaTransferRxTx { spi_dma: self }) + .map(move |_| DmaTransferTxRx::new(self)) } } } diff --git a/examples/src/bin/spi_loopback_dma.rs b/examples/src/bin/spi_loopback_dma.rs index e5f55c3fe..1698db802 100644 --- a/examples/src/bin/spi_loopback_dma.rs +++ b/examples/src/bin/spi_loopback_dma.rs @@ -81,7 +81,7 @@ fn main() -> ! { send[send.len() - 1] = i; i = i.wrapping_add(1); - let transfer = spi.dma_transfer(&mut send, &mut receive).unwrap(); + let mut transfer = spi.dma_transfer(&mut send, &mut receive).unwrap(); // here we could do something else while DMA transfer is in progress let mut n = 0; // Check is_done until the transfer is almost done (32000 bytes at 100kHz is