#![cfg_attr(docsrs, procmacros::doc_replace(
"dma_channel" => {
cfg(any(esp32, esp32s2)) => "DMA_I2S0",
cfg(not(any(esp32, esp32s2))) => "DMA_CH0"
},
"mclk" => {
cfg(not(esp32)) => "let i2s = i2s.with_mclk(peripherals.GPIO0);",
_ => ""
}
))]
#![doc = include_str!("tdm_slot_philips.svg")]
#![doc = include_str!("tdm_slot_msb.svg")]
#![doc = include_str!("tdm_slot_pcm_short.svg")]
#![doc = include_str!("tdm_slot_pcm_long.svg")]
use enumset::{EnumSet, EnumSetType};
use private::*;
use crate::{
Async,
Blocking,
DriverMode,
RegisterToggle,
dma::{
Channel,
ChannelRx,
ChannelTx,
DescriptorChain,
DmaChannelFor,
DmaEligible,
DmaError,
DmaTransferRx,
DmaTransferRxCircular,
DmaTransferTx,
DmaTransferTxCircular,
PeripheralRxChannel,
PeripheralTxChannel,
ReadBuffer,
WriteBuffer,
dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx},
},
gpio::{OutputConfig, interconnect::PeripheralOutput},
i2s::AnyI2s,
interrupt::{InterruptConfigurable, InterruptHandler},
system::PeripheralGuard,
time::Rate,
};
#[derive(Debug, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2sInterrupt {
RxHung,
TxHung,
#[cfg(not(any(esp32, esp32s2)))]
RxDone,
#[cfg(not(any(esp32, esp32s2)))]
TxDone,
}
#[cfg(any(esp32, esp32s2, esp32s3))]
pub(crate) const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 6;
#[cfg(any(esp32c3, esp32c6, esp32h2))]
pub(crate) const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 9;
pub(crate) const I2S_LL_MCLK_DIVIDER_MAX: usize = (1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1;
pub trait AcceptedWord: crate::private::Sealed {}
impl AcceptedWord for u8 {}
impl AcceptedWord for u16 {}
impl AcceptedWord for u32 {}
impl AcceptedWord for i8 {}
impl AcceptedWord for i16 {}
impl AcceptedWord for i32 {}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(clippy::enum_variant_names, reason = "peripheral is unstable")]
pub enum Error {
Unknown,
DmaError(DmaError),
IllegalArgument,
}
impl From<DmaError> for Error {
fn from(value: DmaError) -> Self {
Error::DmaError(value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(not(any(esp32, esp32s2)))]
pub enum DataFormat {
Data32Channel32,
Data32Channel24,
Data32Channel16,
Data32Channel8,
Data16Channel16,
Data16Channel8,
Data8Channel8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(esp32s2)]
pub enum DataFormat {
Data32Channel32,
Data24Channel24,
Data16Channel16,
Data8Channel8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(esp32)]
pub enum DataFormat {
Data32Channel32,
Data16Channel16,
}
#[cfg(not(any(esp32, esp32s2)))]
impl DataFormat {
pub fn data_bits(&self) -> u8 {
match self {
DataFormat::Data32Channel32 => 32,
DataFormat::Data32Channel24 => 32,
DataFormat::Data32Channel16 => 32,
DataFormat::Data32Channel8 => 32,
DataFormat::Data16Channel16 => 16,
DataFormat::Data16Channel8 => 16,
DataFormat::Data8Channel8 => 8,
}
}
pub fn channel_bits(&self) -> u8 {
match self {
DataFormat::Data32Channel32 => 32,
DataFormat::Data32Channel24 => 24,
DataFormat::Data32Channel16 => 16,
DataFormat::Data32Channel8 => 8,
DataFormat::Data16Channel16 => 16,
DataFormat::Data16Channel8 => 8,
DataFormat::Data8Channel8 => 8,
}
}
}
#[cfg(esp32s2)]
impl DataFormat {
pub fn data_bits(&self) -> u8 {
match self {
DataFormat::Data32Channel32 => 32,
DataFormat::Data24Channel24 => 24,
DataFormat::Data16Channel16 => 16,
DataFormat::Data8Channel8 => 8,
}
}
pub fn channel_bits(&self) -> u8 {
match self {
DataFormat::Data32Channel32 => 32,
DataFormat::Data24Channel24 => 24,
DataFormat::Data16Channel16 => 16,
DataFormat::Data8Channel8 => 8,
}
}
}
#[cfg(esp32)]
impl DataFormat {
pub fn data_bits(&self) -> u8 {
match self {
DataFormat::Data32Channel32 => 32,
DataFormat::Data16Channel16 => 16,
}
}
pub fn channel_bits(&self) -> u8 {
match self {
DataFormat::Data32Channel32 => 32,
DataFormat::Data16Channel16 => 16,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BitOrder {
#[default]
MsbFirst,
LsbFirst,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Endianness {
#[default]
LittleEndian,
BigEndian,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WsWidth {
#[default]
HalfFrame,
#[cfg(not(any(esp32, esp32s2)))]
OneChannel,
Bit,
#[cfg(not(any(esp32, esp32s2)))]
Bits(u16),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Polarity {
#[default]
ActiveHigh,
ActiveLow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Channels {
count: u8,
mask: u16,
fill: Option<u32>,
}
impl Channels {
pub const STEREO: Channels = Channels::new_impl(2, 0b11, None);
pub const MONO: Channels = Channels::new_impl(2, 0b01, None);
pub const LEFT: Channels = Channels::new_impl(2, 0b01, Some(0));
pub const RIGHT: Channels = Channels::new_impl(2, 0b10, Some(0));
#[procmacros::doc_replace]
#[cfg(not(any(esp32, esp32s2)))]
pub const fn new(count: u8, mask: u16, fill: Option<u32>) -> Self {
Self::new_impl(count, mask, fill)
}
const fn new_impl(count: u8, mut mask: u16, fill: Option<u32>) -> Self {
mask &= (1 << count) - 1;
Self { count, mask, fill }
}
#[cfg(not(any(esp32, esp32s2)))]
fn active_count(&self) -> u8 {
self.mask.count_ones() as u8
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
rx_config: UnitConfig,
tx_config: UnitConfig,
signal_loopback: bool,
#[cfg(any(esp32, esp32s2))]
sample_rate: Rate,
#[cfg(any(esp32, esp32s2))]
data_format: DataFormat,
}
impl Config {
pub fn new_tdm_philips() -> Self {
Self {
rx_config: UnitConfig::new_tdm_philips(),
tx_config: UnitConfig::new_tdm_philips(),
..Default::default()
}
}
pub fn new_tdm_msb() -> Self {
Self {
rx_config: UnitConfig::new_tdm_msb(),
tx_config: UnitConfig::new_tdm_msb(),
..Default::default()
}
}
pub fn new_tdm_pcm_short() -> Self {
Self {
rx_config: UnitConfig::new_tdm_pcm_short(),
tx_config: UnitConfig::new_tdm_pcm_short(),
..Default::default()
}
}
#[cfg(not(any(esp32, esp32s2)))]
pub fn new_tdm_pcm_long() -> Self {
Self {
rx_config: UnitConfig::new_tdm_pcm_long(),
tx_config: UnitConfig::new_tdm_pcm_long(),
..Default::default()
}
}
#[must_use]
#[cfg(not(any(esp32, esp32s2)))]
pub fn with_sample_rate(mut self, sample_rate: Rate) -> Self {
self.rx_config.sample_rate = sample_rate;
self.tx_config.sample_rate = sample_rate;
self
}
#[must_use]
pub fn with_channels(mut self, channels: Channels) -> Self {
self.rx_config.channels = channels;
self.tx_config.channels = channels;
self
}
#[must_use]
#[cfg(not(any(esp32, esp32s2)))]
pub fn with_data_format(mut self, data_format: DataFormat) -> Self {
self.rx_config.data_format = data_format;
self.tx_config.data_format = data_format;
self
}
#[must_use]
pub fn with_ws_width(mut self, ws_width: WsWidth) -> Self {
self.rx_config.ws_width = ws_width;
self.tx_config.ws_width = ws_width;
self
}
#[must_use]
pub fn with_ws_polarity(mut self, ws_polarity: Polarity) -> Self {
self.rx_config.ws_polarity = ws_polarity;
self.tx_config.ws_polarity = ws_polarity;
self
}
#[must_use]
pub fn with_msb_shift(mut self, msb_shift: bool) -> Self {
self.rx_config.msb_shift = msb_shift;
self.tx_config.msb_shift = msb_shift;
self
}
#[cfg(not(esp32))]
#[must_use]
pub fn with_endianness(mut self, endianness: Endianness) -> Self {
self.rx_config.endianness = endianness;
self.tx_config.endianness = endianness;
self
}
#[cfg(not(any(esp32, esp32s2)))]
#[must_use]
pub fn with_bit_order(mut self, bit_order: BitOrder) -> Self {
self.rx_config.bit_order = bit_order;
self.tx_config.bit_order = bit_order;
self
}
#[cfg(any(esp32, esp32s2))]
fn calculate_clock(&self) -> I2sClockDividers {
I2sClockDividers::new(self.sample_rate, 2, self.data_format.data_bits())
}
fn validate(&self) -> Result<(), ConfigError> {
self.rx_config.validate()?;
self.tx_config.validate()?;
Ok(())
}
}
#[allow(clippy::derivable_impls)]
impl Default for Config {
fn default() -> Self {
Self {
rx_config: UnitConfig::new_tdm_philips(),
tx_config: UnitConfig::new_tdm_philips(),
signal_loopback: false,
#[cfg(any(esp32, esp32s2))]
sample_rate: Rate::from_hz(44100),
#[cfg(any(esp32, esp32s2))]
data_format: DataFormat::Data16Channel16,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct UnitConfig {
#[cfg(not(any(esp32, esp32s2)))]
sample_rate: Rate,
channels: Channels,
#[cfg(not(any(esp32, esp32s2)))]
data_format: DataFormat,
ws_width: WsWidth,
ws_polarity: Polarity,
msb_shift: bool,
#[cfg(not(esp32))]
endianness: Endianness,
#[cfg(not(any(esp32, esp32s2)))]
bit_order: BitOrder,
}
impl UnitConfig {
pub fn new_tdm_philips() -> Self {
Self {
#[cfg(not(any(esp32, esp32s2)))]
sample_rate: Rate::from_hz(44100),
channels: Channels::STEREO,
#[cfg(not(any(esp32, esp32s2)))]
data_format: DataFormat::Data16Channel16,
ws_width: WsWidth::HalfFrame,
ws_polarity: Polarity::ActiveLow,
msb_shift: true,
#[cfg(not(esp32))]
endianness: Endianness::LittleEndian,
#[cfg(not(any(esp32, esp32s2)))]
bit_order: BitOrder::MsbFirst,
}
}
pub fn new_tdm_msb() -> Self {
Self::new_tdm_philips().with_msb_shift(false)
}
pub fn new_tdm_pcm_short() -> Self {
Self::new_tdm_philips()
.with_ws_width(WsWidth::Bit)
.with_ws_polarity(Polarity::ActiveHigh)
}
#[cfg(not(any(esp32, esp32s2)))]
pub fn new_tdm_pcm_long() -> Self {
Self::new_tdm_philips()
.with_ws_width(WsWidth::OneChannel)
.with_ws_polarity(Polarity::ActiveHigh)
}
fn validate(&self) -> Result<(), ConfigError> {
#[cfg(not(any(esp32, esp32s2)))]
if self.channels.active_count() == 0 || self.channels.count > 16 {
return Err(ConfigError::ChannelsOutOfRange);
}
Ok(())
}
#[cfg(not(any(esp32, esp32s2)))]
fn calculate_ws_width(&self) -> Result<u16, ConfigError> {
let ws_width = match self.ws_width {
WsWidth::HalfFrame => {
self.data_format.data_bits() as u16 * self.channels.count as u16 / 2
}
WsWidth::Bit => 1,
WsWidth::OneChannel => self.data_format.data_bits() as u16,
WsWidth::Bits(bits) => bits,
};
#[cfg(not(esp32h2))]
const MAX_WS_WIDTH: u16 = 128;
#[cfg(esp32h2)]
const MAX_WS_WIDTH: u16 = 512;
if !(1..=MAX_WS_WIDTH).contains(&ws_width)
|| ws_width > self.data_format.data_bits() as u16 * self.channels.count as u16
{
return Err(ConfigError::WsWidthOutOfRange);
}
Ok(ws_width)
}
#[cfg(not(any(esp32, esp32s2)))]
fn calculate_clock(&self) -> I2sClockDividers {
I2sClockDividers::new(
self.sample_rate,
self.channels.count,
self.data_format.data_bits(),
)
}
}
impl Default for UnitConfig {
fn default() -> Self {
Self::new_tdm_philips()
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigError {
#[cfg(not(any(esp32, esp32s2)))]
ChannelsOutOfRange,
#[cfg(not(any(esp32, esp32s2)))]
WsWidthOutOfRange,
}
impl core::error::Error for ConfigError {}
impl core::fmt::Display for ConfigError {
#[allow(unused_variables)]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
#[cfg(not(any(esp32, esp32s2)))]
ConfigError::ChannelsOutOfRange => {
write!(
f,
"Provided channels configuration has no active channels or has over 16 total channels"
)
}
#[cfg(not(any(esp32, esp32s2)))]
ConfigError::WsWidthOutOfRange => {
write!(
f,
"The requested WS signal width is out of supported range (1..=128)"
)
}
}
}
}
#[non_exhaustive]
pub struct I2s<'d, Dm>
where
Dm: DriverMode,
{
pub i2s_rx: RxCreator<'d, Dm>,
pub i2s_tx: TxCreator<'d, Dm>,
}
impl<Dm> I2s<'_, Dm>
where
Dm: DriverMode,
{
#[cfg_attr(
not(multi_core),
doc = "Registers an interrupt handler for the peripheral."
)]
#[cfg_attr(
multi_core,
doc = "Registers an interrupt handler for the peripheral on the current core."
)]
#[doc = ""]
#[instability::unstable]
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
self.i2s_tx.i2s.set_interrupt_handler(handler);
}
#[instability::unstable]
pub fn listen(&mut self, interrupts: impl Into<EnumSet<I2sInterrupt>>) {
self.i2s_tx.i2s.enable_listen(interrupts.into(), true);
}
#[instability::unstable]
pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<I2sInterrupt>>) {
self.i2s_tx.i2s.enable_listen(interrupts.into(), false);
}
#[instability::unstable]
pub fn interrupts(&mut self) -> EnumSet<I2sInterrupt> {
self.i2s_tx.i2s.interrupts()
}
#[instability::unstable]
pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<I2sInterrupt>>) {
self.i2s_tx.i2s.clear_interrupts(interrupts.into());
}
}
impl<Dm> crate::private::Sealed for I2s<'_, Dm> where Dm: DriverMode {}
impl<Dm> InterruptConfigurable for I2s<'_, Dm>
where
Dm: DriverMode,
{
fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) {
I2s::set_interrupt_handler(self, handler);
}
}
impl<'d> I2s<'d, Blocking> {
pub fn new(
i2s: impl Instance + 'd,
channel: impl DmaChannelFor<AnyI2s<'d>>,
config: Config,
) -> Result<Self, ConfigError> {
let channel = Channel::new(channel.degrade());
channel.runtime_ensure_compatible(&i2s);
let i2s = i2s.degrade();
let peripheral = i2s.peripheral();
let rx_guard = PeripheralGuard::new(peripheral);
let tx_guard = PeripheralGuard::new(peripheral);
i2s.set_master();
i2s.configure(&config)?;
i2s.update();
Ok(Self {
i2s_rx: RxCreator {
i2s: unsafe { i2s.clone_unchecked() },
rx_channel: channel.rx,
guard: rx_guard,
#[cfg(any(esp32, esp32s2))]
data_format: config.data_format,
},
i2s_tx: TxCreator {
i2s,
tx_channel: channel.tx,
guard: tx_guard,
#[cfg(any(esp32, esp32s2))]
data_format: config.data_format,
},
})
}
pub fn into_async(self) -> I2s<'d, Async> {
I2s {
i2s_rx: RxCreator {
i2s: self.i2s_rx.i2s,
rx_channel: self.i2s_rx.rx_channel.into_async(),
guard: self.i2s_rx.guard,
#[cfg(any(esp32, esp32s2))]
data_format: self.i2s_rx.data_format,
},
i2s_tx: TxCreator {
i2s: self.i2s_tx.i2s,
tx_channel: self.i2s_tx.tx_channel.into_async(),
guard: self.i2s_tx.guard,
#[cfg(any(esp32, esp32s2))]
data_format: self.i2s_tx.data_format,
},
}
}
}
#[cfg(esp32)]
mod esp32 {
use super::*;
use crate::gpio::OutputSignal;
pub trait ClkPin<'d>: PeripheralOutput<'d> {
#[doc(hidden)]
fn signal(&self) -> OutputSignal;
}
impl<'d> ClkPin<'d> for crate::peripherals::GPIO0<'d> {
fn signal(&self) -> OutputSignal {
OutputSignal::CLK_OUT1
}
}
impl<'d> ClkPin<'d> for crate::peripherals::GPIO1<'d> {
fn signal(&self) -> OutputSignal {
OutputSignal::CLK_OUT3
}
}
impl<'d> ClkPin<'d> for crate::peripherals::GPIO3<'d> {
fn signal(&self) -> OutputSignal {
OutputSignal::CLK_OUT2
}
}
}
#[cfg(esp32)]
pub use esp32::ClkPin;
impl<'d, Dm> I2s<'d, Dm>
where
Dm: DriverMode,
{
#[cfg(not(esp32))]
pub fn with_mclk(self, mclk: impl PeripheralOutput<'d>) -> Self {
let mclk = mclk.into();
mclk.apply_output_config(&OutputConfig::default());
mclk.set_output_enable(true);
self.i2s_tx.i2s.mclk_signal().connect_to(&mclk);
self
}
#[cfg(esp32)]
pub fn with_mclk(self, mclk: impl ClkPin<'d>) -> Self {
use crate::{gpio::OutputSignal, peripherals::IO_MUX};
let clk_signal = mclk.signal();
let mclk = mclk.into();
mclk.apply_output_config(&OutputConfig::default());
mclk.set_output_enable(true);
let selector = match self.i2s_rx.i2s.0 {
super::any::Inner::I2s0(_) => 0x0,
super::any::Inner::I2s1(_) => 0xF,
};
IO_MUX::regs().pin_ctrl().modify(|_, w| unsafe {
match clk_signal {
OutputSignal::CLK_OUT1 => w.clk1().bits(selector),
OutputSignal::CLK_OUT2 => w.clk2().bits(selector),
OutputSignal::CLK_OUT3 => w.clk3().bits(selector),
_ => unreachable!(),
}
});
clk_signal.connect_to(&mclk);
self
}
}
pub struct I2sTx<'d, Dm>
where
Dm: DriverMode,
{
i2s: AnyI2s<'d>,
tx_channel: ChannelTx<Dm, PeripheralTxChannel<AnyI2s<'d>>>,
tx_chain: DescriptorChain,
_guard: PeripheralGuard,
#[cfg(any(esp32, esp32s2))]
data_format: DataFormat,
}
impl<Dm> core::fmt::Debug for I2sTx<'_, Dm>
where
Dm: DriverMode,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("I2sTx").finish()
}
}
impl<Dm> DmaSupport for I2sTx<'_, Dm>
where
Dm: DriverMode,
{
type DriverMode = Dm;
fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) {
self.i2s.wait_for_tx_done();
}
fn peripheral_dma_stop(&mut self) {
self.i2s.tx_stop();
}
}
impl<'d, Dm> DmaSupportTx for I2sTx<'d, Dm>
where
Dm: DriverMode,
{
type Channel = PeripheralTxChannel<AnyI2s<'d>>;
fn tx(&mut self) -> &mut ChannelTx<Dm, PeripheralTxChannel<AnyI2s<'d>>> {
&mut self.tx_channel
}
fn chain(&mut self) -> &mut DescriptorChain {
&mut self.tx_chain
}
}
impl<Dm> I2sTx<'_, Dm>
where
Dm: DriverMode,
{
fn write(&mut self, data: &[u8]) -> Result<(), Error> {
self.start_tx_transfer(&data, false)?;
self.i2s.wait_for_tx_done();
Ok(())
}
fn start_tx_transfer<'t, TXBUF>(
&'t mut self,
words: &'t TXBUF,
circular: bool,
) -> Result<(), Error>
where
TXBUF: ReadBuffer,
Dm: DriverMode,
{
let (ptr, len) = unsafe { words.read_buffer() };
self.i2s.reset_tx();
unsafe {
self.tx_chain.fill_for_tx(circular, ptr, len)?;
self.tx_channel
.prepare_transfer_without_start(self.i2s.dma_peripheral(), &self.tx_chain)
.and_then(|_| self.tx_channel.start_transfer())?;
}
self.i2s.tx_start();
Ok(())
}
pub fn apply_config(&mut self, tx_config: &UnitConfig) -> Result<(), ConfigError> {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2))] {
self.i2s.configure_tx(tx_config, self.data_format)
} else {
self.i2s.configure_tx(tx_config)
}
}
}
pub fn write_words(&mut self, words: &[impl AcceptedWord]) -> Result<(), Error> {
self.write(unsafe {
core::slice::from_raw_parts(words.as_ptr().cast::<u8>(), core::mem::size_of_val(words))
})
}
pub fn write_dma<'t>(
&'t mut self,
words: &'t impl ReadBuffer,
) -> Result<DmaTransferTx<'t, Self>, Error>
where
Self: DmaSupportTx,
{
self.start_tx_transfer(words, false)?;
Ok(DmaTransferTx::new(self))
}
pub fn write_dma_circular<'t>(
&'t mut self,
words: &'t impl ReadBuffer,
) -> Result<DmaTransferTxCircular<'t, Self>, Error>
where
Self: DmaSupportTx,
{
self.start_tx_transfer(words, true)?;
Ok(DmaTransferTxCircular::new(self))
}
}
pub struct I2sRx<'d, Dm>
where
Dm: DriverMode,
{
i2s: AnyI2s<'d>,
rx_channel: ChannelRx<Dm, PeripheralRxChannel<AnyI2s<'d>>>,
rx_chain: DescriptorChain,
_guard: PeripheralGuard,
#[cfg(any(esp32, esp32s2))]
data_format: DataFormat,
}
impl<Dm> core::fmt::Debug for I2sRx<'_, Dm>
where
Dm: DriverMode,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("I2sRx").finish()
}
}
impl<Dm> DmaSupport for I2sRx<'_, Dm>
where
Dm: DriverMode,
{
type DriverMode = Dm;
fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) {
self.i2s.wait_for_rx_done();
}
fn peripheral_dma_stop(&mut self) {
self.i2s.reset_rx();
}
}
#[instability::unstable]
impl<'d, Dm> DmaSupportRx for I2sRx<'d, Dm>
where
Dm: DriverMode,
{
type Channel = PeripheralRxChannel<AnyI2s<'d>>;
fn rx(&mut self) -> &mut ChannelRx<Dm, PeripheralRxChannel<AnyI2s<'d>>> {
&mut self.rx_channel
}
fn chain(&mut self) -> &mut DescriptorChain {
&mut self.rx_chain
}
}
impl<Dm> I2sRx<'_, Dm>
where
Dm: DriverMode,
{
fn read(&mut self, mut data: &mut [u8]) -> Result<(), Error> {
self.start_rx_transfer(&mut data, false)?;
self.i2s.wait_for_rx_done();
Ok(())
}
fn start_rx_transfer<'t, RXBUF>(
&'t mut self,
words: &'t mut RXBUF,
circular: bool,
) -> Result<(), Error>
where
RXBUF: WriteBuffer,
{
let (ptr, len) = unsafe { words.write_buffer() };
if !len.is_multiple_of(4) {
return Err(Error::IllegalArgument);
}
self.i2s.reset_rx();
unsafe {
self.rx_chain.fill_for_rx(circular, ptr, len)?;
self.rx_channel
.prepare_transfer_without_start(self.i2s.dma_peripheral(), &self.rx_chain)
.and_then(|_| self.rx_channel.start_transfer())?;
}
self.i2s.rx_start(len);
Ok(())
}
pub fn apply_config(&mut self, rx_config: &UnitConfig) -> Result<(), ConfigError> {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2))] {
self.i2s.configure_rx(rx_config, self.data_format)
} else {
self.i2s.configure_rx(rx_config)
}
}
}
pub fn read_words(&mut self, words: &mut [impl AcceptedWord]) -> Result<(), Error> {
if core::mem::size_of_val(words) > 4096 || words.is_empty() {
return Err(Error::IllegalArgument);
}
self.read(unsafe {
core::slice::from_raw_parts_mut(
words.as_mut_ptr().cast::<u8>(),
core::mem::size_of_val(words),
)
})
}
pub fn read_dma<'t>(
&'t mut self,
words: &'t mut impl WriteBuffer,
) -> Result<DmaTransferRx<'t, Self>, Error>
where
Self: DmaSupportRx,
{
self.start_rx_transfer(words, false)?;
Ok(DmaTransferRx::new(self))
}
pub fn read_dma_circular<'t>(
&'t mut self,
words: &'t mut impl WriteBuffer,
) -> Result<DmaTransferRxCircular<'t, Self>, Error>
where
Self: DmaSupportRx,
{
self.start_rx_transfer(words, true)?;
Ok(DmaTransferRxCircular::new(self))
}
}
pub trait Instance: RegisterAccessPrivate + super::any::Degrade {}
#[cfg(soc_has_i2s0)]
impl Instance for crate::peripherals::I2S0<'_> {}
#[cfg(soc_has_i2s1)]
impl Instance for crate::peripherals::I2S1<'_> {}
impl Instance for AnyI2s<'_> {}
mod private {
use enumset::EnumSet;
use super::*;
#[cfg(not(soc_has_i2s1))]
use crate::pac::i2s0::RegisterBlock;
use crate::{
DriverMode,
dma::{ChannelRx, ChannelTx, DescriptorChain, DmaDescriptor, DmaEligible},
gpio::{
InputConfig,
InputSignal,
OutputConfig,
OutputSignal,
interconnect::{PeripheralInput, PeripheralOutput},
},
i2s::any::Inner as AnyI2sInner,
interrupt::InterruptHandler,
peripherals::I2S0,
};
#[cfg(soc_has_i2s1)]
use crate::{pac::i2s1::RegisterBlock, peripherals::I2S1};
pub struct TxCreator<'d, Dm>
where
Dm: DriverMode,
{
pub i2s: AnyI2s<'d>,
pub tx_channel: ChannelTx<Dm, PeripheralTxChannel<AnyI2s<'d>>>,
pub(crate) guard: PeripheralGuard,
#[cfg(any(esp32, esp32s2))]
pub(crate) data_format: DataFormat,
}
impl<'d, Dm> TxCreator<'d, Dm>
where
Dm: DriverMode,
{
pub fn build(self, descriptors: &'static mut [DmaDescriptor]) -> I2sTx<'d, Dm> {
let peripheral = self.i2s.peripheral();
I2sTx {
i2s: self.i2s,
tx_channel: self.tx_channel,
tx_chain: DescriptorChain::new(descriptors),
_guard: PeripheralGuard::new(peripheral),
#[cfg(any(esp32, esp32s2))]
data_format: self.data_format,
}
}
pub fn with_bclk(self, bclk: impl PeripheralOutput<'d>) -> Self {
let bclk = bclk.into();
bclk.apply_output_config(&OutputConfig::default());
bclk.set_output_enable(true);
self.i2s.bclk_signal().connect_to(&bclk);
self
}
pub fn with_ws(self, ws: impl PeripheralOutput<'d>) -> Self {
let ws = ws.into();
ws.apply_output_config(&OutputConfig::default());
ws.set_output_enable(true);
self.i2s.ws_signal().connect_to(&ws);
self
}
pub fn with_dout(self, dout: impl PeripheralOutput<'d>) -> Self {
let dout = dout.into();
dout.apply_output_config(&OutputConfig::default());
dout.set_output_enable(true);
self.i2s.dout_signal().connect_to(&dout);
self
}
}
pub struct RxCreator<'d, Dm>
where
Dm: DriverMode,
{
pub i2s: AnyI2s<'d>,
pub rx_channel: ChannelRx<Dm, PeripheralRxChannel<AnyI2s<'d>>>,
pub(crate) guard: PeripheralGuard,
#[cfg(any(esp32, esp32s2))]
pub(crate) data_format: DataFormat,
}
impl<'d, Dm> RxCreator<'d, Dm>
where
Dm: DriverMode,
{
pub fn build(self, descriptors: &'static mut [DmaDescriptor]) -> I2sRx<'d, Dm> {
let peripheral = self.i2s.peripheral();
I2sRx {
i2s: self.i2s,
rx_channel: self.rx_channel,
rx_chain: DescriptorChain::new(descriptors),
_guard: PeripheralGuard::new(peripheral),
#[cfg(any(esp32, esp32s2))]
data_format: self.data_format,
}
}
pub fn with_bclk(self, bclk: impl PeripheralOutput<'d>) -> Self {
let bclk = bclk.into();
bclk.apply_output_config(&OutputConfig::default());
bclk.set_output_enable(true);
self.i2s.bclk_rx_signal().connect_to(&bclk);
self
}
pub fn with_ws(self, ws: impl PeripheralOutput<'d>) -> Self {
let ws = ws.into();
ws.apply_output_config(&OutputConfig::default());
ws.set_output_enable(true);
self.i2s.ws_rx_signal().connect_to(&ws);
self
}
pub fn with_din(self, din: impl PeripheralInput<'d>) -> Self {
let din = din.into();
din.apply_input_config(&InputConfig::default());
din.set_input_enable(true);
self.i2s.din_signal().connect_to(&din);
self
}
}
#[allow(private_bounds)]
pub trait RegBlock: DmaEligible {
fn regs(&self) -> &RegisterBlock;
fn peripheral(&self) -> crate::system::Peripheral;
}
pub trait Signals: RegBlock {
#[cfg(not(esp32))] fn mclk_signal(&self) -> OutputSignal;
fn bclk_signal(&self) -> OutputSignal;
fn ws_signal(&self) -> OutputSignal;
fn dout_signal(&self) -> OutputSignal;
fn bclk_rx_signal(&self) -> OutputSignal;
fn ws_rx_signal(&self) -> OutputSignal;
fn din_signal(&self) -> InputSignal;
}
#[cfg(any(esp32, esp32s2))]
pub trait RegisterAccessPrivate: Signals + RegBlock {
fn enable_listen(&self, interrupts: EnumSet<I2sInterrupt>, enable: bool) {
self.regs().int_ena().modify(|_, w| {
for interrupt in interrupts {
match interrupt {
I2sInterrupt::RxHung => w.rx_hung().bit(enable),
I2sInterrupt::TxHung => w.tx_hung().bit(enable),
};
}
w
});
}
fn interrupts(&self) -> EnumSet<I2sInterrupt> {
let mut res = EnumSet::new();
let ints = self.regs().int_st().read();
if ints.rx_hung().bit() {
res.insert(I2sInterrupt::RxHung);
}
if ints.tx_hung().bit() {
res.insert(I2sInterrupt::TxHung);
}
res
}
fn clear_interrupts(&self, interrupts: EnumSet<I2sInterrupt>) {
self.regs().int_clr().write(|w| {
for interrupt in interrupts {
match interrupt {
I2sInterrupt::RxHung => w.rx_hung().clear_bit_by_one(),
I2sInterrupt::TxHung => w.tx_hung().clear_bit_by_one(),
};
}
w
});
}
fn set_clock(&self, clock_settings: I2sClockDividers) {
self.regs().clkm_conf().modify(|r, w| unsafe {
w.bits(r.bits() | (crate::soc::constants::I2S_DEFAULT_CLK_SRC << 21))
});
#[cfg(esp32)]
self.regs()
.clkm_conf()
.modify(|_, w| w.clka_ena().clear_bit());
self.regs().clkm_conf().modify(|_, w| unsafe {
w.clk_en().set_bit();
w.clkm_div_num().bits(clock_settings.mclk_divider as u8);
w.clkm_div_a().bits(clock_settings.denominator as u8);
w.clkm_div_b().bits(clock_settings.numerator as u8)
});
self.regs().sample_rate_conf().modify(|_, w| unsafe {
w.tx_bck_div_num().bits(clock_settings.bclk_divider as u8);
w.rx_bck_div_num().bits(clock_settings.bclk_divider as u8)
});
}
fn configure(&self, config: &Config) -> Result<(), ConfigError> {
config.validate()?;
self.configure_tx(&config.tx_config, config.data_format)?;
self.configure_rx(&config.rx_config, config.data_format)?;
self.set_clock(config.calculate_clock());
self.regs().sample_rate_conf().modify(|_, w| unsafe {
w.tx_bits_mod().bits(config.data_format.data_bits());
w.rx_bits_mod().bits(config.data_format.data_bits())
});
self.regs().conf().modify(|_, w| {
w.tx_slave_mod().clear_bit();
w.rx_slave_mod().bit(config.signal_loopback);
w.tx_msb_right().set_bit();
w.rx_msb_right().set_bit();
w.tx_right_first().set_bit();
w.rx_right_first().set_bit();
w.tx_mono().clear_bit();
w.rx_mono().clear_bit();
w.sig_loopback().bit(config.signal_loopback)
});
self.regs().fifo_conf().modify(|_, w| w.dscr_en().set_bit());
self.regs().conf1().modify(|_, w| {
w.tx_pcm_bypass().set_bit();
w.rx_pcm_bypass().set_bit()
});
self.regs().pd_conf().modify(|_, w| {
w.fifo_force_pu().set_bit();
w.fifo_force_pd().clear_bit()
});
self.regs().conf2().modify(|_, w| {
w.camera_en().clear_bit();
w.lcd_en().clear_bit()
});
Ok(())
}
fn configure_tx(
&self,
config: &UnitConfig,
data_format: DataFormat,
) -> Result<(), ConfigError> {
config.validate()?;
let chan_mod = match config.channels {
Channels::STEREO | Channels::MONO => 0,
Channels::LEFT => 3,
Channels::RIGHT => 4,
_ => unreachable!(),
};
let fifo_mod = match (data_format.data_bits(), config.channels == Channels::STEREO) {
(8 | 16, true) => 0,
(8 | 16, false) => 1,
(24 | 32, true) => 2,
(24 | 32, false) => 3,
_ => unreachable!(),
};
self.regs().conf().modify(|_, w| {
w.tx_msb_shift().bit(config.msb_shift);
w.tx_short_sync().bit(config.ws_width == WsWidth::Bit)
});
#[cfg(not(esp32))]
self.regs().conf().modify(|_, w| {
w.tx_dma_equal().bit(config.channels != Channels::STEREO);
w.tx_big_endian()
.bit(config.endianness == Endianness::BigEndian)
});
self.regs().fifo_conf().modify(|_, w| unsafe {
w.tx_fifo_mod().bits(fifo_mod);
w.tx_fifo_mod_force_en().set_bit()
});
self.regs()
.conf_sigle_data()
.modify(|_, w| unsafe { w.sigle_data().bits(config.channels.fill.unwrap_or(0)) });
self.regs()
.conf_chan()
.modify(|_, w| unsafe { w.tx_chan_mod().bits(chan_mod) });
Ok(())
}
fn configure_rx(
&self,
config: &UnitConfig,
data_format: DataFormat,
) -> Result<(), ConfigError> {
config.validate()?;
let chan_mod = match config.channels {
Channels::STEREO => 0,
Channels::LEFT | Channels::MONO => 1,
Channels::RIGHT => 2,
_ => unreachable!(),
};
let fifo_mod = match (data_format.data_bits(), config.channels == Channels::STEREO) {
(8 | 16, true) => 0,
(8 | 16, false) => 1,
(24 | 32, true) => 2,
(24 | 32, false) => 3,
_ => unreachable!(),
};
self.regs().conf().modify(|_, w| {
w.rx_msb_shift().bit(config.msb_shift);
w.rx_short_sync().bit(config.ws_width == WsWidth::Bit)
});
#[cfg(not(esp32))]
self.regs().conf().modify(|_, w| {
w.rx_dma_equal().bit(config.channels != Channels::STEREO);
w.rx_big_endian()
.bit(config.endianness == Endianness::BigEndian)
});
self.regs().fifo_conf().modify(|_, w| unsafe {
w.rx_fifo_mod().bits(fifo_mod);
w.rx_fifo_mod_force_en().set_bit()
});
self.regs()
.conf_chan()
.modify(|_, w| unsafe { w.rx_chan_mod().bits(chan_mod) });
Ok(())
}
fn set_master(&self) {
self.regs().conf().modify(|_, w| {
w.rx_slave_mod().clear_bit();
w.tx_slave_mod().clear_bit()
});
}
fn update(&self) {
}
fn reset_tx(&self) {
self.regs().conf().toggle(|w, bit| {
w.tx_reset().bit(bit);
w.tx_fifo_reset().bit(bit)
});
self.regs().lc_conf().toggle(|w, bit| w.out_rst().bit(bit));
self.regs().int_clr().write(|w| {
w.out_done().clear_bit_by_one();
w.out_total_eof().clear_bit_by_one()
});
}
fn tx_start(&self) {
self.regs().conf().modify(|_, w| w.tx_start().set_bit());
while self.regs().state().read().tx_idle().bit_is_set() {
}
}
fn tx_stop(&self) {
self.regs().conf().modify(|_, w| w.tx_start().clear_bit());
}
fn wait_for_tx_done(&self) {
while self.regs().state().read().tx_idle().bit_is_clear() {
}
self.regs().conf().modify(|_, w| w.tx_start().clear_bit());
}
fn reset_rx(&self) {
self.regs().conf().toggle(|w, bit| {
w.rx_reset().bit(bit);
w.rx_fifo_reset().bit(bit)
});
self.regs().lc_conf().toggle(|w, bit| w.in_rst().bit(bit));
self.regs().int_clr().write(|w| {
w.in_done().clear_bit_by_one();
w.in_suc_eof().clear_bit_by_one()
});
}
fn rx_start(&self, len: usize) {
self.regs()
.int_clr()
.write(|w| w.in_suc_eof().clear_bit_by_one());
cfg_if::cfg_if! {
if #[cfg(esp32)] {
let eof_num = len / 4;
} else {
let eof_num = len - 1;
}
}
self.regs()
.rxeof_num()
.modify(|_, w| unsafe { w.rx_eof_num().bits(eof_num as u32) });
self.regs().conf().modify(|_, w| w.rx_start().set_bit());
}
fn wait_for_rx_done(&self) {
while self.regs().int_raw().read().in_suc_eof().bit_is_clear() {
}
self.regs()
.int_clr()
.write(|w| w.in_suc_eof().clear_bit_by_one());
}
}
#[cfg(any(esp32c3, esp32s3, esp32c6, esp32h2))]
pub trait RegisterAccessPrivate: Signals + RegBlock {
fn enable_listen(&self, interrupts: EnumSet<I2sInterrupt>, enable: bool) {
self.regs().int_ena().modify(|_, w| {
for interrupt in interrupts {
match interrupt {
I2sInterrupt::RxHung => w.rx_hung().bit(enable),
I2sInterrupt::TxHung => w.tx_hung().bit(enable),
I2sInterrupt::RxDone => w.rx_done().bit(enable),
I2sInterrupt::TxDone => w.tx_done().bit(enable),
};
}
w
});
}
fn listen(&self, interrupts: impl Into<EnumSet<I2sInterrupt>>) {
self.enable_listen(interrupts.into(), true);
}
fn unlisten(&self, interrupts: impl Into<EnumSet<I2sInterrupt>>) {
self.enable_listen(interrupts.into(), false);
}
fn interrupts(&self) -> EnumSet<I2sInterrupt> {
let mut res = EnumSet::new();
let ints = self.regs().int_st().read();
if ints.rx_hung().bit() {
res.insert(I2sInterrupt::RxHung);
}
if ints.tx_hung().bit() {
res.insert(I2sInterrupt::TxHung);
}
if ints.rx_done().bit() {
res.insert(I2sInterrupt::RxDone);
}
if ints.tx_done().bit() {
res.insert(I2sInterrupt::TxDone);
}
res
}
fn clear_interrupts(&self, interrupts: EnumSet<I2sInterrupt>) {
self.regs().int_clr().write(|w| {
for interrupt in interrupts {
match interrupt {
I2sInterrupt::RxHung => w.rx_hung().clear_bit_by_one(),
I2sInterrupt::TxHung => w.tx_hung().clear_bit_by_one(),
I2sInterrupt::RxDone => w.rx_done().clear_bit_by_one(),
I2sInterrupt::TxDone => w.tx_done().clear_bit_by_one(),
};
}
w
});
}
#[cfg(any(esp32c3, esp32s3))]
fn set_tx_clock(&self, clock_settings: I2sClockDividers) {
let clkm_div = clock_settings.mclk_dividers();
self.regs().tx_clkm_div_conf().modify(|_, w| unsafe {
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);
w.tx_clkm_div_z().bits(clkm_div.z as u16)
});
self.regs().tx_clkm_conf().modify(|_, w| unsafe {
w.clk_en().set_bit();
w.tx_clk_active().set_bit();
w.tx_clk_sel()
.bits(crate::soc::constants::I2S_DEFAULT_CLK_SRC);
w.tx_clkm_div_num().bits(clock_settings.mclk_divider as u8)
});
self.regs().tx_conf1().modify(|_, w| unsafe {
w.tx_bck_div_num()
.bits((clock_settings.bclk_divider - 1) as u8)
});
}
#[cfg(any(esp32c3, esp32s3))]
fn set_rx_clock(&self, clock_settings: I2sClockDividers) {
let clkm_div = clock_settings.mclk_dividers();
self.regs().rx_clkm_div_conf().modify(|_, w| unsafe {
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);
w.rx_clkm_div_z().bits(clkm_div.z as u16)
});
self.regs().rx_clkm_conf().modify(|_, w| unsafe {
w.rx_clk_active().set_bit();
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)
});
self.regs().rx_conf1().modify(|_, w| unsafe {
w.rx_bck_div_num()
.bits((clock_settings.bclk_divider - 1) as u8)
});
}
#[cfg(any(esp32c6, esp32h2))]
fn set_tx_clock(&self, clock_settings: I2sClockDividers) {
use crate::peripherals::PCR;
let clkm_div = clock_settings.mclk_dividers();
PCR::regs().i2s_tx_clkm_div_conf().modify(|_, w| unsafe {
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);
w.i2s_tx_clkm_div_z().bits(clkm_div.z as u16)
});
PCR::regs().i2s_tx_clkm_conf().modify(|_, w| unsafe {
w.i2s_tx_clkm_en().set_bit();
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)
});
#[cfg(not(esp32h2))]
self.regs().tx_conf1().modify(|_, w| unsafe {
w.tx_bck_div_num()
.bits((clock_settings.bclk_divider - 1) as u8)
});
#[cfg(esp32h2)]
self.regs().tx_conf().modify(|_, w| unsafe {
w.tx_bck_div_num()
.bits((clock_settings.bclk_divider - 1) as u8)
});
}
#[cfg(any(esp32c6, esp32h2))]
fn set_rx_clock(&self, clock_settings: I2sClockDividers) {
use crate::peripherals::PCR;
let clkm_div = clock_settings.mclk_dividers();
PCR::regs().i2s_rx_clkm_div_conf().modify(|_, w| unsafe {
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);
w.i2s_rx_clkm_div_z().bits(clkm_div.z as u16)
});
PCR::regs().i2s_rx_clkm_conf().modify(|_, w| unsafe {
w.i2s_rx_clkm_en().set_bit();
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))]
self.regs().rx_conf1().modify(|_, w| unsafe {
w.rx_bck_div_num()
.bits((clock_settings.bclk_divider - 1) as u8)
});
#[cfg(esp32h2)]
self.regs().rx_conf().modify(|_, w| unsafe {
w.rx_bck_div_num()
.bits((clock_settings.bclk_divider - 1) as u8)
});
}
fn configure(&self, config: &Config) -> Result<(), ConfigError> {
config.validate()?;
self.configure_tx(&config.tx_config)?;
self.configure_rx(&config.rx_config)?;
self.regs()
.tx_conf()
.modify(|_, w| w.sig_loopback().bit(config.signal_loopback));
self.regs()
.rx_conf()
.modify(|_, w| w.rx_slave_mod().bit(config.signal_loopback));
Ok(())
}
fn configure_tx(&self, config: &UnitConfig) -> Result<(), ConfigError> {
use bitfield::Bit;
config.validate()?;
let ws_width = config.calculate_ws_width()?;
self.set_tx_clock(config.calculate_clock());
self.regs().tx_conf1().modify(|_, w| unsafe {
#[cfg(not(esp32h2))]
w.tx_msb_shift().bit(config.msb_shift);
#[allow(clippy::useless_conversion)]
w.tx_tdm_ws_width().bits((ws_width - 1).try_into().unwrap());
w.tx_bits_mod().bits(config.data_format.data_bits() - 1);
w.tx_tdm_chan_bits()
.bits(config.data_format.channel_bits() - 1);
w.tx_half_sample_bits()
.bits((config.data_format.data_bits() * config.channels.count) / 2 - 1)
});
self.regs().tx_conf().modify(|_, w| unsafe {
w.tx_mono().clear_bit();
w.tx_mono_fst_vld().set_bit();
w.tx_stop_en().set_bit();
w.tx_chan_equal().bit(config.channels.fill.is_none());
w.tx_tdm_en().set_bit();
w.tx_pdm_en().clear_bit();
w.tx_pcm_bypass().set_bit();
#[cfg(esp32h2)]
w.tx_msb_shift().bit(config.msb_shift);
w.tx_big_endian()
.bit(config.endianness == Endianness::BigEndian);
w.tx_bit_order().bit(config.bit_order == BitOrder::LsbFirst);
w.tx_ws_idle_pol()
.bit(config.ws_polarity == Polarity::ActiveHigh);
w.tx_chan_mod().bits(0)
});
self.regs().tx_tdm_ctrl().modify(|_, w| unsafe {
w.tx_tdm_tot_chan_num().bits(config.channels.count - 1);
w.tx_tdm_chan0_en().bit(config.channels.mask.bit(0));
w.tx_tdm_chan1_en().bit(config.channels.mask.bit(1));
w.tx_tdm_chan2_en().bit(config.channels.mask.bit(2));
w.tx_tdm_chan3_en().bit(config.channels.mask.bit(3));
w.tx_tdm_chan4_en().bit(config.channels.mask.bit(4));
w.tx_tdm_chan5_en().bit(config.channels.mask.bit(5));
w.tx_tdm_chan6_en().bit(config.channels.mask.bit(6));
w.tx_tdm_chan7_en().bit(config.channels.mask.bit(7));
w.tx_tdm_chan8_en().bit(config.channels.mask.bit(8));
w.tx_tdm_chan9_en().bit(config.channels.mask.bit(9));
w.tx_tdm_chan10_en().bit(config.channels.mask.bit(10));
w.tx_tdm_chan11_en().bit(config.channels.mask.bit(11));
w.tx_tdm_chan12_en().bit(config.channels.mask.bit(12));
w.tx_tdm_chan13_en().bit(config.channels.mask.bit(13));
w.tx_tdm_chan14_en().bit(config.channels.mask.bit(14));
w.tx_tdm_chan15_en().bit(config.channels.mask.bit(15));
w.tx_tdm_skip_msk_en().clear_bit()
});
self.regs()
.conf_sigle_data()
.modify(|_, w| unsafe { w.single_data().bits(config.channels.fill.unwrap_or(0)) });
Ok(())
}
fn configure_rx(&self, config: &UnitConfig) -> Result<(), ConfigError> {
use bitfield::Bit;
config.validate()?;
let ws_width = config.calculate_ws_width()?;
self.set_rx_clock(config.calculate_clock());
self.regs().rx_conf1().modify(|_, w| unsafe {
#[cfg(not(esp32h2))]
w.rx_msb_shift().bit(config.msb_shift);
#[allow(clippy::useless_conversion)]
w.rx_tdm_ws_width().bits((ws_width - 1).try_into().unwrap());
w.rx_bits_mod().bits(config.data_format.data_bits() - 1);
w.rx_tdm_chan_bits()
.bits(config.data_format.channel_bits() - 1);
w.rx_half_sample_bits()
.bits((config.data_format.data_bits() * config.channels.count) / 2 - 1)
});
self.regs().rx_conf().modify(|_, w| unsafe {
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();
#[cfg(esp32h2)]
w.rx_msb_shift().bit(config.msb_shift);
w.rx_big_endian()
.bit(config.endianness == Endianness::BigEndian);
w.rx_bit_order().bit(config.bit_order == BitOrder::LsbFirst);
w.rx_ws_idle_pol()
.bit(config.ws_polarity == Polarity::ActiveHigh)
});
self.regs().rx_tdm_ctrl().modify(|_, w| unsafe {
w.rx_tdm_tot_chan_num().bits(config.channels.count - 1);
w.rx_tdm_pdm_chan0_en().bit(config.channels.mask.bit(0));
w.rx_tdm_pdm_chan1_en().bit(config.channels.mask.bit(1));
w.rx_tdm_pdm_chan2_en().bit(config.channels.mask.bit(2));
w.rx_tdm_pdm_chan3_en().bit(config.channels.mask.bit(3));
w.rx_tdm_pdm_chan4_en().bit(config.channels.mask.bit(4));
w.rx_tdm_pdm_chan5_en().bit(config.channels.mask.bit(5));
w.rx_tdm_pdm_chan6_en().bit(config.channels.mask.bit(6));
w.rx_tdm_pdm_chan7_en().bit(config.channels.mask.bit(7));
w.rx_tdm_chan8_en().bit(config.channels.mask.bit(8));
w.rx_tdm_chan9_en().bit(config.channels.mask.bit(9));
w.rx_tdm_chan10_en().bit(config.channels.mask.bit(10));
w.rx_tdm_chan11_en().bit(config.channels.mask.bit(11));
w.rx_tdm_chan12_en().bit(config.channels.mask.bit(12));
w.rx_tdm_chan13_en().bit(config.channels.mask.bit(13));
w.rx_tdm_chan14_en().bit(config.channels.mask.bit(14));
w.rx_tdm_chan15_en().bit(config.channels.mask.bit(15))
});
Ok(())
}
fn set_master(&self) {
self.regs()
.tx_conf()
.modify(|_, w| w.tx_slave_mod().clear_bit());
self.regs()
.rx_conf()
.modify(|_, w| w.rx_slave_mod().clear_bit());
}
fn update(&self) {
self.regs()
.tx_conf()
.toggle(|w, bit| w.tx_update().bit(!bit));
self.regs()
.rx_conf()
.toggle(|w, bit| w.rx_update().bit(!bit));
}
fn reset_tx(&self) {
self.regs().tx_conf().toggle(|w, bit| {
w.tx_reset().bit(bit);
w.tx_fifo_reset().bit(bit)
});
self.regs().int_clr().write(|w| {
w.tx_done().clear_bit_by_one();
w.tx_hung().clear_bit_by_one()
});
}
fn tx_start(&self) {
self.regs().tx_conf().modify(|_, w| w.tx_start().set_bit());
}
fn tx_stop(&self) {
self.regs()
.tx_conf()
.modify(|_, w| w.tx_start().clear_bit());
}
fn wait_for_tx_done(&self) {
while self.regs().state().read().tx_idle().bit_is_clear() {
}
self.regs()
.tx_conf()
.modify(|_, w| w.tx_start().clear_bit());
}
fn reset_rx(&self) {
self.regs()
.rx_conf()
.modify(|_, w| w.rx_start().clear_bit());
self.regs().rx_conf().toggle(|w, bit| {
w.rx_reset().bit(bit);
w.rx_fifo_reset().bit(bit)
});
self.regs().int_clr().write(|w| {
w.rx_done().clear_bit_by_one();
w.rx_hung().clear_bit_by_one()
});
}
fn rx_start(&self, len: usize) {
let len = len - 1;
self.regs()
.rxeof_num()
.write(|w| unsafe { w.rx_eof_num().bits(len as u16) });
self.regs().rx_conf().modify(|_, w| w.rx_start().set_bit());
}
fn wait_for_rx_done(&self) {
while self.regs().int_raw().read().rx_done().bit_is_clear() {
}
self.regs()
.int_clr()
.write(|w| w.rx_done().clear_bit_by_one());
}
}
impl RegBlock for I2S0<'_> {
fn regs(&self) -> &RegisterBlock {
unsafe { &*I2S0::PTR.cast::<RegisterBlock>() }
}
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::I2s0
}
}
impl RegisterAccessPrivate for I2S0<'_> {}
impl Signals for crate::peripherals::I2S0<'_> {
#[cfg(not(esp32))] fn mclk_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(esp32s2)] {
OutputSignal::CLK_I2S
} else if #[cfg(esp32s3)] {
OutputSignal::I2S0_MCLK
} else {
OutputSignal::I2S_MCLK
}
}
}
fn bclk_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2, esp32s3))] {
OutputSignal::I2S0O_BCK
} else {
OutputSignal::I2SO_BCK
}
}
}
fn ws_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2, esp32s3))] {
OutputSignal::I2S0O_WS
} else {
OutputSignal::I2SO_WS
}
}
}
fn dout_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
OutputSignal::I2S0O_DATA_23
} else if #[cfg(esp32s2)] {
OutputSignal::I2S0O_DATA_OUT23
} else if #[cfg(esp32s3)] {
OutputSignal::I2S0O_SD
} else {
OutputSignal::I2SO_SD
}
}
}
fn bclk_rx_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2, esp32s3))] {
OutputSignal::I2S0I_BCK
} else {
OutputSignal::I2SI_BCK
}
}
}
fn ws_rx_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2, esp32s3))] {
OutputSignal::I2S0I_WS
} else {
OutputSignal::I2SI_WS
}
}
}
fn din_signal(&self) -> InputSignal {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::I2S0I_DATA_15
} else if #[cfg(esp32s2)] {
InputSignal::I2S0I_DATA_IN15
} else if #[cfg(esp32s3)] {
InputSignal::I2S0I_SD
} else {
InputSignal::I2SI_SD
}
}
}
}
#[cfg(soc_has_i2s1)]
impl RegBlock for I2S1<'_> {
fn regs(&self) -> &RegisterBlock {
unsafe { &*I2S1::PTR.cast::<RegisterBlock>() }
}
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::I2s1
}
}
#[cfg(soc_has_i2s1)]
impl RegisterAccessPrivate for I2S1<'_> {}
#[cfg(soc_has_i2s1)]
impl Signals for crate::peripherals::I2S1<'_> {
#[cfg(not(esp32))] fn mclk_signal(&self) -> OutputSignal {
OutputSignal::I2S1_MCLK
}
fn bclk_signal(&self) -> OutputSignal {
OutputSignal::I2S1O_BCK
}
fn ws_signal(&self) -> OutputSignal {
OutputSignal::I2S1O_WS
}
fn dout_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
OutputSignal::I2S1O_DATA_23
} else {
OutputSignal::I2S1O_SD
}
}
}
fn bclk_rx_signal(&self) -> OutputSignal {
OutputSignal::I2S1I_BCK
}
fn ws_rx_signal(&self) -> OutputSignal {
OutputSignal::I2S1I_WS
}
fn din_signal(&self) -> InputSignal {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::I2S1I_DATA_15
} else {
InputSignal::I2S1I_SD
}
}
}
}
impl RegBlock for super::AnyI2s<'_> {
fn regs(&self) -> &RegisterBlock {
match &self.0 {
#[cfg(soc_has_i2s0)]
AnyI2sInner::I2s0(i2s) => RegBlock::regs(i2s),
#[cfg(soc_has_i2s1)]
AnyI2sInner::I2s1(i2s) => RegBlock::regs(i2s),
}
}
delegate::delegate! {
to match &self.0 {
#[cfg(soc_has_i2s0)]
AnyI2sInner::I2s0(i2s) => i2s,
#[cfg(soc_has_i2s1)]
AnyI2sInner::I2s1(i2s) => i2s,
} {
fn peripheral(&self) -> crate::system::Peripheral;
}
}
}
impl RegisterAccessPrivate for super::AnyI2s<'_> {}
impl super::AnyI2s<'_> {
delegate::delegate! {
to match &self.0 {
#[cfg(soc_has_i2s0)]
AnyI2sInner::I2s0(i2s) => i2s,
#[cfg(soc_has_i2s1)]
AnyI2sInner::I2s1(i2s) => i2s,
} {
fn bind_peri_interrupt(&self, handler: InterruptHandler);
fn disable_peri_interrupt_on_all_cores(&self);
}
}
pub(super) fn set_interrupt_handler(&self, handler: InterruptHandler) {
self.disable_peri_interrupt_on_all_cores();
self.bind_peri_interrupt(handler);
}
}
impl Signals for super::AnyI2s<'_> {
delegate::delegate! {
to match &self.0 {
#[cfg(soc_has_i2s0)]
AnyI2sInner::I2s0(i2s) => i2s,
#[cfg(soc_has_i2s1)]
AnyI2sInner::I2s1(i2s) => i2s,
} {
#[cfg(not(esp32))]
fn mclk_signal(&self) -> OutputSignal;
fn bclk_signal(&self) -> OutputSignal;
fn ws_signal(&self) -> OutputSignal;
fn dout_signal(&self) -> OutputSignal;
fn bclk_rx_signal(&self) -> OutputSignal;
fn ws_rx_signal(&self) -> OutputSignal;
fn din_signal(&self) -> InputSignal;
}
}
}
pub struct I2sClockDividers {
mclk_divider: u32,
bclk_divider: u32,
denominator: u32,
numerator: u32,
}
#[cfg(any(esp32c3, esp32s3, esp32c6, esp32h2))]
pub struct I2sMclkDividers {
x: u32,
y: u32,
z: u32,
yn1: bool,
}
impl I2sClockDividers {
pub fn new(sample_rate: Rate, channels: u8, data_bits: u8) -> I2sClockDividers {
let mclk_multiple = if data_bits == 24 { 192 } else { 256 };
let sclk = crate::soc::constants::I2S_SCLK;
let rate = sample_rate.as_hz();
let bclk = rate * channels as u32 * data_bits as u32;
let mclk = rate * mclk_multiple;
let bclk_divider = mclk / bclk;
let mut mclk_divider = sclk / mclk;
let mut ma: u32;
let mut mb: u32;
let mut denominator: u32 = 0;
let mut numerator: u32 = 0;
let freq_diff = sclk.abs_diff(mclk * mclk_divider);
if freq_diff != 0 {
let decimal = freq_diff as u64 * 10000 / mclk as u64;
if decimal > 1250000 / 126 {
mclk_divider += 1;
} else {
let mut min: u32 = !0;
for a in 2..=I2S_LL_MCLK_DIVIDER_MAX {
let b = (a as u64) * (freq_diff as u64 * 10000u64 / mclk as u64) + 5000;
ma = ((freq_diff as u64 * 10000u64 * a as u64) / 10000) as u32;
mb = (mclk as u64 * (b / 10000)) as u32;
if ma == mb {
denominator = a as u32;
numerator = (b / 10000) as u32;
break;
}
if mb.abs_diff(ma) < min {
denominator = a as u32;
numerator = (b / 10000) as u32;
min = mb.abs_diff(ma);
}
}
}
}
I2sClockDividers {
mclk_divider,
bclk_divider,
denominator,
numerator,
}
}
#[cfg(any(esp32c3, esp32s3, esp32c6, esp32h2))]
fn mclk_dividers(&self) -> I2sMclkDividers {
let x;
let y;
let z;
let yn1;
if self.denominator == 0 || self.numerator == 0 {
x = 0;
y = 0;
z = 0;
yn1 = true;
} else if self.numerator > self.denominator / 2 {
x = self
.denominator
.overflowing_div(self.denominator.overflowing_sub(self.numerator).0)
.0
.overflowing_sub(1)
.0;
y = self.denominator % (self.denominator.overflowing_sub(self.numerator).0);
z = self.denominator.overflowing_sub(self.numerator).0;
yn1 = true;
} else {
x = self.denominator / self.numerator - 1;
y = self.denominator % self.numerator;
z = self.numerator;
yn1 = false;
}
I2sMclkDividers { x, y, z, yn1 }
}
}
}
pub mod asynch {
use super::{Error, I2sRx, I2sTx, RegisterAccessPrivate};
use crate::{
Async,
dma::{
DmaEligible,
ReadBuffer,
RxCircularState,
TxCircularState,
WriteBuffer,
asynch::{DmaRxDoneChFuture, DmaRxFuture, DmaTxDoneChFuture, DmaTxFuture},
},
};
impl<'d> I2sTx<'d, Async> {
pub async fn write_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
let (ptr, len) = (words.as_ptr(), words.len());
self.i2s.reset_tx();
let future = DmaTxFuture::new(&mut self.tx_channel);
unsafe {
self.tx_chain.fill_for_tx(false, ptr, len)?;
future
.tx
.prepare_transfer_without_start(self.i2s.dma_peripheral(), &self.tx_chain)
.and_then(|_| future.tx.start_transfer())?;
}
self.i2s.tx_start();
future.await?;
Ok(())
}
pub fn write_dma_circular_async<TXBUF: ReadBuffer>(
mut self,
words: TXBUF,
) -> Result<I2sWriteDmaTransferAsync<'d, TXBUF>, Error> {
let (ptr, len) = unsafe { words.read_buffer() };
self.i2s.reset_tx();
unsafe {
self.tx_chain.fill_for_tx(true, ptr, len)?;
self.tx_channel
.prepare_transfer_without_start(self.i2s.dma_peripheral(), &self.tx_chain)
.and_then(|_| self.tx_channel.start_transfer())?;
}
self.i2s.tx_start();
let state = TxCircularState::new(&mut self.tx_chain);
Ok(I2sWriteDmaTransferAsync {
i2s_tx: self,
state,
_buffer: words,
})
}
}
pub struct I2sWriteDmaTransferAsync<'d, BUFFER> {
i2s_tx: I2sTx<'d, Async>,
state: TxCircularState,
_buffer: BUFFER,
}
impl<BUFFER> I2sWriteDmaTransferAsync<'_, BUFFER> {
pub async fn available(&mut self) -> Result<usize, Error> {
loop {
self.state.update(&self.i2s_tx.tx_channel)?;
let res = self.state.available;
if res != 0 {
break Ok(res);
}
DmaTxDoneChFuture::new(&mut self.i2s_tx.tx_channel).await?
}
}
pub async fn push(&mut self, data: &[u8]) -> Result<usize, Error> {
let avail = self.available().await?;
let to_send = usize::min(avail, data.len());
Ok(self.state.push(&data[..to_send])?)
}
pub async fn push_with(
&mut self,
f: impl FnOnce(&mut [u8]) -> usize,
) -> Result<usize, Error> {
let _avail = self.available().await;
Ok(self.state.push_with(f)?)
}
}
impl<'d> I2sRx<'d, Async> {
pub async fn read_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
let (ptr, len) = (words.as_mut_ptr(), words.len());
if !len.is_multiple_of(4) {
return Err(Error::IllegalArgument);
}
self.i2s.reset_rx();
let future = DmaRxFuture::new(&mut self.rx_channel);
unsafe {
self.rx_chain.fill_for_rx(false, ptr, len)?;
future
.rx
.prepare_transfer_without_start(self.i2s.dma_peripheral(), &self.rx_chain)
.and_then(|_| future.rx.start_transfer())?;
}
self.i2s.rx_start(len);
future.await?;
Ok(())
}
pub fn read_dma_circular_async<RXBUF>(
mut self,
mut words: RXBUF,
) -> Result<I2sReadDmaTransferAsync<'d, RXBUF>, Error>
where
RXBUF: WriteBuffer,
{
let (ptr, len) = unsafe { words.write_buffer() };
if !len.is_multiple_of(4) {
return Err(Error::IllegalArgument);
}
self.i2s.reset_rx();
unsafe {
self.rx_chain.fill_for_rx(true, ptr, len)?;
self.rx_channel
.prepare_transfer_without_start(self.i2s.dma_peripheral(), &self.rx_chain)
.and_then(|_| self.rx_channel.start_transfer())?;
}
self.i2s.rx_start(len);
let state = RxCircularState::new(&mut self.rx_chain);
Ok(I2sReadDmaTransferAsync {
i2s_rx: self,
state,
_buffer: words,
})
}
}
pub struct I2sReadDmaTransferAsync<'d, BUFFER> {
i2s_rx: I2sRx<'d, Async>,
state: RxCircularState,
_buffer: BUFFER,
}
impl<BUFFER> I2sReadDmaTransferAsync<'_, BUFFER> {
pub async fn available(&mut self) -> Result<usize, Error> {
loop {
self.state.update()?;
let res = self.state.available;
if res != 0 {
break Ok(res);
}
DmaRxDoneChFuture::new(&mut self.i2s_rx.rx_channel).await?;
}
}
pub async fn pop(&mut self, data: &mut [u8]) -> Result<usize, Error> {
let avail = self.available().await?;
let to_rcv = usize::min(avail, data.len());
Ok(self.state.pop(&mut data[..to_rcv])?)
}
}
}