From 4aab18f429a617979ccd60d7b2dfcaae482e5e4f Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 25 Apr 2024 11:17:46 +0200 Subject: [PATCH 1/6] SPI Slave for ESP32 / ESP32-S2 --- esp-hal/src/spi/mod.rs | 1 - esp-hal/src/spi/slave.rs | 251 ++++++++++++++++++++++-------- examples/src/bin/spi_slave_dma.rs | 180 +++++++++++++-------- 3 files changed, 305 insertions(+), 127 deletions(-) diff --git a/esp-hal/src/spi/mod.rs b/esp-hal/src/spi/mod.rs index 1236b6e7510..af2b14c82b0 100644 --- a/esp-hal/src/spi/mod.rs +++ b/esp-hal/src/spi/mod.rs @@ -7,7 +7,6 @@ use crate::dma::DmaError; pub mod master; -#[cfg(all(any(spi0, spi1, spi2, spi3), not(pdma)))] pub mod slave; /// SPI errors diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 648184ab9f8..3c99deb74a5 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -186,14 +186,6 @@ pub mod dma { ) -> SpiDma<'d, crate::peripherals::SPI2, C, DmaMode> { channel.tx.init_channel(); // no need to call this for both, TX and RX - #[cfg(esp32)] - match self.data_mode { - SpiMode::Mode0 | SpiMode::Mode2 => { - self.spi.invert_i_edge(); - } - _ => {} - } - SpiDma { spi: self.spi, channel, @@ -215,14 +207,6 @@ pub mod dma { ) -> SpiDma<'d, crate::peripherals::SPI3, C, DmaMode> { channel.tx.init_channel(); // no need to call this for both, TX and RX - #[cfg(esp32)] - match self.data_mode { - SpiMode::Mode0 | SpiMode::Mode2 => { - self.spi.invert_i_edge(); - } - _ => {} - } - SpiDma { spi: self.spi, channel, @@ -423,20 +407,25 @@ where self.enable_dma(); reset_dma_before_load_dma_dscr(reg_block); - tx.prepare_transfer_without_start( - self.dma_peripheral(), - false, - write_buffer_ptr, - write_buffer_len, - )?; + rx.prepare_transfer_without_start( false, self.dma_peripheral(), read_buffer_ptr, read_buffer_len, )?; + #[cfg(any(esp32, esp32s2))] + rx.start_transfer()?; + + tx.prepare_transfer_without_start( + self.dma_peripheral(), + false, + write_buffer_ptr, + write_buffer_len, + )?; + #[cfg(any(esp32, esp32s2))] + tx.start_transfer()?; - self.clear_dma_interrupts(); reset_dma_before_usr_cmd(reg_block); // On the esp32, all full-duplex transfers are single, and all half-duplex @@ -447,8 +436,31 @@ where .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); - tx.start_transfer()?; - Ok(rx.start_transfer()?) + #[cfg(esp32)] + { + reg_block + .slv_rdbuf_dlen() + .write(|w| w.slv_rdbuf_dbitlen().bits((read_buffer_len * 8 - 1) as u32)); + reg_block.slv_wrbuf_dlen().write(|w| { + w.slv_wrbuf_dbitlen() + .bits((write_buffer_len * 8 - 1) as u32) + }); + + reg_block.user().modify(|_, w| w.usr_mosi().set_bit()); + reg_block.user().modify(|_, w| w.usr_miso().set_bit()); + } + + self.clear_dma_interrupts(); + self.setup_for_flush(); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); + + #[cfg(not(any(esp32, esp32s2)))] + { + rx.start_transfer()?; + tx.start_transfer()?; + } + + Ok(()) } fn start_write_bytes_dma( @@ -464,9 +476,11 @@ where self.enable_dma(); reset_dma_before_load_dma_dscr(reg_block); + tx.prepare_transfer_without_start(self.dma_peripheral(), false, ptr, len)?; + #[cfg(any(esp32, esp32s2))] + tx.start_transfer()?; - self.clear_dma_interrupts(); reset_dma_before_usr_cmd(reg_block); // On the esp32, all full-duplex transfers are single, and all half-duplex @@ -477,7 +491,25 @@ where .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); - Ok(tx.start_transfer()?) + #[cfg(esp32)] + { + reg_block + .slv_rdbuf_dlen() + .write(|w| unsafe { w.slv_rdbuf_dbitlen().bits((len * 8 - 1) as u32) }); + + reg_block.user().modify(|_, w| w.usr_mosi().set_bit()); + } + + self.clear_dma_interrupts(); + self.setup_for_flush(); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); + + #[cfg(not(any(esp32, esp32s2)))] + { + tx.start_transfer()?; + } + + Ok(()) } unsafe fn start_read_bytes_dma( @@ -493,11 +525,10 @@ where self.enable_dma(); reset_dma_before_load_dma_dscr(reg_block); - unsafe { - rx.prepare_transfer_without_start(false, self.dma_peripheral(), ptr, len)?; - } + rx.prepare_transfer_without_start(false, self.dma_peripheral(), ptr, len)?; + #[cfg(any(esp32, esp32s2))] + rx.start_transfer()?; - self.clear_dma_interrupts(); reset_dma_before_usr_cmd(reg_block); // On the esp32, all full-duplex transfers are single, and all half-duplex @@ -508,7 +539,29 @@ where .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); - Ok(rx.start_transfer()?) + #[cfg(esp32)] + { + reg_block + .slv_rdbuf_dlen() + .write(|w| unsafe { w.slv_rdbuf_dbitlen().bits((len * 8 - 1) as u32) }); + + reg_block + .slv_wrbuf_dlen() + .write(|w| w.slv_wrbuf_dbitlen().bits((len * 8 - 1) as u32)); + + reg_block.user().modify(|_, w| w.usr_miso().set_bit()); + } + + self.clear_dma_interrupts(); + self.setup_for_flush(); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); + + #[cfg(not(any(esp32, esp32s2)))] + { + rx.start_transfer()?; + } + + Ok(()) } fn dma_peripheral(&self) -> DmaPeripheral { @@ -558,7 +611,7 @@ where #[cfg(any(esp32, esp32s2))] fn clear_dma_interrupts(&self) { let reg_block = self.register_block(); - reg_block.dma_int_clr.write(|w| { + reg_block.dma_int_clr().write(|w| { w.inlink_dscr_empty() .clear_bit_by_one() .outlink_dscr_error() @@ -593,15 +646,24 @@ fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { }); } -#[cfg(any(esp32, esp32s2))] -fn reset_dma_before_usr_cmd(_reg_block: &RegisterBlock) {} +#[cfg(any(esp32))] +fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { + reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); + reg_block.slave().modify(|_, w| w.sync_reset().clear_bit()); +} + +#[cfg(any(esp32s2))] +fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { + reg_block.slave().modify(|_, w| w.soft_reset().set_bit()); + reg_block.slave().modify(|_, w| w.soft_reset().clear_bit()); +} #[cfg(not(any(esp32, esp32s2)))] fn reset_dma_before_load_dma_dscr(_reg_block: &RegisterBlock) {} #[cfg(any(esp32, esp32s2))] fn reset_dma_before_load_dma_dscr(reg_block: &RegisterBlock) { - reg_block.dma_conf.modify(|_, w| { + reg_block.dma_conf().modify(|_, w| { w.out_rst() .set_bit() .in_rst() @@ -611,6 +673,27 @@ fn reset_dma_before_load_dma_dscr(reg_block: &RegisterBlock) { .ahbm_rst() .set_bit() }); + + #[cfg(esp32s2)] + reg_block + .dma_conf() + .modify(|_, w| w.dma_infifo_full_clr().set_bit()); + + reg_block.dma_conf().modify(|_, w| { + w.out_rst() + .clear_bit() + .in_rst() + .clear_bit() + .ahbm_fifo_rst() + .clear_bit() + .ahbm_rst() + .clear_bit() + }); + + #[cfg(esp32s2)] + reg_block + .dma_conf() + .modify(|_, w| w.dma_infifo_full_clr().clear_bit()); } impl InstanceDma for crate::peripherals::SPI2 @@ -647,6 +730,11 @@ pub trait Instance: private::Sealed { /// Initialize for full-duplex 1 bit mode fn init(&mut self) { let reg_block = self.register_block(); + + reg_block.clock().write(|w| unsafe { w.bits(0) }); + reg_block.user().write(|w| unsafe { w.bits(0) }); + reg_block.ctrl().write(|w| unsafe { w.bits(0) }); + reg_block.slave().write(|w| w.mode().set_bit()); reg_block.user().modify(|_, w| { @@ -655,15 +743,17 @@ pub trait Instance: private::Sealed { .doutdin() .set_bit() .usr_miso() - .set_bit() + .clear_bit() .usr_mosi() - .set_bit() + .clear_bit() .usr_dummy_idle() .clear_bit() .usr_addr() .clear_bit() .usr_command() .clear_bit() + .sio() + .clear_bit() }); #[cfg(not(any(esp32, esp32s2)))] @@ -708,7 +798,7 @@ pub trait Instance: private::Sealed { .user() .modify(|_, w| w.tsck_i_edge().clear_bit().rsck_i_edge().clear_bit()); #[cfg(esp32s2)] - reg_block.ctrl1.modify(|_, w| w.clk_mode_13().clear_bit()); + reg_block.ctrl1().modify(|_, w| w.clk_mode_13().clear_bit()); #[cfg(not(esp32s2))] reg_block.slave().modify(|_, w| w.clk_mode_13().clear_bit()); } @@ -746,40 +836,77 @@ pub trait Instance: private::Sealed { #[cfg(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.pin.modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user.modify(|_, w| w.ck_i_edge().clear_bit()); + reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); + reg_block.user().modify(|_, w| w.ck_i_edge().set_bit()); + reg_block.ctrl2().modify(|_, w| unsafe { + w.miso_delay_mode().bits(0); + w.miso_delay_num().bits(0); + w.mosi_delay_mode().bits(2); + w.mosi_delay_num().bits(2) + }); } SpiMode::Mode1 => { - reg_block.pin.modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user.modify(|_, w| w.ck_i_edge().set_bit()); + reg_block.pin().modify(|_, w| w.ck_idle_edge().set_bit()); + reg_block.user().modify(|_, w| w.ck_i_edge().set_bit()); + reg_block.ctrl2().modify(|_, w| unsafe { + w.miso_delay_mode().bits(2); + w.miso_delay_num().bits(0); + w.mosi_delay_mode().bits(0); + w.mosi_delay_num().bits(0) + }); } SpiMode::Mode2 => { - reg_block.pin.modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user.modify(|_, w| w.ck_i_edge().set_bit()); + reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); + reg_block.user().modify(|_, w| w.ck_i_edge().set_bit()); + reg_block.ctrl2().modify(|_, w| unsafe { + w.miso_delay_mode().bits(0); + w.miso_delay_num().bits(0); + w.mosi_delay_mode().bits(1); + w.mosi_delay_num().bits(2) + }); } SpiMode::Mode3 => { - reg_block.pin.modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user.modify(|_, w| w.ck_i_edge().clear_bit()); + reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); + reg_block.user().modify(|_, w| w.ck_i_edge().clear_bit()); + reg_block.ctrl2().modify(|_, w| unsafe { + w.miso_delay_mode().bits(1); + w.miso_delay_num().bits(0); + w.mosi_delay_mode().bits(0); + w.mosi_delay_num().bits(0) + }); } } - self - } - // The ESP32 needs its _edge bits inverted in DMA slave mode, when in mode 0 or - // 2. set_data_mode above sets the registers up for non-DMA mode. - #[cfg(esp32)] - fn invert_i_edge(&self) { - let reg_block = self.register_block(); + // Silicon issues exists in mode 0 and 2 with DMA, change clock phase to + // avoid dma issue. This will cause slave output to appear at most half a + // spi clock before + match data_mode { + SpiMode::Mode0 => { + reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); + reg_block.user().modify(|_, w| w.ck_i_edge().set_bit()); + reg_block.ctrl2().modify(|_, w| unsafe { + w.miso_delay_mode().bits(0); + w.miso_delay_num().bits(2); + w.mosi_delay_mode().bits(0); + w.mosi_delay_num().bits(3) + }); + } + SpiMode::Mode2 => { + reg_block.pin().modify(|_, w| w.ck_idle_edge().set_bit()); + reg_block.user().modify(|_, w| w.ck_i_edge().clear_bit()); + reg_block.ctrl2().modify(|_, w| unsafe { + w.miso_delay_mode().bits(0); + w.miso_delay_num().bits(2); + w.mosi_delay_mode().bits(0); + w.mosi_delay_num().bits(3) + }); + } + _ => (), + } - reg_block - .pin - .modify(|r, w| w.ck_idle_edge().variant(r.ck_idle_edge().bit_is_clear())); - reg_block - .user - .modify(|r, w| w.ck_i_edge().variant(r.ck_i_edge().bit_is_clear())); + self } fn is_bus_busy(&self) -> bool { @@ -787,7 +914,7 @@ pub trait Instance: private::Sealed { #[cfg(any(esp32, esp32s2))] { - reg_block.slave.read().trans_done().bit_is_clear() + reg_block.slave().read().trans_done().bit_is_clear() } #[cfg(not(any(esp32, esp32s2)))] { diff --git a/examples/src/bin/spi_slave_dma.rs b/examples/src/bin/spi_slave_dma.rs index 13c57c4e4d9..2878b66ebe6 100644 --- a/examples/src/bin/spi_slave_dma.rs +++ b/examples/src/bin/spi_slave_dma.rs @@ -1,6 +1,20 @@ //! SPI slave loopback test using DMA //! +//! ESP32 / ESP32-S2 //! Following pins are used for the slave: +//! SCLK GPIO2 +//! MISO GPIO4 +//! MOSI GPIO15 +//! CS GPIO17 +//! +//! Following pins are used for the (bitbang) master: +//! SCLK GPIO0 +//! MISO GPIO5 +//! MOSI GPIO16 +//! CS GPIO18 +//! +//! Other chips: +//! //! SCLK GPIO0 //! MISO GPIO1 //! MOSI GPIO2 @@ -23,10 +37,11 @@ //! pins except SCLK. SCLK is between MOSI and VDD3P3_RTC on the barebones chip, //! so no immediate neighbor is available. -//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 +//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 esp32 esp32s2 #![no_std] #![no_main] +#![feature(asm_experimental_arch)] use esp_backtrace as _; use esp_hal::{ @@ -45,6 +60,20 @@ use esp_hal::{ }; use esp_println::println; +cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + const MASTER_CS: u8 = 18; + const MASTER_MOSI: u8 = 16; + const MASTER_SCLK: u8 = 0; + const MASTER_MISO: u8 = 5; + } else { + const MASTER_CS: u8 = 9; + const MASTER_MOSI: u8 = 8; + const MASTER_SCLK: u8 = 4; + const MASTER_MISO: u8 = 5; + } +} + #[entry] fn main() -> ! { let peripherals = Peripherals::take(); @@ -65,7 +94,13 @@ fn main() -> ! { master_mosi.set_low(); let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = dma.spi2channel; + } else { + let dma_channel = dma.channel0; + } + } let (tx_buffer, mut tx_descriptors, rx_buffer, mut rx_descriptors) = dma_buffers!(32000); @@ -108,39 +143,23 @@ fn main() -> ! { slave_receive.fill(0xff); i = i.wrapping_add(1); + println!("Iteration {i}"); + + println!("Do `dma_transfer`"); + let transfer = spi .dma_transfer(&mut slave_send, &mut slave_receive) .unwrap(); - // Bit-bang out the contents of master_send and read into master_receive - // as quickly as manageable. MSB first. Mode 0, so sampled on the rising - // edge and set on the falling edge. - master_cs.set_low(); - for (j, v) in master_send.iter().enumerate() { - let mut b = *v; - let mut rb = 0u8; - for _ in 0..8 { - if b & 128 != 0 { - master_mosi.set_high(); - } else { - master_mosi.set_low(); - } - master_sclk.set_low(); - b <<= 1; - rb <<= 1; - // NB: adding about 24 NOPs here makes the clock's duty cycle - // run at about 50% ... but we don't strictly need the delay, - // either. - master_sclk.set_high(); - if master_miso.is_high() { - rb |= 1; - } - } - master_receive[j] = rb; - } - master_cs.set_high(); - master_sclk.set_low(); - // the buffers and spi is moved into the transfer and we can get it back via - // `wait` + + bitbang_master( + master_send, + master_receive, + &mut master_cs, + &mut master_mosi, + &mut master_sclk, + &master_miso, + ); + transfer.wait().unwrap(); println!( "slave got {:x?} .. {:x?}, master got {:x?} .. {:x?}", @@ -152,25 +171,19 @@ fn main() -> ! { delay.delay_millis(250); + println!("Do `dma_read`"); slave_receive.fill(0xff); let transfer = spi.dma_read(&mut slave_receive).unwrap(); - master_cs.set_high(); - - master_cs.set_low(); - for v in master_send.iter() { - let mut b = *v; - for _ in 0..8 { - if b & 128 != 0 { - master_mosi.set_high(); - } else { - master_mosi.set_low(); - } - b <<= 1; - master_sclk.set_low(); - master_sclk.set_high(); - } - } - master_cs.set_high(); + + bitbang_master( + master_send, + master_receive, + &mut master_cs, + &mut master_mosi, + &mut master_sclk, + &master_miso, + ); + transfer.wait().unwrap(); println!( "slave got {:x?} .. {:x?}", @@ -179,31 +192,70 @@ fn main() -> ! { ); delay.delay_millis(250); + + println!("Do `dma_write`"); let transfer = spi.dma_write(&mut slave_send).unwrap(); master_receive.fill(0); - master_cs.set_low(); - for (j, _) in master_send.iter().enumerate() { - let mut rb = 0u8; - for _ in 0..8 { - master_sclk.set_low(); - rb <<= 1; - master_sclk.set_high(); - if master_miso.is_high() { - rb |= 1; - } - } - master_receive[j] = rb; - } - master_cs.set_high(); - transfer.wait().unwrap(); + bitbang_master( + master_send, + master_receive, + &mut master_cs, + &mut master_mosi, + &mut master_sclk, + &master_miso, + ); + transfer.wait().unwrap(); println!( "master got {:x?} .. {:x?}", &master_receive[..10], &master_receive[master_receive.len() - 10..], ); + + delay.delay_millis(250); + println!(); } } + +fn bitbang_master( + master_send: &[u8], + master_receive: &mut [u8], + master_cs: &mut GpioPin, MASTER_CS>, + master_mosi: &mut GpioPin, MASTER_MOSI>, + master_sclk: &mut GpioPin, MASTER_SCLK>, + master_miso: &GpioPin, MASTER_MISO>, +) { + // Bit-bang out the contents of master_send and read into master_receive + // as quickly as manageable. MSB first. Mode 0, so sampled on the rising + // edge and set on the falling edge. + master_cs.set_low(); + for (j, v) in master_send.iter().enumerate() { + let mut b = *v; + let mut rb = 0u8; + for _ in 0..8 { + if b & 128 != 0 { + master_mosi.set_high(); + } else { + master_mosi.set_low(); + } + master_sclk.set_low(); + b <<= 1; + rb <<= 1; + // NB: adding about 24 NOPs here makes the clock's duty cycle + // run at about 50% ... but we don't strictly need the delay, + // either. + master_sclk.set_high(); + if master_miso.is_high() { + rb |= 1; + } + } + master_receive[j] = rb; + } + master_sclk.set_low(); + + master_cs.set_high(); + master_sclk.set_low(); +} From 53f4224ef7be4d00976a30cb1f0ae446550dab27 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Fri, 26 Apr 2024 17:02:13 +0200 Subject: [PATCH 2/6] CHANGELOG.md --- esp-hal/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 7ba7be5cfec..39c02b8ac2b 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `time::current_time` API (#1503) - ESP32-S3: Add LCD_CAM Camera driver (#1483) - `embassy-usb` support (#1517) +- SPI Slave support for ESP32, ESP32-S2 (#1514) ### Fixed From ac30a31a37a56bbcec861f395aa28159974c489f Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Fri, 26 Apr 2024 17:06:09 +0200 Subject: [PATCH 3/6] Clippy... --- esp-hal/src/spi/slave.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 3c99deb74a5..37e8b488017 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -646,13 +646,13 @@ fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { }); } -#[cfg(any(esp32))] +#[cfg(esp32)] fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); reg_block.slave().modify(|_, w| w.sync_reset().clear_bit()); } -#[cfg(any(esp32s2))] +#[cfg(esp32s2)] fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { reg_block.slave().modify(|_, w| w.soft_reset().set_bit()); reg_block.slave().modify(|_, w| w.soft_reset().clear_bit()); From d1c8584a4f008b75ef6a1619b5e50193b2135c97 Mon Sep 17 00:00:00 2001 From: Juraj Sadel Date: Fri, 17 May 2024 15:59:30 +0200 Subject: [PATCH 4/6] Revert ESP32 changes and keep ESP32-S2 only --- esp-hal/CHANGELOG.md | 2 +- esp-hal/src/spi/mod.rs | 1 + esp-hal/src/spi/slave.rs | 244 +++--------------------------- examples/src/bin/spi_slave_dma.rs | 43 +----- 4 files changed, 31 insertions(+), 259 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 39c02b8ac2b..dd44a293a4f 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `time::current_time` API (#1503) - ESP32-S3: Add LCD_CAM Camera driver (#1483) - `embassy-usb` support (#1517) -- SPI Slave support for ESP32, ESP32-S2 (#1514) +- SPI Slave support for ESP32-S2 (#1514) ### Fixed diff --git a/esp-hal/src/spi/mod.rs b/esp-hal/src/spi/mod.rs index af2b14c82b0..23f3fc7ca9d 100644 --- a/esp-hal/src/spi/mod.rs +++ b/esp-hal/src/spi/mod.rs @@ -7,6 +7,7 @@ use crate::dma::DmaError; pub mod master; +#[cfg(not(esp32))] pub mod slave; /// SPI errors diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 37e8b488017..9cc3d83e995 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -414,7 +414,7 @@ where read_buffer_ptr, read_buffer_len, )?; - #[cfg(any(esp32, esp32s2))] + #[cfg(esp32s2)] rx.start_transfer()?; tx.prepare_transfer_without_start( @@ -423,38 +423,23 @@ where write_buffer_ptr, write_buffer_len, )?; - #[cfg(any(esp32, esp32s2))] + #[cfg(esp32s2)] tx.start_transfer()?; reset_dma_before_usr_cmd(reg_block); - // On the esp32, all full-duplex transfers are single, and all half-duplex + // Note: On the esp32, all full-duplex transfers are single, and all half-duplex // transfers use the cmd/addr/dummy/data sequence (but are still // single). - #[cfg(not(esp32))] reg_block .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); - #[cfg(esp32)] - { - reg_block - .slv_rdbuf_dlen() - .write(|w| w.slv_rdbuf_dbitlen().bits((read_buffer_len * 8 - 1) as u32)); - reg_block.slv_wrbuf_dlen().write(|w| { - w.slv_wrbuf_dbitlen() - .bits((write_buffer_len * 8 - 1) as u32) - }); - - reg_block.user().modify(|_, w| w.usr_mosi().set_bit()); - reg_block.user().modify(|_, w| w.usr_miso().set_bit()); - } - self.clear_dma_interrupts(); self.setup_for_flush(); reg_block.cmd().modify(|_, w| w.usr().set_bit()); - #[cfg(not(any(esp32, esp32s2)))] + #[cfg(not(esp32s2))] { rx.start_transfer()?; tx.start_transfer()?; @@ -478,33 +463,23 @@ where reset_dma_before_load_dma_dscr(reg_block); tx.prepare_transfer_without_start(self.dma_peripheral(), false, ptr, len)?; - #[cfg(any(esp32, esp32s2))] + #[cfg(esp32s2)] tx.start_transfer()?; reset_dma_before_usr_cmd(reg_block); - // On the esp32, all full-duplex transfers are single, and all half-duplex + // Note: On the esp32, all full-duplex transfers are single, and all half-duplex // transfers use the cmd/addr/dummy/data sequence (but are still // single). - #[cfg(not(esp32))] reg_block .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); - #[cfg(esp32)] - { - reg_block - .slv_rdbuf_dlen() - .write(|w| unsafe { w.slv_rdbuf_dbitlen().bits((len * 8 - 1) as u32) }); - - reg_block.user().modify(|_, w| w.usr_mosi().set_bit()); - } - self.clear_dma_interrupts(); self.setup_for_flush(); reg_block.cmd().modify(|_, w| w.usr().set_bit()); - #[cfg(not(any(esp32, esp32s2)))] + #[cfg(not(esp32s2))] { tx.start_transfer()?; } @@ -526,37 +501,23 @@ where reset_dma_before_load_dma_dscr(reg_block); rx.prepare_transfer_without_start(false, self.dma_peripheral(), ptr, len)?; - #[cfg(any(esp32, esp32s2))] + #[cfg(esp32s2)] rx.start_transfer()?; reset_dma_before_usr_cmd(reg_block); - // On the esp32, all full-duplex transfers are single, and all half-duplex + // Note: On the esp32, all full-duplex transfers are single, and all half-duplex // transfers use the cmd/addr/dummy/data sequence (but are still // single). - #[cfg(not(esp32))] reg_block .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); - #[cfg(esp32)] - { - reg_block - .slv_rdbuf_dlen() - .write(|w| unsafe { w.slv_rdbuf_dbitlen().bits((len * 8 - 1) as u32) }); - - reg_block - .slv_wrbuf_dlen() - .write(|w| w.slv_wrbuf_dbitlen().bits((len * 8 - 1) as u32)); - - reg_block.user().modify(|_, w| w.usr_miso().set_bit()); - } - self.clear_dma_interrupts(); self.setup_for_flush(); reg_block.cmd().modify(|_, w| w.usr().set_bit()); - #[cfg(not(any(esp32, esp32s2)))] + #[cfg(not(esp32s2))] { rx.start_transfer()?; } @@ -586,7 +547,7 @@ where }); } - #[cfg(any(esp32, esp32s2))] + #[cfg(esp32s2)] fn enable_dma(&self) { // for non GDMA this is done in `assign_tx_device` / `assign_rx_device` } @@ -608,7 +569,7 @@ where }); } - #[cfg(any(esp32, esp32s2))] + #[cfg(esp32s2)] fn clear_dma_interrupts(&self) { let reg_block = self.register_block(); reg_block.dma_int_clr().write(|w| { @@ -634,7 +595,7 @@ where } } -#[cfg(not(any(esp32, esp32s2)))] +#[cfg(not(esp32s2))] fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { reg_block.dma_conf().modify(|_, w| { w.rx_afifo_rst() @@ -646,22 +607,16 @@ fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { }); } -#[cfg(esp32)] -fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { - reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); - reg_block.slave().modify(|_, w| w.sync_reset().clear_bit()); -} - #[cfg(esp32s2)] fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { reg_block.slave().modify(|_, w| w.soft_reset().set_bit()); reg_block.slave().modify(|_, w| w.soft_reset().clear_bit()); } -#[cfg(not(any(esp32, esp32s2)))] +#[cfg(not(esp32s2))] fn reset_dma_before_load_dma_dscr(_reg_block: &RegisterBlock) {} -#[cfg(any(esp32, esp32s2))] +#[cfg(esp32s2)] fn reset_dma_before_load_dma_dscr(reg_block: &RegisterBlock) { reg_block.dma_conf().modify(|_, w| { w.out_rst() @@ -756,7 +711,7 @@ pub trait Instance: private::Sealed { .clear_bit() }); - #[cfg(not(any(esp32, esp32s2)))] + #[cfg(not(esp32s2))] reg_block.clk_gate().modify(|_, w| { w.clk_en() .clear_bit() @@ -766,7 +721,7 @@ pub trait Instance: private::Sealed { .clear_bit() }); - #[cfg(not(any(esp32, esp32s2)))] + #[cfg(not(esp32s2))] reg_block.ctrl().modify(|_, w| { w.q_pol() .clear_bit() @@ -781,14 +736,9 @@ pub trait Instance: private::Sealed { .ctrl() .modify(|_, w| w.q_pol().clear_bit().d_pol().clear_bit().wp().clear_bit()); - #[cfg(esp32)] - reg_block.ctrl().modify(|_, w| w.wp().clear_bit()); - - #[cfg(not(esp32))] reg_block.misc().write(|w| unsafe { w.bits(0) }); } - #[cfg(not(esp32))] fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self { let reg_block = self.register_block(); @@ -833,90 +783,14 @@ pub trait Instance: private::Sealed { self } - #[cfg(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.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user().modify(|_, w| w.ck_i_edge().set_bit()); - reg_block.ctrl2().modify(|_, w| unsafe { - w.miso_delay_mode().bits(0); - w.miso_delay_num().bits(0); - w.mosi_delay_mode().bits(2); - w.mosi_delay_num().bits(2) - }); - } - SpiMode::Mode1 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user().modify(|_, w| w.ck_i_edge().set_bit()); - reg_block.ctrl2().modify(|_, w| unsafe { - w.miso_delay_mode().bits(2); - w.miso_delay_num().bits(0); - w.mosi_delay_mode().bits(0); - w.mosi_delay_num().bits(0) - }); - } - SpiMode::Mode2 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user().modify(|_, w| w.ck_i_edge().set_bit()); - reg_block.ctrl2().modify(|_, w| unsafe { - w.miso_delay_mode().bits(0); - w.miso_delay_num().bits(0); - w.mosi_delay_mode().bits(1); - w.mosi_delay_num().bits(2) - }); - } - SpiMode::Mode3 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user().modify(|_, w| w.ck_i_edge().clear_bit()); - reg_block.ctrl2().modify(|_, w| unsafe { - w.miso_delay_mode().bits(1); - w.miso_delay_num().bits(0); - w.mosi_delay_mode().bits(0); - w.mosi_delay_num().bits(0) - }); - } - } - - // Silicon issues exists in mode 0 and 2 with DMA, change clock phase to - // avoid dma issue. This will cause slave output to appear at most half a - // spi clock before - match data_mode { - SpiMode::Mode0 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user().modify(|_, w| w.ck_i_edge().set_bit()); - reg_block.ctrl2().modify(|_, w| unsafe { - w.miso_delay_mode().bits(0); - w.miso_delay_num().bits(2); - w.mosi_delay_mode().bits(0); - w.mosi_delay_num().bits(3) - }); - } - SpiMode::Mode2 => { - reg_block.pin().modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user().modify(|_, w| w.ck_i_edge().clear_bit()); - reg_block.ctrl2().modify(|_, w| unsafe { - w.miso_delay_mode().bits(0); - w.miso_delay_num().bits(2); - w.mosi_delay_mode().bits(0); - w.mosi_delay_num().bits(3) - }); - } - _ => (), - } - - self - } - fn is_bus_busy(&self) -> bool { let reg_block = self.register_block(); - #[cfg(any(esp32, esp32s2))] + #[cfg(esp32s2)] { reg_block.slave().read().trans_done().bit_is_clear() } - #[cfg(not(any(esp32, esp32s2)))] + #[cfg(not(esp32s2))] { reg_block.dma_int_raw().read().trans_done().bit_is_clear() } @@ -933,11 +807,11 @@ pub trait Instance: private::Sealed { // Clear the transaction-done interrupt flag so flush() can work properly. Not // used in DMA mode. fn setup_for_flush(&self) { - #[cfg(any(esp32, esp32s2))] + #[cfg(esp32s2)] self.register_block() .slave() .modify(|_, w| w.trans_done().clear_bit()); - #[cfg(not(any(esp32, esp32s2)))] + #[cfg(not(esp32s2))] self.register_block() .dma_int_clr() .write(|w| w.trans_done().clear_bit_by_one()); @@ -982,82 +856,6 @@ impl Instance for crate::peripherals::SPI2 { } } -#[cfg(esp32)] -impl Instance for crate::peripherals::SPI2 { - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn sclk_signal(&self) -> InputSignal { - InputSignal::HSPICLK - } - - #[inline(always)] - fn mosi_signal(&self) -> InputSignal { - InputSignal::HSPID - } - - #[inline(always)] - fn miso_signal(&self) -> OutputSignal { - OutputSignal::HSPIQ - } - - #[inline(always)] - fn cs_signal(&self) -> InputSignal { - InputSignal::HSPICS0 - } - - #[inline(always)] - fn enable_peripheral(&self) { - PeripheralClockControl::enable(crate::system::Peripheral::Spi2); - } - - #[inline(always)] - fn spi_num(&self) -> u8 { - 2 - } -} - -#[cfg(esp32)] -impl Instance for crate::peripherals::SPI3 { - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn sclk_signal(&self) -> InputSignal { - InputSignal::VSPICLK - } - - #[inline(always)] - fn mosi_signal(&self) -> InputSignal { - InputSignal::VSPID - } - - #[inline(always)] - fn miso_signal(&self) -> OutputSignal { - OutputSignal::VSPIQ - } - - #[inline(always)] - fn cs_signal(&self) -> InputSignal { - InputSignal::VSPICS0 - } - - #[inline(always)] - fn enable_peripheral(&self) { - PeripheralClockControl::enable(crate::system::Peripheral::Spi3) - } - - #[inline(always)] - fn spi_num(&self) -> u8 { - 3 - } -} - #[cfg(any(esp32s2, esp32s3))] impl Instance for crate::peripherals::SPI2 { #[inline(always)] diff --git a/examples/src/bin/spi_slave_dma.rs b/examples/src/bin/spi_slave_dma.rs index 2878b66ebe6..3521ea5d44e 100644 --- a/examples/src/bin/spi_slave_dma.rs +++ b/examples/src/bin/spi_slave_dma.rs @@ -1,19 +1,6 @@ //! SPI slave loopback test using DMA //! -//! ESP32 / ESP32-S2 -//! Following pins are used for the slave: -//! SCLK GPIO2 -//! MISO GPIO4 -//! MOSI GPIO15 -//! CS GPIO17 -//! -//! Following pins are used for the (bitbang) master: -//! SCLK GPIO0 -//! MISO GPIO5 -//! MOSI GPIO16 -//! CS GPIO18 -//! -//! Other chips: +//! Following pins are used for the (bitbang) slave: //! //! SCLK GPIO0 //! MISO GPIO1 @@ -37,7 +24,7 @@ //! pins except SCLK. SCLK is between MOSI and VDD3P3_RTC on the barebones chip, //! so no immediate neighbor is available. -//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 esp32 esp32s2 +//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] @@ -49,7 +36,7 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, - gpio::{Input, Io, Level, Output, Pull}, + gpio::{Gpio4, Gpio5, Gpio8, Gpio9, Input, Io, Level, Output, Pull}, peripherals::Peripherals, prelude::*, spi::{ @@ -60,20 +47,6 @@ use esp_hal::{ }; use esp_println::println; -cfg_if::cfg_if! { - if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { - const MASTER_CS: u8 = 18; - const MASTER_MOSI: u8 = 16; - const MASTER_SCLK: u8 = 0; - const MASTER_MISO: u8 = 5; - } else { - const MASTER_CS: u8 = 9; - const MASTER_MOSI: u8 = 8; - const MASTER_SCLK: u8 = 4; - const MASTER_MISO: u8 = 5; - } -} - #[entry] fn main() -> ! { let peripherals = Peripherals::take(); @@ -95,7 +68,7 @@ fn main() -> ! { let dma = Dma::new(peripherals.DMA); cfg_if::cfg_if! { - if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + if #[cfg(feature = "esp32s2")] { let dma_channel = dma.spi2channel; } else { let dma_channel = dma.channel0; @@ -223,10 +196,10 @@ fn main() -> ! { fn bitbang_master( master_send: &[u8], master_receive: &mut [u8], - master_cs: &mut GpioPin, MASTER_CS>, - master_mosi: &mut GpioPin, MASTER_MOSI>, - master_sclk: &mut GpioPin, MASTER_SCLK>, - master_miso: &GpioPin, MASTER_MISO>, + master_cs: &mut Output, + master_mosi: &mut Output, + master_sclk: &mut Output, + master_miso: &Input, ) { // Bit-bang out the contents of master_send and read into master_receive // as quickly as manageable. MSB first. Mode 0, so sampled on the rising From f7eb47b5acb900930013294bd4b93700a80936aa Mon Sep 17 00:00:00 2001 From: Juraj Sadel Date: Fri, 17 May 2024 16:27:18 +0200 Subject: [PATCH 5/6] changelog --- esp-hal/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index dd44a293a4f..bc4ff2dc62a 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `time::current_time` API (#1503) - ESP32-S3: Add LCD_CAM Camera driver (#1483) - `embassy-usb` support (#1517) -- SPI Slave support for ESP32-S2 (#1514) +- SPI Slave support for ESP32-S2 (#1562) ### Fixed From 3c9d7e59e248acb80cefffaec03012ec218e1a58 Mon Sep 17 00:00:00 2001 From: Juraj Sadel Date: Tue, 21 May 2024 16:23:07 +0200 Subject: [PATCH 6/6] review comments --- esp-hal/src/spi/slave.rs | 48 +++++++++------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 9cc3d83e995..3dec7ae36bd 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -408,29 +408,22 @@ where reset_dma_before_load_dma_dscr(reg_block); - rx.prepare_transfer_without_start( - false, - self.dma_peripheral(), - read_buffer_ptr, - read_buffer_len, - )?; - #[cfg(esp32s2)] - rx.start_transfer()?; - tx.prepare_transfer_without_start( self.dma_peripheral(), false, write_buffer_ptr, write_buffer_len, )?; - #[cfg(esp32s2)] - tx.start_transfer()?; + + rx.prepare_transfer_without_start( + false, + self.dma_peripheral(), + read_buffer_ptr, + read_buffer_len, + )?; reset_dma_before_usr_cmd(reg_block); - // Note: On the esp32, all full-duplex transfers are single, and all half-duplex - // transfers use the cmd/addr/dummy/data sequence (but are still - // single). reg_block .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); @@ -439,11 +432,8 @@ where self.setup_for_flush(); reg_block.cmd().modify(|_, w| w.usr().set_bit()); - #[cfg(not(esp32s2))] - { - rx.start_transfer()?; - tx.start_transfer()?; - } + rx.start_transfer()?; + tx.start_transfer()?; Ok(()) } @@ -463,14 +453,9 @@ where reset_dma_before_load_dma_dscr(reg_block); tx.prepare_transfer_without_start(self.dma_peripheral(), false, ptr, len)?; - #[cfg(esp32s2)] - tx.start_transfer()?; reset_dma_before_usr_cmd(reg_block); - // Note: On the esp32, all full-duplex transfers are single, and all half-duplex - // transfers use the cmd/addr/dummy/data sequence (but are still - // single). reg_block .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); @@ -479,10 +464,7 @@ where self.setup_for_flush(); reg_block.cmd().modify(|_, w| w.usr().set_bit()); - #[cfg(not(esp32s2))] - { - tx.start_transfer()?; - } + tx.start_transfer()?; Ok(()) } @@ -501,14 +483,9 @@ where reset_dma_before_load_dma_dscr(reg_block); rx.prepare_transfer_without_start(false, self.dma_peripheral(), ptr, len)?; - #[cfg(esp32s2)] - rx.start_transfer()?; reset_dma_before_usr_cmd(reg_block); - // Note: On the esp32, all full-duplex transfers are single, and all half-duplex - // transfers use the cmd/addr/dummy/data sequence (but are still - // single). reg_block .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); @@ -517,10 +494,7 @@ where self.setup_for_flush(); reg_block.cmd().modify(|_, w| w.usr().set_bit()); - #[cfg(not(esp32s2))] - { - rx.start_transfer()?; - } + rx.start_transfer()?; Ok(()) }