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}