Extract SPI metadata (#3702)

* Extract SPI M/S metadata

* Clean up additional instance trait

* Further SPI cleanups, add OSPI pin setters

* Re-generate semver baseline

* Fix typo
This commit is contained in:
Dániel Buga 2025-07-01 13:13:26 +02:00 committed by GitHub
parent 9eadaa147f
commit 175f4a16b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 380 additions and 338 deletions

View File

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `i2c::master::BusTimeout::Disabled` for ESP32-S2 (#3591)
- The `const CHANNEL: u8` parameter of RMT channels can now be erased via `Channel::degrade()`. (#3505)
- ESP32-C6: GPIO6 now implements `AnalogPin` (#3668)
- SPI master: Expose octal SPI-specific `with_sio` functions (#3702)
### Changed

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -41,6 +41,7 @@ use core::marker::PhantomData;
#[instability::unstable]
pub use dma::*;
use enumset::{EnumSet, EnumSetType};
use procmacros::enable_doc_switch;
#[cfg(place_spi_master_driver_in_ram)]
use procmacros::ram;
@ -59,7 +60,7 @@ use crate::{
OutputConfig,
OutputSignal,
PinGuard,
interconnect::{PeripheralInput, PeripheralOutput},
interconnect::{self, PeripheralInput, PeripheralOutput},
},
interrupt::InterruptHandler,
pac::spi2::RegisterBlock,
@ -611,12 +612,20 @@ impl Config {
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct SpiPinGuard {
mosi_pin: PinGuard,
sclk_pin: PinGuard,
cs_pin: PinGuard,
sio0_pin: PinGuard,
sio1_pin: PinGuard,
sio2_pin: Option<PinGuard>,
sio3_pin: Option<PinGuard>,
#[cfg(spi_master_has_octal)]
sio4_pin: Option<PinGuard>,
#[cfg(spi_master_has_octal)]
sio5_pin: Option<PinGuard>,
#[cfg(spi_master_has_octal)]
sio6_pin: Option<PinGuard>,
#[cfg(spi_master_has_octal)]
sio7_pin: Option<PinGuard>,
}
/// Configuration errors.
@ -679,42 +688,40 @@ impl<'d> Spi<'d, Blocking> {
pub fn new(spi: impl Instance + 'd, config: Config) -> Result<Self, ConfigError> {
let guard = PeripheralGuard::new(spi.info().peripheral);
let mosi_pin = PinGuard::new_unconnected(spi.info().mosi);
let sclk_pin = PinGuard::new_unconnected(spi.info().sclk);
let cs_pin = PinGuard::new_unconnected(spi.info().cs[0]);
let sio1_pin = PinGuard::new_unconnected(spi.info().sio1_output);
let sio2_pin = spi.info().sio2_output.map(PinGuard::new_unconnected);
let sio3_pin = spi.info().sio3_output.map(PinGuard::new_unconnected);
let mut this = Spi {
spi: spi.degrade(),
_mode: PhantomData,
guard,
pins: SpiPinGuard {
mosi_pin,
sclk_pin,
cs_pin,
sio1_pin,
sio2_pin,
sio3_pin,
sclk_pin: PinGuard::new_unconnected(spi.info().sclk),
cs_pin: PinGuard::new_unconnected(spi.info().cs(0)),
sio0_pin: PinGuard::new_unconnected(spi.info().sio_output(0)),
sio1_pin: PinGuard::new_unconnected(spi.info().sio_output(1)),
sio2_pin: spi.info().opt_sio_output(2).map(PinGuard::new_unconnected),
sio3_pin: spi.info().opt_sio_output(3).map(PinGuard::new_unconnected),
#[cfg(spi_master_has_octal)]
sio4_pin: spi.info().opt_sio_output(4).map(PinGuard::new_unconnected),
#[cfg(spi_master_has_octal)]
sio5_pin: spi.info().opt_sio_output(5).map(PinGuard::new_unconnected),
#[cfg(spi_master_has_octal)]
sio6_pin: spi.info().opt_sio_output(6).map(PinGuard::new_unconnected),
#[cfg(spi_master_has_octal)]
sio7_pin: spi.info().opt_sio_output(7).map(PinGuard::new_unconnected),
},
spi: spi.degrade(),
};
this.driver().init();
this.apply_config(&config)?;
let this = this
.with_sio0(NoPin)
.with_sio1(NoPin)
.with_sck(NoPin)
.with_cs(NoPin);
let this = this.with_sck(NoPin).with_cs(NoPin);
let is_qspi = this.driver().info.sio2_input.is_some();
if is_qspi {
unwrap!(this.driver().info.sio2_input).connect_to(&NoPin);
unwrap!(this.driver().info.sio2_output).connect_to(&NoPin);
unwrap!(this.driver().info.sio3_input).connect_to(&NoPin);
unwrap!(this.driver().info.sio3_output).connect_to(&NoPin);
for sio in 0..8 {
if let Some(signal) = this.driver().info.opt_sio_input(sio) {
signal.connect_to(&NoPin);
}
if let Some(signal) = this.driver().info.opt_sio_output(sio) {
signal.connect_to(&NoPin);
}
}
Ok(this)
@ -722,7 +729,7 @@ impl<'d> Spi<'d, Blocking> {
/// Converts the SPI instance into async mode.
pub fn into_async(mut self) -> Spi<'d, Async> {
self.set_interrupt_handler(self.spi.handler());
self.set_interrupt_handler(self.spi.info().async_handler);
Spi {
spi: self.spi,
_mode: PhantomData,
@ -731,6 +738,7 @@ impl<'d> Spi<'d, Blocking> {
}
}
#[enable_doc_switch]
/// Configures the SPI instance to use DMA with the specified channel.
///
/// This method prepares the SPI instance for DMA transfers using SPI
@ -742,10 +750,9 @@ impl<'d> Spi<'d, Blocking> {
/// # use esp_hal::spi::master::{Config, Spi};
/// # use esp_hal::dma::{DmaRxBuf, DmaTxBuf};
/// # use esp_hal::dma_buffers;
#[cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = peripherals.DMA_SPI2;")]
#[cfg_attr(
not(any(esp32, esp32s2)),
doc = "let dma_channel = peripherals.DMA_CH0;"
#[doc_switch(
cfg(any(esp32, esp32s2)) => "let dma_channel = peripherals.DMA_SPI2;",
_ => "let dma_channel = peripherals.DMA_CH0;",
)]
/// let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) =
/// dma_buffers!(32000);
@ -776,13 +783,10 @@ impl<'d> Spi<'d, Blocking> {
SpiDma::new(self.spi, self.pins, channel.degrade())
}
#[cfg_attr(
not(multi_core),
doc = "Registers an interrupt handler for the peripheral."
)]
#[cfg_attr(
multi_core,
doc = "Registers an interrupt handler for the peripheral on the current core."
#[enable_doc_switch]
#[doc_switch(
cfg(multi_core) => "Registers an interrupt handler for the peripheral on the current core.",
_ => "Registers an interrupt handler for the peripheral.",
)]
#[doc = ""]
/// Note that this will replace any previously registered interrupt
@ -843,17 +847,28 @@ impl<'d> Spi<'d, Async> {
}
}
macro_rules! def_with_sio_pin {
($fn:ident, $field:ident, $n:literal) => {
#[doc = concat!(" Assign the SIO", stringify!($n), " pin for the SPI instance.")]
#[doc = " "]
#[doc = " Enables both input and output functionality for the pin, and connects it"]
#[doc = concat!(" to the SIO", stringify!($n), " output and input signals.")]
#[instability::unstable]
pub fn $fn(mut self, sio: impl PeripheralOutput<'d>) -> Self {
self.pins.$field = Some(self.connect_sio_pin(sio.into(), $n));
self
}
};
}
impl<'d, Dm> Spi<'d, Dm>
where
Dm: DriverMode,
{
fn connect_sio_pin(
&self,
pin: impl PeripheralOutput<'d>,
in_signal: InputSignal,
out_signal: OutputSignal,
) -> PinGuard {
let pin = pin.into();
fn connect_sio_pin(&self, pin: interconnect::OutputSignal<'d>, n: usize) -> PinGuard {
let in_signal = self.spi.info().sio_input(n);
let out_signal = self.spi.info().sio_output(n);
pin.apply_input_config(&InputConfig::default());
pin.apply_output_config(&OutputConfig::default());
@ -865,9 +880,17 @@ where
pin.connect_with_guard(out_signal)
}
fn connect_output_pin(&self, pin: impl PeripheralOutput<'d>, signal: OutputSignal) -> PinGuard {
let pin = pin.into();
fn connect_sio_output_pin(&self, pin: interconnect::OutputSignal<'d>, n: usize) -> PinGuard {
let out_signal = self.spi.info().sio_output(n);
self.connect_output_pin(pin, out_signal)
}
fn connect_output_pin(
&self,
pin: interconnect::OutputSignal<'d>,
signal: OutputSignal,
) -> PinGuard {
pin.apply_output_config(&OutputConfig::default());
pin.set_output_enable(true); // TODO turn this bool into a Yes/No/PeripheralControl trio
@ -881,7 +904,7 @@ where
///
/// Disconnects the previous pin that was assigned with `with_sck`.
pub fn with_sck(mut self, sclk: impl PeripheralOutput<'d>) -> Self {
self.pins.sclk_pin = self.connect_output_pin(sclk, self.driver().info.sclk);
self.pins.sclk_pin = self.connect_output_pin(sclk.into(), self.driver().info.sclk);
self
}
@ -895,7 +918,7 @@ where
/// Disconnects the previous pin that was assigned with `with_mosi` or
/// `with_sio0`.
pub fn with_mosi(mut self, mosi: impl PeripheralOutput<'d>) -> Self {
self.pins.mosi_pin = self.connect_output_pin(mosi, self.driver().info.mosi);
self.pins.sio0_pin = self.connect_sio_output_pin(mosi.into(), 0);
self
}
@ -913,7 +936,7 @@ where
miso.apply_input_config(&InputConfig::default());
miso.set_input_enable(true);
self.driver().info.miso.connect_to(&miso);
self.driver().info.sio_input(1).connect_to(&miso);
self
}
@ -921,20 +944,18 @@ where
/// Assign the SIO0 pin for the SPI instance.
///
/// Enables both input and output functionality for the pin, and connects it
/// to the MOSI signal and SIO0 input signal.
/// to the MOSI output signal and SIO0 input signal.
///
/// Disconnects the previous pin that was assigned with `with_sio0` or
/// `with_mosi`.
///
/// Use this if any of the devices on the bus use half-duplex SPI.
///
/// The pin is configured to open-drain mode.
///
/// Note: You do not need to call [Self::with_mosi] when this is used.
/// See also [Self::with_mosi] when you only need a one-directional MOSI
/// signal.
#[instability::unstable]
pub fn with_sio0(mut self, mosi: impl PeripheralOutput<'d>) -> Self {
self.pins.mosi_pin =
self.connect_sio_pin(mosi, self.driver().info.sio0_input, self.driver().info.mosi);
self.pins.sio0_pin = self.connect_sio_pin(mosi.into(), 0);
self
}
@ -942,55 +963,35 @@ where
/// Assign the SIO1/MISO pin for the SPI instance.
///
/// Enables both input and output functionality for the pin, and connects it
/// to the MISO signal and SIO1 input signal.
/// to the MISO input signal and SIO1 output signal.
///
/// Disconnects the previous pin that was assigned with `with_sio1`.
///
/// Use this if any of the devices on the bus use half-duplex SPI.
///
/// The pin is configured to open-drain mode.
///
/// Note: You do not need to call [Self::with_miso] when this is used.
/// See also [Self::with_miso] when you only need a one-directional MISO
/// signal.
#[instability::unstable]
pub fn with_sio1(mut self, sio1: impl PeripheralOutput<'d>) -> Self {
self.pins.sio1_pin = self.connect_sio_pin(
sio1,
self.driver().info.miso,
self.driver().info.sio1_output,
);
self.pins.sio1_pin = self.connect_sio_pin(sio1.into(), 1);
self
}
/// Assign the SIO2 pin for the SPI instance.
///
/// Enables both input and output functionality for the pin, and connects it
/// to the SIO2 output and input signals.
#[instability::unstable]
pub fn with_sio2(mut self, sio2: impl PeripheralOutput<'d>) -> Self {
self.pins.sio2_pin = Some(self.connect_sio_pin(
sio2,
unwrap!(self.driver().info.sio2_input),
unwrap!(self.driver().info.sio2_output),
));
def_with_sio_pin!(with_sio2, sio2_pin, 2);
def_with_sio_pin!(with_sio3, sio3_pin, 3);
self
}
#[cfg(spi_master_has_octal)]
def_with_sio_pin!(with_sio4, sio4_pin, 4);
/// Assign the SIO3 pin for the SPI instance.
///
/// Enables both input and output functionality for the pin, and connects it
/// to the SIO3 output and input signals.
#[instability::unstable]
pub fn with_sio3(mut self, sio3: impl PeripheralOutput<'d>) -> Self {
self.pins.sio3_pin = Some(self.connect_sio_pin(
sio3,
unwrap!(self.driver().info.sio3_input),
unwrap!(self.driver().info.sio3_output),
));
#[cfg(spi_master_has_octal)]
def_with_sio_pin!(with_sio5, sio5_pin, 5);
self
}
#[cfg(spi_master_has_octal)]
def_with_sio_pin!(with_sio6, sio6_pin, 6);
#[cfg(spi_master_has_octal)]
def_with_sio_pin!(with_sio7, sio7_pin, 7);
/// Assign the CS (Chip Select) pin for the SPI instance.
///
@ -1005,17 +1006,20 @@ where
/// mechanism to select which CS line to use.
#[instability::unstable]
pub fn with_cs(mut self, cs: impl PeripheralOutput<'d>) -> Self {
self.pins.cs_pin = self.connect_output_pin(cs, self.driver().info.cs[0]);
self.pins.cs_pin = self.connect_output_pin(cs.into(), self.driver().info.cs(0));
self
}
#[procmacros::enable_doc_switch]
/// Change the bus configuration.
///
/// # Errors
///
/// If frequency passed in config exceeds
#[cfg_attr(not(esp32h2), doc = " 80MHz")]
#[cfg_attr(esp32h2, doc = " 48MHz")]
#[doc_switch(
cfg(esp32h2) => " 48MHz",
_ => " 80MHz",
)]
/// or is below 70kHz,
/// [`ConfigError::UnsupportedFrequency`] error will be returned.
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
@ -1185,12 +1189,15 @@ mod dma {
sync::atomic::{Ordering, fence},
};
use procmacros::enable_doc_switch;
use super::*;
use crate::{
dma::{Channel, DmaRxBuf, DmaTxBuf, EmptyBuf, PeripheralDmaChannel, asynch::DmaRxFuture},
spi::master::dma::asynch::DropGuard,
};
#[enable_doc_switch]
/// A DMA capable SPI instance.
///
/// Using `SpiDma` is not recommended unless you wish
@ -1204,10 +1211,9 @@ mod dma {
/// # use esp_hal::spi::master::{Config, Spi};
/// # use esp_hal::dma::{DmaRxBuf, DmaTxBuf};
/// # use esp_hal::dma_buffers;
#[cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = peripherals.DMA_SPI2;")]
#[cfg_attr(
not(any(esp32, esp32s2)),
doc = "let dma_channel = peripherals.DMA_CH0;"
#[doc_switch(
cfg(any(esp32, esp32s2)) => "let dma_channel = peripherals.DMA_SPI2;",
_ => "let dma_channel = peripherals.DMA_CH0;",
)]
/// let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) =
/// dma_buffers!(32000);
@ -1254,7 +1260,7 @@ mod dma {
/// Converts the SPI instance into async mode.
#[instability::unstable]
pub fn into_async(mut self) -> SpiDma<'d, Async> {
self.set_interrupt_handler(self.spi.handler());
self.set_interrupt_handler(self.spi.info().async_handler);
SpiDma {
spi: self.spi,
channel: self.channel.into_async(),
@ -2649,16 +2655,30 @@ mod ehal1 {
}
/// SPI peripheral instance.
#[doc(hidden)]
#[allow(private_bounds)]
pub trait PeripheralInstance: private::Sealed + DmaEligible {
/// Returns the peripheral data describing this SPI instance.
fn info(&self) -> &'static Info;
#[cfg_attr(not(feature = "unstable"), expect(private_bounds))] // DmaEligible
pub trait Instance: private::Sealed + IntoAnySpi + DmaEligible {
#[doc(hidden)]
/// Returns the peripheral data and state describing this instance.
fn parts(&self) -> (&'static Info, &'static State);
/// Returns the peripheral data describing this instance.
#[doc(hidden)]
#[inline(always)]
fn info(&self) -> &'static Info {
self.parts().0
}
/// Returns the peripheral state for this instance.
#[doc(hidden)]
#[inline(always)]
fn state(&self) -> &'static State {
self.parts().1
}
}
/// Marker trait for QSPI-capable SPI peripherals.
#[doc(hidden)]
pub trait QspiInstance: PeripheralInstance {}
pub trait QspiInstance: Instance {}
/// Peripheral data describing a particular SPI instance.
#[doc(hidden)]
@ -2672,67 +2692,39 @@ pub struct Info {
/// The system peripheral marker.
pub peripheral: crate::system::Peripheral,
/// Interrupt handler for the asynchronous operations.
pub async_handler: InterruptHandler,
/// SCLK signal.
pub sclk: OutputSignal,
/// MOSI signal.
pub mosi: OutputSignal,
/// MISO signal.
pub miso: InputSignal,
/// Chip select signals.
pub cs: &'static [OutputSignal],
/// SIO0 (MOSI) input signal for half-duplex mode.
pub sio0_input: InputSignal,
pub sio_inputs: &'static [InputSignal],
pub sio_outputs: &'static [OutputSignal],
}
/// SIO1 (MISO) output signal for half-duplex mode.
pub sio1_output: OutputSignal,
impl Info {
fn cs(&self, n: usize) -> OutputSignal {
*unwrap!(self.cs.get(n), "CS{} is not defined", n)
}
/// SIO2 output signal for QSPI mode.
pub sio2_output: Option<OutputSignal>,
fn opt_sio_input(&self, n: usize) -> Option<InputSignal> {
self.sio_inputs.get(n).copied()
}
/// SIO2 input signal for QSPI mode.
pub sio2_input: Option<InputSignal>,
fn opt_sio_output(&self, n: usize) -> Option<OutputSignal> {
self.sio_outputs.get(n).copied()
}
/// SIO3 output signal for QSPI mode.
pub sio3_output: Option<OutputSignal>,
fn sio_input(&self, n: usize) -> InputSignal {
unwrap!(self.opt_sio_input(n), "SIO{} is not defined", n)
}
/// SIO3 input signal for QSPI mode.
pub sio3_input: Option<InputSignal>,
/// SIO4 output signal for OPI mode.
#[cfg(spi_octal)]
pub sio4_output: Option<OutputSignal>,
/// SIO4 input signal for OPI mode.
#[cfg(spi_octal)]
pub sio4_input: Option<InputSignal>,
/// SIO5 output signal for OPI mode.
#[cfg(spi_octal)]
pub sio5_output: Option<OutputSignal>,
/// SIO5 input signal for OPI mode.
#[cfg(spi_octal)]
pub sio5_input: Option<InputSignal>,
/// SIO6 output signal for OPI mode.
#[cfg(spi_octal)]
pub sio6_output: Option<OutputSignal>,
/// SIO6 input signal for OPI mode.
#[cfg(spi_octal)]
pub sio6_input: Option<InputSignal>,
/// SIO7 output signal for OPI mode.
#[cfg(spi_octal)]
pub sio7_output: Option<OutputSignal>,
/// SIO7 input signal for OPI mode.
#[cfg(spi_octal)]
pub sio7_input: Option<InputSignal>,
fn sio_output(&self, n: usize) -> OutputSignal {
unwrap!(self.opt_sio_output(n), "SIO{} is not defined", n)
}
}
struct DmaDriver {
@ -3694,89 +3686,46 @@ impl PartialEq for Info {
unsafe impl Sync for Info {}
macro_rules! spi_instance {
($num:literal, $sclk:ident, $mosi:ident, $miso:ident, [$($cs:ident),+] $(, $sio2:ident, $sio3:ident $(, $sio4:ident, $sio5:ident, $sio6:ident, $sio7:ident)?)?) => {
paste::paste! {
impl PeripheralInstance for crate::peripherals::[<SPI $num>]<'_> {
#[inline(always)]
fn info(&self) -> &'static Info {
static INFO: Info = Info {
register_block: crate::peripherals::[<SPI $num>]::regs(),
peripheral: crate::system::Peripheral::[<Spi $num>],
sclk: OutputSignal::$sclk,
mosi: OutputSignal::$mosi,
miso: InputSignal::$miso,
cs: &[$(OutputSignal::$cs),+],
sio0_input: InputSignal::$mosi,
sio1_output: OutputSignal::$miso,
sio2_output: $crate::if_set!($(Some(OutputSignal::$sio2))?, None),
sio2_input: $crate::if_set!($(Some(InputSignal::$sio2))?, None),
sio3_output: $crate::if_set!($(Some(OutputSignal::$sio3))?, None),
sio3_input: $crate::if_set!($(Some(InputSignal::$sio3))?, None),
#[cfg(spi_octal)]
sio4_output: $crate::if_set!($($(Some(OutputSignal::$sio4))?)?, None),
#[cfg(spi_octal)]
sio4_input: $crate::if_set!($($(Some(InputSignal::$sio4))?)?, None),
#[cfg(spi_octal)]
sio5_output: $crate::if_set!($($(Some(OutputSignal::$sio5))?)?, None),
#[cfg(spi_octal)]
sio5_input: $crate::if_set!($($(Some(InputSignal::$sio5))?)?, None),
#[cfg(spi_octal)]
sio6_output: $crate::if_set!($($(Some(OutputSignal::$sio6))?)?, None),
#[cfg(spi_octal)]
sio6_input: $crate::if_set!($($(Some(InputSignal::$sio6))?)?, None),
#[cfg(spi_octal)]
sio7_output: $crate::if_set!($($(Some(OutputSignal::$sio7))?)?, None),
#[cfg(spi_octal)]
sio7_input: $crate::if_set!($($(Some(InputSignal::$sio7))?)?, None),
};
&INFO
crate::peripherals::for_each_spi_master! {
($peri:ident, $sys:ident, $sclk:ident [$($cs:ident),+] [$($sio:ident),*] $(, $is_qspi:tt)?) => {
impl Instance for crate::peripherals::$peri<'_> {
#[inline(always)]
fn parts(&self) -> (&'static Info, &'static State) {
#[crate::handler]
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn irq_handler() {
handle_async(&INFO, &STATE)
}
static INFO: Info = Info {
register_block: crate::peripherals::$peri::regs(),
peripheral: crate::system::Peripheral::$sys,
async_handler: irq_handler,
sclk: OutputSignal::$sclk,
cs: &[$(OutputSignal::$cs),+],
sio_inputs: &[$(InputSignal::$sio),*],
sio_outputs: &[$(OutputSignal::$sio),*],
};
static STATE: State = State {
waker: AtomicWaker::new(),
#[cfg(esp32)]
esp32_hack: Esp32Hack {
timing_miso_delay: Cell::new(None),
extra_dummy: Cell::new(0),
},
};
(&INFO, &STATE)
}
$(
// If the extra pins are set, implement QspiInstance
$crate::ignore!($sio2);
impl QspiInstance for crate::peripherals::[<SPI $num>]<'_> {}
)?
}
}
}
#[cfg(spi_master_spi2)]
cfg_if::cfg_if! {
if #[cfg(esp32)] {
spi_instance!(2, HSPICLK, HSPID, HSPIQ, [HSPICS0, HSPICS1, HSPICS2], HSPIWP, HSPIHD);
} else if #[cfg(any(esp32s2, esp32s3))] {
spi_instance!(2, FSPICLK, FSPID, FSPIQ, [FSPICS0, FSPICS1, FSPICS2, FSPICS3, FSPICS4, FSPICS5], FSPIWP, FSPIHD, FSPIIO4, FSPIIO5, FSPIIO6, FSPIIO7);
} else {
spi_instance!(2, FSPICLK, FSPID, FSPIQ, [FSPICS0, FSPICS1, FSPICS2, FSPICS3, FSPICS4, FSPICS5], FSPIWP, FSPIHD);
}
}
#[cfg(spi_master_spi3)]
cfg_if::cfg_if! {
if #[cfg(esp32)] {
spi_instance!(3, VSPICLK, VSPID, VSPIQ, [VSPICS0, VSPICS1, VSPICS2], VSPIWP, VSPIHD);
} else if #[cfg(esp32s3)] {
spi_instance!(3, SPI3_CLK, SPI3_D, SPI3_Q, [SPI3_CS0, SPI3_CS1, SPI3_CS2], SPI3_WP, SPI3_HD);
} else {
spi_instance!(3, SPI3_CLK, SPI3_D, SPI3_Q, [SPI3_CS0, SPI3_CS1, SPI3_CS2]);
}
}
impl PeripheralInstance for AnySpi<'_> {
delegate::delegate! {
to match &self.0 {
#[cfg(spi_master_spi2)]
AnySpiInner::Spi2(spi) => spi,
#[cfg(spi_master_spi3)]
AnySpiInner::Spi3(spi) => spi,
} {
fn info(&self) -> &'static Info;
}
}
$(
// If the extra pins are set, implement QspiInstance
$crate::ignore!($is_qspi);
impl QspiInstance for crate::peripherals::$peri<'_> {}
)?
};
}
impl QspiInstance for AnySpi<'_> {}
@ -3799,10 +3748,7 @@ struct Esp32Hack {
unsafe impl Sync for Esp32Hack {}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn handle_async(instance: impl Instance) {
let state = instance.state();
let info = instance.info();
fn handle_async(info: &'static Info, state: &'static State) {
let driver = Driver { info, state };
if driver.interrupts().contains(SpiInterrupt::TransferDone) {
driver.enable_listen(SpiInterrupt::TransferDone.into(), false);
@ -3810,43 +3756,6 @@ fn handle_async(instance: impl Instance) {
}
}
/// A peripheral singleton compatible with the SPI master driver.
pub trait Instance: PeripheralInstance + IntoAnySpi {
#[doc(hidden)]
fn state(&self) -> &'static State;
#[doc(hidden)]
fn handler(&self) -> InterruptHandler;
}
macro_rules! master_instance {
($peri:ident) => {
impl Instance for $crate::peripherals::$peri<'_> {
fn state(&self) -> &'static State {
static STATE: State = State {
waker: AtomicWaker::new(),
#[cfg(esp32)]
esp32_hack: Esp32Hack {
timing_miso_delay: Cell::new(None),
extra_dummy: Cell::new(0),
},
};
&STATE
}
fn handler(&self) -> InterruptHandler {
#[$crate::handler]
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn handle() {
handle_async(unsafe { $crate::peripherals::$peri::steal() })
}
handle
}
}
};
}
/// SPI data mode
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -3860,7 +3769,7 @@ pub enum DataMode {
Dual,
/// 4 bit, 4 data lines. (SIO0 .. SIO3)
Quad,
#[cfg(spi_octal)]
#[cfg(spi_master_has_octal)]
/// 8 bit, 8 data lines. (SIO0 .. SIO7)
Octal,
}
@ -3891,11 +3800,6 @@ impl<'d> DmaEligible for AnySpi<'d> {
}
}
#[cfg(spi_master_spi2)]
master_instance!(SPI2);
#[cfg(spi_master_spi3)]
master_instance!(SPI3);
impl Instance for AnySpi<'_> {
delegate::delegate! {
to match &self.0 {
@ -3904,8 +3808,7 @@ impl Instance for AnySpi<'_> {
#[cfg(spi_master_spi3)]
AnySpiInner::Spi3(spi) => spi,
} {
fn state(&self) -> &'static State;
fn handler(&self) -> InterruptHandler;
fn parts(&self) -> (&'static Info, &'static State);
}
}
}

View File

@ -787,23 +787,21 @@ impl PartialEq for Info {
unsafe impl Sync for Info {}
macro_rules! spi_instance {
($num:literal, $sclk:ident, $mosi:ident, $miso:ident, $cs:ident) => {
paste::paste! {
impl Instance for crate::peripherals::[<SPI $num>]<'_> {
#[inline(always)]
fn info(&self) -> &'static Info {
static INFO: Info = Info {
register_block: crate::peripherals::[<SPI $num>]::regs(),
peripheral: crate::system::Peripheral::[<Spi $num>],
sclk: InputSignal::$sclk,
mosi: InputSignal::$mosi,
miso: OutputSignal::$miso,
cs: InputSignal::$cs,
};
crate::peripherals::for_each_spi_slave! {
($peri:ident, $sys:ident, $sclk:ident, $mosi:ident, $miso:ident, $cs:ident) => {
impl Instance for crate::peripherals::$peri<'_> {
#[inline(always)]
fn info(&self) -> &'static Info {
static INFO: Info = Info {
register_block: crate::peripherals::$peri::regs(),
peripheral: crate::system::Peripheral::$sys,
sclk: InputSignal::$sclk,
mosi: InputSignal::$mosi,
miso: OutputSignal::$miso,
cs: InputSignal::$cs,
};
&INFO
}
&INFO
}
}
};
@ -834,21 +832,6 @@ impl<'d> DmaEligible for AnySpi<'d> {
}
}
}
cfg_if::cfg_if! {
if #[cfg(esp32)] {
#[cfg(spi_master_spi2)]
spi_instance!(2, HSPICLK, HSPID, HSPIQ, HSPICS0);
#[cfg(spi_master_spi3)]
spi_instance!(3, VSPICLK, VSPID, VSPIQ, VSPICS0);
} else {
#[cfg(spi_master_spi2)]
spi_instance!(2, FSPICLK, FSPID, FSPIQ, FSPICS0);
#[cfg(spi_master_spi3)]
spi_instance!(3, SPI3_CLK, SPI3_D, SPI3_Q, SPI3_CS0);
}
}
impl Instance for AnySpi<'_> {
delegate::delegate! {
to match &self.0 {

View File

@ -575,7 +575,16 @@ channel_ram_size = 64
[device.spi_master]
support_status = "supported"
instances = [{ name = "spi2" }, { name = "spi3" }]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "HSPICLK", sio = ["HSPID", "HSPIQ", "HSPIWP", "HSPIHD"], cs = ["HSPICS0", "HSPICS1", "HSPICS2"] },
{ name = "spi3", sys_instance = "Spi3", sclk = "VSPICLK", sio = ["VSPID", "VSPIQ", "VSPIWP", "VSPIHD"], cs = ["VSPICS0", "VSPICS1", "VSPICS2"] },
]
[device.spi_slave]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "HSPICLK", mosi = "HSPID", miso = "HSPIQ", cs = "HSPICS0" },
{ name = "spi3", sys_instance = "Spi3", sclk = "VSPICLK", mosi = "VSPID", miso = "VSPIQ", cs = "VSPICS0" },
]
[device.timergroup]
timg_has_timer1 = true
@ -611,7 +620,6 @@ support_status = "not_supported"
[device.pcnt]
[device.sd_host]
[device.sd_slave]
[device.spi_slave]
[device.touch]
[device.twai]

