Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix I2s async-tx #1833

Merged
merged 5 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion esp-hal/src/dma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions hil-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ harness = false
name = "i2s"
harness = false

[[test]]
name = "i2s_async"
harness = false

[[test]]
name = "spi_full_duplex"
harness = false
Expand Down
153 changes: 153 additions & 0 deletions hil-test/tests/i2s_async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//! 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 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 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 {
use super::*;

#[init]
async fn init() {}

#[test]
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;
}
}

assert!(!failed);
}
}