stm32f429_hal/
i2s.rs

1//! I2S bus
2use core::marker::PhantomData;
3
4use stm32f429::{SPI1, SPI2, SPI3};
5
6use gpio::gpioa::PA4;
7use gpio::gpiob::{PB5, PB9, PB10, PB12, PB13, PB14, PB15};
8use gpio::gpioc::{PC2, PC3, PC6, PC7};
9use gpio::{AF5, AF6};
10use rcc::{APB1, APB2, Clocks};
11// use time::{KiloHertz, MegaHertz};
12use dma::*;
13
14/// SD: Serial Data (mapped on the MOSI pin) to transmit or receive
15/// the two time- multiplexed data channels (in half-duplex mode
16/// only).
17pub unsafe trait SdPin<I2S> {}
18// unsafe impl SdPin<I2S1> for PA7<AF5> {}
19// unsafe impl SdPin<I2S1> for PB5<AF5> {}
20unsafe impl SdPin<SPI3> for PB5<AF6> {}
21unsafe impl SdPin<SPI2> for PB15<AF5> {}
22unsafe impl SdPin<SPI2> for PC3<AF5> {}
23
24/// WS: Word Select (mapped on the NSS pin) is the data control signal output in master
25/// mode and input in slave mode.
26pub unsafe trait WsPin<SPI1> {}
27// unsafe impl WsPin<SPI1> for PA4<AF5> {}
28unsafe impl WsPin<SPI3> for PA4<AF6> {}
29// unsafe impl WsPin<SPI1> for PA15<AF5> {}
30// unsafe impl WsPin<SPI3> for PA15<AF6> {}
31unsafe impl WsPin<SPI2> for PB9<AF5> {}
32unsafe impl WsPin<SPI2> for PB12<AF5> {}
33
34/// CK: Serial Clock (mapped on the SCK pin) is the serial clock output in master mode
35/// and serial clock input in slave mode.
36pub unsafe trait CkPin<I2S> {}
37// unsafe impl CkPin<SPI1> for PA5<AF5> {}
38// unsafe impl CkPin<SPI1> for PB3<AF5> {}
39// unsafe impl CkPin<SPI3> for PB3<AF6> {}
40unsafe impl CkPin<SPI2> for PB10<AF5> {}
41unsafe impl CkPin<SPI2> for PB13<AF5> {}
42
43/// SPI2ext_SD and SPI3ext_SD: additional pins (mapped on the MISO pin) to control the
44/// I 2 S full duplex mode.
45pub unsafe trait ExtSdPin<I2S> {}
46// unsafe impl ExtSdPin<SPI1> for PA6<AF5> {}
47// unsafe impl ExtSdPin<SPI1> for PB4<AF5> {}
48// unsafe impl ExtSdPin<SPI3> for PB4<AF6> {}
49unsafe impl ExtSdPin<SPI2> for PB14<AF6> {}
50unsafe impl ExtSdPin<SPI2> for PC2<AF6> {}
51
52/// MCK: Master Clock (mapped separately) is used, when the I 2 S is configured in master
53/// mode (and when the MCKOE bit in the SPI_I2SPR register is set), to output this
54/// additional clock generated at a preconfigured frequency rate equal to 256 × F S , where
55/// F S is the audio sampling frequency.
56pub unsafe trait MckPin<I2S> {}
57unsafe impl MckPin<SPI2> for PC6<AF5> {}
58unsafe impl MckPin<SPI3> for PC7<AF6> {}
59// TODO: continue after PC7
60
61/// Rx direction
62pub struct DmaRx;
63/// Tx direction
64pub struct DmaTx;
65
66/// Possible DMA configuration for an SPI device
67pub unsafe trait I2sDmaStream<STREAM, CHANNEL, DIRECTION> {}
68// DMA: there are only impls for SPI1..3, not 4..6 yet
69unsafe impl I2sDmaStream<SPI3, C0, DmaRx> for dma1::S0 {}
70unsafe impl I2sDmaStream<SPI3, C0, DmaRx> for dma1::S2 {}
71unsafe impl I2sDmaStream<SPI2, C0, DmaRx> for dma1::S3 {}
72unsafe impl I2sDmaStream<SPI2, C0, DmaTx> for dma1::S4 {}
73unsafe impl I2sDmaStream<SPI3, C0, DmaTx> for dma1::S5 {}
74unsafe impl I2sDmaStream<SPI3, C0, DmaTx> for dma1::S7 {}
75unsafe impl I2sDmaStream<SPI1, C3, DmaRx> for dma2::S0 {}
76unsafe impl I2sDmaStream<SPI1, C3, DmaRx> for dma2::S2 {}
77unsafe impl I2sDmaStream<SPI1, C3, DmaTx> for dma2::S3 {}
78unsafe impl I2sDmaStream<SPI1, C3, DmaTx> for dma2::S5 {}
79
80
81/// Slave role (doesn't provide clock)
82pub struct SlaveRole {}
83/// Master role (provides clock)
84pub struct MasterRole {}
85
86/// I2S standard
87pub enum I2sStandard {
88    /// I2S Philips standard.
89    Philips = 0b00,
90    /// MSB justified standard (left justified)
91    MsbJustified = 0b01,
92    /// LSB justified standard (right justified)
93    LsbJustified = 0b10,
94    /// PCM standard
95    Pcm = 0b11,
96}
97
98/// I2S peripheral
99#[allow(unused)]
100pub struct I2s<SPI, SD, CK, WS> {
101    spi: SPI,
102    sd: SD,
103    ck: CK,
104    ws: WS,
105}
106
107/// I2S peripheral
108#[allow(unused)]
109pub struct I2sOutput<Role, Data, SPI, SD, CK, WS> {
110    role: PhantomData<Role>,
111    data: PhantomData<Data>,
112    spi: SPI,
113    sd: SD,
114    ck: CK,
115    ws: WS,
116}
117
118/// Implemented by data types that fit the device's data width: `u16`,
119/// and `u32`.
120pub trait I2sData {
121    /// Value for I2C `datlen` register field.
122    fn datlen() -> u8;
123    /// Run given `f` closure for each 16-bit part of the value.
124    fn for_u16<F: Fn(u16)>(&self, f: F);
125}
126
127impl I2sData for u16 {
128    fn datlen() -> u8 {
129        0b00
130    }
131    #[inline]
132    fn for_u16<F: Fn(u16)>(&self, f: F) {
133        f(*self);
134    }
135}
136
137impl I2sData for u32 {
138    fn datlen() -> u8 {
139        0b10
140    }
141    #[inline]
142    fn for_u16<F: Fn(u16)>(&self, f: F) {
143        f((*self >> 16) as u16);
144        f(*self as u16);
145    }
146}
147
148macro_rules! hal {
149    ($($SPIX:ident: ($spiX:ident, $APBX:ident, $spiXen:ident, $spiXrst:ident),)+) => {
150        $(
151            /// I2S interface on SPI pins
152            impl<SD, CK, WS> I2s<$SPIX, SD, CK, WS> {
153                /// Initialize I2S interface
154                pub fn $spiX(
155                    spi: $SPIX,
156                    sd: SD,
157                    ck: CK,
158                    ws: WS,
159                    _clocks: Clocks,
160                    apb: &mut $APBX,
161                ) -> Self where
162                    SD: SdPin<$SPIX>,
163                    CK: CkPin<$SPIX>,
164                    WS: WsPin<$SPIX>,
165                {
166                    // Enable peripheral
167                    apb.enr().modify(|_, w| w.$spiXen().set_bit());
168                    // Reset peripheral
169                    apb.rstr().modify(|_, w| w.$spiXrst().set_bit());
170                    apb.rstr().modify(|_, w| w.$spiXrst().clear_bit());
171                    
172                    I2s { spi, sd, ck, ws }
173                }
174
175                /// Configure in slave mode as output
176                pub fn into_slave_output<S: I2sData>(self, standard: I2sStandard) -> I2sOutput<SlaveRole, S, $SPIX, SD, CK, WS> {
177                    self.spi.i2scfgr.modify(|_, w| {
178                        unsafe {
179                            // Select I2S mode
180                            w.i2smod().set_bit()
181                                // Configuration (slave, output)
182                                .i2scfg().bits(0b00)
183                                .i2sstd().bits(standard as u8)
184                                // data length
185                                .datlen().bits(S::datlen())
186                                // "auto"
187                                .chlen().clear_bit()
188                        }
189                    });
190                    // If needed, select all the potential interrupt
191                    // sources and the DMA capabilities by writing the
192                    // SPI_CR2 register.
193
194                    // The I2SE bit in SPI_I2SCFGR register must be
195                    // set.
196                    self.spi.i2scfgr.modify(|_, w| w.i2se().set_bit());
197
198                    I2sOutput {
199                        role: PhantomData,
200                        data: PhantomData,
201                        spi: self.spi,
202                        sd: self.sd,
203                        ck: self.ck,
204                        ws: self.ws,
205                    }
206                }
207            }
208
209            impl<'s, Role, S: I2sData + Sized + 's, SD, CK, WS> I2sOutput<Role, S, $SPIX, SD, CK, WS> {
210                /// Disable and return `I2s`
211                pub fn into_i2s(self) -> I2s<$SPIX, SD, CK, WS> {
212                    // Wait
213                    while self.spi.sr.read().bsy().bit() ||
214                        ! self.spi.sr.read().txe().bit() {}
215                    // Disable first
216                    self.spi.i2scfgr.modify(|_, w| w.i2se().clear_bit());
217
218                    I2s {
219                        spi: self.spi,
220                        sd: self.sd,
221                        ck: self.ck,
222                        ws: self.ws,
223                    }
224                }
225
226                /// Write data word
227                pub fn write(&mut self, data: S) {
228                    data.for_u16(|word| {
229                        while ! self.spi.sr.read().txe().bit() {}
230                        self.spi.dr.write(|w| unsafe { w.dr().bits(word) });
231                    });
232                }
233
234                // /// Start writing with DMA
235                // pub fn dma_transfer<X: Transfer<STREAM>, STREAM, C>(&mut self, stream: STREAM, _channel: C, data: (&'s [S], &'s [S])) -> X
236                // where STREAM: DmaStreamTransfer<&'s [S], S, X> + I2sDmaStream<$SPIX, C, DmaTx>,
237                //       C: DmaChannel,
238                // {
239                //     // Let SPI/I2S make a DMA request whenever the TXE flag is set
240                //     self.spi.cr2.modify(|_, w| w.txdmaen().set_bit());
241                //     // Writing a 16-bit register here,
242                //     // even if rust2svd-generated code
243                //     // <T> accesses it as 32-bit aligned.
244                //     let dr: &mut S = unsafe {
245                //         &mut *(&self.spi.dr as *const _ as *mut S)
246                //     };
247
248                //     let source = (data.0, data.1);
249                //     stream.start_transfer::<C>(source, dr)
250                // }
251            }
252        )+
253    }
254}
255
256hal! {
257    SPI1: (spi1, APB2, spi1en, spi1rst),
258    SPI2: (spi2, APB1, spi2en, spi2rst),
259    SPI3: (spi3, APB1, spi3en, spi3rst),
260    // SPI4: (spi4, APB2, spi4en, spi4rst),
261    // SPI5: (spi5, APB2, spi5en, spi5rst),
262    // SPI6: (spi6, APB2, spi6en, spi6rst),
263}