View File

@ -215,7 +215,14 @@ status_registers = 2
[device.spi_master]
support_status = "supported"
instances = [{ name = "spi2" }]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", sio = ["FSPID", "FSPIQ", "FSPIWP", "FSPIHD"], cs = ["FSPICS0", "FSPICS1", "FSPICS2", "FSPICS3", "FSPICS4", "FSPICS5"] },
]
[device.spi_slave]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", mosi = "FSPID", miso = "FSPIQ", cs = "FSPICS0" },
]
[device.timergroup]
instances = [{ name = "timg0" }]
@ -237,7 +244,6 @@ instances = [
## Interfaces
[device.ledc]
[device.spi_slave]
## Miscellaneous
[device.assist_debug]

View File

@ -269,7 +269,14 @@ channel_ram_size = 48
[device.spi_master]
support_status = "supported"
instances = [{ name = "spi2" }]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", sio = ["FSPID", "FSPIQ", "FSPIWP", "FSPIHD"], cs = ["FSPICS0", "FSPICS1", "FSPICS2", "FSPICS3", "FSPICS4", "FSPICS5"] },
]
[device.spi_slave]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", mosi = "FSPID", miso = "FSPIQ", cs = "FSPICS0" },
]
[device.timergroup]
instances = [{ name = "timg0" }, { name = "timg1" }]
@ -296,7 +303,6 @@ support_status = "not_supported"
## Interfaces
[device.i2s]
[device.ledc]
[device.spi_slave]
[device.twai]
[device.usb_serial_jtag]

