Add missing parts and Cleanup
This commit is contained in:
parent
1ed260b105
commit
6b88057aef
@ -1,27 +1,27 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
//! I2S
|
//! Support for I2S audio
|
||||||
|
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_cortex_m::interrupt::{InterruptExt, Priority};
|
use embassy_cortex_m::interrupt::InterruptExt;
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_common::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||||
use crate::interrupt::Interrupt;
|
use crate::interrupt::Interrupt;
|
||||||
use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL};
|
use crate::pac::i2s::RegisterBlock;
|
||||||
use crate::Peripheral;
|
use crate::Peripheral;
|
||||||
|
|
||||||
// TODO: Define those in lib.rs somewhere else
|
// TODO: Define those in lib.rs somewhere else
|
||||||
//
|
|
||||||
// I2S EasyDMA MAXCNT bit length = 14
|
/// I2S EasyDMA MAXCNT bit length = 14
|
||||||
const MAX_DMA_MAXCNT: u32 = 1 << 14;
|
const MAX_DMA_MAXCNT: u32 = 1 << 14;
|
||||||
|
|
||||||
// Limits for Easy DMA - it can only read from data ram
|
/// Limits for Easy DMA - it can only read from data ram
|
||||||
pub const SRAM_LOWER: usize = 0x2000_0000;
|
pub const SRAM_LOWER: usize = 0x2000_0000;
|
||||||
pub const SRAM_UPPER: usize = 0x3000_0000;
|
pub const SRAM_UPPER: usize = 0x3000_0000;
|
||||||
|
|
||||||
@ -36,35 +36,144 @@ pub enum Error {
|
|||||||
BufferLengthMisaligned,
|
BufferLengthMisaligned,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const MODE_MASTER_8000: Mode = Mode::Master {
|
/// Approximate sample rates.
|
||||||
freq: MckFreq::_32MDiv125,
|
///
|
||||||
ratio: Ratio::_32x,
|
/// Those are common sample rates that can not be configured without an small error.
|
||||||
}; // error = 0
|
///
|
||||||
pub const MODE_MASTER_11025: Mode = Mode::Master {
|
/// For custom master clock configuration, please refer to [Mode].
|
||||||
freq: MckFreq::_32MDiv15,
|
#[derive(Clone, Copy)]
|
||||||
ratio: Ratio::_192x,
|
pub enum ApproxSampleRate {
|
||||||
}; // error = 86
|
_11025,
|
||||||
pub const MODE_MASTER_16000: Mode = Mode::Master {
|
_16000,
|
||||||
freq: MckFreq::_32MDiv21,
|
_22050,
|
||||||
ratio: Ratio::_96x,
|
_32000,
|
||||||
}; // error = 127
|
_44100,
|
||||||
pub const MODE_MASTER_22050: Mode = Mode::Master {
|
_48000,
|
||||||
freq: MckFreq::_32MDiv15,
|
}
|
||||||
ratio: Ratio::_96x,
|
|
||||||
}; // error = 172
|
|
||||||
pub const MODE_MASTER_32000: Mode = Mode::Master {
|
|
||||||
freq: MckFreq::_32MDiv21,
|
|
||||||
ratio: Ratio::_48x,
|
|
||||||
}; // error = 254
|
|
||||||
pub const MODE_MASTER_44100: Mode = Mode::Master {
|
|
||||||
freq: MckFreq::_32MDiv15,
|
|
||||||
ratio: Ratio::_48x,
|
|
||||||
}; // error = 344
|
|
||||||
pub const MODE_MASTER_48000: Mode = Mode::Master {
|
|
||||||
freq: MckFreq::_32MDiv21,
|
|
||||||
ratio: Ratio::_32x,
|
|
||||||
}; // error = 381
|
|
||||||
|
|
||||||
|
impl From<ApproxSampleRate> for Mode {
|
||||||
|
fn from(value: ApproxSampleRate) -> Self {
|
||||||
|
match value {
|
||||||
|
// error = 86
|
||||||
|
ApproxSampleRate::_11025 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv15,
|
||||||
|
ratio: Ratio::_192x,
|
||||||
|
},
|
||||||
|
// error = 127
|
||||||
|
ApproxSampleRate::_16000 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv21,
|
||||||
|
ratio: Ratio::_96x,
|
||||||
|
},
|
||||||
|
// error = 172
|
||||||
|
ApproxSampleRate::_22050 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv15,
|
||||||
|
ratio: Ratio::_96x,
|
||||||
|
},
|
||||||
|
// error = 254
|
||||||
|
ApproxSampleRate::_32000 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv21,
|
||||||
|
ratio: Ratio::_48x,
|
||||||
|
},
|
||||||
|
// error = 344
|
||||||
|
ApproxSampleRate::_44100 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv15,
|
||||||
|
ratio: Ratio::_48x,
|
||||||
|
},
|
||||||
|
// error = 381
|
||||||
|
ApproxSampleRate::_48000 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv21,
|
||||||
|
ratio: Ratio::_32x,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApproxSampleRate {
|
||||||
|
pub fn sample_rate(&self) -> u32 {
|
||||||
|
// This will always provide a Master mode, so it is safe to unwrap.
|
||||||
|
Mode::from(*self).sample_rate().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exact sample rates.
|
||||||
|
///
|
||||||
|
/// Those are non standard sample rates that can be configured without error.
|
||||||
|
///
|
||||||
|
/// For custom master clock configuration, please refer to [Mode].
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ExactSampleRate {
|
||||||
|
_8000,
|
||||||
|
_10582,
|
||||||
|
_12500,
|
||||||
|
_15625,
|
||||||
|
_15873,
|
||||||
|
_25000,
|
||||||
|
_31250,
|
||||||
|
_50000,
|
||||||
|
_62500,
|
||||||
|
_100000,
|
||||||
|
_125000,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExactSampleRate {
|
||||||
|
pub fn sample_rate(&self) -> u32 {
|
||||||
|
// This will always provide a Master mode, so it is safe to unwrap.
|
||||||
|
Mode::from(*self).sample_rate().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ExactSampleRate> for Mode {
|
||||||
|
fn from(value: ExactSampleRate) -> Self {
|
||||||
|
match value {
|
||||||
|
ExactSampleRate::_8000 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv125,
|
||||||
|
ratio: Ratio::_32x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_10582 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv63,
|
||||||
|
ratio: Ratio::_48x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_12500 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv10,
|
||||||
|
ratio: Ratio::_256x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_15625 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv32,
|
||||||
|
ratio: Ratio::_64x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_15873 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv63,
|
||||||
|
ratio: Ratio::_32x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_25000 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv10,
|
||||||
|
ratio: Ratio::_128x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_31250 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv32,
|
||||||
|
ratio: Ratio::_32x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_50000 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv10,
|
||||||
|
ratio: Ratio::_64x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_62500 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv16,
|
||||||
|
ratio: Ratio::_32x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_100000 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv10,
|
||||||
|
ratio: Ratio::_32x,
|
||||||
|
},
|
||||||
|
ExactSampleRate::_125000 => Mode::Master {
|
||||||
|
freq: MckFreq::_32MDiv8,
|
||||||
|
ratio: Ratio::_32x,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// I2S configuration.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -78,7 +187,7 @@ pub struct Config {
|
|||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
mode: MODE_MASTER_32000,
|
mode: ExactSampleRate::_31250.into(),
|
||||||
swidth: SampleWidth::_16bit,
|
swidth: SampleWidth::_16bit,
|
||||||
align: Align::Left,
|
align: Align::Left,
|
||||||
format: Format::I2S,
|
format: Format::I2S,
|
||||||
@ -132,10 +241,12 @@ impl MckFreq {
|
|||||||
256000,
|
256000,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// Return the value that needs to be written to the register.
|
||||||
pub fn to_register_value(&self) -> u32 {
|
pub fn to_register_value(&self) -> u32 {
|
||||||
Self::REGISTER_VALUES[usize::from(*self)]
|
Self::REGISTER_VALUES[usize::from(*self)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the master clock frequency.
|
||||||
pub fn to_frequency(&self) -> u32 {
|
pub fn to_frequency(&self) -> u32 {
|
||||||
Self::FREQUENCIES[usize::from(*self)]
|
Self::FREQUENCIES[usize::from(*self)]
|
||||||
}
|
}
|
||||||
@ -147,7 +258,10 @@ impl From<MckFreq> for usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MCK / LRCK ratio.
|
/// Master clock frequency ratio
|
||||||
|
///
|
||||||
|
/// Sample Rate = LRCK = MCK / Ratio
|
||||||
|
///
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
pub enum Ratio {
|
pub enum Ratio {
|
||||||
_32x,
|
_32x,
|
||||||
@ -175,6 +289,7 @@ impl From<Ratio> for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sample width.
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
pub enum SampleWidth {
|
pub enum SampleWidth {
|
||||||
_8bit,
|
_8bit,
|
||||||
@ -188,7 +303,7 @@ impl From<SampleWidth> for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alignment of sample within a frame.
|
/// Channel used for the most significant sample value in a frame.
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
pub enum Align {
|
pub enum Align {
|
||||||
Left,
|
Left,
|
||||||
@ -220,11 +335,13 @@ impl From<Format> for bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable channels.
|
/// Channels
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
pub enum Channels {
|
pub enum Channels {
|
||||||
Stereo,
|
Stereo,
|
||||||
|
/// Mono left
|
||||||
Left,
|
Left,
|
||||||
|
/// Mono right
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,8 +352,6 @@ impl From<Channels> for u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload.
|
/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload.
|
||||||
///
|
|
||||||
/// For more details about EasyDMA, consult the module documentation.
|
|
||||||
pub struct I2S<'d, T: Instance> {
|
pub struct I2S<'d, T: Instance> {
|
||||||
_p: PeripheralRef<'d, T>,
|
_p: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
@ -278,29 +393,32 @@ impl<'d, T: Instance> I2S<'d, T> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
into_ref!(i2s, irq, mck, sck, lrck, sdin, sdout);
|
into_ref!(i2s, irq, mck, sck, lrck, sdin, sdout);
|
||||||
|
|
||||||
let r = T::regs();
|
Self::apply_config(&config);
|
||||||
Self::apply_config(&r.config, &config);
|
Self::select_pins(mck, sck, lrck, sdin, sdout);
|
||||||
Self::select_pins(&r.psel, mck, sck, lrck, sdin, sdout);
|
Self::setup_interrupt(irq);
|
||||||
Self::setup_interrupt(irq, r);
|
|
||||||
|
|
||||||
r.enable.write(|w| w.enable().enabled());
|
T::regs().enable.write(|w| w.enable().enabled());
|
||||||
|
|
||||||
Self { _p: i2s }
|
Self { _p: i2s }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// I2S output only
|
||||||
pub fn output(self) -> Output<'d, T> {
|
pub fn output(self) -> Output<'d, T> {
|
||||||
Output { _p: self._p }
|
Output { _p: self._p }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// I2S input only
|
||||||
pub fn input(self) -> Input<'d, T> {
|
pub fn input(self) -> Input<'d, T> {
|
||||||
Input { _p: self._p }
|
Input { _p: self._p }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// I2S full duplex (input and output)
|
||||||
pub fn full_duplex(self) -> FullDuplex<'d, T> {
|
pub fn full_duplex(self) -> FullDuplex<'d, T> {
|
||||||
FullDuplex { _p: self._p }
|
FullDuplex { _p: self._p }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_config(c: &CONFIG, config: &Config) {
|
fn apply_config(config: &Config) {
|
||||||
|
let c = &T::regs().config;
|
||||||
match config.mode {
|
match config.mode {
|
||||||
Mode::Master { freq, ratio } => {
|
Mode::Master { freq, ratio } => {
|
||||||
c.mode.write(|w| w.mode().master());
|
c.mode.write(|w| w.mode().master());
|
||||||
@ -322,13 +440,14 @@ impl<'d, T: Instance> I2S<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn select_pins(
|
fn select_pins(
|
||||||
psel: &PSEL,
|
|
||||||
mck: PeripheralRef<'d, AnyPin>,
|
mck: PeripheralRef<'d, AnyPin>,
|
||||||
sck: PeripheralRef<'d, AnyPin>,
|
sck: PeripheralRef<'d, AnyPin>,
|
||||||
lrck: PeripheralRef<'d, AnyPin>,
|
lrck: PeripheralRef<'d, AnyPin>,
|
||||||
sdin: PeripheralRef<'d, AnyPin>,
|
sdin: PeripheralRef<'d, AnyPin>,
|
||||||
sdout: PeripheralRef<'d, AnyPin>,
|
sdout: PeripheralRef<'d, AnyPin>,
|
||||||
) {
|
) {
|
||||||
|
let psel = &T::regs().psel;
|
||||||
|
|
||||||
psel.mck.write(|w| {
|
psel.mck.write(|w| {
|
||||||
unsafe { w.bits(mck.psel_bits()) };
|
unsafe { w.bits(mck.psel_bits()) };
|
||||||
w.connect().connected()
|
w.connect().connected()
|
||||||
@ -355,21 +474,23 @@ impl<'d, T: Instance> I2S<'d, T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>, r: &RegisterBlock) {
|
fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>) {
|
||||||
irq.set_handler(Self::on_interrupt);
|
irq.set_handler(Self::on_interrupt);
|
||||||
// irq.set_priority(Priority::P1); // TODO review priorities
|
|
||||||
irq.unpend();
|
irq.unpend();
|
||||||
irq.enable();
|
irq.enable();
|
||||||
|
|
||||||
let device = Device::<T>::new();
|
let device = Device::<T>::new();
|
||||||
device.disable_tx_ptr_interrupt();
|
device.disable_tx_ptr_interrupt();
|
||||||
device.disable_rx_ptr_interrupt();
|
device.disable_rx_ptr_interrupt();
|
||||||
|
device.disable_stopped_interrupt();
|
||||||
|
|
||||||
device.reset_tx_ptr_event();
|
device.reset_tx_ptr_event();
|
||||||
device.reset_rx_ptr_event();
|
device.reset_rx_ptr_event();
|
||||||
|
device.reset_stopped_event();
|
||||||
|
|
||||||
device.enable_tx_ptr_interrupt();
|
device.enable_tx_ptr_interrupt();
|
||||||
device.enable_rx_ptr_interrupt();
|
device.enable_rx_ptr_interrupt();
|
||||||
|
device.enable_stopped_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt(_: *mut ()) {
|
fn on_interrupt(_: *mut ()) {
|
||||||
@ -387,41 +508,40 @@ impl<'d, T: Instance> I2S<'d, T> {
|
|||||||
s.rx_waker.wake();
|
s.rx_waker.wake();
|
||||||
device.disable_rx_ptr_interrupt();
|
device.disable_rx_ptr_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if device.is_stopped() {
|
||||||
|
trace!("STOPPED INT");
|
||||||
|
s.stop_waker.wake();
|
||||||
|
device.disable_stopped_interrupt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Output<'d, T: Instance> {
|
async fn stop() {
|
||||||
_p: PeripheralRef<'d, T>,
|
compiler_fence(Ordering::SeqCst);
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> Output<'d, T> {
|
|
||||||
/// Starts I2S transfer.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn start<B>(&self, buffer: B) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
B: Buffer,
|
|
||||||
{
|
|
||||||
// TODO what to do if it is started already?
|
|
||||||
|
|
||||||
let device = Device::<T>::new();
|
let device = Device::<T>::new();
|
||||||
device.enable();
|
device.stop();
|
||||||
device.set_tx_buffer(buffer)?;
|
|
||||||
device.enable_tx();
|
|
||||||
device.start();
|
|
||||||
|
|
||||||
Ok(())
|
T::state().started.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
|
poll_fn(|cx| {
|
||||||
|
T::state().stop_waker.register(cx.waker());
|
||||||
|
|
||||||
|
if device.is_stopped() {
|
||||||
|
trace!("STOP: Ready");
|
||||||
|
device.reset_stopped_event();
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
trace!("STOP: Pending");
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
device.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the I2S transfer and waits until it has stopped.
|
async fn send<B>(buffer: B) -> Result<(), Error>
|
||||||
#[inline(always)]
|
|
||||||
pub async fn stop(&self) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transmits the given `buffer`.
|
|
||||||
/// Buffer address must be 4 byte aligned and located in RAM.
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
pub async fn send<B>(&mut self, buffer: B) -> Result<(), Error>
|
|
||||||
where
|
where
|
||||||
B: Buffer,
|
B: Buffer,
|
||||||
{
|
{
|
||||||
@ -454,24 +574,191 @@ impl<'d, T: Instance> Output<'d, T> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn receive<B>(buffer: B) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
trace!("RECEIVE: {}", buffer.bytes_ptr() as u32);
|
||||||
|
|
||||||
|
let device = Device::<T>::new();
|
||||||
|
let drop = device.on_rx_drop();
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
poll_fn(|cx| {
|
||||||
|
T::state().rx_waker.register(cx.waker());
|
||||||
|
|
||||||
|
if device.is_rx_ptr_updated() {
|
||||||
|
trace!("RX POLL: Ready");
|
||||||
|
device.reset_rx_ptr_event();
|
||||||
|
device.enable_rx_ptr_interrupt();
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
trace!("RX POLL: Pending");
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
device.set_rx_buffer(buffer)?;
|
||||||
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
drop.defuse();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// I2S output
|
||||||
|
pub struct Output<'d, T: Instance> {
|
||||||
|
_p: PeripheralRef<'d, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Output<'d, T> {
|
||||||
|
/// Prepare the initial buffer and start the I2S transfer.
|
||||||
|
pub async fn start<B>(&self, buffer: B) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
let device = Device::<T>::new();
|
||||||
|
|
||||||
|
let s = T::state();
|
||||||
|
if s.started.load(Ordering::Relaxed) {
|
||||||
|
self.stop().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
device.enable();
|
||||||
|
device.enable_tx();
|
||||||
|
device.set_tx_buffer(buffer)?;
|
||||||
|
|
||||||
|
s.started.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
device.start();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the I2S transfer and waits until it has stopped.
|
||||||
|
#[inline(always)]
|
||||||
|
pub async fn stop(&self) {
|
||||||
|
I2S::<T>::stop().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the given `buffer` for transmission in the DMA.
|
||||||
|
/// Buffer address must be 4 byte aligned and located in RAM.
|
||||||
|
/// The buffer must not be written while being used by the DMA,
|
||||||
|
/// which takes two other `send`s being awaited.
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
pub async fn send<B>(&mut self, buffer: B) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
I2S::<T>::send(buffer).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// I2S input
|
||||||
pub struct Input<'d, T: Instance> {
|
pub struct Input<'d, T: Instance> {
|
||||||
_p: PeripheralRef<'d, T>,
|
_p: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Input<'d, T> {
|
impl<'d, T: Instance> Input<'d, T> {
|
||||||
// TODO
|
/// Prepare the initial buffer and start the I2S transfer.
|
||||||
|
pub async fn start<B>(&self, buffer: B) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
let device = Device::<T>::new();
|
||||||
|
|
||||||
|
let s = T::state();
|
||||||
|
if s.started.load(Ordering::Relaxed) {
|
||||||
|
self.stop().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
device.enable();
|
||||||
|
device.enable_rx();
|
||||||
|
device.set_rx_buffer(buffer)?;
|
||||||
|
|
||||||
|
s.started.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
device.start();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the I2S transfer and waits until it has stopped.
|
||||||
|
#[inline(always)]
|
||||||
|
pub async fn stop(&self) {
|
||||||
|
I2S::<T>::stop().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the given `buffer` for reception from the DMA.
|
||||||
|
/// Buffer address must be 4 byte aligned and located in RAM.
|
||||||
|
/// The buffer must not be read while being used by the DMA,
|
||||||
|
/// which takes two other `receive`s being awaited.
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
pub async fn receive<B>(&mut self, buffer: B) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
I2S::<T>::receive(buffer).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// I2S ful duplex (input & output)
|
||||||
pub struct FullDuplex<'d, T: Instance> {
|
pub struct FullDuplex<'d, T: Instance> {
|
||||||
_p: PeripheralRef<'d, T>,
|
_p: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> FullDuplex<'d, T> {
|
impl<'d, T: Instance> FullDuplex<'d, T> {
|
||||||
// TODO
|
/// Prepare the initial buffers and start the I2S transfer.
|
||||||
|
pub async fn start<B>(&self, buffer_out: B, buffer_in: B) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
let device = Device::<T>::new();
|
||||||
|
|
||||||
|
let s = T::state();
|
||||||
|
if s.started.load(Ordering::Relaxed) {
|
||||||
|
self.stop().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
device.enable();
|
||||||
|
device.enable_tx();
|
||||||
|
device.enable_rx();
|
||||||
|
device.set_tx_buffer(buffer_out)?;
|
||||||
|
device.set_rx_buffer(buffer_in)?;
|
||||||
|
|
||||||
|
s.started.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
device.start();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the I2S transfer and waits until it has stopped.
|
||||||
|
#[inline(always)]
|
||||||
|
pub async fn stop(&self) {
|
||||||
|
I2S::<T>::stop().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the given `buffer_out` and `buffer_in` for transmission/reception from the DMA.
|
||||||
|
/// Buffer address must be 4 byte aligned and located in RAM.
|
||||||
|
/// The buffers must not be written/read while being used by the DMA,
|
||||||
|
/// which takes two other `send_and_receive` operations being awaited.
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
pub async fn send_and_receive<B>(&mut self, buffer_out: B, buffer_in: B) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
I2S::<T>::send(buffer_out).await?;
|
||||||
|
I2S::<T>::receive(buffer_in).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper encapsulating common I2S device operations.
|
||||||
struct Device<T>(&'static RegisterBlock, PhantomData<T>);
|
struct Device<T>(&'static RegisterBlock, PhantomData<T>);
|
||||||
|
|
||||||
impl<T: Instance> Device<T> {
|
impl<T: Instance> Device<T> {
|
||||||
@ -521,6 +808,34 @@ impl<T: Instance> Device<T> {
|
|||||||
self.0.tasks_start.write(|w| unsafe { w.bits(1) });
|
self.0.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn stop(&self) {
|
||||||
|
self.0.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_stopped(&self) -> bool {
|
||||||
|
self.0.events_stopped.read().bits() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset_stopped_event(&self) {
|
||||||
|
trace!("STOPPED EVENT: Reset");
|
||||||
|
self.0.events_stopped.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn disable_stopped_interrupt(&self) {
|
||||||
|
trace!("STOPPED INTERRUPT: Disabled");
|
||||||
|
self.0.intenclr.write(|w| w.stopped().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn enable_stopped_interrupt(&self) {
|
||||||
|
trace!("STOPPED INTERRUPT: Enabled");
|
||||||
|
self.0.intenset.write(|w| w.stopped().set());
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_tx_buffer<B>(&self, buffer: B) -> Result<(), Error>
|
fn set_tx_buffer<B>(&self, buffer: B) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
@ -606,6 +921,23 @@ impl<T: Instance> Device<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn on_rx_drop(&self) -> OnDrop<fn()> {
|
||||||
|
OnDrop::new(move || {
|
||||||
|
trace!("RX DROP: Stopping");
|
||||||
|
|
||||||
|
let device = Device::<T>::new();
|
||||||
|
device.disable_rx_ptr_interrupt();
|
||||||
|
device.reset_rx_ptr_event();
|
||||||
|
device.disable_rx();
|
||||||
|
|
||||||
|
// TX is stopped almost instantly, spinning is fine.
|
||||||
|
while !device.is_rx_ptr_updated() {}
|
||||||
|
|
||||||
|
trace!("RX DROP: Stopped");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_buffer<B>(buffer: B) -> Result<(u32, u32), Error>
|
fn validate_buffer<B>(buffer: B) -> Result<(u32, u32), Error>
|
||||||
where
|
where
|
||||||
B: Buffer,
|
B: Buffer,
|
||||||
@ -632,6 +964,56 @@ impl<T: Instance> Device<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sample details
|
||||||
|
pub trait Sample: Sized + Copy + Default {
|
||||||
|
const WIDTH: usize;
|
||||||
|
const SCALE: Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sample for i8 {
|
||||||
|
const WIDTH: usize = 8;
|
||||||
|
const SCALE: Self = 1 << (Self::WIDTH - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sample for i16 {
|
||||||
|
const WIDTH: usize = 16;
|
||||||
|
const SCALE: Self = 1 << (Self::WIDTH - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sample for i32 {
|
||||||
|
const WIDTH: usize = 24;
|
||||||
|
const SCALE: Self = 1 << (Self::WIDTH - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A 4-bytes aligned [Buffer].
|
||||||
|
#[repr(align(4))]
|
||||||
|
pub struct AlignedBuffer<T: Sample, const N: usize>([T; N]);
|
||||||
|
|
||||||
|
impl<T: Sample, const N: usize> AlignedBuffer<T, N> {
|
||||||
|
pub fn new(array: [T; N]) -> Self {
|
||||||
|
Self(array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sample, const N: usize> Default for AlignedBuffer<T, N> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self([T::default(); N])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sample, const N: usize> AsRef<[T]> for AlignedBuffer<T, N> {
|
||||||
|
fn as_ref(&self) -> &[T] {
|
||||||
|
self.0.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Sample, const N: usize> AsMut<[T]> for AlignedBuffer<T, N> {
|
||||||
|
fn as_mut(&mut self) -> &mut [T] {
|
||||||
|
self.0.as_mut_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Common operations required for a buffer to be used by the DMA
|
||||||
pub trait Buffer: Sized {
|
pub trait Buffer: Sized {
|
||||||
fn bytes_ptr(&self) -> *const u8;
|
fn bytes_ptr(&self) -> *const u8;
|
||||||
fn bytes_len(&self) -> usize;
|
fn bytes_len(&self) -> usize;
|
||||||
@ -674,22 +1056,25 @@ impl Buffer for &[i32] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
use core::sync::atomic::AtomicI32;
|
use core::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use super::*;
|
/// Peripheral static state
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
pub started: AtomicBool,
|
||||||
pub rx_waker: AtomicWaker,
|
pub rx_waker: AtomicWaker,
|
||||||
pub tx_waker: AtomicWaker,
|
pub tx_waker: AtomicWaker,
|
||||||
|
pub stop_waker: AtomicWaker,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
started: AtomicBool::new(false),
|
||||||
rx_waker: AtomicWaker::new(),
|
rx_waker: AtomicWaker::new(),
|
||||||
tx_waker: AtomicWaker::new(),
|
tx_waker: AtomicWaker::new(),
|
||||||
|
stop_waker: AtomicWaker::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -704,8 +1089,6 @@ pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
|||||||
type Interrupt: Interrupt;
|
type Interrupt: Interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Unsure why this macro is flagged as unused by CI when in fact it's used elsewhere?
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! impl_i2s {
|
macro_rules! impl_i2s {
|
||||||
($type:ident, $pac_type:ident, $irq:ident) => {
|
($type:ident, $pac_type:ident, $irq:ident) => {
|
||||||
impl crate::i2s::sealed::Instance for peripherals::$type {
|
impl crate::i2s::sealed::Instance for peripherals::$type {
|
||||||
|
@ -4,59 +4,41 @@
|
|||||||
|
|
||||||
use core::f32::consts::PI;
|
use core::f32::consts::PI;
|
||||||
|
|
||||||
use defmt::{error, info, trace};
|
use defmt::{error, info};
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_nrf::gpio::{Input, Pin, Pull};
|
use embassy_nrf::i2s::{self, Sample as _};
|
||||||
use embassy_nrf::i2s::{Channels, MckFreq, Mode, Ratio, SampleWidth, MODE_MASTER_32000};
|
use embassy_nrf::interrupt;
|
||||||
use embassy_nrf::pac::ficr::info;
|
|
||||||
use embassy_nrf::{i2s, interrupt};
|
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
#[repr(align(4))]
|
|
||||||
pub struct AlignedBuffer<T: ?Sized>(T);
|
|
||||||
|
|
||||||
impl<T> AsRef<T> for AlignedBuffer<T> {
|
|
||||||
fn as_ref(&self) -> &T {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AsMut<T> for AlignedBuffer<T> {
|
|
||||||
fn as_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
let p = embassy_nrf::init(Default::default());
|
let p = embassy_nrf::init(Default::default());
|
||||||
|
|
||||||
let mut config = i2s::Config::default();
|
let mut config = i2s::Config::default();
|
||||||
config.mode = MODE_MASTER_32000;
|
config.mode = i2s::ExactSampleRate::_50000.into();
|
||||||
// config.mode = Mode::Master {
|
config.channels = i2s::Channels::Left;
|
||||||
// freq: MckFreq::_32MDiv10,
|
config.swidth = i2s::SampleWidth::_16bit;
|
||||||
// ratio: Ratio::_256x,
|
|
||||||
// }; // 12500 Hz
|
|
||||||
config.channels = Channels::Left;
|
|
||||||
config.swidth = SampleWidth::_16bit;
|
|
||||||
let sample_rate = config.mode.sample_rate().expect("I2S Master");
|
let sample_rate = config.mode.sample_rate().expect("I2S Master");
|
||||||
let inv_sample_rate = 1.0 / sample_rate as f32;
|
let inv_sample_rate = 1.0 / sample_rate as f32;
|
||||||
|
|
||||||
info!("Sample rate: {}", sample_rate);
|
info!("Sample rate: {}", sample_rate);
|
||||||
|
|
||||||
// Wait for a button press
|
// Wait for a button press
|
||||||
|
// use embassy_nrf::gpio::{Input, Pin, Pull};
|
||||||
// let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up);
|
// let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up);
|
||||||
// btn1.wait_for_low().await;
|
// btn1.wait_for_low().await;
|
||||||
|
|
||||||
let irq = interrupt::take!(I2S);
|
let irq = interrupt::take!(I2S);
|
||||||
let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config).output();
|
let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config)
|
||||||
|
.output();
|
||||||
|
|
||||||
type Sample = i16;
|
type Sample = i16;
|
||||||
const MAX_UNIPOLAR_VALUE: Sample = (1 << 15) as Sample;
|
const NUM_SAMPLES: usize = 6000;
|
||||||
const NUM_SAMPLES: usize = 2000;
|
|
||||||
let mut buffers: [AlignedBuffer<[Sample; NUM_SAMPLES]>; 3] = [
|
let mut buffers: [i2s::AlignedBuffer<Sample, NUM_SAMPLES>; 3] = [
|
||||||
AlignedBuffer([0; NUM_SAMPLES]),
|
i2s::AlignedBuffer::default(),
|
||||||
AlignedBuffer([0; NUM_SAMPLES]),
|
i2s::AlignedBuffer::default(),
|
||||||
AlignedBuffer([0; NUM_SAMPLES]),
|
i2s::AlignedBuffer::default(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut carrier = SineOsc::new();
|
let mut carrier = SineOsc::new();
|
||||||
@ -66,32 +48,29 @@ async fn main(_spawner: Spawner) {
|
|||||||
freq_mod.set_amplitude(1.0);
|
freq_mod.set_amplitude(1.0);
|
||||||
|
|
||||||
let mut amp_mod = SineOsc::new();
|
let mut amp_mod = SineOsc::new();
|
||||||
amp_mod.set_frequency(4.0, inv_sample_rate);
|
amp_mod.set_frequency(16.0, inv_sample_rate);
|
||||||
amp_mod.set_amplitude(0.5);
|
amp_mod.set_amplitude(0.5);
|
||||||
|
|
||||||
let mut generate = |buf: &mut [Sample]| {
|
let mut generate = |buf: &mut [Sample]| {
|
||||||
let ptr = buf as *const [Sample] as *const Sample as u32;
|
for sample in &mut buf.chunks_mut(1) {
|
||||||
trace!("GEN: {}", ptr);
|
|
||||||
|
|
||||||
for sample in &mut buf.as_mut().chunks_mut(1) {
|
|
||||||
let signal = carrier.generate();
|
|
||||||
let freq_modulation = bipolar_to_unipolar(freq_mod.generate());
|
let freq_modulation = bipolar_to_unipolar(freq_mod.generate());
|
||||||
carrier.set_frequency(220.0 + 220.0 * freq_modulation, inv_sample_rate);
|
carrier.set_frequency(220.0 + 440.0 * freq_modulation, inv_sample_rate);
|
||||||
let amp_modulation = bipolar_to_unipolar(amp_mod.generate());
|
let amp_modulation = bipolar_to_unipolar(amp_mod.generate());
|
||||||
carrier.set_amplitude(amp_modulation);
|
carrier.set_amplitude(amp_modulation);
|
||||||
let value = (MAX_UNIPOLAR_VALUE as f32 * signal) as Sample;
|
let signal = carrier.generate();
|
||||||
|
let value = (Sample::SCALE as f32 * signal) as Sample;
|
||||||
sample[0] = value;
|
sample[0] = value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
generate(buffers[0].as_mut().as_mut_slice());
|
generate(buffers[0].as_mut());
|
||||||
generate(buffers[1].as_mut().as_mut_slice());
|
generate(buffers[1].as_mut());
|
||||||
|
|
||||||
i2s.start(buffers[0].as_ref().as_slice()).expect("I2S Start");
|
i2s.start(buffers[0].as_ref()).await.expect("I2S Start");
|
||||||
|
|
||||||
let mut index = 1;
|
let mut index = 1;
|
||||||
loop {
|
loop {
|
||||||
if let Err(err) = i2s.send(buffers[index].as_ref().as_slice()).await {
|
if let Err(err) = i2s.send(buffers[index].as_ref()).await {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +78,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
if index >= 3 {
|
if index >= 3 {
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
generate(buffers[index].as_mut().as_mut_slice());
|
generate(buffers[index].as_mut());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user