Merge pull request #3023 from liarokapisv/i2s-ring-buffered
Revise I2S interface to ring-buffered.
This commit is contained in:
		
						commit
						0225c2a0f2
					
				@ -1,12 +1,13 @@
 | 
				
			|||||||
//! Inter-IC Sound (I2S)
 | 
					//! Inter-IC Sound (I2S)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use embassy_futures::join::join;
 | 
				
			||||||
use embassy_hal_internal::into_ref;
 | 
					use embassy_hal_internal::into_ref;
 | 
				
			||||||
 | 
					use stm32_metapac::spi::vals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::dma::ChannelAndRequest;
 | 
					use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer};
 | 
				
			||||||
use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
 | 
					use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
 | 
				
			||||||
use crate::mode::Async;
 | 
					use crate::mode::Async;
 | 
				
			||||||
use crate::pac::spi::vals;
 | 
					use crate::spi::{Config as SpiConfig, RegsExt as _, *};
 | 
				
			||||||
use crate::spi::{Config as SpiConfig, *};
 | 
					 | 
				
			||||||
use crate::time::Hertz;
 | 
					use crate::time::Hertz;
 | 
				
			||||||
use crate::{Peripheral, PeripheralRef};
 | 
					use crate::{Peripheral, PeripheralRef};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -19,6 +20,19 @@ pub enum Mode {
 | 
				
			|||||||
    Slave,
 | 
					    Slave,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// I2S function
 | 
				
			||||||
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
 | 
					#[allow(dead_code)]
 | 
				
			||||||
 | 
					enum Function {
 | 
				
			||||||
 | 
					    /// Transmit audio data
 | 
				
			||||||
 | 
					    Transmit,
 | 
				
			||||||
 | 
					    /// Receive audio data
 | 
				
			||||||
 | 
					    Receive,
 | 
				
			||||||
 | 
					    #[cfg(spi_v3)]
 | 
				
			||||||
 | 
					    /// Transmit and Receive audio data
 | 
				
			||||||
 | 
					    FullDuplex,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// I2C standard
 | 
					/// I2C standard
 | 
				
			||||||
#[derive(Copy, Clone)]
 | 
					#[derive(Copy, Clone)]
 | 
				
			||||||
pub enum Standard {
 | 
					pub enum Standard {
 | 
				
			||||||
@ -34,6 +48,30 @@ pub enum Standard {
 | 
				
			|||||||
    PcmShortSync,
 | 
					    PcmShortSync,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// SAI error
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
				
			||||||
 | 
					pub enum Error {
 | 
				
			||||||
 | 
					    /// `write` called on a SAI in receive mode.
 | 
				
			||||||
 | 
					    NotATransmitter,
 | 
				
			||||||
 | 
					    /// `read` called on a SAI in transmit mode.
 | 
				
			||||||
 | 
					    NotAReceiver,
 | 
				
			||||||
 | 
					    /// Overrun
 | 
				
			||||||
 | 
					    Overrun,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<ringbuffer::Error> for Error {
 | 
				
			||||||
 | 
					    fn from(#[allow(unused)] err: ringbuffer::Error) -> Self {
 | 
				
			||||||
 | 
					        #[cfg(feature = "defmt")]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if err == ringbuffer::Error::DmaUnsynced {
 | 
				
			||||||
 | 
					                defmt::error!("Ringbuffer broken invariants detected!");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Self::Overrun
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Standard {
 | 
					impl Standard {
 | 
				
			||||||
    #[cfg(any(spi_v1, spi_v3, spi_f1))]
 | 
					    #[cfg(any(spi_v1, spi_v3, spi_f1))]
 | 
				
			||||||
    const fn i2sstd(&self) -> vals::I2sstd {
 | 
					    const fn i2sstd(&self) -> vals::I2sstd {
 | 
				
			||||||
@ -142,31 +180,62 @@ impl Default for Config {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// I2S driver writer. Useful for moving write functionality across tasks.
 | 
				
			||||||
 | 
					pub struct Writer<'s, 'd, W: Word>(&'s mut WritableRingBuffer<'d, W>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'s, 'd, W: Word> Writer<'s, 'd, W> {
 | 
				
			||||||
 | 
					    /// Write data to the I2S ringbuffer.
 | 
				
			||||||
 | 
					    /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background.
 | 
				
			||||||
 | 
					    /// If thfre’s no space in the buffer, this waits until there is.
 | 
				
			||||||
 | 
					    pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        self.0.write_exact(data).await?;
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Reset the ring buffer to its initial state.
 | 
				
			||||||
 | 
					    /// Can be used to recover from overrun.
 | 
				
			||||||
 | 
					    /// The ringbuffer will always auto-reset on Overrun in any case.
 | 
				
			||||||
 | 
					    pub fn reset(&mut self) {
 | 
				
			||||||
 | 
					        self.0.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// I2S driver reader. Useful for moving read functionality across tasks.
 | 
				
			||||||
 | 
					pub struct Reader<'s, 'd, W: Word>(&'s mut ReadableRingBuffer<'d, W>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'s, 'd, W: Word> Reader<'s, 'd, W> {
 | 
				
			||||||
 | 
					    /// Read data from the I2S ringbuffer.
 | 
				
			||||||
 | 
					    /// SAI is always receiving data in the background. This function pops already-received data from the buffer.
 | 
				
			||||||
 | 
					    /// If there’s less than data.len() data in the buffer, this waits until there is.
 | 
				
			||||||
 | 
					    pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        self.0.read_exact(data).await?;
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Reset the ring buffer to its initial state.
 | 
				
			||||||
 | 
					    /// Can be used to prevent overrun.
 | 
				
			||||||
 | 
					    /// The ringbuffer will always auto-reset on Overrun in any case.
 | 
				
			||||||
 | 
					    pub fn reset(&mut self) {
 | 
				
			||||||
 | 
					        self.0.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// I2S driver.
 | 
					/// I2S driver.
 | 
				
			||||||
pub struct I2S<'d> {
 | 
					pub struct I2S<'d, W: Word> {
 | 
				
			||||||
    _peri: Spi<'d, Async>,
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
 | 
					    mode: Mode,
 | 
				
			||||||
 | 
					    spi: Spi<'d, Async>,
 | 
				
			||||||
    txsd: Option<PeripheralRef<'d, AnyPin>>,
 | 
					    txsd: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
    rxsd: Option<PeripheralRef<'d, AnyPin>>,
 | 
					    rxsd: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
    ws: Option<PeripheralRef<'d, AnyPin>>,
 | 
					    ws: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
    ck: Option<PeripheralRef<'d, AnyPin>>,
 | 
					    ck: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
    mck: Option<PeripheralRef<'d, AnyPin>>,
 | 
					    mck: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
 | 
					    tx_ring_buffer: Option<WritableRingBuffer<'d, W>>,
 | 
				
			||||||
 | 
					    rx_ring_buffer: Option<ReadableRingBuffer<'d, W>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// I2S function
 | 
					impl<'d, W: Word> I2S<'d, W> {
 | 
				
			||||||
#[derive(Copy, Clone)]
 | 
					    /// Create a transmitter driver.
 | 
				
			||||||
#[allow(dead_code)]
 | 
					 | 
				
			||||||
enum Function {
 | 
					 | 
				
			||||||
    /// Transmit audio data
 | 
					 | 
				
			||||||
    Transmit,
 | 
					 | 
				
			||||||
    /// Receive audio data
 | 
					 | 
				
			||||||
    Receive,
 | 
					 | 
				
			||||||
    #[cfg(spi_v3)]
 | 
					 | 
				
			||||||
    /// Transmit and Receive audio data
 | 
					 | 
				
			||||||
    FullDuplex,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'d> I2S<'d> {
 | 
					 | 
				
			||||||
    /// Create a transmitter driver
 | 
					 | 
				
			||||||
    pub fn new_txonly<T: Instance>(
 | 
					    pub fn new_txonly<T: Instance>(
 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
        sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
 | 
					        sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
 | 
				
			||||||
@ -174,18 +243,18 @@ impl<'d> I2S<'d> {
 | 
				
			|||||||
        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
					        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
				
			||||||
        mck: impl Peripheral<P = impl MckPin<T>> + 'd,
 | 
					        mck: impl Peripheral<P = impl MckPin<T>> + 'd,
 | 
				
			||||||
        txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
 | 
					        txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
 | 
				
			||||||
 | 
					        txdma_buf: &'d mut [W],
 | 
				
			||||||
        freq: Hertz,
 | 
					        freq: Hertz,
 | 
				
			||||||
        config: Config,
 | 
					        config: Config,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        into_ref!(sd);
 | 
					 | 
				
			||||||
        Self::new_inner(
 | 
					        Self::new_inner(
 | 
				
			||||||
            peri,
 | 
					            peri,
 | 
				
			||||||
            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
					            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
				
			||||||
            None,
 | 
					            None,
 | 
				
			||||||
            ws,
 | 
					            ws,
 | 
				
			||||||
            ck,
 | 
					            ck,
 | 
				
			||||||
            mck,
 | 
					            new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
				
			||||||
            new_dma!(txdma),
 | 
					            new_dma!(txdma).map(|d| (d, txdma_buf)),
 | 
				
			||||||
            None,
 | 
					            None,
 | 
				
			||||||
            freq,
 | 
					            freq,
 | 
				
			||||||
            config,
 | 
					            config,
 | 
				
			||||||
@ -193,42 +262,61 @@ impl<'d> I2S<'d> {
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Create a receiver driver
 | 
					    /// Create a transmitter driver without a master clock pin.
 | 
				
			||||||
 | 
					    pub fn new_txonly_nomck<T: Instance>(
 | 
				
			||||||
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
 | 
					        sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
 | 
				
			||||||
 | 
					        ws: impl Peripheral<P = impl WsPin<T>> + 'd,
 | 
				
			||||||
 | 
					        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
				
			||||||
 | 
					        txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
 | 
				
			||||||
 | 
					        txdma_buf: &'d mut [W],
 | 
				
			||||||
 | 
					        freq: Hertz,
 | 
				
			||||||
 | 
					        config: Config,
 | 
				
			||||||
 | 
					    ) -> Self {
 | 
				
			||||||
 | 
					        Self::new_inner(
 | 
				
			||||||
 | 
					            peri,
 | 
				
			||||||
 | 
					            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            ws,
 | 
				
			||||||
 | 
					            ck,
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            new_dma!(txdma).map(|d| (d, txdma_buf)),
 | 
				
			||||||
 | 
					            None,
 | 
				
			||||||
 | 
					            freq,
 | 
				
			||||||
 | 
					            config,
 | 
				
			||||||
 | 
					            Function::Transmit,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Create a receiver driver.
 | 
				
			||||||
    pub fn new_rxonly<T: Instance>(
 | 
					    pub fn new_rxonly<T: Instance>(
 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
        sd: impl Peripheral<P = impl MisoPin<T>> + 'd,
 | 
					        sd: impl Peripheral<P = impl MisoPin<T>> + 'd,
 | 
				
			||||||
        ws: impl Peripheral<P = impl WsPin<T>> + 'd,
 | 
					        ws: impl Peripheral<P = impl WsPin<T>> + 'd,
 | 
				
			||||||
        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
					        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
				
			||||||
        mck: impl Peripheral<P = impl MckPin<T>> + 'd,
 | 
					        mck: impl Peripheral<P = impl MckPin<T>> + 'd,
 | 
				
			||||||
        #[cfg(not(spi_v3))] txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
 | 
					 | 
				
			||||||
        rxdma: impl Peripheral<P = impl RxDma<T>> + 'd,
 | 
					        rxdma: impl Peripheral<P = impl RxDma<T>> + 'd,
 | 
				
			||||||
 | 
					        rxdma_buf: &'d mut [W],
 | 
				
			||||||
        freq: Hertz,
 | 
					        freq: Hertz,
 | 
				
			||||||
        config: Config,
 | 
					        config: Config,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        into_ref!(sd);
 | 
					 | 
				
			||||||
        Self::new_inner(
 | 
					        Self::new_inner(
 | 
				
			||||||
            peri,
 | 
					            peri,
 | 
				
			||||||
            None,
 | 
					            None,
 | 
				
			||||||
            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
					            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
				
			||||||
            ws,
 | 
					            ws,
 | 
				
			||||||
            ck,
 | 
					            ck,
 | 
				
			||||||
            mck,
 | 
					            new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
				
			||||||
            #[cfg(not(spi_v3))]
 | 
					 | 
				
			||||||
            new_dma!(txdma),
 | 
					 | 
				
			||||||
            #[cfg(spi_v3)]
 | 
					 | 
				
			||||||
            None,
 | 
					            None,
 | 
				
			||||||
            new_dma!(rxdma),
 | 
					            new_dma!(rxdma).map(|d| (d, rxdma_buf)),
 | 
				
			||||||
            freq,
 | 
					            freq,
 | 
				
			||||||
            config,
 | 
					            config,
 | 
				
			||||||
            #[cfg(not(spi_v3))]
 | 
					 | 
				
			||||||
            Function::Transmit,
 | 
					 | 
				
			||||||
            #[cfg(spi_v3)]
 | 
					 | 
				
			||||||
            Function::Receive,
 | 
					            Function::Receive,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[cfg(spi_v3)]
 | 
					    #[cfg(spi_v3)]
 | 
				
			||||||
    /// Create a full duplex transmitter driver
 | 
					    /// Create a full duplex driver.
 | 
				
			||||||
    pub fn new_full_duplex<T: Instance>(
 | 
					    pub fn new_full_duplex<T: Instance>(
 | 
				
			||||||
        peri: impl Peripheral<P = T> + 'd,
 | 
					        peri: impl Peripheral<P = T> + 'd,
 | 
				
			||||||
        txsd: impl Peripheral<P = impl MosiPin<T>> + 'd,
 | 
					        txsd: impl Peripheral<P = impl MosiPin<T>> + 'd,
 | 
				
			||||||
@ -237,44 +325,144 @@ impl<'d> I2S<'d> {
 | 
				
			|||||||
        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
					        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
				
			||||||
        mck: impl Peripheral<P = impl MckPin<T>> + 'd,
 | 
					        mck: impl Peripheral<P = impl MckPin<T>> + 'd,
 | 
				
			||||||
        txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
 | 
					        txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
 | 
				
			||||||
 | 
					        txdma_buf: &'d mut [W],
 | 
				
			||||||
        rxdma: impl Peripheral<P = impl RxDma<T>> + 'd,
 | 
					        rxdma: impl Peripheral<P = impl RxDma<T>> + 'd,
 | 
				
			||||||
 | 
					        rxdma_buf: &'d mut [W],
 | 
				
			||||||
        freq: Hertz,
 | 
					        freq: Hertz,
 | 
				
			||||||
        config: Config,
 | 
					        config: Config,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        into_ref!(txsd, rxsd);
 | 
					 | 
				
			||||||
        Self::new_inner(
 | 
					        Self::new_inner(
 | 
				
			||||||
            peri,
 | 
					            peri,
 | 
				
			||||||
            new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
					            new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
				
			||||||
            new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
					            new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
				
			||||||
            ws,
 | 
					            ws,
 | 
				
			||||||
            ck,
 | 
					            ck,
 | 
				
			||||||
            mck,
 | 
					            new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
 | 
				
			||||||
            new_dma!(txdma),
 | 
					            new_dma!(txdma).map(|d| (d, txdma_buf)),
 | 
				
			||||||
            new_dma!(rxdma),
 | 
					            new_dma!(rxdma).map(|d| (d, rxdma_buf)),
 | 
				
			||||||
            freq,
 | 
					            freq,
 | 
				
			||||||
            config,
 | 
					            config,
 | 
				
			||||||
            Function::FullDuplex,
 | 
					            Function::FullDuplex,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Write audio data.
 | 
					    /// Start I2S driver.
 | 
				
			||||||
    pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
 | 
					    pub fn start(&mut self) {
 | 
				
			||||||
        self._peri.read(data).await
 | 
					        self.spi.info.regs.cr1().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_spe(false);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        self.spi.set_word_size(W::CONFIG);
 | 
				
			||||||
 | 
					        if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
 | 
				
			||||||
 | 
					            tx_ring_buffer.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            set_txdmaen(self.spi.info.regs, true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
 | 
				
			||||||
 | 
					            rx_ring_buffer.start();
 | 
				
			||||||
 | 
					            // SPIv3 clears rxfifo on SPE=0
 | 
				
			||||||
 | 
					            #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
				
			||||||
 | 
					            flush_rx_fifo(self.spi.info.regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            set_rxdmaen(self.spi.info.regs, true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.spi.info.regs.cr1().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_spe(true);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
				
			||||||
 | 
					        self.spi.info.regs.cr1().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_cstart(true);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Write audio data.
 | 
					    /// Reset the ring buffer to its initial state.
 | 
				
			||||||
    pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> {
 | 
					    /// Can be used to recover from overrun.
 | 
				
			||||||
        self._peri.write(data).await
 | 
					    pub fn clear(&mut self) {
 | 
				
			||||||
 | 
					        if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
 | 
				
			||||||
 | 
					            rx_ring_buffer.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
 | 
				
			||||||
 | 
					            tx_ring_buffer.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Transfer audio data.
 | 
					    /// Stop I2S driver.
 | 
				
			||||||
    pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
 | 
					    pub async fn stop(&mut self) {
 | 
				
			||||||
        self._peri.transfer(read, write).await
 | 
					        let regs = self.spi.info.regs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx_f = async {
 | 
				
			||||||
 | 
					            if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
 | 
				
			||||||
 | 
					                tx_ring_buffer.stop().await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                set_txdmaen(regs, false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let rx_f = async {
 | 
				
			||||||
 | 
					            if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
 | 
				
			||||||
 | 
					                rx_ring_buffer.stop().await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                set_rxdmaen(regs, false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        join(rx_f, tx_f).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(any(spi_v3, spi_v4, spi_v5))]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if let Mode::Master = self.mode {
 | 
				
			||||||
 | 
					                regs.cr1().modify(|w| {
 | 
				
			||||||
 | 
					                    w.set_csusp(true);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while regs.cr1().read().cstart() {}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        regs.cr1().modify(|w| {
 | 
				
			||||||
 | 
					            w.set_spe(false);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.clear();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Transfer audio data in place.
 | 
					    /// Split the driver into a Reader/Writer pair.
 | 
				
			||||||
    pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
 | 
					    /// Useful for splitting the reader/writer functionality across tasks or
 | 
				
			||||||
        self._peri.transfer_in_place(data).await
 | 
					    /// for calling the read/write methods in parallel.
 | 
				
			||||||
 | 
					    pub fn split<'s>(&'s mut self) -> Result<(Reader<'s, 'd, W>, Writer<'s, 'd, W>), Error> {
 | 
				
			||||||
 | 
					        match (&mut self.rx_ring_buffer, &mut self.tx_ring_buffer) {
 | 
				
			||||||
 | 
					            (None, _) => Err(Error::NotAReceiver),
 | 
				
			||||||
 | 
					            (_, None) => Err(Error::NotATransmitter),
 | 
				
			||||||
 | 
					            (Some(rx_ring), Some(tx_ring)) => Ok((Reader(rx_ring), Writer(tx_ring))),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Read data from the I2S ringbuffer.
 | 
				
			||||||
 | 
					    /// SAI is always receiving data in the background. This function pops already-received data from the buffer.
 | 
				
			||||||
 | 
					    /// If there’s less than data.len() data in the buffer, this waits until there is.
 | 
				
			||||||
 | 
					    pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        match &mut self.rx_ring_buffer {
 | 
				
			||||||
 | 
					            Some(ring) => Reader(ring).read(data).await,
 | 
				
			||||||
 | 
					            _ => Err(Error::NotAReceiver),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write data to the I2S ringbuffer.
 | 
				
			||||||
 | 
					    /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background.
 | 
				
			||||||
 | 
					    /// If thfre’s no space in the buffer, this waits until there is.
 | 
				
			||||||
 | 
					    pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        match &mut self.tx_ring_buffer {
 | 
				
			||||||
 | 
					            Some(ring) => Writer(ring).write(data).await,
 | 
				
			||||||
 | 
					            _ => Err(Error::NotATransmitter),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Write data directly to the raw I2S ringbuffer.
 | 
				
			||||||
 | 
					    /// This can be used to fill the buffer before starting the DMA transfer.
 | 
				
			||||||
 | 
					    pub async fn write_immediate(&mut self, data: &mut [W]) -> Result<(usize, usize), Error> {
 | 
				
			||||||
 | 
					        match &mut self.tx_ring_buffer {
 | 
				
			||||||
 | 
					            Some(ring) => Ok(ring.write_immediate(data)?),
 | 
				
			||||||
 | 
					            _ => return Err(Error::NotATransmitter),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn new_inner<T: Instance>(
 | 
					    fn new_inner<T: Instance>(
 | 
				
			||||||
@ -283,23 +471,23 @@ impl<'d> I2S<'d> {
 | 
				
			|||||||
        rxsd: Option<PeripheralRef<'d, AnyPin>>,
 | 
					        rxsd: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
        ws: impl Peripheral<P = impl WsPin<T>> + 'd,
 | 
					        ws: impl Peripheral<P = impl WsPin<T>> + 'd,
 | 
				
			||||||
        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
					        ck: impl Peripheral<P = impl CkPin<T>> + 'd,
 | 
				
			||||||
        mck: impl Peripheral<P = impl MckPin<T>> + 'd,
 | 
					        mck: Option<PeripheralRef<'d, AnyPin>>,
 | 
				
			||||||
        txdma: Option<ChannelAndRequest<'d>>,
 | 
					        txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,
 | 
				
			||||||
        rxdma: Option<ChannelAndRequest<'d>>,
 | 
					        rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,
 | 
				
			||||||
        freq: Hertz,
 | 
					        freq: Hertz,
 | 
				
			||||||
        config: Config,
 | 
					        config: Config,
 | 
				
			||||||
        function: Function,
 | 
					        function: Function,
 | 
				
			||||||
    ) -> Self {
 | 
					    ) -> Self {
 | 
				
			||||||
        into_ref!(ws, ck, mck);
 | 
					        into_ref!(ws, ck);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 | 
					        ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 | 
				
			||||||
        ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 | 
					        ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 | 
				
			||||||
        mck.set_as_af(mck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut spi_cfg = SpiConfig::default();
 | 
					        let spi = Spi::new_internal(peri, None, None, {
 | 
				
			||||||
        spi_cfg.frequency = freq;
 | 
					            let mut config = SpiConfig::default();
 | 
				
			||||||
 | 
					            config.frequency = freq;
 | 
				
			||||||
        let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg);
 | 
					            config
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let regs = T::info().regs;
 | 
					        let regs = T::info().regs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -390,22 +578,29 @@ impl<'d> I2S<'d> {
 | 
				
			|||||||
                w.set_i2se(true);
 | 
					                w.set_i2se(true);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #[cfg(spi_v3)]
 | 
					            let mut opts = TransferOptions::default();
 | 
				
			||||||
            regs.cr1().modify(|w| w.set_spe(true));
 | 
					            opts.half_transfer_ir = true;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					            Self {
 | 
				
			||||||
            _peri: spi,
 | 
					                mode: config.mode,
 | 
				
			||||||
            txsd: txsd.map(|w| w.map_into()),
 | 
					                spi,
 | 
				
			||||||
            rxsd: rxsd.map(|w| w.map_into()),
 | 
					                txsd: txsd.map(|w| w.map_into()),
 | 
				
			||||||
            ws: Some(ws.map_into()),
 | 
					                rxsd: rxsd.map(|w| w.map_into()),
 | 
				
			||||||
            ck: Some(ck.map_into()),
 | 
					                ws: Some(ws.map_into()),
 | 
				
			||||||
            mck: Some(mck.map_into()),
 | 
					                ck: Some(ck.map_into()),
 | 
				
			||||||
 | 
					                mck: mck.map(|w| w.map_into()),
 | 
				
			||||||
 | 
					                tx_ring_buffer: txdma.map(|(ch, buf)| unsafe {
 | 
				
			||||||
 | 
					                    WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts)
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					                rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe {
 | 
				
			||||||
 | 
					                    ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts)
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'d> Drop for I2S<'d> {
 | 
					impl<'d, W: Word> Drop for I2S<'d, W> {
 | 
				
			||||||
    fn drop(&mut self) {
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
        self.txsd.as_ref().map(|x| x.set_as_disconnected());
 | 
					        self.txsd.as_ref().map(|x| x.set_as_disconnected());
 | 
				
			||||||
        self.rxsd.as_ref().map(|x| x.set_as_disconnected());
 | 
					        self.rxsd.as_ref().map(|x| x.set_as_disconnected());
 | 
				
			||||||
 | 
				
			|||||||
@ -311,8 +311,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Set SPI word size. Disables SPI if needed, you have to enable it back yourself.
 | 
					    pub(crate) fn set_word_size(&mut self, word_size: word_impl::Config) {
 | 
				
			||||||
    fn set_word_size(&mut self, word_size: word_impl::Config) {
 | 
					 | 
				
			||||||
        if self.current_word_size == word_size {
 | 
					        if self.current_word_size == word_size {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -895,7 +894,7 @@ fn compute_frequency(kernel_clock: Hertz, br: Br) -> Hertz {
 | 
				
			|||||||
    kernel_clock / div
 | 
					    kernel_clock / div
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
trait RegsExt {
 | 
					pub(crate) trait RegsExt {
 | 
				
			||||||
    fn tx_ptr<W>(&self) -> *mut W;
 | 
					    fn tx_ptr<W>(&self) -> *mut W;
 | 
				
			||||||
    fn rx_ptr<W>(&self) -> *mut W;
 | 
					    fn rx_ptr<W>(&self) -> *mut W;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -983,7 +982,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn flush_rx_fifo(regs: Regs) {
 | 
					pub(crate) fn flush_rx_fifo(regs: Regs) {
 | 
				
			||||||
    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
					    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
				
			||||||
    while regs.sr().read().rxne() {
 | 
					    while regs.sr().read().rxne() {
 | 
				
			||||||
        #[cfg(not(spi_v2))]
 | 
					        #[cfg(not(spi_v2))]
 | 
				
			||||||
@ -997,7 +996,7 @@ fn flush_rx_fifo(regs: Regs) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn set_txdmaen(regs: Regs, val: bool) {
 | 
					pub(crate) fn set_txdmaen(regs: Regs, val: bool) {
 | 
				
			||||||
    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
					    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
				
			||||||
    regs.cr2().modify(|reg| {
 | 
					    regs.cr2().modify(|reg| {
 | 
				
			||||||
        reg.set_txdmaen(val);
 | 
					        reg.set_txdmaen(val);
 | 
				
			||||||
@ -1008,7 +1007,7 @@ fn set_txdmaen(regs: Regs, val: bool) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn set_rxdmaen(regs: Regs, val: bool) {
 | 
					pub(crate) fn set_rxdmaen(regs: Regs, val: bool) {
 | 
				
			||||||
    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
					    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
 | 
				
			||||||
    regs.cr2().modify(|reg| {
 | 
					    regs.cr2().modify(|reg| {
 | 
				
			||||||
        reg.set_rxdmaen(val);
 | 
					        reg.set_rxdmaen(val);
 | 
				
			||||||
@ -1169,7 +1168,7 @@ impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
trait SealedWord {
 | 
					pub(crate) trait SealedWord {
 | 
				
			||||||
    const CONFIG: word_impl::Config;
 | 
					    const CONFIG: word_impl::Config;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,10 @@
 | 
				
			|||||||
#![no_std]
 | 
					#![no_std]
 | 
				
			||||||
#![no_main]
 | 
					#![no_main]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use core::fmt::Write;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use defmt::*;
 | 
					use defmt::*;
 | 
				
			||||||
use embassy_executor::Spawner;
 | 
					use embassy_executor::Spawner;
 | 
				
			||||||
use embassy_stm32::i2s::{Config, I2S};
 | 
					use embassy_stm32::i2s::{Config, I2S};
 | 
				
			||||||
use embassy_stm32::time::Hertz;
 | 
					use embassy_stm32::time::Hertz;
 | 
				
			||||||
use heapless::String;
 | 
					 | 
				
			||||||
use {defmt_rtt as _, panic_probe as _};
 | 
					use {defmt_rtt as _, panic_probe as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[embassy_executor::main]
 | 
					#[embassy_executor::main]
 | 
				
			||||||
@ -15,6 +12,8 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
    let p = embassy_stm32::init(Default::default());
 | 
					    let p = embassy_stm32::init(Default::default());
 | 
				
			||||||
    info!("Hello World!");
 | 
					    info!("Hello World!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut dma_buffer = [0x00_u16; 128];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut i2s = I2S::new_txonly(
 | 
					    let mut i2s = I2S::new_txonly(
 | 
				
			||||||
        p.SPI2,
 | 
					        p.SPI2,
 | 
				
			||||||
        p.PC3,  // sd
 | 
					        p.PC3,  // sd
 | 
				
			||||||
@ -22,13 +21,13 @@ async fn main(_spawner: Spawner) {
 | 
				
			|||||||
        p.PB10, // ck
 | 
					        p.PB10, // ck
 | 
				
			||||||
        p.PC6,  // mck
 | 
					        p.PC6,  // mck
 | 
				
			||||||
        p.DMA1_CH4,
 | 
					        p.DMA1_CH4,
 | 
				
			||||||
 | 
					        &mut dma_buffer,
 | 
				
			||||||
        Hertz(1_000_000),
 | 
					        Hertz(1_000_000),
 | 
				
			||||||
        Config::default(),
 | 
					        Config::default(),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for n in 0u32.. {
 | 
					    for i in 0_u16.. {
 | 
				
			||||||
        let mut write: String<128> = String::new();
 | 
					        i2s.write(&mut [i * 2; 64]).await.ok();
 | 
				
			||||||
        core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap();
 | 
					        i2s.write(&mut [i * 2 + 1; 64]).await.ok();
 | 
				
			||||||
        i2s.write(&mut write.as_bytes()).await.ok();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user