//! I2S bus use core::marker::PhantomData; use stm32f446::{SPI1, SPI2, SPI3}; use gpio::gpioa::PA4; use gpio::gpiob::{PB10, PB12, PB13, PB14, PB15, PB5, PB9}; use gpio::gpioc::{PC2, PC3, PC6, PC7}; use gpio::{AF5, AF6}; use rcc::{APB1, APB2, Clocks}; // use time::{KiloHertz, MegaHertz}; use dma::*; /// SD: Serial Data (mapped on the MOSI pin) to transmit or receive /// the two time- multiplexed data channels (in half-duplex mode /// only). pub unsafe trait SdPin {} // unsafe impl SdPin for PA7 {} // unsafe impl SdPin for PB5 {} unsafe impl SdPin for PB5 {} unsafe impl SdPin for PB15 {} unsafe impl SdPin for PC3 {} /// WS: Word Select (mapped on the NSS pin) is the data control signal output in master /// mode and input in slave mode. pub unsafe trait WsPin {} // unsafe impl WsPin for PA4 {} unsafe impl WsPin for PA4 {} // unsafe impl WsPin for PA15 {} // unsafe impl WsPin for PA15 {} unsafe impl WsPin for PB9 {} unsafe impl WsPin for PB12 {} /// CK: Serial Clock (mapped on the SCK pin) is the serial clock output in master mode /// and serial clock input in slave mode. pub unsafe trait CkPin {} // unsafe impl CkPin for PA5 {} // unsafe impl CkPin for PB3 {} // unsafe impl CkPin for PB3 {} unsafe impl CkPin for PB10 {} unsafe impl CkPin for PB13 {} /// SPI2ext_SD and SPI3ext_SD: additional pins (mapped on the MISO pin) to control the /// I 2 S full duplex mode. pub unsafe trait ExtSdPin {} // unsafe impl ExtSdPin for PA6 {} // unsafe impl ExtSdPin for PB4 {} // unsafe impl ExtSdPin for PB4 {} unsafe impl ExtSdPin for PB14 {} unsafe impl ExtSdPin for PC2 {} /// MCK: Master Clock (mapped separately) is used, when the I 2 S is configured in master /// mode (and when the MCKOE bit in the SPI_I2SPR register is set), to output this /// additional clock generated at a preconfigured frequency rate equal to 256 × F S , where /// F S is the audio sampling frequency. pub unsafe trait MckPin {} unsafe impl MckPin for PC6 {} unsafe impl MckPin for PC7 {} // TODO: continue after PC7 /// Rx direction pub struct DmaRx; /// Tx direction pub struct DmaTx; /// Possible DMA configuration for an SPI device pub unsafe trait I2sDmaStream {} // DMA: there are only impls for SPI1..3, not 4..6 yet unsafe impl I2sDmaStream for dma1::S0 {} unsafe impl I2sDmaStream for dma1::S2 {} unsafe impl I2sDmaStream for dma1::S3 {} unsafe impl I2sDmaStream for dma1::S4 {} unsafe impl I2sDmaStream for dma1::S5 {} unsafe impl I2sDmaStream for dma1::S7 {} unsafe impl I2sDmaStream for dma2::S0 {} unsafe impl I2sDmaStream for dma2::S2 {} unsafe impl I2sDmaStream for dma2::S3 {} unsafe impl I2sDmaStream for dma2::S5 {} /// Slave role (doesn't provide clock) pub struct SlaveRole {} /// Master role (provides clock) pub struct MasterRole {} /// I2S standard pub enum I2sStandard { /// I2S Philips standard. Philips = 0b00, /// MSB justified standard (left justified) MsbJustified = 0b01, /// LSB justified standard (right justified) LsbJustified = 0b10, /// PCM standard Pcm = 0b11, } /// I2S peripheral #[allow(unused)] pub struct I2s { spi: SPI, sd: SD, ck: CK, ws: WS, } /// I2S peripheral #[allow(unused)] pub struct I2sOutput { role: PhantomData, data: PhantomData, spi: SPI, sd: SD, ck: CK, ws: WS, } /// Implemented by data types that fit the device's data width: `u16`, /// and `u32`. pub trait I2sData { /// Value for I2C `datlen` register field. fn datlen() -> u8; /// Run given `f` closure for each 16-bit part of the value. fn for_u16(&self, f: F); } impl I2sData for u16 { fn datlen() -> u8 { 0b00 } #[inline] fn for_u16(&self, f: F) { f(*self); } } impl I2sData for u32 { fn datlen() -> u8 { 0b10 } #[inline] fn for_u16(&self, f: F) { f((*self >> 16) as u16); f(*self as u16); } } macro_rules! hal { ($($SPIX:ident: ($spiX:ident, $APBX:ident, $spiXen:ident, $spiXrst:ident),)+) => { $( /// I2S interface on SPI pins impl I2s<$SPIX, SD, CK, WS> { /// Initialize I2S interface pub fn $spiX( spi: $SPIX, sd: SD, ck: CK, ws: WS, _clocks: Clocks, apb: &mut $APBX, ) -> Self where SD: SdPin<$SPIX>, CK: CkPin<$SPIX>, WS: WsPin<$SPIX>, { // Enable peripheral apb.enr().modify(|_, w| w.$spiXen().set_bit()); // Reset peripheral apb.rstr().modify(|_, w| w.$spiXrst().set_bit()); apb.rstr().modify(|_, w| w.$spiXrst().clear_bit()); I2s { spi, sd, ck, ws } } /// Configure in slave mode as output pub fn into_slave_output(self, standard: I2sStandard) -> I2sOutput { self.spi.i2scfgr.modify(|_, w| { unsafe { // Select I2S mode w.i2smod().set_bit() // Configuration (slave, output) .i2scfg().bits(0b00) .i2sstd().bits(standard as u8) // data length .datlen().bits(S::datlen()) // "auto" .chlen().clear_bit() } }); // If needed, select all the potential interrupt // sources and the DMA capabilities by writing the // SPI_CR2 register. // The I2SE bit in SPI_I2SCFGR register must be // set. self.spi.i2scfgr.modify(|_, w| w.i2se().set_bit()); I2sOutput { role: PhantomData, data: PhantomData, spi: self.spi, sd: self.sd, ck: self.ck, ws: self.ws, } } } impl<'s, Role, S: I2sData + Sized + 's, SD, CK, WS> I2sOutput { /// Disable and return `I2s` pub fn into_i2s(self) -> I2s<$SPIX, SD, CK, WS> { // Wait while self.spi.sr.read().bsy().bit() || ! self.spi.sr.read().txe().bit() {} // Disable first self.spi.i2scfgr.modify(|_, w| w.i2se().clear_bit()); I2s { spi: self.spi, sd: self.sd, ck: self.ck, ws: self.ws, } } /// Write data word pub fn write(&mut self, data: S) { data.for_u16(|word| { while ! self.spi.sr.read().txe().bit() {} self.spi.dr.write(|w| { w.dr().bits(word) }); }); } /// Start writing with DMA pub fn dma_transfer, STREAM, C>(&mut self, stream: STREAM, _channel: C, data: (&'s [S], &'s [S])) -> X where STREAM: DmaStreamTransfer + I2sDmaStream<$SPIX, C, DmaTx>, C: DmaChannel, { // Let SPI/I2S make a DMA request whenever the TXE flag is set self.spi.cr2.modify(|_, w| w.txdmaen().set_bit()); // Writing a 16-bit register here, // even if rust2svd-generated code // accesses it as 32-bit aligned. let dr: &mut u16 = unsafe { &mut *(&self.spi.dr as *const _ as *mut u16) }; stream.start_transfer::(data.0, data.1, dr) } } )+ } } hal! { SPI1: (spi1, APB2, spi1en, spi1rst), SPI2: (spi2, APB1, spi2en, spi2rst), SPI3: (spi3, APB1, spi3en, spi3rst), // SPI4: (spi4, APB2, spi4en, spi4rst), // SPI5: (spi5, APB2, spi5en, spi5rst), // SPI6: (spi6, APB2, spi6en, spi6rst), }