From a754e411b190f43d9ddcb8be8cfe57a3d3061b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 21 Oct 2024 16:03:14 +0200 Subject: [PATCH] SPI type erasure (#2334) * Move SPI peripheral type to the last position * Implement AnySpi * Convert peripheral types * Add new_typed constructors * Implement PDMA * Fix conditional * Clean up constructors * Fix test * Move stuff to utils * Extract any macros * Merge PeripheralMarker defs into peripherals macro * Changelogs * Implement fn degrade * Changelog num * Fix typo * Rename type-erased dma channel * Remove degrade fn * Remove utils * Explain peripherals macro --- esp-hal/CHANGELOG.md | 3 + esp-hal/MIGRATING-0.21.md | 23 ++ esp-hal/src/dma/pdma.rs | 45 +++ esp-hal/src/i2s.rs | 13 - esp-hal/src/lcd_cam/mod.rs | 8 +- esp-hal/src/lib.rs | 23 +- esp-hal/src/macros.rs | 115 ++++++ esp-hal/src/peripheral.rs | 74 ++-- esp-hal/src/soc/esp32/peripherals.rs | 8 +- esp-hal/src/soc/esp32c2/peripherals.rs | 2 +- esp-hal/src/soc/esp32c3/peripherals.rs | 4 +- esp-hal/src/soc/esp32c6/peripherals.rs | 4 +- esp-hal/src/soc/esp32h2/peripherals.rs | 4 +- esp-hal/src/soc/esp32s2/peripherals.rs | 7 +- esp-hal/src/soc/esp32s3/peripherals.rs | 10 +- esp-hal/src/spi/master.rs | 351 ++++++++++++------ esp-hal/src/spi/mod.rs | 32 +- esp-hal/src/spi/slave.rs | 106 ++++-- hil-test/tests/embassy_interrupt_spi_dma.rs | 3 +- hil-test/tests/qspi.rs | 2 +- hil-test/tests/spi_full_duplex.rs | 3 +- hil-test/tests/spi_half_duplex_read.rs | 3 +- hil-test/tests/spi_half_duplex_write.rs | 3 +- hil-test/tests/spi_half_duplex_write_psram.rs | 3 +- hil-test/tests/spi_slave.rs | 3 +- 25 files changed, 573 insertions(+), 279 deletions(-) create mode 100644 esp-hal/src/macros.rs diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 4cc717c9c..b620a9a49 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -13,10 +13,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A new config option `PLACE_ANON_IN_RAM` to improve performance (especially for interrupts) at the cost of RAM usage (#2331) - Add burst transfer support to DMA buffers (#2336) - `AnyPin` now implements `From>`. (#2326) +- Added `AnySpi` and `AnySpiDmaChannel`. (#2334) - `Pins::steal()` to unsafely obtain GPIO. (#2335) ### Changed +- Peripheral type erasure for SPI (#2334) + ### Fixed ### Removed diff --git a/esp-hal/MIGRATING-0.21.md b/esp-hal/MIGRATING-0.21.md index 2421b6b95..5eeb638de 100644 --- a/esp-hal/MIGRATING-0.21.md +++ b/esp-hal/MIGRATING-0.21.md @@ -22,3 +22,26 @@ For example: // ... } ``` + +## Peripheral types are now optional + +You no longer have to specify the peripheral instance in the driver's type for the following +peripherals: + +- SPI (both master and slave) + +```diff +-Spi<'static, SPI2, FullDuplexMode> ++Spi<'static, FullDuplexMode> + +-SpiDma<'static, SPI2, HalfDuplexMode, Blocking> ++SpiDma<'static, HalfDuplexMode, Blocking> +``` + +Note that you may still specify the instance if you need to. To do this, we provide `_typed` +versions of the constructors (for example: `new_typed`, `new_half_duplex_typed`). Please note that +the peripheral instance has been moved to the last generic parameter position. + +```rust +let spi: Spi<'static, FullDuplexMode, SPI2> = Spi::new_typed(peripherals.SPI2, 1.MHz(), SpiMode::Mode0); +``` diff --git a/esp-hal/src/dma/pdma.rs b/esp-hal/src/dma/pdma.rs index 6399826e3..6a5b50201 100644 --- a/esp-hal/src/dma/pdma.rs +++ b/esp-hal/src/dma/pdma.rs @@ -366,6 +366,15 @@ macro_rules! ImplSpiChannel { } } + impl DmaChannelConvert for [] { + fn degrade_rx(rx: SpiDmaRxChannelImpl) -> SpiDmaRxChannelImpl { + SpiDmaRxChannelImpl(rx.0.into()) + } + fn degrade_tx(tx: SpiDmaTxChannelImpl) -> SpiDmaTxChannelImpl { + SpiDmaTxChannelImpl(tx.0.into()) + } + } + impl $crate::private::Sealed for [] {} #[doc = concat!("Creates a channel for SPI", $num)] @@ -897,3 +906,39 @@ where ); } } + +/// A marker for SPI-compatible type-erased DMA channels. +pub struct AnySpiDmaChannel; + +impl crate::private::Sealed for AnySpiDmaChannel {} + +impl DmaChannel for AnySpiDmaChannel { + type Rx = SpiDmaRxChannelImpl; + type Tx = SpiDmaTxChannelImpl; +} + +crate::any_enum! { + #[doc(hidden)] + pub enum AnySpiDmaChannelInner { + Spi2(Spi2DmaChannel), + Spi3(Spi3DmaChannel), + } +} + +impl crate::private::Sealed for AnySpiDmaChannelInner {} + +impl PdmaChannel for AnySpiDmaChannelInner { + type RegisterBlock = SpiRegisterBlock; + + delegate::delegate! { + to match self { + AnySpiDmaChannelInner::Spi2(channel) => channel, + AnySpiDmaChannelInner::Spi3(channel) => channel, + } { + fn register_block(&self) -> &SpiRegisterBlock; + fn tx_waker(&self) -> &'static AtomicWaker; + fn rx_waker(&self) -> &'static AtomicWaker; + fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool; + } + } +} diff --git a/esp-hal/src/i2s.rs b/esp-hal/src/i2s.rs index 7ca4da148..70bfe8cd9 100644 --- a/esp-hal/src/i2s.rs +++ b/esp-hal/src/i2s.rs @@ -1545,12 +1545,6 @@ mod private { } } - impl PeripheralMarker for I2S0 { - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::I2s0 - } - } - impl RegBlock for I2S0 { fn register_block(&self) -> &RegisterBlock { unsafe { &*I2S0::PTR.cast::() } @@ -1650,13 +1644,6 @@ mod private { } } - #[cfg(i2s1)] - impl PeripheralMarker for I2S1 { - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::I2s1 - } - } - #[cfg(i2s1)] impl RegBlock for I2S1 { fn register_block(&self) -> &RegisterBlock { diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index a1f55b7e1..38a29f83b 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -136,13 +136,7 @@ pub mod asynch { } mod private { - use crate::{dma::PeripheralMarker, peripherals::LCD_CAM}; - - impl PeripheralMarker for LCD_CAM { - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::LcdCam - } - } + use crate::peripherals::LCD_CAM; pub(crate) struct Instance; diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index df92a6d6f..4a2df7a91 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -142,7 +142,6 @@ mod fmt; #[cfg(riscv)] pub use esp_riscv_rt::{self, entry, riscv}; -pub use procmacros as macros; #[cfg(xtensa)] pub use xtensa_lx; #[cfg(xtensa)] @@ -241,6 +240,8 @@ pub mod debugger; #[doc(hidden)] pub mod sync; +pub mod macros; + /// State of the CPU saved when entering exception or interrupt pub mod trapframe { #[cfg(riscv)] @@ -449,26 +450,6 @@ unsafe extern "C" fn stack_chk_fail() { panic!("Stack corruption detected"); } -#[doc(hidden)] -/// Helper macro for checking doctest code snippets -#[macro_export] -macro_rules! before_snippet { - () => { - r#" -# #![no_std] -# use esp_hal::prelude::*; -# use procmacros::handler; -# use esp_hal::interrupt; -# #[panic_handler] -# fn panic(_ : &core::panic::PanicInfo) -> ! { -# loop {} -# } -# fn main() { -# let mut peripherals = esp_hal::init(esp_hal::Config::default()); -"# - }; -} - use crate::{ clock::{Clocks, CpuClock}, config::{WatchdogConfig, WatchdogStatus}, diff --git a/esp-hal/src/macros.rs b/esp-hal/src/macros.rs new file mode 100644 index 000000000..4f2fa8748 --- /dev/null +++ b/esp-hal/src/macros.rs @@ -0,0 +1,115 @@ +//! Macros used by the HAL. +//! +//! Most of the macros in this module are hidden and intended for internal use +//! only. For the list of public macros, see the [procmacros](https://docs.rs/esp-hal-procmacros/latest/esp_hal_procmacros/) +//! documentation. + +pub use procmacros::*; + +#[doc(hidden)] +/// Helper macro for checking doctest code snippets +#[macro_export] +macro_rules! before_snippet { + () => { + r#" +# #![no_std] +# use esp_hal::prelude::*; +# use procmacros::handler; +# use esp_hal::interrupt; +# #[panic_handler] +# fn panic(_ : &core::panic::PanicInfo) -> ! { +# loop {} +# } +# fn main() { +# let mut peripherals = esp_hal::init(esp_hal::Config::default()); +"# + }; +} + +#[doc(hidden)] +/// Shorthand to define enums with From implementations. +#[macro_export] +macro_rules! any_enum { + ($(#[$meta:meta])* $vis:vis enum $name:ident { + $( + $(#[$variant_meta:meta])* + $variant:ident($inner:ty) + ),* $(,)? + }) => { + $(#[$meta])* + $vis enum $name { + $( + $(#[$variant_meta])* + $variant($inner), + )* + } + + $( + $(#[$variant_meta])* + impl From<$inner> for $name { + fn from(inner: $inner) -> Self { + $name::$variant(inner) + } + } + )* + }; +} + +#[doc(hidden)] +/// Shorthand to define AnyPeripheral instances. +#[macro_export] +macro_rules! any_peripheral { + ($(#[$meta:meta])* $vis:vis peripheral $name:ident { + $( + $(#[cfg($variant_meta:meta)])* + $variant:ident($inner:ty) + ),* $(,)? + }) => { + paste::paste! { + $(#[$meta])* + $vis struct $name([< $name Inner >]); + impl $crate::private::Sealed for $name {} + + impl $crate::dma::PeripheralMarker for $name { + #[inline(always)] + fn peripheral(&self) -> $crate::system::Peripheral { + match &self.0 { + $( + $(#[cfg($variant_meta)])* + [<$name Inner>]::$variant(inner) => inner.peripheral(), + )* + } + } + } + + impl $crate::peripheral::Peripheral for $name { + type P = $name; + + unsafe fn clone_unchecked(&self) -> Self::P { + match &self.0 { + $( + $(#[cfg($variant_meta)])* + [<$name Inner>]::$variant(inner) => $name::from(inner.clone_unchecked()), + )* + } + } + } + + enum [< $name Inner >] { + $( + $(#[cfg($variant_meta)])* + $variant($inner), + )* + } + + $( + $(#[cfg($variant_meta)])* + impl From<$inner> for $name { + fn from(inner: $inner) -> Self { + Self([< $name Inner >]::$variant(inner)) + } + } + )* + } + }; +} diff --git a/esp-hal/src/peripheral.rs b/esp-hal/src/peripheral.rs index 7075f0f9b..7d5b665e2 100644 --- a/esp-hal/src/peripheral.rs +++ b/esp-hal/src/peripheral.rs @@ -207,16 +207,34 @@ impl Peripheral for PeripheralRef<'_, T> { } mod peripheral_macros { + /// Creates a new `Peripherals` struct and its associated methods. + /// + /// The macro has a few fields doing different things, in the form of + /// `[first] second <= third (fourth)`. + /// - The first field is the name of the `Peripherals` enum variant. This is + /// optional and used to create `PeripheralMarker` implementations for + /// DMA-eligible peripherals. + /// - The second field is the name of the peripheral, as it appears in the + /// `Peripherals` struct. + /// - The third field is the name of the peripheral as it appears in the + /// PAC. This may be `virtual` if the peripheral is not present in the + /// PAC. + /// - The fourth field is an optional list of interrupts that can be bound + /// to the peripheral. #[doc(hidden)] #[macro_export] macro_rules! peripherals { - ($($(#[$cfg:meta])? $name:ident <= $from_pac:tt $(($($interrupt:ident),*))? ),*$(,)?) => { + ( + $( + $([$enum_variant:ident])? $name:ident <= $from_pac:tt $(($($interrupt:ident),*))? + ), *$(,)? + ) => { /// Contains the generated peripherals which implement [`Peripheral`] mod peripherals { pub use super::pac::*; $( - $crate::create_peripheral!($(#[$cfg])? $name <= $from_pac); + $crate::create_peripheral!($([$enum_variant])? $name <= $from_pac); )* } @@ -224,7 +242,6 @@ mod peripheral_macros { #[allow(non_snake_case)] pub struct Peripherals { $( - $(#[$cfg])? /// Each field represents a hardware peripheral. pub $name: peripherals::$name, )* @@ -245,9 +262,7 @@ mod peripheral_macros { Self::steal() }) } - } - impl Peripherals { /// Unsafely create an instance of this peripheral out of thin air. /// /// # Safety @@ -257,7 +272,6 @@ mod peripheral_macros { pub unsafe fn steal() -> Self { Self { $( - $(#[$cfg])? $name: peripherals::$name::steal(), )* } @@ -313,8 +327,7 @@ mod peripheral_macros { #[macro_export] /// Macro to create a peripheral structure. macro_rules! create_peripheral { - ($(#[$cfg:meta])? $name:ident <= virtual) => { - $(#[$cfg])? + ($([$enum_variant:ident])? $name:ident <= virtual) => { #[derive(Debug)] #[allow(non_camel_case_types)] /// Represents a virtual peripheral with no associated hardware. @@ -323,7 +336,6 @@ mod peripheral_macros { /// is defined as virtual. pub struct $name { _inner: () } - $(#[$cfg])? impl $name { /// Unsafely create an instance of this peripheral out of thin air. /// @@ -346,29 +358,21 @@ mod peripheral_macros { } impl $crate::private::Sealed for $name {} - }; - ($(#[$cfg:meta])? $name:ident <= $base:ident) => { - $(#[$cfg])? - #[derive(Debug)] - #[allow(non_camel_case_types)] - /// Represents a concrete hardware peripheral. - /// - /// This struct is generated by the `create_peripheral!` macro when the peripheral - /// is tied to an actual hardware device. - pub struct $name { _inner: () } - $(#[$cfg])? - impl $name { - /// Unsafely create an instance of this peripheral out of thin air. - /// - /// # Safety - /// - /// You must ensure that you're only using one instance of this type at a time. - #[inline] - pub unsafe fn steal() -> Self { - Self { _inner: () } + $( + impl $crate::dma::PeripheralMarker for $crate::peripherals::$name { + #[inline(always)] + fn peripheral(&self) -> $crate::system::Peripheral { + $crate::system::Peripheral::$enum_variant + } } + )? + }; + ($([$enum_variant:ident])? $name:ident <= $base:ident) => { + $crate::create_peripheral!($([$enum_variant])? $name <= virtual); + + impl $name { #[doc = r"Pointer to the register block"] pub const PTR: *const ::Target = super::pac::$base::PTR; @@ -388,22 +392,10 @@ mod peripheral_macros { } impl core::ops::DerefMut for $name { - fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *(Self::PTR as *mut _) } } } - - impl $crate::peripheral::Peripheral for $name { - type P = $name; - - #[inline] - unsafe fn clone_unchecked(&self) -> Self::P { - Self::steal() - } - } - - impl $crate::private::Sealed for $name {} }; } } diff --git a/esp-hal/src/soc/esp32/peripherals.rs b/esp-hal/src/soc/esp32/peripherals.rs index ab9b31ce6..6216b55bf 100644 --- a/esp-hal/src/soc/esp32/peripherals.rs +++ b/esp-hal/src/soc/esp32/peripherals.rs @@ -38,8 +38,8 @@ crate::peripherals! { HINF <= HINF, I2C0 <= I2C0, I2C1 <= I2C1, - I2S0 <= I2S0 (I2S0), - I2S1 <= I2S1 (I2S1), + [I2s0] I2S0 <= I2S0 (I2S0), + [I2s1] I2S1 <= I2S1 (I2S1), IO_MUX <= IO_MUX, LEDC <= LEDC, MCPWM0 <= MCPWM0, @@ -60,8 +60,8 @@ crate::peripherals! { SLCHOST <= SLCHOST, SPI0 <= SPI0, SPI1 <= SPI1, - SPI2 <= SPI2 (SPI2_DMA, SPI2), - SPI3 <= SPI3 (SPI3_DMA, SPI3), + [Spi2] SPI2 <= SPI2 (SPI2_DMA, SPI2), + [Spi3] SPI3 <= SPI3 (SPI3_DMA, SPI3), SYSTEM <= DPORT, SW_INTERRUPT <= virtual, TIMG0 <= TIMG0, diff --git a/esp-hal/src/soc/esp32c2/peripherals.rs b/esp-hal/src/soc/esp32c2/peripherals.rs index 3cd86959d..d6de7f899 100644 --- a/esp-hal/src/soc/esp32c2/peripherals.rs +++ b/esp-hal/src/soc/esp32c2/peripherals.rs @@ -40,7 +40,7 @@ crate::peripherals! { SHA <= SHA, SPI0 <= SPI0, SPI1 <= SPI1, - SPI2 <= SPI2 (SPI2), + [Spi2] SPI2 <= SPI2 (SPI2), SYSTEM <= SYSTEM, SYSTIMER <= SYSTIMER, SW_INTERRUPT <= virtual, diff --git a/esp-hal/src/soc/esp32c3/peripherals.rs b/esp-hal/src/soc/esp32c3/peripherals.rs index dd5308df0..5f5487711 100644 --- a/esp-hal/src/soc/esp32c3/peripherals.rs +++ b/esp-hal/src/soc/esp32c3/peripherals.rs @@ -34,7 +34,7 @@ crate::peripherals! { GPIO_SD <= GPIO_SD, HMAC <= HMAC, I2C0 <= I2C0, - I2S0 <= I2S0 (I2S0), + [I2s0] I2S0 <= I2S0 (I2S0), INTERRUPT_CORE0 <= INTERRUPT_CORE0, IO_MUX <= IO_MUX, LEDC <= LEDC, @@ -47,7 +47,7 @@ crate::peripherals! { SHA <= SHA, SPI0 <= SPI0, SPI1 <= SPI1, - SPI2 <= SPI2 (SPI2), + [Spi2] SPI2 <= SPI2 (SPI2), SYSTEM <= SYSTEM, SYSTIMER <= SYSTIMER, SW_INTERRUPT <= virtual, diff --git a/esp-hal/src/soc/esp32c6/peripherals.rs b/esp-hal/src/soc/esp32c6/peripherals.rs index ef74459ed..dd44390e0 100644 --- a/esp-hal/src/soc/esp32c6/peripherals.rs +++ b/esp-hal/src/soc/esp32c6/peripherals.rs @@ -37,7 +37,7 @@ crate::peripherals! { HP_APM <= HP_APM, HP_SYS <= HP_SYS, I2C0 <= I2C0, - I2S0 <= I2S0 (I2S0), + [I2s0] I2S0 <= I2S0 (I2S0), IEEE802154 <= IEEE802154, INTERRUPT_CORE0 <= INTERRUPT_CORE0, INTPRI <= INTPRI, @@ -73,7 +73,7 @@ crate::peripherals! { SOC_ETM <= SOC_ETM, SPI0 <= SPI0, SPI1 <= SPI1, - SPI2 <= SPI2 (SPI2), + [Spi2] SPI2 <= SPI2 (SPI2), SYSTEM <= PCR, SYSTIMER <= SYSTIMER, SW_INTERRUPT <= virtual, diff --git a/esp-hal/src/soc/esp32h2/peripherals.rs b/esp-hal/src/soc/esp32h2/peripherals.rs index 7fbda39c9..52a8aac08 100644 --- a/esp-hal/src/soc/esp32h2/peripherals.rs +++ b/esp-hal/src/soc/esp32h2/peripherals.rs @@ -35,7 +35,7 @@ crate::peripherals! { HP_SYS <= HP_SYS, I2C0 <= I2C0, I2C1 <= I2C1, - I2S0 <= I2S0 (I2S0), + [I2s0] I2S0 <= I2S0 (I2S0), IEEE802154 <= IEEE802154, INTERRUPT_CORE0 <= INTERRUPT_CORE0, INTPRI <= INTPRI, @@ -65,7 +65,7 @@ crate::peripherals! { SOC_ETM <= SOC_ETM, SPI0 <= SPI0, SPI1 <= SPI1, - SPI2 <= SPI2 (SPI2), + [Spi2] SPI2 <= SPI2 (SPI2), SYSTEM <= PCR, SYSTIMER <= SYSTIMER, SW_INTERRUPT <= virtual, diff --git a/esp-hal/src/soc/esp32s2/peripherals.rs b/esp-hal/src/soc/esp32s2/peripherals.rs index 31d989cf2..c1a105da1 100644 --- a/esp-hal/src/soc/esp32s2/peripherals.rs +++ b/esp-hal/src/soc/esp32s2/peripherals.rs @@ -35,7 +35,7 @@ crate::peripherals! { HMAC <= HMAC, I2C0 <= I2C0, I2C1 <= I2C1, - I2S0 <= I2S0 (I2S0), + [I2s0] I2S0 <= I2S0 (I2S0), INTERRUPT_CORE0 <= INTERRUPT_CORE0, IO_MUX <= IO_MUX, LEDC <= LEDC, @@ -52,9 +52,8 @@ crate::peripherals! { SHA <= SHA, SPI0 <= SPI0, SPI1 <= SPI1, - SPI2 <= SPI2 (SPI2_DMA, SPI2), - SPI3 <= SPI3 (SPI3_DMA, SPI3), - SPI4 <= SPI4, + [Spi2] SPI2 <= SPI2 (SPI2_DMA, SPI2), + [Spi3] SPI3 <= SPI3 (SPI3_DMA, SPI3), SYSCON <= SYSCON, SYSTEM <= SYSTEM, SYSTIMER <= SYSTIMER, diff --git a/esp-hal/src/soc/esp32s3/peripherals.rs b/esp-hal/src/soc/esp32s3/peripherals.rs index 9e0592abf..9a1a0a0c2 100644 --- a/esp-hal/src/soc/esp32s3/peripherals.rs +++ b/esp-hal/src/soc/esp32s3/peripherals.rs @@ -36,12 +36,12 @@ crate::peripherals! { HMAC <= HMAC, I2C0 <= I2C0, I2C1 <= I2C1, - I2S0 <= I2S0 (I2S0), - I2S1 <= I2S1 (I2S1), + [I2s0] I2S0 <= I2S0 (I2S0), + [I2s1] I2S1 <= I2S1 (I2S1), INTERRUPT_CORE0 <= INTERRUPT_CORE0, INTERRUPT_CORE1 <= INTERRUPT_CORE1, IO_MUX <= IO_MUX, - LCD_CAM <= LCD_CAM, + [LcdCam] LCD_CAM <= LCD_CAM, LEDC <= LEDC, LPWR <= RTC_CNTL, PCNT <= PCNT, @@ -59,8 +59,8 @@ crate::peripherals! { SHA <= SHA, SPI0 <= SPI0, SPI1 <= SPI1, - SPI2 <= SPI2 (SPI2), - SPI3 <= SPI3 (SPI3), + [Spi2] SPI2 <= SPI2 (SPI2), + [Spi3] SPI3 <= SPI3 (SPI3), SYSTEM <= SYSTEM, SYSTIMER <= SYSTIMER, SW_INTERRUPT <= virtual, diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index ad5aa335b..40cd66771 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -87,6 +87,7 @@ use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::spi2::RegisterBlock, private, + spi::AnySpi, system::PeripheralClockControl, Mode, }; @@ -452,15 +453,35 @@ pub trait HalfDuplexReadWrite { } /// SPI peripheral driver -pub struct Spi<'d, T, M> { +pub struct Spi<'d, M, T = AnySpi> { spi: PeripheralRef<'d, T>, _mode: PhantomData, } -impl<'d, T, M> Spi<'d, T, M> +impl<'d, M, T> Spi<'d, M, T> where T: Instance, { + fn new_internal( + spi: impl Peripheral

+ 'd> + 'd, + frequency: HertzU32, + mode: SpiMode, + ) -> Spi<'d, M, T> { + crate::into_ref!(spi); + + let mut spi = Spi { + spi: spi.map_into(), + _mode: PhantomData, + }; + spi.spi.reset_peripheral(); + spi.spi.enable_peripheral(); + spi.spi.setup(frequency); + spi.spi.init(); + spi.spi.set_data_mode(mode); + + spi + } + /// Assign the SCK (Serial Clock) pin for the SPI instance. /// /// Sets the specified pin to push-pull output and connects it to the SPI @@ -486,7 +507,7 @@ where } } -impl<'d, T, M> Spi<'d, T, M> +impl<'d, M, T> Spi<'d, M, T> where T: InstanceDma, M: DuplexMode, @@ -499,7 +520,7 @@ where pub fn with_dma( self, channel: crate::dma::Channel<'d, CH, DmaMode>, - ) -> SpiDma<'d, T, M, DmaMode> + ) -> SpiDma<'d, M, DmaMode, T> where CH: DmaChannelConvert, DmaMode: Mode, @@ -508,7 +529,7 @@ where } } -impl<'d, T> Spi<'d, T, FullDuplexMode> +impl<'d, T> Spi<'d, FullDuplexMode, T> where T: Instance, { @@ -544,7 +565,21 @@ where } } -impl<'d, T> Spi<'d, T, FullDuplexMode> +impl<'d> Spi<'d, FullDuplexMode> { + /// Constructs an SPI instance in 8bit dataframe mode. + /// + /// All pins are optional. Setup these pins using + /// [with_pins](Self::with_pins) or individual methods for each pin. + pub fn new( + spi: impl Peripheral

+ 'd> + 'd, + frequency: HertzU32, + mode: SpiMode, + ) -> Spi<'d, FullDuplexMode> { + Self::new_typed(spi, frequency, mode) + } +} + +impl<'d, T> Spi<'d, FullDuplexMode, T> where T: Instance, { @@ -552,13 +587,15 @@ where /// /// All pins are optional. Setup these pins using /// [with_pins](Self::with_pins) or individual methods for each pin. - pub fn new( - spi: impl Peripheral