View File

@ -394,7 +394,14 @@ channel_ram_size = 48
[device.spi_master]
support_status = "supported"
instances = [{ name = "spi2" }]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", sio = ["FSPID", "FSPIQ", "FSPIWP", "FSPIHD"], cs = ["FSPICS0", "FSPICS1", "FSPICS2", "FSPICS3", "FSPICS4", "FSPICS5"] },
]
[device.spi_slave]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", mosi = "FSPID", miso = "FSPIQ", cs = "FSPICS0" },
]
[device.timergroup]
instances = [{ name = "timg0" }, { name = "timg1" }]
@ -429,7 +436,6 @@ has_wifi6 = true
[device.mcpwm]
[device.parl_io]
[device.pcnt]
[device.spi_slave]
[device.sd_slave]
[device.twai]
[device.usb_serial_jtag]

View File

@ -339,7 +339,14 @@ channel_ram_size = 48
[device.spi_master]
support_status = "supported"
instances = [{ name = "spi2" }]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", sio = ["FSPID", "FSPIQ", "FSPIWP", "FSPIHD"], cs = ["FSPICS0", "FSPICS1", "FSPICS2", "FSPICS3", "FSPICS4", "FSPICS5"] },
]
[device.spi_slave]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", mosi = "FSPID", miso = "FSPIQ", cs = "FSPICS0" },
]
[device.timergroup]
instances = [{ name = "timg0" }, { name = "timg1" }]
@ -370,7 +377,6 @@ support_status = "not_supported"
[device.mcpwm]
[device.parl_io]
[device.pcnt]
[device.spi_slave]
[device.twai]
[device.usb_serial_jtag]

