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
This commit is contained in:
Dániel Buga 2024-10-07 17:00:15 +02:00 committed by GitHub
parent ba96bac776
commit af3f892381
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 295 additions and 310 deletions

View File

@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Introduce `DmaRxStreamBuf` (#2242) - Introduce `DmaRxStreamBuf` (#2242)
- Implement `embedded_hal_async::delay::DelayNs` for `TIMGx` timers (#2084) - Implement `embedded_hal_async::delay::DelayNs` for `TIMGx` timers (#2084)
- Added `Efuse::read_bit` (#2259) - 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) - Added `Rtc::disable_rom_message_printing` (S3 and H2 only) (#2280)
### Changed ### Changed

View File

@ -364,7 +364,8 @@ mod gdma;
mod pdma; mod pdma;
/// Kinds of interrupt to listen to. /// Kinds of interrupt to listen to.
#[derive(EnumSetType)] #[derive(Debug, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DmaInterrupt { pub enum DmaInterrupt {
/// RX is done /// RX is done
RxDone, RxDone,
@ -373,7 +374,8 @@ pub enum DmaInterrupt {
} }
/// Types of interrupts emitted by the TX channel. /// Types of interrupts emitted by the TX channel.
#[derive(EnumSetType)] #[derive(Debug, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DmaTxInterrupt { pub enum DmaTxInterrupt {
/// Triggered when all data corresponding to a linked list (including /// Triggered when all data corresponding to a linked list (including
/// multiple descriptors) have been sent via transmit channel. /// multiple descriptors) have been sent via transmit channel.
@ -394,7 +396,8 @@ pub enum DmaTxInterrupt {
} }
/// Types of interrupts emitted by the RX channel. /// Types of interrupts emitted by the RX channel.
#[derive(EnumSetType)] #[derive(Debug, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DmaRxInterrupt { pub enum DmaRxInterrupt {
/// Triggered when the size of the buffer pointed by receive descriptors /// Triggered when the size of the buffer pointed by receive descriptors
/// is smaller than the length of data to be received via receive channel. /// is smaller than the length of data to be received via receive channel.

View File

@ -141,8 +141,6 @@ pub enum InputSignal {
U0DSR = 16, U0DSR = 16,
U1RXD = 17, U1RXD = 17,
U1CTS = 18, U1CTS = 18,
I2CM_SDA = 20,
EXT_I2C_SDA = 22,
I2S0O_BCK = 23, I2S0O_BCK = 23,
I2S1O_BCK = 24, I2S1O_BCK = 24,
I2S0O_WS = 25, I2S0O_WS = 25,
@ -157,8 +155,6 @@ pub enum InputSignal {
PWM0_F0 = 34, PWM0_F0 = 34,
PWM0_F1 = 35, PWM0_F1 = 35,
PWM0_F2 = 36, PWM0_F2 = 36,
GPIO_BT_ACTIVE = 37,
GPIO_BT_PRIORITY = 38,
PCNT0_SIG_CH0 = 39, PCNT0_SIG_CH0 = 39,
PCNT0_SIG_CH1 = 40, PCNT0_SIG_CH1 = 40,
PCNT0_CTRL_CH0 = 41, PCNT0_CTRL_CH0 = 41,
@ -209,7 +205,6 @@ pub enum InputSignal {
RMT_SIG_5 = 88, RMT_SIG_5 = 88,
RMT_SIG_6 = 89, RMT_SIG_6 = 89,
RMT_SIG_7 = 90, RMT_SIG_7 = 90,
EXT_ADC_START = 93,
TWAI_RX = 94, TWAI_RX = 94,
I2CEXT1_SCL = 95, I2CEXT1_SCL = 95,
I2CEXT1_SDA = 96, I2CEXT1_SDA = 96,
@ -231,29 +226,6 @@ pub enum InputSignal {
PWM1_CAP0 = 112, PWM1_CAP0 = 112,
PWM1_CAP1 = 113, PWM1_CAP1 = 113,
PWM1_CAP2 = 114, 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_0 = 140,
I2S0I_DATA_1 = 141, I2S0I_DATA_1 = 141,
I2S0I_DATA_2 = 142, I2S0I_DATA_2 = 142,
@ -303,11 +275,6 @@ pub enum InputSignal {
PCMFSYNC = 204, PCMFSYNC = 204,
PCMCLK = 205, PCMCLK = 205,
PCMDIN = 206, 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_DATA0 = 512,
SD_DATA1, SD_DATA1,
@ -365,10 +332,6 @@ pub enum OutputSignal {
U0DTR = 16, U0DTR = 16,
U1TXD = 17, U1TXD = 17,
U1RTS = 18, U1RTS = 18,
I2CM_SCL = 19,
I2CM_SDA = 20,
EXT2C_SCL = 21,
EXT2C_SDA = 22,
I2S0O_BCK = 23, I2S0O_BCK = 23,
I2S1O_BCK = 24, I2S1O_BCK = 24,
I2S0O_WS = 25, I2S0O_WS = 25,
@ -384,27 +347,6 @@ pub enum OutputSignal {
PWM0_1B = 35, PWM0_1B = 35,
PWM0_2A = 36, PWM0_2A = 36,
PWM0_2B = 37, 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, HSPICS1 = 61,
HSPICS2 = 62, HSPICS2 = 62,
VSPICLK = 63, VSPICLK = 63,
@ -458,29 +400,9 @@ pub enum OutputSignal {
PWM1_1B = 111, PWM1_1B = 111,
PWM1_2A = 112, PWM1_2A = 112,
PWM1_2B = 113, 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_TX = 123,
TWAI_BUS_OFF_ON = 124, TWAI_BUS_OFF_ON = 124,
TWAI_CLKOUT = 125, 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_0 = 140,
I2S0O_DATA_1 = 141, I2S0O_DATA_1 = 141,
I2S0O_DATA_2 = 142, I2S0O_DATA_2 = 142,
@ -531,14 +453,6 @@ pub enum OutputSignal {
I2S1O_DATA_21 = 187, I2S1O_DATA_21 = 187,
I2S1O_DATA_22 = 188, I2S1O_DATA_22 = 188,
I2S1O_DATA_23 = 189, 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, U2TXD = 198,
U2RTS = 199, U2RTS = 199,
EMAC_MDC = 200, EMAC_MDC = 200,

View File

@ -12,7 +12,6 @@
use crate::dma::DmaError; use crate::dma::DmaError;
pub mod master; pub mod master;
#[cfg(not(esp32))]
pub mod slave; pub mod slave;
/// SPI errors /// SPI errors

View File

@ -22,8 +22,8 @@
//! # use esp_hal::dma::Dma; //! # use esp_hal::dma::Dma;
//! # use esp_hal::gpio::Io; //! # use esp_hal::gpio::Io;
//! let dma = Dma::new(peripherals.DMA); //! let dma = Dma::new(peripherals.DMA);
#![cfg_attr(esp32s2, doc = "let dma_channel = dma.spi2channel;")] #![cfg_attr(pdma, doc = "let dma_channel = dma.spi2channel;")]
#![cfg_attr(not(esp32s2), doc = "let dma_channel = dma.channel0;")] #![cfg_attr(gdma, doc = "let dma_channel = dma.channel0;")]
//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
//! let sclk = io.pins.gpio0; //! let sclk = io.pins.gpio0;
//! let miso = io.pins.gpio1; //! let miso = io.pins.gpio1;
@ -58,16 +58,17 @@
//! ## Implementation State //! ## Implementation State
//! //!
//! There are several options for working with the SPI peripheral in slave mode, //! There are several options for working with the SPI peripheral in slave mode,
//! but the code currently only supports single transfers (not segmented //! but the code currently only supports:
//! transfers), full duplex, single bit (not dual or quad SPI), and DMA mode //! - Single transfers (not segmented transfers)
//! (not CPU mode). It also does not support blocking operations, as the actual //! - 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, //! transfer is controlled by the SPI master; if these are necessary,
//! then the DmaTransfer trait instance can be wait()ed on or polled for //! then the `DmaTransfer` object can be `wait()`ed on or polled for
//! is_done(). //! `is_done()`.
//! - ESP32 does not support SPI Slave. See [tracking issue].
//! //!
//! [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 core::marker::PhantomData;
use super::{Error, FullDuplexMode, SpiMode}; use super::{Error, FullDuplexMode, SpiMode};
@ -82,7 +83,9 @@ use crate::{
const MAX_DMA_SIZE: usize = 32768 - 32; 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> { pub struct Spi<'d, T, M> {
spi: PeripheralRef<'d, T>, spi: PeripheralRef<'d, T>,
#[allow(dead_code)] #[allow(dead_code)]
@ -138,7 +141,7 @@ where
_mode: PhantomData, _mode: PhantomData,
}; };
spi.spi.init(); spi.spi.init();
spi.spi.set_data_mode(mode); spi.spi.set_data_mode(mode, false);
spi spi
} }
@ -174,8 +177,9 @@ pub mod dma {
impl<'d> Spi<'d, crate::peripherals::SPI2, FullDuplexMode> { impl<'d> Spi<'d, crate::peripherals::SPI2, FullDuplexMode> {
/// Configures the SPI3 peripheral with the provided DMA channel and /// Configures the SPI3 peripheral with the provided DMA channel and
/// descriptors. /// descriptors.
#[cfg_attr(esp32, doc = "\n\n**Note**: ESP32 only supports Mode 1 and 3.")]
pub fn with_dma<C, DmaMode>( pub fn with_dma<C, DmaMode>(
self, mut self,
channel: Channel<'d, C, DmaMode>, channel: Channel<'d, C, DmaMode>,
rx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor],
tx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor],
@ -185,6 +189,7 @@ pub mod dma {
C::P: SpiPeripheral + Spi2Peripheral, C::P: SpiPeripheral + Spi2Peripheral,
DmaMode: Mode, DmaMode: Mode,
{ {
self.spi.set_data_mode(self.data_mode, true);
SpiDma { SpiDma {
spi: self.spi, spi: self.spi,
channel, channel,
@ -198,8 +203,9 @@ pub mod dma {
impl<'d> Spi<'d, crate::peripherals::SPI3, FullDuplexMode> { impl<'d> Spi<'d, crate::peripherals::SPI3, FullDuplexMode> {
/// Configures the SPI3 peripheral with the provided DMA channel and /// Configures the SPI3 peripheral with the provided DMA channel and
/// descriptors. /// descriptors.
#[cfg_attr(esp32, doc = "\n\n**Note**: ESP32 only supports Mode 1 and 3.")]
pub fn with_dma<C, DmaMode>( pub fn with_dma<C, DmaMode>(
self, mut self,
channel: Channel<'d, C, DmaMode>, channel: Channel<'d, C, DmaMode>,
rx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor],
tx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor],
@ -209,6 +215,7 @@ pub mod dma {
C::P: SpiPeripheral + Spi3Peripheral, C::P: SpiPeripheral + Spi3Peripheral,
DmaMode: Mode, DmaMode: Mode,
{ {
self.spi.set_data_mode(self.data_mode, true);
SpiDma { SpiDma {
spi: self.spi, spi: self.spi,
channel, channel,
@ -327,7 +334,16 @@ pub mod dma {
unsafe { unsafe {
self.spi 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)) .map(move |_| DmaTransferTx::new(self))
} }
} }
@ -353,7 +369,16 @@ pub mod dma {
unsafe { unsafe {
self.spi 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)) .map(move |_| DmaTransferRx::new(self))
} }
} }
@ -421,21 +446,26 @@ pub trait InstanceDma: Instance {
{ {
let reg_block = self.register_block(); let reg_block = self.register_block();
rx.is_done();
tx.is_done();
self.enable_dma(); self.enable_dma();
reset_dma_before_load_dma_dscr(reg_block); reset_spi(reg_block);
rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; if read_buffer_len > 0 {
rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain)?; 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)?; if write_buffer_len > 0 {
tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?; tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_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); reset_dma_before_usr_cmd(reg_block);
#[cfg(not(esp32))]
reg_block reg_block
.dma_conf() .dma_conf()
.modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit());
@ -444,101 +474,52 @@ pub trait InstanceDma: Instance {
self.setup_for_flush(); self.setup_for_flush();
reg_block.cmd().modify(|_, w| w.usr().set_bit()); reg_block.cmd().modify(|_, w| w.usr().set_bit());
rx.start_transfer()?; if read_buffer_len > 0 {
tx.start_transfer()?; rx.start_transfer()?;
}
if write_buffer_len > 0 {
tx.start_transfer()?;
}
Ok(()) Ok(())
} }
unsafe fn start_write_bytes_dma<TX>(
&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();
reset_dma_before_load_dma_dscr(reg_block);
tx_chain.fill_for_tx(false, ptr, len)?;
tx.prepare_transfer_without_start(self.dma_peripheral(), tx_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());
tx.start_transfer()?;
Ok(())
}
unsafe fn start_read_bytes_dma<RX>(
&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());
rx.start_transfer()?;
Ok(())
}
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
fn enable_dma(&self) { fn enable_dma(&self) {
let reg_block = self.register_block(); let reg_block = self.register_block();
reg_block.dma_conf().modify(|_, w| { #[cfg(gdma)]
w.dma_tx_ena().set_bit(); {
w.dma_rx_ena().set_bit(); reg_block.dma_conf().modify(|_, w| {
w.rx_eof_en().clear_bit() w.dma_tx_ena().set_bit();
}); w.dma_rx_ena().set_bit();
w.rx_eof_en().clear_bit()
});
}
#[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(esp32s2)]
fn enable_dma(&self) {
// for non GDMA this is done in `assign_tx_device` / `assign_rx_device`
}
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
fn clear_dma_interrupts(&self) { fn clear_dma_interrupts(&self) {
let reg_block = self.register_block(); let reg_block = self.register_block();
#[cfg(gdma)]
reg_block.dma_int_clr().write(|w| { reg_block.dma_int_clr().write(|w| {
w.dma_infifo_full_err().clear_bit_by_one(); w.dma_infifo_full_err().clear_bit_by_one();
w.dma_outfifo_empty_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_rx_afifo_wfull_err().clear_bit_by_one();
w.mst_tx_afifo_rempty_err().clear_bit_by_one() w.mst_tx_afifo_rempty_err().clear_bit_by_one()
}); });
}
#[cfg(esp32s2)] #[cfg(pdma)]
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(); w.inlink_dscr_empty().clear_bit_by_one();
w.outlink_dscr_error().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) { fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) {
#[cfg(gdma)]
reg_block.dma_conf().modify(|_, w| { reg_block.dma_conf().modify(|_, w| {
w.rx_afifo_rst().set_bit(); w.rx_afifo_rst().set_bit();
w.buf_afifo_rst().set_bit(); w.buf_afifo_rst().set_bit();
w.dma_afifo_rst().set_bit() w.dma_afifo_rst().set_bit()
}); });
}
#[cfg(esp32s2)] #[cfg(pdma)]
fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { let _ = reg_block;
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());
} }
impl InstanceDma for crate::peripherals::SPI2 { impl InstanceDma for crate::peripherals::SPI2 {
@ -626,6 +585,8 @@ impl InstanceDma for crate::peripherals::SPI3 {
pub trait Instance: private::Sealed { pub trait Instance: private::Sealed {
fn register_block(&self) -> &RegisterBlock; fn register_block(&self) -> &RegisterBlock;
fn peripheral(&self) -> crate::system::Peripheral;
fn sclk_signal(&self) -> InputSignal; fn sclk_signal(&self) -> InputSignal;
fn mosi_signal(&self) -> InputSignal; fn mosi_signal(&self) -> InputSignal;
@ -634,8 +595,6 @@ pub trait Instance: private::Sealed {
fn cs_signal(&self) -> InputSignal; fn cs_signal(&self) -> InputSignal;
fn peripheral(&self) -> crate::system::Peripheral;
#[inline(always)] #[inline(always)]
fn reset_peripheral(&self) { fn reset_peripheral(&self) {
PeripheralClockControl::reset(self.peripheral()); PeripheralClockControl::reset(self.peripheral());
@ -648,6 +607,24 @@ pub trait Instance: private::Sealed {
fn spi_num(&self) -> u8; 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 /// Initialize for full-duplex 1 bit mode
fn init(&mut self) { fn init(&mut self) {
let reg_block = self.register_block(); 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.user().write(|w| unsafe { w.bits(0) });
reg_block.ctrl().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| { reg_block.user().modify(|_, w| {
w.usr_miso_highpart().clear_bit();
w.doutdin().set_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() w.sio().clear_bit()
}); });
#[cfg(not(esp32s2))] #[cfg(not(esp32))]
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
});
reg_block.misc().write(|w| unsafe { w.bits(0) }); 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(); 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)
}
}
});
reg_block.user().modify(|_, w| { if dma {
w.tsck_i_edge() assert!(
.bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)); matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3),
w.rsck_i_edge() "Mode {:?} is not supported with DMA",
.bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)) data_mode
}); );
cfg_if::cfg_if! {
if #[cfg(esp32s2)] {
let ctrl1_reg = reg_block.ctrl1();
} else {
let ctrl1_reg = reg_block.slave();
} }
} }
ctrl1_reg.modify(|_, w| {
w.clk_mode_13() #[cfg(not(esp32))]
.bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3)) {
}); _ = 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))
});
}
self self
} }
@ -716,11 +728,11 @@ pub trait Instance: private::Sealed {
fn is_bus_busy(&self) -> bool { fn is_bus_busy(&self) -> bool {
let reg_block = self.register_block(); let reg_block = self.register_block();
#[cfg(esp32s2)] #[cfg(pdma)]
{ {
reg_block.slave().read().trans_done().bit_is_clear() 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() reg_block.dma_int_raw().read().trans_done().bit_is_clear()
} }
@ -737,51 +749,76 @@ pub trait Instance: private::Sealed {
// Clear the transaction-done interrupt flag so flush() can work properly. Not // Clear the transaction-done interrupt flag so flush() can work properly. Not
// used in DMA mode. // used in DMA mode.
fn setup_for_flush(&self) { fn setup_for_flush(&self) {
#[cfg(esp32s2)] #[cfg(pdma)]
self.register_block() self.register_block()
.slave() .slave()
.modify(|_, w| w.trans_done().clear_bit()); .modify(|_, w| w.trans_done().clear_bit());
#[cfg(not(esp32s2))] #[cfg(gdma)]
self.register_block() self.register_block()
.dma_int_clr() .dma_int_clr()
.write(|w| w.trans_done().clear_bit_by_one()); .write(|w| w.trans_done().clear_bit_by_one());
} }
} }
#[cfg(spi2)]
impl Instance for crate::peripherals::SPI2 { impl Instance for crate::peripherals::SPI2 {
#[inline(always)] #[inline(always)]
fn register_block(&self) -> &RegisterBlock { fn register_block(&self) -> &RegisterBlock {
self self
} }
#[inline(always)]
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::Spi2
}
#[inline(always)] #[inline(always)]
fn spi_num(&self) -> u8 { fn spi_num(&self) -> u8 {
2 2
} }
#[inline(always)]
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::Spi2
}
#[inline(always)] #[inline(always)]
fn sclk_signal(&self) -> InputSignal { fn sclk_signal(&self) -> InputSignal {
InputSignal::FSPICLK cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::HSPICLK
} else {
InputSignal::FSPICLK
}
}
} }
#[inline(always)] #[inline(always)]
fn mosi_signal(&self) -> InputSignal { fn mosi_signal(&self) -> InputSignal {
InputSignal::FSPID cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::HSPID
} else {
InputSignal::FSPID
}
}
} }
#[inline(always)] #[inline(always)]
fn miso_signal(&self) -> OutputSignal { fn miso_signal(&self) -> OutputSignal {
OutputSignal::FSPIQ cfg_if::cfg_if! {
if #[cfg(esp32)] {
OutputSignal::HSPIQ
} else {
OutputSignal::FSPIQ
}
}
} }
#[inline(always)] #[inline(always)]
fn cs_signal(&self) -> InputSignal { fn cs_signal(&self) -> InputSignal {
InputSignal::FSPICS0 cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::HSPICS0
} else {
InputSignal::FSPICS0
}
}
} }
} }
@ -792,33 +829,57 @@ impl Instance for crate::peripherals::SPI3 {
self self
} }
#[inline(always)]
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::Spi3
}
#[inline(always)] #[inline(always)]
fn spi_num(&self) -> u8 { fn spi_num(&self) -> u8 {
3 3
} }
#[inline(always)]
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::Spi3
}
#[inline(always)] #[inline(always)]
fn sclk_signal(&self) -> InputSignal { fn sclk_signal(&self) -> InputSignal {
InputSignal::SPI3_CLK cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::VSPICLK
} else {
InputSignal::SPI3_CLK
}
}
} }
#[inline(always)] #[inline(always)]
fn mosi_signal(&self) -> InputSignal { fn mosi_signal(&self) -> InputSignal {
InputSignal::SPI3_D cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::VSPID
} else {
InputSignal::SPI3_D
}
}
} }
#[inline(always)] #[inline(always)]
fn miso_signal(&self) -> OutputSignal { fn miso_signal(&self) -> OutputSignal {
OutputSignal::SPI3_Q cfg_if::cfg_if! {
if #[cfg(esp32)] {
OutputSignal::VSPIQ
} else {
OutputSignal::SPI3_Q
}
}
} }
#[inline(always)] #[inline(always)]
fn cs_signal(&self) -> InputSignal { fn cs_signal(&self) -> InputSignal {
InputSignal::SPI3_CS0 cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::VSPICS0
} else {
InputSignal::SPI3_CS0
}
}
} }
} }

