Additional move base dma api (#1672)

* Additional owning DMA transfers for SPI half-duplex

* CHANGELOG.md

* CHANGELOG.md

* Clippy
This commit is contained in:
Björn Quentin 2024-06-19 15:17:22 +02:00 committed by GitHub
parent 1630868d06
commit d34a872230
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 363 additions and 7 deletions

View File

@ -12,9 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- uart: Constructors now require TX and RX pins (#1592) - uart: Constructors now require TX and RX pins (#1592)
- uart: Added `Uart::new_with_default_pins` constructor (#1592) - uart: Added `Uart::new_with_default_pins` constructor (#1592)
- uart: Added `UartTx` and `UartRx` constructors (#1592) - uart: Added `UartTx` and `UartRx` constructors (#1592)
- Add Flex / AnyFlex GPIO pin driver (#1659) - Add Flex / AnyFlex GPIO pin driver (#1659)
- Add new `DmaError::UnsupportedMemoryRegion` - used memory regions are checked when preparing a transfer now (#1670) - Add new `DmaError::UnsupportedMemoryRegion` - used memory regions are checked when preparing a transfer now (#1670)
- Add DmaTransactionTxOwned, DmaTransactionRxOwned, DmaTransactionTxRxOwned, functions to do owning transfers added to SPI half-duplex (#1672)
### Fixed ### Fixed

View File

@ -113,6 +113,7 @@ impl DmaDescriptor {
} }
} }
use embedded_dma::{ReadBuffer, WriteBuffer};
use enumset::{EnumSet, EnumSetType}; use enumset::{EnumSet, EnumSetType};
#[cfg(gdma)] #[cfg(gdma)]
@ -1690,6 +1691,226 @@ where
} }
} }
/// DMA transaction for TX transfers with moved-in/moved-out peripheral and
/// buffer
#[non_exhaustive]
#[must_use]
pub struct DmaTransferTxOwned<I, T>
where
I: dma_private::DmaSupportTx,
T: ReadBuffer<Word = u8>,
{
instance: I,
tx_buffer: T,
}
impl<I, T> DmaTransferTxOwned<I, T>
where
I: dma_private::DmaSupportTx,
T: ReadBuffer<Word = u8>,
{
pub(crate) fn new(instance: I, tx_buffer: T) -> Self {
Self {
instance,
tx_buffer,
}
}
/// Wait for the transfer to finish and return the peripheral and the
/// buffer.
pub fn wait(mut self) -> Result<(I, T), (DmaError, I, T)> {
self.instance.peripheral_wait_dma(true, false);
let err = self.instance.tx().has_error();
// We need to have a `Drop` implementation, because we accept
// managed buffers that can free their memory on drop. Because of that
// we can't move out of the `Transfer`'s fields, so we use `ptr::read`
// and `mem::forget`.
//
// NOTE(unsafe) There is no panic branch between getting the resources
// and forgetting `self`.
let (instance, tx_buffer) = unsafe {
let instance = core::ptr::read(&self.instance);
let tx_buffer = core::ptr::read(&self.tx_buffer);
core::mem::forget(self);
(instance, tx_buffer)
};
if err {
Err((DmaError::DescriptorError, instance, tx_buffer))
} else {
Ok((instance, tx_buffer))
}
}
/// Check if the transfer is finished.
pub fn is_done(&mut self) -> bool {
self.instance.tx().is_done()
}
}
impl<I, T> Drop for DmaTransferTxOwned<I, T>
where
I: dma_private::DmaSupportTx,
T: ReadBuffer<Word = u8>,
{
fn drop(&mut self) {
self.instance.peripheral_wait_dma(true, false);
}
}
/// DMA transaction for RX transfers with moved-in/moved-out peripheral and
/// buffer
#[non_exhaustive]
#[must_use]
pub struct DmaTransferRxOwned<I, R>
where
I: dma_private::DmaSupportRx,
R: WriteBuffer<Word = u8>,
{
instance: I,
rx_buffer: R,
}
impl<I, R> DmaTransferRxOwned<I, R>
where
I: dma_private::DmaSupportRx,
R: WriteBuffer<Word = u8>,
{
pub(crate) fn new(instance: I, rx_buffer: R) -> Self {
Self {
instance,
rx_buffer,
}
}
/// Wait for the transfer to finish and return the peripheral and the
/// buffers.
pub fn wait(mut self) -> Result<(I, R), (DmaError, I, R)> {
self.instance.peripheral_wait_dma(false, true);
let err = self.instance.rx().has_error();
// We need to have a `Drop` implementation, because we accept
// managed buffers that can free their memory on drop. Because of that
// we can't move out of the `Transfer`'s fields, so we use `ptr::read`
// and `mem::forget`.
//
// NOTE(unsafe) There is no panic branch between getting the resources
// and forgetting `self`.
let (instance, rx_buffer) = unsafe {
let instance = core::ptr::read(&self.instance);
let rx_buffer = core::ptr::read(&self.rx_buffer);
core::mem::forget(self);
(instance, rx_buffer)
};
if err {
Err((DmaError::DescriptorError, instance, rx_buffer))
} else {
Ok((instance, rx_buffer))
}
}
/// Check if the transfer is finished.
pub fn is_done(&mut self) -> bool {
self.instance.rx().is_done()
}
}
impl<I, R> Drop for DmaTransferRxOwned<I, R>
where
I: dma_private::DmaSupportRx,
R: WriteBuffer<Word = u8>,
{
fn drop(&mut self) {
self.instance.peripheral_wait_dma(false, true);
}
}
/// DMA transaction for TX+RX transfers with moved-in/moved-out peripheral and
/// buffers
#[non_exhaustive]
#[must_use]
pub struct DmaTransferTxRxOwned<I, T, R>
where
I: dma_private::DmaSupportTx + dma_private::DmaSupportRx,
T: ReadBuffer<Word = u8>,
R: WriteBuffer<Word = u8>,
{
instance: I,
tx_buffer: T,
rx_buffer: R,
}
impl<I, T, R> DmaTransferTxRxOwned<I, T, R>
where
I: dma_private::DmaSupportTx + dma_private::DmaSupportRx,
T: ReadBuffer<Word = u8>,
R: WriteBuffer<Word = u8>,
{
pub(crate) fn new(instance: I, tx_buffer: T, rx_buffer: R) -> Self {
Self {
instance,
tx_buffer,
rx_buffer,
}
}
/// Wait for the transfer to finish and return the peripheral and the
/// buffers.
#[allow(clippy::type_complexity)]
pub fn wait(mut self) -> Result<(I, T, R), (DmaError, I, T, R)> {
self.instance.peripheral_wait_dma(true, true);
let err = self.instance.tx().has_error() || self.instance.rx().has_error();
// We need to have a `Drop` implementation, because we accept
// managed buffers that can free their memory on drop. Because of that
// we can't move out of the `Transfer`'s fields, so we use `ptr::read`
// and `mem::forget`.
//
// NOTE(unsafe) There is no panic branch between getting the resources
// and forgetting `self`.
let (instance, tx_buffer, rx_buffer) = unsafe {
let instance = core::ptr::read(&self.instance);
let tx_buffer = core::ptr::read(&self.tx_buffer);
let rx_buffer = core::ptr::read(&self.rx_buffer);
core::mem::forget(self);
(instance, tx_buffer, rx_buffer)
};
if err {
Err((DmaError::DescriptorError, instance, tx_buffer, rx_buffer))
} else {
Ok((instance, tx_buffer, rx_buffer))
}
}
/// Check if the transfer is finished.
pub fn is_done(&mut self) -> bool {
self.instance.tx().is_done() && self.instance.rx().is_done()
}
}
impl<I, T, R> Drop for DmaTransferTxRxOwned<I, T, R>
where
I: dma_private::DmaSupportTx + dma_private::DmaSupportRx,
T: ReadBuffer<Word = u8>,
R: WriteBuffer<Word = u8>,
{
fn drop(&mut self) {
self.instance.peripheral_wait_dma(true, true);
}
}
/// DMA transaction for TX only circular transfers /// DMA transaction for TX only circular transfers
#[non_exhaustive] #[non_exhaustive]
#[must_use] #[must_use]

