From 5fdd521a767fd8825a2d55d6b833fd99627353d7 Mon Sep 17 00:00:00 2001
From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com>
Date: Thu, 8 Dec 2022 20:22:50 +0100
Subject: [PATCH] Move the responsibility to manage buffers to the I2S stream
---
embassy-nrf/src/i2s.rs | 157 +++++++++++++++++++--------
examples/nrf/src/bin/i2s_effect.rs | 117 ++++++++++++++++++++
examples/nrf/src/bin/i2s_monitor.rs | 115 ++++++++++++++++++++
examples/nrf/src/bin/i2s_waveform.rs | 26 ++---
4 files changed, 353 insertions(+), 62 deletions(-)
create mode 100644 examples/nrf/src/bin/i2s_effect.rs
create mode 100644 examples/nrf/src/bin/i2s_monitor.rs
diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs
index 08d4093f2..7e9507751 100644
--- a/embassy-nrf/src/i2s.rs
+++ b/embassy-nrf/src/i2s.rs
@@ -19,6 +19,8 @@ use crate::pac::i2s::RegisterBlock;
use crate::util::{slice_in_ram_or, slice_ptr_parts};
use crate::{Peripheral, EASY_DMA_SIZE};
+pub type DoubleBuffering = MultiBuffering;
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
@@ -379,27 +381,47 @@ impl<'d, T: Instance> I2S<'d, T> {
}
/// I2S output only
- pub fn output(mut self, sdout: impl Peripheral
+ 'd) -> OutputStream<'d, T> {
+ pub fn output + 'd,
+ buffers: MultiBuffering + 'd) -> InputStream<'d, T> {
+ pub fn input + 'd,
+ buffers: MultiBuffering + 'd,
sdout: impl Peripheral + 'd,
- ) -> FullDuplexStream<'d, T> {
+ buffers_out: MultiBuffering,
+ ) -> OutputStream<'d, T, S, NB, NS> {
self.sdout = Some(sdout.into_ref().map_into());
- OutputStream { _p: self.build() }
+ OutputStream {
+ _p: self.build(),
+ buffers,
+ }
}
/// I2S input only
- pub fn input(mut self, sdin: impl Peripheral,
+ ) -> InputStream<'d, T, S, NB, NS> {
self.sdin = Some(sdin.into_ref().map_into());
- InputStream { _p: self.build() }
+ InputStream {
+ _p: self.build(),
+ buffers,
+ }
}
/// I2S full duplex (input and output)
- pub fn full_duplex(
+ pub fn full_duplex,
+ buffers_in: MultiBuffering,
+ ) -> FullDuplexStream<'d, T, S, NB, NS> {
self.sdout = Some(sdout.into_ref().map_into());
self.sdin = Some(sdin.into_ref().map_into());
- FullDuplexStream { _p: self.build() }
+ FullDuplexStream {
+ _p: self.build(),
+ buffers_out,
+ buffers_in,
+ }
}
fn build(self) -> PeripheralRef<'d, T> {
@@ -651,14 +673,19 @@ impl<'d, T: Instance> I2S<'d, T> {
}
/// I2S output
-pub struct OutputStream<'d, T: Instance> {
+pub struct OutputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> {
_p: PeripheralRef<'d, T>,
+ buffers: MultiBuffering,
}
-impl<'d, T: Instance> OutputStream<'d, T> {
+impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream<'d, T, S, NB, NS> {
+ /// Get a mutable reference to the current buffer.
+ pub fn buffer(&mut self) -> &mut [S] {
+ self.buffers.get_mut()
+ }
+
/// Prepare the initial buffer and start the I2S transfer.
- #[allow(unused_mut)]
- pub async fn start(&mut self, buffer: &[S]) -> Result<(), Error>
+ pub async fn start(&mut self) -> Result<(), Error>
where
S: Sample,
{
@@ -672,7 +699,7 @@ impl<'d, T: Instance> OutputStream<'d, T> {
device.enable();
device.enable_tx();
- device.update_tx(buffer as *const [S])?;
+ device.update_tx(self.buffers.switch())?;
s.started.store(true, Ordering::Relaxed);
@@ -689,28 +716,30 @@ impl<'d, T: Instance> OutputStream<'d, T> {
I2S::(&mut self, buffer: &[S]) -> Result<(), Error>
+ /// Sends the current buffer for transmission in the DMA.
+ /// Switches to use the next available buffer.
+ pub async fn send(&mut self) -> Result<(), Error>
where
S: Sample,
{
- I2S::,
}
-impl<'d, T: Instance> InputStream<'d, T> {
+impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<'d, T, S, NB, NS> {
+ /// Get a mutable reference to the current buffer.
+ pub fn buffer(&mut self) -> &mut [S] {
+ self.buffers.get_mut()
+ }
+
/// Prepare the initial buffer and start the I2S transfer.
- #[allow(unused_mut)]
- pub async fn start(&mut self, buffer: &mut [S]) -> Result<(), Error>
+ pub async fn start(&mut self) -> Result<(), Error>
where
S: Sample,
{
@@ -724,7 +753,7 @@ impl<'d, T: Instance> InputStream<'d, T> {
device.enable();
device.enable_rx();
- device.update_rx(buffer as *mut [S])?;
+ device.update_rx(self.buffers.switch())?;
s.started.store(true, Ordering::Relaxed);
@@ -741,28 +770,32 @@ impl<'d, T: Instance> InputStream<'d, T> {
I2S::(&mut self, buffer: &mut [S]) -> Result<(), Error>
+ pub async fn receive(&mut self) -> Result<(), Error>
where
S: Sample,
{
- I2S::,
+ buffers_in: MultiBuffering,
}
-impl<'d, T: Instance> FullDuplexStream<'d, T> {
+impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStream<'d, T, S, NB, NS> {
+ /// Get the current output and input buffers.
+ pub fn buffers(&mut self) -> (&mut [S], &[S]) {
+ (self.buffers_out.get_mut(), self.buffers_in.get())
+ }
+
/// Prepare the initial buffers and start the I2S transfer.
- #[allow(unused_mut)]
- pub async fn start(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error>
+ pub async fn start(&mut self) -> Result<(), Error>
where
S: Sample,
{
@@ -777,8 +810,8 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> {
device.enable_tx();
device.enable_rx();
- device.update_tx(buffer_out as *const [S])?;
- device.update_rx(buffer_in as *mut [S])?;
+ device.update_tx(self.buffers_out.switch())?;
+ device.update_rx(self.buffers_in.switch_mut())?;
s.started.store(true, Ordering::Relaxed);
@@ -796,17 +829,14 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> {
I2S::(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error>
+ /// Sets the current buffers for output and input for transmission/reception from the DMA.
+ /// Switch to use the next available buffers for output/input.
+ pub async fn send_and_receive(&mut self) -> Result<(), Error>
where
S: Sample,
{
- I2S::; NB],
+ index: usize,
+}
+
+impl {
+ pub fn new() -> Self {
+ assert!(NB > 1);
+ Self {
+ buffers: [AlignedBuffer::::default(); NB],
+ index: 0,
+ }
+ }
+
+ fn get(&self) -> &[S] {
+ &self.buffers[self.index]
+ }
+
+ fn get_mut(&mut self) -> &mut [S] {
+ &mut self.buffers[self.index]
+ }
+
+ /// Advance to use the next buffer and return a non mutable pointer to the previous one.
+ fn switch(&mut self) -> *const [S] {
+ let prev_index = self.index;
+ self.index = (self.index + 1) % NB;
+ self.buffers[prev_index].deref() as *const [S]
+ }
+
+ /// Advance to use the next buffer and return a mutable pointer to the previous one.
+ fn switch_mut(&mut self) -> *mut [S] {
+ let prev_index = self.index;
+ self.index = (self.index + 1) % NB;
+ self.buffers[prev_index].deref_mut() as *mut [S]
+ }
+}
+
pub(crate) mod sealed {
use core::sync::atomic::AtomicBool;
diff --git a/examples/nrf/src/bin/i2s_effect.rs b/examples/nrf/src/bin/i2s_effect.rs
new file mode 100644
index 000000000..3cca005b1
--- /dev/null
+++ b/examples/nrf/src/bin/i2s_effect.rs
@@ -0,0 +1,117 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use core::f32::consts::PI;
+
+use defmt::{error, info};
+use embassy_executor::Spawner;
+use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S};
+use embassy_nrf::interrupt;
+use {defmt_rtt as _, panic_probe as _};
+
+type Sample = i16;
+
+const NUM_BUFFERS: usize = 2;
+const NUM_SAMPLES: usize = 4;
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+ let p = embassy_nrf::init(Default::default());
+
+ let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into();
+
+ let sample_rate = master_clock.sample_rate();
+ info!("Sample rate: {}", sample_rate);
+
+ let config = Config::default()
+ .sample_width(SampleWidth::_16bit)
+ .channels(Channels::MonoLeft);
+
+ let irq = interrupt::take!(I2S);
+ let buffers_out = MultiBuffering::