diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index eef43046777..ae5c5a78cc8 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -1329,42 +1329,7 @@ mod dma { // set cmd, address, dummy cycles let reg_block = self.spi.register_block(); - if !cmd.is_none() { - reg_block.user2().modify(|_, w| unsafe { - w.usr_command_bitlen() - .bits((cmd.width() - 1) as u8) - .usr_command_value() - .bits(cmd.value()) - }); - } - - #[cfg(not(esp32))] - if !address.is_none() { - reg_block.user1().modify(|_, w| unsafe { - w.usr_addr_bitlen().bits((address.width() - 1) as u8) - }); - - let addr = address.value() << (32 - address.width()); - reg_block - .addr() - .write(|w| unsafe { w.usr_addr_value().bits(addr) }); - } - - #[cfg(esp32)] - if !address.is_none() { - reg_block.user1().modify(|r, w| unsafe { - w.bits(r.bits() & !(0x3f << 26) | (((address.width() - 1) as u32) & 0x3f) << 26) - }); - - let addr = address.value() << (32 - address.width()); - reg_block.addr().write(|w| unsafe { w.bits(addr) }); - } - - if dummy > 0 { - reg_block - .user1() - .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); - } + set_up_common_phases(reg_block, cmd, address, dummy); let result = unsafe { self.spi @@ -1406,42 +1371,7 @@ mod dma { // set cmd, address, dummy cycles let reg_block = self.spi.register_block(); - if !cmd.is_none() { - reg_block.user2().modify(|_, w| unsafe { - w.usr_command_bitlen() - .bits((cmd.width() - 1) as u8) - .usr_command_value() - .bits(cmd.value()) - }); - } - - #[cfg(not(esp32))] - if !address.is_none() { - reg_block.user1().modify(|_, w| unsafe { - w.usr_addr_bitlen().bits((address.width() - 1) as u8) - }); - - let addr = address.value() << (32 - address.width()); - reg_block - .addr() - .write(|w| unsafe { w.usr_addr_value().bits(addr) }); - } - - #[cfg(esp32)] - if !address.is_none() { - reg_block.user1().modify(|r, w| unsafe { - w.bits(r.bits() & !(0x3f << 26) | (((address.width() - 1) as u32) & 0x3f) << 26) - }); - - let addr = address.value() << (32 - address.width()); - reg_block.addr().write(|w| unsafe { w.bits(addr) }); - } - - if dummy > 0 { - reg_block - .user1() - .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); - } + set_up_common_phases(reg_block, cmd, address, dummy); let result = unsafe { self.spi @@ -2454,34 +2384,22 @@ pub trait Instance: private::Sealed { fn init(&mut self) { let reg_block = self.register_block(); reg_block.user().modify(|_, w| { - w.usr_miso_highpart() - .clear_bit() - .usr_miso_highpart() - .clear_bit() - .doutdin() - .set_bit() - .usr_miso() - .set_bit() - .usr_mosi() - .set_bit() - .cs_hold() - .set_bit() - .usr_dummy_idle() - .set_bit() - .usr_addr() - .clear_bit() - .usr_command() - .clear_bit() + w.usr_miso_highpart().clear_bit(); + w.usr_mosi_highpart().clear_bit(); + w.doutdin().set_bit(); + w.usr_miso().set_bit(); + w.usr_mosi().set_bit(); + w.cs_hold().set_bit(); + w.usr_dummy_idle().set_bit(); + w.usr_addr().clear_bit(); + w.usr_command().clear_bit() }); #[cfg(gdma)] reg_block.clk_gate().modify(|_, w| { - w.clk_en() - .set_bit() - .mst_clk_active() - .set_bit() - .mst_clk_sel() - .set_bit() + w.clk_en().set_bit(); + w.mst_clk_active().set_bit(); + w.mst_clk_sel().set_bit() }); #[cfg(any(esp32c6, esp32h2))] @@ -2495,18 +2413,17 @@ pub trait Instance: private::Sealed { #[cfg(gdma)] reg_block.ctrl().modify(|_, w| { - w.q_pol() - .clear_bit() - .d_pol() - .clear_bit() - .hold_pol() - .clear_bit() + w.q_pol().clear_bit(); + w.d_pol().clear_bit(); + w.hold_pol().clear_bit() }); #[cfg(esp32s2)] - reg_block - .ctrl() - .modify(|_, w| w.q_pol().clear_bit().d_pol().clear_bit().wp().clear_bit()); + reg_block.ctrl().modify(|_, w| { + w.q_pol().clear_bit(); + w.d_pol().clear_bit(); + w.wp().clear_bit() + }); #[cfg(esp32)] reg_block.ctrl().modify(|_, w| w.wp().clear_bit()); @@ -2525,56 +2442,14 @@ pub trait Instance: private::Sealed { data_mode: SpiDataMode, ) { let reg_block = self.register_block(); - match cmd_mode { - SpiDataMode::Single => reg_block - .ctrl() - .modify(|_, w| w.fcmd_dual().clear_bit().fcmd_quad().clear_bit()), - SpiDataMode::Dual => reg_block - .ctrl() - .modify(|_, w| w.fcmd_dual().set_bit().fcmd_quad().clear_bit()), - SpiDataMode::Quad => reg_block - .ctrl() - .modify(|_, w| w.fcmd_dual().clear_bit().fcmd_quad().set_bit()), - } - - match address_mode { - SpiDataMode::Single => reg_block - .ctrl() - .modify(|_, w| w.faddr_dual().clear_bit().faddr_quad().clear_bit()), - SpiDataMode::Dual => reg_block - .ctrl() - .modify(|_, w| w.faddr_dual().set_bit().faddr_quad().clear_bit()), - SpiDataMode::Quad => reg_block - .ctrl() - .modify(|_, w| w.faddr_dual().clear_bit().faddr_quad().set_bit()), - } - - match data_mode { - SpiDataMode::Single => { - reg_block - .ctrl() - .modify(|_, w| w.fread_dual().clear_bit().fread_quad().clear_bit()); - reg_block - .user() - .modify(|_, w| w.fwrite_dual().clear_bit().fwrite_quad().clear_bit()); - } - SpiDataMode::Dual => { - reg_block - .ctrl() - .modify(|_, w| w.fread_dual().set_bit().fread_quad().clear_bit()); - reg_block - .user() - .modify(|_, w| w.fwrite_dual().set_bit().fwrite_quad().clear_bit()); - } - SpiDataMode::Quad => { - reg_block - .ctrl() - .modify(|_, w| w.fread_quad().set_bit().fread_dual().clear_bit()); - reg_block - .user() - .modify(|_, w| w.fwrite_quad().set_bit().fwrite_dual().clear_bit()); - } - } + reg_block.ctrl().modify(|_, w| { + w.fcmd_dual().bit(cmd_mode == SpiDataMode::Dual); + w.fcmd_quad().bit(cmd_mode == SpiDataMode::Quad); + w.faddr_dual().bit(address_mode == SpiDataMode::Dual); + w.faddr_quad().bit(address_mode == SpiDataMode::Quad); + w.fread_dual().bit(data_mode == SpiDataMode::Dual); + w.fread_quad().bit(data_mode == SpiDataMode::Quad) + }); } #[cfg(esp32)] @@ -2590,134 +2465,38 @@ pub trait Instance: private::Sealed { _ => panic!("Only 1-bit command supported"), } - match (address_mode, data_mode) { - (SpiDataMode::Single, SpiDataMode::Single) => { - reg_block.ctrl().modify(|_, w| { - w.fread_dio() - .clear_bit() - .fread_qio() - .clear_bit() - .fread_dual() - .clear_bit() - .fread_quad() - .clear_bit() - }); - - reg_block.user().modify(|_, w| { - w.fwrite_dio() - .clear_bit() - .fwrite_qio() - .clear_bit() - .fwrite_dual() - .clear_bit() - .fwrite_quad() - .clear_bit() - }); - } - (SpiDataMode::Single, SpiDataMode::Dual) => { - reg_block.ctrl().modify(|_, w| { - w.fread_dio() - .clear_bit() - .fread_qio() - .clear_bit() - .fread_dual() - .set_bit() - .fread_quad() - .clear_bit() - }); - - reg_block.user().modify(|_, w| { - w.fwrite_dio() - .clear_bit() - .fwrite_qio() - .clear_bit() - .fwrite_dual() - .set_bit() - .fwrite_quad() - .clear_bit() - }); - } - (SpiDataMode::Single, SpiDataMode::Quad) => { - reg_block.ctrl().modify(|_, w| { - w.fread_dio() - .clear_bit() - .fread_qio() - .clear_bit() - .fread_dual() - .clear_bit() - .fread_quad() - .set_bit() - }); - - reg_block.user().modify(|_, w| { - w.fwrite_dio() - .clear_bit() - .fwrite_qio() - .clear_bit() - .fwrite_dual() - .clear_bit() - .fwrite_quad() - .set_bit() - }); - } - (SpiDataMode::Dual, SpiDataMode::Single) => { - panic!("Unsupported combination of data-modes") - } - (SpiDataMode::Dual, SpiDataMode::Dual) => { + match address_mode { + SpiDataMode::Single => { reg_block.ctrl().modify(|_, w| { - w.fread_dio() - .set_bit() - .fread_qio() - .clear_bit() - .fread_dual() - .clear_bit() - .fread_quad() - .clear_bit() + w.fread_dio().clear_bit(); + w.fread_qio().clear_bit(); + w.fread_dual().bit(data_mode == SpiDataMode::Dual); + w.fread_quad().bit(data_mode == SpiDataMode::Quad) }); reg_block.user().modify(|_, w| { - w.fwrite_dio() - .set_bit() - .fwrite_qio() - .clear_bit() - .fwrite_dual() - .clear_bit() - .fwrite_quad() - .clear_bit() + w.fwrite_dio().clear_bit(); + w.fwrite_qio().clear_bit(); + w.fwrite_dual().bit(data_mode == SpiDataMode::Dual); + w.fwrite_quad().bit(data_mode == SpiDataMode::Quad) }); } - (SpiDataMode::Dual, SpiDataMode::Quad) => { - panic!("Unsupported combination of data-modes") - } - (SpiDataMode::Quad, SpiDataMode::Single) => { - panic!("Unsupported combination of data-modes") - } - (SpiDataMode::Quad, SpiDataMode::Dual) => { - panic!("Unsupported combination of data-modes") - } - (SpiDataMode::Quad, SpiDataMode::Quad) => { + address_mode if address_mode == data_mode => { reg_block.ctrl().modify(|_, w| { - w.fread_dio() - .clear_bit() - .fread_qio() - .set_bit() - .fread_dual() - .clear_bit() - .fread_quad() - .clear_bit() + w.fread_dio().bit(address_mode == SpiDataMode::Dual); + w.fread_qio().bit(address_mode == SpiDataMode::Quad); + w.fread_dual().clear_bit(); + w.fread_quad().clear_bit() }); reg_block.user().modify(|_, w| { - w.fwrite_dio() - .clear_bit() - .fwrite_qio() - .set_bit() - .fwrite_dual() - .clear_bit() - .fwrite_quad() - .clear_bit() + w.fwrite_dio().bit(address_mode == SpiDataMode::Dual); + w.fwrite_qio().bit(address_mode == SpiDataMode::Quad); + w.fwrite_dual().clear_bit(); + w.fwrite_quad().clear_bit() }); } + _ => panic!("Unsupported combination of data-modes"), } } @@ -2875,53 +2654,26 @@ pub trait Instance: private::Sealed { } } - #[cfg(not(esp32))] fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self { let reg_block = self.register_block(); - match data_mode { - SpiMode::Mode0 => { - reg_block.misc().modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user().modify(|_, w| w.ck_out_edge().clear_bit()); - } - SpiMode::Mode1 => { - reg_block.misc().modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user().modify(|_, w| w.ck_out_edge().set_bit()); - } - SpiMode::Mode2 => { - reg_block.misc().modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user().modify(|_, w| w.ck_out_edge().set_bit()); - } - SpiMode::Mode3 => { - reg_block.misc().modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user().modify(|_, w| w.ck_out_edge().clear_bit()); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let pin_reg = reg_block.pin(); + } else { + let pin_reg = reg_block.misc(); } - } - self - } + }; - #[cfg(esp32)] - fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self { - let reg_block = self.register_block(); + pin_reg.modify(|_, w| { + w.ck_idle_edge() + .bit(matches!(data_mode, SpiMode::Mode2 | SpiMode::Mode3)) + }); + reg_block.user().modify(|_, w| { + w.ck_out_edge() + .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)) + }); - match data_mode { - SpiMode::Mode0 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user().modify(|_, w| w.ck_out_edge().clear_bit()); - } - SpiMode::Mode1 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user().modify(|_, w| w.ck_out_edge().set_bit()); - } - SpiMode::Mode2 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user().modify(|_, w| w.ck_out_edge().set_bit()); - } - SpiMode::Mode3 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user().modify(|_, w| w.ck_out_edge().clear_bit()); - } - } self } @@ -2929,12 +2681,9 @@ pub trait Instance: private::Sealed { // Disable clock source #[cfg(gdma)] self.register_block().clk_gate().modify(|_, w| { - w.clk_en() - .clear_bit() - .mst_clk_active() - .clear_bit() - .mst_clk_sel() - .clear_bit() + w.clk_en().clear_bit(); + w.mst_clk_active().clear_bit(); + w.mst_clk_sel().clear_bit() }); // Change clock frequency @@ -2943,12 +2692,9 @@ pub trait Instance: private::Sealed { // Enable clock source #[cfg(gdma)] self.register_block().clk_gate().modify(|_, w| { - w.clk_en() - .set_bit() - .mst_clk_active() - .set_bit() - .mst_clk_sel() - .set_bit() + w.clk_en().set_bit(); + w.mst_clk_active().set_bit(); + w.mst_clk_sel().set_bit() }); } @@ -3156,36 +2902,23 @@ pub trait Instance: private::Sealed { ) { let reg_block = self.register_block(); reg_block.user().modify(|_, w| { - w.usr_miso_highpart() - .clear_bit() - .usr_miso_highpart() - .clear_bit() - .doutdin() - .clear_bit() - .usr_miso() - .bit(!is_write && !no_mosi_miso) - .usr_mosi() - .bit(is_write && !no_mosi_miso) - .cs_hold() - .set_bit() - .usr_dummy_idle() - .bit(dummy_idle) - .usr_dummy() - .bit(dummy_state) - .usr_addr() - .bit(address_state) - .usr_command() - .bit(command_state) + w.usr_miso_highpart().clear_bit(); + w.usr_mosi_highpart().clear_bit(); + w.doutdin().clear_bit(); + w.usr_miso().bit(!is_write && !no_mosi_miso); + w.usr_mosi().bit(is_write && !no_mosi_miso); + w.cs_hold().set_bit(); + w.usr_dummy_idle().bit(dummy_idle); + w.usr_dummy().bit(dummy_state); + w.usr_addr().bit(address_state); + w.usr_command().bit(command_state) }); #[cfg(gdma)] reg_block.clk_gate().modify(|_, w| { - w.clk_en() - .set_bit() - .mst_clk_active() - .set_bit() - .mst_clk_sel() - .set_bit() + w.clk_en().set_bit(); + w.mst_clk_active().set_bit(); + w.mst_clk_sel().set_bit() }); #[cfg(any(esp32c6, esp32h2))] @@ -3223,42 +2956,7 @@ pub trait Instance: private::Sealed { // set cmd, address, dummy cycles let reg_block = self.register_block(); - if !cmd.is_none() { - reg_block.user2().modify(|_, w| unsafe { - w.usr_command_bitlen() - .bits((cmd.width() - 1) as u8) - .usr_command_value() - .bits(cmd.value()) - }); - } - - #[cfg(not(esp32))] - if !address.is_none() { - reg_block - .user1() - .modify(|_, w| unsafe { w.usr_addr_bitlen().bits((address.width() - 1) as u8) }); - - let addr = address.value() << (32 - address.width()); - reg_block - .addr() - .write(|w| unsafe { w.usr_addr_value().bits(addr) }); - } - - #[cfg(esp32)] - if !address.is_none() { - reg_block.user1().modify(|r, w| unsafe { - w.bits(r.bits() & !(0x3f << 26) | (((address.width() - 1) as u32) & 0x3f) << 26) - }); - - let addr = address.value() << (32 - address.width()); - reg_block.addr().write(|w| unsafe { w.bits(addr) }); - } - - if dummy > 0 { - reg_block - .user1() - .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); - } + set_up_common_phases(reg_block, cmd, address, dummy); if !buffer.is_empty() { // re-using the full-duplex write here @@ -3288,42 +2986,7 @@ pub trait Instance: private::Sealed { // set cmd, address, dummy cycles let reg_block = self.register_block(); - if !cmd.is_none() { - reg_block.user2().modify(|_, w| unsafe { - w.usr_command_bitlen() - .bits((cmd.width() - 1) as u8) - .usr_command_value() - .bits(cmd.value()) - }); - } - - #[cfg(not(esp32))] - if !address.is_none() { - reg_block - .user1() - .modify(|_, w| unsafe { w.usr_addr_bitlen().bits((address.width() - 1) as u8) }); - - let addr = address.value() << (32 - address.width()); - reg_block - .addr() - .write(|w| unsafe { w.usr_addr_value().bits(addr) }); - } - - #[cfg(esp32)] - if !address.is_none() { - reg_block.user1().modify(|r, w| unsafe { - w.bits(r.bits() & !(0x3f << 26) | (((address.width() - 1) as u32) & 0x3f) << 26) - }); - - let addr = address.value() << (32 - address.width()); - reg_block.addr().write(|w| unsafe { w.bits(addr) }); - } - - if dummy > 0 { - reg_block - .user1() - .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); - } + set_up_common_phases(reg_block, cmd, address, dummy); self.configure_datalen(buffer.len(), 0); self.start_operation(); @@ -3386,6 +3049,43 @@ pub trait Instance: private::Sealed { } } +fn set_up_common_phases(reg_block: &RegisterBlock, cmd: Command, address: Address, dummy: u8) { + if !cmd.is_none() { + reg_block.user2().modify(|_, w| unsafe { + w.usr_command_bitlen().bits((cmd.width() - 1) as u8); + w.usr_command_value().bits(cmd.value()) + }); + } + + #[cfg(not(esp32))] + if !address.is_none() { + reg_block + .user1() + .modify(|_, w| unsafe { w.usr_addr_bitlen().bits((address.width() - 1) as u8) }); + + let addr = address.value() << (32 - address.width()); + reg_block + .addr() + .write(|w| unsafe { w.usr_addr_value().bits(addr) }); + } + + #[cfg(esp32)] + if !address.is_none() { + reg_block.user1().modify(|r, w| unsafe { + w.bits(r.bits() & !(0x3f << 26) | (((address.width() - 1) as u32) & 0x3f) << 26) + }); + + let addr = address.value() << (32 - address.width()); + reg_block.addr().write(|w| unsafe { w.bits(addr) }); + } + + if dummy > 0 { + reg_block + .user1() + .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); + } +} + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))] impl Instance for crate::peripherals::SPI2 { #[inline(always)] diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index bef22fea4dc..c774a6f23d6 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -72,15 +72,7 @@ name = "lcd_cam_i8080_async" harness = false [[test]] -name = "qspi_read" -harness = false - -[[test]] -name = "qspi_write" -harness = false - -[[test]] -name = "qspi_write_read" +name = "qspi" harness = false [[test]] diff --git a/hil-test/src/lib.rs b/hil-test/src/lib.rs index b29c9f8c250..8b0e99ab7c8 100644 --- a/hil-test/src/lib.rs +++ b/hil-test/src/lib.rs @@ -59,3 +59,24 @@ macro_rules! common_test_pins { } }}; } + +// A GPIO that's not connected to anything. We use the BOOT pin for this, but +// beware: it has a pullup. +#[macro_export] +macro_rules! unconnected_pin { + ($io:expr) => {{ + cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s2, esp32s3))] { + $io.pins.gpio0 + } else if #[cfg(esp32c6)] { + $io.pins.gpio9 + } else if #[cfg(esp32h2)] { + $io.pins.gpio9 + } else if #[cfg(esp32c2)] { + $io.pins.gpio8 + } else { + $io.pins.gpio9 + } + } + }}; +} diff --git a/hil-test/tests/qspi.rs b/hil-test/tests/qspi.rs new file mode 100644 index 00000000000..9cbe88bbf42 --- /dev/null +++ b/hil-test/tests/qspi.rs @@ -0,0 +1,400 @@ +//! QSPI Test Suite + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: defmt + +#![no_std] +#![no_main] + +#[cfg(pcnt)] +use esp_hal::pcnt::{channel::EdgeMode, unit::Unit, Pcnt}; +use esp_hal::{ + dma::{Channel, Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, + dma_buffers, + gpio::{AnyPin, Input, Io, Level, NoPin, Output, Pull}, + prelude::*, + spi::{ + master::{Address, Command, Spi, SpiDma}, + HalfDuplexMode, + SpiDataMode, + SpiMode, + }, + Blocking, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s2))] { + use esp_hal::dma::Spi2DmaChannel as DmaChannel0; + } else { + use esp_hal::dma::DmaChannel0; + } +} + +cfg_if::cfg_if! { + if #[cfg(esp32)] { + const COMMAND_DATA_MODES: [SpiDataMode; 1] = [SpiDataMode::Single]; + } else { + const COMMAND_DATA_MODES: [SpiDataMode; 2] = [SpiDataMode::Single, SpiDataMode::Quad]; + } +} + +type SpiUnderTest = + SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>; + +struct Context { + spi: esp_hal::peripherals::SPI2, + #[cfg(pcnt)] + pcnt: esp_hal::peripherals::PCNT, + dma_channel: Channel<'static, DmaChannel0, Blocking>, + gpios: [AnyPin; 3], +} + +fn transfer_read( + spi: SpiUnderTest, + dma_rx_buf: DmaRxBuf, + command: Command, +) -> (SpiUnderTest, DmaRxBuf) { + let transfer = spi + .read(SpiDataMode::Quad, command, Address::None, 0, dma_rx_buf) + .map_err(|e| e.0) + .unwrap(); + transfer.wait() +} + +fn transfer_write( + spi: SpiUnderTest, + dma_tx_buf: DmaTxBuf, + write: u8, + command_data_mode: SpiDataMode, +) -> (SpiUnderTest, DmaTxBuf) { + let transfer = spi + .write( + SpiDataMode::Quad, + Command::Command8(write as u16, command_data_mode), + Address::Address24( + write as u32 | (write as u32) << 8 | (write as u32) << 16, + SpiDataMode::Quad, + ), + 0, + dma_tx_buf, + ) + .map_err(|e| e.0) + .unwrap(); + transfer.wait() +} + +fn execute_read(mut spi: SpiUnderTest, mut miso_mirror: Output<'static>, expected: u8) { + const DMA_BUFFER_SIZE: usize = 4; + + let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE); + let mut dma_rx_buf = DmaRxBuf::new(descriptors, buffer).unwrap(); + + miso_mirror.set_low(); + (spi, dma_rx_buf) = transfer_read(spi, dma_rx_buf, Command::None); + assert_eq!(dma_rx_buf.as_slice(), &[0; DMA_BUFFER_SIZE]); + + // Set two bits in the written bytes to 1 + miso_mirror.set_high(); + (_, dma_rx_buf) = transfer_read(spi, dma_rx_buf, Command::None); + assert_eq!(dma_rx_buf.as_slice(), &[expected; DMA_BUFFER_SIZE]); +} + +// Regression test for https://github.com/esp-rs/esp-hal/issues/1860 +fn execute_write_read(mut spi: SpiUnderTest, mut mosi_mirror: Output<'static>, expected: u8) { + const DMA_BUFFER_SIZE: usize = 4; + + let (rx_buffer, rx_descriptors, buffer, descriptors) = + dma_buffers!(DMA_BUFFER_SIZE, DMA_BUFFER_SIZE); + let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); + let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); + + dma_tx_buf.fill(&[0x00; DMA_BUFFER_SIZE]); + + for command_data_mode in COMMAND_DATA_MODES { + (spi, dma_tx_buf) = transfer_write(spi, dma_tx_buf, expected, command_data_mode); + + mosi_mirror.set_high(); + + (spi, dma_rx_buf) = transfer_read( + spi, + dma_rx_buf, + Command::Command8(expected as u16, command_data_mode), + ); + assert_eq!(dma_rx_buf.as_slice(), &[expected; DMA_BUFFER_SIZE]); + } +} + +#[cfg(pcnt)] +fn execute_write(unit: Unit<'static, 0>, mut spi: SpiUnderTest, write: u8) { + const DMA_BUFFER_SIZE: usize = 4; + + let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE); + let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); + + for command_data_mode in COMMAND_DATA_MODES { + dma_tx_buf.fill(&[write; DMA_BUFFER_SIZE]); + + // Send command + data. + // Should read 8 bits: 1 command bit, 3 address bits, 4 data bits + unit.clear(); + (spi, dma_tx_buf) = transfer_write(spi, dma_tx_buf, write, command_data_mode); + assert_eq!(unit.get_value(), 8); + + // Send command + address only + // Should read 4 bits: 1 command bit, 3 address bits + dma_tx_buf.set_length(0); + unit.clear(); + (spi, dma_tx_buf) = transfer_write(spi, dma_tx_buf, write, command_data_mode); + assert_eq!(unit.get_value(), 4); + } +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let (mut pin, mut pin_mirror) = hil_test::common_test_pins!(io); + let mut unconnected_pin = hil_test::unconnected_pin!(io); + + // Make sure pins have no pullups + let _ = Input::new(&mut pin, Pull::None); + let _ = Input::new(&mut pin_mirror, Pull::None); + let _ = Input::new(&mut unconnected_pin, Pull::None); + + let dma = Dma::new(peripherals.DMA); + + cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s2))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } + + let dma_channel = dma_channel.configure(false, DmaPriority::Priority0); + + Context { + spi: peripherals.SPI2, + #[cfg(pcnt)] + pcnt: peripherals.PCNT, + dma_channel, + gpios: [ + pin.degrade(), + pin_mirror.degrade(), + unconnected_pin.degrade(), + ], + } + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin_0(ctx: Context) { + let [pin, pin_mirror, _] = ctx.gpios; + let pin_mirror = Output::new(pin_mirror, Level::High); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, pin, NoPin, NoPin, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_read(spi, pin_mirror, 0b0001_0001); + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin_1(ctx: Context) { + let [pin, pin_mirror, _] = ctx.gpios; + let pin_mirror = Output::new(pin_mirror, Level::High); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, NoPin, pin, NoPin, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_read(spi, pin_mirror, 0b0010_0010); + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin_2(ctx: Context) { + let [pin, pin_mirror, _] = ctx.gpios; + let pin_mirror = Output::new(pin_mirror, Level::High); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, NoPin, NoPin, pin, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_read(spi, pin_mirror, 0b0100_0100); + } + + #[test] + #[timeout(3)] + fn test_spi_reads_correctly_from_gpio_pin_3(ctx: Context) { + let [pin, pin_mirror, _] = ctx.gpios; + let pin_mirror = Output::new(pin_mirror, Level::High); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, NoPin, NoPin, NoPin, pin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_read(spi, pin_mirror, 0b1000_1000); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_and_reads_correctly_pin_0(ctx: Context) { + let [pin, pin_mirror, _] = ctx.gpios; + let pin_mirror = Output::new(pin_mirror, Level::High); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, pin, NoPin, NoPin, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_write_read(spi, pin_mirror, 0b0001_0001); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_and_reads_correctly_pin_1(ctx: Context) { + let [pin, pin_mirror, _] = ctx.gpios; + let pin_mirror = Output::new(pin_mirror, Level::High); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, NoPin, pin, NoPin, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_write_read(spi, pin_mirror, 0b0010_0010); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_and_reads_correctly_pin_2(ctx: Context) { + let [pin, pin_mirror, _] = ctx.gpios; + let pin_mirror = Output::new(pin_mirror, Level::High); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, NoPin, NoPin, pin, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_write_read(spi, pin_mirror, 0b0100_0100); + } + + #[test] + #[timeout(3)] + fn test_spi_writes_and_reads_correctly_pin_3(ctx: Context) { + let [pin, pin_mirror, _] = ctx.gpios; + let pin_mirror = Output::new(pin_mirror, Level::High); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, NoPin, NoPin, NoPin, pin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_write_read(spi, pin_mirror, 0b1000_1000); + } + + #[test] + #[timeout(3)] + #[cfg(pcnt)] + fn test_spi_writes_correctly_to_pin_0(ctx: Context) { + // For PCNT-using tests we swap the pins around so that the PCNT is not pulled + // up by a resistor if the command phase doesn't drive its line. + let [_, _, mosi] = ctx.gpios; + + let pcnt = Pcnt::new(ctx.pcnt); + let unit = pcnt.unit0; + + unit.channel0.set_edge_signal(mosi.peripheral_input()); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, mosi, NoPin, NoPin, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_write(unit, spi, 0b0000_0001); + } + + #[test] + #[timeout(3)] + #[cfg(pcnt)] + fn test_spi_writes_correctly_to_pin_1(ctx: Context) { + // For PCNT-using tests we swap the pins around so that the PCNT is not pulled + // up by a resistor if the command phase doesn't drive its line. + let [gpio, _, mosi] = ctx.gpios; + + let pcnt = Pcnt::new(ctx.pcnt); + let unit = pcnt.unit0; + + unit.channel0.set_edge_signal(mosi.peripheral_input()); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + unit.channel1.set_edge_signal(gpio.peripheral_input()); + unit.channel1 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, mosi, gpio, NoPin, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_write(unit, spi, 0b0000_0010); + } + + #[test] + #[timeout(3)] + #[cfg(pcnt)] + fn test_spi_writes_correctly_to_pin_2(ctx: Context) { + // For PCNT-using tests we swap the pins around so that the PCNT is not pulled + // up by a resistor if the command phase doesn't drive its line. + let [gpio, _, mosi] = ctx.gpios; + + let pcnt = Pcnt::new(ctx.pcnt); + let unit = pcnt.unit0; + + unit.channel0.set_edge_signal(mosi.peripheral_input()); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + unit.channel1.set_edge_signal(gpio.peripheral_input()); + unit.channel1 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, mosi, NoPin, gpio, NoPin, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_write(unit, spi, 0b0000_0100); + } + + #[test] + #[timeout(3)] + #[cfg(pcnt)] + fn test_spi_writes_correctly_to_pin_3(ctx: Context) { + // For PCNT-using tests we swap the pins around so that the PCNT is not pulled + // up by a resistor if the command phase doesn't drive its line. + let [gpio, _, mosi] = ctx.gpios; + + let pcnt = Pcnt::new(ctx.pcnt); + let unit = pcnt.unit0; + + unit.channel0.set_edge_signal(mosi.peripheral_input()); + unit.channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + unit.channel1.set_edge_signal(gpio.peripheral_input()); + unit.channel1 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) + .with_pins(NoPin, mosi, NoPin, NoPin, gpio, NoPin) + .with_dma(ctx.dma_channel); + + super::execute_write(unit, spi, 0b0000_1000); + } +} diff --git a/hil-test/tests/qspi_read.rs b/hil-test/tests/qspi_read.rs deleted file mode 100644 index 8f6debc1701..00000000000 --- a/hil-test/tests/qspi_read.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! QSPI Read Test - -//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 - -#![no_std] -#![no_main] - -use esp_hal::{ - dma::{Channel, Dma, DmaPriority, DmaRxBuf}, - dma_buffers, - gpio::{AnyPin, Io, Level, NoPin, Output}, - prelude::*, - spi::{ - master::{Address, Command, Spi, SpiDma}, - HalfDuplexMode, - SpiDataMode, - SpiMode, - }, - 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: esp_hal::peripherals::SPI2, - dma_channel: Channel<'static, DmaChannel0, Blocking>, - miso: AnyPin, - miso_mirror: Output<'static>, -} - -fn execute( - mut spi: SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>, - mut miso_mirror: Output<'static>, - wanted: u8, -) { - const DMA_BUFFER_SIZE: usize = 4; - - let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE); - let mut dma_rx_buf = DmaRxBuf::new(descriptors, buffer).unwrap(); - - miso_mirror.set_low(); - - let transfer = spi - .read( - SpiDataMode::Quad, - Command::None, - Address::None, - 0, - dma_rx_buf, - ) - .map_err(|e| e.0) - .unwrap(); - (spi, dma_rx_buf) = transfer.wait(); - - assert_eq!(dma_rx_buf.as_slice(), &[0; DMA_BUFFER_SIZE]); - - // SPI should read all '1's - miso_mirror.set_high(); - - let transfer = spi - .read( - SpiDataMode::Quad, - Command::None, - Address::None, - 0, - dma_rx_buf, - ) - .map_err(|e| e.0) - .unwrap(); - - (_, dma_rx_buf) = transfer.wait(); - - assert_eq!(dma_rx_buf.as_slice(), &[wanted; DMA_BUFFER_SIZE]); -} - -#[cfg(test)] -#[embedded_test::tests] -mod tests { - use super::*; - - #[init] - fn init() -> Context { - let peripherals = esp_hal::init(esp_hal::Config::default()); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let (miso, miso_mirror) = hil_test::common_test_pins!(io); - - let miso = miso.degrade(); - let miso_mirror = Output::new(miso_mirror, Level::High); - - 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 dma_channel = dma_channel.configure(false, DmaPriority::Priority0); - - Context { - spi: peripherals.SPI2, - dma_channel, - miso, - miso_mirror, - } - } - - #[test] - #[timeout(3)] - fn test_spi_reads_correctly_from_gpio_pin_0(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, ctx.miso, NoPin, NoPin, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - super::execute(spi, ctx.miso_mirror, 0b0001_0001); - } - - #[test] - #[timeout(3)] - fn test_spi_reads_correctly_from_gpio_pin_1(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, ctx.miso, NoPin, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - super::execute(spi, ctx.miso_mirror, 0b0010_0010); - } - - #[test] - #[timeout(3)] - fn test_spi_reads_correctly_from_gpio_pin_2(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, NoPin, ctx.miso, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - // SPI should read '0b10111011' - super::execute(spi, ctx.miso_mirror, 0b0100_0100); - } - - #[test] - #[timeout(3)] - fn test_spi_reads_correctly_from_gpio_pin_3(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, NoPin, NoPin, ctx.miso, NoPin) - .with_dma(ctx.dma_channel); - - // SPI should read '0b01110111' - super::execute(spi, ctx.miso_mirror, 0b1000_1000); - } -} diff --git a/hil-test/tests/qspi_write.rs b/hil-test/tests/qspi_write.rs deleted file mode 100644 index 3950024e885..00000000000 --- a/hil-test/tests/qspi_write.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! QSPI Write Test - -//% CHIPS: esp32c6 esp32h2 esp32s2 esp32s3 - -#![no_std] -#![no_main] - -use esp_hal::{ - dma::{Channel, Dma, DmaPriority, DmaTxBuf}, - dma_buffers, - gpio::{interconnect::InputSignal, AnyPin, Io, NoPin}, - pcnt::{channel::EdgeMode, unit::Unit, Pcnt}, - prelude::*, - spi::{ - master::{Address, Command, Spi, SpiDma}, - HalfDuplexMode, - SpiDataMode, - SpiMode, - }, - 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: esp_hal::peripherals::SPI2, - pcnt_source: InputSignal, - pcnt: esp_hal::peripherals::PCNT, - dma_channel: Channel<'static, DmaChannel0, Blocking>, - mosi: AnyPin, -} - -fn execute( - unit: Unit<'static, 0>, - mut spi: SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>, - write: u8, -) { - const DMA_BUFFER_SIZE: usize = 4; - - let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE); - let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); - - dma_tx_buf.fill(&[write; DMA_BUFFER_SIZE]); - - let transfer = spi - .write( - SpiDataMode::Quad, - Command::Command8(write as u16, SpiDataMode::Quad), - Address::Address24( - write as u32 | (write as u32) << 8 | (write as u32) << 16, - SpiDataMode::Quad, - ), - 0, - dma_tx_buf, - ) - .map_err(|e| e.0) - .unwrap(); - (spi, dma_tx_buf) = transfer.wait(); - - assert_eq!(unit.get_value(), 8); - - dma_tx_buf.set_length(0); - let transfer = spi - .write( - SpiDataMode::Quad, - Command::Command8(write as u16, SpiDataMode::Quad), - Address::Address24( - write as u32 | (write as u32) << 8 | (write as u32) << 16, - SpiDataMode::Quad, - ), - 0, - dma_tx_buf, - ) - .map_err(|e| e.0) - .unwrap(); - _ = transfer.wait(); - - assert_eq!(unit.get_value(), 8 + 4); -} - -#[cfg(test)] -#[embedded_test::tests] -mod tests { - use super::*; - - #[init] - fn init() -> Context { - let peripherals = esp_hal::init(esp_hal::Config::default()); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let (mosi, _) = hil_test::common_test_pins!(io); - - let mosi = mosi.degrade(); - - 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 dma_channel = dma_channel.configure(false, DmaPriority::Priority0); - - Context { - spi: peripherals.SPI2, - pcnt_source: mosi.peripheral_input(), - pcnt: peripherals.PCNT, - dma_channel, - mosi, - } - } - - #[test] - #[timeout(3)] - fn test_spi_writes_correctly_to_pin_0(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, ctx.mosi, NoPin, NoPin, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - let pcnt = Pcnt::new(ctx.pcnt); - let unit = pcnt.unit0; - - unit.channel0.set_edge_signal(ctx.pcnt_source); - unit.channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - - super::execute(unit, spi, 0b0000_0001); - } - - #[test] - #[timeout(3)] - fn test_spi_writes_correctly_to_pin_1(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, ctx.mosi, NoPin, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - let pcnt = Pcnt::new(ctx.pcnt); - let unit = pcnt.unit0; - - unit.channel0.set_edge_signal(ctx.pcnt_source); - unit.channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - - super::execute(unit, spi, 0b0000_0010); - } - - #[test] - #[timeout(3)] - fn test_spi_writes_correctly_to_pin_2(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, NoPin, ctx.mosi, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - let pcnt = Pcnt::new(ctx.pcnt); - let unit = pcnt.unit0; - - unit.channel0.set_edge_signal(ctx.pcnt_source); - unit.channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - - super::execute(unit, spi, 0b0000_0100); - } - - #[test] - #[timeout(3)] - fn test_spi_writes_correctly_to_pin_3(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, NoPin, NoPin, ctx.mosi, NoPin) - .with_dma(ctx.dma_channel); - - let pcnt = Pcnt::new(ctx.pcnt); - let unit = pcnt.unit0; - - unit.channel0.set_edge_signal(ctx.pcnt_source); - unit.channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - - super::execute(unit, spi, 0b0000_1000); - } -} diff --git a/hil-test/tests/qspi_write_read.rs b/hil-test/tests/qspi_write_read.rs deleted file mode 100644 index 0c683b2d79e..00000000000 --- a/hil-test/tests/qspi_write_read.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! QSPI Write + Read Test -//! -//! Make sure issue #1860 doesn't affect us - -//% CHIPS: esp32c6 esp32h2 esp32s2 esp32s3 - -#![no_std] -#![no_main] - -use esp_hal::{ - dma::{Channel, Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, - dma_buffers, - gpio::{AnyPin, Io, Level, NoPin, Output}, - prelude::*, - spi::{ - master::{Address, Command, Spi, SpiDma}, - HalfDuplexMode, - SpiDataMode, - SpiMode, - }, - 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: esp_hal::peripherals::SPI2, - dma_channel: Channel<'static, DmaChannel0, Blocking>, - mosi: AnyPin, - mosi_mirror: Output<'static>, -} - -fn execute( - mut spi: SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>, - mut mosi_mirror: Output<'static>, - wanted: u8, -) { - const DMA_BUFFER_SIZE: usize = 4; - - let (rx_buffer, rx_descriptors, buffer, descriptors) = - dma_buffers!(DMA_BUFFER_SIZE, DMA_BUFFER_SIZE); - let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); - let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); - - dma_tx_buf.fill(&[0x00; DMA_BUFFER_SIZE]); - - let transfer = spi - .write( - SpiDataMode::Quad, - Command::Command8(wanted as u16, SpiDataMode::Quad), - Address::Address24( - wanted as u32 | (wanted as u32) << 8 | (wanted as u32) << 16, - SpiDataMode::Quad, - ), - 0, - dma_tx_buf, - ) - .map_err(|e| e.0) - .unwrap(); - (spi, _) = transfer.wait(); - - mosi_mirror.set_high(); - - let transfer = spi - .read( - SpiDataMode::Quad, - Command::None, - Address::None, - 0, - dma_rx_buf, - ) - .map_err(|e| e.0) - .unwrap(); - (_, dma_rx_buf) = transfer.wait(); - assert_eq!(dma_rx_buf.as_slice(), &[wanted; DMA_BUFFER_SIZE]); -} - -#[cfg(test)] -#[embedded_test::tests] -mod tests { - use super::*; - - #[init] - fn init() -> Context { - let peripherals = esp_hal::init(esp_hal::Config::default()); - - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let (mosi, mosi_mirror) = hil_test::common_test_pins!(io); - - let mosi = mosi.degrade(); - let mosi_mirror = Output::new(mosi_mirror, Level::High); - - 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 dma_channel = dma_channel.configure(false, DmaPriority::Priority0); - - Context { - spi: peripherals.SPI2, - dma_channel, - mosi, - mosi_mirror, - } - } - - #[test] - #[timeout(3)] - fn test_spi_writes_correctly_to_pin_0(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, ctx.mosi, NoPin, NoPin, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - super::execute(spi, ctx.mosi_mirror, 0b0001_0001); - } - - #[test] - #[timeout(3)] - fn test_spi_writes_correctly_to_pin_1(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, ctx.mosi, NoPin, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - super::execute(spi, ctx.mosi_mirror, 0b0010_0010); - } - - #[test] - #[timeout(3)] - fn test_spi_writes_correctly_to_pin_2(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, NoPin, ctx.mosi, NoPin, NoPin) - .with_dma(ctx.dma_channel); - - super::execute(spi, ctx.mosi_mirror, 0b0100_0100); - } - - #[test] - #[timeout(3)] - fn test_spi_writes_correctly_to_pin_3(ctx: Context) { - let spi = Spi::new_half_duplex(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_pins(NoPin, NoPin, NoPin, NoPin, ctx.mosi, NoPin) - .with_dma(ctx.dma_channel); - - super::execute(spi, ctx.mosi_mirror, 0b1000_1000); - } -}