View File

@ -846,8 +846,11 @@ pub mod dma {
Channel, Channel,
ChannelTypes, ChannelTypes,
DmaTransferRx, DmaTransferRx,
DmaTransferRxOwned,
DmaTransferTx, DmaTransferTx,
DmaTransferTxOwned,
DmaTransferTxRx, DmaTransferTxRx,
DmaTransferTxRxOwned,
Spi2Peripheral, Spi2Peripheral,
SpiPeripheral, SpiPeripheral,
TxPrivate, TxPrivate,
@ -1068,6 +1071,32 @@ pub mod dma {
&'t mut self, &'t mut self,
words: &'t TXBUF, words: &'t TXBUF,
) -> Result<DmaTransferTx<Self>, super::Error> ) -> Result<DmaTransferTx<Self>, super::Error>
where
TXBUF: ReadBuffer<Word = u8>,
{
self.dma_write_start(words)?;
Ok(DmaTransferTx::new(self))
}
/// Perform a DMA write.
///
/// This will return a [DmaTransferTxOwned] owning the buffer and the
/// SPI instance. The maximum amount of data to be sent is 32736
/// bytes.
#[cfg_attr(feature = "place-spi-driver-in-ram", ram)]
pub fn dma_write_owned<TXBUF>(
mut self,
words: TXBUF,
) -> Result<DmaTransferTxOwned<Self, TXBUF>, super::Error>
where
TXBUF: ReadBuffer<Word = u8>,
{
self.dma_write_start(&words)?;
Ok(DmaTransferTxOwned::new(self, words))
}
#[cfg_attr(feature = "place-spi-driver-in-ram", ram)]
fn dma_write_start<'t, TXBUF>(&'t mut self, words: &'t TXBUF) -> Result<(), super::Error>
where where
TXBUF: ReadBuffer<Word = u8>, TXBUF: ReadBuffer<Word = u8>,
{ {
@ -1081,7 +1110,7 @@ pub mod dma {
self.spi self.spi
.start_write_bytes_dma(ptr, len, &mut self.channel.tx, false)?; .start_write_bytes_dma(ptr, len, &mut self.channel.tx, false)?;
} }
Ok(DmaTransferTx::new(self)) Ok(())
} }
/// Perform a DMA read. /// Perform a DMA read.
@ -1093,6 +1122,32 @@ pub mod dma {
&'t mut self, &'t mut self,
words: &'t mut RXBUF, words: &'t mut RXBUF,
) -> Result<DmaTransferRx<Self>, super::Error> ) -> Result<DmaTransferRx<Self>, super::Error>
where
RXBUF: WriteBuffer<Word = u8>,
{
self.dma_read_start(words)?;
Ok(DmaTransferRx::new(self))
}
/// Perform a DMA read.
///
/// This will return a [DmaTransferRxOwned] owning the buffer and
/// the SPI instance. The maximum amount of data to be
/// received is 32736 bytes.
#[cfg_attr(feature = "place-spi-driver-in-ram", ram)]
pub fn dma_read_owned<RXBUF>(
mut self,
mut words: RXBUF,
) -> Result<DmaTransferRxOwned<Self, RXBUF>, super::Error>
where
RXBUF: WriteBuffer<Word = u8>,
{
self.dma_read_start(&mut words)?;
Ok(DmaTransferRxOwned::new(self, words))
}
#[cfg_attr(feature = "place-spi-driver-in-ram", ram)]
fn dma_read_start<'t, RXBUF>(&'t mut self, words: &'t mut RXBUF) -> Result<(), super::Error>
where where
RXBUF: WriteBuffer<Word = u8>, RXBUF: WriteBuffer<Word = u8>,
{ {
@ -1106,19 +1161,50 @@ pub mod dma {
self.spi self.spi
.start_read_bytes_dma(ptr, len, &mut self.channel.rx, false)?; .start_read_bytes_dma(ptr, len, &mut self.channel.rx, false)?;
} }
Ok(DmaTransferRx::new(self))
Ok(())
} }
/// Perform a DMA transfer. /// Perform a DMA transfer.
/// ///
/// This will return a [DmaTransferTxRx] owning the buffer(s) and the /// This will return a [DmaTransferTxRx].
/// SPI instance. The maximum amount of data to be sent/received /// The maximum amount of data to be sent/received is 32736 bytes.
/// is 32736 bytes.
pub fn dma_transfer<'t, TXBUF, RXBUF>( pub fn dma_transfer<'t, TXBUF, RXBUF>(
&'t mut self, &'t mut self,
words: &'t TXBUF, words: &'t TXBUF,
read_buffer: &'t mut RXBUF, read_buffer: &'t mut RXBUF,
) -> Result<DmaTransferTxRx<Self>, super::Error> ) -> Result<DmaTransferTxRx<Self>, super::Error>
where
TXBUF: ReadBuffer<Word = u8>,
RXBUF: WriteBuffer<Word = u8>,
{
self.dma_transfer_start(words, read_buffer)?;
Ok(DmaTransferTxRx::new(self))
}
/// Perform a DMA transfer
///
/// This will return a [DmaTransferTxRxOwned] owning the buffers and
/// the SPI instance. The maximum amount of data to be
/// sent/received is 32736 bytes.
pub fn dma_transfer_owned<TXBUF, RXBUF>(
mut self,
words: TXBUF,
mut read_buffer: RXBUF,
) -> Result<DmaTransferTxRxOwned<Self, TXBUF, RXBUF>, super::Error>
where
TXBUF: ReadBuffer<Word = u8>,
RXBUF: WriteBuffer<Word = u8>,
{
self.dma_transfer_start(&words, &mut read_buffer)?;
Ok(DmaTransferTxRxOwned::new(self, words, read_buffer))
}
fn dma_transfer_start<'t, TXBUF, RXBUF>(
&'t mut self,
words: &'t TXBUF,
read_buffer: &'t mut RXBUF,
) -> Result<(), super::Error>
where where
TXBUF: ReadBuffer<Word = u8>, TXBUF: ReadBuffer<Word = u8>,
RXBUF: WriteBuffer<Word = u8>, RXBUF: WriteBuffer<Word = u8>,
@ -1140,7 +1226,8 @@ pub mod dma {
&mut self.channel.rx, &mut self.channel.rx,
)?; )?;
} }
Ok(DmaTransferTxRx::new(self))
Ok(())
} }
} }