View File

@ -69,7 +69,6 @@ symbols = [
"psram",
"psram_dma",
"ulp_riscv_core",
"spi_octal",
# ROM capabilities
"rom_crc_le",
@ -360,7 +359,17 @@ channel_ram_size = 64
[device.spi_master]
support_status = "supported"
instances = [{ name = "spi2" }, { name = "spi3" }]
has_octal = true
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", sio = ["FSPID", "FSPIQ", "FSPIWP", "FSPIHD", "FSPIIO4", "FSPIIO5", "FSPIIO6", "FSPIIO7"], cs = ["FSPICS0", "FSPICS1", "FSPICS2", "FSPICS3", "FSPICS4", "FSPICS5"] },
{ name = "spi3", sys_instance = "Spi3", sclk = "SPI3_CLK", sio = ["SPI3_D", "SPI3_Q"], cs = ["SPI3_CS0", "SPI3_CS1", "SPI3_CS2"] },
]
[device.spi_slave]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", mosi = "FSPID", miso = "FSPIQ", cs = "FSPICS0" },
{ name = "spi3", sys_instance = "Spi3", sclk = "SPI3_CLK", mosi = "SPI3_D", miso = "SPI3_Q", cs = "SPI3_CS0" },
]
[device.timergroup]
timg_has_timer1 = true
@ -397,7 +406,6 @@ support_status = "not_supported"
## Interfaces
[device.i2s]
[device.ledc]
[device.spi_slave]
[device.pcnt]
[device.twai]
[device.usb_otg]

