Fix buffer overruns
This commit is contained in:
parent
4fe834db2f
commit
1ed260b105
@ -3,6 +3,7 @@
|
|||||||
//! I2S
|
//! I2S
|
||||||
|
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
|
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;
|
||||||
|
|
||||||
@ -10,7 +11,6 @@ use embassy_cortex_m::interrupt::{InterruptExt, Priority};
|
|||||||
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::sealed::Pin as _;
|
|
||||||
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, CONFIG, PSEL};
|
||||||
@ -31,9 +31,9 @@ pub const SRAM_UPPER: usize = 0x3000_0000;
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
BufferTooLong,
|
BufferTooLong,
|
||||||
BufferZeroLength,
|
BufferZeroLength,
|
||||||
DMABufferNotInDataMemory,
|
BufferNotInDataMemory,
|
||||||
BufferMisaligned,
|
BufferMisaligned,
|
||||||
// TODO: add other error variants.
|
BufferLengthMisaligned,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const MODE_MASTER_8000: Mode = Mode::Master {
|
pub const MODE_MASTER_8000: Mode = Mode::Master {
|
||||||
@ -122,12 +122,12 @@ pub enum MckFreq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MckFreq {
|
impl MckFreq {
|
||||||
const REGISTER_VALUES: &[u32] = &[
|
const REGISTER_VALUES: &'static [u32] = &[
|
||||||
0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000,
|
0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000,
|
||||||
0x08000000, 0x06000000, 0x04100000, 0x020C0000,
|
0x08000000, 0x06000000, 0x04100000, 0x020C0000,
|
||||||
];
|
];
|
||||||
|
|
||||||
const FREQUENCIES: &[u32] = &[
|
const FREQUENCIES: &'static [u32] = &[
|
||||||
4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936,
|
4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936,
|
||||||
256000,
|
256000,
|
||||||
];
|
];
|
||||||
@ -162,7 +162,7 @@ pub enum Ratio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ratio {
|
impl Ratio {
|
||||||
const RATIOS: &[u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512];
|
const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512];
|
||||||
|
|
||||||
pub fn to_divisor(&self) -> u32 {
|
pub fn to_divisor(&self) -> u32 {
|
||||||
Self::RATIOS[u8::from(*self) as usize]
|
Self::RATIOS[u8::from(*self) as usize]
|
||||||
@ -234,23 +234,10 @@ impl From<Channels> for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interface to the UARTE 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.
|
/// For more details about EasyDMA, consult the module documentation.
|
||||||
pub struct I2S<'d, T: Instance> {
|
pub struct I2S<'d, T: Instance> {
|
||||||
output: I2sOutput<'d, T>,
|
|
||||||
input: I2sInput<'d, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transmitter interface to the UARTE peripheral obtained
|
|
||||||
/// via [Uarte]::split.
|
|
||||||
pub struct I2sOutput<'d, T: Instance> {
|
|
||||||
_p: PeripheralRef<'d, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receiver interface to the UARTE peripheral obtained
|
|
||||||
/// via [Uarte]::split.
|
|
||||||
pub struct I2sInput<'d, T: Instance> {
|
|
||||||
_p: PeripheralRef<'d, T>,
|
_p: PeripheralRef<'d, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,78 +285,19 @@ impl<'d, T: Instance> I2S<'d, T> {
|
|||||||
|
|
||||||
r.enable.write(|w| w.enable().enabled());
|
r.enable.write(|w| w.enable().enabled());
|
||||||
|
|
||||||
Self {
|
Self { _p: i2s }
|
||||||
output: I2sOutput {
|
|
||||||
_p: unsafe { i2s.clone_unchecked() },
|
|
||||||
},
|
|
||||||
input: I2sInput { _p: i2s },
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables the I2S module.
|
pub fn output(self) -> Output<'d, T> {
|
||||||
#[inline(always)]
|
Output { _p: self._p }
|
||||||
pub fn enable(&self) -> &Self {
|
|
||||||
let r = T::regs();
|
|
||||||
r.enable.write(|w| w.enable().enabled());
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disables the I2S module.
|
pub fn input(self) -> Input<'d, T> {
|
||||||
#[inline(always)]
|
Input { _p: self._p }
|
||||||
pub fn disable(&self) -> &Self {
|
|
||||||
let r = T::regs();
|
|
||||||
r.enable.write(|w| w.enable().disabled());
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts I2S transfer.
|
pub fn full_duplex(self) -> FullDuplex<'d, T> {
|
||||||
#[inline(always)]
|
FullDuplex { _p: self._p }
|
||||||
pub fn start(&self) -> &Self {
|
|
||||||
let r = T::regs();
|
|
||||||
self.enable();
|
|
||||||
trace!("START");
|
|
||||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stops the I2S transfer and waits until it has stopped.
|
|
||||||
#[inline(always)]
|
|
||||||
pub async fn stop(&self) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enables/disables I2S transmission (TX).
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_tx_enabled(&self, enabled: bool) -> &Self {
|
|
||||||
let r = T::regs();
|
|
||||||
r.config.txen.write(|w| w.txen().bit(enabled));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enables/disables I2S reception (RX).
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_rx_enabled(&self, enabled: bool) -> &Self {
|
|
||||||
let r = T::regs();
|
|
||||||
r.config.rxen.write(|w| w.rxen().bit(enabled));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transmits the given `buffer`.
|
|
||||||
/// Buffer address must be 4 byte aligned and located in RAM.
|
|
||||||
pub async fn tx<B>(&mut self, buffer: B) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
B: Buffer,
|
|
||||||
{
|
|
||||||
self.output.tx(buffer).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receives data into the given `buffer` until it's filled.
|
|
||||||
/// Buffer address must be 4 byte aligned and located in RAM.
|
|
||||||
pub async fn rx<B>(&mut self, buffer: B) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
B: Buffer,
|
|
||||||
{
|
|
||||||
self.input.rx(buffer).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_config(c: &CONFIG, config: &Config) {
|
fn apply_config(c: &CONFIG, config: &Config) {
|
||||||
@ -433,103 +361,94 @@ impl<'d, T: Instance> I2S<'d, T> {
|
|||||||
irq.unpend();
|
irq.unpend();
|
||||||
irq.enable();
|
irq.enable();
|
||||||
|
|
||||||
r.intenclr.write(|w| w.rxptrupd().clear());
|
let device = Device::<T>::new();
|
||||||
r.intenclr.write(|w| w.txptrupd().clear());
|
device.disable_tx_ptr_interrupt();
|
||||||
|
device.disable_rx_ptr_interrupt();
|
||||||
|
|
||||||
r.events_rxptrupd.reset();
|
device.reset_tx_ptr_event();
|
||||||
r.events_txptrupd.reset();
|
device.reset_rx_ptr_event();
|
||||||
|
|
||||||
r.intenset.write(|w| w.rxptrupd().set());
|
device.enable_tx_ptr_interrupt();
|
||||||
r.intenset.write(|w| w.txptrupd().set());
|
device.enable_rx_ptr_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_interrupt(_: *mut ()) {
|
fn on_interrupt(_: *mut ()) {
|
||||||
let r = T::regs();
|
let device = Device::<T>::new();
|
||||||
let s = T::state();
|
let s = T::state();
|
||||||
|
|
||||||
if r.events_txptrupd.read().bits() != 0 {
|
if device.is_tx_ptr_updated() {
|
||||||
trace!("[{}] INT", s.seq.load(Ordering::Relaxed));
|
trace!("TX INT");
|
||||||
s.tx_waker.wake();
|
s.tx_waker.wake();
|
||||||
r.intenclr.write(|w| w.txptrupd().clear());
|
device.disable_tx_ptr_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.events_rxptrupd.read().bits() != 0 {
|
if device.is_rx_ptr_updated() {
|
||||||
|
trace!("RX INT");
|
||||||
s.rx_waker.wake();
|
s.rx_waker.wake();
|
||||||
r.intenclr.write(|w| w.rxptrupd().clear());
|
device.disable_rx_ptr_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
s.overruns.fetch_add(1, Ordering::Relaxed);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> I2sOutput<'d, T> {
|
pub struct Output<'d, T: Instance> {
|
||||||
/// Transmits the given `buffer`.
|
_p: PeripheralRef<'d, T>,
|
||||||
/// Buffer address must be 4 byte aligned and located in RAM.
|
}
|
||||||
#[allow(unused_mut)]
|
|
||||||
pub async fn tx<B>(&mut self, buffer: B) -> Result<(), Error>
|
impl<'d, T: Instance> Output<'d, T> {
|
||||||
|
/// Starts I2S transfer.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn start<B>(&self, buffer: B) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
B: Buffer,
|
B: Buffer,
|
||||||
{
|
{
|
||||||
let ptr = buffer.bytes_ptr();
|
// TODO what to do if it is started already?
|
||||||
let len = buffer.bytes_len();
|
|
||||||
|
|
||||||
if ptr as u32 % 4 != 0 {
|
let device = Device::<T>::new();
|
||||||
return Err(Error::BufferMisaligned);
|
device.enable();
|
||||||
}
|
device.set_tx_buffer(buffer)?;
|
||||||
if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
|
device.enable_tx();
|
||||||
return Err(Error::DMABufferNotInDataMemory);
|
device.start();
|
||||||
}
|
|
||||||
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
|
|
||||||
if maxcnt > MAX_DMA_MAXCNT {
|
|
||||||
return Err(Error::BufferTooLong);
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = T::regs();
|
Ok(())
|
||||||
let s = T::state();
|
}
|
||||||
|
|
||||||
let seq = s.seq.fetch_add(1, Ordering::Relaxed);
|
/// Stops the I2S transfer and waits until it has stopped.
|
||||||
if r.events_txptrupd.read().bits() != 0 && seq > 1 {
|
#[inline(always)]
|
||||||
warn!("XRUN!");
|
pub async fn stop(&self) {
|
||||||
loop {}
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
let drop = OnDrop::new(move || {
|
/// Transmits the given `buffer`.
|
||||||
trace!("write drop: stopping");
|
/// 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
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
trace!("SEND: {}", buffer.bytes_ptr() as u32);
|
||||||
|
|
||||||
r.intenclr.write(|w| w.txptrupd().clear());
|
let device = Device::<T>::new();
|
||||||
r.events_txptrupd.reset();
|
let drop = device.on_tx_drop();
|
||||||
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");
|
|
||||||
});
|
|
||||||
|
|
||||||
trace!("[{}] PTR", s.seq.load(Ordering::Relaxed));
|
|
||||||
r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
|
|
||||||
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
|
||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
s.tx_waker.register(cx.waker());
|
T::state().tx_waker.register(cx.waker());
|
||||||
if r.events_txptrupd.read().bits() != 0 || seq == 0 {
|
|
||||||
trace!("[{}] POLL Ready", s.seq.load(Ordering::Relaxed));
|
if device.is_tx_ptr_updated() {
|
||||||
r.events_txptrupd.reset();
|
trace!("TX POLL: Ready");
|
||||||
r.intenset.write(|w| w.txptrupd().set());
|
device.reset_tx_ptr_event();
|
||||||
let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed);
|
device.enable_tx_ptr_interrupt();
|
||||||
if overruns != 0 {
|
|
||||||
warn!("XRUN: {}", overruns);
|
|
||||||
s.overruns.store(0, Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
} else {
|
} else {
|
||||||
trace!("[{}] POLL Pending", s.seq.load(Ordering::Relaxed));
|
trace!("TX POLL: Pending");
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
device.set_tx_buffer(buffer)?;
|
||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
drop.defuse();
|
drop.defuse();
|
||||||
|
|
||||||
@ -537,40 +456,180 @@ impl<'d, T: Instance> I2sOutput<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> I2sInput<'d, T> {
|
pub struct Input<'d, T: Instance> {
|
||||||
/// Receives into the given `buffer`.
|
_p: PeripheralRef<'d, T>,
|
||||||
/// Buffer address must be 4 byte aligned and located in RAM.
|
}
|
||||||
#[allow(unused_mut)]
|
|
||||||
pub async fn rx<B>(&mut self, buffer: B) -> Result<(), Error>
|
impl<'d, T: Instance> Input<'d, T> {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FullDuplex<'d, T: Instance> {
|
||||||
|
_p: PeripheralRef<'d, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> FullDuplex<'d, T> {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Device<T>(&'static RegisterBlock, PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: Instance> Device<T> {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self(T::regs(), PhantomData)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable(&self) {
|
||||||
|
trace!("ENABLED");
|
||||||
|
self.0.enable.write(|w| w.enable().enabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn disable(&self) {
|
||||||
|
trace!("DISABLED");
|
||||||
|
self.0.enable.write(|w| w.enable().disabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn enable_tx(&self) {
|
||||||
|
trace!("TX ENABLED");
|
||||||
|
self.0.config.txen.write(|w| w.txen().enabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn disable_tx(&self) {
|
||||||
|
trace!("TX DISABLED");
|
||||||
|
self.0.config.txen.write(|w| w.txen().disabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn enable_rx(&self) {
|
||||||
|
trace!("RX ENABLED");
|
||||||
|
self.0.config.rxen.write(|w| w.rxen().enabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn disable_rx(&self) {
|
||||||
|
trace!("RX DISABLED");
|
||||||
|
self.0.config.rxen.write(|w| w.rxen().disabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn start(&self) {
|
||||||
|
trace!("START");
|
||||||
|
self.0.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_tx_buffer<B>(&self, buffer: B) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
B: Buffer,
|
B: Buffer,
|
||||||
{
|
{
|
||||||
let ptr = buffer.bytes_ptr();
|
let (ptr, maxcnt) = Self::validate_buffer(buffer)?;
|
||||||
let len = buffer.bytes_len();
|
self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
|
||||||
|
self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) });
|
||||||
if ptr as u32 % 4 != 0 {
|
|
||||||
return Err(Error::BufferMisaligned);
|
|
||||||
}
|
|
||||||
if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
|
|
||||||
return Err(Error::DMABufferNotInDataMemory);
|
|
||||||
}
|
|
||||||
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
|
|
||||||
if maxcnt > MAX_DMA_MAXCNT {
|
|
||||||
return Err(Error::BufferTooLong);
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = T::regs();
|
|
||||||
let _s = T::state();
|
|
||||||
|
|
||||||
// TODO we can not progress until the last buffer written in RXD.PTR
|
|
||||||
// has started the transmission.
|
|
||||||
// We can use some sync primitive from `embassy-sync`.
|
|
||||||
|
|
||||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
|
||||||
r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_rx_buffer<B>(&self, buffer: B) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
let (ptr, maxcnt) = Self::validate_buffer(buffer)?;
|
||||||
|
self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
|
||||||
|
self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_tx_ptr_updated(&self) -> bool {
|
||||||
|
self.0.events_txptrupd.read().bits() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_rx_ptr_updated(&self) -> bool {
|
||||||
|
self.0.events_rxptrupd.read().bits() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset_tx_ptr_event(&self) {
|
||||||
|
trace!("TX PTR EVENT: Reset");
|
||||||
|
self.0.events_txptrupd.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset_rx_ptr_event(&self) {
|
||||||
|
trace!("RX PTR EVENT: Reset");
|
||||||
|
self.0.events_rxptrupd.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn disable_tx_ptr_interrupt(&self) {
|
||||||
|
trace!("TX PTR INTERRUPT: Disabled");
|
||||||
|
self.0.intenclr.write(|w| w.txptrupd().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn disable_rx_ptr_interrupt(&self) {
|
||||||
|
trace!("RX PTR INTERRUPT: Disabled");
|
||||||
|
self.0.intenclr.write(|w| w.rxptrupd().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn enable_tx_ptr_interrupt(&self) {
|
||||||
|
trace!("TX PTR INTERRUPT: Enabled");
|
||||||
|
self.0.intenset.write(|w| w.txptrupd().set());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn enable_rx_ptr_interrupt(&self) {
|
||||||
|
trace!("RX PTR INTERRUPT: Enabled");
|
||||||
|
self.0.intenclr.write(|w| w.rxptrupd().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn on_tx_drop(&self) -> OnDrop<fn()> {
|
||||||
|
OnDrop::new(move || {
|
||||||
|
trace!("TX DROP: Stopping");
|
||||||
|
|
||||||
|
let device = Device::<T>::new();
|
||||||
|
device.disable_tx_ptr_interrupt();
|
||||||
|
device.reset_tx_ptr_event();
|
||||||
|
device.disable_tx();
|
||||||
|
|
||||||
|
// TX is stopped almost instantly, spinning is fine.
|
||||||
|
while !device.is_tx_ptr_updated() {}
|
||||||
|
|
||||||
|
trace!("TX DROP: Stopped");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_buffer<B>(buffer: B) -> Result<(u32, u32), Error>
|
||||||
|
where
|
||||||
|
B: Buffer,
|
||||||
|
{
|
||||||
|
let ptr = buffer.bytes_ptr() as u32;
|
||||||
|
let len = buffer.bytes_len();
|
||||||
|
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
|
||||||
|
|
||||||
|
trace!("PTR={}, MAXCNT={}", ptr, maxcnt);
|
||||||
|
|
||||||
|
// TODO can we avoid repeating all those runtime checks for the same buffer again and again?
|
||||||
|
|
||||||
|
if ptr % 4 != 0 {
|
||||||
|
Err(Error::BufferMisaligned)
|
||||||
|
} else if len % 4 != 0 {
|
||||||
|
Err(Error::BufferLengthMisaligned)
|
||||||
|
} else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
|
||||||
|
Err(Error::BufferNotInDataMemory)
|
||||||
|
} else if maxcnt > MAX_DMA_MAXCNT {
|
||||||
|
Err(Error::BufferTooLong)
|
||||||
|
} else {
|
||||||
|
Ok((ptr, maxcnt))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Buffer: Sized {
|
pub trait Buffer: Sized {
|
||||||
@ -578,10 +637,10 @@ pub trait Buffer: Sized {
|
|||||||
fn bytes_len(&self) -> usize;
|
fn bytes_len(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Buffer for &[u8] {
|
impl Buffer for &[i8] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bytes_ptr(&self) -> *const u8 {
|
fn bytes_ptr(&self) -> *const u8 {
|
||||||
self.as_ptr()
|
self.as_ptr() as *const u8
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -610,7 +669,7 @@ impl Buffer for &[i32] {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bytes_len(&self) -> usize {
|
fn bytes_len(&self) -> usize {
|
||||||
self.len() * core::mem::size_of::<i16>()
|
self.len() * core::mem::size_of::<i32>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,8 +683,6 @@ pub(crate) mod sealed {
|
|||||||
pub struct State {
|
pub struct State {
|
||||||
pub rx_waker: AtomicWaker,
|
pub rx_waker: AtomicWaker,
|
||||||
pub tx_waker: AtomicWaker,
|
pub tx_waker: AtomicWaker,
|
||||||
pub overruns: AtomicI32,
|
|
||||||
pub seq: AtomicI32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
@ -633,8 +690,6 @@ pub(crate) mod sealed {
|
|||||||
Self {
|
Self {
|
||||||
rx_waker: AtomicWaker::new(),
|
rx_waker: AtomicWaker::new(),
|
||||||
tx_waker: AtomicWaker::new(),
|
tx_waker: AtomicWaker::new(),
|
||||||
overruns: AtomicI32::new(0),
|
|
||||||
seq: AtomicI32::new(0),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -654,7 +709,7 @@ pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
|||||||
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 {
|
||||||
fn regs() -> &'static pac::i2s::RegisterBlock {
|
fn regs() -> &'static crate::pac::i2s::RegisterBlock {
|
||||||
unsafe { &*pac::$pac_type::ptr() }
|
unsafe { &*pac::$pac_type::ptr() }
|
||||||
}
|
}
|
||||||
fn state() -> &'static crate::i2s::sealed::State {
|
fn state() -> &'static crate::i2s::sealed::State {
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
// Example inspired by RTIC's I2S demo: https://github.com/nrf-rs/nrf-hal/blob/master/examples/i2s-controller-demo/src/main.rs
|
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use core::f32::consts::PI;
|
use core::f32::consts::PI;
|
||||||
|
|
||||||
use defmt::{error, info};
|
use defmt::{error, info, trace};
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000, Channels};
|
use embassy_nrf::gpio::{Input, Pin, Pull};
|
||||||
|
use embassy_nrf::i2s::{Channels, MckFreq, Mode, Ratio, SampleWidth, MODE_MASTER_32000};
|
||||||
use embassy_nrf::pac::ficr::info;
|
use embassy_nrf::pac::ficr::info;
|
||||||
use embassy_nrf::{i2s, interrupt};
|
use embassy_nrf::{i2s, interrupt};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
@ -32,60 +31,79 @@ impl<T> AsMut<T> for AlignedBuffer<T> {
|
|||||||
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_16000;
|
config.mode = MODE_MASTER_32000;
|
||||||
config.mode = Mode::Master {
|
// config.mode = Mode::Master {
|
||||||
freq: MckFreq::_32MDiv10,
|
// freq: MckFreq::_32MDiv10,
|
||||||
ratio: Ratio::_256x,
|
// ratio: Ratio::_256x,
|
||||||
}; // 12500 Hz
|
// }; // 12500 Hz
|
||||||
config.channels = Channels::Left;
|
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);
|
||||||
|
|
||||||
let irq = interrupt::take!(I2S);
|
// Wait for a button press
|
||||||
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);
|
// let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up);
|
||||||
|
// btn1.wait_for_low().await;
|
||||||
|
|
||||||
const BUF_SAMPLES: usize = 500;
|
let irq = interrupt::take!(I2S);
|
||||||
const BUF_SIZE: usize = BUF_SAMPLES;
|
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 buf = AlignedBuffer([0i16; BUF_SIZE]);
|
|
||||||
|
type Sample = i16;
|
||||||
|
const MAX_UNIPOLAR_VALUE: Sample = (1 << 15) as Sample;
|
||||||
|
const NUM_SAMPLES: usize = 2000;
|
||||||
|
let mut buffers: [AlignedBuffer<[Sample; NUM_SAMPLES]>; 3] = [
|
||||||
|
AlignedBuffer([0; NUM_SAMPLES]),
|
||||||
|
AlignedBuffer([0; NUM_SAMPLES]),
|
||||||
|
AlignedBuffer([0; NUM_SAMPLES]),
|
||||||
|
];
|
||||||
|
|
||||||
let mut carrier = SineOsc::new();
|
let mut carrier = SineOsc::new();
|
||||||
carrier.set_frequency(16.0, inv_sample_rate);
|
|
||||||
|
|
||||||
// let mut modulator = SineOsc::new();
|
let mut freq_mod = SineOsc::new();
|
||||||
// modulator.set_frequency(0.01, inv_sample_rate);
|
freq_mod.set_frequency(8.0, inv_sample_rate);
|
||||||
// modulator.set_amplitude(0.2);
|
freq_mod.set_amplitude(1.0);
|
||||||
|
|
||||||
let mut generate = |buf: &mut [i16]| {
|
let mut amp_mod = SineOsc::new();
|
||||||
for sample in buf.as_mut() {
|
amp_mod.set_frequency(4.0, inv_sample_rate);
|
||||||
|
amp_mod.set_amplitude(0.5);
|
||||||
|
|
||||||
|
let mut generate = |buf: &mut [Sample]| {
|
||||||
|
let ptr = buf as *const [Sample] as *const Sample as u32;
|
||||||
|
trace!("GEN: {}", ptr);
|
||||||
|
|
||||||
|
for sample in &mut buf.as_mut().chunks_mut(1) {
|
||||||
let signal = carrier.generate();
|
let signal = carrier.generate();
|
||||||
// let modulation = bipolar_to_unipolar(modulator.generate());
|
let freq_modulation = bipolar_to_unipolar(freq_mod.generate());
|
||||||
// carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate);
|
carrier.set_frequency(220.0 + 220.0 * freq_modulation, inv_sample_rate);
|
||||||
// carrier.set_amplitude((modulation);
|
let amp_modulation = bipolar_to_unipolar(amp_mod.generate());
|
||||||
let value = (i16::MAX as f32 * signal) as i16;
|
carrier.set_amplitude(amp_modulation);
|
||||||
*sample = value;
|
let value = (MAX_UNIPOLAR_VALUE as f32 * signal) as Sample;
|
||||||
|
sample[0] = value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
generate(buf.as_mut().as_mut_slice());
|
generate(buffers[0].as_mut().as_mut_slice());
|
||||||
|
generate(buffers[1].as_mut().as_mut_slice());
|
||||||
|
|
||||||
if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await {
|
i2s.start(buffers[0].as_ref().as_slice()).expect("I2S Start");
|
||||||
error!("{}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
i2s.set_tx_enabled(true);
|
|
||||||
i2s.start();
|
|
||||||
|
|
||||||
|
let mut index = 1;
|
||||||
loop {
|
loop {
|
||||||
generate(buf.as_mut().as_mut_slice());
|
if let Err(err) = i2s.send(buffers[index].as_ref().as_slice()).await {
|
||||||
|
|
||||||
if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await {
|
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
index += 1;
|
||||||
|
if index >= 3 {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
generate(buffers[index].as_mut().as_mut_slice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct SineOsc {
|
struct SineOsc {
|
||||||
amplitude: f32,
|
amplitude: f32,
|
||||||
modulo: f32,
|
modulo: f32,
|
||||||
|
Loading…
Reference in New Issue
Block a user