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:
parent
ba96bac776
commit
af3f892381
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
use crate::dma::DmaError;
|
||||
|
||||
pub mod master;
|
||||
#[cfg(not(esp32))]
|
||||
pub mod slave;
|
||||
|
||||
/// SPI errors
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user