Skip to content

Commit 9f35d2a

Browse files
committed
convert I2S more like SAI and add dma ringbuffer.
1 parent 30fddad commit 9f35d2a

File tree

2 files changed

+138
-127
lines changed

2 files changed

+138
-127
lines changed

embassy-stm32/src/i2s.rs

Lines changed: 137 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Inter-IC Sound (I2S)
22
use embassy_hal_internal::into_ref;
3-
use stm32_metapac::SPI3;
43

4+
use crate::dma::{ringbuffer, word, Channel, NoDma, TransferOptions, WritableRingBuffer};
55
use crate::gpio::sealed::{AFType, Pin as _};
66
use crate::gpio::AnyPin;
77
use crate::pac::spi::vals;
@@ -154,27 +154,31 @@ impl Default for Config {
154154
}
155155

156156
/// I2S driver.
157-
pub struct I2S<'d, T: Instance, Tx, Rx> {
158-
pub _peri: Spi<'d, T, Tx, Rx>,
157+
pub struct I2S<'d, T: Instance, C: Channel, W: word::Word> {
158+
_peri: Spi<'d, T, NoDma, NoDma>,
159159
sd: Option<PeripheralRef<'d, AnyPin>>,
160160
ws: Option<PeripheralRef<'d, AnyPin>>,
161161
ck: Option<PeripheralRef<'d, AnyPin>>,
162162
mck: Option<PeripheralRef<'d, AnyPin>>,
163+
ring_buffer: WritableRingBuffer<'d, C, W>,
163164
}
164165

165-
impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
166+
impl<'d, T: Instance, C: Channel, W: word::Word> I2S<'d, T, C, W> {
166167
/// Note: Full-Duplex modes are not supported at this time
167168
pub fn new_no_mck(
168169
peri: impl Peripheral<P = T> + 'd,
169170
sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
170171
ws: impl Peripheral<P = impl WsPin<T>> + 'd,
171172
ck: impl Peripheral<P = impl CkPin<T>> + 'd,
172-
txdma: impl Peripheral<P = Tx> + 'd,
173-
rxdma: impl Peripheral<P = Rx> + 'd,
173+
txdma: impl Peripheral<P = C> + 'd,
174+
dma_buf: &'d mut [W],
174175
freq: Hertz,
175176
config: Config,
176-
) -> Self {
177-
into_ref!(sd, ws, ck);
177+
) -> Self
178+
where
179+
C: Channel + TxDma<T>,
180+
{
181+
into_ref!(sd, ws, ck, txdma);
178182

179183
sd.set_as_af(sd.af_num(), AFType::OutputPushPull);
180184
sd.set_speed(crate::gpio::Speed::VeryHigh);
@@ -187,7 +191,7 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
187191

188192
let mut spi_cfg = SpiConfig::default();
189193
spi_cfg.frequency = freq;
190-
let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg);
194+
let spi = Spi::new_internal(peri, NoDma, NoDma, spi_cfg);
191195

192196
#[cfg(all(rcc_f4, not(stm32f410)))]
193197
let pclk = Hertz(38_400_000); // unsafe { get_freqs() }.plli2s1_r.unwrap();
@@ -197,7 +201,7 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
197201

198202
let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format);
199203

200-
defmt::println!("odd: {}, div {}", odd, div);
204+
// defmt::println!("odd: {}, div {}", odd, div);
201205

202206
#[cfg(any(spi_v1, spi_f1))]
203207
{
@@ -218,9 +222,6 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
218222
w.set_mckoe(config.master_clock);
219223
});
220224

221-
// T::REGS.cr2().modify(|r| r.set_txdmaen(true));
222-
// T::REGS.dr().write(|w| w.set_dr(0x0000_u16));
223-
224225
// 2. Select the CKPOL bit to define the steady level for the communication clock. Set the
225226
// MCKOE bit in the SPI_I2SPR register if the master clock MCK needs to be provided to
226227
// the external DAC/ADC audio component (the I2SDIV and ODD values should be
@@ -261,168 +262,178 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
261262
});
262263
}
263264

