aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/i2s.rs339
-rw-r--r--embassy-stm32/src/spi/mod.rs13
-rw-r--r--examples/stm32f4/src/bin/i2s_dma.rs13
3 files changed, 279 insertions, 86 deletions
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs
index 094de2461..f11371f98 100644
--- a/embassy-stm32/src/i2s.rs
+++ b/embassy-stm32/src/i2s.rs
@@ -1,12 +1,13 @@
1//! Inter-IC Sound (I2S) 1//! Inter-IC Sound (I2S)
2 2
3use embassy_futures::join::join;
3use embassy_hal_internal::into_ref; 4use embassy_hal_internal::into_ref;
5use stm32_metapac::spi::vals;
4 6
5use crate::dma::ChannelAndRequest; 7use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer};
6use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; 8use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
7use crate::mode::Async; 9use crate::mode::Async;
8use crate::pac::spi::vals; 10use crate::spi::{Config as SpiConfig, RegsExt as _, *};
9use crate::spi::{Config as SpiConfig, *};
10use crate::time::Hertz; 11use crate::time::Hertz;
11use crate::{Peripheral, PeripheralRef}; 12use crate::{Peripheral, PeripheralRef};
12 13
@@ -19,6 +20,19 @@ pub enum Mode {
19 Slave, 20 Slave,
20} 21}
21 22
23/// I2S function
24#[derive(Copy, Clone)]
25#[allow(dead_code)]
26enum Function {
27 /// Transmit audio data
28 Transmit,
29 /// Receive audio data
30 Receive,
31 #[cfg(spi_v3)]
32 /// Transmit and Receive audio data
33 FullDuplex,
34}
35
22/// I2C standard 36/// I2C standard
23#[derive(Copy, Clone)] 37#[derive(Copy, Clone)]
24pub enum Standard { 38pub enum Standard {
@@ -34,6 +48,30 @@ pub enum Standard {
34 PcmShortSync, 48 PcmShortSync,
35} 49}
36 50
51/// SAI error
52#[derive(Debug, PartialEq, Eq)]
53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54pub enum Error {
55 /// `write` called on a SAI in receive mode.
56 NotATransmitter,
57 /// `read` called on a SAI in transmit mode.
58 NotAReceiver,
59 /// Overrun
60 Overrun,
61}
62
63impl From<ringbuffer::Error> for Error {
64 fn from(#[allow(unused)] err: ringbuffer::Error) -> Self {
65 #[cfg(feature = "defmt")]
66 {
67 if err == ringbuffer::Error::DmaUnsynced {
68 defmt::error!("Ringbuffer broken invariants detected!");
69 }
70 }
71 Self::Overrun
72 }
73}
74
37impl Standard { 75impl Standard {
38 #[cfg(any(spi_v1, spi_v3, spi_f1))] 76 #[cfg(any(spi_v1, spi_v3, spi_f1))]
39 const fn i2sstd(&self) -> vals::I2sstd { 77 const fn i2sstd(&self) -> vals::I2sstd {
@@ -142,31 +180,62 @@ impl Default for Config {
142 } 180 }
143} 181}
144 182
183/// I2S driver writer. Useful for moving write functionality across tasks.
184pub struct Writer<'s, 'd, W: Word>(&'s mut WritableRingBuffer<'d, W>);
185
186impl<'s, 'd, W: Word> Writer<'s, 'd, W> {
187 /// Write data to the I2S ringbuffer.
188 /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background.
189 /// If thfre’s no space in the buffer, this waits until there is.
190 pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
191 self.0.write_exact(data).await?;
192 Ok(())
193 }
194
195 /// Reset the ring buffer to its initial state.
196 /// Can be used to recover from overrun.
197 /// The ringbuffer will always auto-reset on Overrun in any case.
198 pub fn reset(&mut self) {
199 self.0.clear();
200 }
201}
202
203/// I2S driver reader. Useful for moving read functionality across tasks.
204pub struct Reader<'s, 'd, W: Word>(&'s mut ReadableRingBuffer<'d, W>);
205
206impl<'s, 'd, W: Word> Reader<'s, 'd, W> {
207 /// Read data from the I2S ringbuffer.
208 /// SAI is always receiving data in the background. This function pops already-received data from the buffer.
209 /// If there’s less than data.len() data in the buffer, this waits until there is.
210 pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
211 self.0.read_exact(data).await?;
212 Ok(())
213 }
214
215 /// Reset the ring buffer to its initial state.
216 /// Can be used to prevent overrun.
217 /// The ringbuffer will always auto-reset on Overrun in any case.
218 pub fn reset(&mut self) {
219 self.0.clear();
220 }
221}
222
145/// I2S driver. 223/// I2S driver.
146pub struct I2S<'d> { 224pub struct I2S<'d, W: Word> {
147 _peri: Spi<'d, Async>, 225 #[allow(dead_code)]
226 mode: Mode,
227 spi: Spi<'d, Async>,
148 txsd: Option<PeripheralRef<'d, AnyPin>>, 228 txsd: Option<PeripheralRef<'d, AnyPin>>,
149 rxsd: Option<PeripheralRef<'d, AnyPin>>, 229 rxsd: Option<PeripheralRef<'d, AnyPin>>,
150 ws: Option<PeripheralRef<'d, AnyPin>>, 230 ws: Option<PeripheralRef<'d, AnyPin>>,
151 ck: Option<PeripheralRef<'d, AnyPin>>, 231 ck: Option<PeripheralRef<'d, AnyPin>>,
152 mck: Option<PeripheralRef<'d, AnyPin>>, 232 mck: Option<PeripheralRef<'d, AnyPin>>,
233 tx_ring_buffer: Option<WritableRingBuffer<'d, W>>,
234 rx_ring_buffer: Option<ReadableRingBuffer<'d, W>>,
153} 235}
154 236
155/// I2S function 237impl<'d, W: Word> I2S<'d, W> {
156#[derive(Copy, Clone)] 238 /// Create a transmitter driver.
157#[allow(dead_code)]
158enum Function {
159 /// Transmit audio data
160 Transmit,
161 /// Receive audio data
162 Receive,
163 #[cfg(spi_v3)]
164 /// Transmit and Receive audio data
165 FullDuplex,
166}
167
168impl<'d> I2S<'d> {
169 /// Create a transmitter driver
170 pub fn new_txonly<T: Instance>( 239 pub fn new_txonly<T: Instance>(
171 peri: impl Peripheral<P = T> + 'd, 240 peri: impl Peripheral<P = T> + 'd,
172 sd: impl Peripheral<P = impl MosiPin<T>> + 'd, 241 sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
@@ -174,18 +243,18 @@ impl<'d> I2S<'d> {
174 ck: impl Peripheral<P = impl CkPin<T>> + 'd, 243 ck: impl Peripheral<P = impl CkPin<T>> + 'd,
175 mck: impl Peripheral<P = impl MckPin<T>> + 'd, 244 mck: impl Peripheral<P = impl MckPin<T>> + 'd,
176 txdma: impl Peripheral<P = impl TxDma<T>> + 'd, 245 txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
246 txdma_buf: &'d mut [W],
177 freq: Hertz, 247 freq: Hertz,
178 config: Config, 248 config: Config,
179 ) -> Self { 249 ) -> Self {
180 into_ref!(sd);
181 Self::new_inner( 250 Self::new_inner(
182 peri, 251 peri,
183 new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 252 new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
184 None, 253 None,
185 ws, 254 ws,
186 ck, 255 ck,
187 mck, 256 new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
188 new_dma!(txdma), 257 new_dma!(txdma).map(|d| (d, txdma_buf)),
189 None, 258 None,
190 freq, 259 freq,
191 config, 260 config,
@@ -193,42 +262,61 @@ impl<'d> I2S<'d> {
193 ) 262 )
194 } 263 }
195 264
196 /// Create a receiver driver 265 /// Create a transmitter driver without a master clock pin.
266 pub fn new_txonly_nomck<T: Instance>(
267 peri: impl Peripheral<P = T> + 'd,
268 sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
269 ws: impl Peripheral<P = impl WsPin<T>> + 'd,
270 ck: impl Peripheral<P = impl CkPin<T>> + 'd,
271 txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
272 txdma_buf: &'d mut [W],
273 freq: Hertz,
274 config: Config,
275 ) -> Self {
276 Self::new_inner(
277 peri,
278 new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
279 None,
280 ws,
281 ck,
282 None,
283 new_dma!(txdma).map(|d| (d, txdma_buf)),
284 None,
285 freq,
286 config,
287 Function::Transmit,
288 )
289 }
290
291 /// Create a receiver driver.
197 pub fn new_rxonly<T: Instance>( 292 pub fn new_rxonly<T: Instance>(
198 peri: impl Peripheral<P = T> + 'd, 293 peri: impl Peripheral<P = T> + 'd,
199 sd: impl Peripheral<P = impl MisoPin<T>> + 'd, 294 sd: impl Peripheral<P = impl MisoPin<T>> + 'd,
200 ws: impl Peripheral<P = impl WsPin<T>> + 'd, 295 ws: impl Peripheral<P = impl WsPin<T>> + 'd,
201 ck: impl Peripheral<P = impl CkPin<T>> + 'd, 296 ck: impl Peripheral<P = impl CkPin<T>> + 'd,
202 mck: impl Peripheral<P = impl MckPin<T>> + 'd, 297 mck: impl Peripheral<P = impl MckPin<T>> + 'd,
203 #[cfg(not(spi_v3))] txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
204 rxdma: impl Peripheral<P = impl RxDma<T>> + 'd, 298 rxdma: impl Peripheral<P = impl RxDma<T>> + 'd,
299 rxdma_buf: &'d mut [W],
205 freq: Hertz, 300 freq: Hertz,
206 config: Config, 301 config: Config,
207 ) -> Self { 302 ) -> Self {
208 into_ref!(sd);
209 Self::new_inner( 303 Self::new_inner(
210 peri, 304 peri,
211 None, 305 None,
212 new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 306 new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
213 ws, 307 ws,
214 ck, 308 ck,
215 mck, 309 new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
216 #[cfg(not(spi_v3))]
217 new_dma!(txdma),
218 #[cfg(spi_v3)]
219 None, 310 None,
220 new_dma!(rxdma), 311 new_dma!(rxdma).map(|d| (d, rxdma_buf)),
221 freq, 312 freq,
222 config, 313 config,
223 #[cfg(not(spi_v3))]
224 Function::Transmit,
225 #[cfg(spi_v3)]
226 Function::Receive, 314 Function::Receive,
227 ) 315 )
228 } 316 }
229 317
230 #[cfg(spi_v3)] 318 #[cfg(spi_v3)]
231 /// Create a full duplex transmitter driver 319 /// Create a full duplex driver.
232 pub fn new_full_duplex<T: Instance>( 320 pub fn new_full_duplex<T: Instance>(
233 peri: impl Peripheral<P = T> + 'd, 321 peri: impl Peripheral<P = T> + 'd,
234 txsd: impl Peripheral<P = impl MosiPin<T>> + 'd, 322 txsd: impl Peripheral<P = impl MosiPin<T>> + 'd,
@@ -237,44 +325,144 @@ impl<'d> I2S<'d> {
237 ck: impl Peripheral<P = impl CkPin<T>> + 'd, 325 ck: impl Peripheral<P = impl CkPin<T>> + 'd,
238 mck: impl Peripheral<P = impl MckPin<T>> + 'd, 326 mck: impl Peripheral<P = impl MckPin<T>> + 'd,
239 txdma: impl Peripheral<P = impl TxDma<T>> + 'd, 327 txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
328 txdma_buf: &'d mut [W],
240 rxdma: impl Peripheral<P = impl RxDma<T>> + 'd, 329 rxdma: impl Peripheral<P = impl RxDma<T>> + 'd,
330 rxdma_buf: &'d mut [W],
241 freq: Hertz, 331 freq: Hertz,
242 config: Config, 332 config: Config,
243 ) -> Self { 333 ) -> Self {
244 into_ref!(txsd, rxsd);
245 Self::new_inner( 334 Self::new_inner(
246 peri, 335 peri,
247 new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 336 new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
248 new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), 337 new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
249 ws, 338 ws,
250 ck, 339 ck,
251 mck, 340 new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
252 new_dma!(txdma), 341 new_dma!(txdma).map(|d| (d, txdma_buf)),
253 new_dma!(rxdma), 342 new_dma!(rxdma).map(|d| (d, rxdma_buf)),
254 freq, 343 freq,
255 config, 344 config,
256 Function::FullDuplex, 345 Function::FullDuplex,
257 ) 346 )
258 } 347 }
259 348
260 /// Write audio data. 349 /// Start I2S driver.
261 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 350 pub fn start(&mut self) {
262 self._peri.read(data).await 351 self.spi.info.regs.cr1().modify(|w| {
352 w.set_spe(false);
353 });
354 self.spi.set_word_size(W::CONFIG);
355 if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
356 tx_ring_buffer.start();
357
358 set_txdmaen(self.spi.info.regs, true);
359 }
360 if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
361 rx_ring_buffer.start();
362 // SPIv3 clears rxfifo on SPE=0
363 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
364 flush_rx_fifo(self.spi.info.regs);
365
366 set_rxdmaen(self.spi.info.regs, true);
367 }
368 self.spi.info.regs.cr1().modify(|w| {
369 w.set_spe(true);
370 });
371 #[cfg(any(spi_v3, spi_v4, spi_v5))]
372 self.spi.info.regs.cr1().modify(|w| {
373 w.set_cstart(true);
374 });
375 }
376
377 /// Reset the ring buffer to its initial state.
378 /// Can be used to recover from overrun.
379 pub fn clear(&mut self) {
380 if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
381 rx_ring_buffer.clear();
382 }
383 if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
384 tx_ring_buffer.clear();
385 }
386 }
387
388 /// Stop I2S driver.
389 pub async fn stop(&mut self) {
390 let regs = self.spi.info.regs;
391
392 let tx_f = async {
393 if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
394 tx_ring_buffer.stop().await;
395
396 set_txdmaen(regs, false);
397 }
398 };
399
400 let rx_f = async {
401 if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
402 rx_ring_buffer.stop().await;
403
404 set_rxdmaen(regs, false);
405 }
406 };
407
408 join(rx_f, tx_f).await;
409
410 #[cfg(any(spi_v3, spi_v4, spi_v5))]
411 {
412 if let Mode::Master = self.mode {
413 regs.cr1().modify(|w| {
414 w.set_csusp(true);
415 });
416
417 while regs.cr1().read().cstart() {}
418 }
419 }
420
421 regs.cr1().modify(|w| {
422 w.set_spe(false);
423 });
424
425 self.clear();
426 }
427
428 /// Split the driver into a Reader/Writer pair.
429 /// Useful for splitting the reader/writer functionality across tasks or
430 /// for calling the read/write methods in parallel.
431 pub fn split<'s>(&'s mut self) -> Result<(Reader<'s, 'd, W>, Writer<'s, 'd, W>), Error> {
432 match (&mut self.rx_ring_buffer, &mut self.tx_ring_buffer) {
433 (None, _) => Err(Error::NotAReceiver),
434 (_, None) => Err(Error::NotATransmitter),
435 (Some(rx_ring), Some(tx_ring)) => Ok((Reader(rx_ring), Writer(tx_ring))),
436 }
263 } 437 }
264 438
265 /// Write audio data. 439 /// Read data from the I2S ringbuffer.
266 pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { 440 /// SAI is always receiving data in the background. This function pops already-received data from the buffer.
267 self._peri.write(data).await 441 /// If there’s less than data.len() data in the buffer, this waits until there is.
442 pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
443 match &mut self.rx_ring_buffer {
444 Some(ring) => Reader(ring).read(data).await,
445 _ => Err(Error::NotAReceiver),
446 }
268 } 447 }
269 448
270 /// Transfer audio data. 449 /// Write data to the I2S ringbuffer.
271 pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { 450 /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background.
272 self._peri.transfer(read, write).await 451 /// If thfre’s no space in the buffer, this waits until there is.
452 pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
453 match &mut self.tx_ring_buffer {
454 Some(ring) => Writer(ring).write(data).await,
455 _ => Err(Error::NotATransmitter),
456 }
273 } 457 }
274 458
275 /// Transfer audio data in place. 459 /// Write data directly to the raw I2S ringbuffer.
276 pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 460 /// This can be used to fill the buffer before starting the DMA transfer.
277 self._peri.transfer_in_place(data).await 461 pub async fn write_immediate(&mut self, data: &mut [W]) -> Result<(usize, usize), Error> {
462 match &mut self.tx_ring_buffer {
463 Some(ring) => Ok(ring.write_immediate(data)?),
464 _ => return Err(Error::NotATransmitter),
465 }
278 } 466 }
279 467
280 fn new_inner<T: Instance>( 468 fn new_inner<T: Instance>(
@@ -283,23 +471,23 @@ impl<'d> I2S<'d> {
283 rxsd: Option<PeripheralRef<'d, AnyPin>>, 471 rxsd: Option<PeripheralRef<'d, AnyPin>>,
284 ws: impl Peripheral<P = impl WsPin<T>> + 'd, 472 ws: impl Peripheral<P = impl WsPin<T>> + 'd,
285 ck: impl Peripheral<P = impl CkPin<T>> + 'd, 473 ck: impl Peripheral<P = impl CkPin<T>> + 'd,
286 mck: impl Peripheral<P = impl MckPin<T>> + 'd, 474 mck: Option<PeripheralRef<'d, AnyPin>>,
287 txdma: Option<ChannelAndRequest<'d>>, 475 txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,
288 rxdma: Option<ChannelAndRequest<'d>>, 476 rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,
289 freq: Hertz, 477 freq: Hertz,
290 config: Config, 478 config: Config,
291 function: Function, 479 function: Function,
292 ) -> Self { 480 ) -> Self {
293 into_ref!(ws, ck, mck); 481 into_ref!(ws, ck);
294 482
295 ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 483 ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
296 ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); 484 ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
297 mck.set_as_af(mck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
298 485
299 let mut spi_cfg = SpiConfig::default(); 486 let spi = Spi::new_internal(peri, None, None, {
300 spi_cfg.frequency = freq; 487 let mut config = SpiConfig::default();
301 488 config.frequency = freq;
302 let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); 489 config
490 });
303 491
304 let regs = T::info().regs; 492 let regs = T::info().regs;
305 493
@@ -390,22 +578,29 @@ impl<'d> I2S<'d> {
390 w.set_i2se(true); 578 w.set_i2se(true);
391 }); 579 });
392 580
393 #[cfg(spi_v3)] 581 let mut opts = TransferOptions::default();
394 regs.cr1().modify(|w| w.set_spe(true)); 582 opts.half_transfer_ir = true;
395 } 583
396 584 Self {
397 Self { 585 mode: config.mode,
398 _peri: spi, 586 spi,
399 txsd: txsd.map(|w| w.map_into()), 587 txsd: txsd.map(|w| w.map_into()),
400 rxsd: rxsd.map(|w| w.map_into()), 588 rxsd: rxsd.map(|w| w.map_into()),
401 ws: Some(ws.map_into()), 589 ws: Some(ws.map_into()),
402 ck: Some(ck.map_into()), 590 ck: Some(ck.map_into()),
403 mck: Some(mck.map_into()), 591 mck: mck.map(|w| w.map_into()),
592 tx_ring_buffer: txdma.map(|(ch, buf)| unsafe {
593 WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts)
594 }),
595 rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe {
596 ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts)
597 }),
598 }
404 } 599 }
405 } 600 }
406} 601}
407 602
408impl<'d> Drop for I2S<'d> { 603impl<'d, W: Word> Drop for I2S<'d, W> {
409 fn drop(&mut self) { 604 fn drop(&mut self) {
410 self.txsd.as_ref().map(|x| x.set_as_disconnected()); 605 self.txsd.as_ref().map(|x| x.set_as_disconnected());
411 self.rxsd.as_ref().map(|x| x.set_as_disconnected()); 606 self.rxsd.as_ref().map(|x| x.set_as_disconnected());
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index d034c028e..a65b0cc64 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -311,8 +311,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
311 } 311 }
312 } 312 }
313 313
314 /// Set SPI word size. Disables SPI if needed, you have to enable it back yourself. 314 pub(crate) fn set_word_size(&mut self, word_size: word_impl::Config) {
315 fn set_word_size(&mut self, word_size: word_impl::Config) {
316 if self.current_word_size == word_size { 315 if self.current_word_size == word_size {
317 return; 316 return;
318 } 317 }
@@ -895,7 +894,7 @@ fn compute_frequency(kernel_clock: Hertz, br: Br) -> Hertz {
895 kernel_clock / div 894 kernel_clock / div
896} 895}
897 896
898trait RegsExt { 897pub(crate) trait RegsExt {
899 fn tx_ptr<W>(&self) -> *mut W; 898 fn tx_ptr<W>(&self) -> *mut W;
900 fn rx_ptr<W>(&self) -> *mut W; 899 fn rx_ptr<W>(&self) -> *mut W;
901} 900}
@@ -983,7 +982,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
983 } 982 }
984} 983}
985 984
986fn flush_rx_fifo(regs: Regs) { 985pub(crate) fn flush_rx_fifo(regs: Regs) {
987 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 986 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
988 while regs.sr().read().rxne() { 987 while regs.sr().read().rxne() {
989 #[cfg(not(spi_v2))] 988 #[cfg(not(spi_v2))]
@@ -997,7 +996,7 @@ fn flush_rx_fifo(regs: Regs) {
997 } 996 }
998} 997}
999 998
1000fn set_txdmaen(regs: Regs, val: bool) { 999pub(crate) fn set_txdmaen(regs: Regs, val: bool) {
1001 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1000 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
1002 regs.cr2().modify(|reg| { 1001 regs.cr2().modify(|reg| {
1003 reg.set_txdmaen(val); 1002 reg.set_txdmaen(val);
@@ -1008,7 +1007,7 @@ fn set_txdmaen(regs: Regs, val: bool) {
1008 }); 1007 });
1009} 1008}
1010 1009
1011fn set_rxdmaen(regs: Regs, val: bool) { 1010pub(crate) fn set_rxdmaen(regs: Regs, val: bool) {
1012 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1011 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
1013 regs.cr2().modify(|reg| { 1012 regs.cr2().modify(|reg| {
1014 reg.set_rxdmaen(val); 1013 reg.set_rxdmaen(val);
@@ -1169,7 +1168,7 @@ impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> {
1169 } 1168 }
1170} 1169}
1171 1170
1172trait SealedWord { 1171pub(crate) trait SealedWord {
1173 const CONFIG: word_impl::Config; 1172 const CONFIG: word_impl::Config;
1174} 1173}
1175 1174
diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs
index 27b165f1b..68392847b 100644
--- a/examples/stm32f4/src/bin/i2s_dma.rs
+++ b/examples/stm32f4/src/bin/i2s_dma.rs
@@ -1,13 +1,10 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::fmt::Write;
5
6use defmt::*; 4use defmt::*;
7use embassy_executor::Spawner; 5use embassy_executor::Spawner;
8use embassy_stm32::i2s::{Config, I2S}; 6use embassy_stm32::i2s::{Config, I2S};
9use embassy_stm32::time::Hertz; 7use embassy_stm32::time::Hertz;
10use heapless::String;
11use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
12 9
13#[embassy_executor::main] 10#[embassy_executor::main]
@@ -15,6 +12,8 @@ async fn main(_spawner: Spawner) {
15 let p = embassy_stm32::init(Default::default()); 12 let p = embassy_stm32::init(Default::default());
16 info!("Hello World!"); 13 info!("Hello World!");
17 14
15 let mut dma_buffer = [0x00_u16; 128];
16
18 let mut i2s = I2S::new_txonly( 17 let mut i2s = I2S::new_txonly(
19 p.SPI2, 18 p.SPI2,
20 p.PC3, // sd 19 p.PC3, // sd
@@ -22,13 +21,13 @@ async fn main(_spawner: Spawner) {
22 p.PB10, // ck 21 p.PB10, // ck
23 p.PC6, // mck 22 p.PC6, // mck
24 p.DMA1_CH4, 23 p.DMA1_CH4,
24 &mut dma_buffer,
25 Hertz(1_000_000), 25 Hertz(1_000_000),
26 Config::default(), 26 Config::default(),
27 ); 27 );
28 28
29 for n in 0u32.. { 29 for i in 0_u16.. {
30 let mut write: String<128> = String::new(); 30 i2s.write(&mut [i * 2; 64]).await.ok();
31 core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); 31 i2s.write(&mut [i * 2 + 1; 64]).await.ok();
32 i2s.write(&mut write.as_bytes()).await.ok();
33 } 32 }
34} 33}