| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- //! I2S interface with DMA data transfer.
-
- use cortex_m::interrupt;
- use stm32f4xx_hal::dma;
- use stm32f4xx_hal::dma::traits::Stream;
- use stm32f4xx_hal::gpio::gpiob::{PB12, PB13, PB14, PB15};
- use stm32f4xx_hal::gpio::gpioc::PC6;
- use stm32f4xx_hal::gpio::{Alternate, AF5, AF6};
- use stm32f4xx_hal::stm32::{DMA1, I2S2EXT, RCC, SPI2};
-
- use crate::audio_buffer::{AudioBuffer, ReadPacket, WritePacket, CHANNELS, SAMPLES_PER_PACKET};
-
- /// Clocks for the I2S peripheral.
- #[derive(Clone, Copy)]
- pub struct I2SClocks {
- plli2sclk: u32,
- }
-
- impl I2SClocks {
- /// Initializes the I2S clocks.
- ///
- /// This function must only be called once at startup. It assumes that the HSE is already
- /// initialized and the HSE frequency is 8MHz.
- pub unsafe fn init() -> I2SClocks {
- // The following values result in a sample frequency of 47991.07Hz:
- // - PLLI2SN = 43
- // - PLLI2SR = 4
- // - I2SDIV = 3
- // - I2SODD = 1
-
- let rcc = &*RCC::ptr();
- // Configure PLL.
- rcc.plli2scfgr
- .modify(|_, w| w.plli2sn().bits(43).plli2sq().bits(4).plli2sr().bits(4));
- // Enable PLL.
- rcc.cr.modify(|_, w| w.plli2son().set_bit());
- // Wait for PLL to stabilise
- while rcc.cr.read().plli2srdy().bit_is_clear() {}
-
- // Ensure that the PLL is selected as the I2S clock source.
- rcc.cfgr.modify(|_, w| w.i2ssrc().clear_bit());
-
- I2SClocks {
- plli2sclk: 86000000,
- }
- }
- }
-
- pub type I2S2 = (SPI2, I2S2EXT);
- pub type I2S3 = (SPI3, I2S3EXT);
-
- /// Trait for master clock pins.
- pub trait MCK<I2S> {}
-
- impl MCK<I2S2> for PC6<Alternate5> {}
- impl MCK<I2S3> for PC7<Alternate6> {}
-
- /// Trait for word select pins.
- pub trait WS<I2S> {}
-
- impl WS<I2S3> for PA4<Alternate6> {}
- impl WS<I2S3> for PA15<Alternate6> {}
- impl WS<I2S2> for PB9<Alternate5> {}
- impl WS<I2S2> for PB12<Alternate5> {}
- impl WS<I2S2> for PI0<Alternate5> {}
-
- /// Trait for bit clock pins.
- pub trait CK<I2S> {}
-
- impl CK<I2S3> for PB3<Alternate6> {}
- impl CK<I2S2> for PB10<Alternate5> {}
- impl CK<I2S2> for PB13<Alternate5> {}
- impl CK<I2S3> for PC10<Alternate6> {}
- impl CK<I2S2> for PD3<Alternate5> {}
- impl CK<I2S2> for PI1<Alternate5> {}
-
- /// Trait for serial data input pins.
- pub trait SDI<I2S> {}
-
- impl SDI<I2S3> for PB4<Alternate7> {}
- impl SDI<I2S2> for PB14<Alternate6> {}
- impl SDI<I2S2> for PC2<Alternate6> {}
- impl SDI<I2S3> for PC11<Alternate5> {}
- impl SDI<I2S2> for PI2<Alternate6> {}
-
- /// Trait for serial data output pins.
- pub trait SDO<I2S> {}
-
- impl SDO<I2S3> for PB5<Alternate6> {}
- impl SDO<I2S2> for PB15<Alternate5> {}
- impl SDO<I2S2> for PC3<Alternate5> {}
- impl SDO<I2S3> for PC12<Alternate6> {}
- impl SDO<I2S3> for PD6<Alternate5> {}
- impl SDO<I2S2> for PI3<Alternate5> {}
-
- /// I2S interface with DMA data transfer.
- pub struct I2S<SPI, I2SEXT> {
- spi: SPI,
- i2sext: I2SEXT,
- rx_dma: dma::Stream3<DMA1>, // TODO: Correct DMA unit?
- tx_dma: dma::Stream4<DMA1>,
- in_: &'static AudioBuffer,
- in_packets: [Option<WritePacket>; 2],
- out: &'static AudioBuffer,
- out_packets: [Option<ReadPacket>; 2],
- }
-
- macro_rules! i2s_impl {
- ($SPIX:ident, $I2SXEXT:ident, $spiXen:ident,$spiXrst:ident) => {
- impl I2S<SPIX, I2SXEXT> {
- /// Initializes the I2S interface to read from/write to the specified buffers.
- ///
- /// The interface is only started when the `start()` function is called.
- pub fn new(
- spi: SPIX,
- i2sext: I2SXEXT,
- mut rx_dma: dma::Stream3<DMA1>, // TODO: Correct DMA streams
- mut tx_dma: dma::Stream4<DMA1>,
- mck: MCK<(SPIX, I2SXEXT)>,
- ws: WS<(SPIX, I2SXEXT)>,
- ck: CK<(SPIX, I2SXEXT)>,
- sdin: SDI<(SPIX, I2SXEXT)>,
- sdout: SDO<(SPIX, I2SXEXT)>,
- in_: &'static AudioBuffer,
- out: &'static AudioBuffer,
- _clocks: I2SClocks,
- ) -> Self {
- // Enable and reset the peripheral.
- let rcc = unsafe { &*RCC::ptr() };
- rcc.apb1enr.modify(|_, w| w.spi2en().set_bit());
- rcc.apb1rstr.modify(|_, w| w.spi2rst().set_bit());
- rcc.apb1rstr.modify(|_, w| w.spi2rst().clear_bit());
- //rcc.ahb1enr.modify(|_, w| w.dma1en().set_bit());
-
- // Disable SS output.
- spi.cr2.write(|w| w.ssoe().clear_bit());
-
- // Configure the pins.
- // TODO
-
- // Configure the clocks.
- // For the values, see the comment in I2SClocks::init().
- spi.i2spr
- .write(|w| unsafe { w.mckoe().set_bit().odd().set_bit().i2sdiv().bits(3) });
- i2sext
- .i2spr
- .write(|w| unsafe { w.mckoe().set_bit().odd().set_bit().i2sdiv().bits(3) });
-
- // Configure I2S.
- spi.i2scfgr.modify(|_, w| unsafe {
- w.i2smod()
- .set_bit()
- .i2se()
- .clear_bit()
- .i2scfg()
- .master_tx()
- .i2sstd()
- .bits(0b01)
- .ckpol()
- .idle_high()
- .datlen()
- .bits(0b10)
- .chlen()
- .set_bit()
- });
- i2sext.i2scfgr.modify(|_, w| unsafe {
- w.i2smod()
- .set_bit()
- .i2se()
- .clear_bit()
- .i2scfg()
- .slave_rx()
- .i2sstd()
- .bits(0b01)
- .ckpol()
- .idle_high()
- .datlen()
- .bits(0b10)
- .chlen()
- .set_bit()
- });
- i2sext.cr2.modify(|_, w| w.rxdmaen().set_bit());
- spi.cr2.modify(|_, w| w.txdmaen().set_bit());
-
- rx_dma.set_channel(dma::Channel3);
- rx_dma.clear_interrupts();
- unsafe {
- rx_dma.set_memory_size(1);
- rx_dma.set_peripheral_size(1);
- }
- rx_dma.set_memory_increment(true);
- rx_dma.set_direction(dma::PeripheralToMemory);
- rx_dma.set_interrupts_enable(true, false, true, true);
- rx_dma.set_double_buffer(true);
- rx_dma.set_fifo_enable(false);
- rx_dma.set_number_of_transfers((SAMPLES_PER_PACKET * CHANNELS * 2) as u16);
-
- tx_dma.set_channel(dma::Channel0);
- tx_dma.clear_interrupts();
- unsafe {
- tx_dma.set_memory_size(1);
- tx_dma.set_peripheral_size(1);
- }
- tx_dma.set_memory_increment(true);
- tx_dma.set_direction(dma::MemoryToPeripheral);
- tx_dma.set_interrupts_enable(true, false, true, true);
- tx_dma.set_double_buffer(true);
- tx_dma.set_fifo_enable(false);
- tx_dma.set_number_of_transfers((SAMPLES_PER_PACKET * CHANNELS * 2) as u16);
-
- const DR_OFFSET: usize = 0x0c;
- let spi_dr = SPI2::ptr() as usize + DR_OFFSET;
- let i2sext_dr = I2S2EXT::ptr() as usize + DR_OFFSET;
- rx_dma.set_peripheral_address(i2sext_dr as u32);
- tx_dma.set_peripheral_address(spi_dr as u32);
-
- I2S {
- spi,
- i2sext,
- rx_dma,
- tx_dma,
- in_,
- in_packets: [None, None],
- out,
- out_packets: [None, None],
- }
- }
-
- /// Starts the data stream of the I2S interface.
- pub fn start(&mut self) {
- interrupt::free(|cs| {
- for i in 0..2 {
- self.in_packets[i] = self.in_.borrow_write(cs);
- }
- for i in 0..2 {
- self.out_packets[i] = self.out.borrow_read(cs);
- }
- });
-
- // The system was just started, and some buffers were filled with dummy data, so we can
- // assume that borrowing was successful.
- let in_0 = self.in_packets[0].as_mut().unwrap().as_mut().as_ptr() as usize as u32;
- let in_1 = self.in_packets[1].as_mut().unwrap().as_mut().as_ptr() as usize as u32;
- self.rx_dma.set_memory_address(in_0);
- self.rx_dma.set_memory_double_buffer_address(in_1);
- /*self.dma.st[3].m0ar.write(|w| unsafe { w.bits(in_0) });
- self.dma.st[3].m1ar.write(|w| unsafe { w.bits(in_1) });*/
- let out_0 = self.out_packets[0].as_ref().unwrap().as_ref().as_ptr() as usize as u32;
- let out_1 = self.out_packets[1].as_ref().unwrap().as_ref().as_ptr() as usize as u32;
- self.tx_dma.set_memory_address(out_0);
- self.tx_dma.set_memory_double_buffer_address(out_1);
- /*self.dma.st[4].m0ar.write(|w| unsafe { w.bits(out_0) });
- self.dma.st[4].m1ar.write(|w| unsafe { w.bits(out_1) });*/
-
- // Enable DMA and activate the I2S peripherals.
- /*self.dma.st[3].cr.modify(|_, w| w.en().set_bit());
- self.dma.st[4].cr.modify(|_, w| w.en().set_bit());*/
- unsafe {
- self.rx_dma.enable();
- self.tx_dma.enable();
- }
- self.i2sext.i2scfgr.modify(|_, w| w.i2se().set_bit());
- self.spi.i2scfgr.modify(|_, w| w.i2se().set_bit());
-
- /*loop {
- if self.spi.sr.read().txe().bit_is_set() {
- self.spi.dr.write(|w| unsafe { w.bits(0xaaaa) });
- }
- if self.i2sext.sr.read().rxne().bit_is_set() {
- self.i2sext.dr.read();
- }
- }*/
- }
-
- /// Reads data from the output audio buffer and writes to the input audio buffer as required.
- ///
- /// This function needs to be called from the DMA interrupt handler.
- pub fn poll(&mut self) {
- // TODO
- }
- }
- // TODO
- };
- }
-
- i2s_impl!(SPI2, I2S2EXT, spi2en, spi2rst);
- i2s_impl!(SPI3, I2S3EXT, spi3en, spi3rst);
|