#![cfg_attr(esp32, doc = "")]
#![cfg_attr(
esp32,
doc = "I2S0 does not support true 8bit parallel output, so if you want to do 8bit"
)]
#![cfg_attr(
esp32,
doc = "you should use I2S1. If you have to use I2S0, it will only output the even"
)]
#![cfg_attr(esp32, doc = "bytes! so [A, B, C, D] will be output as [A, C]!!!!")]
#![cfg_attr(esp32, doc = "")]
#![doc = crate::before_snippet!()]
use core::{
mem::ManuallyDrop,
ops::{Deref, DerefMut},
};
use crate::{
dma::{
asynch::DmaTxFuture,
Channel,
ChannelTx,
DmaChannelFor,
DmaEligible,
DmaError,
DmaPeripheral,
DmaTxBuffer,
PeripheralTxChannel,
Tx,
},
gpio::{
interconnect::{OutputConnection, PeripheralOutput},
OutputSignal,
},
pac::i2s0::RegisterBlock,
peripheral::{Peripheral, PeripheralRef},
peripherals::{I2S0, I2S1},
system::PeripheralGuard,
time::Rate,
Async,
Blocking,
DriverMode,
};
#[doc(hidden)]
pub trait TxPins<'d> {
fn bus_width(&self) -> u8;
fn configure(&mut self, instance: impl Peripheral<P = impl Instance>);
}
pub struct TxSixteenBits<'d> {
pins: [PeripheralRef<'d, OutputConnection>; 16],
}
impl<'d> TxSixteenBits<'d> {
#[allow(clippy::too_many_arguments)]
pub fn new(
pin_0: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_1: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_2: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_3: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_4: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_5: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_6: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_7: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_8: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_9: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_10: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_11: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_12: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_13: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_14: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_15: impl Peripheral<P = impl PeripheralOutput> + 'd,
) -> Self {
crate::into_mapped_ref!(
pin_0, pin_1, pin_2, pin_3, pin_4, pin_5, pin_6, pin_7, pin_8, pin_9, pin_10, pin_11,
pin_12, pin_13, pin_14, pin_15
);
Self {
pins: [
pin_0, pin_1, pin_2, pin_3, pin_4, pin_5, pin_6, pin_7, pin_8, pin_9, pin_10,
pin_11, pin_12, pin_13, pin_14, pin_15,
],
}
}
}
impl<'d> TxPins<'d> for TxSixteenBits<'d> {
fn bus_width(&self) -> u8 {
self.pins.len() as u8
}
fn configure(&mut self, instance: impl Peripheral<P = impl Instance>) {
crate::into_ref!(instance);
let bits = self.bus_width();
for (i, pin) in self.pins.iter_mut().enumerate() {
pin.set_to_push_pull_output();
instance.data_out_signal(i, bits).connect_to(pin);
}
}
}
pub struct TxEightBits<'d> {
pins: [PeripheralRef<'d, OutputConnection>; 8],
}
impl<'d> TxEightBits<'d> {
#[allow(clippy::too_many_arguments)]
pub fn new(
pin_0: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_1: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_2: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_3: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_4: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_5: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_6: impl Peripheral<P = impl PeripheralOutput> + 'd,
pin_7: impl Peripheral<P = impl PeripheralOutput> + 'd,
) -> Self {
crate::into_mapped_ref!(pin_0, pin_1, pin_2, pin_3, pin_4, pin_5, pin_6, pin_7);
Self {
pins: [pin_0, pin_1, pin_2, pin_3, pin_4, pin_5, pin_6, pin_7],
}
}
}
impl<'d> TxPins<'d> for TxEightBits<'d> {
fn bus_width(&self) -> u8 {
self.pins.len() as u8
}
fn configure(&mut self, instance: impl Peripheral<P = impl Instance>) {
crate::into_ref!(instance);
let bits = self.bus_width();
for (i, pin) in self.pins.iter_mut().enumerate() {
pin.set_to_push_pull_output();
instance.data_out_signal(i, bits).connect_to(pin);
}
}
}
pub struct I2sParallel<'d, Dm>
where
Dm: DriverMode,
{
instance: PeripheralRef<'d, AnyI2s>,
tx_channel: ChannelTx<'d, Dm, PeripheralTxChannel<AnyI2s>>,
_guard: PeripheralGuard,
}
impl<'d> I2sParallel<'d, Blocking> {
pub fn new<CH>(
i2s: impl Peripheral<P = impl Instance> + 'd,
channel: impl Peripheral<P = CH> + 'd,
frequency: Rate,
mut pins: impl TxPins<'d>,
clock_pin: impl Peripheral<P = impl PeripheralOutput> + 'd,
) -> Self
where
CH: DmaChannelFor<AnyI2s>,
{
crate::into_mapped_ref!(i2s);
crate::into_mapped_ref!(clock_pin);
let channel = Channel::new(channel.map(|ch| ch.degrade()));
channel.runtime_ensure_compatible(&i2s);
let guard = PeripheralGuard::new(i2s.peripheral());
i2s.setup(frequency, pins.bus_width());
clock_pin.set_to_push_pull_output();
i2s.ws_signal().connect_to(clock_pin);
pins.configure(i2s.reborrow());
Self {
instance: i2s,
tx_channel: channel.tx,
_guard: guard,
}
}
pub fn into_async(self) -> I2sParallel<'d, Async> {
I2sParallel {
instance: self.instance,
tx_channel: self.tx_channel.into_async(),
_guard: self._guard,
}
}
}
impl<'d> I2sParallel<'d, Async> {
pub fn into_blocking(self) -> I2sParallel<'d, Blocking> {
I2sParallel {
instance: self.instance,
tx_channel: self.tx_channel.into_blocking(),
_guard: self._guard,
}
}
}
impl<'d, Dm> I2sParallel<'d, Dm>
where
Dm: DriverMode,
{
pub fn send<BUF: DmaTxBuffer>(
mut self,
mut data: BUF,
) -> Result<I2sParallelTransfer<'d, BUF, Dm>, (DmaError, Self, BUF)> {
self.instance.tx_reset();
self.instance.tx_fifo_reset();
let result = unsafe {
self.tx_channel
.prepare_transfer(self.instance.dma_peripheral(), &mut data)
}
.and_then(|_| self.tx_channel.start_transfer());
if let Err(err) = result {
return Err((err, self, data));
}
self.instance.tx_start();
Ok(I2sParallelTransfer {
i2s: ManuallyDrop::new(self),
buf_view: ManuallyDrop::new(data.into_view()),
})
}
}
pub struct I2sParallelTransfer<'d, BUF, Dm>
where
BUF: DmaTxBuffer,
Dm: DriverMode,
{
i2s: ManuallyDrop<I2sParallel<'d, Dm>>,
buf_view: ManuallyDrop<BUF::View>,
}
impl<'d, BUF, Dm> I2sParallelTransfer<'d, BUF, Dm>
where
BUF: DmaTxBuffer,
Dm: DriverMode,
{
pub fn is_done(&self) -> bool {
self.i2s.instance.is_tx_done()
}
pub fn wait(mut self) -> (I2sParallel<'d, Dm>, BUF) {
self.i2s.instance.tx_wait_done();
let i2s = unsafe { ManuallyDrop::take(&mut self.i2s) };
let view = unsafe { ManuallyDrop::take(&mut self.buf_view) };
core::mem::forget(self);
(i2s, BUF::from_view(view))
}
fn stop_peripherals(&mut self) {
self.i2s.instance.tx_stop();
self.i2s.tx_channel.stop_transfer();
}
}
impl<BUF> I2sParallelTransfer<'_, BUF, Async>
where
BUF: DmaTxBuffer,
{
pub async fn wait_for_done(&mut self) -> Result<(), DmaError> {
DmaTxFuture::new(&mut self.i2s.tx_channel).await
}
}
impl<BUF, Dm> Deref for I2sParallelTransfer<'_, BUF, Dm>
where
BUF: DmaTxBuffer,
Dm: DriverMode,
{
type Target = BUF::View;
fn deref(&self) -> &Self::Target {
&self.buf_view
}
}
impl<BUF, Dm> DerefMut for I2sParallelTransfer<'_, BUF, Dm>
where
BUF: DmaTxBuffer,
Dm: DriverMode,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buf_view
}
}
impl<BUF, Dm> Drop for I2sParallelTransfer<'_, BUF, Dm>
where
BUF: DmaTxBuffer,
Dm: DriverMode,
{
fn drop(&mut self) {
self.stop_peripherals();
let view = unsafe {
ManuallyDrop::drop(&mut self.i2s);
ManuallyDrop::take(&mut self.buf_view)
};
let _ = BUF::from_view(view);
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct I2sClockDividers {
pub mclk_divider: u32,
pub bclk_divider: u32,
pub denominator: u32,
pub numerator: u32,
}
fn calculate_clock(sample_rate: Rate, data_bits: u8) -> I2sClockDividers {
let sclk = crate::soc::constants::I2S_SCLK; let rate = sample_rate.as_hz();
let mclk = rate * 2;
let bclk_divider: u32 = if data_bits == 8 { 2 } else { 1 };
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..=crate::i2s::master::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 as u32;
min = mb.abs_diff(ma);
}
}
}
}
I2sClockDividers {
mclk_divider,
bclk_divider,
denominator,
numerator,
}
}
#[doc(hidden)]
pub trait Instance: Peripheral<P = Self> + DmaEligible + Into<AnyI2s> + 'static {
fn regs(&self) -> &RegisterBlock;
fn peripheral(&self) -> crate::system::Peripheral;
fn ws_signal(&self) -> OutputSignal;
fn data_out_signal(&self, i: usize, bits: u8) -> OutputSignal;
fn rx_reset(&self) {
self.regs().conf().modify(|_, w| w.rx_reset().set_bit());
self.regs().conf().modify(|_, w| w.rx_reset().clear_bit());
}
fn rx_dma_reset(&self) {
self.regs().lc_conf().modify(|_, w| w.in_rst().set_bit());
self.regs().lc_conf().modify(|_, w| w.in_rst().clear_bit());
}
fn rx_fifo_reset(&self) {
self.regs()
.conf()
.modify(|_, w| w.rx_fifo_reset().set_bit());
self.regs()
.conf()
.modify(|_, w| w.rx_fifo_reset().clear_bit());
}
fn tx_reset(&self) {
self.regs().conf().modify(|_, w| w.tx_reset().set_bit());
xtensa_lx::timer::delay(100);
self.regs().conf().modify(|_, w| w.tx_reset().clear_bit());
}
fn tx_dma_reset(&self) {
self.regs().lc_conf().modify(|_, w| w.out_rst().set_bit());
self.regs().lc_conf().modify(|_, w| w.out_rst().clear_bit());
}
fn tx_fifo_reset(&self) {
self.regs()
.conf()
.modify(|_, w| w.tx_fifo_reset().set_bit());
self.regs()
.conf()
.modify(|_, w| w.tx_fifo_reset().clear_bit());
}
fn tx_clear_interrupts(&self) {
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) {
while self.regs().int_raw().read().tx_rempty().bit_is_clear() {
}
xtensa_lx::timer::delay(1);
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 is_tx_done(&self) -> bool {
self.regs().state().read().tx_idle().bit_is_set()
}
fn tx_wait_done(&self) {
while self.regs().state().read().tx_idle().bit_is_clear() {
}
self.regs().conf().modify(|_, w| w.tx_start().clear_bit());
self.regs().int_clr().write(|w| {
w.out_done().clear_bit_by_one();
w.out_total_eof().clear_bit_by_one()
});
}
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)
});
self.regs().clkm_conf().modify(|_, w| unsafe {
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 setup(&self, frequency: Rate, bits: u8) {
self.set_clock(calculate_clock(frequency, bits));
self.rx_reset();
self.tx_reset();
self.rx_fifo_reset();
self.tx_fifo_reset();
self.rx_dma_reset();
self.tx_dma_reset();
self.regs().conf2().write(|w| {
w.lcd_tx_wrx2_en().bit(bits == 8);
w.lcd_en().set_bit()
});
self.regs().sample_rate_conf().modify(|_, w| unsafe {
w.rx_bits_mod().bits(bits);
w.tx_bits_mod().bits(bits)
});
self.regs().fifo_conf().write(|w| unsafe {
w.rx_fifo_mod_force_en().set_bit();
w.tx_fifo_mod_force_en().set_bit();
w.rx_fifo_mod().bits(1);
w.tx_fifo_mod().bits(1);
w.rx_data_num().bits(32);
w.tx_data_num().bits(32);
w.dscr_en().set_bit()
});
self.regs().conf1().write(|w| {
w.tx_stop_en().set_bit();
w.rx_pcm_bypass().set_bit();
w.tx_pcm_bypass().set_bit()
});
self.regs().conf_chan().write(|w| unsafe {
w.rx_chan_mod().bits(1);
w.tx_chan_mod().bits(1)
});
self.regs().conf().modify(|_, w| {
w.rx_mono().set_bit();
w.tx_mono().set_bit();
w.rx_right_first().set_bit();
w.tx_right_first().set_bit()
});
self.regs().timing().reset();
self.regs().pd_conf().modify(|_, w| {
w.fifo_force_pu().set_bit();
w.fifo_force_pd().clear_bit()
});
}
}
impl Instance for I2S0 {
fn regs(&self) -> &RegisterBlock {
unsafe { &*I2S0::PTR.cast::<RegisterBlock>() }
}
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::I2s0
}
fn ws_signal(&self) -> OutputSignal {
OutputSignal::I2S0O_WS
}
fn data_out_signal(&self, i: usize, bits: u8) -> OutputSignal {
assert!(
bits == 8 || bits == 16,
"Number of bits must be 8 or 16, got {}",
bits
);
match i + 8 {
0 => OutputSignal::I2S0O_DATA_0,
1 => OutputSignal::I2S0O_DATA_1,
2 => OutputSignal::I2S0O_DATA_2,
3 => OutputSignal::I2S0O_DATA_3,
4 => OutputSignal::I2S0O_DATA_4,
5 => OutputSignal::I2S0O_DATA_5,
6 => OutputSignal::I2S0O_DATA_6,
7 => OutputSignal::I2S0O_DATA_7,
8 => OutputSignal::I2S0O_DATA_8,
9 => OutputSignal::I2S0O_DATA_9,
10 => OutputSignal::I2S0O_DATA_10,
11 => OutputSignal::I2S0O_DATA_11,
12 => OutputSignal::I2S0O_DATA_12,
13 => OutputSignal::I2S0O_DATA_13,
14 => OutputSignal::I2S0O_DATA_14,
15 => OutputSignal::I2S0O_DATA_15,
16 => OutputSignal::I2S0O_DATA_16,
17 => OutputSignal::I2S0O_DATA_17,
18 => OutputSignal::I2S0O_DATA_18,
19 => OutputSignal::I2S0O_DATA_19,
20 => OutputSignal::I2S0O_DATA_20,
21 => OutputSignal::I2S0O_DATA_21,
22 => OutputSignal::I2S0O_DATA_22,
23 => OutputSignal::I2S0O_DATA_23,
other => panic!("Invalid I2S0 Dout pin {}", other),
}
}
}
impl Instance for I2S1 {
fn regs(&self) -> &RegisterBlock {
unsafe { &*I2S1::PTR.cast::<RegisterBlock>() }
}
fn peripheral(&self) -> crate::system::Peripheral {
crate::system::Peripheral::I2s1
}
fn ws_signal(&self) -> OutputSignal {
OutputSignal::I2S1O_WS
}
fn data_out_signal(&self, i: usize, bits: u8) -> OutputSignal {
assert!(
bits == 8 || bits == 16,
"Number of bits must be 8 or 16, got {}",
bits
);
let pin_offset = if bits == 16 { 8 } else { 0 };
match i + pin_offset {
0 => OutputSignal::I2S1O_DATA_0,
1 => OutputSignal::I2S1O_DATA_1,
2 => OutputSignal::I2S1O_DATA_2,
3 => OutputSignal::I2S1O_DATA_3,
4 => OutputSignal::I2S1O_DATA_4,
5 => OutputSignal::I2S1O_DATA_5,
6 => OutputSignal::I2S1O_DATA_6,
7 => OutputSignal::I2S1O_DATA_7,
8 => OutputSignal::I2S1O_DATA_8,
9 => OutputSignal::I2S1O_DATA_9,
10 => OutputSignal::I2S1O_DATA_10,
11 => OutputSignal::I2S1O_DATA_11,
12 => OutputSignal::I2S1O_DATA_12,
13 => OutputSignal::I2S1O_DATA_13,
14 => OutputSignal::I2S1O_DATA_14,
15 => OutputSignal::I2S1O_DATA_15,
16 => OutputSignal::I2S1O_DATA_16,
17 => OutputSignal::I2S1O_DATA_17,
18 => OutputSignal::I2S1O_DATA_18,
19 => OutputSignal::I2S1O_DATA_19,
20 => OutputSignal::I2S1O_DATA_20,
21 => OutputSignal::I2S1O_DATA_21,
22 => OutputSignal::I2S1O_DATA_22,
23 => OutputSignal::I2S1O_DATA_23,
other => panic!("Invalid I2S1 Dout pin {}", other),
}
}
}
crate::any_peripheral! {
pub peripheral AnyI2s {
I2s0(crate::peripherals::I2S0),
I2s1(crate::peripherals::I2S1),
}
}
impl DmaEligible for AnyI2s {
type Dma = crate::dma::AnyI2sDmaChannel;
fn dma_peripheral(&self) -> DmaPeripheral {
match &self.0 {
AnyI2sInner::I2s0(_) => DmaPeripheral::I2s0,
AnyI2sInner::I2s1(_) => DmaPeripheral::I2s1,
}
}
}
impl Instance for AnyI2s {
delegate::delegate! {
to match &self.0 {
AnyI2sInner::I2s0(i2s) => i2s,
AnyI2sInner::I2s1(i2s) => i2s,
} {
fn regs(&self) -> &RegisterBlock;
fn peripheral(&self) -> crate::system::Peripheral;
fn ws_signal(&self) -> OutputSignal;
fn data_out_signal(&self, i: usize, bits: u8) -> OutputSignal ;
}
}
}