diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index b2077b1eb34..d27e5fbc53e 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -1046,6 +1046,9 @@ pub mod dma { dma_buf: Buf, is_rx: bool, is_tx: bool, + + rx_future_awaited: bool, + tx_future_awaited: bool, } impl<'d, T, C, M, DmaMode, Buf> SpiDmaTransfer<'d, T, C, M, DmaMode, Buf> @@ -1056,14 +1059,30 @@ pub mod dma { M: DuplexMode, DmaMode: Mode, { + fn new( + spi_dma: SpiDma<'d, T, C, M, DmaMode>, + dma_buf: Buf, + is_rx: bool, + is_tx: bool, + ) -> Self { + Self { + spi_dma, + dma_buf, + is_rx, + is_tx, + rx_future_awaited: false, + tx_future_awaited: false, + } + } + pub fn is_done(&self) -> bool { - if self.is_tx && !self.spi_dma.channel.tx.is_done() { + if self.is_tx && !self.tx_future_awaited && !self.spi_dma.channel.tx.is_done() { return false; } if self.spi_dma.spi.busy() { return false; } - if self.is_rx { + if self.is_rx && !self.rx_future_awaited { // If this is an asymmetric transfer and the RX side is smaller, the RX channel // will never be "done" as it won't have enough descriptors/buffer to receive // the EOF bit from the SPI. So instead the RX channel will hit @@ -1096,14 +1115,16 @@ pub mod dma { M: DuplexMode, { pub async fn wait_for_done(&mut self) { - if self.is_tx { + if self.is_tx && !self.tx_future_awaited { let _ = DmaTxFuture::new(&mut self.spi_dma.channel.tx).await; + self.tx_future_awaited = true; } // As a future enhancement, setup Spi Future in here as well. - if self.is_rx { + if self.is_rx && !self.rx_future_awaited { let _ = DmaRxFuture::new(&mut self.spi_dma.channel.rx).await; + self.rx_future_awaited = true; } } } @@ -1141,12 +1162,7 @@ pub mod dma { return Err((e, self, buffer)); } - Ok(SpiDmaTransfer { - spi_dma: self, - dma_buf: buffer, - is_tx: true, - is_rx: false, - }) + Ok(SpiDmaTransfer::new(self, buffer, true, false)) } /// Perform a DMA read. @@ -1161,7 +1177,7 @@ pub mod dma { buffer: DmaRxBuf, ) -> Result, (Error, Self, DmaRxBuf)> { - let bytes_to_read = buffer.capacity(); + let bytes_to_read = buffer.len(); if bytes_to_read > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } @@ -1174,12 +1190,7 @@ pub mod dma { return Err((e, self, buffer)); } - Ok(SpiDmaTransfer { - spi_dma: self, - dma_buf: buffer, - is_tx: false, - is_rx: true, - }) + Ok(SpiDmaTransfer::new(self, buffer, false, true)) } /// Perform a DMA transfer @@ -1197,7 +1208,7 @@ pub mod dma { (Error, Self, DmaTxBuf, DmaRxBuf), > { let bytes_to_write = tx_buffer.len(); - let bytes_to_read = rx_buffer.capacity(); + let bytes_to_read = rx_buffer.len(); if bytes_to_write > MAX_DMA_SIZE || bytes_to_read > MAX_DMA_SIZE { return Err(( @@ -1222,12 +1233,12 @@ pub mod dma { return Err((e, self, tx_buffer, rx_buffer)); } - Ok(SpiDmaTransfer { - spi_dma: self, - dma_buf: (tx_buffer, rx_buffer), - is_tx: true, - is_rx: true, - }) + Ok(SpiDmaTransfer::new( + self, + (tx_buffer, rx_buffer), + true, + true, + )) } } @@ -1250,7 +1261,7 @@ pub mod dma { buffer: DmaRxBuf, ) -> Result, (Error, Self, DmaRxBuf)> { - let bytes_to_read = buffer.capacity(); + let bytes_to_read = buffer.len(); if bytes_to_read > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } @@ -1313,12 +1324,7 @@ pub mod dma { return Err((e, self, buffer)); } - Ok(SpiDmaTransfer { - spi_dma: self, - dma_buf: buffer, - is_tx: false, - is_rx: true, - }) + Ok(SpiDmaTransfer::new(self, buffer, false, true)) } #[allow(clippy::type_complexity)] @@ -1395,12 +1401,7 @@ pub mod dma { return Err((e, self, buffer)); } - Ok(SpiDmaTransfer { - spi_dma: self, - dma_buf: buffer, - is_tx: true, - is_rx: false, - }) + Ok(SpiDmaTransfer::new(self, buffer, true, false)) } } diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 02c1a7c76d3..2f827cdaaff 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -60,6 +60,10 @@ harness = false name = "spi_full_duplex" harness = false +[[test]] +name = "spi_full_duplex_async" +harness = false + [[test]] name = "spi_full_duplex_dma" harness = false diff --git a/hil-test/tests/spi_full_duplex_async.rs b/hil-test/tests/spi_full_duplex_async.rs new file mode 100644 index 00000000000..5c03815e272 --- /dev/null +++ b/hil-test/tests/spi_full_duplex_async.rs @@ -0,0 +1,78 @@ +//! SPI Full Duplex Async Test +//! +//! Folowing pins are used: +//! SCLK GPIO0 +//! MISO GPIO2 +//! MOSI GPIO3 +//! CS GPIO8 +//! +//! Connect MISO (GPIO2) and MOSI (GPIO3) pins. + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use esp_backtrace as _; + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + use esp_hal::{ + clock::ClockControl, + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + gpio::Io, + peripherals::Peripherals, + prelude::*, + spi::{ + master::{dma::asynch::SpiDmaAsyncBus, prelude::*, Spi}, + SpiMode, + }, + system::SystemControl, + }; + + #[init] + async fn init() {} + + #[test] + #[timeout(3)] + async fn test_transfer() { + 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.gpio3; + let cs = io.pins.gpio8; + + 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, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(4); + let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + + 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_for_async(false, DmaPriority::Priority0)); + + let mut spi_bus = SpiDmaAsyncBus::new(spi, dma_tx_buf, dma_rx_buf); + + let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut buffer = [0; 8]; + + embedded_hal_async::spi::SpiBus::transfer(&mut spi_bus, &mut buffer, &send_buffer) + .await + .unwrap(); + + assert_eq!(send_buffer, buffer); + } +}