Some minor I2S cleanups (#2190)
* Clean up rx_start * Improve readability of register manipulation * Enable failing tests on ESP32 * Improve consistency * Merge tests * Disable ESP32 for now * Clean up some cfgs * Avoid as pointer-casts * Inline wait_for_dma_done * Remove a level of indirection * Fix typos * Resolve questionmarks
This commit is contained in:
parent
f765a6b094
commit
46be3b19b5
@ -2980,11 +2980,6 @@ pub(crate) mod asynch {
|
||||
pub fn new(rx: &'a mut RX) -> Self {
|
||||
Self { rx }
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // Dead on the C2
|
||||
pub fn rx(&mut self) -> &mut RX {
|
||||
self.rx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, RX> core::future::Future for DmaRxFuture<'a, RX>
|
||||
|
||||
@ -534,7 +534,7 @@ where
|
||||
DmaMode: Mode,
|
||||
{
|
||||
fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) {
|
||||
self.wait_tx_dma_done().ok();
|
||||
T::wait_for_tx_done();
|
||||
}
|
||||
|
||||
fn peripheral_dma_stop(&mut self) {
|
||||
@ -575,7 +575,7 @@ where
|
||||
}
|
||||
|
||||
fn write_bytes(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
let ptr = data as *const _ as *const u8;
|
||||
let ptr = data.as_ptr();
|
||||
|
||||
// Reset TX unit and TX FIFO
|
||||
T::reset_tx();
|
||||
@ -632,13 +632,6 @@ where
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_tx_dma_done(&self) -> Result<(), Error> {
|
||||
// wait until I2S_TX_IDLE is 1
|
||||
T::wait_for_tx_done();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T, W, CH, DmaMode> I2sWrite<W> for I2sTx<'d, T, CH, DmaMode>
|
||||
@ -650,10 +643,7 @@ where
|
||||
{
|
||||
fn write(&mut self, words: &[W]) -> Result<(), Error> {
|
||||
self.write_bytes(unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
words as *const _ as *const u8,
|
||||
core::mem::size_of_val(words),
|
||||
)
|
||||
core::slice::from_raw_parts(words.as_ptr().cast::<u8>(), core::mem::size_of_val(words))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -756,14 +746,14 @@ where
|
||||
}
|
||||
|
||||
fn read_bytes(&mut self, data: &mut [u8]) -> Result<(), Error> {
|
||||
let ptr = data as *mut _ as *mut u8;
|
||||
let ptr = data.as_mut_ptr();
|
||||
|
||||
// Reset RX unit and RX FIFO
|
||||
T::reset_rx();
|
||||
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
// configure DMA inlink
|
||||
unsafe {
|
||||
self.rx_chain.fill_for_rx(false, ptr, data.len())?;
|
||||
self.rx_channel
|
||||
@ -771,12 +761,10 @@ where
|
||||
.and_then(|_| self.rx_channel.start_transfer())?;
|
||||
}
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
// start: set I2S_TX_START
|
||||
// start: set I2S_RX_START
|
||||
T::rx_start(data.len());
|
||||
|
||||
// wait until I2S_TX_IDLE is 1
|
||||
// wait until I2S_RX_IDLE is 1
|
||||
T::wait_for_rx_done();
|
||||
|
||||
Ok(())
|
||||
@ -796,12 +784,12 @@ where
|
||||
return Err(Error::IllegalArgument);
|
||||
}
|
||||
|
||||
// Reset TX unit and TX FIFO
|
||||
// Reset RX unit and RX FIFO
|
||||
T::reset_rx();
|
||||
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
// configure DMA inlink
|
||||
unsafe {
|
||||
self.rx_chain.fill_for_rx(circular, ptr, len)?;
|
||||
self.rx_channel
|
||||
@ -809,8 +797,6 @@ where
|
||||
.and_then(|_| self.rx_channel.start_transfer())?;
|
||||
}
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
// start: set I2S_RX_START
|
||||
T::rx_start(len);
|
||||
Ok(())
|
||||
@ -831,7 +817,7 @@ where
|
||||
|
||||
self.read_bytes(unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
words as *mut _ as *mut u8,
|
||||
words.as_mut_ptr().cast::<u8>(),
|
||||
core::mem::size_of_val(words),
|
||||
)
|
||||
})
|
||||
@ -1112,24 +1098,18 @@ mod private {
|
||||
i2s.clkm_conf().modify(|_, w| w.clka_ena().clear_bit());
|
||||
|
||||
i2s.clkm_conf().modify(|_, w| unsafe {
|
||||
w.clk_en()
|
||||
.set_bit()
|
||||
.clkm_div_num()
|
||||
.bits(clock_settings.mclk_divider as u8)
|
||||
w.clk_en().set_bit();
|
||||
w.clkm_div_num().bits(clock_settings.mclk_divider as u8)
|
||||
});
|
||||
|
||||
i2s.clkm_conf().modify(|_, w| unsafe {
|
||||
w.clkm_div_a()
|
||||
.bits(clock_settings.denominator as u8)
|
||||
.clkm_div_b()
|
||||
.bits(clock_settings.numerator as u8)
|
||||
w.clkm_div_a().bits(clock_settings.denominator as u8);
|
||||
w.clkm_div_b().bits(clock_settings.numerator as u8)
|
||||
});
|
||||
|
||||
i2s.sample_rate_conf().modify(|_, w| unsafe {
|
||||
w.tx_bck_div_num()
|
||||
.bits(clock_settings.bclk_divider as u8)
|
||||
.rx_bck_div_num()
|
||||
.bits(clock_settings.bclk_divider as u8)
|
||||
w.tx_bck_div_num().bits(clock_settings.bclk_divider as u8);
|
||||
w.rx_bck_div_num().bits(clock_settings.bclk_divider as u8)
|
||||
});
|
||||
}
|
||||
|
||||
@ -1147,58 +1127,53 @@ mod private {
|
||||
.modify(|_, w| unsafe { w.rx_bits_mod().bits(data_format.channel_bits()) });
|
||||
|
||||
i2s.conf().modify(|_, w| {
|
||||
w.tx_slave_mod()
|
||||
.clear_bit()
|
||||
.rx_slave_mod()
|
||||
.clear_bit()
|
||||
.tx_msb_shift()
|
||||
.set_bit() // ?
|
||||
.rx_msb_shift()
|
||||
.set_bit() // ?
|
||||
.tx_short_sync()
|
||||
.bit(false) //??
|
||||
.rx_short_sync()
|
||||
.bit(false) //??
|
||||
.tx_msb_right()
|
||||
.clear_bit()
|
||||
.rx_msb_right()
|
||||
.clear_bit()
|
||||
.tx_right_first()
|
||||
.clear_bit()
|
||||
.rx_right_first()
|
||||
.clear_bit()
|
||||
.tx_mono()
|
||||
.clear_bit()
|
||||
.rx_mono()
|
||||
.clear_bit()
|
||||
.sig_loopback()
|
||||
.clear_bit()
|
||||
w.tx_slave_mod().clear_bit();
|
||||
w.rx_slave_mod().clear_bit();
|
||||
// If the I2S_RX_MSB_SHIFT bit and the I2S_TX_MSB_SHIFT bit of register
|
||||
// I2S_CONF_REG are set to 1, respectively, the I2S module will use the Philips
|
||||
// standard when receiving and transmitting data.
|
||||
w.tx_msb_shift().set_bit();
|
||||
w.rx_msb_shift().set_bit();
|
||||
// Short frame synchronization
|
||||
w.tx_short_sync().bit(false);
|
||||
w.rx_short_sync().bit(false);
|
||||
w.tx_msb_right().clear_bit();
|
||||
w.rx_msb_right().clear_bit();
|
||||
w.tx_right_first().clear_bit();
|
||||
w.rx_right_first().clear_bit();
|
||||
w.tx_mono().clear_bit();
|
||||
w.rx_mono().clear_bit();
|
||||
w.sig_loopback().clear_bit()
|
||||
});
|
||||
|
||||
i2s.fifo_conf().modify(|_, w| unsafe {
|
||||
w.tx_fifo_mod()
|
||||
.bits(fifo_mod)
|
||||
.tx_fifo_mod_force_en()
|
||||
.set_bit()
|
||||
.dscr_en()
|
||||
.set_bit()
|
||||
.rx_fifo_mod()
|
||||
.bits(fifo_mod)
|
||||
.rx_fifo_mod_force_en()
|
||||
.set_bit()
|
||||
w.tx_fifo_mod().bits(fifo_mod);
|
||||
w.tx_fifo_mod_force_en().set_bit();
|
||||
w.dscr_en().set_bit();
|
||||
w.rx_fifo_mod().bits(fifo_mod);
|
||||
w.rx_fifo_mod_force_en().set_bit()
|
||||
});
|
||||
|
||||
i2s.conf_chan()
|
||||
.modify(|_, w| unsafe { w.tx_chan_mod().bits(0).rx_chan_mod().bits(0) }); // for now only stereo
|
||||
i2s.conf_chan().modify(|_, w| unsafe {
|
||||
// for now only stereo
|
||||
w.tx_chan_mod().bits(0);
|
||||
w.rx_chan_mod().bits(0)
|
||||
});
|
||||
|
||||
i2s.conf1()
|
||||
.modify(|_, w| w.tx_pcm_bypass().set_bit().rx_pcm_bypass().set_bit());
|
||||
i2s.conf1().modify(|_, w| {
|
||||
w.tx_pcm_bypass().set_bit();
|
||||
w.rx_pcm_bypass().set_bit()
|
||||
});
|
||||
|
||||
i2s.pd_conf()
|
||||
.modify(|_, w| w.fifo_force_pu().set_bit().fifo_force_pd().clear_bit());
|
||||
i2s.pd_conf().modify(|_, w| {
|
||||
w.fifo_force_pu().set_bit();
|
||||
w.fifo_force_pd().clear_bit()
|
||||
});
|
||||
|
||||
i2s.conf2()
|
||||
.modify(|_, w| w.camera_en().clear_bit().lcd_en().clear_bit());
|
||||
i2s.conf2().modify(|_, w| {
|
||||
w.camera_en().clear_bit();
|
||||
w.lcd_en().clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn set_master() {
|
||||
@ -1213,19 +1188,21 @@ mod private {
|
||||
|
||||
fn reset_tx() {
|
||||
let i2s = Self::register_block();
|
||||
i2s.conf()
|
||||
.modify(|_, w| w.tx_reset().set_bit().tx_fifo_reset().set_bit());
|
||||
i2s.conf()
|
||||
.modify(|_, w| w.tx_reset().clear_bit().tx_fifo_reset().clear_bit());
|
||||
i2s.conf().modify(|_, w| {
|
||||
w.tx_reset().set_bit();
|
||||
w.tx_fifo_reset().set_bit()
|
||||
});
|
||||
i2s.conf().modify(|_, w| {
|
||||
w.tx_reset().clear_bit();
|
||||
w.tx_fifo_reset().clear_bit()
|
||||
});
|
||||
|
||||
i2s.lc_conf().modify(|_, w| w.out_rst().set_bit());
|
||||
i2s.lc_conf().modify(|_, w| w.out_rst().clear_bit());
|
||||
|
||||
i2s.int_clr().write(|w| {
|
||||
w.out_done()
|
||||
.clear_bit_by_one()
|
||||
.out_total_eof()
|
||||
.clear_bit_by_one()
|
||||
w.out_done().clear_bit_by_one();
|
||||
w.out_total_eof().clear_bit_by_one()
|
||||
});
|
||||
}
|
||||
|
||||
@ -1254,38 +1231,40 @@ mod private {
|
||||
|
||||
fn reset_rx() {
|
||||
let i2s = Self::register_block();
|
||||
i2s.conf()
|
||||
.modify(|_, w| w.rx_reset().set_bit().rx_fifo_reset().set_bit());
|
||||
i2s.conf()
|
||||
.modify(|_, w| w.rx_reset().clear_bit().rx_fifo_reset().clear_bit());
|
||||
i2s.conf().modify(|_, w| {
|
||||
w.rx_reset().set_bit();
|
||||
w.rx_fifo_reset().set_bit()
|
||||
});
|
||||
i2s.conf().modify(|_, w| {
|
||||
w.rx_reset().clear_bit();
|
||||
w.rx_fifo_reset().clear_bit()
|
||||
});
|
||||
|
||||
i2s.lc_conf().modify(|_, w| w.in_rst().set_bit());
|
||||
i2s.lc_conf().modify(|_, w| w.in_rst().clear_bit());
|
||||
|
||||
i2s.int_clr().write(|w| {
|
||||
w.in_done()
|
||||
.clear_bit_by_one()
|
||||
.in_suc_eof()
|
||||
.clear_bit_by_one()
|
||||
w.in_done().clear_bit_by_one();
|
||||
w.in_suc_eof().clear_bit_by_one()
|
||||
});
|
||||
}
|
||||
|
||||
fn rx_start(len: usize) {
|
||||
#[cfg(not(esp32))]
|
||||
let len = len - 1;
|
||||
|
||||
let i2s = Self::register_block();
|
||||
|
||||
i2s.int_clr().write(|w| w.in_suc_eof().clear_bit_by_one());
|
||||
|
||||
#[cfg(not(esp32))]
|
||||
i2s.rxeof_num()
|
||||
.modify(|_, w| unsafe { w.rx_eof_num().bits(len as u32) });
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32)] {
|
||||
// On ESP32, the eof_num count in words.
|
||||
let eof_num = len / 4;
|
||||
} else {
|
||||
let eof_num = len - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// On ESP32, the eof_num count in words.
|
||||
#[cfg(esp32)]
|
||||
i2s.rxeof_num()
|
||||
.modify(|_, w| unsafe { w.rx_eof_num().bits((len / 4) as u32) });
|
||||
.modify(|_, w| unsafe { w.rx_eof_num().bits(eof_num as u32) });
|
||||
|
||||
i2s.conf().modify(|_, w| w.rx_start().set_bit());
|
||||
}
|
||||
@ -1432,14 +1411,10 @@ mod private {
|
||||
}
|
||||
|
||||
i2s.tx_clkm_div_conf().modify(|_, w| unsafe {
|
||||
w.tx_clkm_div_x()
|
||||
.bits(clkm_div_x as u16)
|
||||
.tx_clkm_div_y()
|
||||
.bits(clkm_div_y as u16)
|
||||
.tx_clkm_div_yn1()
|
||||
.bit(clkm_div_yn1 != 0)
|
||||
.tx_clkm_div_z()
|
||||
.bits(clkm_div_z as u16)
|
||||
w.tx_clkm_div_x().bits(clkm_div_x as u16);
|
||||
w.tx_clkm_div_y().bits(clkm_div_y as u16);
|
||||
w.tx_clkm_div_yn1().bit(clkm_div_yn1 != 0);
|
||||
w.tx_clkm_div_z().bits(clkm_div_z as u16)
|
||||
});
|
||||
|
||||
i2s.tx_clkm_conf().modify(|_, w| unsafe {
|
||||
@ -1459,25 +1434,19 @@ mod private {
|
||||
});
|
||||
|
||||
i2s.rx_clkm_div_conf().modify(|_, w| unsafe {
|
||||
w.rx_clkm_div_x()
|
||||
.bits(clkm_div_x as u16)
|
||||
.rx_clkm_div_y()
|
||||
.bits(clkm_div_y as u16)
|
||||
.rx_clkm_div_yn1()
|
||||
.bit(clkm_div_yn1 != 0)
|
||||
.rx_clkm_div_z()
|
||||
.bits(clkm_div_z as u16)
|
||||
w.rx_clkm_div_x().bits(clkm_div_x as u16);
|
||||
w.rx_clkm_div_y().bits(clkm_div_y as u16);
|
||||
w.rx_clkm_div_yn1().bit(clkm_div_yn1 != 0);
|
||||
w.rx_clkm_div_z().bits(clkm_div_z as u16)
|
||||
});
|
||||
|
||||
i2s.rx_clkm_conf().modify(|_, w| unsafe {
|
||||
w.rx_clk_active()
|
||||
.set_bit()
|
||||
.rx_clk_sel()
|
||||
.bits(crate::soc::constants::I2S_DEFAULT_CLK_SRC) // for now fixed at 160MHz
|
||||
.rx_clkm_div_num()
|
||||
.bits(clock_settings.mclk_divider as u8)
|
||||
.mclk_sel()
|
||||
.bit(true)
|
||||
w.rx_clk_active().set_bit();
|
||||
// for now fixed at 160MHz
|
||||
w.rx_clk_sel()
|
||||
.bits(crate::soc::constants::I2S_DEFAULT_CLK_SRC);
|
||||
w.rx_clkm_div_num().bits(clock_settings.mclk_divider as u8);
|
||||
w.mclk_sel().bit(true)
|
||||
});
|
||||
|
||||
i2s.rx_conf1().modify(|_, w| unsafe {
|
||||
@ -1531,22 +1500,18 @@ mod private {
|
||||
}
|
||||
|
||||
pcr.i2s_tx_clkm_div_conf().modify(|_, w| unsafe {
|
||||
w.i2s_tx_clkm_div_x()
|
||||
.bits(clkm_div_x as u16)
|
||||
.i2s_tx_clkm_div_y()
|
||||
.bits(clkm_div_y as u16)
|
||||
.i2s_tx_clkm_div_yn1()
|
||||
.bit(clkm_div_yn1 != 0)
|
||||
.i2s_tx_clkm_div_z()
|
||||
.bits(clkm_div_z as u16)
|
||||
w.i2s_tx_clkm_div_x().bits(clkm_div_x as u16);
|
||||
w.i2s_tx_clkm_div_y().bits(clkm_div_y as u16);
|
||||
w.i2s_tx_clkm_div_yn1().bit(clkm_div_yn1 != 0);
|
||||
w.i2s_tx_clkm_div_z().bits(clkm_div_z as u16)
|
||||
});
|
||||
|
||||
pcr.i2s_tx_clkm_conf().modify(|_, w| unsafe {
|
||||
w.i2s_tx_clkm_en()
|
||||
.set_bit()
|
||||
.i2s_tx_clkm_sel()
|
||||
.bits(crate::soc::constants::I2S_DEFAULT_CLK_SRC) // for now fixed at 160MHz for C6 and 96MHz for H2
|
||||
.i2s_tx_clkm_div_num()
|
||||
w.i2s_tx_clkm_en().set_bit();
|
||||
// for now fixed at 160MHz for C6 and 96MHz for H2
|
||||
w.i2s_tx_clkm_sel()
|
||||
.bits(crate::soc::constants::I2S_DEFAULT_CLK_SRC);
|
||||
w.i2s_tx_clkm_div_num()
|
||||
.bits(clock_settings.mclk_divider as u8)
|
||||
});
|
||||
|
||||
@ -1562,25 +1527,20 @@ mod private {
|
||||
});
|
||||
|
||||
pcr.i2s_rx_clkm_div_conf().modify(|_, w| unsafe {
|
||||
w.i2s_rx_clkm_div_x()
|
||||
.bits(clkm_div_x as u16)
|
||||
.i2s_rx_clkm_div_y()
|
||||
.bits(clkm_div_y as u16)
|
||||
.i2s_rx_clkm_div_yn1()
|
||||
.bit(clkm_div_yn1 != 0)
|
||||
.i2s_rx_clkm_div_z()
|
||||
.bits(clkm_div_z as u16)
|
||||
w.i2s_rx_clkm_div_x().bits(clkm_div_x as u16);
|
||||
w.i2s_rx_clkm_div_y().bits(clkm_div_y as u16);
|
||||
w.i2s_rx_clkm_div_yn1().bit(clkm_div_yn1 != 0);
|
||||
w.i2s_rx_clkm_div_z().bits(clkm_div_z as u16)
|
||||
});
|
||||
|
||||
pcr.i2s_rx_clkm_conf().modify(|_, w| unsafe {
|
||||
w.i2s_rx_clkm_en()
|
||||
.set_bit()
|
||||
.i2s_rx_clkm_sel()
|
||||
.bits(crate::soc::constants::I2S_DEFAULT_CLK_SRC) // for now fixed at 160MHz for C6 and 96MHz for H2
|
||||
.i2s_rx_clkm_div_num()
|
||||
.bits(clock_settings.mclk_divider as u8)
|
||||
.i2s_mclk_sel()
|
||||
.bit(true)
|
||||
w.i2s_rx_clkm_en().set_bit();
|
||||
// for now fixed at 160MHz for C6 and 96MHz for H2
|
||||
w.i2s_rx_clkm_sel()
|
||||
.bits(crate::soc::constants::I2S_DEFAULT_CLK_SRC);
|
||||
w.i2s_rx_clkm_div_num()
|
||||
.bits(clock_settings.mclk_divider as u8);
|
||||
w.i2s_mclk_sel().bit(true)
|
||||
});
|
||||
#[cfg(not(esp32h2))]
|
||||
i2s.rx_conf1().modify(|_, w| unsafe {
|
||||
@ -1600,88 +1560,55 @@ mod private {
|
||||
#[allow(clippy::useless_conversion)]
|
||||
i2s.tx_conf1().modify(|_, w| unsafe {
|
||||
w.tx_tdm_ws_width()
|
||||
.bits((data_format.channel_bits() - 1).into())
|
||||
.tx_bits_mod()
|
||||
.bits(data_format.data_bits() - 1)
|
||||
.tx_tdm_chan_bits()
|
||||
.bits(data_format.channel_bits() - 1)
|
||||
.tx_half_sample_bits()
|
||||
.bits(data_format.channel_bits() - 1)
|
||||
.bits((data_format.channel_bits() - 1).into());
|
||||
w.tx_bits_mod().bits(data_format.data_bits() - 1);
|
||||
w.tx_tdm_chan_bits().bits(data_format.channel_bits() - 1);
|
||||
w.tx_half_sample_bits().bits(data_format.channel_bits() - 1)
|
||||
});
|
||||
#[cfg(not(esp32h2))]
|
||||
i2s.tx_conf1().modify(|_, w| w.tx_msb_shift().set_bit());
|
||||
#[cfg(esp32h2)]
|
||||
i2s.tx_conf().modify(|_, w| w.tx_msb_shift().set_bit());
|
||||
i2s.tx_conf().modify(|_, w| unsafe {
|
||||
w.tx_mono()
|
||||
.clear_bit()
|
||||
.tx_mono_fst_vld()
|
||||
.set_bit()
|
||||
.tx_stop_en()
|
||||
.set_bit()
|
||||
.tx_chan_equal()
|
||||
.clear_bit()
|
||||
.tx_tdm_en()
|
||||
.set_bit()
|
||||
.tx_pdm_en()
|
||||
.clear_bit()
|
||||
.tx_pcm_bypass()
|
||||
.set_bit()
|
||||
.tx_big_endian()
|
||||
.clear_bit()
|
||||
.tx_bit_order()
|
||||
.clear_bit()
|
||||
.tx_chan_mod()
|
||||
.bits(0)
|
||||
w.tx_mono().clear_bit();
|
||||
w.tx_mono_fst_vld().set_bit();
|
||||
w.tx_stop_en().set_bit();
|
||||
w.tx_chan_equal().clear_bit();
|
||||
w.tx_tdm_en().set_bit();
|
||||
w.tx_pdm_en().clear_bit();
|
||||
w.tx_pcm_bypass().set_bit();
|
||||
w.tx_big_endian().clear_bit();
|
||||
w.tx_bit_order().clear_bit();
|
||||
w.tx_chan_mod().bits(0)
|
||||
});
|
||||
|
||||
i2s.tx_tdm_ctrl().modify(|_, w| unsafe {
|
||||
w.tx_tdm_tot_chan_num()
|
||||
.bits(1)
|
||||
.tx_tdm_chan0_en()
|
||||
.set_bit()
|
||||
.tx_tdm_chan1_en()
|
||||
.set_bit()
|
||||
.tx_tdm_chan2_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan3_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan4_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan5_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan6_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan7_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan8_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan9_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan10_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan11_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan12_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan13_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan14_en()
|
||||
.clear_bit()
|
||||
.tx_tdm_chan15_en()
|
||||
.clear_bit()
|
||||
w.tx_tdm_tot_chan_num().bits(1);
|
||||
w.tx_tdm_chan0_en().set_bit();
|
||||
w.tx_tdm_chan1_en().set_bit();
|
||||
w.tx_tdm_chan2_en().clear_bit();
|
||||
w.tx_tdm_chan3_en().clear_bit();
|
||||
w.tx_tdm_chan4_en().clear_bit();
|
||||
w.tx_tdm_chan5_en().clear_bit();
|
||||
w.tx_tdm_chan6_en().clear_bit();
|
||||
w.tx_tdm_chan7_en().clear_bit();
|
||||
w.tx_tdm_chan8_en().clear_bit();
|
||||
w.tx_tdm_chan9_en().clear_bit();
|
||||
w.tx_tdm_chan10_en().clear_bit();
|
||||
w.tx_tdm_chan11_en().clear_bit();
|
||||
w.tx_tdm_chan12_en().clear_bit();
|
||||
w.tx_tdm_chan13_en().clear_bit();
|
||||
w.tx_tdm_chan14_en().clear_bit();
|
||||
w.tx_tdm_chan15_en().clear_bit()
|
||||
});
|
||||
|
||||
#[allow(clippy::useless_conversion)]
|
||||
i2s.rx_conf1().modify(|_, w| unsafe {
|
||||
w.rx_tdm_ws_width()
|
||||
.bits((data_format.channel_bits() - 1).into())
|
||||
.rx_bits_mod()
|
||||
.bits(data_format.data_bits() - 1)
|
||||
.rx_tdm_chan_bits()
|
||||
.bits(data_format.channel_bits() - 1)
|
||||
.rx_half_sample_bits()
|
||||
.bits(data_format.channel_bits() - 1)
|
||||
.bits((data_format.channel_bits() - 1).into());
|
||||
w.rx_bits_mod().bits(data_format.data_bits() - 1);
|
||||
w.rx_tdm_chan_bits().bits(data_format.channel_bits() - 1);
|
||||
w.rx_half_sample_bits().bits(data_format.channel_bits() - 1)
|
||||
});
|
||||
#[cfg(not(esp32h2))]
|
||||
i2s.rx_conf1().modify(|_, w| w.rx_msb_shift().set_bit());
|
||||
@ -1689,59 +1616,34 @@ mod private {
|
||||
i2s.rx_conf().modify(|_, w| w.rx_msb_shift().set_bit());
|
||||
|
||||
i2s.rx_conf().modify(|_, w| unsafe {
|
||||
w.rx_mono()
|
||||
.clear_bit()
|
||||
.rx_mono_fst_vld()
|
||||
.set_bit()
|
||||
.rx_stop_mode()
|
||||
.bits(2)
|
||||
.rx_tdm_en()
|
||||
.set_bit()
|
||||
.rx_pdm_en()
|
||||
.clear_bit()
|
||||
.rx_pcm_bypass()
|
||||
.set_bit()
|
||||
.rx_big_endian()
|
||||
.clear_bit()
|
||||
.rx_bit_order()
|
||||
.clear_bit()
|
||||
w.rx_mono().clear_bit();
|
||||
w.rx_mono_fst_vld().set_bit();
|
||||
w.rx_stop_mode().bits(2);
|
||||
w.rx_tdm_en().set_bit();
|
||||
w.rx_pdm_en().clear_bit();
|
||||
w.rx_pcm_bypass().set_bit();
|
||||
w.rx_big_endian().clear_bit();
|
||||
w.rx_bit_order().clear_bit()
|
||||
});
|
||||
|
||||
i2s.rx_tdm_ctrl().modify(|_, w| unsafe {
|
||||
w.rx_tdm_tot_chan_num()
|
||||
.bits(1)
|
||||
.rx_tdm_pdm_chan0_en()
|
||||
.set_bit()
|
||||
.rx_tdm_pdm_chan1_en()
|
||||
.set_bit()
|
||||
.rx_tdm_pdm_chan2_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_pdm_chan3_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_pdm_chan4_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_pdm_chan5_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_pdm_chan6_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_pdm_chan7_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_chan8_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_chan9_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_chan10_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_chan11_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_chan12_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_chan13_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_chan14_en()
|
||||
.clear_bit()
|
||||
.rx_tdm_chan15_en()
|
||||
.clear_bit()
|
||||
w.rx_tdm_tot_chan_num().bits(1);
|
||||
w.rx_tdm_pdm_chan0_en().set_bit();
|
||||
w.rx_tdm_pdm_chan1_en().set_bit();
|
||||
w.rx_tdm_pdm_chan2_en().clear_bit();
|
||||
w.rx_tdm_pdm_chan3_en().clear_bit();
|
||||
w.rx_tdm_pdm_chan4_en().clear_bit();
|
||||
w.rx_tdm_pdm_chan5_en().clear_bit();
|
||||
w.rx_tdm_pdm_chan6_en().clear_bit();
|
||||
w.rx_tdm_pdm_chan7_en().clear_bit();
|
||||
w.rx_tdm_chan8_en().clear_bit();
|
||||
w.rx_tdm_chan9_en().clear_bit();
|
||||
w.rx_tdm_chan10_en().clear_bit();
|
||||
w.rx_tdm_chan11_en().clear_bit();
|
||||
w.rx_tdm_chan12_en().clear_bit();
|
||||
w.rx_tdm_chan13_en().clear_bit();
|
||||
w.rx_tdm_chan14_en().clear_bit();
|
||||
w.rx_tdm_chan15_en().clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
@ -1762,13 +1664,19 @@ mod private {
|
||||
|
||||
fn reset_tx() {
|
||||
let i2s = Self::register_block();
|
||||
i2s.tx_conf()
|
||||
.modify(|_, w| w.tx_reset().set_bit().tx_fifo_reset().set_bit());
|
||||
i2s.tx_conf()
|
||||
.modify(|_, w| w.tx_reset().clear_bit().tx_fifo_reset().clear_bit());
|
||||
i2s.tx_conf().modify(|_, w| {
|
||||
w.tx_reset().set_bit();
|
||||
w.tx_fifo_reset().set_bit()
|
||||
});
|
||||
i2s.tx_conf().modify(|_, w| {
|
||||
w.tx_reset().clear_bit();
|
||||
w.tx_fifo_reset().clear_bit()
|
||||
});
|
||||
|
||||
i2s.int_clr()
|
||||
.write(|w| w.tx_done().clear_bit_by_one().tx_hung().clear_bit_by_one());
|
||||
i2s.int_clr().write(|w| {
|
||||
w.tx_done().clear_bit_by_one();
|
||||
w.tx_hung().clear_bit_by_one()
|
||||
});
|
||||
}
|
||||
|
||||
fn tx_start() {
|
||||
@ -1792,13 +1700,19 @@ mod private {
|
||||
|
||||
fn reset_rx() {
|
||||
let i2s = Self::register_block();
|
||||
i2s.rx_conf()
|
||||
.modify(|_, w| w.rx_reset().set_bit().rx_fifo_reset().set_bit());
|
||||
i2s.rx_conf()
|
||||
.modify(|_, w| w.rx_reset().clear_bit().rx_fifo_reset().clear_bit());
|
||||
i2s.rx_conf().modify(|_, w| {
|
||||
w.rx_reset().set_bit();
|
||||
w.rx_fifo_reset().set_bit()
|
||||
});
|
||||
i2s.rx_conf().modify(|_, w| {
|
||||
w.rx_reset().clear_bit();
|
||||
w.rx_fifo_reset().clear_bit()
|
||||
});
|
||||
|
||||
i2s.int_clr()
|
||||
.write(|w| w.rx_done().clear_bit_by_one().rx_hung().clear_bit_by_one());
|
||||
i2s.int_clr().write(|w| {
|
||||
w.rx_done().clear_bit_by_one();
|
||||
w.rx_hung().clear_bit_by_one()
|
||||
});
|
||||
}
|
||||
|
||||
fn rx_start(len: usize) {
|
||||
@ -2060,7 +1974,7 @@ mod private {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(esp32s3, esp32))]
|
||||
#[cfg(i2s1)]
|
||||
impl RegBlock for I2S1 {
|
||||
fn register_block() -> &'static RegisterBlock {
|
||||
unsafe { &*I2S1::PTR.cast::<RegisterBlock>() }
|
||||
@ -2076,7 +1990,7 @@ mod private {
|
||||
}
|
||||
impl super::RegisterAccess for I2S0 {}
|
||||
|
||||
#[cfg(any(esp32s3, esp32))]
|
||||
#[cfg(i2s1)]
|
||||
impl RegisterAccessPrivate for I2S1 {
|
||||
fn set_interrupt_handler(handler: InterruptHandler) {
|
||||
unsafe { crate::peripherals::I2S1::steal() }.bind_i2s1_interrupt(handler.handler());
|
||||
@ -2084,7 +1998,7 @@ mod private {
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
#[cfg(any(esp32s3, esp32))]
|
||||
#[cfg(i2s1)]
|
||||
impl super::RegisterAccess for I2S1 {}
|
||||
|
||||
pub trait I2s0Instance {}
|
||||
@ -2224,10 +2138,10 @@ pub mod asynch {
|
||||
self.tx_chain.fill_for_tx(false, ptr, len)?;
|
||||
future
|
||||
.tx
|
||||
.prepare_transfer_without_start(T::get_dma_peripheral(), &self.tx_chain)?;
|
||||
.prepare_transfer_without_start(T::get_dma_peripheral(), &self.tx_chain)
|
||||
.and_then(|_| future.tx.start_transfer())?;
|
||||
}
|
||||
|
||||
future.tx.start_transfer()?;
|
||||
T::tx_start();
|
||||
future.await?;
|
||||
|
||||
@ -2352,27 +2266,21 @@ pub mod asynch {
|
||||
return Err(Error::IllegalArgument);
|
||||
}
|
||||
|
||||
// Reset TX unit and TX FIFO
|
||||
// Reset RX unit and RX FIFO
|
||||
T::reset_rx();
|
||||
|
||||
let mut future = DmaRxFuture::new(&mut self.rx_channel);
|
||||
let future = DmaRxFuture::new(&mut self.rx_channel);
|
||||
|
||||
// configure DMA outlink
|
||||
// configure DMA inlink
|
||||
unsafe {
|
||||
self.rx_chain.fill_for_rx(false, ptr, len)?;
|
||||
future
|
||||
.rx()
|
||||
.prepare_transfer_without_start(T::get_dma_peripheral(), &self.rx_chain)?;
|
||||
.rx
|
||||
.prepare_transfer_without_start(T::get_dma_peripheral(), &self.rx_chain)
|
||||
.and_then(|_| future.rx.start_transfer())?;
|
||||
}
|
||||
future.rx().start_transfer()?;
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
// start: set I2S_RX_START
|
||||
#[cfg(not(esp32))]
|
||||
T::rx_start(len - 1);
|
||||
|
||||
#[cfg(esp32)]
|
||||
T::rx_start(len);
|
||||
|
||||
future.await?;
|
||||
@ -2393,12 +2301,12 @@ pub mod asynch {
|
||||
return Err(Error::IllegalArgument);
|
||||
}
|
||||
|
||||
// Reset TX unit and TX FIFO
|
||||
// Reset RX unit and RX FIFO
|
||||
T::reset_rx();
|
||||
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
// configure DMA inlink
|
||||
unsafe {
|
||||
self.rx_chain.fill_for_rx(true, ptr, len)?;
|
||||
self.rx_channel
|
||||
@ -2406,13 +2314,7 @@ pub mod asynch {
|
||||
.and_then(|_| self.rx_channel.start_transfer())?;
|
||||
}
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
// start: set I2S_RX_START
|
||||
#[cfg(not(esp32))]
|
||||
T::rx_start(len - 1);
|
||||
|
||||
#[cfg(esp32)]
|
||||
T::rx_start(len);
|
||||
|
||||
let state = RxCircularState::new(&mut self.rx_chain);
|
||||
|
||||
@ -59,10 +59,6 @@ harness = false
|
||||
name = "i2s"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "i2s_async"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "lcd_cam_i8080"
|
||||
harness = false
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
//! I2S Loopback Test
|
||||
//!
|
||||
//! 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
|
||||
//! with loopback mode enabled).
|
||||
|
||||
//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
|
||||
//% FEATURES: generic-queue
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
@ -13,11 +14,25 @@ use esp_hal::{
|
||||
dma::{Dma, DmaPriority},
|
||||
dma_buffers,
|
||||
gpio::{Io, NoPin},
|
||||
i2s::{DataFormat, I2s, I2sReadDma, I2sWriteDma, Standard},
|
||||
i2s::{asynch::*, DataFormat, I2s, I2sReadDma, I2sTx, I2sWriteDma, Standard},
|
||||
peripherals::I2S0,
|
||||
prelude::*,
|
||||
Async,
|
||||
};
|
||||
use hil_test as _;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32, esp32s2))] {
|
||||
use esp_hal::dma::I2s0DmaChannel as DmaChannel0;
|
||||
type DmaChannel0Creator = esp_hal::dma::I2s0DmaChannelCreator;
|
||||
} else {
|
||||
use esp_hal::dma::DmaChannel0;
|
||||
type DmaChannel0Creator = esp_hal::dma::ChannelCreator<0>;
|
||||
}
|
||||
}
|
||||
|
||||
const BUFFER_SIZE: usize = 2000;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SampleSource {
|
||||
i: u8,
|
||||
@ -43,19 +58,68 @@ impl Iterator for SampleSource {
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn writer(tx_buffer: &'static mut [u8], i2s_tx: I2sTx<'static, I2S0, DmaChannel0, Async>) {
|
||||
let mut samples = SampleSource::new();
|
||||
for b in tx_buffer.iter_mut() {
|
||||
*b = samples.next().unwrap();
|
||||
}
|
||||
|
||||
let mut tx_transfer = i2s_tx.write_dma_circular_async(tx_buffer).unwrap();
|
||||
|
||||
loop {
|
||||
tx_transfer
|
||||
.push_with(|buffer| {
|
||||
for b in buffer.iter_mut() {
|
||||
*b = samples.next().unwrap();
|
||||
}
|
||||
buffer.len()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_loopback() {
|
||||
unsafe {
|
||||
let i2s = esp_hal::peripherals::I2S0::steal();
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32, esp32s2))] {
|
||||
i2s.conf().modify(|_, w| w.sig_loopback().set_bit());
|
||||
i2s.conf().modify(|_, w| w.rx_slave_mod().set_bit());
|
||||
} else {
|
||||
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().set_bit());
|
||||
i2s.rx_conf().modify(|_, w| w.rx_update().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[embedded_test::tests]
|
||||
#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())]
|
||||
mod tests {
|
||||
// defmt::* is load-bearing, it ensures that the assert in dma_buffers! is not
|
||||
// using defmt's non-const assert. Doing so would result in a compile error.
|
||||
#[allow(unused_imports)]
|
||||
use defmt::{assert, assert_eq, *};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_i2s_loopback() {
|
||||
struct Context {
|
||||
io: Io,
|
||||
dma_channel: DmaChannel0Creator,
|
||||
i2s: I2S0,
|
||||
}
|
||||
|
||||
#[init]
|
||||
fn init() -> Context {
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
let delay = Delay::new();
|
||||
|
||||
let dma = Dma::new(peripherals.DMA);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
@ -66,19 +130,86 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let (mut rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(16000, 16000);
|
||||
Context {
|
||||
io,
|
||||
dma_channel,
|
||||
i2s: peripherals.I2S0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn test_i2s_loopback_async(ctx: Context) {
|
||||
let spawner = embassy_executor::Spawner::for_current_executor().await;
|
||||
|
||||
let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) =
|
||||
esp_hal::dma_circular_buffers!(BUFFER_SIZE, BUFFER_SIZE);
|
||||
|
||||
let i2s = I2s::new(
|
||||
peripherals.I2S0,
|
||||
ctx.i2s,
|
||||
Standard::Philips,
|
||||
DataFormat::Data16Channel16,
|
||||
16000.Hz(),
|
||||
dma_channel.configure(false, DmaPriority::Priority0),
|
||||
ctx.dma_channel
|
||||
.configure_for_async(false, DmaPriority::Priority0),
|
||||
rx_descriptors,
|
||||
tx_descriptors,
|
||||
);
|
||||
|
||||
let (_, dout) = hil_test::common_test_pins!(io);
|
||||
let (_, dout) = hil_test::common_test_pins!(ctx.io);
|
||||
|
||||
let din = dout.peripheral_input();
|
||||
|
||||
let i2s_tx = i2s
|
||||
.i2s_tx
|
||||
.with_bclk(NoPin)
|
||||
.with_ws(NoPin)
|
||||
.with_dout(dout)
|
||||
.build();
|
||||
|
||||
let i2s_rx = i2s
|
||||
.i2s_rx
|
||||
.with_bclk(NoPin)
|
||||
.with_ws(NoPin)
|
||||
.with_din(din)
|
||||
.build();
|
||||
|
||||
enable_loopback();
|
||||
|
||||
let mut rx_transfer = i2s_rx.read_dma_circular_async(rx_buffer).unwrap();
|
||||
spawner.must_spawn(writer(tx_buffer, i2s_tx));
|
||||
|
||||
let mut rcv = [0u8; BUFFER_SIZE];
|
||||
let mut sample_idx = 0;
|
||||
let mut samples = SampleSource::new();
|
||||
for _ in 0..30 {
|
||||
let len = rx_transfer.pop(&mut rcv).await.unwrap();
|
||||
for &b in &rcv[..len] {
|
||||
let expected = samples.next().unwrap();
|
||||
assert_eq!(
|
||||
b, expected,
|
||||
"Sample #{} does not match ({} != {})",
|
||||
sample_idx, b, expected
|
||||
);
|
||||
sample_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i2s_loopback(ctx: Context) {
|
||||
let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(16000, 16000);
|
||||
|
||||
let i2s = I2s::new(
|
||||
ctx.i2s,
|
||||
Standard::Philips,
|
||||
DataFormat::Data16Channel16,
|
||||
16000.Hz(),
|
||||
ctx.dma_channel.configure(false, DmaPriority::Priority0),
|
||||
rx_descriptors,
|
||||
tx_descriptors,
|
||||
);
|
||||
|
||||
let (_, dout) = hil_test::common_test_pins!(ctx.io);
|
||||
|
||||
let din = dout.peripheral_input();
|
||||
|
||||
@ -96,22 +227,7 @@ mod tests {
|
||||
.with_din(din)
|
||||
.build();
|
||||
|
||||
// enable loopback testing
|
||||
unsafe {
|
||||
let i2s = esp_hal::peripherals::I2S0::steal();
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32s2)] {
|
||||
i2s.conf().modify(|_, w| w.sig_loopback().set_bit());
|
||||
i2s.conf().modify(|_, w| w.rx_slave_mod().set_bit());
|
||||
} else {
|
||||
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().set_bit());
|
||||
i2s.rx_conf().modify(|_, w| w.rx_update().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
enable_loopback();
|
||||
|
||||
let mut samples = SampleSource::new();
|
||||
for b in tx_buffer.iter_mut() {
|
||||
@ -121,14 +237,14 @@ mod tests {
|
||||
let mut rcv = [0u8; 11000];
|
||||
let mut filler = [0x1u8; 12000];
|
||||
|
||||
let mut rx_transfer = i2s_rx.read_dma_circular(&mut rx_buffer).unwrap();
|
||||
let mut rx_transfer = i2s_rx.read_dma_circular(rx_buffer).unwrap();
|
||||
// trying to pop data before calling `available` should just do nothing
|
||||
assert_eq!(0, rx_transfer.pop(&mut rcv[..100]).unwrap());
|
||||
|
||||
// no data available yet
|
||||
assert_eq!(0, rx_transfer.available());
|
||||
|
||||
let mut tx_transfer = i2s_tx.write_dma_circular(&tx_buffer).unwrap();
|
||||
let mut tx_transfer = i2s_tx.write_dma_circular(tx_buffer).unwrap();
|
||||
|
||||
let mut iteration = 0;
|
||||
let mut sample_idx = 0;
|
||||
@ -180,7 +296,7 @@ mod tests {
|
||||
if iteration == 1 {
|
||||
// delay to make it likely `available` will need to handle more than one
|
||||
// descriptor next time
|
||||
delay.delay_millis(160);
|
||||
Delay::new().delay_millis(160);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,174 +0,0 @@
|
||||
//! I2S Loopback Test (Async)
|
||||
//!
|
||||
//! 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 esp32h2 esp32s2 esp32s3
|
||||
//% FEATURES: generic-queue
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp_hal::{
|
||||
dma::{Dma, DmaPriority},
|
||||
gpio::{Io, NoPin},
|
||||
i2s::{asynch::*, DataFormat, I2s, I2sTx, Standard},
|
||||
peripherals::I2S0,
|
||||
prelude::*,
|
||||
Async,
|
||||
};
|
||||
use hil_test as _;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32, esp32s2))] {
|
||||
use esp_hal::dma::I2s0DmaChannel as DmaChannel0;
|
||||
} else {
|
||||
use esp_hal::dma::DmaChannel0;
|
||||
}
|
||||
}
|
||||
|
||||
const BUFFER_SIZE: usize = 2000;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SampleSource {
|
||||
i: u8,
|
||||
}
|
||||
|
||||
impl SampleSource {
|
||||
// choose values which DON'T restart on every descriptor buffer's start
|
||||
const ADD: u8 = 5;
|
||||
const CUT_OFF: u8 = 113;
|
||||
|
||||
fn new() -> Self {
|
||||
Self { i: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SampleSource {
|
||||
type Item = u8;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let i = self.i;
|
||||
self.i = (i + Self::ADD) % Self::CUT_OFF;
|
||||
Some(i)
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn writer(tx_buffer: &'static mut [u8], i2s_tx: I2sTx<'static, I2S0, DmaChannel0, Async>) {
|
||||
let mut samples = SampleSource::new();
|
||||
for b in tx_buffer.iter_mut() {
|
||||
*b = samples.next().unwrap();
|
||||
}
|
||||
|
||||
let mut tx_transfer = i2s_tx.write_dma_circular_async(tx_buffer).unwrap();
|
||||
|
||||
loop {
|
||||
tx_transfer
|
||||
.push_with(|buffer| {
|
||||
for b in buffer.iter_mut() {
|
||||
*b = samples.next().unwrap();
|
||||
}
|
||||
buffer.len()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())]
|
||||
mod tests {
|
||||
// defmt::* is load-bearing, it ensures that the assert in dma_buffers! is not
|
||||
// using defmt's non-const assert. Doing so would result in a compile error.
|
||||
#[allow(unused_imports)]
|
||||
use defmt::{assert_eq, *};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
async fn test_i2s_loopback() {
|
||||
let spawner = embassy_executor::Spawner::for_current_executor().await;
|
||||
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
let dma = Dma::new(peripherals.DMA);
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32, esp32s2))] {
|
||||
let dma_channel = dma.i2s0channel;
|
||||
} else {
|
||||
let dma_channel = dma.channel0;
|
||||
}
|
||||
}
|
||||
|
||||
let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) =
|
||||
esp_hal::dma_circular_buffers!(BUFFER_SIZE, BUFFER_SIZE);
|
||||
|
||||
let i2s = I2s::new(
|
||||
peripherals.I2S0,
|
||||
Standard::Philips,
|
||||
DataFormat::Data16Channel16,
|
||||
16000.Hz(),
|
||||
dma_channel.configure_for_async(false, DmaPriority::Priority0),
|
||||
rx_descriptors,
|
||||
tx_descriptors,
|
||||
);
|
||||
|
||||
let (_, dout) = hil_test::common_test_pins!(io);
|
||||
|
||||
let din = dout.peripheral_input();
|
||||
|
||||
let i2s_tx = i2s
|
||||
.i2s_tx
|
||||
.with_bclk(NoPin)
|
||||
.with_ws(NoPin)
|
||||
.with_dout(dout)
|
||||
.build();
|
||||
|
||||
let i2s_rx = i2s
|
||||
.i2s_rx
|
||||
.with_bclk(NoPin)
|
||||
.with_ws(NoPin)
|
||||
.with_din(din)
|
||||
.build();
|
||||
|
||||
// enable loopback testing
|
||||
unsafe {
|
||||
let i2s = esp_hal::peripherals::I2S0::steal();
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32s2)] {
|
||||
i2s.conf().modify(|_, w| w.sig_loopback().set_bit());
|
||||
i2s.conf().modify(|_, w| w.rx_slave_mod().set_bit());
|
||||
} else {
|
||||
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().set_bit());
|
||||
i2s.rx_conf().modify(|_, w| w.rx_update().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut rx_transfer = i2s_rx.read_dma_circular_async(rx_buffer).unwrap();
|
||||
spawner.must_spawn(writer(tx_buffer, i2s_tx));
|
||||
|
||||
let mut rcv = [0u8; BUFFER_SIZE];
|
||||
let mut sample_idx = 0;
|
||||
let mut samples = SampleSource::new();
|
||||
for _ in 0..30 {
|
||||
let len = rx_transfer.pop(&mut rcv).await.unwrap();
|
||||
for &b in &rcv[..len] {
|
||||
let expected = samples.next().unwrap();
|
||||
assert_eq!(
|
||||
b, expected,
|
||||
"Sample #{} does not match ({} != {})",
|
||||
sample_idx, b, expected
|
||||
);
|
||||
sample_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user