From af3f892381ebd00816a5a3a3b2cc79b7c1401ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 7 Oct 2024 17:00:15 +0200 Subject: [PATCH] ESP32 spi slave mode (#2278) * Enable test on ESP32 * Enable module on ESP32 * Add ESP32 signal map * Change pins * Remove unknown signals * Return to low level in test * Fix bitlength * Merge enable and reset * Impl debug traits on DMA flags * Disallow mods 0 and 2 * Docs tweaks * Changelog * Undo wait change --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/dma/mod.rs | 9 +- esp-hal/src/soc/esp32/gpio.rs | 86 ------ esp-hal/src/spi/mod.rs | 1 - esp-hal/src/spi/slave.rs | 477 +++++++++++++++++++--------------- hil-test/tests/spi_slave.rs | 21 +- 6 files changed, 290 insertions(+), 305 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 0d7d5bf688b..f129e75067b 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Introduce `DmaRxStreamBuf` (#2242) - Implement `embedded_hal_async::delay::DelayNs` for `TIMGx` timers (#2084) - Added `Efuse::read_bit` (#2259) +- Limited SPI slave support for ESP32 (Modes 1 and 3 only) (#2278) - Added `Rtc::disable_rom_message_printing` (S3 and H2 only) (#2280) ### Changed diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index bcdcfe6d221..5e66769aee6 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -364,7 +364,8 @@ mod gdma; mod pdma; /// Kinds of interrupt to listen to. -#[derive(EnumSetType)] +#[derive(Debug, EnumSetType)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DmaInterrupt { /// RX is done RxDone, @@ -373,7 +374,8 @@ pub enum DmaInterrupt { } /// Types of interrupts emitted by the TX channel. -#[derive(EnumSetType)] +#[derive(Debug, EnumSetType)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DmaTxInterrupt { /// Triggered when all data corresponding to a linked list (including /// multiple descriptors) have been sent via transmit channel. @@ -394,7 +396,8 @@ pub enum DmaTxInterrupt { } /// Types of interrupts emitted by the RX channel. -#[derive(EnumSetType)] +#[derive(Debug, EnumSetType)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DmaRxInterrupt { /// Triggered when the size of the buffer pointed by receive descriptors /// is smaller than the length of data to be received via receive channel. diff --git a/esp-hal/src/soc/esp32/gpio.rs b/esp-hal/src/soc/esp32/gpio.rs index 44395379cef..c5a2f2d2ed2 100644 --- a/esp-hal/src/soc/esp32/gpio.rs +++ b/esp-hal/src/soc/esp32/gpio.rs @@ -141,8 +141,6 @@ pub enum InputSignal { U0DSR = 16, U1RXD = 17, U1CTS = 18, - I2CM_SDA = 20, - EXT_I2C_SDA = 22, I2S0O_BCK = 23, I2S1O_BCK = 24, I2S0O_WS = 25, @@ -157,8 +155,6 @@ pub enum InputSignal { PWM0_F0 = 34, PWM0_F1 = 35, PWM0_F2 = 36, - GPIO_BT_ACTIVE = 37, - GPIO_BT_PRIORITY = 38, PCNT0_SIG_CH0 = 39, PCNT0_SIG_CH1 = 40, PCNT0_CTRL_CH0 = 41, @@ -209,7 +205,6 @@ pub enum InputSignal { RMT_SIG_5 = 88, RMT_SIG_6 = 89, RMT_SIG_7 = 90, - EXT_ADC_START = 93, TWAI_RX = 94, I2CEXT1_SCL = 95, I2CEXT1_SDA = 96, @@ -231,29 +226,6 @@ pub enum InputSignal { PWM1_CAP0 = 112, PWM1_CAP1 = 113, PWM1_CAP2 = 114, - PWM2_FLTA = 115, - PWM2_FLTB = 116, - PWM2_CAP1 = 117, - PWM2_CAP2 = 118, - PWM2_CAP3 = 119, - PWM3_FLTA = 120, - PWM3_FLTB = 121, - PWM3_CAP1 = 122, - PWM3_CAP2 = 123, - PWM3_CAP3 = 124, - CAN_CLKOUT = 125, - SPID4 = 128, - SPID5 = 129, - SPID6 = 130, - SPID7 = 131, - HSPID4 = 132, - HSPID5 = 133, - HSPID6 = 134, - HSPID7 = 135, - VSPID4 = 136, - VSPID5 = 137, - VSPID6 = 138, - VSPID7 = 139, I2S0I_DATA_0 = 140, I2S0I_DATA_1 = 141, I2S0I_DATA_2 = 142, @@ -303,11 +275,6 @@ pub enum InputSignal { PCMFSYNC = 204, PCMCLK = 205, PCMDIN = 206, - SIG_IN_FUNC224 = 224, - SIG_IN_FUNC225 = 225, - SIG_IN_FUNC226 = 226, - SIG_IN_FUNC227 = 227, - SIG_IN_FUNC228 = 228, SD_DATA0 = 512, SD_DATA1, @@ -365,10 +332,6 @@ pub enum OutputSignal { U0DTR = 16, U1TXD = 17, U1RTS = 18, - I2CM_SCL = 19, - I2CM_SDA = 20, - EXT2C_SCL = 21, - EXT2C_SDA = 22, I2S0O_BCK = 23, I2S1O_BCK = 24, I2S0O_WS = 25, @@ -384,27 +347,6 @@ pub enum OutputSignal { PWM0_1B = 35, PWM0_2A = 36, PWM0_2B = 37, - GPIO_WLAN_ACTIVE = 40, - BB_DIAG0 = 41, - BB_DIAG1 = 42, - BB_DIAG2 = 43, - BB_DIAG3 = 44, - BB_DIAG4 = 45, - BB_DIAG5 = 46, - BB_DIAG6 = 47, - BB_DIAG7 = 48, - BB_DIAG8 = 49, - BB_DIAG9 = 50, - BB_DIAG10 = 51, - BB_DIAG11 = 52, - BB_DIAG12 = 53, - BB_DIAG13 = 54, - BB_DIAG14 = 55, - BB_DIAG15 = 56, - BB_DIAG16 = 57, - BB_DIAG17 = 58, - BB_DIAG18 = 59, - BB_DIAG19 = 60, HSPICS1 = 61, HSPICS2 = 62, VSPICLK = 63, @@ -458,29 +400,9 @@ pub enum OutputSignal { PWM1_1B = 111, PWM1_2A = 112, PWM1_2B = 113, - PWM2_1H = 114, - PWM2_1L = 115, - PWM2_2H = 116, - PWM2_2L = 117, - PWM2_3H = 118, - PWM2_3L = 119, - PWM2_4H = 120, - PWM2_4L = 121, TWAI_TX = 123, TWAI_BUS_OFF_ON = 124, TWAI_CLKOUT = 125, - SPID4 = 128, - SPID5 = 129, - SPID6 = 130, - SPID7 = 131, - HSPID4 = 132, - HSPID5 = 133, - HSPID6 = 134, - HSPID7 = 135, - VSPID4 = 136, - VSPID5 = 137, - VSPID6 = 138, - VSPID7 = 139, I2S0O_DATA_0 = 140, I2S0O_DATA_1 = 141, I2S0O_DATA_2 = 142, @@ -531,14 +453,6 @@ pub enum OutputSignal { I2S1O_DATA_21 = 187, I2S1O_DATA_22 = 188, I2S1O_DATA_23 = 189, - PWM3_1H = 190, - PWM3_1L = 191, - PWM3_2H = 192, - PWM3_2L = 193, - PWM3_3H = 194, - PWM3_3L = 195, - PWM3_4H = 196, - PWM3_4L = 197, U2TXD = 198, U2RTS = 199, EMAC_MDC = 200, diff --git a/esp-hal/src/spi/mod.rs b/esp-hal/src/spi/mod.rs index 363bb7c09c3..32786fb24c0 100644 --- a/esp-hal/src/spi/mod.rs +++ b/esp-hal/src/spi/mod.rs @@ -12,7 +12,6 @@ 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 d1ab8954665..98d7bf318de 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -22,8 +22,8 @@ //! # use esp_hal::dma::Dma; //! # use esp_hal::gpio::Io; //! let dma = Dma::new(peripherals.DMA); -#![cfg_attr(esp32s2, doc = "let dma_channel = dma.spi2channel;")] -#![cfg_attr(not(esp32s2), doc = "let dma_channel = dma.channel0;")] +#![cfg_attr(pdma, doc = "let dma_channel = dma.spi2channel;")] +#![cfg_attr(gdma, doc = "let dma_channel = dma.channel0;")] //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! let sclk = io.pins.gpio0; //! let miso = io.pins.gpio1; @@ -58,16 +58,17 @@ //! ## Implementation State //! //! There are several options for working with the SPI peripheral in slave mode, -//! but the code currently only supports single transfers (not segmented -//! transfers), full duplex, single bit (not dual or quad SPI), and DMA mode -//! (not CPU mode). It also does not support blocking operations, as the actual +//! but the code currently only supports: +//! - Single transfers (not segmented transfers) +//! - Full duplex, single bit (not dual or quad SPI) +//! - DMA mode (not CPU mode). +#![cfg_attr(esp32, doc = "- ESP32 only supports SPI mode 1 and 3.\n\n")] +//! It also does not support blocking operations, as the actual //! transfer is controlled by the SPI master; if these are necessary, -//! then the DmaTransfer trait instance can be wait()ed on or polled for -//! is_done(). -//! - ESP32 does not support SPI Slave. See [tracking issue]. +//! then the `DmaTransfer` object can be `wait()`ed on or polled for +//! `is_done()`. //! -//! [tracking issue]: https://github.com/esp-rs/esp-hal/issues/469 - +//! See [tracking issue](https://github.com/esp-rs/esp-hal/issues/469) for more information. use core::marker::PhantomData; use super::{Error, FullDuplexMode, SpiMode}; @@ -82,7 +83,9 @@ use crate::{ const MAX_DMA_SIZE: usize = 32768 - 32; -/// SPI peripheral driver +/// SPI peripheral driver. +/// +/// See the [module-level documentation][self] for more details. pub struct Spi<'d, T, M> { spi: PeripheralRef<'d, T>, #[allow(dead_code)] @@ -138,7 +141,7 @@ where _mode: PhantomData, }; spi.spi.init(); - spi.spi.set_data_mode(mode); + spi.spi.set_data_mode(mode, false); spi } @@ -174,8 +177,9 @@ pub mod dma { impl<'d> Spi<'d, crate::peripherals::SPI2, FullDuplexMode> { /// Configures the SPI3 peripheral with the provided DMA channel and /// descriptors. + #[cfg_attr(esp32, doc = "\n\n**Note**: ESP32 only supports Mode 1 and 3.")] pub fn with_dma( - self, + mut self, channel: Channel<'d, C, DmaMode>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], @@ -185,6 +189,7 @@ pub mod dma { C::P: SpiPeripheral + Spi2Peripheral, DmaMode: Mode, { + self.spi.set_data_mode(self.data_mode, true); SpiDma { spi: self.spi, channel, @@ -198,8 +203,9 @@ pub mod dma { impl<'d> Spi<'d, crate::peripherals::SPI3, FullDuplexMode> { /// Configures the SPI3 peripheral with the provided DMA channel and /// descriptors. + #[cfg_attr(esp32, doc = "\n\n**Note**: ESP32 only supports Mode 1 and 3.")] pub fn with_dma( - self, + mut self, channel: Channel<'d, C, DmaMode>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], @@ -209,6 +215,7 @@ pub mod dma { C::P: SpiPeripheral + Spi3Peripheral, DmaMode: Mode, { + self.spi.set_data_mode(self.data_mode, true); SpiDma { spi: self.spi, channel, @@ -327,7 +334,16 @@ pub mod dma { unsafe { self.spi - .start_write_bytes_dma(&mut self.tx_chain, ptr, len, &mut self.channel.tx) + .start_transfer_dma( + &mut self.rx_chain, + &mut self.tx_chain, + core::ptr::null_mut(), + 0, + ptr, + len, + &mut self.channel.rx, + &mut self.channel.tx, + ) .map(move |_| DmaTransferTx::new(self)) } } @@ -353,7 +369,16 @@ pub mod dma { unsafe { self.spi - .start_read_bytes_dma(&mut self.rx_chain, ptr, len, &mut self.channel.rx) + .start_transfer_dma( + &mut self.rx_chain, + &mut self.tx_chain, + ptr, + len, + core::ptr::null(), + 0, + &mut self.channel.rx, + &mut self.channel.tx, + ) .map(move |_| DmaTransferRx::new(self)) } } @@ -421,58 +446,26 @@ pub trait InstanceDma: Instance { { let reg_block = self.register_block(); - rx.is_done(); - tx.is_done(); - self.enable_dma(); - reset_dma_before_load_dma_dscr(reg_block); - - rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; - rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain)?; - - tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; - tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?; - - reset_dma_before_usr_cmd(reg_block); + reset_spi(reg_block); - 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(()) - } - - unsafe fn start_write_bytes_dma( - &mut self, - tx_chain: &mut DescriptorChain, - ptr: *const u8, - len: usize, - tx: &mut TX, - ) -> Result<(), Error> - where - TX: Tx, - { - let reg_block = self.register_block(); - - tx.is_done(); - - self.enable_dma(); + if read_buffer_len > 0 { + rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; + rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain)?; + } - reset_dma_before_load_dma_dscr(reg_block); + if write_buffer_len > 0 { + tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; + tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?; + } - tx_chain.fill_for_tx(false, ptr, len)?; - tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?; + #[cfg(esp32)] + self.prepare_length_and_lines(read_buffer_len, write_buffer_len); reset_dma_before_usr_cmd(reg_block); + #[cfg(not(esp32))] reg_block .dma_conf() .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); @@ -481,64 +474,52 @@ pub trait InstanceDma: Instance { self.setup_for_flush(); reg_block.cmd().modify(|_, w| w.usr().set_bit()); - tx.start_transfer()?; - - Ok(()) - } - - unsafe fn start_read_bytes_dma( - &mut self, - rx_chain: &mut DescriptorChain, - ptr: *mut u8, - len: usize, - rx: &mut RX, - ) -> Result<(), Error> - where - RX: Rx, - { - let reg_block = self.register_block(); - - rx.is_done(); - - self.enable_dma(); - - reset_dma_before_load_dma_dscr(reg_block); - rx_chain.fill_for_rx(false, ptr, len)?; - rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain)?; - - reset_dma_before_usr_cmd(reg_block); - - 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()); + if read_buffer_len > 0 { + rx.start_transfer()?; + } - rx.start_transfer()?; + if write_buffer_len > 0 { + tx.start_transfer()?; + } Ok(()) } - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] fn enable_dma(&self) { let reg_block = self.register_block(); - reg_block.dma_conf().modify(|_, w| { - w.dma_tx_ena().set_bit(); - w.dma_rx_ena().set_bit(); - w.rx_eof_en().clear_bit() - }); - } + #[cfg(gdma)] + { + reg_block.dma_conf().modify(|_, w| { + w.dma_tx_ena().set_bit(); + w.dma_rx_ena().set_bit(); + w.rx_eof_en().clear_bit() + }); + } - #[cfg(esp32s2)] - fn enable_dma(&self) { - // for non GDMA this is done in `assign_tx_device` / `assign_rx_device` + #[cfg(pdma)] + { + fn set_rst_bit(reg_block: &RegisterBlock, bit: bool) { + reg_block.dma_conf().modify(|_, w| { + w.in_rst().bit(bit); + w.out_rst().bit(bit); + w.ahbm_fifo_rst().bit(bit); + w.ahbm_rst().bit(bit) + }); + + #[cfg(esp32s2)] + reg_block + .dma_conf() + .modify(|_, w| w.dma_infifo_full_clr().bit(bit)); + } + set_rst_bit(reg_block, true); + set_rst_bit(reg_block, false); + } } - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] fn clear_dma_interrupts(&self) { let reg_block = self.register_block(); + + #[cfg(gdma)] reg_block.dma_int_clr().write(|w| { w.dma_infifo_full_err().clear_bit_by_one(); w.dma_outfifo_empty_err().clear_bit_by_one(); @@ -546,11 +527,8 @@ pub trait InstanceDma: Instance { w.mst_rx_afifo_wfull_err().clear_bit_by_one(); w.mst_tx_afifo_rempty_err().clear_bit_by_one() }); - } - #[cfg(esp32s2)] - fn clear_dma_interrupts(&self) { - let reg_block = self.register_block(); + #[cfg(pdma)] reg_block.dma_int_clr().write(|w| { w.inlink_dscr_empty().clear_bit_by_one(); w.outlink_dscr_error().clear_bit_by_one(); @@ -565,49 +543,30 @@ pub trait InstanceDma: Instance { } } -#[cfg(not(esp32s2))] +fn reset_spi(reg_block: &RegisterBlock) { + #[cfg(esp32)] + { + reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); + reg_block.slave().modify(|_, w| w.sync_reset().clear_bit()); + } + + #[cfg(not(esp32))] + { + reg_block.slave().modify(|_, w| w.soft_reset().set_bit()); + reg_block.slave().modify(|_, w| w.soft_reset().clear_bit()); + } +} + fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { + #[cfg(gdma)] reg_block.dma_conf().modify(|_, w| { w.rx_afifo_rst().set_bit(); w.buf_afifo_rst().set_bit(); w.dma_afifo_rst().set_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(esp32s2))] -fn reset_dma_before_load_dma_dscr(_reg_block: &RegisterBlock) {} - -#[cfg(esp32s2)] -fn reset_dma_before_load_dma_dscr(reg_block: &RegisterBlock) { - reg_block.dma_conf().modify(|_, w| { - w.out_rst().set_bit(); - w.in_rst().set_bit(); - w.ahbm_fifo_rst().set_bit(); - w.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(); - w.in_rst().clear_bit(); - w.ahbm_fifo_rst().clear_bit(); - w.ahbm_rst().clear_bit() - }); - - #[cfg(esp32s2)] - reg_block - .dma_conf() - .modify(|_, w| w.dma_infifo_full_clr().clear_bit()); + #[cfg(pdma)] + let _ = reg_block; } impl InstanceDma for crate::peripherals::SPI2 { @@ -626,6 +585,8 @@ impl InstanceDma for crate::peripherals::SPI3 { pub trait Instance: private::Sealed { fn register_block(&self) -> &RegisterBlock; + fn peripheral(&self) -> crate::system::Peripheral; + fn sclk_signal(&self) -> InputSignal; fn mosi_signal(&self) -> InputSignal; @@ -634,8 +595,6 @@ pub trait Instance: private::Sealed { fn cs_signal(&self) -> InputSignal; - fn peripheral(&self) -> crate::system::Peripheral; - #[inline(always)] fn reset_peripheral(&self) { PeripheralClockControl::reset(self.peripheral()); @@ -648,6 +607,24 @@ pub trait Instance: private::Sealed { fn spi_num(&self) -> u8; + #[cfg(esp32)] + fn prepare_length_and_lines(&self, rx_len: usize, tx_len: usize) { + let reg_block = self.register_block(); + + reg_block + .slv_rdbuf_dlen() + .write(|w| unsafe { w.bits((rx_len as u32 * 8).saturating_sub(1)) }); + reg_block + .slv_wrbuf_dlen() + .write(|w| unsafe { w.bits((tx_len as u32 * 8).saturating_sub(1)) }); + + // SPI Slave mode on ESP32 requires MOSI/MISO enable + reg_block.user().modify(|_, w| { + w.usr_mosi().bit(rx_len > 0); + w.usr_miso().bit(tx_len > 0) + }); + } + /// Initialize for full-duplex 1 bit mode fn init(&mut self) { let reg_block = self.register_block(); @@ -656,59 +633,94 @@ pub trait Instance: private::Sealed { 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.slave().write(|w| { + #[cfg(esp32)] + w.slv_wr_rd_buf_en().set_bit(); + + w.mode().set_bit() + }); + reset_spi(reg_block); reg_block.user().modify(|_, w| { - w.usr_miso_highpart().clear_bit(); w.doutdin().set_bit(); - w.usr_miso().clear_bit(); - w.usr_mosi().clear_bit(); - w.usr_dummy_idle().clear_bit(); - w.usr_addr().clear_bit(); - w.usr_command().clear_bit(); w.sio().clear_bit() }); - #[cfg(not(esp32s2))] - reg_block.clk_gate().modify(|_, w| { - w.clk_en().clear_bit(); - w.mst_clk_active().clear_bit(); - w.mst_clk_sel().clear_bit() - }); - - reg_block.ctrl().modify(|_, w| { - w.q_pol().clear_bit(); - w.d_pol().clear_bit(); - #[cfg(not(esp32s2))] - w.hold_pol().clear_bit(); - #[cfg(esp32s2)] - w.wp().clear_bit(); - w - }); - + #[cfg(not(esp32))] reg_block.misc().write(|w| unsafe { w.bits(0) }); } - fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self { + fn set_data_mode(&mut self, data_mode: SpiMode, dma: bool) -> &mut Self { let reg_block = self.register_block(); + #[cfg(esp32)] + { + reg_block.pin().modify(|_, w| { + w.ck_idle_edge() + .bit(matches!(data_mode, SpiMode::Mode0 | SpiMode::Mode1)) + }); + reg_block.user().modify(|_, w| { + w.ck_i_edge() + .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)) + }); + reg_block.ctrl2().modify(|_, w| unsafe { + match data_mode { + SpiMode::Mode0 => { + 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 => { + 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 => { + 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 => { + w.miso_delay_mode().bits(1); + w.miso_delay_num().bits(0); + w.mosi_delay_mode().bits(0); + w.mosi_delay_num().bits(0) + } + } + }); + + if dma { + assert!( + matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3), + "Mode {:?} is not supported with DMA", + data_mode + ); + } + } - reg_block.user().modify(|_, w| { - w.tsck_i_edge() - .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)); - w.rsck_i_edge() - .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)) - }); - cfg_if::cfg_if! { - if #[cfg(esp32s2)] { - let ctrl1_reg = reg_block.ctrl1(); - } else { - let ctrl1_reg = reg_block.slave(); + #[cfg(not(esp32))] + { + _ = dma; + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + let ctrl1_reg = reg_block.ctrl1(); + } else { + let ctrl1_reg = reg_block.slave(); + } } + reg_block.user().modify(|_, w| { + w.tsck_i_edge() + .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)); + w.rsck_i_edge() + .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)) + }); + ctrl1_reg.modify(|_, w| { + w.clk_mode_13() + .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3)) + }); } - ctrl1_reg.modify(|_, w| { - w.clk_mode_13() - .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3)) - }); self } @@ -716,11 +728,11 @@ pub trait Instance: private::Sealed { fn is_bus_busy(&self) -> bool { let reg_block = self.register_block(); - #[cfg(esp32s2)] + #[cfg(pdma)] { reg_block.slave().read().trans_done().bit_is_clear() } - #[cfg(not(esp32s2))] + #[cfg(gdma)] { reg_block.dma_int_raw().read().trans_done().bit_is_clear() } @@ -737,17 +749,18 @@ 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(esp32s2)] + #[cfg(pdma)] self.register_block() .slave() .modify(|_, w| w.trans_done().clear_bit()); - #[cfg(not(esp32s2))] + #[cfg(gdma)] self.register_block() .dma_int_clr() .write(|w| w.trans_done().clear_bit_by_one()); } } +#[cfg(spi2)] impl Instance for crate::peripherals::SPI2 { #[inline(always)] fn register_block(&self) -> &RegisterBlock { @@ -755,33 +768,57 @@ impl Instance for crate::peripherals::SPI2 { } #[inline(always)] - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::Spi2 + fn spi_num(&self) -> u8 { + 2 } #[inline(always)] - fn spi_num(&self) -> u8 { - 2 + fn peripheral(&self) -> crate::system::Peripheral { + crate::system::Peripheral::Spi2 } #[inline(always)] fn sclk_signal(&self) -> InputSignal { - InputSignal::FSPICLK + cfg_if::cfg_if! { + if #[cfg(esp32)] { + InputSignal::HSPICLK + } else { + InputSignal::FSPICLK + } + } } #[inline(always)] fn mosi_signal(&self) -> InputSignal { - InputSignal::FSPID + cfg_if::cfg_if! { + if #[cfg(esp32)] { + InputSignal::HSPID + } else { + InputSignal::FSPID + } + } } #[inline(always)] fn miso_signal(&self) -> OutputSignal { - OutputSignal::FSPIQ + cfg_if::cfg_if! { + if #[cfg(esp32)] { + OutputSignal::HSPIQ + } else { + OutputSignal::FSPIQ + } + } } #[inline(always)] fn cs_signal(&self) -> InputSignal { - InputSignal::FSPICS0 + cfg_if::cfg_if! { + if #[cfg(esp32)] { + InputSignal::HSPICS0 + } else { + InputSignal::FSPICS0 + } + } } } @@ -793,32 +830,56 @@ impl Instance for crate::peripherals::SPI3 { } #[inline(always)] - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::Spi3 + fn spi_num(&self) -> u8 { + 3 } #[inline(always)] - fn spi_num(&self) -> u8 { - 3 + fn peripheral(&self) -> crate::system::Peripheral { + crate::system::Peripheral::Spi3 } #[inline(always)] fn sclk_signal(&self) -> InputSignal { - InputSignal::SPI3_CLK + cfg_if::cfg_if! { + if #[cfg(esp32)] { + InputSignal::VSPICLK + } else { + InputSignal::SPI3_CLK + } + } } #[inline(always)] fn mosi_signal(&self) -> InputSignal { - InputSignal::SPI3_D + cfg_if::cfg_if! { + if #[cfg(esp32)] { + InputSignal::VSPID + } else { + InputSignal::SPI3_D + } + } } #[inline(always)] fn miso_signal(&self) -> OutputSignal { - OutputSignal::SPI3_Q + cfg_if::cfg_if! { + if #[cfg(esp32)] { + OutputSignal::VSPIQ + } else { + OutputSignal::SPI3_Q + } + } } #[inline(always)] fn cs_signal(&self) -> InputSignal { - InputSignal::SPI3_CS0 + cfg_if::cfg_if! { + if #[cfg(esp32)] { + InputSignal::VSPICS0 + } else { + InputSignal::SPI3_CS0 + } + } } } diff --git a/hil-test/tests/spi_slave.rs b/hil-test/tests/spi_slave.rs index a8b73a4cd9e..7009edfc80f 100644 --- a/hil-test/tests/spi_slave.rs +++ b/hil-test/tests/spi_slave.rs @@ -1,6 +1,9 @@ //! SPI slave mode test suite. +//! +//! ESP32 does not support Modes 0 and 2 (properly, at least), so here we're +//! testing Mode 1. -//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] @@ -51,20 +54,22 @@ impl BitbangSpi { } fn assert_cs(&mut self) { + self.sclk.set_level(Level::Low); self.cs.set_level(Level::Low); } fn deassert_cs(&mut self) { + self.sclk.set_level(Level::Low); self.cs.set_level(Level::High); } - // Mode 0, so sampled on the rising edge and set on the falling edge. + // Mode 1, so sampled on the rising edge and set on the falling edge. fn shift_bit(&mut self, bit: bool) -> bool { self.mosi.set_level(Level::from(bit)); - self.sclk.set_level(Level::Low); + self.sclk.set_level(Level::High); let miso = self.miso.get_level().into(); - self.sclk.set_level(Level::High); + self.sclk.set_level(Level::Low); miso } @@ -98,6 +103,7 @@ mod tests { let peripherals = esp_hal::init(esp_hal::Config::default()); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let (mosi_pin, miso_pin) = hil_test::i2c_pins!(io); let (sclk_pin, sclk_gpio) = hil_test::common_test_pins!(io); let cs_pin = hil_test::unconnected_pin!(io); @@ -115,6 +121,7 @@ mod tests { let cs = cs_pin.peripheral_input(); let mosi = mosi_pin.peripheral_input(); let mut miso = miso_pin.peripheral_input(); + let sclk_signal = sclk_pin.peripheral_input(); let mosi_gpio = Output::new(mosi_pin, Level::Low); let cs_gpio = Output::new(cs_pin, Level::High); @@ -122,11 +129,11 @@ mod tests { let spi = Spi::new( peripherals.SPI2, - sclk_pin, + sclk_signal, mosi, miso_pin, cs, - SpiMode::Mode0, + SpiMode::Mode1, ); miso.enable_input(true, unsafe { esp_hal::Internal::conjure() }); @@ -145,8 +152,8 @@ mod tests { let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(DMA_SIZE); let mut spi = ctx.spi.with_dma( ctx.dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, rx_descriptors, + tx_descriptors, ); let slave_send = tx_buffer; let slave_receive = rx_buffer;