From b581033371266bb084adbfb2f2a25a334518adf9 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 18 Jul 2024 15:14:35 +0200 Subject: [PATCH 1/5] Fix I2s async-tx --- esp-hal/src/dma/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index a40786b8092..5c71abbd647 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -1452,7 +1452,7 @@ where #[cfg(feature = "async")] fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - CH::Rx::waker() + CH::Tx::waker() } fn is_listening_out_descriptor_error(&self) -> bool { From fb7169bf735ba7ce19e59c851db89db8e2a61067 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 18 Jul 2024 15:16:52 +0200 Subject: [PATCH 2/5] CHANGELOG.md --- esp-hal/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 40a0756dba6..df465d84cf3 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fix I2S async-tx (#1833) + ### Removed - This package no longer re-exports the `esp_hal_procmacros::main` macro (#1828) From fa845501ba1d1ab806655d4c1acadc27add907fc Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 18 Jul 2024 17:15:16 +0200 Subject: [PATCH 3/5] Add async i2s test --- hil-test/Cargo.toml | 4 + hil-test/tests/i2s_async.rs | 171 ++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 hil-test/tests/i2s_async.rs diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 2e1a042be80..02c1a7c76d3 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -52,6 +52,10 @@ harness = false name = "i2s" harness = false +[[test]] +name = "i2s_async" +harness = false + [[test]] name = "spi_full_duplex" harness = false diff --git a/hil-test/tests/i2s_async.rs b/hil-test/tests/i2s_async.rs new file mode 100644 index 00000000000..e5654896ffc --- /dev/null +++ b/hil-test/tests/i2s_async.rs @@ -0,0 +1,171 @@ +//! I2S Loopback Test (Async) +//! +//! It's assumed GPIO2 is connected to GPIO3 +//! +//! This test uses I2S TX to transmit known data to I2S RX (forced to slave mode +//! with loopback mode enabled). It's using circular DMA mode + +//% CHIPS: esp32c3 esp32c6 esp32s3 esp32h2 + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use embassy_executor::Spawner; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + dma::{Dma, DmaChannel0, DmaPriority}, + gpio::Io, + i2s::{asynch::*, DataFormat, I2s, Standard}, + peripheral::Peripheral, + peripherals::Peripherals, + prelude::*, + system::SystemControl, +}; + +// choose values which DON'T restart on every descriptor buffer's start +const ADD: u8 = 5; +const CUT_OFF: u8 = 113; + +#[embassy_executor::task] +async fn test_i2s_loopback_invoker(spawner: Spawner) { + let outcome; + { + outcome = test_i2s_loopback(spawner).await; + } + embedded_test::export::check_outcome(outcome); +} + +async fn test_i2s_loopback(spawner: Spawner) { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(peripherals.DMA); + let dma_channel = dma.channel0; + + #[allow(non_upper_case_globals)] + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + esp_hal::dma_circular_buffers!(2000, 2000); + + let i2s = I2s::new( + peripherals.I2S0, + Standard::Philips, + DataFormat::Data16Channel16, + 16000.Hz(), + dma_channel.configure_for_async(false, DmaPriority::Priority0), + tx_descriptors, + rx_descriptors, + &clocks, + ); + + let i2s_tx = i2s + .i2s_tx + .with_bclk(unsafe { io.pins.gpio0.clone_unchecked() }) + .with_ws(unsafe { io.pins.gpio1.clone_unchecked() }) + .with_dout(unsafe { io.pins.gpio2.clone_unchecked() }) + .build(); + + let i2s_rx = i2s + .i2s_rx + .with_bclk(io.pins.gpio0) + .with_ws(io.pins.gpio1) + .with_din(io.pins.gpio3) + .build(); + + // enable loopback testing + unsafe { + let i2s = esp_hal::peripherals::I2S0::steal(); + i2s.tx_conf().modify(|_, w| w.sig_loopback().set_bit()); + + i2s.rx_conf().modify(|_, w| w.rx_slave_mod().set_bit()); + + i2s.tx_conf().modify(|_, w| w.tx_update().clear_bit()); + i2s.tx_conf().modify(|_, w| w.tx_update().set_bit()); + + i2s.rx_conf().modify(|_, w| w.rx_update().clear_bit()); + i2s.rx_conf().modify(|_, w| w.rx_update().set_bit()); + } + + let mut iteration = 0; + let mut failed = false; + let mut check_i: u8 = 0; + let mut i = 0; + for b in tx_buffer.iter_mut() { + *b = i; + i = (i + ADD) % CUT_OFF; + } + + let mut rcv = [0u8; 2000]; + + let mut rx_transfer = i2s_rx.read_dma_circular_async(rx_buffer).unwrap(); + let tx_transfer = i2s_tx.write_dma_circular_async(tx_buffer).unwrap(); + + spawner.must_spawn(writer(i, tx_transfer)); + + 'outer: loop { + let len = rx_transfer.pop(&mut rcv).await.unwrap(); + for &b in &rcv[..len] { + if b != check_i { + failed = true; + break 'outer; + } + check_i = (check_i + ADD) % CUT_OFF; + } + iteration += 1; + + if iteration > 30 { + break; + } + } + + assert!(!failed); +} + +#[embassy_executor::task] +async fn writer( + i: u8, + mut transfer: I2sWriteDmaTransferAsync< + 'static, + esp_hal::peripherals::I2S0, + DmaChannel0, + &'static mut [u8; 2000], + >, +) { + let mut i = i; + loop { + transfer + .push_with(|buffer| { + for b in buffer.iter_mut() { + *b = i; + i = (i + ADD) % CUT_OFF; + } + buffer.len() + }) + .await + .unwrap(); + } +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + #[init] + fn init() {} + + #[test] + fn run_test() { + unsafe fn __make_static(t: &mut T) -> &'static mut T { + ::core::mem::transmute(t) + } + + let mut executor = esp_hal_embassy::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + executor.run(|spawner| { + spawner.must_spawn(super::test_i2s_loopback_invoker(spawner)); + }); + } +} From e2dfb56d6242bfc843920bc1013ef5cbfcd09b67 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 18 Jul 2024 17:30:07 +0200 Subject: [PATCH 4/5] Cleanup --- hil-test/tests/i2s_async.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hil-test/tests/i2s_async.rs b/hil-test/tests/i2s_async.rs index e5654896ffc..a0424cad82f 100644 --- a/hil-test/tests/i2s_async.rs +++ b/hil-test/tests/i2s_async.rs @@ -151,7 +151,7 @@ async fn writer( } #[cfg(test)] -#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests] mod tests { #[init] fn init() {} From b9c8254cb0b6921bd69d929aa1595a7daa73aae7 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Fri, 19 Jul 2024 12:46:46 +0200 Subject: [PATCH 5/5] Use `Spawner::for_current_executor()` instead of manually creating the executor --- hil-test/tests/i2s_async.rs | 198 ++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 108 deletions(-) diff --git a/hil-test/tests/i2s_async.rs b/hil-test/tests/i2s_async.rs index a0424cad82f..bc6b816cec1 100644 --- a/hil-test/tests/i2s_async.rs +++ b/hil-test/tests/i2s_async.rs @@ -11,7 +11,6 @@ #![no_main] use defmt_rtt as _; -use embassy_executor::Spawner; use esp_backtrace as _; use esp_hal::{ clock::ClockControl, @@ -28,103 +27,6 @@ use esp_hal::{ const ADD: u8 = 5; const CUT_OFF: u8 = 113; -#[embassy_executor::task] -async fn test_i2s_loopback_invoker(spawner: Spawner) { - let outcome; - { - outcome = test_i2s_loopback(spawner).await; - } - embedded_test::export::check_outcome(outcome); -} - -async fn test_i2s_loopback(spawner: Spawner) { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; - - #[allow(non_upper_case_globals)] - let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = - esp_hal::dma_circular_buffers!(2000, 2000); - - let i2s = I2s::new( - peripherals.I2S0, - Standard::Philips, - DataFormat::Data16Channel16, - 16000.Hz(), - dma_channel.configure_for_async(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - &clocks, - ); - - let i2s_tx = i2s - .i2s_tx - .with_bclk(unsafe { io.pins.gpio0.clone_unchecked() }) - .with_ws(unsafe { io.pins.gpio1.clone_unchecked() }) - .with_dout(unsafe { io.pins.gpio2.clone_unchecked() }) - .build(); - - let i2s_rx = i2s - .i2s_rx - .with_bclk(io.pins.gpio0) - .with_ws(io.pins.gpio1) - .with_din(io.pins.gpio3) - .build(); - - // enable loopback testing - unsafe { - let i2s = esp_hal::peripherals::I2S0::steal(); - i2s.tx_conf().modify(|_, w| w.sig_loopback().set_bit()); - - i2s.rx_conf().modify(|_, w| w.rx_slave_mod().set_bit()); - - i2s.tx_conf().modify(|_, w| w.tx_update().clear_bit()); - i2s.tx_conf().modify(|_, w| w.tx_update().set_bit()); - - i2s.rx_conf().modify(|_, w| w.rx_update().clear_bit()); - i2s.rx_conf().modify(|_, w| w.rx_update().set_bit()); - } - - let mut iteration = 0; - let mut failed = false; - let mut check_i: u8 = 0; - let mut i = 0; - for b in tx_buffer.iter_mut() { - *b = i; - i = (i + ADD) % CUT_OFF; - } - - let mut rcv = [0u8; 2000]; - - let mut rx_transfer = i2s_rx.read_dma_circular_async(rx_buffer).unwrap(); - let tx_transfer = i2s_tx.write_dma_circular_async(tx_buffer).unwrap(); - - spawner.must_spawn(writer(i, tx_transfer)); - - 'outer: loop { - let len = rx_transfer.pop(&mut rcv).await.unwrap(); - for &b in &rcv[..len] { - if b != check_i { - failed = true; - break 'outer; - } - check_i = (check_i + ADD) % CUT_OFF; - } - iteration += 1; - - if iteration > 30 { - break; - } - } - - assert!(!failed); -} - #[embassy_executor::task] async fn writer( i: u8, @@ -151,21 +53,101 @@ async fn writer( } #[cfg(test)] -#[embedded_test::tests] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] mod tests { + use super::*; + #[init] - fn init() {} + async fn init() {} #[test] - fn run_test() { - unsafe fn __make_static(t: &mut T) -> &'static mut T { - ::core::mem::transmute(t) + async fn test_i2s_loopback() { + let spawner = embassy_executor::Spawner::for_current_executor().await; + + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(peripherals.DMA); + let dma_channel = dma.channel0; + + #[allow(non_upper_case_globals)] + let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = + esp_hal::dma_circular_buffers!(2000, 2000); + + let i2s = I2s::new( + peripherals.I2S0, + Standard::Philips, + DataFormat::Data16Channel16, + 16000.Hz(), + dma_channel.configure_for_async(false, DmaPriority::Priority0), + tx_descriptors, + rx_descriptors, + &clocks, + ); + + let i2s_tx = i2s + .i2s_tx + .with_bclk(unsafe { io.pins.gpio0.clone_unchecked() }) + .with_ws(unsafe { io.pins.gpio1.clone_unchecked() }) + .with_dout(unsafe { io.pins.gpio2.clone_unchecked() }) + .build(); + + let i2s_rx = i2s + .i2s_rx + .with_bclk(io.pins.gpio0) + .with_ws(io.pins.gpio1) + .with_din(io.pins.gpio3) + .build(); + + // enable loopback testing + unsafe { + let i2s = esp_hal::peripherals::I2S0::steal(); + i2s.tx_conf().modify(|_, w| w.sig_loopback().set_bit()); + + i2s.rx_conf().modify(|_, w| w.rx_slave_mod().set_bit()); + + i2s.tx_conf().modify(|_, w| w.tx_update().clear_bit()); + i2s.tx_conf().modify(|_, w| w.tx_update().set_bit()); + + i2s.rx_conf().modify(|_, w| w.rx_update().clear_bit()); + i2s.rx_conf().modify(|_, w| w.rx_update().set_bit()); + } + + let mut iteration = 0; + let mut failed = false; + let mut check_i: u8 = 0; + let mut i = 0; + for b in tx_buffer.iter_mut() { + *b = i; + i = (i + ADD) % CUT_OFF; + } + + let mut rcv = [0u8; 2000]; + + let mut rx_transfer = i2s_rx.read_dma_circular_async(rx_buffer).unwrap(); + let tx_transfer = i2s_tx.write_dma_circular_async(tx_buffer).unwrap(); + + spawner.must_spawn(writer(i, tx_transfer)); + + 'outer: loop { + let len = rx_transfer.pop(&mut rcv).await.unwrap(); + for &b in &rcv[..len] { + if b != check_i { + failed = true; + break 'outer; + } + check_i = (check_i + ADD) % CUT_OFF; + } + iteration += 1; + + if iteration > 30 { + break; + } } - let mut executor = esp_hal_embassy::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(super::test_i2s_loopback_invoker(spawner)); - }); + assert!(!failed); } }