+ 'd, + pub fn new_typed( + spi: impl Peripheral

+ 'd> + 'd, frequency: HertzU32, mode: SpiMode, - ) -> Spi<'d, T, FullDuplexMode> { - crate::into_ref!(spi); - Self::new_internal(spi, frequency, mode) + ) -> Spi<'d, FullDuplexMode, T> { + let spi = Spi::::new_internal(spi, frequency, mode); + + // Disconnect any lingering connections + spi.with_pins(NoPin, NoPin, NoPin, NoPin) } /// Assign the MOSI (Master Out Slave In) pin for the SPI instance. @@ -614,26 +651,6 @@ where .with_cs(cs) } - pub(crate) fn new_internal( - spi: PeripheralRef<'d, T>, - frequency: HertzU32, - mode: SpiMode, - ) -> Spi<'d, T, FullDuplexMode> { - spi.reset_peripheral(); - spi.enable_peripheral(); - - let mut spi = Spi { - spi, - _mode: PhantomData, - }; - spi.spi.setup(frequency); - spi.spi.init(); - spi.spi.set_data_mode(mode); - - // Disconnect any lingering connections - spi.with_pins(NoPin, NoPin, NoPin, NoPin) - } - /// Change the bus frequency of the SPI instance. /// /// This method allows user to update the bus frequency for the SPI @@ -643,7 +660,21 @@ where } } -impl<'d, T> Spi<'d, T, HalfDuplexMode> +impl<'d> Spi<'d, HalfDuplexMode> { + /// Constructs an SPI instance in half-duplex mode. + /// + /// All pins are optional. Setup these pins using + /// [with_pins](Self::with_pins) or individual methods for each pin. + pub fn new_half_duplex( + spi: impl Peripheral

+ 'd> + 'd, + frequency: HertzU32, + mode: SpiMode, + ) -> Spi<'d, HalfDuplexMode> { + Self::new_half_duplex_typed(spi, frequency, mode) + } +} + +impl<'d, T> Spi<'d, HalfDuplexMode, T> where T: ExtendedInstance, { @@ -651,13 +682,15 @@ where /// /// All pins are optional. Setup these pins using /// [with_pins](Self::with_pins) or individual methods for each pin. - pub fn new_half_duplex( - spi: impl Peripheral

