Interrupts, async, sine oscillator

This commit is contained in:
Christian Perez Llamas
2022-11-12 18:48:57 +01:00
parent 10e3c3f2ec
commit 122a31d208
3 changed files with 330 additions and 102 deletions

View File

@ -2,17 +2,18 @@
//! I2S
//use core::future::poll_fn;
//use core::sync::atomic::{compiler_fence, Ordering};
//use core::task::Poll;
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
//use embassy_hal_common::drop::OnDrop;
use embassy_cortex_m::interrupt::{InterruptExt, Priority};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
//use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::Interrupt;
use crate::pac::i2s::CONFIG;
use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL};
use crate::Peripheral;
// TODO: Define those in lib.rs somewhere else
@ -35,10 +36,39 @@ pub enum Error {
// TODO: add other error variants.
}
pub const MODE_MASTER_8000: Mode = Mode::Master {
freq: MckFreq::_32MDiv125,
ratio: Ratio::_32x,
}; // error = 0
pub const MODE_MASTER_11025: Mode = Mode::Master {
freq: MckFreq::_32MDiv15,
ratio: Ratio::_192x,
}; // error = 86
pub const MODE_MASTER_16000: Mode = Mode::Master {
freq: MckFreq::_32MDiv21,
ratio: Ratio::_96x,
}; // error = 127
pub const MODE_MASTER_22050: Mode = Mode::Master {
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
#[derive(Clone)]
#[non_exhaustive]
pub struct Config {
pub ratio: Ratio,
pub mode: Mode,
pub swidth: SampleWidth,
pub align: Align,
pub format: Format,
@ -48,7 +78,7 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Self {
ratio: Ratio::_32x,
mode: MODE_MASTER_32000,
swidth: SampleWidth::_16bit,
align: Align::Left,
format: Format::I2S,
@ -57,6 +87,66 @@ impl Default for Config {
}
}
/// I2S Mode
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Mode {
Master { freq: MckFreq, ratio: Ratio },
Slave,
}
impl Mode {
pub fn sample_rate(&self) -> Option<u32> {
match self {
Mode::Master { freq, ratio } => Some(freq.to_frequency() / ratio.to_divisor()),
Mode::Slave => None,
}
}
}
/// Master clock generator frequency.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum MckFreq {
_32MDiv8,
_32MDiv10,
_32MDiv11,
_32MDiv15,
_32MDiv16,
_32MDiv21,
_32MDiv23,
_32MDiv30,
_32MDiv31,
_32MDiv32,
_32MDiv42,
_32MDiv63,
_32MDiv125,
}
impl MckFreq {
const REGISTER_VALUES: &[u32] = &[
0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000,
0x08000000, 0x06000000, 0x04100000, 0x020C0000,
];
const FREQUENCIES: &[u32] = &[
4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936,
256000,
];
pub fn to_register_value(&self) -> u32 {
Self::REGISTER_VALUES[usize::from(*self)]
}
pub fn to_frequency(&self) -> u32 {
Self::FREQUENCIES[usize::from(*self)]
}
}
impl From<MckFreq> for usize {
fn from(variant: MckFreq) -> Self {
variant as _
}
}
/// MCK / LRCK ratio.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Ratio {
@ -71,6 +161,14 @@ pub enum Ratio {
_512x,
}
impl Ratio {
const RATIOS: &[u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512];
pub fn to_divisor(&self) -> u32 {
Self::RATIOS[u8::from(*self) as usize]
}
}
impl From<Ratio> for u8 {
fn from(variant: Ratio) -> Self {
variant as _
@ -136,31 +234,6 @@ impl From<Channels> for u8 {
}
}
/// I2S Mode
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Mode {
Controller,
Peripheral,
}
// /// Master clock generator frequency.
// #[derive(Debug, Eq, PartialEq, Clone, Copy)]
// pub enum MckFreq {
// _32MDiv8 = 0x20000000,
// _32MDiv10 = 0x18000000,
// _32MDiv11 = 0x16000000,
// _32MDiv15 = 0x11000000,
// _32MDiv16 = 0x10000000,
// _32MDiv21 = 0x0C000000,
// _32MDiv23 = 0x0B000000,
// _32MDiv30 = 0x08800000,
// _32MDiv31 = 0x08400000,
// _32MDiv32 = 0x08000000,
// _32MDiv42 = 0x06000000,
// _32MDiv63 = 0x04100000,
// _32MDiv125 = 0x020C0000,
// }
/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload.
///
/// For more details about EasyDMA, consult the module documentation.
@ -185,7 +258,7 @@ impl<'d, T: Instance> I2S<'d, T> {
/// Create a new I2S
pub fn new(
i2s: impl Peripheral<P = T> + 'd,
// irq: impl Peripheral<P = T::Interrupt> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
mck: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd,
lrck: impl Peripheral<P = impl GpioPin> + 'd,
@ -196,7 +269,7 @@ impl<'d, T: Instance> I2S<'d, T> {
into_ref!(mck, sck, lrck, sdin, sdout);
Self::new_inner(
i2s,
// irq,
irq,
mck.map_into(),
sck.map_into(),
lrck.map_into(),
@ -208,7 +281,7 @@ impl<'d, T: Instance> I2S<'d, T> {
fn new_inner(
i2s: impl Peripheral<P = T> + 'd,
// irq: impl Peripheral<P = T::Interrupt> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
mck: PeripheralRef<'d, AnyPin>,
sck: PeripheralRef<'d, AnyPin>,
lrck: PeripheralRef<'d, AnyPin>,
@ -216,36 +289,12 @@ impl<'d, T: Instance> I2S<'d, T> {
sdout: PeripheralRef<'d, AnyPin>,
config: Config,
) -> 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(&r.config, &config);
r.psel.mck.write(|w| {
unsafe { w.bits(mck.psel_bits()) };
w.connect().connected()
});
r.psel.sck.write(|w| {
unsafe { w.bits(sck.psel_bits()) };
w.connect().connected()
});
r.psel.lrck.write(|w| {
unsafe { w.bits(lrck.psel_bits()) };
w.connect().connected()
});
r.psel.sdin.write(|w| {
unsafe { w.bits(sdin.psel_bits()) };
w.connect().connected()
});
r.psel.sdout.write(|w| {
unsafe { w.bits(sdout.psel_bits()) };
w.connect().connected()
});
Self::select_pins(&r.psel, mck, sck, lrck, sdin, sdout);
Self::setup_interrupt(irq, r);
r.enable.write(|w| w.enable().enabled());
@ -322,19 +371,87 @@ impl<'d, T: Instance> I2S<'d, T> {
self.input.rx(buffer).await
}
fn apply_config(c: &CONFIG, config: &Config) {
// TODO support slave too
c.mcken.write(|w| w.mcken().enabled());
c.mckfreq.write(|w| w.mckfreq()._32mdiv16());
c.mode.write(|w| w.mode().master());
fn on_interrupt(_: *mut ()) {
let r = T::regs();
let s = T::state();
if r.events_txptrupd.read().bits() != 0 {
s.tx_waker.wake();
r.intenclr.write(|w| w.txptrupd().clear());
}
if r.events_rxptrupd.read().bits() != 0 {
s.rx_waker.wake();
r.intenclr.write(|w| w.rxptrupd().clear());
}
}
fn apply_config(c: &CONFIG, config: &Config) {
match config.mode {
Mode::Master { freq, ratio } => {
c.mode.write(|w| w.mode().master());
c.mcken.write(|w| w.mcken().enabled());
c.mckfreq
.write(|w| unsafe { w.mckfreq().bits(freq.to_register_value()) });
c.ratio.write(|w| unsafe { w.ratio().bits(ratio.into()) });
}
Mode::Slave => {
c.mode.write(|w| w.mode().slave());
}
};
c.ratio.write(|w| unsafe { w.ratio().bits(config.ratio.into()) });
c.swidth.write(|w| unsafe { w.swidth().bits(config.swidth.into()) });
c.align.write(|w| w.align().bit(config.align.into()));
c.format.write(|w| w.format().bit(config.format.into()));
c.channels
.write(|w| unsafe { w.channels().bits(config.channels.into()) });
}
fn select_pins(
psel: &PSEL,
mck: PeripheralRef<'d, AnyPin>,
sck: PeripheralRef<'d, AnyPin>,
lrck: PeripheralRef<'d, AnyPin>,
sdin: PeripheralRef<'d, AnyPin>,
sdout: PeripheralRef<'d, AnyPin>,
) {
psel.mck.write(|w| {
unsafe { w.bits(mck.psel_bits()) };
w.connect().connected()
});
psel.sck.write(|w| {
unsafe { w.bits(sck.psel_bits()) };
w.connect().connected()
});
psel.lrck.write(|w| {
unsafe { w.bits(lrck.psel_bits()) };
w.connect().connected()
});
psel.sdin.write(|w| {
unsafe { w.bits(sdin.psel_bits()) };
w.connect().connected()
});
psel.sdout.write(|w| {
unsafe { w.bits(sdout.psel_bits()) };
w.connect().connected()
});
}
fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>, r: &RegisterBlock) {
irq.set_handler(Self::on_interrupt);
irq.set_priority(Priority::P1); // TODO review priorities
irq.unpend();
irq.enable();
r.intenclr.write(|w| w.rxptrupd().clear());
r.intenclr.write(|w| w.txptrupd().clear());
r.events_rxptrupd.reset();
r.events_txptrupd.reset();
}
}
impl<'d, T: Instance> I2sOutput<'d, T> {
@ -360,15 +477,40 @@ impl<'d, T: Instance> I2sOutput<'d, T> {
}
let r = T::regs();
let _s = T::state();
let s = T::state();
// TODO we can not progress until the last buffer written in TXD.PTR
// has started the transmission.
// We can use some sync primitive from `embassy-sync`.
let drop = OnDrop::new(move || {
trace!("write drop: stopping");
r.intenclr.write(|w| w.txptrupd().clear());
r.events_txptrupd.reset();
r.config.txen.write(|w| w.txen().disabled());
// TX is stopped almost instantly, spinning is fine.
while r.events_txptrupd.read().bits() == 0 {}
trace!("write drop: stopped");
});
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
r.intenset.write(|w| w.txptrupd().set());
compiler_fence(Ordering::SeqCst);
poll_fn(|cx| {
s.tx_waker.register(cx.waker());
if r.events_txptrupd.read().bits() != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
compiler_fence(Ordering::SeqCst);
drop.defuse();
Ok(())
}
}
@ -451,23 +593,19 @@ impl Buffer for &[i32] {
}
pub(crate) mod sealed {
use core::sync::atomic::AtomicU8;
use embassy_sync::waitqueue::AtomicWaker;
//use super::*;
pub struct State {
pub input_waker: AtomicWaker,
pub output_waker: AtomicWaker,
pub buffers_refcount: AtomicU8,
pub rx_waker: AtomicWaker,
pub tx_waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
input_waker: AtomicWaker::new(),
output_waker: AtomicWaker::new(),
buffers_refcount: AtomicU8::new(0),
rx_waker: AtomicWaker::new(),
tx_waker: AtomicWaker::new(),
}
}
}

View File

@ -74,7 +74,7 @@ pub mod buffered_uarte;
pub mod gpio;
#[cfg(feature = "gpiote")]
pub mod gpiote;
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840",))]
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
pub mod i2s;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
pub mod nvmc;