* Implement I2S type erasure

* Clean up
This commit is contained in:
Dániel Buga 2024-10-22 08:33:27 +02:00 committed by GitHub
parent a754e411b1
commit d914a0301f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 204 additions and 50 deletions

View File

@ -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<GpioPin<N>>`. (#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

View File

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

View File

@ -787,6 +787,15 @@ macro_rules! ImplI2sChannel {
}
}
impl DmaChannelConvert<AnyI2sDmaChannel> for [<I2s $num DmaChannel>] {
fn degrade_rx(rx: I2sDmaRxChannelImpl<Self>) -> I2sDmaRxChannelImpl<AnyI2sDmaChannelInner> {
I2sDmaRxChannelImpl(rx.0.into())
}
fn degrade_tx(tx: I2sDmaTxChannelImpl<Self>) -> I2sDmaTxChannelImpl<AnyI2sDmaChannelInner> {
I2sDmaTxChannelImpl(tx.0.into())
}
}
#[doc = concat!("Creates a channel for I2S", $num)]
pub struct [<I2s $num DmaChannelCreator>] {}
@ -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<AnyI2sDmaChannelInner>;
type Tx = I2sDmaTxChannelImpl<AnyI2sDmaChannelInner>;
}
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;
}
}
}

View File

@ -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<DmaMode>,
}
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<CH>(
i2s: impl Peripheral<P = T> + 'd,
i2s: PeripheralRef<'d, T>,
standard: Standard,
data_format: DataFormat,
sample_rate: impl Into<fugit::HertzU32>,
@ -282,7 +283,6 @@ where
where
CH: DmaChannelConvert<T::Dma>,
{
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<CH>(
i2s: impl Peripheral<P = impl RegisterAccess> + 'd,
standard: Standard,
data_format: DataFormat,
sample_rate: impl Into<fugit::HertzU32>,
channel: Channel<'d, CH, DmaMode>,
rx_descriptors: &'static mut [DmaDescriptor],
tx_descriptors: &'static mut [DmaDescriptor],
) -> Self
where
CH: DmaChannelConvert<<AnyI2s as DmaEligible>::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<CH>(
pub fn new_typed<CH>(
i2s: impl Peripheral<P = T> + 'd,
standard: Standard,
data_format: DataFormat,
@ -388,8 +419,8 @@ where
) -> Self
where
CH: DmaChannelConvert<T::Dma>,
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<DmaMode>,
}
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<DmaMode>,
}
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<DmaMode>,
}
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<DmaMode>,
}
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<P = Self> + PeripheralMarker + DmaEligible
crate::peripheral::Peripheral<P = Self>
+ PeripheralMarker
+ DmaEligible
+ Into<super::AnyI2s>
+ '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<TXBUF: ReadBuffer>(
mut self,
words: TXBUF,
) -> Result<I2sWriteDmaTransferAsync<'d, T, TXBUF>, Error> {
) -> Result<I2sWriteDmaTransferAsync<'d, TXBUF, T>, 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<RXBUF>(
mut self,
mut words: RXBUF,
) -> Result<I2sReadDmaTransferAsync<'d, T, RXBUF>, Error>
) -> Result<I2sReadDmaTransferAsync<'d, RXBUF, T>, 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,
}
}
}

View File

@ -463,14 +463,14 @@ where
T: Instance,
{
fn new_internal(
spi: impl Peripheral<P = impl Into<T> + 'd> + 'd,
spi: impl Peripheral<P = T> + '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<P = impl Into<AnySpi> + 'd> + 'd,
spi: impl Peripheral<P = impl Instance> + '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<P = impl Into<T> + 'd> + 'd,
spi: impl Peripheral<P = T> + '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<P = impl Into<AnySpi> + 'd> + 'd,
spi: impl Peripheral<P = impl Instance> + '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<P = impl Into<T> + 'd> + 'd,
spi: impl Peripheral<P = T> + '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<AnySpi> + 'static {
fn register_block(&self) -> &RegisterBlock;
fn sclk_signal(&self) -> OutputSignal;

View File

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