View File

@ -267,4 +267,52 @@ mod tests {
)) ))
)); ));
} }
#[test]
#[timeout(3)]
fn test_symmetric_dma_transfer_owned() {
const DMA_BUFFER_SIZE: usize = 4096;
let peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
let sclk = io.pins.gpio0;
let miso = io.pins.gpio2;
let mosi = io.pins.gpio4;
let cs = io.pins.gpio5;
let dma = Dma::new(peripherals.DMA);
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
let dma_channel = dma.spi2channel;
#[cfg(not(any(feature = "esp32", feature = "esp32s2")))]
let dma_channel = dma.channel0;
let (tx_buffer, mut tx_descriptors, rx_buffer, mut rx_descriptors) =
dma_buffers!(DMA_BUFFER_SIZE);
let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks)
.with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs))
.with_dma(dma_channel.configure(
false,
&mut tx_descriptors,
&mut rx_descriptors,
DmaPriority::Priority0,
));
// DMA buffer require a static life-time
let send = tx_buffer;
let receive = rx_buffer;
send.copy_from_slice(&[0x55u8; 4096]);
for byte in 0..send.len() {
send[byte] = byte as u8;
}
let transfer = spi.dma_transfer_owned(send, receive).unwrap();
let (_, send, receive) = transfer.wait().unwrap();
assert_eq!(send, receive);
}
} }