diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 7eb4df640..ba898bfad 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -79,6 +79,10 @@ harness = false name = "spi_full_duplex_dma_async" harness = false +[[test]] +name = "spi_full_duplex_dma_pcnt" +harness = false + [[test]] name = "spi_half_duplex_read" harness = false diff --git a/hil-test/tests/spi_full_duplex.rs b/hil-test/tests/spi_full_duplex.rs index fba387655..0502f0c1d 100644 --- a/hil-test/tests/spi_full_duplex.rs +++ b/hil-test/tests/spi_full_duplex.rs @@ -28,8 +28,15 @@ struct Context { spi: Spi<'static, esp_hal::peripherals::SPI2, FullDuplexMode>, } -impl Context { - pub fn init() -> Self { +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use defmt::assert_eq; + + use super::*; + + #[init] + fn init() -> Context { let peripherals = Peripherals::take(); let system = SystemControl::new(peripherals.SYSTEM); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); @@ -49,19 +56,6 @@ impl Context { Context { spi } } -} - -#[cfg(test)] -#[embedded_test::tests] -mod tests { - use defmt::assert_eq; - - use super::*; - - #[init] - fn init() -> Context { - Context::init() - } #[test] #[timeout(3)] diff --git a/hil-test/tests/spi_full_duplex_dma.rs b/hil-test/tests/spi_full_duplex_dma.rs index 938d2cec2..3988d59f2 100644 --- a/hil-test/tests/spi_full_duplex_dma.rs +++ b/hil-test/tests/spi_full_duplex_dma.rs @@ -6,349 +6,145 @@ //! 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 +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] use esp_hal::{ clock::ClockControl, - dma::{Dma, DmaPriority}, + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::Io, - peripherals::Peripherals, + peripherals::{Peripherals, SPI2}, prelude::*, - spi::{master::Spi, SpiMode}, + spi::{ + master::{dma::SpiDma, Spi}, + FullDuplexMode, + SpiMode, + }, system::SystemControl, + Blocking, }; use hil_test as _; +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: SpiDma<'static, SPI2, DmaChannel0, FullDuplexMode, Blocking>, +} + #[cfg(test)] #[embedded_test::tests] mod tests { use defmt::assert_eq; - use esp_hal::{ - dma::{DmaRxBuf, DmaTxBuf}, - spi::master::dma::SpiDmaBus, - }; use super::*; - #[test] - #[timeout(3)] - fn test_symmetric_dma_transfer() { - const DMA_BUFFER_SIZE: usize = 4; - + #[init] + fn init() -> Context { 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 miso = io.pins.gpio2; 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!(DMA_BUFFER_SIZE); - let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } 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, DmaPriority::Priority0)); + Context { spi } + } + + #[test] + #[timeout(3)] + fn test_symmetric_dma_transfer(ctx: Context) { + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(4); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + dma_tx_buf.fill(&[0xde, 0xad, 0xbe, 0xef]); - let transfer = spi + let transfer = ctx + .spi .dma_transfer(dma_tx_buf, dma_rx_buf) .map_err(|e| e.0) .unwrap(); - (_, (dma_tx_buf, dma_rx_buf)) = transfer.wait(); + let (_, (dma_tx_buf, dma_rx_buf)) = transfer.wait(); assert_eq!(dma_tx_buf.as_slice(), dma_rx_buf.as_slice()); } #[test] #[timeout(3)] - // S3 is disabled due to https://github.com/esp-rs/esp-hal/issues/1524#issuecomment-2255306292 - #[cfg(not(feature = "esp32s3"))] - fn test_asymmetric_dma_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; - + fn test_asymmetric_dma_transfer(ctx: Context) { let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(4, 2); let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut 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(false, DmaPriority::Priority0)); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); dma_tx_buf.fill(&[0xde, 0xad, 0xbe, 0xef]); - let transfer = spi + let transfer = ctx + .spi .dma_transfer(dma_tx_buf, dma_rx_buf) .map_err(|e| e.0) .unwrap(); - (_, (dma_tx_buf, dma_rx_buf)) = transfer.wait(); + let (_, (dma_tx_buf, dma_rx_buf)) = transfer.wait(); assert_eq!(dma_tx_buf.as_slice()[0..1], dma_rx_buf.as_slice()[0..1]); } #[test] #[timeout(3)] - fn test_symmetric_dma_transfer_huge_buffer() { - 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.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!(DMA_BUFFER_SIZE); + fn test_symmetric_dma_transfer_huge_buffer(ctx: Context) { + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(4096); let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut 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(false, DmaPriority::Priority0)); + let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); for (i, d) in dma_tx_buf.as_mut_slice().iter_mut().enumerate() { *d = i as _; } - let transfer = spi + let transfer = ctx + .spi .dma_transfer(dma_tx_buf, dma_rx_buf) .map_err(|e| e.0) .unwrap(); - (_, (dma_tx_buf, dma_rx_buf)) = transfer.wait(); + let (_, (dma_tx_buf, dma_rx_buf)) = transfer.wait(); assert_eq!(dma_tx_buf.as_slice(), dma_rx_buf.as_slice()); } #[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 dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); - - 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)); - - 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); - - // Fill the buffer where each byte has 3 pos edges. - dma_tx_buf.as_mut_slice().fill(0b0110_1010); - - assert_eq!(out_pin.is_set_low(), true); - - for i in 1..4 { - dma_rx_buf.as_mut_slice().copy_from_slice(&[5, 5, 5, 5, 5]); - let transfer = spi.dma_read(dma_rx_buf).map_err(|e| e.0).unwrap(); - (spi, dma_rx_buf) = transfer.wait(); - assert_eq!(dma_rx_buf.as_slice(), &[0, 0, 0, 0, 0]); - - let transfer = spi.dma_write(dma_tx_buf).map_err(|e| e.0).unwrap(); - (spi, dma_tx_buf) = transfer.wait(); - 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 dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); - - 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)); - - 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); - - // Fill the buffer where each byte has 3 pos edges. - dma_tx_buf.as_mut_slice().fill(0b0110_1010); - - assert_eq!(out_pin.is_set_low(), true); - - for i in 1..4 { - dma_rx_buf.as_mut_slice().copy_from_slice(&[5, 5, 5, 5, 5]); - let transfer = spi.dma_read(dma_rx_buf).map_err(|e| e.0).unwrap(); - (spi, dma_rx_buf) = transfer.wait(); - assert_eq!(dma_rx_buf.as_slice(), &[0, 0, 0, 0, 0]); - - let transfer = spi - .dma_transfer(dma_tx_buf, dma_rx_buf) - .map_err(|e| e.0) - .unwrap(); - (spi, (dma_tx_buf, dma_rx_buf)) = transfer.wait(); - assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); - } - } - - #[test] - #[timeout(3)] - fn test_dma_bus_symmetric_transfer() { - const DMA_BUFFER_SIZE: usize = 4; - - 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!(DMA_BUFFER_SIZE); + fn test_dma_bus_symmetric_transfer(ctx: Context) { + 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 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)) - .with_buffers(dma_tx_buf, dma_rx_buf); + let mut spi = ctx.spi.with_buffers(dma_tx_buf, dma_rx_buf); let tx_buf = [0xde, 0xad, 0xbe, 0xef]; let mut rx_buf = [0; 4]; @@ -360,32 +156,12 @@ mod tests { #[test] #[timeout(3)] - fn test_dma_bus_asymmetric_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; - + fn test_dma_bus_asymmetric_transfer(ctx: Context) { 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 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)) - .with_buffers(dma_tx_buf, dma_rx_buf); + let mut spi = ctx.spi.with_buffers(dma_tx_buf, dma_rx_buf); let tx_buf = [0xde, 0xad, 0xbe, 0xef]; let mut rx_buf = [0; 4]; @@ -397,34 +173,14 @@ mod tests { #[test] #[timeout(3)] - fn test_dma_bus_symmetric_transfer_huge_buffer() { + fn test_dma_bus_symmetric_transfer_huge_buffer(ctx: Context) { 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.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!(40); let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); - 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)) - .with_buffers(dma_tx_buf, dma_rx_buf); + let mut spi = ctx.spi.with_buffers(dma_tx_buf, dma_rx_buf); let tx_buf = core::array::from_fn(|i| i as _); let mut rx_buf = [0; DMA_BUFFER_SIZE]; diff --git a/hil-test/tests/spi_full_duplex_dma_async.rs b/hil-test/tests/spi_full_duplex_dma_async.rs index ba2a8f865..75fee9b2d 100644 --- a/hil-test/tests/spi_full_duplex_dma_async.rs +++ b/hil-test/tests/spi_full_duplex_dma_async.rs @@ -23,33 +23,53 @@ use embedded_hal_async::spi::SpiBus; use esp_hal::{ clock::ClockControl, - dma::{Dma, DmaPriority}, + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, - gpio::{Io, Level, Output, Pull}, + gpio::{GpioPin, Io, Level, Output, Pull}, pcnt::{ channel::{EdgeMode, PcntInputConfig, PcntSource}, + unit::Unit, Pcnt, }, - peripherals::Peripherals, + peripherals::{Peripherals, SPI2}, prelude::*, - spi::{master::Spi, SpiMode}, + spi::{ + master::{dma::asynch::SpiDmaAsyncBus, Spi}, + SpiMode, + }, system::SystemControl, }; use hil_test as _; +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +const DMA_BUFFER_SIZE: usize = 5; + +struct Context { + spi: SpiDmaAsyncBus<'static, SPI2, DmaChannel0>, + pcnt_unit: Unit<'static, 0>, + out_pin: Output<'static, GpioPin<5>>, + mosi_mirror: GpioPin<2>, +} + #[cfg(test)] #[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] mod tests { use defmt::assert_eq; - use esp_hal::dma::{DmaRxBuf, DmaTxBuf}; use super::*; - #[test] - #[timeout(3)] - async fn test_async_dma_read_dma_write_pcnt() { - const DMA_BUFFER_SIZE: usize = 5; - + #[init] + fn init() -> Context { let peripherals = Peripherals::take(); let system = SystemControl::new(peripherals.SYSTEM); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); @@ -68,88 +88,40 @@ mod tests { 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; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) + 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)) .with_buffers(dma_tx_buf, dma_rx_buf); - let unit = pcnt.unit0; - unit.channel0.set_edge_signal(PcntSource::from_pin( + Context { + spi, + pcnt_unit: pcnt.unit0, + out_pin, mosi_mirror, - PcntInputConfig { pull: Pull::Down }, - )); - unit.channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - - let mut receive = [0; DMA_BUFFER_SIZE]; - - // Fill the buffer where each byte has 3 pos edges. - let transmit = [0b0110_1010; DMA_BUFFER_SIZE]; - - 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, &mut receive).await.unwrap(); - assert_eq!(receive, [0, 0, 0, 0, 0]); - - SpiBus::write(&mut spi, &transmit).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 dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); - - 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)) - .with_buffers(dma_tx_buf, dma_rx_buf); - - let unit = pcnt.unit0; - unit.channel0.set_edge_signal(PcntSource::from_pin( - mosi_mirror, + async fn test_async_dma_read_dma_write_pcnt(mut ctx: Context) { + ctx.pcnt_unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, PcntInputConfig { pull: Pull::Down }, )); - unit.channel0 + ctx.pcnt_unit + .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); let mut receive = [0; DMA_BUFFER_SIZE]; @@ -157,17 +129,45 @@ mod tests { // Fill the buffer where each byte has 3 pos edges. let transmit = [0b0110_1010; DMA_BUFFER_SIZE]; - assert_eq!(out_pin.is_set_low(), true); + assert_eq!(ctx.out_pin.is_set_low(), true); for i in 1..4 { receive.copy_from_slice(&[5, 5, 5, 5, 5]); - SpiBus::read(&mut spi, &mut receive).await.unwrap(); + SpiBus::read(&mut ctx.spi, &mut receive).await.unwrap(); assert_eq!(receive, [0, 0, 0, 0, 0]); - SpiBus::transfer(&mut spi, &mut receive, &transmit) + SpiBus::write(&mut ctx.spi, &transmit).await.unwrap(); + assert_eq!(ctx.pcnt_unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } + + #[test] + #[timeout(3)] + async fn test_async_dma_read_dma_transfer_pcnt(mut ctx: Context) { + ctx.pcnt_unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + ctx.pcnt_unit + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let mut receive = [0; DMA_BUFFER_SIZE]; + + // Fill the buffer where each byte has 3 pos edges. + let transmit = [0b0110_1010; DMA_BUFFER_SIZE]; + + assert_eq!(ctx.out_pin.is_set_low(), true); + + for i in 1..4 { + receive.copy_from_slice(&[5, 5, 5, 5, 5]); + SpiBus::read(&mut ctx.spi, &mut receive).await.unwrap(); + assert_eq!(receive, [0, 0, 0, 0, 0]); + + SpiBus::transfer(&mut ctx.spi, &mut receive, &transmit) .await .unwrap(); - assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + assert_eq!(ctx.pcnt_unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); } } } diff --git a/hil-test/tests/spi_full_duplex_dma_pcnt.rs b/hil-test/tests/spi_full_duplex_dma_pcnt.rs new file mode 100644 index 000000000..e30478424 --- /dev/null +++ b/hil-test/tests/spi_full_duplex_dma_pcnt.rs @@ -0,0 +1,180 @@ +//! SPI Full Duplex DMA ASYNC Test with PCNT readback. +//! +//! Folowing pins are used: +//! SCLK GPIO0 +//! MOSI GPIO3 +//! 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 MISO (GPIO2) and MOSI (GPIO3) pins. + +//% CHIPS: esp32 esp32c6 esp32h2 esp32s3 + +#![no_std] +#![no_main] + +use esp_hal::{ + clock::ClockControl, + dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + gpio::{GpioPin, Io, Level, Output, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + unit::Unit, + Pcnt, + }, + peripherals::{Peripherals, SPI2}, + prelude::*, + spi::{ + master::{dma::SpiDma, Spi}, + FullDuplexMode, + SpiMode, + }, + system::SystemControl, + Blocking, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: SpiDma<'static, SPI2, DmaChannel0, FullDuplexMode, Blocking>, + pcnt_unit: Unit<'static, 0>, + out_pin: Output<'static, GpioPin<5>>, + mosi_mirror: GpioPin<2>, +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use defmt::assert_eq; + + use super::*; + + #[init] + fn init() -> Context { + 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 mosi = io.pins.gpio3; + let miso = io.pins.gpio6; + let cs = io.pins.gpio8; + + let dma = Dma::new(peripherals.DMA); + + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + 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, DmaPriority::Priority0)); + + let pcnt = Pcnt::new(peripherals.PCNT); + + let mut out_pin = Output::new(io.pins.gpio5, Level::Low); + out_pin.set_low(); + assert_eq!(out_pin.is_set_low(), true); + let mosi_mirror = io.pins.gpio2; + + Context { + spi, + pcnt_unit: pcnt.unit0, + out_pin, + mosi_mirror, + } + } + + #[test] + #[timeout(3)] + fn test_dma_read_dma_write_pcnt(ctx: Context) { + const DMA_BUFFER_SIZE: usize = 5; + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + + let unit = ctx.pcnt_unit; + let mut spi = ctx.spi; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + // Fill the buffer where each byte has 3 pos edges. + dma_tx_buf.as_mut_slice().fill(0b0110_1010); + + assert_eq!(ctx.out_pin.is_set_low(), true); + + for i in 1..4 { + dma_rx_buf.as_mut_slice().copy_from_slice(&[5, 5, 5, 5, 5]); + let transfer = spi.dma_read(dma_rx_buf).map_err(|e| e.0).unwrap(); + (spi, dma_rx_buf) = transfer.wait(); + assert_eq!(dma_rx_buf.as_slice(), &[0, 0, 0, 0, 0]); + + let transfer = spi.dma_write(dma_tx_buf).map_err(|e| e.0).unwrap(); + (spi, dma_tx_buf) = transfer.wait(); + assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } + + #[test] + #[timeout(3)] + fn test_dma_read_dma_transfer_pcnt(ctx: Context) { + const DMA_BUFFER_SIZE: usize = 5; + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); + let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + + let unit = ctx.pcnt_unit; + let mut spi = ctx.spi; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, + PcntInputConfig { pull: Pull::Down }, + )); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + // Fill the buffer where each byte has 3 pos edges. + dma_tx_buf.as_mut_slice().fill(0b0110_1010); + + assert_eq!(ctx.out_pin.is_set_low(), true); + + for i in 1..4 { + dma_rx_buf.as_mut_slice().copy_from_slice(&[5, 5, 5, 5, 5]); + let transfer = spi.dma_read(dma_rx_buf).map_err(|e| e.0).unwrap(); + (spi, dma_rx_buf) = transfer.wait(); + assert_eq!(dma_rx_buf.as_slice(), &[0, 0, 0, 0, 0]); + + let transfer = spi + .dma_transfer(dma_tx_buf, dma_rx_buf) + .map_err(|e| e.0) + .unwrap(); + (spi, (dma_tx_buf, dma_rx_buf)) = transfer.wait(); + assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + } + } +} diff --git a/hil-test/tests/spi_half_duplex_read.rs b/hil-test/tests/spi_half_duplex_read.rs index bcafe7cf9..ab392a520 100644 --- a/hil-test/tests/spi_half_duplex_read.rs +++ b/hil-test/tests/spi_half_duplex_read.rs @@ -8,39 +8,54 @@ //! //! Connect MISO (GPIO2) and GPIO (GPIO3) pins. -//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] +use esp_hal::{ + clock::ClockControl, + dma::{Dma, DmaPriority, DmaRxBuf}, + dma_buffers, + gpio::{GpioPin, Io, Level, Output}, + peripherals::{Peripherals, SPI2}, + prelude::*, + spi::{ + master::{dma::SpiDma, Address, Command, Spi}, + HalfDuplexMode, + SpiDataMode, + SpiMode, + }, + system::SystemControl, + Blocking, +}; use hil_test as _; +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: SpiDma<'static, SPI2, DmaChannel0, HalfDuplexMode, Blocking>, + miso_mirror: Output<'static, GpioPin<3>>, +} + #[cfg(test)] #[embedded_test::tests] mod tests { - use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaPriority, DmaRxBuf}, - dma_buffers, - gpio::{Io, Level, Output}, - peripherals::Peripherals, - prelude::_fugit_RateExtU32, - spi::{ - master::{Address, Command, Spi}, - SpiDataMode, - SpiMode, - }, - system::SystemControl, - }; + use defmt::assert_eq; + + use super::*; #[init] - fn init() {} - - #[test] - #[timeout(3)] - fn test_spi_reads_correctly_from_gpio_pin() { - const DMA_BUFFER_SIZE: usize = 4; - + fn init() -> Context { let peripherals = Peripherals::take(); let system = SystemControl::new(peripherals.SYSTEM); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); @@ -49,25 +64,38 @@ mod tests { let sclk = io.pins.gpio0; let miso = io.pins.gpio2; - let mut miso_mirror = Output::new(io.pins.gpio3, Level::High); + let miso_mirror = Output::new(io.pins.gpio3, Level::High); 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; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } - let (buffer, descriptors, _, _) = dma_buffers!(DMA_BUFFER_SIZE, 0); - let mut dma_rx_buf = DmaRxBuf::new(descriptors, buffer).unwrap(); - - let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) + let spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) .with_sck(sclk) .with_miso(miso) .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + Context { spi, miso_mirror } + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin(mut ctx: Context) { + const DMA_BUFFER_SIZE: usize = 4; + + let (buffer, descriptors, _, _) = dma_buffers!(DMA_BUFFER_SIZE, 0); + let mut dma_rx_buf = DmaRxBuf::new(descriptors, buffer).unwrap(); + // SPI should read '0's from the MISO pin - miso_mirror.set_low(); + ctx.miso_mirror.set_low(); + + let mut spi = ctx.spi; let transfer = spi .read( @@ -84,7 +112,7 @@ mod tests { assert_eq!(dma_rx_buf.as_slice(), &[0x00; DMA_BUFFER_SIZE]); // SPI should read '1's from the MISO pin - miso_mirror.set_high(); + ctx.miso_mirror.set_high(); let transfer = spi .read( diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index 06bb458be..1acd34b17 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -8,71 +8,105 @@ //! //! Connect MOSI (GPIO2) and PCNT (GPIO3) pins. -//% CHIPS: esp32 esp32c6 esp32h2 esp32s3 +//% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] +use esp_hal::{ + clock::ClockControl, + dma::{Dma, DmaPriority, DmaTxBuf}, + dma_buffers, + gpio::{GpioPin, Io, Pull}, + pcnt::{ + channel::{EdgeMode, PcntInputConfig, PcntSource}, + unit::Unit, + Pcnt, + }, + peripherals::{Peripherals, SPI2}, + prelude::*, + spi::{ + master::{dma::SpiDma, Address, Command, Spi}, + HalfDuplexMode, + SpiDataMode, + SpiMode, + }, + system::SystemControl, + Blocking, +}; use hil_test as _; +cfg_if::cfg_if! { + if #[cfg(any( + feature = "esp32", + feature = "esp32s2", + ))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +struct Context { + spi: SpiDma<'static, SPI2, DmaChannel0, HalfDuplexMode, Blocking>, + pcnt_unit: Unit<'static, 0>, + mosi_mirror: GpioPin<3>, +} + #[cfg(test)] #[embedded_test::tests] mod tests { - use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaPriority, DmaTxBuf}, - dma_buffers, - gpio::{Io, Pull}, - pcnt::{ - channel::{EdgeMode, PcntInputConfig, PcntSource}, - Pcnt, - }, - peripherals::Peripherals, - prelude::_fugit_RateExtU32, - spi::{ - master::{Address, Command, Spi}, - SpiDataMode, - SpiMode, - }, - system::SystemControl, - }; + use defmt::assert_eq; + + use super::*; #[init] - fn init() {} - - #[test] - #[timeout(3)] - fn test_spi_writes_are_correctly_by_pcnt() { - const DMA_BUFFER_SIZE: usize = 4; - + fn init() -> Context { 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 dma = Dma::new(peripherals.DMA); - let sclk = io.pins.gpio0; let mosi = io.pins.gpio2; let mosi_mirror = io.pins.gpio3; - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.spi2channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; + let pcnt = Pcnt::new(peripherals.PCNT); + let dma = Dma::new(peripherals.DMA); - let (buffer, descriptors, _, _) = dma_buffers!(DMA_BUFFER_SIZE, 0); - let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } - let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) + let spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks) .with_sck(sclk) .with_mosi(mosi) .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); - let unit = pcnt.unit0; - unit.channel0.set_edge_signal(PcntSource::from_pin( + Context { + spi, mosi_mirror, + pcnt_unit: pcnt.unit0, + } + } + + #[test] + #[timeout(3)] + fn test_spi_writes_are_correctly_by_pcnt(ctx: Context) { + const DMA_BUFFER_SIZE: usize = 4; + + let (buffer, descriptors, _, _) = dma_buffers!(DMA_BUFFER_SIZE, 0); + let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); + + let unit = ctx.pcnt_unit; + let mut spi = ctx.spi; + + unit.channel0.set_edge_signal(PcntSource::from_pin( + ctx.mosi_mirror, PcntInputConfig { pull: Pull::Down }, )); unit.channel0