Enable QSPI tests on ESP32 and clean up (#2198)

* Clean up qspi test cfgs, enable on ESP32

* Correct comments

* Clean up init_spi_data_mode

* Fix qspi_write on ESP32

* Further cleanup in SPI driver

* Clean up qspi_read

* Fix qspi_write_read test on ESP32

* Merge QSPI tests

* Clean up test

* Attempt to fix test GPIO assingment

* Update esp-hal/src/spi/master.rs

Co-authored-by: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com>

* Update esp-hal/src/spi/master.rs

* Make sure pins have no internal pullups

---------

Co-authored-by: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com>
This commit is contained in:
Dániel Buga 2024-09-23 10:37:35 +02:00 committed by GitHub
parent 3ee8fa61ee
commit 89d9de67eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 544 additions and 951 deletions

View File

@ -1329,42 +1329,7 @@ mod dma {
// set cmd, address, dummy cycles // set cmd, address, dummy cycles
let reg_block = self.spi.register_block(); let reg_block = self.spi.register_block();
if !cmd.is_none() { set_up_common_phases(reg_block, cmd, address, dummy);
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) });
}
let result = unsafe { let result = unsafe {
self.spi self.spi
@ -1406,42 +1371,7 @@ mod dma {
// set cmd, address, dummy cycles // set cmd, address, dummy cycles
let reg_block = self.spi.register_block(); let reg_block = self.spi.register_block();
if !cmd.is_none() { set_up_common_phases(reg_block, cmd, address, dummy);
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) });
}
let result = unsafe { let result = unsafe {
self.spi self.spi
@ -2454,34 +2384,22 @@ pub trait Instance: private::Sealed {
fn init(&mut self) { fn init(&mut self) {
let reg_block = self.register_block(); let reg_block = self.register_block();
reg_block.user().modify(|_, w| { reg_block.user().modify(|_, w| {
w.usr_miso_highpart() w.usr_miso_highpart().clear_bit();
.clear_bit() w.usr_mosi_highpart().clear_bit();
.usr_miso_highpart() w.doutdin().set_bit();
.clear_bit() w.usr_miso().set_bit();
.doutdin() w.usr_mosi().set_bit();
.set_bit() w.cs_hold().set_bit();
.usr_miso() w.usr_dummy_idle().set_bit();
.set_bit() w.usr_addr().clear_bit();
.usr_mosi() w.usr_command().clear_bit()
.set_bit()
.cs_hold()
.set_bit()
.usr_dummy_idle()
.set_bit()
.usr_addr()
.clear_bit()
.usr_command()
.clear_bit()
}); });
#[cfg(gdma)] #[cfg(gdma)]
reg_block.clk_gate().modify(|_, w| { reg_block.clk_gate().modify(|_, w| {
w.clk_en() w.clk_en().set_bit();
.set_bit() w.mst_clk_active().set_bit();
.mst_clk_active() w.mst_clk_sel().set_bit()
.set_bit()
.mst_clk_sel()
.set_bit()
}); });
#[cfg(any(esp32c6, esp32h2))] #[cfg(any(esp32c6, esp32h2))]
@ -2495,18 +2413,17 @@ pub trait Instance: private::Sealed {
#[cfg(gdma)] #[cfg(gdma)]
reg_block.ctrl().modify(|_, w| { reg_block.ctrl().modify(|_, w| {
w.q_pol() w.q_pol().clear_bit();
.clear_bit() w.d_pol().clear_bit();
.d_pol() w.hold_pol().clear_bit()
.clear_bit()
.hold_pol()
.clear_bit()
}); });
#[cfg(esp32s2)] #[cfg(esp32s2)]
reg_block reg_block.ctrl().modify(|_, w| {
.ctrl() w.q_pol().clear_bit();
.modify(|_, w| w.q_pol().clear_bit().d_pol().clear_bit().wp().clear_bit()); w.d_pol().clear_bit();
w.wp().clear_bit()
});
#[cfg(esp32)] #[cfg(esp32)]
reg_block.ctrl().modify(|_, w| w.wp().clear_bit()); reg_block.ctrl().modify(|_, w| w.wp().clear_bit());
@ -2525,56 +2442,14 @@ pub trait Instance: private::Sealed {
data_mode: SpiDataMode, data_mode: SpiDataMode,
) { ) {
let reg_block = self.register_block(); let reg_block = self.register_block();
match cmd_mode { reg_block.ctrl().modify(|_, w| {
SpiDataMode::Single => reg_block w.fcmd_dual().bit(cmd_mode == SpiDataMode::Dual);
.ctrl() w.fcmd_quad().bit(cmd_mode == SpiDataMode::Quad);
.modify(|_, w| w.fcmd_dual().clear_bit().fcmd_quad().clear_bit()), w.faddr_dual().bit(address_mode == SpiDataMode::Dual);
SpiDataMode::Dual => reg_block w.faddr_quad().bit(address_mode == SpiDataMode::Quad);
.ctrl() w.fread_dual().bit(data_mode == SpiDataMode::Dual);
.modify(|_, w| w.fcmd_dual().set_bit().fcmd_quad().clear_bit()), w.fread_quad().bit(data_mode == SpiDataMode::Quad)
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());
}
}
} }
#[cfg(esp32)] #[cfg(esp32)]
@ -2590,134 +2465,38 @@ pub trait Instance: private::Sealed {
_ => panic!("Only 1-bit command supported"), _ => panic!("Only 1-bit command supported"),
} }
match (address_mode, data_mode) { match address_mode {
(SpiDataMode::Single, SpiDataMode::Single) => { SpiDataMode::Single => {
reg_block.ctrl().modify(|_, w| { reg_block.ctrl().modify(|_, w| {
w.fread_dio() w.fread_dio().clear_bit();
.clear_bit() w.fread_qio().clear_bit();
.fread_qio() w.fread_dual().bit(data_mode == SpiDataMode::Dual);
.clear_bit() w.fread_quad().bit(data_mode == SpiDataMode::Quad)
.fread_dual()
.clear_bit()
.fread_quad()
.clear_bit()
}); });
reg_block.user().modify(|_, w| { reg_block.user().modify(|_, w| {
w.fwrite_dio() w.fwrite_dio().clear_bit();
.clear_bit() w.fwrite_qio().clear_bit();
.fwrite_qio() w.fwrite_dual().bit(data_mode == SpiDataMode::Dual);
.clear_bit() w.fwrite_quad().bit(data_mode == SpiDataMode::Quad)
.fwrite_dual()
.clear_bit()
.fwrite_quad()
.clear_bit()
}); });
} }
(SpiDataMode::Single, SpiDataMode::Dual) => { address_mode if address_mode == data_mode => {
reg_block.ctrl().modify(|_, w| { reg_block.ctrl().modify(|_, w| {
w.fread_dio() w.fread_dio().bit(address_mode == SpiDataMode::Dual);
.clear_bit() w.fread_qio().bit(address_mode == SpiDataMode::Quad);
.fread_qio() w.fread_dual().clear_bit();
.clear_bit() w.fread_quad().clear_bit()
.fread_dual()
.set_bit()
.fread_quad()
.clear_bit()
}); });
reg_block.user().modify(|_, w| { reg_block.user().modify(|_, w| {
w.fwrite_dio() w.fwrite_dio().bit(address_mode == SpiDataMode::Dual);
.clear_bit() w.fwrite_qio().bit(address_mode == SpiDataMode::Quad);
.fwrite_qio() w.fwrite_dual().clear_bit();
.clear_bit() w.fwrite_quad().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) => {
reg_block.ctrl().modify(|_, w| {
w.fread_dio()
.set_bit()
.fread_qio()
.clear_bit()
.fread_dual()
.clear_bit()
.fread_quad()
.clear_bit()
});
reg_block.user().modify(|_, w| {
w.fwrite_dio()
.set_bit()
.fwrite_qio()
.clear_bit()
.fwrite_dual()
.clear_bit()
.fwrite_quad()
.clear_bit()
});
}
(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) => {
reg_block.ctrl().modify(|_, w| {
w.fread_dio()
.clear_bit()
.fread_qio()
.set_bit()
.fread_dual()
.clear_bit()
.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()
}); });
} }
_ => 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 { fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self {
let reg_block = self.register_block(); let reg_block = self.register_block();
match data_mode { cfg_if::cfg_if! {
SpiMode::Mode0 => { if #[cfg(esp32)] {
reg_block.misc().modify(|_, w| w.ck_idle_edge().clear_bit()); let pin_reg = reg_block.pin();
reg_block.user().modify(|_, w| w.ck_out_edge().clear_bit()); } else {
} let pin_reg = reg_block.misc();
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());
}
}
self
} }
};
#[cfg(esp32)] pin_reg.modify(|_, w| {
fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self { w.ck_idle_edge()
let reg_block = self.register_block(); .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 self
} }
@ -2929,12 +2681,9 @@ pub trait Instance: private::Sealed {
// Disable clock source // Disable clock source
#[cfg(gdma)] #[cfg(gdma)]
self.register_block().clk_gate().modify(|_, w| { self.register_block().clk_gate().modify(|_, w| {
w.clk_en() w.clk_en().clear_bit();
.clear_bit() w.mst_clk_active().clear_bit();
.mst_clk_active() w.mst_clk_sel().clear_bit()
.clear_bit()
.mst_clk_sel()
.clear_bit()
}); });
// Change clock frequency // Change clock frequency
@ -2943,12 +2692,9 @@ pub trait Instance: private::Sealed {
// Enable clock source // Enable clock source
#[cfg(gdma)] #[cfg(gdma)]
self.register_block().clk_gate().modify(|_, w| { self.register_block().clk_gate().modify(|_, w| {
w.clk_en() w.clk_en().set_bit();
.set_bit() w.mst_clk_active().set_bit();
.mst_clk_active() w.mst_clk_sel().set_bit()
.set_bit()
.mst_clk_sel()
.set_bit()
}); });
} }
@ -3156,36 +2902,23 @@ pub trait Instance: private::Sealed {
) { ) {
let reg_block = self.register_block(); let reg_block = self.register_block();
reg_block.user().modify(|_, w| { reg_block.user().modify(|_, w| {
w.usr_miso_highpart() w.usr_miso_highpart().clear_bit();
.clear_bit() w.usr_mosi_highpart().clear_bit();
.usr_miso_highpart() w.doutdin().clear_bit();
.clear_bit() w.usr_miso().bit(!is_write && !no_mosi_miso);
.doutdin() w.usr_mosi().bit(is_write && !no_mosi_miso);
.clear_bit() w.cs_hold().set_bit();
.usr_miso() w.usr_dummy_idle().bit(dummy_idle);
.bit(!is_write && !no_mosi_miso) w.usr_dummy().bit(dummy_state);
.usr_mosi() w.usr_addr().bit(address_state);
.bit(is_write && !no_mosi_miso) w.usr_command().bit(command_state)
.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)
}); });
#[cfg(gdma)] #[cfg(gdma)]
reg_block.clk_gate().modify(|_, w| { reg_block.clk_gate().modify(|_, w| {
w.clk_en() w.clk_en().set_bit();
.set_bit() w.mst_clk_active().set_bit();
.mst_clk_active() w.mst_clk_sel().set_bit()
.set_bit()
.mst_clk_sel()
.set_bit()
}); });
#[cfg(any(esp32c6, esp32h2))] #[cfg(any(esp32c6, esp32h2))]
@ -3223,42 +2956,7 @@ pub trait Instance: private::Sealed {
// set cmd, address, dummy cycles // set cmd, address, dummy cycles
let reg_block = self.register_block(); let reg_block = self.register_block();
if !cmd.is_none() { set_up_common_phases(reg_block, cmd, address, dummy);
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) });
}
if !buffer.is_empty() { if !buffer.is_empty() {
// re-using the full-duplex write here // re-using the full-duplex write here
@ -3288,42 +2986,7 @@ pub trait Instance: private::Sealed {
// set cmd, address, dummy cycles // set cmd, address, dummy cycles
let reg_block = self.register_block(); let reg_block = self.register_block();
if !cmd.is_none() { set_up_common_phases(reg_block, cmd, address, dummy);
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) });
}
self.configure_datalen(buffer.len(), 0); self.configure_datalen(buffer.len(), 0);
self.start_operation(); 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))] #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))]
impl Instance for crate::peripherals::SPI2 { impl Instance for crate::peripherals::SPI2 {
#[inline(always)] #[inline(always)]

View File

@ -72,15 +72,7 @@ name = "lcd_cam_i8080_async"
harness = false harness = false
[[test]] [[test]]
name = "qspi_read" name = "qspi"
harness = false
[[test]]
name = "qspi_write"
harness = false
[[test]]
name = "qspi_write_read"
harness = false harness = false
[[test]] [[test]]

View File

@ -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
}
}
}};
}

400
hil-test/tests/qspi.rs Normal file
View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}