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:
Dániel Buga 2024-09-19 09:33:52 +02:00 committed by GitHub
parent f765a6b094
commit 46be3b19b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 361 additions and 526 deletions

View File

@ -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>

View File

@ -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);

View File

@ -59,10 +59,6 @@ harness = false
name = "i2s"
harness = false
[[test]]
name = "i2s_async"
harness = false
[[test]]
name = "lcd_cam_i8080"
harness = false

View File

@ -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);
}
}

View File

@ -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;
}
}
}
}