diff --git a/CHANGELOG.md b/CHANGELOG.md index 813d45d62f7..5d38fd45462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implement `defmt::Format` for more types (#786) - Add new_no_miso to Spi FullDuplexMode (#794) - Add UART support for splitting into TX and RX (#754) +- Async support for I2S (#801) ### Changed diff --git a/esp-hal-common/src/dma/gdma.rs b/esp-hal-common/src/dma/gdma.rs index e78328afdce..699fbcfd032 100644 --- a/esp-hal-common/src/dma/gdma.rs +++ b/esp-hal-common/src/dma/gdma.rs @@ -153,6 +153,59 @@ macro_rules! impl_channel { dma.[].modify(|_, w| w.outlink_start().set_bit()); } + fn clear_ch_out_done() { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] + dma.[].write(|w| w.out_done().set_bit()); + #[cfg(any(esp32c6, esp32h2, esp32s3))] + dma.[].write(|w| w.out_done().set_bit()); + } + + fn is_ch_out_done_set() -> bool { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] + let ret = dma.[].read().out_done().bit(); + #[cfg(any(esp32c6, esp32h2, esp32s3))] + let ret = dma.[].read().out_done().bit(); + + ret + } + + fn listen_ch_out_done() { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { + dma.[].modify(|_, w| w.out_done().set_bit()) + } else { + dma.[].modify(|_, w| w.out_done().set_bit()) + } + } + } + + fn unlisten_ch_out_done() { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { + dma.[].modify(|_, w| w.out_done().clear_bit()) + } else { + dma.[].modify(|_, w| w.out_done().clear_bit()) + } + } + } + + fn is_listening_ch_out_done() -> bool { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { + dma.[].read().out_done().bit() + } else { + dma.[].read().out_done().bit() + } + } + } + fn is_out_done() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; @@ -413,6 +466,59 @@ macro_rules! impl_channel { } } } + + fn listen_ch_in_done(){ + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { + dma.[].modify(|_, w| w.in_done().set_bit()) + } else { + dma.[].modify(|_, w| w.in_done().set_bit()) + } + } + } + + fn clear_ch_in_done(){ + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] + dma.[].write(|w| w.in_done().set_bit()); + #[cfg(any(esp32c6, esp32h2, esp32s3))] + dma.[].write(|w| w.in_done().set_bit()); + } + + fn is_ch_in_done_set() -> bool { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] + let ret = dma.[].read().in_done().bit(); + #[cfg(any(esp32c6, esp32h2, esp32s3))] + let ret = dma.[].read().in_done().bit(); + + ret + } + + fn unlisten_ch_in_done() { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { + dma.[].modify(|_, w| w.in_done().clear_bit()) + } else { + dma.[].modify(|_, w| w.in_done().clear_bit()) + } + } + } + + fn is_listening_ch_in_done() -> bool { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { + dma.[].read().in_done().bit() + } else { + dma.[].read().in_done().bit() + } + } + } } #[non_exhaustive] diff --git a/esp-hal-common/src/dma/mod.rs b/esp-hal-common/src/dma/mod.rs index f718313c096..6f32bee1cc7 100644 --- a/esp-hal-common/src/dma/mod.rs +++ b/esp-hal-common/src/dma/mod.rs @@ -284,6 +284,16 @@ pub trait RxPrivate { len: usize, ) -> Result<(), DmaError>; + fn listen_ch_in_done(&self); + + fn clear_ch_in_done(&self); + + fn is_ch_in_done_set(&self) -> bool; + + fn unlisten_ch_in_done(&self); + + fn is_listening_ch_in_done(&self) -> bool; + fn is_done(&self) -> bool; fn is_listening_eof(&self) -> bool; @@ -457,6 +467,26 @@ where Ok(()) } + fn listen_ch_in_done(&self) { + R::listen_ch_in_done(); + } + + fn clear_ch_in_done(&self) { + R::clear_ch_in_done(); + } + + fn is_ch_in_done_set(&self) -> bool { + R::is_ch_in_done_set() + } + + fn unlisten_ch_in_done(&self) { + R::unlisten_ch_in_done(); + } + + fn is_listening_ch_in_done(&self) -> bool { + R::is_listening_ch_in_done() + } + fn is_done(&self) -> bool { self.rx_impl.is_done() } @@ -593,6 +623,16 @@ pub trait TxPrivate { len: usize, ) -> Result<(), DmaError>; + fn clear_ch_out_done(&self); + + fn is_ch_out_done_set(&self) -> bool; + + fn listen_ch_out_done(&self); + + fn unlisten_ch_out_done(&self); + + fn is_listening_ch_out_done(&self) -> bool; + fn is_done(&self) -> bool; fn is_listening_eof(&self) -> bool; @@ -680,6 +720,26 @@ where Ok(()) } + fn clear_ch_out_done(&self) { + R::clear_ch_out_done(); + } + + fn is_ch_out_done_set(&self) -> bool { + R::is_ch_out_done_set() + } + + fn listen_ch_out_done(&self) { + R::listen_ch_out_done(); + } + + fn unlisten_ch_out_done(&self) { + R::unlisten_ch_out_done(); + } + + fn is_listening_ch_out_done(&self) -> bool { + R::is_listening_ch_out_done() + } + fn is_done(&self) -> bool { R::is_out_done() } @@ -771,6 +831,26 @@ where Ok(()) } + fn clear_ch_out_done(&self) { + self.tx_impl.clear_ch_out_done(); + } + + fn is_ch_out_done_set(&self) -> bool { + self.tx_impl.is_ch_out_done_set() + } + + fn listen_ch_out_done(&self) { + self.tx_impl.listen_ch_out_done(); + } + + fn unlisten_ch_out_done(&self) { + self.tx_impl.unlisten_ch_out_done(); + } + + fn is_listening_ch_out_done(&self) -> bool { + self.tx_impl.is_listening_ch_out_done() + } + fn is_done(&self) -> bool { self.tx_impl.is_done() } @@ -912,6 +992,11 @@ pub trait RegisterAccess { fn has_out_descriptor_error() -> bool; fn set_out_peripheral(peripheral: u8); fn start_out(); + fn clear_ch_out_done(); + fn is_ch_out_done_set() -> bool; + fn listen_ch_out_done(); + fn unlisten_ch_out_done(); + fn is_listening_ch_out_done() -> bool; fn is_out_done() -> bool; fn is_out_eof_interrupt_set() -> bool; fn reset_out_eof_interrupt(); @@ -937,6 +1022,12 @@ pub trait RegisterAccess { fn listen_out_eof(); fn unlisten_in_eof(); fn unlisten_out_eof(); + + fn listen_ch_in_done(); + fn clear_ch_in_done(); + fn is_ch_in_done_set() -> bool; + fn unlisten_ch_in_done(); + fn is_listening_ch_in_done() -> bool; } pub trait ChannelTypes { @@ -1047,6 +1138,74 @@ pub(crate) mod asynch { } } + pub struct DmaTxDoneChFuture<'a, TX> { + pub(crate) tx: &'a mut TX, + _a: (), + } + + impl<'a, TX> DmaTxDoneChFuture<'a, TX> + where + TX: Tx, + { + pub fn new(tx: &'a mut TX) -> Self { + tx.listen_ch_out_done(); + Self { tx, _a: () } + } + } + + impl<'a, TX> core::future::Future for DmaTxDoneChFuture<'a, TX> + where + TX: Tx, + { + type Output = (); // TODO handle DMA errors + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll { + TX::waker().register(cx.waker()); + if self.tx.is_listening_ch_out_done() { + Poll::Pending + } else { + Poll::Ready(()) + } + } + } + + pub struct DmaRxDoneChFuture<'a, RX> { + pub(crate) rx: &'a mut RX, + _a: (), + } + + impl<'a, RX> DmaRxDoneChFuture<'a, RX> + where + RX: Rx, + { + pub fn new(rx: &'a mut RX) -> Self { + rx.listen_ch_in_done(); + Self { rx, _a: () } + } + } + + impl<'a, RX> core::future::Future for DmaRxDoneChFuture<'a, RX> + where + RX: Rx, + { + type Output = (); // TODO handle DMA errors + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll { + RX::waker().register(cx.waker()); + if self.rx.is_listening_ch_in_done() { + Poll::Pending + } else { + Poll::Ready(()) + } + } + } + #[cfg(esp32c2)] mod interrupt { use super::*; @@ -1059,7 +1218,7 @@ pub(crate) mod asynch { Channel0TxImpl as ChannelTxImpl, }; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() @@ -1070,6 +1229,12 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } } } @@ -1085,7 +1250,7 @@ pub(crate) mod asynch { Channel0TxImpl as ChannelTxImpl, }; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() @@ -1096,6 +1261,18 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1106,7 +1283,7 @@ pub(crate) mod asynch { Channel1TxImpl as ChannelTxImpl, }; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() @@ -1117,6 +1294,18 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1127,7 +1316,7 @@ pub(crate) mod asynch { Channel2TxImpl as ChannelTxImpl, }; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() @@ -1138,6 +1327,18 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } } @@ -1149,11 +1350,17 @@ pub(crate) mod asynch { fn DMA_IN_CH0() { use crate::dma::gdma::{Channel0 as Channel, Channel0RxImpl as ChannelRxImpl}; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1165,17 +1372,29 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } } #[interrupt] fn DMA_IN_CH1() { use crate::dma::gdma::{Channel1 as Channel, Channel1RxImpl as ChannelRxImpl}; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1187,17 +1406,29 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } } #[interrupt] fn DMA_IN_CH2() { use crate::dma::gdma::{Channel2 as Channel, Channel2RxImpl as ChannelRxImpl}; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1209,6 +1440,12 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } } } @@ -1220,11 +1457,17 @@ pub(crate) mod asynch { fn DMA_IN_CH0() { use crate::dma::gdma::{Channel0 as Channel, Channel0RxImpl as ChannelRxImpl}; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1236,17 +1479,29 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } } #[interrupt] fn DMA_IN_CH1() { use crate::dma::gdma::{Channel1 as Channel, Channel1RxImpl as ChannelRxImpl}; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1258,17 +1513,29 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } } #[interrupt] fn DMA_IN_CH3() { use crate::dma::gdma::{Channel3 as Channel, Channel3RxImpl as ChannelRxImpl}; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1280,17 +1547,29 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } } #[interrupt] fn DMA_IN_CH4() { use crate::dma::gdma::{Channel4 as Channel, Channel4RxImpl as ChannelRxImpl}; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1302,6 +1581,12 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } } } @@ -1317,7 +1602,7 @@ pub(crate) mod asynch { Spi2DmaChannelTxImpl as ChannelTxImpl, }; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() @@ -1328,6 +1613,18 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } #[interrupt] @@ -1338,7 +1635,7 @@ pub(crate) mod asynch { Spi3DmaChannelTxImpl as ChannelTxImpl, }; - if Channel::is_in_done() { + if Channel::is_in_done() && Channel::is_listening_in_eof() { Channel::clear_in_interrupts(); Channel::unlisten_in_eof(); ChannelRxImpl::waker().wake() @@ -1349,6 +1646,85 @@ pub(crate) mod asynch { Channel::unlisten_out_eof(); ChannelTxImpl::waker().wake() } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } + } + + #[interrupt] + fn I2S0() { + use crate::dma::pdma::{ + I2s0DmaChannel as Channel, + I2s0DmaChannelRxImpl as ChannelRxImpl, + I2s0DmaChannelTxImpl as ChannelTxImpl, + }; + + if Channel::is_in_done() && Channel::is_listening_in_eof() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + + if Channel::is_out_done() && Channel::is_listening_out_eof() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } + } + + #[cfg(esp32)] + #[interrupt] + fn I2S1() { + use crate::dma::pdma::{ + I2s1DmaChannel as Channel, + I2s1DmaChannelRxImpl as ChannelRxImpl, + I2s1DmaChannelTxImpl as ChannelTxImpl, + }; + + if Channel::is_in_done() && Channel::is_listening_in_eof() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + + if Channel::is_out_done() && Channel::is_listening_out_eof() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_out_done_set() { + Channel::clear_ch_out_done(); + Channel::unlisten_ch_out_done(); + ChannelTxImpl::waker().wake() + } + + if Channel::is_ch_in_done_set() { + Channel::clear_ch_in_done(); + Channel::unlisten_ch_in_done(); + ChannelRxImpl::waker().wake() + } } } } diff --git a/esp-hal-common/src/dma/pdma.rs b/esp-hal-common/src/dma/pdma.rs index ca9791f45ec..0f276f934d6 100644 --- a/esp-hal-common/src/dma/pdma.rs +++ b/esp-hal-common/src/dma/pdma.rs @@ -105,6 +105,31 @@ macro_rules! ImplSpiChannel { spi.dma_out_link.modify(|_, w| w.outlink_start().set_bit()); } + fn clear_ch_out_done() { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_clr.write(|w| w.out_done_int_clr().set_bit()); + } + + fn is_ch_out_done_set() -> bool { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_raw.read().out_done_int_raw().bit() + } + + fn listen_ch_out_done() { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.modify(|_, w| w.out_done_int_ena().set_bit()); + } + + fn unlisten_ch_out_done() { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.modify(|_, w| w.out_done_int_ena().clear_bit()); + } + + fn is_listening_ch_out_done() -> bool { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.read().out_done_int_ena().bit() + } + fn is_out_done() -> bool { let spi = unsafe { &*crate::peripherals::[]::PTR }; // FIXME this should be out_total_eof_int_raw? but on esp32 this interrupt doesn't seem to fire @@ -226,6 +251,31 @@ macro_rules! ImplSpiChannel { let spi = unsafe { &*crate::peripherals::[]::PTR }; spi.dma_int_ena.modify(|_, w| w.out_total_eof_int_ena().clear_bit()); } + + fn listen_ch_in_done(){ + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.modify(|_, w| w.in_done_int_ena().set_bit()); + } + + fn clear_ch_in_done(){ + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_clr.write(|w| w.in_done_int_clr().set_bit()); + } + + fn is_ch_in_done_set() -> bool { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_raw.read().in_done_int_raw().bit() + } + + fn unlisten_ch_in_done() { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.modify(|_, w| w.in_done_int_ena().clear_bit()); + } + + fn is_listening_ch_in_done() -> bool { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.read().in_done_int_ena().bit() + } } #[non_exhaustive] @@ -369,11 +419,36 @@ macro_rules! ImplI2sChannel { reg_block.out_link.modify(|_, w| w.outlink_start().set_bit()); } - fn is_out_done() -> bool { + fn clear_ch_out_done() { + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_clr.write(|w| w.out_done_int_clr().set_bit()); + } + + fn is_ch_out_done_set() -> bool { let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; reg_block.int_raw.read().out_done_int_raw().bit() } + fn listen_ch_out_done() { + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.modify(|_, w| w.out_done_int_ena().set_bit()); + } + + fn unlisten_ch_out_done() { + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.modify(|_, w| w.out_done_int_ena().clear_bit()); + } + + fn is_listening_ch_out_done() -> bool { + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.read().out_done_int_ena().bit() + } + + fn is_out_done() -> bool { + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_raw.read().out_eof_int_raw().bit() + } + fn last_out_dscr_address() -> usize { let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; reg_block.out_eof_des_addr.read().out_eof_des_addr().bits() as usize @@ -461,23 +536,58 @@ macro_rules! ImplI2sChannel { } fn is_listening_in_eof() -> bool { - todo!() + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.read().in_suc_eof_int_ena().bit() } + fn is_listening_out_eof() -> bool { - todo!() + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.read().out_eof_int_ena().bit() } fn listen_in_eof() { - todo!() + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.modify(|_,w| w.in_suc_eof_int_ena().set_bit() ); } + fn listen_out_eof() { - todo!() + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.modify(|_,w| w.out_eof_int_ena().set_bit() ); } + fn unlisten_in_eof() { - todo!() + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.modify(|_,w| w.in_suc_eof_int_ena().clear_bit() ); } + fn unlisten_out_eof() { - todo!() + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.modify(|_,w| w.out_eof_int_ena().clear_bit() ); + } + + fn listen_ch_in_done(){ + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.modify(|_, w| w.in_done_int_ena().set_bit()); + } + + fn clear_ch_in_done(){ + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_clr.write(|w| w.in_done_int_clr().set_bit()); + } + + fn is_ch_in_done_set() -> bool { + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_raw.read().in_done_int_raw().bit() + } + + fn unlisten_ch_in_done() { + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.modify(|_, w| w.in_done_int_ena().clear_bit()); + } + + fn is_listening_ch_in_done() -> bool { + let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; + reg_block.int_ena.read().in_done_int_ena().bit() } } diff --git a/esp-hal-common/src/i2s.rs b/esp-hal-common/src/i2s.rs index 5b9eefac2e5..af3cc4899bb 100644 --- a/esp-hal-common/src/i2s.rs +++ b/esp-hal-common/src/i2s.rs @@ -864,6 +864,34 @@ where Ok(()) } + + #[cfg(feature = "async")] + fn start_tx_transfer_async( + &mut self, + ptr: *const u8, + len: usize, + circular: bool, + ) -> Result<(), Error> { + // Reset TX unit and TX FIFO + self.register_access.reset_tx(); + + // Enable corresponding interrupts if needed + + // configure DMA outlink + self.tx_channel.prepare_transfer( + self.register_access.get_dma_peripheral(), + circular, + ptr, + len, + )?; + + // set I2S_TX_STOP_EN if needed + + // start: set I2S_TX_START + self.register_access.tx_start(); + + Ok(()) + } } impl<'d, T, P, W, CH> I2sWrite for I2sTx<'d, T, P, CH> @@ -1009,6 +1037,42 @@ where Ok(()) } + + #[cfg(feature = "async")] + fn start_rx_transfer_async( + &mut self, + ptr: *mut u8, + len: usize, + circular: bool, + ) -> Result<(), Error> { + if len % 4 != 0 { + return Err(Error::IllegalArgument); + } + + // Reset TX unit and TX FIFO + self.register_access.reset_rx(); + + // Enable corresponding interrupts if needed + + // configure DMA outlink + self.rx_channel.prepare_transfer( + circular, + self.register_access.get_dma_peripheral(), + ptr, + len, + )?; + + // set I2S_TX_STOP_EN if needed + + // start: set I2S_RX_START + #[cfg(not(esp32))] + self.register_access.rx_start(len - 1); + + #[cfg(esp32)] + self.register_access.rx_start(len); + + Ok(()) + } } impl<'d, W, T, P, CH> I2sRead for I2sRx<'d, T, P, CH> @@ -1076,6 +1140,15 @@ pub trait I2sMclkPin: I2sPins { I: RegisterAccess; } +#[derive(Clone)] +#[non_exhaustive] +pub struct I2sPeripheral0 {} + +#[cfg(any(esp32s3, esp32))] +#[derive(Clone)] +#[non_exhaustive] +pub struct I2sPeripheral1 {} + mod private { use fugit::HertzU32; @@ -1151,10 +1224,10 @@ mod private { } impl Instance for I2S0 { - type Peripheral = I2sPeripheral0; + type Peripheral = super::I2sPeripheral0; - fn register_access(&self) -> I2sPeripheral0 { - I2sPeripheral0 {} + fn register_access(&self) -> super::I2sPeripheral0 { + super::I2sPeripheral0 {} } } @@ -1162,9 +1235,9 @@ mod private { #[cfg(esp32s3)] impl Instance for crate::peripherals::I2S1 { - type Peripheral = I2sPeripheral1; - fn register_access(&self) -> I2sPeripheral1 { - I2sPeripheral1 {} + type Peripheral = super::I2sPeripheral1; + fn register_access(&self) -> super::I2sPeripheral1 { + super::I2sPeripheral1 {} } } @@ -1808,15 +1881,8 @@ mod private { } } - #[derive(Clone)] - pub struct I2sPeripheral0 {} - - #[cfg(any(esp32s3, esp32))] - #[derive(Clone)] - pub struct I2sPeripheral1 {} - #[cfg(any(esp32c3, esp32c6, esp32h2))] - impl Signals for I2sPeripheral0 { + impl Signals for super::I2sPeripheral0 { fn get_peripheral(&self) -> Peripheral { Peripheral::I2s0 } @@ -1855,7 +1921,7 @@ mod private { } #[cfg(esp32s3)] - impl Signals for I2sPeripheral0 { + impl Signals for super::I2sPeripheral0 { fn get_peripheral(&self) -> Peripheral { Peripheral::I2s0 } @@ -1894,7 +1960,7 @@ mod private { } #[cfg(esp32s3)] - impl Signals for I2sPeripheral1 { + impl Signals for super::I2sPeripheral1 { fn get_peripheral(&self) -> Peripheral { Peripheral::I2s1 } @@ -1933,7 +1999,7 @@ mod private { } #[cfg(esp32)] - impl Signals for I2sPeripheral0 { + impl Signals for super::I2sPeripheral0 { fn get_peripheral(&self) -> Peripheral { Peripheral::I2s0 } @@ -1972,7 +2038,7 @@ mod private { } #[cfg(esp32)] - impl Signals for I2sPeripheral1 { + impl Signals for super::I2sPeripheral1 { fn get_peripheral(&self) -> Peripheral { Peripheral::I2s1 } @@ -2011,7 +2077,7 @@ mod private { } #[cfg(esp32s2)] - impl Signals for I2sPeripheral0 { + impl Signals for super::I2sPeripheral0 { fn get_peripheral(&self) -> Peripheral { Peripheral::I2s0 } @@ -2049,26 +2115,26 @@ mod private { } } - impl RegBlock for I2sPeripheral0 { + impl RegBlock for super::I2sPeripheral0 { fn register_block(&self) -> &'static RegisterBlock { unsafe { core::mem::transmute(I2S0::PTR) } } } #[cfg(any(esp32s3, esp32))] - impl RegBlock for I2sPeripheral1 { + impl RegBlock for super::I2sPeripheral1 { fn register_block(&self) -> &'static RegisterBlock { unsafe { core::mem::transmute(crate::peripherals::I2S1::PTR) } } } - impl RegisterAccessPrivate for I2sPeripheral0 {} - impl super::RegisterAccess for I2sPeripheral0 {} + impl RegisterAccessPrivate for super::I2sPeripheral0 {} + impl super::RegisterAccess for super::I2sPeripheral0 {} #[cfg(any(esp32s3, esp32))] - impl RegisterAccessPrivate for I2sPeripheral1 {} + impl RegisterAccessPrivate for super::I2sPeripheral1 {} #[cfg(any(esp32s3, esp32))] - impl super::RegisterAccess for I2sPeripheral1 {} + impl super::RegisterAccess for super::I2sPeripheral1 {} pub struct I2sClockDividers { mclk_divider: u32, @@ -2146,3 +2212,208 @@ mod private { } } } + +#[cfg(feature = "async")] +pub mod asynch { + use embedded_dma::{ReadBuffer, WriteBuffer}; + + use super::{Error, I2sRx, I2sRxPins, I2sTx, I2sTxPins, RegisterAccess}; + use crate::dma::{ + asynch::{DmaRxDoneChFuture, DmaRxFuture, DmaTxDoneChFuture, DmaTxFuture}, + ChannelTypes, + RxPrivate, + TxPrivate, + }; + + /// Initiate an async DMA tx transfer + pub trait I2sWriteDmaAsync<'d, T, P, CH> + where + T: RegisterAccess, + CH: ChannelTypes, + P: I2sTxPins, + { + /// One-shot write I2S. + async fn write_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error>; + + /// Continuously write to I2S. Returns [I2sWriteDmaTransferAsync] + fn write_dma_circular_async( + self, + words: TXBUF, + ) -> Result, Error> + where + TXBUF: ReadBuffer; + } + + impl<'d, T, P, CH> I2sWriteDmaAsync<'d, T, P, CH> for super::I2sTx<'d, T, P, CH> + where + T: RegisterAccess, + CH: ChannelTypes, + P: I2sTxPins, + { + async fn write_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> { + let (ptr, len) = (words.as_ptr(), words.len()); + self.start_tx_transfer_async(ptr, len, false)?; + + let future = DmaTxFuture::new(&mut self.tx_channel); + future.await; + + self.register_access.reset_tx(); + + Ok(()) + } + + fn write_dma_circular_async( + mut self, + words: TXBUF, + ) -> Result, Error> + where + TXBUF: ReadBuffer, + { + let (ptr, len) = unsafe { words.read_buffer() }; + self.start_tx_transfer_async(ptr, len, true)?; + + Ok(I2sWriteDmaTransferAsync { + i2s_tx: self, + _buffer: words, + }) + } + } + + /// An in-progress async circular DMA write transfer. + #[non_exhaustive] + + pub struct I2sWriteDmaTransferAsync<'d, T, P, CH, BUFFER> + where + T: RegisterAccess, + CH: ChannelTypes, + P: I2sTxPins, + { + i2s_tx: I2sTx<'d, T, P, CH>, + _buffer: BUFFER, + } + + impl<'d, T, P, CH, BUFFER> I2sWriteDmaTransferAsync<'d, T, P, CH, BUFFER> + where + T: RegisterAccess, + CH: ChannelTypes, + P: I2sTxPins, + { + /// How many bytes can be pushed into the DMA transaction. + /// Will wait for more than 0 bytes available. + pub async fn available(&mut self) -> usize { + loop { + let res = self.i2s_tx.tx_channel.available(); + + if res != 0 { + break res; + } + + let future = DmaTxDoneChFuture::new(&mut self.i2s_tx.tx_channel); + future.await; + } + } + + /// Push bytes into the DMA transaction. + pub async fn push(&mut self, data: &[u8]) -> Result { + let avail = self.available().await; + let to_send = usize::min(avail, data.len()); + Ok(self.i2s_tx.tx_channel.push(&data[..to_send])?) + } + } + + /// Initiate an async DMA rx transfer + pub trait I2sReadDmaAsync<'d, T, P, CH> + where + T: RegisterAccess, + CH: ChannelTypes, + P: I2sRxPins, + { + /// One-shot read I2S. + async fn read_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error>; + + /// Continuously read frm I2S. Returns [I2sReadDmaTransferAsync] + fn read_dma_circular_async( + self, + words: RXBUF, + ) -> Result, Error> + where + RXBUF: WriteBuffer; + } + + impl<'d, T, P, CH> I2sReadDmaAsync<'d, T, P, CH> for super::I2sRx<'d, T, P, CH> + where + T: RegisterAccess, + CH: ChannelTypes, + P: I2sRxPins, + { + async fn read_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> { + let (ptr, len) = (words.as_mut_ptr(), words.len()); + self.start_rx_transfer_async(ptr, len, false)?; + + let future = DmaRxFuture::new(&mut self.rx_channel); + future.await; + + // ??? self.register_access.reset_tx(); + + Ok(()) + } + + fn read_dma_circular_async( + mut self, + mut words: RXBUF, + ) -> Result, Error> + where + RXBUF: WriteBuffer, + { + let (ptr, len) = unsafe { words.write_buffer() }; + self.start_rx_transfer_async(ptr, len, true)?; + + Ok(I2sReadDmaTransferAsync { + i2s_rx: self, + _buffer: words, + }) + } + } + + /// An in-progress async circular DMA read transfer. + #[non_exhaustive] + + pub struct I2sReadDmaTransferAsync<'d, T, P, CH, BUFFER> + where + T: RegisterAccess, + CH: ChannelTypes, + P: I2sRxPins, + { + i2s_rx: I2sRx<'d, T, P, CH>, + _buffer: BUFFER, + } + + impl<'d, T, P, CH, BUFFER> I2sReadDmaTransferAsync<'d, T, P, CH, BUFFER> + where + T: RegisterAccess, + CH: ChannelTypes, + P: I2sRxPins, + { + /// How many bytes can be popped from the DMA transaction. + /// Will wait for more than 0 bytes available. + pub async fn available(&mut self) -> usize { + loop { + let res = self.i2s_rx.rx_channel.available(); + + if res != 0 { + break res; + } + + let future = DmaRxDoneChFuture::new(&mut self.i2s_rx.rx_channel); + future.await; + } + } + + /// Pop bytes from the DMA transaction. + pub async fn pop(&mut self, data: &mut [u8]) -> Result { + let avail = self.available().await; + let to_rcv = usize::min(avail, data.len()); + Ok(self.i2s_rx.rx_channel.pop(&mut data[..to_rcv])?) + } + } +} diff --git a/esp32-hal/Cargo.toml b/esp32-hal/Cargo.toml index 32a742c1ce3..16b07f26c56 100644 --- a/esp32-hal/Cargo.toml +++ b/esp32-hal/Cargo.toml @@ -129,3 +129,11 @@ required-features = ["embassy", "async"] [[example]] name = "embassy_rmt_rx" required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_sound" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_read" +required-features = ["embassy", "async"] diff --git a/esp32-hal/examples/embassy_i2s_read.rs b/esp32-hal/examples/embassy_i2s_read.rs new file mode 100644 index 00000000000..3f6fa1e452c --- /dev/null +++ b/esp32-hal/examples/embassy_i2s_read.rs @@ -0,0 +1,127 @@ +//! This shows how to continously receive data via I2S +//! +//! Pins used +//! BCLK GPIO12 +//! WS GPIO13 +//! DIN GPIO14 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use esp32_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy::{self, executor::Executor}, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sRx, NoMclk, PinsBclkWsDin, Standard}, + pdma::Dma, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn i2s_task( + i2s_rx: I2sRx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDin< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32_hal::pdma::I2s0DmaChannel, + >, +) { + let buffer = dma_buffer(); + esp_println::println!("Start"); + + let mut data = [0u8; 5000]; + let mut transaction = i2s_rx.read_dma_circular_async(buffer).unwrap(); + loop { + let avail = transaction.available().await; + esp_println::println!("available {}", avail); + + let count = transaction.pop(&mut data).await.unwrap(); + esp_println::println!( + "got {} bytes, {:x?}..{:x?}", + count, + &data[..10], + &data[count - 10..count] + ); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + embassy::init(&clocks, timer_group0.timer0); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.i2s0channel; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + NoMclk {}, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_rx.with_pins(PinsBclkWsDin::new( + io.pins.gpio12, + io.pins.gpio13, + io.pins.gpio14, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32_hal::interrupt::enable( + esp32_hal::peripherals::Interrupt::I2S0, + esp32_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_tx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32-hal/examples/embassy_i2s_sound.rs b/esp32-hal/examples/embassy_i2s_sound.rs new file mode 100644 index 00000000000..922ad62fe3a --- /dev/null +++ b/esp32-hal/examples/embassy_i2s_sound.rs @@ -0,0 +1,165 @@ +//! This shows how to transmit data continously via I2S +//! +//! Pins used +//! BCLK GPIO12 +//! WS GPIO13 +//! DOUT GPIO14 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO12 | +//! | DIN | GPIO14 | +//! | LRCK | GPIO13 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use esp32_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy::{self, executor::Executor}, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sTx, NoMclk, PinsBclkWsDout, Standard}, + pdma::Dma, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[embassy_executor::task] +async fn i2s_task( + i2s_tx: I2sTx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDout< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32_hal::pdma::I2s0DmaChannel, + >, +) { + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + let mut idx = 32000 % data.len(); + + esp_println::println!("Start"); + let mut transaction = i2s_tx.write_dma_circular_async(buffer).unwrap(); + + loop { + for i in 0..filler.len() { + filler[i] = data[(idx + i) % data.len()]; + } + esp_println::println!("Next"); + + let written = transaction.push(&filler).await.unwrap(); + idx = (idx + written) % data.len(); + esp_println::println!("written {}", written); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + embassy::init(&clocks, timer_group0.timer0); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.i2s0channel; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + NoMclk {}, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout::new( + io.pins.gpio12, + io.pins.gpio13, + io.pins.gpio14, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32_hal::interrupt::enable( + esp32_hal::peripherals::Interrupt::I2S0, + esp32_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_tx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index 180584195ba..ff013c7f6e8 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -114,6 +114,14 @@ required-features = ["embassy", "async"] name = "embassy_rmt_rx" required-features = ["embassy", "async"] +[[example]] +name = "embassy_i2s_sound" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_read" +required-features = ["embassy", "async"] + [[example]] name = "direct-vectoring" required-features = ["direct-vectoring"] diff --git a/esp32c3-hal/examples/embassy_i2s_read.rs b/esp32c3-hal/examples/embassy_i2s_read.rs new file mode 100644 index 00000000000..2c7ea3b4098 --- /dev/null +++ b/esp32c3-hal/examples/embassy_i2s_read.rs @@ -0,0 +1,138 @@ +//! This shows how to continously receive data via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO5 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32c3_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy, + gdma::Gdma, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sRx, MclkPin, PinsBclkWsDin, Standard}, + peripherals::Peripherals, + prelude::*, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn i2s_task( + i2s_rx: I2sRx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDin< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32c3_hal::gdma::Channel0, + >, +) { + let buffer = dma_buffer(); + esp_println::println!("Start"); + + let mut data = [0u8; 5000]; + let mut transaction = i2s_rx.read_dma_circular_async(buffer).unwrap(); + loop { + let avail = transaction.available().await; + esp_println::println!("available {}", avail); + + let count = transaction.pop(&mut data).await.unwrap(); + esp_println::println!( + "got {} bytes, {:x?}..{:x?}", + count, + &data[..10], + &data[count - 10..count] + ); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32c3_hal::timer::TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ) + .timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32c3_hal::interrupt::enable( + esp32c3_hal::peripherals::Interrupt::DMA_CH0, + esp32c3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_rx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32c3-hal/examples/embassy_i2s_sound.rs b/esp32c3-hal/examples/embassy_i2s_sound.rs new file mode 100644 index 00000000000..159b556a404 --- /dev/null +++ b/esp32c3-hal/examples/embassy_i2s_sound.rs @@ -0,0 +1,175 @@ +//! This shows how to transmit data continuously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32c3_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy, + gdma::Gdma, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sTx, MclkPin, PinsBclkWsDout, Standard}, + peripherals::Peripherals, + prelude::*, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[embassy_executor::task] +async fn i2s_task( + i2s_tx: I2sTx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDout< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32c3_hal::gdma::Channel0, + >, +) { + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + let mut idx = 32000 % data.len(); + + esp_println::println!("Start"); + let mut transaction = i2s_tx.write_dma_circular_async(buffer).unwrap(); + loop { + for i in 0..filler.len() { + filler[i] = data[(idx + i) % data.len()]; + } + esp_println::println!("Next"); + + let written = transaction.push(&filler).await.unwrap(); + idx = (idx + written) % data.len(); + esp_println::println!("written {}", written); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32c3_hal::timer::TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ) + .timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32c3_hal::interrupt::enable( + esp32c3_hal::peripherals::Interrupt::DMA_CH0, + esp32c3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_tx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32c6-hal/Cargo.toml b/esp32c6-hal/Cargo.toml index d0215ffb984..1f36caf21ba 100644 --- a/esp32c6-hal/Cargo.toml +++ b/esp32c6-hal/Cargo.toml @@ -119,3 +119,11 @@ required-features = ["embassy", "async"] [[example]] name = "embassy_rmt_rx" required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_sound" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_read" +required-features = ["embassy", "async"] diff --git a/esp32c6-hal/examples/embassy_i2s_read.rs b/esp32c6-hal/examples/embassy_i2s_read.rs new file mode 100644 index 00000000000..b57f7d8f1e6 --- /dev/null +++ b/esp32c6-hal/examples/embassy_i2s_read.rs @@ -0,0 +1,138 @@ +//! This shows how to continuously receive data via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO5 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32c6_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy, + gdma::Gdma, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sRx, MclkPin, PinsBclkWsDin, Standard}, + peripherals::Peripherals, + prelude::*, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn i2s_task( + i2s_rx: I2sRx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDin< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32c6_hal::gdma::Channel0, + >, +) { + let buffer = dma_buffer(); + esp_println::println!("Start"); + + let mut data = [0u8; 5000]; + let mut transaction = i2s_rx.read_dma_circular_async(buffer).unwrap(); + loop { + let avail = transaction.available().await; + esp_println::println!("available {}", avail); + + let count = transaction.pop(&mut data).await.unwrap(); + esp_println::println!( + "got {} bytes, {:x?}..{:x?}", + count, + &data[..10], + &data[count - 10..count] + ); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c6_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32c6_hal::timer::TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ) + .timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32c6_hal::interrupt::enable( + esp32c6_hal::peripherals::Interrupt::DMA_IN_CH0, + esp32c6_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_rx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32c6-hal/examples/embassy_i2s_sound.rs b/esp32c6-hal/examples/embassy_i2s_sound.rs new file mode 100644 index 00000000000..e10a1d072b0 --- /dev/null +++ b/esp32c6-hal/examples/embassy_i2s_sound.rs @@ -0,0 +1,175 @@ +//! This shows how to transmit data continuously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32c6_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy, + gdma::Gdma, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sTx, MclkPin, PinsBclkWsDout, Standard}, + peripherals::Peripherals, + prelude::*, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[embassy_executor::task] +async fn i2s_task( + i2s_tx: I2sTx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDout< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32c6_hal::gdma::Channel0, + >, +) { + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + let mut idx = 32000 % data.len(); + + esp_println::println!("Start"); + let mut transaction = i2s_tx.write_dma_circular_async(buffer).unwrap(); + loop { + for i in 0..filler.len() { + filler[i] = data[(idx + i) % data.len()]; + } + esp_println::println!("Next"); + + let written = transaction.push(&filler).await.unwrap(); + idx = (idx + written) % data.len(); + esp_println::println!("written {}", written); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c6_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32c6_hal::timer::TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ) + .timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32c6_hal::interrupt::enable( + esp32c6_hal::peripherals::Interrupt::DMA_OUT_CH0, + esp32c6_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_tx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32c6-hal/examples/i2s_read.rs b/esp32c6-hal/examples/i2s_read.rs index 8fdd78a91f4..daad8e1b5b8 100644 --- a/esp32c6-hal/examples/i2s_read.rs +++ b/esp32c6-hal/examples/i2s_read.rs @@ -1,4 +1,4 @@ -//! This shows how to continously receive data via I2S +//! This shows how to continuously receive data via I2S //! //! Pins used //! MCLK GPIO4 diff --git a/esp32h2-hal/Cargo.toml b/esp32h2-hal/Cargo.toml index 27d70657177..0ffbc44b5ae 100644 --- a/esp32h2-hal/Cargo.toml +++ b/esp32h2-hal/Cargo.toml @@ -119,3 +119,11 @@ required-features = ["interrupt-preemption"] [[example]] name = "direct-vectoring" required-features = ["direct-vectoring"] + +[[example]] +name = "embassy_i2s_sound" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_read" +required-features = ["embassy", "async"] diff --git a/esp32h2-hal/examples/embassy_i2s_read.rs b/esp32h2-hal/examples/embassy_i2s_read.rs new file mode 100644 index 00000000000..1cd8bdbe796 --- /dev/null +++ b/esp32h2-hal/examples/embassy_i2s_read.rs @@ -0,0 +1,138 @@ +//! This shows how to continuously receive data via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO5 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32h2_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy, + gdma::Gdma, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sRx, MclkPin, PinsBclkWsDin, Standard}, + peripherals::Peripherals, + prelude::*, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn i2s_task( + i2s_rx: I2sRx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDin< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32h2_hal::gdma::Channel0, + >, +) { + let buffer = dma_buffer(); + esp_println::println!("Start"); + + let mut data = [0u8; 5000]; + let mut transaction = i2s_rx.read_dma_circular_async(buffer).unwrap(); + loop { + let avail = transaction.available().await; + esp_println::println!("available {}", avail); + + let count = transaction.pop(&mut data).await.unwrap(); + esp_println::println!( + "got {} bytes, {:x?}..{:x?}", + count, + &data[..10], + &data[count - 10..count] + ); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32h2_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32h2_hal::timer::TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ) + .timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32h2_hal::interrupt::enable( + esp32h2_hal::peripherals::Interrupt::DMA_IN_CH0, + esp32h2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_rx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32h2-hal/examples/embassy_i2s_sound.rs b/esp32h2-hal/examples/embassy_i2s_sound.rs new file mode 100644 index 00000000000..4ef6db807cf --- /dev/null +++ b/esp32h2-hal/examples/embassy_i2s_sound.rs @@ -0,0 +1,175 @@ +//! This shows how to transmit data continuously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32h2_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy, + gdma::Gdma, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sTx, MclkPin, PinsBclkWsDout, Standard}, + peripherals::Peripherals, + prelude::*, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[embassy_executor::task] +async fn i2s_task( + i2s_tx: I2sTx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDout< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32h2_hal::gdma::Channel0, + >, +) { + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + let mut idx = 32000 % data.len(); + + esp_println::println!("Start"); + let mut transaction = i2s_tx.write_dma_circular_async(buffer).unwrap(); + loop { + for i in 0..filler.len() { + filler[i] = data[(idx + i) % data.len()]; + } + esp_println::println!("Next"); + + let written = transaction.push(&filler).await.unwrap(); + idx = (idx + written) % data.len(); + esp_println::println!("written {}", written); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32h2_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32h2_hal::timer::TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ) + .timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32h2_hal::interrupt::enable( + esp32h2_hal::peripherals::Interrupt::DMA_OUT_CH0, + esp32h2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_tx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32h2-hal/examples/i2s_read.rs b/esp32h2-hal/examples/i2s_read.rs index ea1c84f15df..ea9550908f6 100644 --- a/esp32h2-hal/examples/i2s_read.rs +++ b/esp32h2-hal/examples/i2s_read.rs @@ -1,4 +1,4 @@ -//! This shows how to continously receive data via I2S +//! This shows how to continuously receive data via I2S //! //! Pins used //! MCLK GPIO4 diff --git a/esp32s2-hal/Cargo.toml b/esp32s2-hal/Cargo.toml index 47d1c128854..37ab34eabbb 100644 --- a/esp32s2-hal/Cargo.toml +++ b/esp32s2-hal/Cargo.toml @@ -126,3 +126,11 @@ required-features = ["embassy", "async"] [[example]] name = "embassy_rmt_rx" required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_sound" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_read" +required-features = ["embassy", "async"] diff --git a/esp32s2-hal/examples/embassy_i2s_read.rs b/esp32s2-hal/examples/embassy_i2s_read.rs new file mode 100644 index 00000000000..e3c9533c428 --- /dev/null +++ b/esp32s2-hal/examples/embassy_i2s_read.rs @@ -0,0 +1,127 @@ +//! This shows how to continuously receive data via I2S +//! +//! Pins used +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO3 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use esp32s2_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy::{self, executor::Executor}, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sRx, NoMclk, PinsBclkWsDin, Standard}, + pdma::Dma, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn i2s_task( + i2s_rx: I2sRx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDin< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32s2_hal::pdma::I2s0DmaChannel, + >, +) { + let buffer = dma_buffer(); + esp_println::println!("Start"); + + let mut data = [0u8; 5000]; + let mut transaction = i2s_rx.read_dma_circular_async(buffer).unwrap(); + loop { + let avail = transaction.available().await; + esp_println::println!("available {}", avail); + + let count = transaction.pop(&mut data).await.unwrap(); + esp_println::println!( + "got {} bytes, {:x?}..{:x?}", + count, + &data[..10], + &data[count - 10..count] + ); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + embassy::init(&clocks, timer_group0.timer0); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.i2s0channel; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + NoMclk {}, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_rx.with_pins(PinsBclkWsDin::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32s2_hal::interrupt::enable( + esp32s2_hal::peripherals::Interrupt::I2S0, + esp32s2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_tx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32s2-hal/examples/embassy_i2s_sound.rs b/esp32s2-hal/examples/embassy_i2s_sound.rs new file mode 100644 index 00000000000..aef7761d7a3 --- /dev/null +++ b/esp32s2-hal/examples/embassy_i2s_sound.rs @@ -0,0 +1,166 @@ +//! This shows how to transmit data continously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use esp32s2_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy::{self, executor::Executor}, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sTx, NoMclk, PinsBclkWsDout, Standard}, + pdma::Dma, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[embassy_executor::task] +async fn i2s_task( + i2s_tx: I2sTx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDout< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32s2_hal::pdma::I2s0DmaChannel, + >, +) { + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + let mut idx = 32000 % data.len(); + + esp_println::println!("Start"); + let mut transaction = i2s_tx.write_dma_circular_async(buffer).unwrap(); + + loop { + for i in 0..filler.len() { + filler[i] = data[(idx + i) % data.len()]; + } + esp_println::println!("Next"); + + let written = transaction.push(&filler).await.unwrap(); + idx = (idx + written) % data.len(); + esp_println::println!("written {}", written); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + embassy::init(&clocks, timer_group0.timer0); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.i2s0channel; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + NoMclk {}, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32s2_hal::interrupt::enable( + esp32s2_hal::peripherals::Interrupt::I2S0, + esp32s2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_tx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32s3-hal/Cargo.toml b/esp32s3-hal/Cargo.toml index 3abb716d5bd..eef19752300 100644 --- a/esp32s3-hal/Cargo.toml +++ b/esp32s3-hal/Cargo.toml @@ -143,3 +143,11 @@ required-features = ["embassy", "async"] [[example]] name = "embassy_rmt_rx" required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_sound" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_i2s_read" +required-features = ["embassy", "async"] diff --git a/esp32s3-hal/examples/embassy_i2s_read.rs b/esp32s3-hal/examples/embassy_i2s_read.rs new file mode 100644 index 00000000000..f9ef575bfc1 --- /dev/null +++ b/esp32s3-hal/examples/embassy_i2s_read.rs @@ -0,0 +1,137 @@ +//! This shows how to continously receive data via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO5 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use esp32s3_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy::{self, executor::Executor}, + gdma::Gdma, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sRx, MclkPin, PinsBclkWsDin, Standard}, + peripherals::Peripherals, + prelude::*, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn i2s_task( + i2s_rx: I2sRx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDin< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32s3_hal::gdma::Channel0, + >, +) { + let buffer = dma_buffer(); + esp_println::println!("Start"); + + let mut data = [0u8; 5000]; + let mut transaction = i2s_rx.read_dma_circular_async(buffer).unwrap(); + loop { + let avail = transaction.available().await; + esp_println::println!("available {}", avail); + + let count = transaction.pop(&mut data).await.unwrap(); + esp_println::println!( + "got {} bytes, {:x?}..{:x?}", + count, + &data[..10], + &data[count - 10..count] + ); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32s3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32s3_hal::timer::TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ) + .timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32s3_hal::interrupt::enable( + esp32s3_hal::peripherals::Interrupt::DMA_IN_CH0, + esp32s3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_rx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32s3-hal/examples/embassy_i2s_sound.rs b/esp32s3-hal/examples/embassy_i2s_sound.rs new file mode 100644 index 00000000000..7d920acbca9 --- /dev/null +++ b/esp32s3-hal/examples/embassy_i2s_sound.rs @@ -0,0 +1,174 @@ +//! This shows how to transmit data continously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use esp32s3_hal::{ + clock::ClockControl, + dma::DmaPriority, + embassy::{self, executor::Executor}, + gdma::Gdma, + gpio::GpioPin, + i2s, + i2s::{asynch::*, DataFormat, I2s, I2s0New, I2sTx, MclkPin, PinsBclkWsDout, Standard}, + peripherals::Peripherals, + prelude::*, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[embassy_executor::task] +async fn i2s_task( + i2s_tx: I2sTx< + 'static, + i2s::I2sPeripheral0, + PinsBclkWsDout< + 'static, + GpioPin, + GpioPin, + GpioPin, + >, + esp32s3_hal::gdma::Channel0, + >, +) { + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + let mut idx = 32000 % data.len(); + + esp_println::println!("Start"); + let mut transaction = i2s_tx.write_dma_circular_async(buffer).unwrap(); + loop { + for i in 0..filler.len() { + filler[i] = data[(idx + i) % data.len()]; + } + esp_println::println!("Next"); + + let written = transaction.push(&filler).await.unwrap(); + idx = (idx + written) % data.len(); + esp_println::println!("written {}", written); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32s3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32s3_hal::timer::TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ) + .timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let tx_descriptors = make_static!([0u32; 20 * 3]); + let rx_descriptors = make_static!([0u32; 8 * 3]); + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin::new(io.pins.gpio4), + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + tx_descriptors, + rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout::new( + io.pins.gpio1, + io.pins.gpio2, + io.pins.gpio3, + )); + + // you need to manually enable the DMA channel's interrupt! + esp32s3_hal::interrupt::enable( + esp32s3_hal::peripherals::Interrupt::DMA_OUT_CH0, + esp32s3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(i2s_task(i2s_tx)).ok(); + }); +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +}