diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index b620a9a49..49878ccd0 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -14,11 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add burst transfer support to DMA buffers (#2336) - `AnyPin` now implements `From>`. (#2326) - Added `AnySpi` and `AnySpiDmaChannel`. (#2334) +- Added `AnyI2s` and `AnyI2sDmaChannel`. (#2367) - `Pins::steal()` to unsafely obtain GPIO. (#2335) ### Changed - Peripheral type erasure for SPI (#2334) +- Peripheral type erasure for I2S (#2367) ### Fixed diff --git a/esp-hal/MIGRATING-0.21.md b/esp-hal/MIGRATING-0.21.md index 5eeb638de..0b8e1938b 100644 --- a/esp-hal/MIGRATING-0.21.md +++ b/esp-hal/MIGRATING-0.21.md @@ -29,6 +29,7 @@ You no longer have to specify the peripheral instance in the driver's type for t peripherals: - SPI (both master and slave) +- I2S ```diff -Spi<'static, SPI2, FullDuplexMode> @@ -36,6 +37,9 @@ peripherals: -SpiDma<'static, SPI2, HalfDuplexMode, Blocking> +SpiDma<'static, HalfDuplexMode, Blocking> + +-I2sTx<'static, I2S0, Async> ++I2sTx<'static, Async> ``` Note that you may still specify the instance if you need to. To do this, we provide `_typed` diff --git a/esp-hal/src/dma/pdma.rs b/esp-hal/src/dma/pdma.rs index 6a5b50201..de853696a 100644 --- a/esp-hal/src/dma/pdma.rs +++ b/esp-hal/src/dma/pdma.rs @@ -787,6 +787,15 @@ macro_rules! ImplI2sChannel { } } + impl DmaChannelConvert for [] { + fn degrade_rx(rx: I2sDmaRxChannelImpl) -> I2sDmaRxChannelImpl { + I2sDmaRxChannelImpl(rx.0.into()) + } + fn degrade_tx(tx: I2sDmaTxChannelImpl) -> I2sDmaTxChannelImpl { + I2sDmaTxChannelImpl(tx.0.into()) + } + } + #[doc = concat!("Creates a channel for I2S", $num)] pub struct [] {} @@ -942,3 +951,41 @@ impl PdmaChannel for AnySpiDmaChannelInner { } } } + +/// A marker for I2S-compatible type-erased DMA channels. +pub struct AnyI2sDmaChannel; + +impl crate::private::Sealed for AnyI2sDmaChannel {} + +impl DmaChannel for AnyI2sDmaChannel { + type Rx = I2sDmaRxChannelImpl; + type Tx = I2sDmaTxChannelImpl; +} + +crate::any_enum! { + #[doc(hidden)] + pub enum AnyI2sDmaChannelInner { + I2s0(I2s0DmaChannel), + #[cfg(i2s1)] + I2s1(I2s1DmaChannel), + } +} + +impl crate::private::Sealed for AnyI2sDmaChannelInner {} + +impl PdmaChannel for AnyI2sDmaChannelInner { + type RegisterBlock = I2sRegisterBlock; + + delegate::delegate! { + to match self { + AnyI2sDmaChannelInner::I2s0(channel) => channel, + #[cfg(i2s1)] + AnyI2sDmaChannelInner::I2s1(channel) => channel, + } { + fn register_block(&self) -> &I2sRegisterBlock; + 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 70bfe8cd9..feac9299e 100644 --- a/esp-hal/src/i2s.rs +++ b/esp-hal/src/i2s.rs @@ -92,6 +92,7 @@ use crate::{ DescriptorChain, DmaChannelConvert, DmaDescriptor, + DmaEligible, DmaError, DmaTransferRx, DmaTransferRxCircular, @@ -252,26 +253,26 @@ impl DataFormat { } /// Instance of the I2S peripheral driver -pub struct I2s<'d, T, DmaMode> +pub struct I2s<'d, DmaMode, T = AnyI2s> where T: RegisterAccess, DmaMode: Mode, { /// Handles the reception (RX) side of the I2S peripheral. - pub i2s_rx: RxCreator<'d, T, DmaMode>, + pub i2s_rx: RxCreator<'d, DmaMode, T>, /// Handles the transmission (TX) side of the I2S peripheral. - pub i2s_tx: TxCreator<'d, T, DmaMode>, + pub i2s_tx: TxCreator<'d, DmaMode, T>, phantom: PhantomData, } -impl<'d, T, DmaMode> I2s<'d, T, DmaMode> +impl<'d, DmaMode, T> I2s<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, { #[allow(clippy::too_many_arguments)] fn new_internal( - i2s: impl Peripheral

+ 'd, + i2s: PeripheralRef<'d, T>, standard: Standard, data_format: DataFormat, sample_rate: impl Into, @@ -282,7 +283,6 @@ where where CH: DmaChannelConvert, { - crate::into_ref!(i2s); channel.runtime_ensure_compatible(&i2s); // on ESP32-C3 / ESP32-S3 and later RX and TX are independent and // could be configured totally independently but for now handle all @@ -314,7 +314,7 @@ where } } -impl<'d, T, DmaMode> I2s<'d, T, DmaMode> +impl<'d, DmaMode, T> I2s<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -352,14 +352,14 @@ where } } -impl<'d, I, DmaMode> crate::private::Sealed for I2s<'d, I, DmaMode> +impl<'d, DmaMode, I> crate::private::Sealed for I2s<'d, DmaMode, I> where I: RegisterAccess, DmaMode: Mode, { } -impl<'d, I, DmaMode> InterruptConfigurable for I2s<'d, I, DmaMode> +impl<'d, DmaMode, I> InterruptConfigurable for I2s<'d, DmaMode, I> where I: RegisterAccess, DmaMode: Mode, @@ -369,7 +369,38 @@ where } } -impl<'d, T, DmaMode> I2s<'d, T, DmaMode> +impl<'d, DmaMode> I2s<'d, DmaMode> +where + DmaMode: Mode, +{ + /// Construct a new I2S peripheral driver instance for the first I2S + /// peripheral + #[allow(clippy::too_many_arguments)] + pub fn new( + i2s: impl Peripheral

+ 'd, + standard: Standard, + data_format: DataFormat, + sample_rate: impl Into, + channel: Channel<'d, CH, DmaMode>, + rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], + ) -> Self + where + CH: DmaChannelConvert<::Dma>, + { + Self::new_typed( + i2s.map_into(), + standard, + data_format, + sample_rate, + channel, + rx_descriptors, + tx_descriptors, + ) + } +} + +impl<'d, DmaMode, T> I2s<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -377,7 +408,7 @@ where /// Construct a new I2S peripheral driver instance for the first I2S /// peripheral #[allow(clippy::too_many_arguments)] - pub fn new( + pub fn new_typed( i2s: impl Peripheral

