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)
- 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

View File

@ -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.

View File

@ -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,

View File

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

View File

@ -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<C, DmaMode>(
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<C, DmaMode>(
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,21 +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);
reset_spi(reg_block);
rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?;
rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain)?;
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)?;
}
tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?;
tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?;
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)?;
}
#[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());
@ -444,101 +474,52 @@ pub trait InstanceDma: Instance {
self.setup_for_flush();
reg_block.cmd().modify(|_, w| w.usr().set_bit());
rx.start_transfer()?;
tx.start_transfer()?;
if read_buffer_len > 0 {
rx.start_transfer()?;
}
if write_buffer_len > 0 {
tx.start_transfer()?;
}
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) {
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(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) {
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)
}
}
});
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();
if dma {
assert!(
matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3),
"Mode {:?} is not supported with DMA",
data_mode
);
}
}
ctrl1_reg.modify(|_, w| {
w.clk_mode_13()
.bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3))
});
#[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))
});
}
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,51 +749,76 @@ 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 {
self
}
#[inline(always)]
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::Spi2
}
#[inline(always)]
fn spi_num(&self) -> u8 {
2
}
#[inline(always)]
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
}
}
}
}
@ -792,33 +829,57 @@ impl Instance for crate::peripherals::SPI3 {
self
}
#[inline(always)]
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::Spi3
}
#[inline(always)]
fn spi_num(&self) -> u8 {
3
}
#[inline(always)]
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
}
}
}
}

View File

@ -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;