//! 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 {} impl MCK for PC6 {} impl MCK for PC7 {} /// Trait for word select pins. pub trait WS {} impl WS for PA4 {} impl WS for PA15 {} impl WS for PB9 {} impl WS for PB12 {} impl WS for PI0 {} /// Trait for bit clock pins. pub trait CK {} impl CK for PB3 {} impl CK for PB10 {} impl CK for PB13 {} impl CK for PC10 {} impl CK for PD3 {} impl CK for PI1 {} /// Trait for serial data input pins. pub trait SDI {} impl SDI for PB4 {} impl SDI for PB14 {} impl SDI for PC2 {} impl SDI for PC11 {} impl SDI for PI2 {} /// Trait for serial data output pins. pub trait SDO {} impl SDO for PB5 {} impl SDO for PB15 {} impl SDO for PC3 {} impl SDO for PC12 {} impl SDO for PD6 {} impl SDO for PI3 {} /// I2S interface with DMA data transfer. pub struct I2S { spi: SPI, i2sext: I2SEXT, rx_dma: dma::Stream3, // TODO: Correct DMA unit? tx_dma: dma::Stream4, in_: &'static AudioBuffer, in_packets: [Option; 2], out: &'static AudioBuffer, out_packets: [Option; 2], } macro_rules! i2s_impl { ($SPIX:ident, $I2SXEXT:ident, $spiXen:ident,$spiXrst:ident) => { impl I2S { /// 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, // TODO: Correct DMA streams mut tx_dma: dma::Stream4, 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);