View File

@ -1,6 +1,9 @@
//! SPI slave mode test suite. //! 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_std]
#![no_main] #![no_main]
@ -51,20 +54,22 @@ impl BitbangSpi {
} }
fn assert_cs(&mut self) { fn assert_cs(&mut self) {
self.sclk.set_level(Level::Low);
self.cs.set_level(Level::Low); self.cs.set_level(Level::Low);
} }
fn deassert_cs(&mut self) { fn deassert_cs(&mut self) {
self.sclk.set_level(Level::Low);
self.cs.set_level(Level::High); 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 { fn shift_bit(&mut self, bit: bool) -> bool {
self.mosi.set_level(Level::from(bit)); 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(); let miso = self.miso.get_level().into();
self.sclk.set_level(Level::High); self.sclk.set_level(Level::Low);
miso miso
} }
@ -98,6 +103,7 @@ mod tests {
let peripherals = esp_hal::init(esp_hal::Config::default()); let peripherals = esp_hal::init(esp_hal::Config::default());
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
let (mosi_pin, miso_pin) = hil_test::i2c_pins!(io); let (mosi_pin, miso_pin) = hil_test::i2c_pins!(io);
let (sclk_pin, sclk_gpio) = hil_test::common_test_pins!(io); let (sclk_pin, sclk_gpio) = hil_test::common_test_pins!(io);
let cs_pin = hil_test::unconnected_pin!(io); let cs_pin = hil_test::unconnected_pin!(io);
@ -115,6 +121,7 @@ mod tests {
let cs = cs_pin.peripheral_input(); let cs = cs_pin.peripheral_input();
let mosi = mosi_pin.peripheral_input(); let mosi = mosi_pin.peripheral_input();
let mut miso = miso_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 mosi_gpio = Output::new(mosi_pin, Level::Low);
let cs_gpio = Output::new(cs_pin, Level::High); let cs_gpio = Output::new(cs_pin, Level::High);
@ -122,11 +129,11 @@ mod tests {
let spi = Spi::new( let spi = Spi::new(
peripherals.SPI2, peripherals.SPI2,
sclk_pin, sclk_signal,
mosi, mosi,
miso_pin, miso_pin,
cs, cs,
SpiMode::Mode0, SpiMode::Mode1,
); );
miso.enable_input(true, unsafe { esp_hal::Internal::conjure() }); 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 (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(DMA_SIZE);
let mut spi = ctx.spi.with_dma( let mut spi = ctx.spi.with_dma(
ctx.dma_channel.configure(false, DmaPriority::Priority0), ctx.dma_channel.configure(false, DmaPriority::Priority0),
tx_descriptors,
rx_descriptors, rx_descriptors,
tx_descriptors,
); );
let slave_send = tx_buffer; let slave_send = tx_buffer;
let slave_receive = rx_buffer; let slave_receive = rx_buffer;