+ 'd, standard: Standard, data_format: DataFormat, @@ -388,8 +419,8 @@ where ) -> Self where CH: DmaChannelConvert, - DmaMode: Mode, { + crate::into_ref!(i2s); Self::new_internal( i2s, standard, @@ -412,7 +443,7 @@ where } /// I2S TX channel -pub struct I2sTx<'d, T, DmaMode> +pub struct I2sTx<'d, DmaMode, T = AnyI2s> where T: RegisterAccess, { @@ -422,7 +453,7 @@ where phantom: PhantomData, } -impl<'d, T, DmaMode> core::fmt::Debug for I2sTx<'d, T, DmaMode> +impl<'d, DmaMode, T> core::fmt::Debug for I2sTx<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -432,7 +463,7 @@ where } } -impl<'d, T, DmaMode> DmaSupport for I2sTx<'d, T, DmaMode> +impl<'d, DmaMode, T> DmaSupport for I2sTx<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -446,7 +477,7 @@ where } } -impl<'d, T, DmaMode> DmaSupportTx for I2sTx<'d, T, DmaMode> +impl<'d, DmaMode, T> DmaSupportTx for I2sTx<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -462,7 +493,7 @@ where } } -impl<'d, T, DmaMode> I2sTx<'d, T, DmaMode> +impl<'d, DmaMode, T> I2sTx<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -544,7 +575,7 @@ where } /// I2S RX channel -pub struct I2sRx<'d, T, DmaMode> +pub struct I2sRx<'d, DmaMode, T = AnyI2s> where T: RegisterAccess, DmaMode: Mode, @@ -555,7 +586,7 @@ where phantom: PhantomData, } -impl<'d, T, DmaMode> core::fmt::Debug for I2sRx<'d, T, DmaMode> +impl<'d, DmaMode, T> core::fmt::Debug for I2sRx<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -565,7 +596,7 @@ where } } -impl<'d, T, DmaMode> DmaSupport for I2sRx<'d, T, DmaMode> +impl<'d, DmaMode, T> DmaSupport for I2sRx<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -579,7 +610,7 @@ where } } -impl<'d, T, DmaMode> DmaSupportRx for I2sRx<'d, T, DmaMode> +impl<'d, DmaMode, T> DmaSupportRx for I2sRx<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -595,7 +626,7 @@ where } } -impl<'d, T, DmaMode> I2sRx<'d, T, DmaMode> +impl<'d, DmaMode, T> I2sRx<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -728,7 +759,7 @@ mod private { Mode, }; - pub struct TxCreator<'d, T, DmaMode> + pub struct TxCreator<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -739,12 +770,12 @@ mod private { pub(crate) phantom: PhantomData, } - impl<'d, T, DmaMode> TxCreator<'d, T, DmaMode> + impl<'d, DmaMode, T> TxCreator<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, { - pub fn build(self) -> I2sTx<'d, T, DmaMode> { + pub fn build(self) -> I2sTx<'d, DmaMode, T> { I2sTx { i2s: self.i2s, tx_channel: self.tx_channel, @@ -787,7 +818,7 @@ mod private { } } - pub struct RxCreator<'d, T, DmaMode> + pub struct RxCreator<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -798,12 +829,12 @@ mod private { pub(crate) phantom: PhantomData, } - impl<'d, T, DmaMode> RxCreator<'d, T, DmaMode> + impl<'d, DmaMode, T> RxCreator<'d, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, { - pub fn build(self) -> I2sRx<'d, T, DmaMode> { + pub fn build(self) -> I2sRx<'d, DmaMode, T> { I2sRx { i2s: self.i2s, rx_channel: self.rx_channel, @@ -847,7 +878,11 @@ mod private { } pub trait RegBlock: - crate::peripheral::Peripheral

+ PeripheralMarker + DmaEligible + crate::peripheral::Peripheral

+ + PeripheralMarker + + DmaEligible + + Into + + 'static { fn register_block(&self) -> &RegisterBlock; } @@ -1559,7 +1594,6 @@ mod private { } } - #[cfg(i2s0)] impl Signals for crate::peripherals::I2S0 { fn mclk_signal(&self) -> OutputSignal { cfg_if::cfg_if! { @@ -1709,6 +1743,48 @@ mod private { } } + impl RegBlock for super::AnyI2s { + delegate::delegate! { + to match &self.0 { + super::AnyI2sInner::I2s0(i2s) => i2s, + #[cfg(i2s1)] + super::AnyI2sInner::I2s1(i2s) => i2s, + } { + fn register_block(&self) -> &RegisterBlock; + } + } + } + + impl RegisterAccessPrivate for super::AnyI2s { + delegate::delegate! { + to match &self.0 { + super::AnyI2sInner::I2s0(i2s) => i2s, + #[cfg(i2s1)] + super::AnyI2sInner::I2s1(i2s) => i2s, + } { + fn set_interrupt_handler(&self, handler: InterruptHandler); + } + } + } + + impl Signals for super::AnyI2s { + delegate::delegate! { + to match &self.0 { + super::AnyI2sInner::I2s0(i2s) => i2s, + #[cfg(i2s1)] + super::AnyI2sInner::I2s1(i2s) => i2s, + } { + fn mclk_signal(&self) -> OutputSignal; + fn bclk_signal(&self) -> OutputSignal; + fn ws_signal(&self) -> OutputSignal; + fn dout_signal(&self) -> OutputSignal; + fn bclk_rx_signal(&self) -> OutputSignal; + fn ws_rx_signal(&self) -> OutputSignal; + fn din_signal(&self) -> InputSignal; + } + } + } + pub struct I2sClockDividers { mclk_divider: u32, bclk_divider: u32, @@ -1801,7 +1877,7 @@ pub mod asynch { Async, }; - impl<'d, T> I2sTx<'d, T, Async> + impl<'d, T> I2sTx<'d, Async, T> where T: RegisterAccess, { @@ -1831,7 +1907,7 @@ pub mod asynch { pub fn write_dma_circular_async( mut self, words: TXBUF, - ) -> Result, Error> { + ) -> Result, Error> { let (ptr, len) = unsafe { words.read_buffer() }; // Reset TX unit and TX FIFO @@ -1862,16 +1938,16 @@ pub mod asynch { } /// An in-progress async circular DMA write transfer. - pub struct I2sWriteDmaTransferAsync<'d, T, BUFFER> + pub struct I2sWriteDmaTransferAsync<'d, BUFFER, T = super::AnyI2s> where T: RegisterAccess, { - i2s_tx: I2sTx<'d, T, Async>, + i2s_tx: I2sTx<'d, Async, T>, state: TxCircularState, _buffer: BUFFER, } - impl<'d, T, BUFFER> I2sWriteDmaTransferAsync<'d, T, BUFFER> + impl<'d, T, BUFFER> I2sWriteDmaTransferAsync<'d, BUFFER, T> where T: RegisterAccess, { @@ -1911,7 +1987,7 @@ pub mod asynch { } } - impl<'d, T> I2sRx<'d, T, Async> + impl<'d, T> I2sRx<'d, Async, T> where T: RegisterAccess, { @@ -1949,7 +2025,7 @@ pub mod asynch { pub fn read_dma_circular_async( mut self, mut words: RXBUF, - ) -> Result, Error> + ) -> Result, Error> where RXBUF: WriteBuffer, { @@ -1985,16 +2061,16 @@ pub mod asynch { } /// An in-progress async circular DMA read transfer. - pub struct I2sReadDmaTransferAsync<'d, T, BUFFER> + pub struct I2sReadDmaTransferAsync<'d, BUFFER, T = super::AnyI2s> where T: RegisterAccess, { - i2s_rx: I2sRx<'d, T, Async>, + i2s_rx: I2sRx<'d, Async, T>, state: RxCircularState, _buffer: BUFFER, } - impl<'d, T, BUFFER> I2sReadDmaTransferAsync<'d, T, BUFFER> + impl<'d, T, BUFFER> I2sReadDmaTransferAsync<'d, BUFFER, T> where T: RegisterAccess, { @@ -2022,3 +2098,28 @@ pub mod asynch { } } } + +crate::any_peripheral! { + /// Any SPI peripheral. + pub peripheral AnyI2s { + #[cfg(i2s0)] + I2s0(crate::peripherals::I2S0), + #[cfg(i2s1)] + I2s1(crate::peripherals::I2S1), + } +} + +impl DmaEligible for AnyI2s { + #[cfg(gdma)] + type Dma = crate::dma::AnyGdmaChannel; + #[cfg(pdma)] + type Dma = crate::dma::AnyI2sDmaChannel; + + fn dma_peripheral(&self) -> crate::dma::DmaPeripheral { + match &self.0 { + AnyI2sInner::I2s0(_) => crate::dma::DmaPeripheral::I2s0, + #[cfg(i2s1)] + AnyI2sInner::I2s1(_) => crate::dma::DmaPeripheral::I2s1, + } + } +} diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index 40cd66771..170506a89 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -463,14 +463,14 @@ where T: Instance, { fn new_internal( - spi: impl Peripheral

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

+ 'd, frequency: HertzU32, mode: SpiMode, ) -> Spi<'d, M, T> { crate::into_ref!(spi); let mut spi = Spi { - spi: spi.map_into(), + spi, _mode: PhantomData, }; spi.spi.reset_peripheral(); @@ -571,11 +571,11 @@ impl<'d> Spi<'d, FullDuplexMode> { /// 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, + spi: impl Peripheral

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

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

+ 'd, frequency: HertzU32, mode: SpiMode, ) -> Spi<'d, FullDuplexMode, T> { @@ -666,11 +666,11 @@ impl<'d> Spi<'d, HalfDuplexMode> { /// 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, + spi: impl Peripheral

+ 'd, frequency: HertzU32, mode: SpiMode, ) -> Spi<'d, HalfDuplexMode> { - Self::new_half_duplex_typed(spi, frequency, mode) + Self::new_half_duplex_typed(spi.map_into(), frequency, mode) } } @@ -683,7 +683,7 @@ 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_typed( - spi: impl Peripheral

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

+ 'd, frequency: HertzU32, mode: SpiMode, ) -> Spi<'d, HalfDuplexMode, T> { @@ -2372,7 +2372,7 @@ pub trait ExtendedInstance: Instance { } #[doc(hidden)] -pub trait Instance: private::Sealed + PeripheralMarker { +pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static { fn register_block(&self) -> &RegisterBlock; fn sclk_signal(&self) -> OutputSignal; diff --git a/hil-test/tests/i2s.rs b/hil-test/tests/i2s.rs index 1146da5dc..2420264ed 100644 --- a/hil-test/tests/i2s.rs +++ b/hil-test/tests/i2s.rs @@ -57,7 +57,7 @@ impl Iterator for SampleSource { } #[embassy_executor::task] -async fn writer(tx_buffer: &'static mut [u8], i2s_tx: I2sTx<'static, I2S0, Async>) { +async fn writer(tx_buffer: &'static mut [u8], i2s_tx: I2sTx<'static, Async>) { let mut samples = SampleSource::new(); for b in tx_buffer.iter_mut() { *b = samples.next().unwrap();