265+
let opts = TransferOptions {
266+
half_transfer_ir: true,
267+
//the new_write() and new_read() always use circular mode
268+
..Default::default()
269+
};
270+
271+
let request = txdma.request();
272+
let data_ptr = T::REGS.dr().as_ptr().cast::<W>();
273+
274+
let ring_buffer = unsafe { WritableRingBuffer::new(txdma, request, data_ptr, dma_buf, opts) };
275+
264276
Self {
265277
_peri: spi,
266278
sd: Some(sd.map_into()),
267279
ws: Some(ws.map_into()),
268280
ck: Some(ck.map_into()),
269281
mck: None,
282+
ring_buffer,
270283
}
271284
}
272285

273-
/// Note: Full-Duplex modes are not supported at this time
274-
pub fn new(
275-
peri: impl Peripheral<P = T> + 'd,
276-
sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
277-
ws: impl Peripheral<P = impl WsPin<T>> + 'd,
278-
ck: impl Peripheral<P = impl CkPin<T>> + 'd,
279-
mck: impl Peripheral<P = impl MckPin<T>> + 'd,
280-
txdma: impl Peripheral<P = Tx> + 'd,
281-
rxdma: impl Peripheral<P = Rx> + 'd,
282-
freq: Hertz,
283-
config: Config,
284-
) -> Self {
285-
into_ref!(sd, ws, ck, mck);
286+
// /// Note: Full-Duplex modes are not supported at this time
287+
// pub fn new(
288+
// peri: impl Peripheral<P = T> + 'd,
289+
// sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
290+
// ws: impl Peripheral<P = impl WsPin<T>> + 'd,
291+
// ck: impl Peripheral<P = impl CkPin<T>> + 'd,
292+
// mck: impl Peripheral<P = impl MckPin<T>> + 'd,
293+
// txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
294+
// freq: Hertz,
295+
// config: Config,
296+
// ) -> Self {
297+
// into_ref!(sd, ws, ck, mck);
286298

287-
sd.set_as_af(sd.af_num(), AFType::OutputPushPull);
288-
sd.set_speed(crate::gpio::Speed::VeryHigh);
299+
// sd.set_as_af(sd.af_num(), AFType::OutputPushPull);
300+
// sd.set_speed(crate::gpio::Speed::VeryHigh);
289301

290-
ws.set_as_af(ws.af_num(), AFType::OutputPushPull);
291-
ws.set_speed(crate::gpio::Speed::VeryHigh);
302+
// ws.set_as_af(ws.af_num(), AFType::OutputPushPull);
303+
// ws.set_speed(crate::gpio::Speed::VeryHigh);
292304

293-
ck.set_as_af(ck.af_num(), AFType::OutputPushPull);
294-
ck.set_speed(crate::gpio::Speed::VeryHigh);
305+
// ck.set_as_af(ck.af_num(), AFType::OutputPushPull);
306+
// ck.set_speed(crate::gpio::Speed::VeryHigh);
295307

296-
mck.set_as_af(mck.af_num(), AFType::OutputPushPull);
297-
mck.set_speed(crate::gpio::Speed::VeryHigh);
308+
// mck.set_as_af(mck.af_num(), AFType::OutputPushPull);
309+
// mck.set_speed(crate::gpio::Speed::VeryHigh);
310+
311+
// let mut spi_cfg = SpiConfig::default();
312+
// spi_cfg.frequency = freq;
313+
// let spi = Spi::new_internal(peri, NoDma, NoDma, spi_cfg);
298314

299-
let mut spi_cfg = SpiConfig::default();
300-
spi_cfg.frequency = freq;
301-
let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg);
302315

303316
// TODO move i2s to the new mux infra.
304317
//#[cfg(all(rcc_f4, not(stm32f410)))]
305318
//let pclk = unsafe { get_freqs() }.plli2s1_q.unwrap();
306319
//#[cfg(stm32f410)]
307320
let pclk = T::frequency();
321+
// #[cfg(all(rcc_f4, not(stm32f410)))]
322+
// let pclk = unsafe { get_freqs() }.plli2s1_q.unwrap();
308323

309-
let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format);
324+
// #[cfg(stm32f410)]
325+
// let pclk = T::frequency();
310326

