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:
parent
3ee8fa61ee
commit
89d9de67eb
@ -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) => {
|
||||
match address_mode {
|
||||
SpiDataMode::Single => {
|
||||
reg_block.ctrl().modify(|_, w| {
|
||||
w.fread_dio()
|
||||
.clear_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()
|
||||
.clear_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::Single, SpiDataMode::Dual) => {
|
||||
address_mode if address_mode == data_mode => {
|
||||
reg_block.ctrl().modify(|_, w| {
|
||||
w.fread_dio()
|
||||
.clear_bit()
|
||||
.fread_qio()
|
||||
.clear_bit()
|
||||
.fread_dual()
|
||||
.set_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()
|
||||
.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()
|
||||
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());
|
||||
}
|
||||
}
|
||||
self
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32)] {
|
||||
let pin_reg = reg_block.pin();
|
||||
} else {
|
||||
let pin_reg = reg_block.misc();
|
||||
}
|
||||
};
|
||||
|
||||
#[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)]
|
||||
|
||||
@ -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]]
|
||||
|
||||
@ -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
400
hil-test/tests/qspi.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user