+ 'd, + pub fn new_half_duplex_typed( + spi: impl Peripheral

+ 'd> + 'd, frequency: HertzU32, mode: SpiMode, - ) -> Spi<'d, T, HalfDuplexMode> { - crate::into_ref!(spi); - Self::new_internal(spi, frequency, mode) + ) -> Spi<'d, HalfDuplexMode, T> { + let spi = Spi::::new_internal(spi, frequency, mode); + + // Disconnect any lingering connections + spi.with_pins(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin) } /// Assign the MOSI (Master Out Slave In) pin for the SPI instance in @@ -761,27 +794,6 @@ where .with_sio3(sio3) .with_cs(cs) } - - pub(crate) fn new_internal( - spi: PeripheralRef<'d, T>, - frequency: HertzU32, - mode: SpiMode, - ) -> Spi<'d, T, HalfDuplexMode> { - spi.reset_peripheral(); - spi.enable_peripheral(); - - let mut spi = Spi { - spi, - _mode: PhantomData::, - }; - spi.spi.setup(frequency); - spi.spi.init(); - spi.spi.set_data_mode(mode); - - // Disconnect any lingering connections - spi.with_pins(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin) - } - /// Change the bus frequency of the SPI instance in half-duplex mode. /// /// This method allows you to update the bus frequency for the SPI @@ -799,7 +811,7 @@ where } } -impl HalfDuplexReadWrite for Spi<'_, T, HalfDuplexMode> +impl HalfDuplexReadWrite for Spi<'_, HalfDuplexMode, T> where T: Instance, { @@ -890,7 +902,7 @@ where } } -impl embedded_hal_02::spi::FullDuplex for Spi<'_, T, FullDuplexMode> +impl embedded_hal_02::spi::FullDuplex for Spi<'_, FullDuplexMode, T> where T: Instance, { @@ -905,7 +917,7 @@ where } } -impl embedded_hal_02::blocking::spi::Transfer for Spi<'_, T, FullDuplexMode> +impl embedded_hal_02::blocking::spi::Transfer for Spi<'_, FullDuplexMode, T> where T: Instance, { @@ -916,7 +928,7 @@ where } } -impl embedded_hal_02::blocking::spi::Write for Spi<'_, T, FullDuplexMode> +impl embedded_hal_02::blocking::spi::Write for Spi<'_, FullDuplexMode, T> where T: Instance, { @@ -959,7 +971,7 @@ mod dma { /// [`SpiDmaBus`] via `with_buffers` to get access /// to a DMA capable SPI bus that implements the /// embedded-hal traits. - pub struct SpiDma<'d, T, D, M> + pub struct SpiDma<'d, D, M, T = AnySpi> where T: InstanceDma, D: DuplexMode, @@ -975,7 +987,7 @@ mod dma { } #[cfg(all(esp32, spi_address_workaround))] - unsafe impl<'d, T, D, M> Send for SpiDma<'d, T, D, M> + unsafe impl<'d, D, M, T> Send for SpiDma<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -983,7 +995,7 @@ mod dma { { } - impl<'d, T, D, M> core::fmt::Debug for SpiDma<'d, T, D, M> + impl<'d, D, M, T> core::fmt::Debug for SpiDma<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -998,7 +1010,7 @@ mod dma { } } - impl<'d, T, D, M> SpiDma<'d, T, D, M> + impl<'d, D, M, T> SpiDma<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -1206,7 +1218,7 @@ mod dma { } } - impl<'d, T, D, M> crate::private::Sealed for SpiDma<'d, T, D, M> + impl<'d, D, M, T> crate::private::Sealed for SpiDma<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -1214,7 +1226,7 @@ mod dma { { } - impl<'d, T, D, M> InterruptConfigurable for SpiDma<'d, T, D, M> + impl<'d, D, M, T> InterruptConfigurable for SpiDma<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -1226,7 +1238,7 @@ mod dma { } } - impl<'d, T, D, M> SpiDma<'d, T, D, M> + impl<'d, D, M, T> SpiDma<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -1246,7 +1258,7 @@ mod dma { self, dma_rx_buf: DmaRxBuf, dma_tx_buf: DmaTxBuf, - ) -> SpiDmaBus<'d, T, D, M> { + ) -> SpiDmaBus<'d, D, M, T> { SpiDmaBus::new(self, dma_rx_buf, dma_tx_buf) } } @@ -1255,23 +1267,23 @@ mod dma { /// /// This structure holds references to the SPI instance, DMA buffers, and /// transfer status. - pub struct SpiDmaTransfer<'d, T, D, M, Buf> + pub struct SpiDmaTransfer<'d, D, M, Buf, T = AnySpi> where T: InstanceDma, D: DuplexMode, M: Mode, { - spi_dma: ManuallyDrop>, + spi_dma: ManuallyDrop>, dma_buf: ManuallyDrop, } - impl<'d, T, D, M, Buf> SpiDmaTransfer<'d, T, D, M, Buf> + impl<'d, D, M, T, Buf> SpiDmaTransfer<'d, D, M, Buf, T> where T: InstanceDma, D: DuplexMode, M: Mode, { - fn new(spi_dma: SpiDma<'d, T, D, M>, dma_buf: Buf) -> Self { + fn new(spi_dma: SpiDma<'d, D, M, T>, dma_buf: Buf) -> Self { Self { spi_dma: ManuallyDrop::new(spi_dma), dma_buf: ManuallyDrop::new(dma_buf), @@ -1290,7 +1302,7 @@ mod dma { /// /// This method blocks until the transfer is finished and returns the /// `SpiDma` instance and the associated buffer. - pub fn wait(mut self) -> (SpiDma<'d, T, D, M>, Buf) { + pub fn wait(mut self) -> (SpiDma<'d, D, M, T>, Buf) { self.spi_dma.wait_for_idle(); let retval = unsafe { ( @@ -1310,7 +1322,7 @@ mod dma { } } - impl<'d, T, D, M, Buf> Drop for SpiDmaTransfer<'d, T, D, M, Buf> + impl<'d, D, M, T, Buf> Drop for SpiDmaTransfer<'d, D, M, Buf, T> where T: InstanceDma, D: DuplexMode, @@ -1329,7 +1341,7 @@ mod dma { } } - impl<'d, T, D, Buf> SpiDmaTransfer<'d, T, D, crate::Async, Buf> + impl<'d, T, D, Buf> SpiDmaTransfer<'d, D, crate::Async, Buf, T> where T: InstanceDma, D: DuplexMode, @@ -1342,7 +1354,7 @@ mod dma { } } - impl<'d, T, M> SpiDma<'d, T, FullDuplexMode, M> + impl<'d, M, T> SpiDma<'d, FullDuplexMode, M, T> where T: InstanceDma, M: Mode, @@ -1377,7 +1389,7 @@ mod dma { pub fn dma_write( mut self, mut buffer: TX, - ) -> Result, (Error, Self, TX)> { + ) -> Result, (Error, Self, TX)> { self.wait_for_idle(); match unsafe { self.start_dma_write(&mut buffer) } { @@ -1416,7 +1428,7 @@ mod dma { pub fn dma_read( mut self, mut buffer: RX, - ) -> Result, (Error, Self, RX)> { + ) -> Result, (Error, Self, RX)> { self.wait_for_idle(); match unsafe { self.start_dma_read(&mut buffer) } { Ok(_) => Ok(SpiDmaTransfer::new(self, buffer)), @@ -1455,7 +1467,7 @@ mod dma { mut self, mut rx_buffer: RX, mut tx_buffer: TX, - ) -> Result, (Error, Self, RX, TX)> + ) -> Result, (Error, Self, RX, TX)> { self.wait_for_idle(); match unsafe { self.start_dma_transfer(&mut rx_buffer, &mut tx_buffer) } { @@ -1465,7 +1477,7 @@ mod dma { } } - impl<'d, T, M> SpiDma<'d, T, HalfDuplexMode, M> + impl<'d, M, T> SpiDma<'d, HalfDuplexMode, M, T> where T: InstanceDma, M: Mode, @@ -1517,7 +1529,7 @@ mod dma { address: Address, dummy: u8, mut buffer: RX, - ) -> Result, (Error, Self, RX)> { + ) -> Result, (Error, Self, RX)> { self.wait_for_idle(); match unsafe { @@ -1584,7 +1596,7 @@ mod dma { address: Address, dummy: u8, mut buffer: TX, - ) -> Result, (Error, Self, TX)> { + ) -> Result, (Error, Self, TX)> { self.wait_for_idle(); match unsafe { @@ -1600,18 +1612,18 @@ mod dma { /// /// This structure is responsible for managing SPI transfers using DMA /// buffers. - pub struct SpiDmaBus<'d, T, D, M> + pub struct SpiDmaBus<'d, D, M, T = AnySpi> where T: InstanceDma, D: DuplexMode, M: Mode, { - spi_dma: SpiDma<'d, T, D, M>, + spi_dma: SpiDma<'d, D, M, T>, rx_buf: DmaRxBuf, tx_buf: DmaTxBuf, } - impl<'d, T, D, M> SpiDmaBus<'d, T, D, M> + impl<'d, D, M, T> SpiDmaBus<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -1619,7 +1631,7 @@ mod dma { { /// Creates a new `SpiDmaBus` with the specified SPI instance and DMA /// buffers. - pub fn new(spi_dma: SpiDma<'d, T, D, M>, rx_buf: DmaRxBuf, tx_buf: DmaTxBuf) -> Self { + pub fn new(spi_dma: SpiDma<'d, D, M, T>, rx_buf: DmaRxBuf, tx_buf: DmaTxBuf) -> Self { Self { spi_dma, rx_buf, @@ -1668,7 +1680,7 @@ mod dma { } } - impl<'d, T, D, M> InterruptConfigurable for SpiDmaBus<'d, T, D, M> + impl<'d, D, M, T> InterruptConfigurable for SpiDmaBus<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -1680,7 +1692,7 @@ mod dma { } } - impl<'d, T, D, M> crate::private::Sealed for SpiDmaBus<'d, T, D, M> + impl<'d, D, M, T> crate::private::Sealed for SpiDmaBus<'d, D, M, T> where T: InstanceDma, D: DuplexMode, @@ -1688,7 +1700,7 @@ mod dma { { } - impl<'d, T, M> SpiDmaBus<'d, T, FullDuplexMode, M> + impl<'d, M, T> SpiDmaBus<'d, FullDuplexMode, M, T> where T: InstanceDma, M: Mode, @@ -1788,7 +1800,7 @@ mod dma { } } - impl<'d, T, M> HalfDuplexReadWrite for SpiDmaBus<'d, T, HalfDuplexMode, M> + impl<'d, M, T> HalfDuplexReadWrite for SpiDmaBus<'d, HalfDuplexMode, M, T> where T: InstanceDma, M: Mode, @@ -1860,7 +1872,7 @@ mod dma { } impl<'d, T> embedded_hal_02::blocking::spi::Transfer - for SpiDmaBus<'d, T, FullDuplexMode, crate::Blocking> + for SpiDmaBus<'d, FullDuplexMode, crate::Blocking, T> where T: InstanceDma, { @@ -1873,7 +1885,7 @@ mod dma { } impl<'d, T> embedded_hal_02::blocking::spi::Write - for SpiDmaBus<'d, T, FullDuplexMode, crate::Blocking> + for SpiDmaBus<'d, FullDuplexMode, crate::Blocking, T> where T: InstanceDma, { @@ -1932,7 +1944,7 @@ mod dma { } } - impl<'d, T> SpiDmaBus<'d, T, FullDuplexMode, crate::Async> + impl<'d, T> SpiDmaBus<'d, FullDuplexMode, crate::Async, T> where T: InstanceDma, { @@ -2046,7 +2058,7 @@ mod dma { } } - impl<'d, T> embedded_hal_async::spi::SpiBus for SpiDmaBus<'d, T, FullDuplexMode, crate::Async> + impl<'d, T> embedded_hal_async::spi::SpiBus for SpiDmaBus<'d, FullDuplexMode, crate::Async, T> where T: InstanceDma, { @@ -2078,7 +2090,7 @@ mod dma { use super::*; - impl<'d, T, M> ErrorType for SpiDmaBus<'d, T, FullDuplexMode, M> + impl<'d, M, T> ErrorType for SpiDmaBus<'d, FullDuplexMode, M, T> where T: InstanceDma, M: Mode, @@ -2086,7 +2098,7 @@ mod dma { type Error = Error; } - impl<'d, T, M> SpiBus for SpiDmaBus<'d, T, FullDuplexMode, M> + impl<'d, M, T> SpiBus for SpiDmaBus<'d, FullDuplexMode, M, T> where T: InstanceDma, M: Mode, @@ -2121,11 +2133,11 @@ mod ehal1 { use super::*; - impl embedded_hal::spi::ErrorType for Spi<'_, T, M> { + impl embedded_hal::spi::ErrorType for Spi<'_, M, T> { type Error = Error; } - impl FullDuplex for Spi<'_, T, FullDuplexMode> + impl FullDuplex for Spi<'_, FullDuplexMode, T> where T: Instance, { @@ -2138,7 +2150,7 @@ mod ehal1 { } } - impl SpiBus for Spi<'_, T, FullDuplexMode> + impl SpiBus for Spi<'_, FullDuplexMode, T> where T: Instance, { @@ -2680,24 +2692,21 @@ pub trait Instance: private::Sealed + PeripheralMarker { } fn ch_bus_freq(&mut self, frequency: HertzU32) { - // Disable clock source - #[cfg(gdma)] - self.register_block().clk_gate().modify(|_, w| { - w.clk_en().clear_bit(); - w.mst_clk_active().clear_bit(); - w.mst_clk_sel().clear_bit() - }); + fn enable_clocks(_reg_block: &RegisterBlock, _enable: bool) { + #[cfg(gdma)] + _reg_block.clk_gate().modify(|_, w| { + w.clk_en().bit(_enable); + w.mst_clk_active().bit(_enable); + w.mst_clk_sel().bit(_enable) + }); + } + + enable_clocks(self.register_block(), false); // Change clock frequency self.setup(frequency); - // Enable clock source - #[cfg(gdma)] - self.register_block().clk_gate().modify(|_, w| { - w.clk_en().set_bit(); - w.mst_clk_active().set_bit(); - w.mst_clk_sel().set_bit() - }); + enable_clocks(self.register_block(), true); } #[cfg(not(any(esp32, esp32c3, esp32s2)))] @@ -3296,3 +3305,115 @@ impl ExtendedInstance for crate::peripherals::SPI3 { } } } + +impl Instance for super::AnySpi { + delegate::delegate! { + to match &self.0 { + super::AnySpiInner::Spi2(spi) => spi, + #[cfg(spi3)] + super::AnySpiInner::Spi3(spi) => spi, + } { + fn register_block(&self) -> &RegisterBlock; + fn spi_num(&self) -> u8; + fn sclk_signal(&self) -> OutputSignal; + fn mosi_signal(&self) -> OutputSignal; + fn miso_signal(&self) -> InputSignal; + fn cs_signal(&self) -> OutputSignal; + } + } + delegate::delegate! { + to match &mut self.0 { + super::AnySpiInner::Spi2(spi) => spi, + #[cfg(spi3)] + super::AnySpiInner::Spi3(spi) => spi, + } { + fn set_interrupt_handler(&mut self, handler: InterruptHandler); + } + } +} + +impl ExtendedInstance for super::AnySpi { + fn sio0_input_signal(&self) -> InputSignal { + match &self.0 { + super::AnySpiInner::Spi2(spi) => spi.sio0_input_signal(), + + #[cfg(all(spi3, any(esp32, esp32s3)))] + super::AnySpiInner::Spi3(spi) => spi.sio0_input_signal(), + + #[cfg(all(spi3, not(any(esp32, esp32s3))))] + super::AnySpiInner::Spi3(_) => unimplemented!("SPI3 is does not support QSPI"), + } + } + + fn sio1_output_signal(&self) -> OutputSignal { + match &self.0 { + super::AnySpiInner::Spi2(spi) => spi.sio1_output_signal(), + + #[cfg(all(spi3, any(esp32, esp32s3)))] + super::AnySpiInner::Spi3(spi) => spi.sio1_output_signal(), + + #[cfg(all(spi3, not(any(esp32, esp32s3))))] + super::AnySpiInner::Spi3(_) => unimplemented!("SPI3 is does not support QSPI"), + } + } + + fn sio2_output_signal(&self) -> OutputSignal { + match &self.0 { + super::AnySpiInner::Spi2(spi) => spi.sio2_output_signal(), + + #[cfg(all(spi3, any(esp32, esp32s3)))] + super::AnySpiInner::Spi3(spi) => spi.sio2_output_signal(), + + #[cfg(all(spi3, not(any(esp32, esp32s3))))] + super::AnySpiInner::Spi3(_) => unimplemented!("SPI3 is does not support QSPI"), + } + } + + fn sio2_input_signal(&self) -> InputSignal { + match &self.0 { + super::AnySpiInner::Spi2(spi) => spi.sio2_input_signal(), + + #[cfg(all(spi3, any(esp32, esp32s3)))] + super::AnySpiInner::Spi3(spi) => spi.sio2_input_signal(), + + #[cfg(all(spi3, not(any(esp32, esp32s3))))] + super::AnySpiInner::Spi3(_) => unimplemented!("SPI3 is does not support QSPI"), + } + } + + fn sio3_output_signal(&self) -> OutputSignal { + match &self.0 { + super::AnySpiInner::Spi2(spi) => spi.sio3_output_signal(), + + #[cfg(all(spi3, any(esp32, esp32s3)))] + super::AnySpiInner::Spi3(spi) => spi.sio3_output_signal(), + + #[cfg(all(spi3, not(any(esp32, esp32s3))))] + super::AnySpiInner::Spi3(_) => unimplemented!("SPI3 is does not support QSPI"), + } + } + + fn sio3_input_signal(&self) -> InputSignal { + match &self.0 { + super::AnySpiInner::Spi2(spi) => spi.sio3_input_signal(), + + #[cfg(all(spi3, any(esp32, esp32s3)))] + super::AnySpiInner::Spi3(spi) => spi.sio3_input_signal(), + + #[cfg(all(spi3, not(any(esp32, esp32s3))))] + super::AnySpiInner::Spi3(_) => unimplemented!("SPI3 is does not support QSPI"), + } + } +} + +impl InstanceDma for super::AnySpi { + delegate::delegate! { + to match &self.0 { + super::AnySpiInner::Spi2(spi) => spi, + #[cfg(spi3)] + super::AnySpiInner::Spi3(spi) => spi, + } { + fn clear_dma_interrupts(&self); + } + } +} diff --git a/esp-hal/src/spi/mod.rs b/esp-hal/src/spi/mod.rs index 250ed0e2b..ff5305901 100644 --- a/esp-hal/src/spi/mod.rs +++ b/esp-hal/src/spi/mod.rs @@ -9,7 +9,7 @@ //! more information on these modes, please refer to the documentation in their //! respective modules. -use crate::dma::{DmaError, PeripheralMarker}; +use crate::dma::{DmaEligible, DmaError}; pub mod master; pub mod slave; @@ -101,18 +101,28 @@ pub struct HalfDuplexMode {} impl DuplexMode for HalfDuplexMode {} impl crate::private::Sealed for HalfDuplexMode {} -#[cfg(spi2)] -impl PeripheralMarker for crate::peripherals::SPI2 { - #[inline(always)] - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::Spi2 +crate::any_peripheral! { + /// Any SPI peripheral. + pub peripheral AnySpi { + #[cfg(spi2)] + Spi2(crate::peripherals::SPI2), + #[cfg(spi3)] + Spi3(crate::peripherals::SPI3), } } -#[cfg(spi3)] -impl PeripheralMarker for crate::peripherals::SPI3 { - #[inline(always)] - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::Spi3 +impl DmaEligible for AnySpi { + #[cfg(gdma)] + type Dma = crate::dma::AnyGdmaChannel; + #[cfg(pdma)] + type Dma = crate::dma::AnySpiDmaChannel; + + fn dma_peripheral(&self) -> crate::dma::DmaPeripheral { + match &self.0 { + #[cfg(spi2)] + AnySpiInner::Spi2(_) => crate::dma::DmaPeripheral::Spi2, + #[cfg(spi3)] + AnySpiInner::Spi3(_) => crate::dma::DmaPeripheral::Spi3, + } } } diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 71e62967a..bd0bc183b 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -78,6 +78,7 @@ use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::spi2::RegisterBlock, private, + spi::AnySpi, system::PeripheralClockControl, }; @@ -86,17 +87,14 @@ const MAX_DMA_SIZE: usize = 32768 - 32; /// SPI peripheral driver. /// /// See the [module-level documentation][self] for more details. -pub struct Spi<'d, T, M> { +pub struct Spi<'d, M, T = AnySpi> { spi: PeripheralRef<'d, T>, #[allow(dead_code)] data_mode: SpiMode, _mode: PhantomData, } -impl<'d, T> Spi<'d, T, FullDuplexMode> -where - T: Instance, -{ +impl<'d> Spi<'d, FullDuplexMode> { /// Constructs an SPI instance in 8bit dataframe mode. pub fn new< SCK: PeripheralInput, @@ -104,42 +102,68 @@ where MISO: PeripheralOutput, CS: PeripheralInput, >( - spi: impl Peripheral

+ 'd, + spi: impl Peripheral

+ 'd> + 'd, sclk: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, cs: impl Peripheral

+ 'd, mode: SpiMode, - ) -> Spi<'d, T, FullDuplexMode> { - crate::into_ref!(spi, sclk, mosi, miso, cs); + ) -> Spi<'d, FullDuplexMode> { + Self::new_typed(spi, sclk, mosi, miso, cs, mode) + } +} +impl<'d, T> Spi<'d, FullDuplexMode, T> +where + T: Instance, +{ + /// Constructs an SPI instance in 8bit dataframe mode. + pub fn new_typed< + SCK: PeripheralInput, + MOSI: PeripheralInput, + MISO: PeripheralOutput, + CS: PeripheralInput, + >( + spi: impl Peripheral

+ 'd> + 'd, + sclk: impl Peripheral

+ 'd, + mosi: impl Peripheral

+ 'd, + miso: impl Peripheral

+ 'd, + cs: impl Peripheral

+ 'd, + mode: SpiMode, + ) -> Spi<'d, FullDuplexMode, T> { + crate::into_ref!(sclk, mosi, miso, cs); + + let this = Self::new_internal(spi, mode); + + // TODO: with_pins et. al. sclk.enable_input(true, private::Internal); - sclk.connect_input_to_peripheral(spi.sclk_signal(), private::Internal); + sclk.connect_input_to_peripheral(this.spi.sclk_signal(), private::Internal); mosi.enable_input(true, private::Internal); - mosi.connect_input_to_peripheral(spi.mosi_signal(), private::Internal); + mosi.connect_input_to_peripheral(this.spi.mosi_signal(), private::Internal); miso.set_to_push_pull_output(private::Internal); - miso.connect_peripheral_to_output(spi.miso_signal(), private::Internal); + miso.connect_peripheral_to_output(this.spi.miso_signal(), private::Internal); cs.enable_input(true, private::Internal); - cs.connect_input_to_peripheral(spi.cs_signal(), private::Internal); + cs.connect_input_to_peripheral(this.spi.cs_signal(), private::Internal); - Self::new_internal(spi, mode) + this } pub(crate) fn new_internal( - spi: PeripheralRef<'d, T>, + spi: impl Peripheral

+ 'd> + 'd, mode: SpiMode, - ) -> Spi<'d, T, FullDuplexMode> { - spi.reset_peripheral(); - spi.enable_peripheral(); + ) -> Spi<'d, FullDuplexMode, T> { + crate::into_ref!(spi); let mut spi = Spi { - spi, + spi: spi.map_into(), data_mode: mode, _mode: PhantomData, }; + spi.spi.reset_peripheral(); + spi.spi.enable_peripheral(); spi.spi.init(); spi.spi.set_data_mode(mode, false); @@ -169,7 +193,7 @@ pub mod dma { Mode, }; - impl<'d, T> Spi<'d, T, FullDuplexMode> + impl<'d, T> Spi<'d, FullDuplexMode, T> where T: InstanceDma, { @@ -181,7 +205,7 @@ pub mod dma { channel: Channel<'d, CH, DmaMode>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], - ) -> SpiDma<'d, T, DmaMode> + ) -> SpiDma<'d, DmaMode, T> where CH: DmaChannelConvert, DmaMode: Mode, @@ -192,7 +216,7 @@ pub mod dma { } /// A DMA capable SPI instance. - pub struct SpiDma<'d, T, DmaMode> + pub struct SpiDma<'d, DmaMode, T = AnySpi> where T: InstanceDma, DmaMode: Mode, @@ -203,7 +227,7 @@ pub mod dma { tx_chain: DescriptorChain, } - impl<'d, T, DmaMode> core::fmt::Debug for SpiDma<'d, T, DmaMode> + impl<'d, DmaMode, T> core::fmt::Debug for SpiDma<'d, DmaMode, T> where T: InstanceDma, DmaMode: Mode, @@ -213,7 +237,7 @@ pub mod dma { } } - impl<'d, T, DmaMode> DmaSupport for SpiDma<'d, T, DmaMode> + impl<'d, DmaMode, T> DmaSupport for SpiDma<'d, DmaMode, T> where T: InstanceDma, DmaMode: Mode, @@ -232,7 +256,7 @@ pub mod dma { } } - impl<'d, T, DmaMode> DmaSupportTx for SpiDma<'d, T, DmaMode> + impl<'d, DmaMode, T> DmaSupportTx for SpiDma<'d, DmaMode, T> where T: InstanceDma, DmaMode: Mode, @@ -248,7 +272,7 @@ pub mod dma { } } - impl<'d, T, DmaMode> DmaSupportRx for SpiDma<'d, T, DmaMode> + impl<'d, DmaMode, T> DmaSupportRx for SpiDma<'d, DmaMode, T> where T: InstanceDma, DmaMode: Mode, @@ -264,7 +288,7 @@ pub mod dma { } } - impl<'d, T, DmaMode> SpiDma<'d, T, DmaMode> + impl<'d, DmaMode, T> SpiDma<'d, DmaMode, T> where T: InstanceDma, DmaMode: Mode, @@ -566,8 +590,6 @@ pub trait Instance: private::Sealed + PeripheralMarker { PeripheralClockControl::enable(self.peripheral()); } - 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(); @@ -728,11 +750,6 @@ impl Instance for crate::peripherals::SPI2 { self } - #[inline(always)] - fn spi_num(&self) -> u8 { - 2 - } - #[inline(always)] fn sclk_signal(&self) -> InputSignal { cfg_if::cfg_if! { @@ -785,11 +802,6 @@ impl Instance for crate::peripherals::SPI3 { self } - #[inline(always)] - fn spi_num(&self) -> u8 { - 3 - } - #[inline(always)] fn sclk_signal(&self) -> InputSignal { cfg_if::cfg_if! { @@ -834,3 +846,21 @@ impl Instance for crate::peripherals::SPI3 { } } } + +impl Instance for super::AnySpi { + delegate::delegate! { + to match &self.0 { + super::AnySpiInner::Spi2(spi) => spi, + #[cfg(spi3)] + super::AnySpiInner::Spi3(spi) => spi, + } { + fn register_block(&self) -> &RegisterBlock; + fn sclk_signal(&self) -> InputSignal; + fn mosi_signal(&self) -> InputSignal; + fn miso_signal(&self) -> OutputSignal; + fn cs_signal(&self) -> InputSignal; + } + } +} + +impl InstanceDma for super::AnySpi {} diff --git a/hil-test/tests/embassy_interrupt_spi_dma.rs b/hil-test/tests/embassy_interrupt_spi_dma.rs index 86177a23c..9c8c40a4a 100644 --- a/hil-test/tests/embassy_interrupt_spi_dma.rs +++ b/hil-test/tests/embassy_interrupt_spi_dma.rs @@ -12,7 +12,6 @@ use esp_hal::{ dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, interrupt::{software::SoftwareInterruptControl, Priority}, - peripherals::SPI3, prelude::*, spi::{ master::{Spi, SpiDma}, @@ -35,7 +34,7 @@ macro_rules! mk_static { } #[embassy_executor::task] -async fn interrupt_driven_task(spi: SpiDma<'static, SPI3, FullDuplexMode, Async>) { +async fn interrupt_driven_task(spi: SpiDma<'static, FullDuplexMode, Async>) { let mut ticker = Ticker::every(Duration::from_millis(1)); let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(128); diff --git a/hil-test/tests/qspi.rs b/hil-test/tests/qspi.rs index d1ec44dce..fa0d18196 100644 --- a/hil-test/tests/qspi.rs +++ b/hil-test/tests/qspi.rs @@ -38,7 +38,7 @@ cfg_if::cfg_if! { } } -type SpiUnderTest = SpiDma<'static, esp_hal::peripherals::SPI2, HalfDuplexMode, Blocking>; +type SpiUnderTest = SpiDma<'static, HalfDuplexMode, Blocking>; struct Context { spi: esp_hal::peripherals::SPI2, diff --git a/hil-test/tests/spi_full_duplex.rs b/hil-test/tests/spi_full_duplex.rs index f9410341f..b8ab6153b 100644 --- a/hil-test/tests/spi_full_duplex.rs +++ b/hil-test/tests/spi_full_duplex.rs @@ -15,7 +15,6 @@ use esp_hal::{ dma::{Dma, DmaDescriptor, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::{Io, Level, NoPin}, - peripherals::SPI2, prelude::*, spi::{master::Spi, FullDuplexMode, SpiMode}, }; @@ -35,7 +34,7 @@ cfg_if::cfg_if! { } struct Context { - spi: Spi<'static, SPI2, FullDuplexMode>, + spi: Spi<'static, FullDuplexMode>, dma_channel: DmaChannelCreator, // Reuse the really large buffer so we don't run out of DRAM with many tests rx_buffer: &'static mut [u8], diff --git a/hil-test/tests/spi_half_duplex_read.rs b/hil-test/tests/spi_half_duplex_read.rs index 5f0c379f4..63d786a9c 100644 --- a/hil-test/tests/spi_half_duplex_read.rs +++ b/hil-test/tests/spi_half_duplex_read.rs @@ -9,7 +9,6 @@ use esp_hal::{ dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::{Io, Level, Output}, - peripherals::SPI2, prelude::*, spi::{ master::{Address, Command, HalfDuplexReadWrite, Spi, SpiDma}, @@ -22,7 +21,7 @@ use esp_hal::{ use hil_test as _; struct Context { - spi: SpiDma<'static, SPI2, HalfDuplexMode, Blocking>, + spi: SpiDma<'static, HalfDuplexMode, Blocking>, miso_mirror: Output<'static>, } diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index eb653963a..75b8a8957 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -10,7 +10,6 @@ use esp_hal::{ dma_buffers, gpio::{interconnect::InputSignal, Io}, pcnt::{channel::EdgeMode, unit::Unit, Pcnt}, - peripherals::SPI2, prelude::*, spi::{ master::{Address, Command, HalfDuplexReadWrite, Spi, SpiDma}, @@ -23,7 +22,7 @@ use esp_hal::{ use hil_test as _; struct Context { - spi: SpiDma<'static, SPI2, HalfDuplexMode, Blocking>, + spi: SpiDma<'static, HalfDuplexMode, Blocking>, pcnt_unit: Unit<'static, 0>, pcnt_source: InputSignal, } diff --git a/hil-test/tests/spi_half_duplex_write_psram.rs b/hil-test/tests/spi_half_duplex_write_psram.rs index b5a6845b1..d0f9e3cdc 100644 --- a/hil-test/tests/spi_half_duplex_write_psram.rs +++ b/hil-test/tests/spi_half_duplex_write_psram.rs @@ -12,7 +12,6 @@ use esp_hal::{ dma_descriptors_chunk_size, gpio::{interconnect::InputSignal, Io}, pcnt::{channel::EdgeMode, unit::Unit, Pcnt}, - peripherals::SPI2, prelude::*, spi::{ master::{Address, Command, HalfDuplexReadWrite, Spi, SpiDma}, @@ -40,7 +39,7 @@ macro_rules! dma_alloc_buffer { } struct Context { - spi: SpiDma<'static, SPI2, HalfDuplexMode, Blocking>, + spi: SpiDma<'static, HalfDuplexMode, Blocking>, pcnt_unit: Unit<'static, 0>, pcnt_source: InputSignal, } diff --git a/hil-test/tests/spi_slave.rs b/hil-test/tests/spi_slave.rs index 7009edfc8..08870aea1 100644 --- a/hil-test/tests/spi_slave.rs +++ b/hil-test/tests/spi_slave.rs @@ -12,7 +12,6 @@ use esp_hal::{ dma::{Dma, DmaPriority}, dma_buffers, gpio::{interconnect::InputSignal, Io, Level, Output, PeripheralInput}, - peripherals::SPI2, spi::{slave::Spi, FullDuplexMode, SpiMode}, }; use hil_test as _; @@ -26,7 +25,7 @@ cfg_if::cfg_if! { } struct Context { - spi: Spi<'static, SPI2, FullDuplexMode>, + spi: Spi<'static, FullDuplexMode>, dma_channel: DmaChannelCreator, bitbang_spi: BitbangSpi, }