diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 7ba7be5cfec..bc4ff2dc62a 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-S2 (#1562) ### Fixed diff --git a/esp-hal/src/spi/mod.rs b/esp-hal/src/spi/mod.rs index 1236b6e7510..23f3fc7ca9d 100644 --- a/esp-hal/src/spi/mod.rs +++ b/esp-hal/src/spi/mod.rs @@ -7,7 +7,7 @@ use crate::dma::DmaError; pub mod master; -#[cfg(all(any(spi0, spi1, spi2, spi3), not(pdma)))] +#[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 648184ab9f8..3dec7ae36bd 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,12 +407,14 @@ 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(), @@ -436,19 +422,20 @@ where read_buffer_len, )?; - self.clear_dma_interrupts(); reset_dma_before_usr_cmd(reg_block); - // 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()); + self.clear_dma_interrupts(); + self.setup_for_flush(); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); + + rx.start_transfer()?; tx.start_transfer()?; - Ok(rx.start_transfer()?) + + Ok(()) } fn start_write_bytes_dma( @@ -464,20 +451,22 @@ where self.enable_dma(); reset_dma_before_load_dma_dscr(reg_block); + tx.prepare_transfer_without_start(self.dma_peripheral(), false, ptr, len)?; - self.clear_dma_interrupts(); reset_dma_before_usr_cmd(reg_block); - // 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()); - Ok(tx.start_transfer()?) + self.clear_dma_interrupts(); + self.setup_for_flush(); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); + + tx.start_transfer()?; + + Ok(()) } unsafe fn start_read_bytes_dma( @@ -493,22 +482,21 @@ 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)?; - self.clear_dma_interrupts(); reset_dma_before_usr_cmd(reg_block); - // 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()); - Ok(rx.start_transfer()?) + self.clear_dma_interrupts(); + self.setup_for_flush(); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); + + rx.start_transfer()?; + + Ok(()) } fn dma_peripheral(&self) -> DmaPeripheral { @@ -533,7 +521,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` } @@ -555,10 +543,10 @@ 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| { + reg_block.dma_int_clr().write(|w| { w.inlink_dscr_empty() .clear_bit_by_one() .outlink_dscr_error() @@ -581,7 +569,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() @@ -593,15 +581,18 @@ fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { }); } -#[cfg(any(esp32, esp32s2))] -fn reset_dma_before_usr_cmd(_reg_block: &RegisterBlock) {} +#[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| { + reg_block.dma_conf().modify(|_, w| { w.out_rst() .set_bit() .in_rst() @@ -611,6 +602,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 +659,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,18 +672,20 @@ 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)))] + #[cfg(not(esp32s2))] reg_block.clk_gate().modify(|_, w| { w.clk_en() .clear_bit() @@ -676,7 +695,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() @@ -691,14 +710,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(); @@ -708,7 +722,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()); } @@ -743,53 +757,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().set_bit()); - reg_block.user.modify(|_, w| w.ck_i_edge().clear_bit()); - } - SpiMode::Mode1 => { - reg_block.pin.modify(|_, w| w.ck_idle_edge().set_bit()); - reg_block.user.modify(|_, w| w.ck_i_edge().set_bit()); - } - SpiMode::Mode2 => { - reg_block.pin.modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user.modify(|_, w| w.ck_i_edge().set_bit()); - } - SpiMode::Mode3 => { - reg_block.pin.modify(|_, w| w.ck_idle_edge().clear_bit()); - reg_block.user.modify(|_, w| w.ck_i_edge().clear_bit()); - } - } - 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(); - - 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())); - } - 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() + 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() } @@ -806,11 +781,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()); @@ -855,82 +830,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 13c57c4e4d9..3521ea5d44e 100644 --- a/examples/src/bin/spi_slave_dma.rs +++ b/examples/src/bin/spi_slave_dma.rs @@ -1,6 +1,7 @@ //! SPI slave loopback test using DMA //! -//! Following pins are used for the slave: +//! Following pins are used for the (bitbang) slave: +//! //! SCLK GPIO0 //! MISO GPIO1 //! MOSI GPIO2 @@ -23,10 +24,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 esp32s2 esp32s3 #![no_std] #![no_main] +#![feature(asm_experimental_arch)] use esp_backtrace as _; use esp_hal::{ @@ -34,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::{ @@ -65,7 +67,13 @@ fn main() -> ! { master_mosi.set_low(); let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + cfg_if::cfg_if! { + if #[cfg(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 +116,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 +144,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 +165,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 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 + // 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(); +}