From e708dbc77790519034b4297d74b5243ac8164540 Mon Sep 17 00:00:00 2001 From: Juraj Sadel Date: Mon, 19 Aug 2024 08:59:55 +0200 Subject: [PATCH] Add more SPI DMA (full-duplex) HIL tests (blocking and async) (#1952) * Add more SPI DMA HIL tests (blocking and async) * move test repetitions into loops instead, add a description about why PCNT is used, import embedded_hal_async::spi * clean up --- hil-test/Cargo.toml | 4 + hil-test/tests/spi_full_duplex_dma.rs | 172 ++++++++++++++++++- hil-test/tests/spi_full_duplex_dma_async.rs | 177 ++++++++++++++++++++ hil-test/tests/spi_half_duplex_write.rs | 2 +- 4 files changed, 349 insertions(+), 6 deletions(-) create mode 100644 hil-test/tests/spi_full_duplex_dma_async.rs diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index ad26f1326a2..30a158d4385 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -72,6 +72,10 @@ harness = false name = "spi_full_duplex_dma" harness = false +[[test]] +name = "spi_full_duplex_dma_async" +harness = false + [[test]] name = "spi_half_duplex_read" harness = false diff --git a/hil-test/tests/spi_full_duplex_dma.rs b/hil-test/tests/spi_full_duplex_dma.rs index a07438ef8e3..1651079ede9 100644 --- a/hil-test/tests/spi_full_duplex_dma.rs +++ b/hil-test/tests/spi_full_duplex_dma.rs @@ -1,4 +1,4 @@ -//! SPI Full Duplex DMA Test +//! SPI Full Duplex DMA ASYNC Test //! //! Folowing pins are used: //! SCLK GPIO0 @@ -6,6 +6,14 @@ //! MOSI GPIO3 //! CS GPIO8 //! +//! Only for test_dma_read_dma_write_pcnt and test_dma_read_dma_transfer_pcnt +//! tests: +//! PCNT GPIO2 +//! OUTPUT GPIO5 (helper to keep MISO LOW) +//! +//! The idea of using PCNT (input) here is to connect MOSI to it and count the +//! edges of whatever SPI writes (in this test case 3 pos edges). +//! //! Connect MISO (GPIO2) and MOSI (GPIO3) pins. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 @@ -68,7 +76,6 @@ mod tests { rx_descriptors, ); - // DMA buffer require a static life-time let mut send = tx_buffer; let mut receive = rx_buffer; @@ -111,7 +118,6 @@ mod tests { rx_descriptors, ); - // DMA buffer require a static life-time let mut send = tx_buffer; let mut receive = rx_buffer; @@ -154,7 +160,6 @@ mod tests { rx_descriptors, ); - // DMA buffer require a static life-time let mut send = tx_buffer; let mut receive = rx_buffer; @@ -305,7 +310,6 @@ mod tests { rx_descriptors, ); - // DMA buffer require a static life-time let send = tx_buffer; let receive = rx_buffer; @@ -318,4 +322,162 @@ mod tests { let (_, send, receive) = transfer.wait().unwrap(); assert_eq!(send, receive); } + + #[test] + #[timeout(3)] + #[cfg(any( + feature = "esp32", + feature = "esp32c6", + feature = "esp32h2", + feature = "esp32s3" + ))] + fn test_dma_read_dma_write_pcnt() { + use esp_hal::{ + gpio::{Level, Output, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + Pcnt, + }, + }; + + const DMA_BUFFER_SIZE: usize = 5; + + 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 pcnt = Pcnt::new(peripherals.PCNT); + let sclk = io.pins.gpio0; + let mosi_mirror = io.pins.gpio2; + let mosi = io.pins.gpio3; + let miso = io.pins.gpio6; + let cs = io.pins.gpio8; + + let mut out_pin = Output::new(io.pins.gpio5, Level::High); + out_pin.set_low(); + assert_eq!(out_pin.is_set_low(), true); + + 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!(DMA_BUFFER_SIZE); + + let mut 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, DmaPriority::Priority0), + tx_descriptors, + rx_descriptors, + ); + + let unit = pcnt.unit0; + unit.channel0.set_edge_signal(PcntSource::from_pin( + mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let mut receive = rx_buffer; + + // Fill the buffer where each byte has 3 pos edges. + tx_buffer.fill(0b0110_1010); + + assert_eq!(out_pin.is_set_low(), true); + + for i in 1..4 { + receive.copy_from_slice(&[5, 5, 5, 5, 5]); + let transfer = spi.dma_read(&mut receive).unwrap(); + transfer.wait().unwrap(); + assert_eq!(receive, &[0, 0, 0, 0, 0]); + + let transfer = spi.dma_write(&tx_buffer).unwrap(); + transfer.wait().unwrap(); + assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } + + #[test] + #[timeout(3)] + #[cfg(any( + feature = "esp32", + feature = "esp32c6", + feature = "esp32h2", + feature = "esp32s3" + ))] + fn test_dma_read_dma_transfer_pcnt() { + use esp_hal::{ + gpio::{Level, Output, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + Pcnt, + }, + }; + + const DMA_BUFFER_SIZE: usize = 5; + + 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 pcnt = Pcnt::new(peripherals.PCNT); + let sclk = io.pins.gpio0; + let mosi_mirror = io.pins.gpio2; + let mosi = io.pins.gpio3; + let miso = io.pins.gpio6; + let cs = io.pins.gpio8; + + let mut out_pin = Output::new(io.pins.gpio5, Level::High); + out_pin.set_low(); + assert_eq!(out_pin.is_set_low(), true); + + 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!(DMA_BUFFER_SIZE); + + let mut 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, DmaPriority::Priority0), + tx_descriptors, + rx_descriptors, + ); + + let unit = pcnt.unit0; + unit.channel0.set_edge_signal(PcntSource::from_pin( + mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let mut receive = rx_buffer; + + // Fill the buffer where each byte has 3 pos edges. + tx_buffer.fill(0b0110_1010); + + assert_eq!(out_pin.is_set_low(), true); + + for i in 1..4 { + receive.copy_from_slice(&[5, 5, 5, 5, 5]); + let transfer = spi.dma_read(&mut receive).unwrap(); + transfer.wait().unwrap(); + assert_eq!(receive, &[0, 0, 0, 0, 0]); + + let transfer = spi.dma_transfer(&tx_buffer, &mut receive).unwrap(); + transfer.wait().unwrap(); + assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } } diff --git a/hil-test/tests/spi_full_duplex_dma_async.rs b/hil-test/tests/spi_full_duplex_dma_async.rs new file mode 100644 index 00000000000..2a6943849da --- /dev/null +++ b/hil-test/tests/spi_full_duplex_dma_async.rs @@ -0,0 +1,177 @@ +//! SPI Full Duplex DMA Test +//! +//! Folowing pins are used: +//! SCLK GPIO0 +//! MOSI GPIO3 +//! MISO GPIO6 +//! CS GPIO8 +//! +//! PCNT GPIO2 +//! OUTPUT GPIO5 (helper to keep MISO LOW) +//! +//! The idea of using PCNT (input) here is to connect MOSI to it and count the +//! edges of whatever SPI writes (in this test case 3 pos edges). +//! +//! Connect PCNT (GPIO2) and MOSI (GPIO3) and MISO (GPIO6) and GPIO5 pins. + +//% CHIPS: esp32 esp32c6 esp32h2 esp32s3 + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use embedded_hal_async::spi::SpiBus; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + dma::{Dma, DmaPriority}, + dma_buffers, + gpio::{Io, Level, Output, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + Pcnt, + }, + peripherals::Peripherals, + prelude::*, + spi::{ + master::{prelude::*, Spi}, + SpiMode, + }, + system::SystemControl, +}; + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + use defmt::assert_eq; + + use super::*; + + #[test] + #[timeout(3)] + async fn test_async_dma_read_dma_write_pcnt() { + const DMA_BUFFER_SIZE: usize = 5; + + 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 pcnt = Pcnt::new(peripherals.PCNT); + let sclk = io.pins.gpio0; + let mosi_mirror = io.pins.gpio2; + let mosi = io.pins.gpio3; + let miso = io.pins.gpio6; + let cs = io.pins.gpio8; + + let mut out_pin = Output::new(io.pins.gpio5, Level::Low); + out_pin.set_low(); + assert_eq!(out_pin.is_set_low(), true); + + 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!(DMA_BUFFER_SIZE); + + let mut 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), + tx_descriptors, + rx_descriptors, + ); + + let unit = pcnt.unit0; + unit.channel0.set_edge_signal(PcntSource::from_pin( + mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let receive = rx_buffer; + + // Fill the buffer where each byte has 3 pos edges. + tx_buffer.fill(0b0110_1010); + + assert_eq!(out_pin.is_set_low(), true); + + for i in 1..4 { + receive.copy_from_slice(&[5, 5, 5, 5, 5]); + SpiBus::read(&mut spi, receive).await.unwrap(); + assert_eq!(receive, &[0, 0, 0, 0, 0]); + + SpiBus::write(&mut spi, tx_buffer).await.unwrap(); + assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } + + #[test] + #[timeout(3)] + async fn test_async_dma_read_dma_transfer_pcnt() { + const DMA_BUFFER_SIZE: usize = 5; + + 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 pcnt = Pcnt::new(peripherals.PCNT); + let sclk = io.pins.gpio0; + let mosi_mirror = io.pins.gpio2; + let mosi = io.pins.gpio3; + let miso = io.pins.gpio6; + let cs = io.pins.gpio8; + + let mut out_pin = Output::new(io.pins.gpio5, Level::High); + out_pin.set_low(); + assert_eq!(out_pin.is_set_low(), true); + + 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!(DMA_BUFFER_SIZE); + + let mut 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), + tx_descriptors, + rx_descriptors, + ); + + let unit = pcnt.unit0; + unit.channel0.set_edge_signal(PcntSource::from_pin( + mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let receive = rx_buffer; + + // Fill the buffer where each byte has 3 pos edges. + tx_buffer.fill(0b0110_1010); + + assert_eq!(out_pin.is_set_low(), true); + + for i in 1..4 { + receive.copy_from_slice(&[5, 5, 5, 5, 5]); + SpiBus::read(&mut spi, receive).await.unwrap(); + assert_eq!(receive, &[0, 0, 0, 0, 0]); + + SpiBus::transfer(&mut spi, receive, tx_buffer) + .await + .unwrap(); + assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } +} diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index c5aed0ec133..e9932159d2f 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -82,7 +82,7 @@ mod tests { unit.channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - // Fill the buffer where each byte 3 pos edges. + // Fill the buffer where each byte has 3 pos edges. tx_buffer.fill(0b0110_1010); let transfer = spi