311-
#[cfg(any(spi_v1, spi_f1))]
312-
{
313-
use stm32_metapac::spi::vals::{I2scfg, Odd};
314327

315-
// 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR register to define the serial clock baud
316-
// rate to reach the proper audio sample frequency. The ODD bit in the SPI_I2SPR
317-
// register also has to be defined.
328+
// let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format);
318329

319-
T::REGS.i2spr().modify(|w| {
320-
w.set_i2sdiv(div);
321-
w.set_odd(match odd {
322-
true => Odd::ODD,
323-
false => Odd::EVEN,
324-
});
330+
// #[cfg(any(spi_v1, spi_f1))]
331+
// {
332+
// use stm32_metapac::spi::vals::{I2scfg, Odd};
325333

326-
w.set_mckoe(config.master_clock);
327-
});
334+
// // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR register to define the serial clock baud
335+
// // rate to reach the proper audio sample frequency. The ODD bit in the SPI_I2SPR
336+
// // register also has to be defined.
328337

329-
// 2. Select the CKPOL bit to define the steady level for the communication clock. Set the
330-
// MCKOE bit in the SPI_I2SPR register if the master clock MCK needs to be provided to
331-
// the external DAC/ADC audio component (the I2SDIV and ODD values should be
332-
// computed depending on the state of the MCK output, for more details refer to
333-
// Section 28.4.4: Clock generator).
338+
// T::REGS.i2spr().modify(|w| {
339+
// w.set_i2sdiv(div);
340+
// w.set_odd(match odd {
341+
// true => Odd::ODD,
342+
// false => Odd::EVEN,
343+
// });
334344

335-
// 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the
336-
// I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the
337-
// DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit.
338-
// Select also the I2S master mode and direction (Transmitter or Receiver) through the
339-
// I2SCFG[1:0] bits in the SPI_I2SCFGR register.
345+
// w.set_mckoe(config.master_clock);
346+
// });
340347

341-
// 4. If needed, select all the potential interruption sources and the DMA capabilities by
342-
// writing the SPI_CR2 register.
348+
// // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the
349+
// // MCKOE bit in the SPI_I2SPR register if the master clock MCK needs to be provided to
350+
// // the external DAC/ADC audio component (the I2SDIV and ODD values should be
351+
// // computed depending on the state of the MCK output, for more details refer to
352+
// // Section 28.4.4: Clock generator).
343353

344-
// 5. The I2SE bit in SPI_I2SCFGR register must be set.
354+
// // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the
355+
// // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the
356+
// // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit.
357+
// // Select also the I2S master mode and direction (Transmitter or Receiver) through the
358+
// // I2SCFG[1:0] bits in the SPI_I2SCFGR register.
345359

346-
T::REGS.i2scfgr().modify(|w| {
347-
w.set_ckpol(config.clock_polarity.ckpol());
360+
// // 4. If needed, select all the potential interruption sources and the DMA capabilities by
361+
// // writing the SPI_CR2 register.
348362

349-
w.set_i2smod(true);
350-
w.set_i2sstd(config.standard.i2sstd());
351-
w.set_pcmsync(config.standard.pcmsync());
363+
// // 5. The I2SE bit in SPI_I2SCFGR register must be set.
352364

353-
w.set_datlen(config.format.datlen());
354-
w.set_chlen(config.format.chlen());
365+
// T::REGS.i2scfgr().modify(|w| {
366+
// w.set_ckpol(config.clock_polarity.ckpol());
355367

356-
w.set_i2scfg(match (config.mode, config.function) {
357-
(Mode::Master, Function::Transmit) => I2scfg::MASTERTX,
358-
(Mode::Master, Function::Receive) => I2scfg::MASTERRX,
359-
(Mode::Slave, Function::Transmit) => I2scfg::SLAVETX,
360-
(Mode::Slave, Function::Receive) => I2scfg::SLAVERX,
361-
});
368+
// w.set_i2smod(true);
369+
// w.set_i2sstd(config.standard.i2sstd());
370+
// w.set_pcmsync(config.standard.pcmsync());
362371

363-
w.set_i2se(true)
364-
});
365-
}
372+
// w.set_datlen(config.format.datlen());
373+
// w.set_chlen(config.format.chlen());
366374