View File

@ -82,7 +82,6 @@ symbols = [
"psram_dma",
"octal_psram",
"ulp_riscv_core",
"spi_octal",
# ROM capabilities
"rom_crc_le",
@ -512,7 +511,17 @@ channel_ram_size = 48
[device.spi_master]
support_status = "supported"
instances = [{ name = "spi2" }, { name = "spi3" }]
has_octal = true
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", sio = ["FSPID", "FSPIQ", "FSPIWP", "FSPIHD", "FSPIIO4", "FSPIIO5", "FSPIIO6", "FSPIIO7"], cs = ["FSPICS0", "FSPICS1", "FSPICS2", "FSPICS3", "FSPICS4", "FSPICS5"] },
{ name = "spi3", sys_instance = "Spi3", sclk = "SPI3_CLK", sio = ["SPI3_D", "SPI3_Q", "SPI3_WP", "SPI3_HD"], cs = ["SPI3_CS0", "SPI3_CS1", "SPI3_CS2"] },
]
[device.spi_slave]
instances = [
{ name = "spi2", sys_instance = "Spi2", sclk = "FSPICLK", mosi = "FSPID", miso = "FSPIQ", cs = "FSPICS0" },
{ name = "spi3", sys_instance = "Spi3", sclk = "SPI3_CLK", mosi = "SPI3_D", miso = "SPI3_Q", cs = "SPI3_CS0" },
]
[device.timergroup]
timg_has_timer1 = true
@ -549,7 +558,6 @@ support_status = "not_supported"
[device.mcpwm]
[device.pcnt]
[device.sd_host]
[device.spi_slave]
[device.twai]
[device.usb_otg]
[device.usb_serial_jtag]