367-
Self {
368-
_peri: spi,
369-
sd: Some(sd.map_into()),
370-
ws: Some(ws.map_into()),
371-
ck: Some(ck.map_into()),
372-
mck: Some(mck.map_into()),
373-
}
374-
}
375+
// w.set_i2scfg(match (config.mode, config.function) {
376+
// (Mode::Master, Function::Transmit) => I2scfg::MASTERTX,
377+
// (Mode::Master, Function::Receive) => I2scfg::MASTERRX,
378+
// (Mode::Slave, Function::Transmit) => I2scfg::SLAVETX,
379+
// (Mode::Slave, Function::Receive) => I2scfg::SLAVERX,
380+
// });
375381

376-
/// Write audio data.
377-
pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error>
378-
where
379-
Tx: TxDma<T>,
380-
{
381-
self._peri.write(data).await
382-
}
382+
// w.set_i2se(true)
383+
// });
384+
// }
383385

384-
/// Write audio data.
385-
pub fn writer(&mut self, data: &[u16]) -> Result<(), Error> {
386-
let mut spi = T::REGS;
386+
// Self {
387+
// _peri: spi,
388+
// sd: Some(sd.map_into()),
389+
// ws: Some(ws.map_into()),
390+
// ck: Some(ck.map_into()),
391+
// mck: Some(mck.map_into()),
392+
// dma: Some(txdma.map_into()),
393+
// }
394+
// }
387395

388-
// spi.cr2().modify(|r| {
389-
// r.set_frf(vals::Frf::TI);
390-
// r.set_ssoe(true);
391-
// });
396+
/// Write audio data.
397+
pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
398+
self.ring_buffer.write_exact(data).await.map_err(|_| Error::Overrun)?;
399+
Ok(())
400+
}
392401

393-
spi.cr1().modify(|r| r.set_spe(true));
402+
/// Start the I2S driver.
403+
pub fn start(&mut self) {
404+
self.ring_buffer.start();
394405

395-
// let dr = spi.dr().as_ptr() as *mut W;
406+
#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
407+
T::REGS.cr2().modify(|reg| {
408+
reg.set_txdmaen(true);
409+
});
396410

397-
for _ in 0..2000 {
398-
for sample in data {
399-
while !spi.sr().read().txe() {}
400-
spi.dr().write(|reg| reg.set_dr(*sample));
401-
}
402-
}
411+
T::REGS.cr1().modify(|w| {
412+
w.set_spe(true);
413+
});
414+
}
403415

404-
// defmt::println!("Wait for TXE go high");
405-
while !spi.sr().read().txe() {}
406-
// defmt::println!("Wait for BSY go low");
407-
// while spi.sr().read().bsy() {}
408-
// defmt::println!("Wait done");
416+
/// Stop the I2S driver.
417+
pub fn stop(&mut self) {
418+
#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
419+
T::REGS.cr2().modify(|reg| {
420+
reg.set_txdmaen(false);
421+
});
409422

410-
spi.cr1().modify(|r| r.set_spe(false));
423+
self.ring_buffer.request_stop();
411424

412-
Ok(())
425+
T::REGS.cr1().modify(|w| {
426+
w.set_spe(false);
427+
});
413428
}
414429

415-
/// Read audio data.
416-
pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error>
417-
where
418-
Tx: TxDma<T>,
419-
Rx: RxDma<T>,
420-
{
421-
self._peri.read(data).await
430+
/// Reset I2S operation.
431+
pub fn reset() {
432+
T::enable_and_reset();
422433
}
423434
}
424435

425-
impl<'d, T: Instance, Tx, Rx> Drop for I2S<'d, T, Tx, Rx> {
436+
impl<'d, T: Instance, C: Channel, W: word::Word> Drop for I2S<'d, T, C, W> {
426437
fn drop(&mut self) {
427438
self.sd.as_ref().map(|x| x.set_as_disconnected());
428439
self.ws.as_ref().map(|x| x.set_as_disconnected());

embassy-stm32/src/spi/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> {
9090
sck: Option<PeripheralRef<'d, AnyPin>>,
9191
mosi: Option<PeripheralRef<'d, AnyPin>>,
9292
miso: Option<PeripheralRef<'d, AnyPin>>,
93-
pub txdma: PeripheralRef<'d, Tx>,
93+
txdma: PeripheralRef<'d, Tx>,
9494
rxdma: PeripheralRef<'d, Rx>,
9595
current_word_size: word_impl::Config,
9696
}

0 commit comments

Comments
 (0)