View File

@ -1,9 +1,13 @@
pub(crate) mod gpio;
pub(crate) mod i2c_master;
pub(crate) mod spi_master;
pub(crate) mod spi_slave;
pub(crate) mod uart;
pub(crate) use gpio::*;
pub(crate) use i2c_master::*;
pub(crate) use spi_master::*;
pub(crate) use spi_slave::*;
pub(crate) use uart::*;
/// Represents a value in the driver configuration.
@ -448,13 +452,16 @@ driver_configs![
peripherals: &["sha"],
properties: {}
},
SpiMasterProperties {
SpiMasterProperties<SpiMasterInstanceConfig> {
driver: spi_master,
name: "SPI master",
peripherals: &["spi2", "spi3"],
properties: {}
properties: {
#[serde(default)]
has_octal: bool,
}
},
SpiSlaveProperties {
SpiSlaveProperties<SpiSlaveInstanceConfig> {
driver: spi_slave,
name: "SPI slave",
peripherals: &["spi2", "spi3"],

View File

@ -0,0 +1,49 @@
use proc_macro2::TokenStream;
use quote::format_ident;
use crate::{cfg::SpiMasterProperties, generate_for_each_macro};
/// Instance configuration, used in [device.spi_slave.instances]
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
pub(crate) struct SpiMasterInstanceConfig {
/// The name of the instance in the `esp_hal::system::Peripheral` enum
pub sys_instance: String,
// IOMUX signal names of the instance's signals:
pub sclk: String,
pub sio: Vec<String>,
pub cs: Vec<String>,
}
/// Generates `for_each_spi_slave!` which can be used to implement the SPI
/// master Instance trait for the relevant peripherals. The macro generates code
/// for each [device.spi_master.instances[X]] instance.
pub(crate) fn generate_spi_master_peripherals(spi_slave: &SpiMasterProperties) -> TokenStream {
let instance_cfgs = spi_slave
.instances
.iter()
.map(|instance| {
let instance_config = &instance.instance_config;
let instance = format_ident!("{}", instance.name.to_uppercase());
let sys = format_ident!("{}", instance_config.sys_instance);
let sclk = format_ident!("{}", instance_config.sclk);
let cs = instance_config.cs.iter().map(|cs| format_ident!("{cs}"));
let sio = instance_config.sio.iter().map(|cs| format_ident!("{cs}"));
let is_qspi = if instance_config.sio.len() > 2 {
quote::quote! { , true }
} else {
quote::quote! {}
};
// The order and meaning of these tokens must match their use in the
// `for_each_i2c_master!` call.
quote::quote! {
#instance, #sys, #sclk [#(#cs),*] [#(#sio),*] #is_qspi
}
})
.collect::<Vec<_>>();
generate_for_each_macro("spi_master", &instance_cfgs)
}

View File

@ -0,0 +1,45 @@
use proc_macro2::TokenStream;
use quote::format_ident;
use crate::{cfg::SpiSlaveProperties, generate_for_each_macro};
/// Instance configuration, used in [device.spi_slave.instances]
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
pub(crate) struct SpiSlaveInstanceConfig {
/// The name of the instance in the `esp_hal::system::Peripheral` enum
pub sys_instance: String,
// IOMUX signal names of the instance's signals:
pub sclk: String,
pub mosi: String,
pub miso: String,
pub cs: String,
}
/// Generates `for_each_spi_slave!` which can be used to implement the SPI
/// slave Instance trait for the relevant peripherals. The macro generates code
/// for each [device.spi_slave.instances[X]] instance.
pub(crate) fn generate_spi_slave_peripherals(spi_slave: &SpiSlaveProperties) -> TokenStream {
let instance_cfgs = spi_slave
.instances
.iter()
.map(|instance| {
let instance_config = &instance.instance_config;
let instance = format_ident!("{}", instance.name.to_uppercase());
let sys = format_ident!("{}", instance_config.sys_instance);
let sclk = format_ident!("{}", instance_config.sclk);
let mosi = format_ident!("{}", instance_config.mosi);
let miso = format_ident!("{}", instance_config.miso);
let cs = format_ident!("{}", instance_config.cs);
// The order and meaning of these tokens must match their use in the
// `for_each_i2c_master!` call.
quote::quote! {
#instance, #sys, #sclk, #mosi, #miso, #cs
}
})
.collect::<Vec<_>>();
generate_for_each_macro("spi_slave", &instance_cfgs)
}

View File

@ -479,11 +479,17 @@ impl Config {
let mut tokens = TokenStream::new();
// TODO: repeat for all drivers that have Instance traits
if let Some(i2c) = self.device.peri_config.i2c_master.as_ref() {
tokens.extend(cfg::generate_i2c_master_peripherals(i2c));
if let Some(peri) = self.device.peri_config.i2c_master.as_ref() {
tokens.extend(cfg::generate_i2c_master_peripherals(peri));
};
if let Some(uart) = self.device.peri_config.uart.as_ref() {
tokens.extend(cfg::generate_uart_peripherals(uart));
if let Some(peri) = self.device.peri_config.uart.as_ref() {
tokens.extend(cfg::generate_uart_peripherals(peri));
}
if let Some(peri) = self.device.peri_config.spi_master.as_ref() {
tokens.extend(cfg::generate_spi_master_peripherals(peri));
};
if let Some(peri) = self.device.peri_config.spi_slave.as_ref() {
tokens.extend(cfg::generate_spi_slave_peripherals(peri));
};
save(out_dir.join(file_name), tokens);