diff --git a/esp-hal-common/src/dma/gdma.rs b/esp-hal-common/src/dma/gdma.rs index 191ad689be4..a33380078a9 100644 --- a/esp-hal-common/src/dma/gdma.rs +++ b/esp-hal-common/src/dma/gdma.rs @@ -119,6 +119,38 @@ macro_rules! impl_channel { ret } + fn last_out_dscr_address() -> usize { + let dma = unsafe { &*crate::pac::DMA::PTR }; + dma.[].read().out_eof_des_addr().bits() as usize + } + + fn is_out_eof_interrupt_set() -> bool { + let dma = unsafe { &*crate::pac::DMA::PTR }; + + #[cfg(not(esp32s3))] + let ret = dma.[].read().out_eof().bit(); + #[cfg(esp32s3)] + let ret = dma.[].read().out_eof().bit(); + + ret + } + + fn reset_out_eof_interrupt() { + let dma = unsafe { &*crate::pac::DMA::PTR }; + + #[cfg(not(esp32s3))] + dma.[].write(|w| { + w.out_eof() + .set_bit() + }); + + #[cfg(esp32s3)] + dma.[].write(|w| { + w.out_eof() + .set_bit() + }); + } + fn set_in_burstmode(burst_mode: bool) { let dma = unsafe { &*crate::pac::DMA::PTR }; @@ -225,6 +257,11 @@ macro_rules! impl_channel { ret } + + fn last_in_dscr_address() -> usize { + let dma = unsafe { &*crate::pac::DMA::PTR }; + dma.[].read().inlink_dscr_bf0().bits() as usize + } } pub struct [] {} @@ -252,6 +289,12 @@ macro_rules! impl_channel { descriptors: tx_descriptors, burst_mode, tx_impl: tx_impl, + write_offset: 0, + write_descr_ptr: core::ptr::null(), + available: 0, + last_seen_handled_descriptor_ptr: core::ptr::null(), + buffer_start: core::ptr::null(), + buffer_len: 0, _phantom: PhantomData::default(), }; @@ -262,6 +305,12 @@ macro_rules! impl_channel { descriptors: rx_descriptors, burst_mode, rx_impl: rx_impl, + read_offset: 0, + read_descr_ptr: core::ptr::null(), + available: 0, + last_seen_handled_descriptor_ptr: core::ptr::null(), + buffer_start: core::ptr::null(), + buffer_len: 0, _phantom: PhantomData::default(), }; @@ -279,6 +328,9 @@ macro_rules! impl_channel { // with GDMA every channel can be used for any peripheral impl SpiPeripheral for [] {} impl Spi2Peripheral for [] {} + impl I2sPeripheral for [] {} + impl I2s0Peripheral for [] {} + impl I2s1Peripheral for [] {} } }; } diff --git a/esp-hal-common/src/dma/mod.rs b/esp-hal-common/src/dma/mod.rs index a08267798be..0371f25f110 100644 --- a/esp-hal-common/src/dma/mod.rs +++ b/esp-hal-common/src/dma/mod.rs @@ -10,6 +10,8 @@ pub mod gdma; #[cfg(any(esp32, esp32s2))] pub mod pdma; +const CHUNK_SIZE: usize = 4092; + /// DMA Errors #[derive(Debug, Clone, Copy)] pub enum DmaError { @@ -17,6 +19,9 @@ pub enum DmaError { OutOfDescriptors, InvalidDescriptorSize, DescriptorError, + Overflow, + Exhausted, + BufferTooSmall, } /// DMA Priorities @@ -36,7 +41,7 @@ pub enum DmaPriority { } /// DMA Priorities -/// The values need to match the TRM +/// The values don't correspond to anything #[cfg(any(esp32, esp32s2))] #[derive(Clone, Copy)] pub enum DmaPriority { @@ -66,12 +71,15 @@ pub enum DmaPeripheral { } /// DMA capable peripherals -/// The values need to match the TRM +/// The values don't correspond to anything #[cfg(any(esp32, esp32s2))] #[derive(Clone, Copy)] pub enum DmaPeripheral { Spi2 = 0, Spi3 = 1, + I2s0 = 2, + #[cfg(esp32)] + I2s1 = 3, } /// DMA capable peripherals @@ -113,7 +121,6 @@ trait DmaLinkedListDw0 { fn set_err_eof(&mut self, err_eof: bool); #[cfg(not(esp32))] fn get_err_eof(&mut self) -> bool; - #[cfg(not(esp32))] fn set_suc_eof(&mut self, suc_eof: bool); fn get_suc_eof(&mut self) -> bool; fn set_owner(&mut self, owner: Owner); @@ -158,7 +165,6 @@ impl DmaLinkedListDw0 for &mut u32 { ((**self & (mask << bit_s)) >> bit_s) != 0 } - #[cfg(not(esp32))] fn set_suc_eof(&mut self, suc_eof: bool) { let mask = 0b1; let bit_s = 30; @@ -200,6 +206,15 @@ pub(crate) mod private { #[cfg(any(esp32, esp32s2, esp32s3))] pub trait Spi3Peripheral: SpiPeripheral + PeripheralMarker {} + /// Marks channels as useable for I2S + pub trait I2sPeripheral: PeripheralMarker {} + + /// Marks channels as useable for I2S0 + pub trait I2s0Peripheral: I2sPeripheral + PeripheralMarker {} + + /// Marks channels as useable for I2S1 + pub trait I2s1Peripheral: I2sPeripheral + PeripheralMarker {} + /// DMA Rx /// /// The functions here are not meant to be used outside the HAL and will be @@ -211,12 +226,17 @@ pub(crate) mod private { fn prepare_transfer( &mut self, + circular: bool, peri: DmaPeripheral, data: *mut u8, len: usize, ) -> Result<(), DmaError>; fn is_done(&mut self) -> bool; + + fn available(&mut self) -> usize; + + fn pop(&mut self, data: &mut [u8]) -> Result; } pub trait RxChannel @@ -231,6 +251,7 @@ pub(crate) mod private { fn prepare_transfer( &mut self, descriptors: &mut [u32], + circular: bool, peri: DmaPeripheral, data: *mut u8, len: usize, @@ -244,16 +265,14 @@ pub(crate) mod private { let mut processed = 0; let mut descr = 0; loop { - let chunk_size = usize::min(4092, len - processed); + let chunk_size = usize::min(CHUNK_SIZE, len - processed); let last = processed + chunk_size >= len; descriptors[descr + 1] = data as u32 + processed as u32; let mut dw0 = &mut descriptors[descr]; - #[cfg(not(esp32))] - dw0.set_suc_eof(last); - + dw0.set_suc_eof(true); dw0.set_owner(Owner::Dma); dw0.set_size(chunk_size as u16); // align to 32 bits? dw0.set_length(0); // actual size of the data!? @@ -262,7 +281,11 @@ pub(crate) mod private { descriptors[descr + 2] = (&descriptors[descr + 3]) as *const _ as *const () as u32; } else { - descriptors[descr + 2] = 0; + descriptors[descr + 2] = if circular { + descriptors.as_ptr() as *const () as u32 + } else { + 0 + }; } processed += chunk_size; @@ -289,6 +312,10 @@ pub(crate) mod private { fn is_done(&mut self) -> bool { R::is_in_done() } + + fn last_in_dscr_address(&self) -> usize { + R::last_in_dscr_address() + } } pub struct ChannelRx<'a, T, R> @@ -299,6 +326,12 @@ pub(crate) mod private { pub descriptors: &'a mut [u32], pub burst_mode: bool, pub rx_impl: T, + pub read_offset: usize, + pub read_descr_ptr: *const u32, + pub available: usize, + pub last_seen_handled_descriptor_ptr: *const u32, + pub buffer_start: *const u8, + pub buffer_len: usize, pub _phantom: PhantomData, } @@ -313,6 +346,7 @@ pub(crate) mod private { fn prepare_transfer( &mut self, + circular: bool, peri: DmaPeripheral, data: *mut u8, len: usize, @@ -321,7 +355,7 @@ pub(crate) mod private { return Err(DmaError::InvalidDescriptorSize); } - if self.descriptors.len() / 3 < len / 4092 { + if self.descriptors.len() / 3 < len / CHUNK_SIZE { return Err(DmaError::OutOfDescriptors); } @@ -329,8 +363,19 @@ pub(crate) mod private { return Err(DmaError::InvalidAlignment); } + if circular && len < CHUNK_SIZE * 2 { + return Err(DmaError::BufferTooSmall); + } + + self.read_offset = 0; + self.available = 0; + self.read_descr_ptr = self.descriptors.as_ptr() as *const u32; + self.last_seen_handled_descriptor_ptr = core::ptr::null(); + self.buffer_start = data; + self.buffer_len = len; + self.rx_impl - .prepare_transfer(self.descriptors, peri, data, len)?; + .prepare_transfer(self.descriptors, circular, peri, data, len)?; Ok(()) } @@ -341,6 +386,118 @@ pub(crate) mod private { fn init_channel(&mut self) { R::init_channel(); } + + fn available(&mut self) -> usize { + let last_dscr = self.rx_impl.last_in_dscr_address() as *const u32; + if !last_dscr.is_null() + && !self.last_seen_handled_descriptor_ptr.is_null() + && self.last_seen_handled_descriptor_ptr != last_dscr + { + let descr_address = last_dscr; + + if descr_address >= self.last_seen_handled_descriptor_ptr { + let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; + + unsafe { + while ptr < descr_address as *const u32 { + let mut dw0 = &mut ptr.read_volatile(); + self.available += dw0.get_length() as usize; + ptr = ptr.offset(3); + } + } + } else { + let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; + + unsafe { + loop { + if ptr.offset(2).read_volatile() == 0 { + break; + } + + let mut dw0 = &mut ptr.read_volatile(); + self.available += dw0.get_length() as usize; + ptr = ptr.offset(3); + } + } + } + + if self.available >= self.buffer_len { + unsafe { + let segment_len = + (&mut self.read_descr_ptr.read_volatile()).get_length() as usize; + self.available -= segment_len; + self.read_offset = (self.read_offset + segment_len) % self.buffer_len; + let next_descriptor = + self.read_descr_ptr.offset(2).read_volatile() as *const u32; + self.read_descr_ptr = if next_descriptor.is_null() { + self.descriptors.as_ptr() as *const u32 + } else { + next_descriptor + } + } + } + + self.last_seen_handled_descriptor_ptr = descr_address; + } else { + self.last_seen_handled_descriptor_ptr = last_dscr; + } + + self.available + } + + fn pop(&mut self, data: &mut [u8]) -> Result { + let avail = self.available(); + + if avail < data.len() { + return Err(super::DmaError::Exhausted); + } + + unsafe { + let dst = data.as_mut_ptr(); + let src = self.buffer_start.offset(self.read_offset as isize) as *const u8; + let count = usize::min(data.len(), self.buffer_len - self.read_offset); + core::ptr::copy_nonoverlapping(src, dst, count); + } + + if self.read_offset + data.len() >= self.buffer_len { + let remainder = (self.read_offset + data.len()) % self.buffer_len; + let src = self.buffer_start as *const u8; + unsafe { + let dst = data.as_mut_ptr().offset((data.len() - remainder) as isize); + core::ptr::copy_nonoverlapping(src, dst, remainder); + } + } + + let mut forward = data.len(); + loop { + unsafe { + let next_descriptor = + self.read_descr_ptr.offset(2).read_volatile() as *const u32; + let segment_len = + (&mut self.read_descr_ptr.read_volatile()).get_length() as usize; + self.read_descr_ptr = if next_descriptor.is_null() { + self.descriptors.as_ptr() as *const u32 + } else { + next_descriptor + }; + + if forward <= segment_len { + break; + } + + forward -= segment_len; + + if forward == 0 { + break; + } + } + } + + self.read_offset = (self.read_offset + data.len()) % self.buffer_len; + self.available -= data.len(); + + Ok(data.len()) + } } /// DMA Tx @@ -355,11 +512,16 @@ pub(crate) mod private { fn prepare_transfer( &mut self, peri: DmaPeripheral, + circular: bool, data: *const u8, len: usize, ) -> Result<(), DmaError>; fn is_done(&mut self) -> bool; + + fn available(&mut self) -> usize; + + fn push(&mut self, data: &[u8]) -> Result; } pub trait TxChannel @@ -374,6 +536,7 @@ pub(crate) mod private { fn prepare_transfer( &mut self, descriptors: &mut [u32], + circular: bool, peri: DmaPeripheral, data: *const u8, len: usize, @@ -387,16 +550,14 @@ pub(crate) mod private { let mut processed = 0; let mut descr = 0; loop { - let chunk_size = usize::min(4092, len - processed); + let chunk_size = usize::min(CHUNK_SIZE, len - processed); let last = processed + chunk_size >= len; descriptors[descr + 1] = data as u32 + processed as u32; let mut dw0 = &mut descriptors[descr]; - #[cfg(not(esp32))] - dw0.set_suc_eof(last); - + dw0.set_suc_eof(true); dw0.set_owner(Owner::Dma); dw0.set_size(chunk_size as u16); // align to 32 bits? dw0.set_length(chunk_size as u16); // actual size of the data!? @@ -405,7 +566,11 @@ pub(crate) mod private { descriptors[descr + 2] = (&descriptors[descr + 3]) as *const _ as *const () as u32; } else { - descriptors[descr + 2] = 0; + if !circular { + descriptors[descr + 2] = 0; + } else { + descriptors[descr + 2] = descriptors.as_ptr() as u32; + } } processed += chunk_size; @@ -432,6 +597,18 @@ pub(crate) mod private { fn is_done(&mut self) -> bool { R::is_out_done() } + + fn descriptors_handled(&self) -> bool { + R::is_out_eof_interrupt_set() + } + + fn reset_descriptors_handled(&self) { + R::reset_out_eof_interrupt(); + } + + fn last_out_dscr_address(&self) -> usize { + R::last_out_dscr_address() + } } pub struct ChannelTx<'a, T, R> @@ -443,6 +620,12 @@ pub(crate) mod private { #[allow(unused)] pub burst_mode: bool, pub tx_impl: T, + pub write_offset: usize, + pub write_descr_ptr: *const u32, + pub available: usize, + pub last_seen_handled_descriptor_ptr: *const u32, + pub buffer_start: *const u8, + pub buffer_len: usize, pub _phantom: PhantomData, } @@ -462,6 +645,7 @@ pub(crate) mod private { fn prepare_transfer( &mut self, peri: DmaPeripheral, + circular: bool, data: *const u8, len: usize, ) -> Result<(), DmaError> { @@ -469,12 +653,23 @@ pub(crate) mod private { return Err(DmaError::InvalidDescriptorSize); } - if self.descriptors.len() / 3 < len / 4092 { + if self.descriptors.len() / 3 < len / CHUNK_SIZE { return Err(DmaError::OutOfDescriptors); } + if circular && len < CHUNK_SIZE * 2 { + return Err(DmaError::BufferTooSmall); + } + + self.write_offset = 0; + self.available = 0; + self.write_descr_ptr = self.descriptors.as_ptr() as *const u32; + self.last_seen_handled_descriptor_ptr = self.descriptors.as_ptr() as *const u32; + self.buffer_start = data; + self.buffer_len = len; + self.tx_impl - .prepare_transfer(self.descriptors, peri, data, len)?; + .prepare_transfer(self.descriptors, circular, peri, data, len)?; Ok(()) } @@ -482,6 +677,113 @@ pub(crate) mod private { fn is_done(&mut self) -> bool { self.tx_impl.is_done() } + + fn available(&mut self) -> usize { + if self.tx_impl.descriptors_handled() { + self.tx_impl.reset_descriptors_handled(); + let descr_address = self.tx_impl.last_out_dscr_address() as *const u32; + + if descr_address >= self.last_seen_handled_descriptor_ptr { + let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; + + unsafe { + while ptr < descr_address as *const u32 { + let mut dw0 = &mut ptr.read_volatile(); + self.available += dw0.get_length() as usize; + ptr = ptr.offset(3); + } + } + } else { + let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; + + unsafe { + loop { + if ptr.offset(2).read_volatile() == 0 { + break; + } + + let mut dw0 = &mut ptr.read_volatile(); + self.available += dw0.get_length() as usize; + ptr = ptr.offset(3); + } + } + } + + if self.available >= self.buffer_len { + unsafe { + let segment_len = + (&mut self.write_descr_ptr.read_volatile()).get_length() as usize; + self.available -= segment_len; + self.write_offset = (self.write_offset + segment_len) % self.buffer_len; + let next_descriptor = + self.write_descr_ptr.offset(2).read_volatile() as *const u32; + self.write_descr_ptr = if next_descriptor.is_null() { + self.descriptors.as_ptr() as *const u32 + } else { + next_descriptor + } + } + } + + self.last_seen_handled_descriptor_ptr = descr_address; + } + + self.available + } + + fn push(&mut self, data: &[u8]) -> Result { + let avail = self.available(); + + if avail < data.len() { + return Err(super::DmaError::Overflow); + } + + unsafe { + let src = data.as_ptr(); + let dst = self.buffer_start.offset(self.write_offset as isize) as *mut u8; + let count = usize::min(data.len(), self.buffer_len - self.write_offset); + core::ptr::copy_nonoverlapping(src, dst, count); + } + + if self.write_offset + data.len() >= self.buffer_len { + let remainder = (self.write_offset + data.len()) % self.buffer_len; + let dst = self.buffer_start as *mut u8; + unsafe { + let src = data.as_ptr().offset((data.len() - remainder) as isize); + core::ptr::copy_nonoverlapping(src, dst, remainder); + } + } + + let mut forward = data.len(); + loop { + unsafe { + let next_descriptor = + self.write_descr_ptr.offset(2).read_volatile() as *const u32; + let segment_len = + (&mut self.write_descr_ptr.read_volatile()).get_length() as usize; + self.write_descr_ptr = if next_descriptor.is_null() { + self.descriptors.as_ptr() as *const u32 + } else { + next_descriptor + }; + + if forward <= segment_len { + break; + } + + forward -= segment_len; + + if forward == 0 { + break; + } + } + } + + self.write_offset = (self.write_offset + data.len()) % self.buffer_len; + self.available -= data.len(); + + Ok(data.len()) + } } pub trait RegisterAccess { @@ -495,6 +797,10 @@ pub(crate) mod private { fn set_out_peripheral(peripheral: u8); fn start_out(); fn is_out_done() -> bool; + fn is_out_eof_interrupt_set() -> bool; + fn reset_out_eof_interrupt(); + fn last_out_dscr_address() -> usize; + fn set_in_burstmode(burst_mode: bool); fn set_in_priority(priority: DmaPriority); fn clear_in_interrupts(); @@ -504,6 +810,7 @@ pub(crate) mod private { fn set_in_peripheral(peripheral: u8); fn start_in(); fn is_in_done() -> bool; + fn last_in_dscr_address() -> usize; } } diff --git a/esp-hal-common/src/dma/pdma.rs b/esp-hal-common/src/dma/pdma.rs index 96c48f0e32a..4979cb1ea35 100644 --- a/esp-hal-common/src/dma/pdma.rs +++ b/esp-hal-common/src/dma/pdma.rs @@ -86,6 +86,24 @@ macro_rules! ImplSpiChannel { spi.dma_int_raw.read().out_done_int_raw().bit() } + fn last_out_dscr_address() -> usize { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.out_eof_des_addr.read().dma_out_eof_des_addr().bits() as usize + } + + fn is_out_eof_interrupt_set() -> bool { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_int_raw.read().out_eof_int_raw().bit() + } + + fn reset_out_eof_interrupt() { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_int_clr.write(|w| { + w.out_eof_int_clr() + .set_bit() + }); + } + fn set_in_burstmode(burst_mode: bool) { let spi = unsafe { &*crate::pac::[]::PTR }; spi.dma_conf @@ -138,6 +156,11 @@ macro_rules! ImplSpiChannel { let spi = unsafe { &*crate::pac::[]::PTR }; spi.dma_int_raw.read().in_done_int_raw().bit() } + + fn last_in_dscr_address() -> usize { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.inlink_dscr_bf0.read().dma_inlink_dscr_bf0().bits() as usize + } } pub struct [] {} @@ -169,6 +192,12 @@ macro_rules! ImplSpiChannel { descriptors: tx_descriptors, burst_mode, tx_impl: tx_impl, + write_offset: 0, + write_descr_ptr: core::ptr::null(), + available: 0, + last_seen_handled_descriptor_ptr: core::ptr::null(), + buffer_start: core::ptr::null(), + buffer_len: 0, _phantom: PhantomData::default(), }; @@ -179,6 +208,217 @@ macro_rules! ImplSpiChannel { descriptors: rx_descriptors, burst_mode, rx_impl: rx_impl, + read_offset: 0, + read_descr_ptr: core::ptr::null(), + available: 0, + last_seen_handled_descriptor_ptr: core::ptr::null(), + buffer_start: core::ptr::null(), + buffer_len: 0, + _phantom: PhantomData::default(), + }; + + Channel { + tx: tx_channel, + rx: rx_channel, + _phantom: PhantomData::default(), + } + } + } + } + }; +} + +macro_rules! ImplI2sChannel { + ($num: literal, $peripheral: literal) => { + paste::paste! { + pub struct [] {} + + impl RegisterAccess for [] { + fn init_channel() { + // nothing to do + } + + fn set_out_burstmode(burst_mode: bool) { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.lc_conf + .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); + } + + fn set_out_priority(_priority: DmaPriority) {} + + fn clear_out_interrupts() { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.int_clr.write(|w| { + w.out_done_int_clr() + .set_bit() + .out_eof_int_clr() + .set_bit() + .out_total_eof_int_clr() + .set_bit() + .out_dscr_err_int_clr() + .set_bit() + }); + } + + fn reset_out() { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.lc_conf.modify(|_, w| w.out_rst().set_bit()); + reg_block.lc_conf.modify(|_, w| w.out_rst().clear_bit()); + } + + fn set_out_descriptors(address: u32) { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.out_link + .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + } + + fn has_out_descriptor_error() -> bool { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.int_raw.read().out_dscr_err_int_raw().bit() + } + + fn set_out_peripheral(_peripheral: u8) { + // no-op + } + + fn start_out() { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.out_link.modify(|_, w| w.outlink_start().set_bit()); + } + + fn is_out_done() -> bool { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.int_raw.read().out_done_int_raw().bit() + } + + fn last_out_dscr_address() -> usize { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.out_eof_des_addr.read().out_eof_des_addr().bits() as usize + } + + fn is_out_eof_interrupt_set() -> bool { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.int_raw.read().out_eof_int_raw().bit() + } + + fn reset_out_eof_interrupt() { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.int_clr.write(|w| { + w.out_eof_int_clr() + .set_bit() + }); + } + + fn set_in_burstmode(burst_mode: bool) { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.lc_conf + .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); + } + + fn set_in_priority(_priority: DmaPriority) {} + + fn clear_in_interrupts() { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.int_clr.write(|w| { + w.in_done_int_clr() + .set_bit() + .in_err_eof_int_clr() + .set_bit() + .in_suc_eof_int_clr() + .set_bit() + .in_dscr_err_int_clr() + .set_bit() + }); + } + + fn reset_in() { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.lc_conf.modify(|_, w| w.in_rst().set_bit()); + reg_block.lc_conf.modify(|_, w| w.in_rst().clear_bit()); + } + + fn set_in_descriptors(address: u32) { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.in_link + .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); + } + + fn has_in_descriptor_error() -> bool { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.int_raw.read().in_dscr_err_int_raw().bit() + } + + fn set_in_peripheral(_peripheral: u8) { + // no-op + } + + fn start_in() { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.in_link.modify(|_, w| w.inlink_start().set_bit()); + } + + fn is_in_done() -> bool { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.int_raw.read().in_done_int_raw().bit() + } + + fn last_in_dscr_address() -> usize { + let reg_block = unsafe { &*crate::pac::[<$peripheral>]::PTR }; + reg_block.inlink_dscr_bf0.read().inlink_dscr_bf0().bits() as usize + } + } + + pub struct [] {} + + impl<'a> TxChannel<[]> for [] {} + + pub struct [] {} + + impl<'a> RxChannel<[]> for [] {} + + pub struct [] {} + + impl [] { + pub fn configure<'a>( + self, + burst_mode: bool, + tx_descriptors: &'a mut [u32], + rx_descriptors: &'a mut [u32], + priority: DmaPriority, + ) -> Channel< + ChannelTx<'a,[], []>, + ChannelRx<'a,[], []>, + [], + > { + let mut tx_impl = [] {}; + tx_impl.init(burst_mode, priority); + + let tx_channel = ChannelTx { + descriptors: tx_descriptors, + burst_mode, + tx_impl: tx_impl, + write_offset: 0, + write_descr_ptr: core::ptr::null(), + available: 0, + last_seen_handled_descriptor_ptr: core::ptr::null(), + buffer_start: core::ptr::null(), + buffer_len: 0, + _phantom: PhantomData::default(), + }; + + let mut rx_impl = [] {}; + rx_impl.init(burst_mode, priority); + + let rx_channel = ChannelRx { + descriptors: rx_descriptors, + burst_mode, + rx_impl: rx_impl, + read_offset: 0, + read_descr_ptr: core::ptr::null(), + available: 0, + last_seen_handled_descriptor_ptr: core::ptr::null(), + buffer_start: core::ptr::null(), + buffer_len: 0, _phantom: PhantomData::default(), }; @@ -193,7 +433,7 @@ macro_rules! ImplSpiChannel { }; } -/// Crate private implementatin details +/// Crate private implementation details pub(crate) mod private { use crate::dma::{private::*, *}; @@ -209,6 +449,25 @@ pub(crate) mod private { ImplSpiChannel!(2); ImplSpiChannel!(3); + + pub struct I2s0DmaSuitablePeripheral {} + impl PeripheralMarker for I2s0DmaSuitablePeripheral {} + impl I2sPeripheral for I2s0DmaSuitablePeripheral {} + impl I2s0Peripheral for I2s0DmaSuitablePeripheral {} + + #[cfg(esp32)] + ImplI2sChannel!(0, "I2S0"); + + #[cfg(esp32s2)] + ImplI2sChannel!(0, "I2S"); + + pub struct I2s1DmaSuitablePeripheral {} + impl PeripheralMarker for I2s1DmaSuitablePeripheral {} + impl I2sPeripheral for I2s1DmaSuitablePeripheral {} + impl I2s1Peripheral for I2s1DmaSuitablePeripheral {} + + #[cfg(esp32)] + ImplI2sChannel!(1, "I2S1"); } /// DMA Peripheral @@ -218,6 +477,9 @@ pub struct Dma { _inner: crate::system::Dma, pub spi2channel: Spi2DmaChannelCreator, pub spi3channel: Spi3DmaChannelCreator, + pub i2s0channel: I2s0DmaChannelCreator, + #[cfg(esp32)] + pub i2s1channel: I2s1DmaChannelCreator, } impl Dma { @@ -232,6 +494,9 @@ impl Dma { _inner: dma, spi2channel: Spi2DmaChannelCreator {}, spi3channel: Spi3DmaChannelCreator {}, + i2s0channel: I2s0DmaChannelCreator {}, + #[cfg(esp32)] + i2s1channel: I2s1DmaChannelCreator {}, } } } diff --git a/esp-hal-common/src/gpio/esp32s2.rs b/esp-hal-common/src/gpio/esp32s2.rs index 4930cfe971a..4e11908ba72 100644 --- a/esp-hal-common/src/gpio/esp32s2.rs +++ b/esp-hal-common/src/gpio/esp32s2.rs @@ -60,6 +60,7 @@ pub enum InputSignal { SUBSPID = 128, SUBSPIHD = 129, SUBSPIWP = 130, + I2S0I_DATA_IN15 = 158, SUBSPID4 = 167, SUBSPID5 = 168, SUBSPID6 = 169, @@ -73,95 +74,97 @@ pub enum InputSignal { #[allow(non_camel_case_types)] #[derive(PartialEq, Copy, Clone)] pub enum OutputSignal { - SPIQ = 0, - SPID = 1, - SPIHD = 2, - SPIWP = 3, - SPICLK = 4, - SPICS0 = 5, - SPICS1 = 6, - SPID4 = 7, - SPID5 = 8, - SPID6 = 9, - SPID7 = 10, - SPIDQS = 11, - U0TXD = 14, - U0RTS = 15, - U0DTR = 16, - U1TXD = 17, - U1RTS = 18, - U1DTR = 21, - I2S0O_BCK = 23, - I2S0O_WS = 25, - I2S0I_BCK = 27, - I2S0I_WS = 28, - I2CEXT0_SCL = 29, - I2CEXT0_SDA = 30, - SDIO_TOHOST_INT = 31, - SPI3_CLK = 72, - SPI3_Q = 73, - SPI3_D = 74, - SPI3_HD = 75, - SPI3_CS0 = 76, - SPI3_CS1 = 77, - SPI3_CS2 = 78, - LEDC_LS_SIG0 = 79, - LEDC_LS_SIG1 = 80, - LEDC_LS_SIG2 = 81, - LEDC_LS_SIG3 = 82, - LEDC_LS_SIG4 = 83, - LEDC_LS_SIG5 = 84, - LEDC_LS_SIG6 = 85, - LEDC_LS_SIG7 = 86, - RMT_SIG_OUT0 = 87, - RMT_SIG_OUT1 = 88, - RMT_SIG_OUT2 = 89, - RMT_SIG_OUT3 = 90, - I2CEXT1_SCL = 95, - I2CEXT1_SDA = 96, - GPIO_SD0 = 100, - GPIO_SD1 = 101, - GPIO_SD2 = 102, - GPIO_SD3 = 103, - GPIO_SD4 = 104, - GPIO_SD5 = 105, - GPIO_SD6 = 106, - GPIO_SD7 = 107, - FSPICLK = 108, - FSPIQ = 109, - FSPID = 110, - FSPIHD = 111, - FSPIWP = 112, - FSPIIO4 = 113, - FSPIIO5 = 114, - FSPIIO6 = 115, - FSPIIO7 = 116, - FSPICS0 = 117, - FSPICS1 = 118, - FSPICS2 = 119, - FSPICS3 = 120, - FSPICS4 = 121, - FSPICS5 = 122, - SUBSPICLK = 126, - SUBSPIQ = 127, - SUBSPID = 128, - SUBSPIHD = 129, - SUBSPIWP = 130, - SUBSPICS0 = 131, - SUBSPICS1 = 132, - FSPIDQS = 133, - FSPI_HSYNC = 134, - FSPI_VSYNC = 135, - FSPI_DE = 136, - FSPICD = 137, - SPI3_CD = 139, - SPI3_DQS = 140, - SUBSPID4 = 167, - SUBSPID5 = 168, - SUBSPID6 = 169, - SUBSPID7 = 170, - SUBSPIDQS = 171, - PCMFSYNC = 209, - PCMCLK = 210, - GPIO = 256, + SPIQ = 0, + SPID = 1, + SPIHD = 2, + SPIWP = 3, + SPICLK = 4, + SPICS0 = 5, + SPICS1 = 6, + SPID4 = 7, + SPID5 = 8, + SPID6 = 9, + SPID7 = 10, + SPIDQS = 11, + U0TXD = 14, + U0RTS = 15, + U0DTR = 16, + U1TXD = 17, + U1RTS = 18, + U1DTR = 21, + I2S0O_BCK = 23, + I2S0O_WS = 25, + I2S0I_BCK = 27, + I2S0I_WS = 28, + I2CEXT0_SCL = 29, + I2CEXT0_SDA = 30, + SDIO_TOHOST_INT = 31, + SPI3_CLK = 72, + SPI3_Q = 73, + SPI3_D = 74, + SPI3_HD = 75, + SPI3_CS0 = 76, + SPI3_CS1 = 77, + SPI3_CS2 = 78, + LEDC_LS_SIG0 = 79, + LEDC_LS_SIG1 = 80, + LEDC_LS_SIG2 = 81, + LEDC_LS_SIG3 = 82, + LEDC_LS_SIG4 = 83, + LEDC_LS_SIG5 = 84, + LEDC_LS_SIG6 = 85, + LEDC_LS_SIG7 = 86, + RMT_SIG_OUT0 = 87, + RMT_SIG_OUT1 = 88, + RMT_SIG_OUT2 = 89, + RMT_SIG_OUT3 = 90, + I2CEXT1_SCL = 95, + I2CEXT1_SDA = 96, + GPIO_SD0 = 100, + GPIO_SD1 = 101, + GPIO_SD2 = 102, + GPIO_SD3 = 103, + GPIO_SD4 = 104, + GPIO_SD5 = 105, + GPIO_SD6 = 106, + GPIO_SD7 = 107, + FSPICLK = 108, + FSPIQ = 109, + FSPID = 110, + FSPIHD = 111, + FSPIWP = 112, + FSPIIO4 = 113, + FSPIIO5 = 114, + FSPIIO6 = 115, + FSPIIO7 = 116, + FSPICS0 = 117, + FSPICS1 = 118, + FSPICS2 = 119, + FSPICS3 = 120, + FSPICS4 = 121, + FSPICS5 = 122, + SUBSPICLK = 126, + SUBSPIQ = 127, + SUBSPID = 128, + SUBSPIHD = 129, + SUBSPIWP = 130, + SUBSPICS0 = 131, + SUBSPICS1 = 132, + FSPIDQS = 133, + FSPI_HSYNC = 134, + FSPI_VSYNC = 135, + FSPI_DE = 136, + FSPICD = 137, + SPI3_CD = 139, + SPI3_DQS = 140, + I2S0O_DATA_OUT23 = 166, + SUBSPID4 = 167, + SUBSPID5 = 168, + SUBSPID6 = 169, + SUBSPID7 = 170, + SUBSPIDQS = 171, + PCMFSYNC = 209, + PCMCLK = 210, + CLK_I2S = 251, + GPIO = 256, } diff --git a/esp-hal-common/src/i2s.rs b/esp-hal-common/src/i2s.rs new file mode 100644 index 00000000000..534cf605373 --- /dev/null +++ b/esp-hal-common/src/i2s.rs @@ -0,0 +1,1725 @@ +//! I2S Master + +use embedded_dma::{ReadBuffer, WriteBuffer}; +use private::*; + +#[cfg(any(esp32s3))] +use crate::dma::private::I2s1Peripheral; +use crate::{ + clock::Clocks, + dma::{ + private::{I2s0Peripheral, I2sPeripheral, Rx, Tx}, + Channel, + DmaError, + DmaTransfer, + }, + system::PeripheralClockControl, + InputPin, + OutputPin, +}; + +trait AcceptedWord {} +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)] +pub enum Error { + Unknown, + DmaError(DmaError), + IllegalArgument, +} + +impl From for Error { + fn from(value: DmaError) -> Self { + Error::DmaError(value) + } +} + +pub enum Standard { + Philips, + // Tdm, + // Pdm, +} + +#[cfg(not(any(esp32, esp32s2)))] +pub enum DataFormat { + Data32Channel32, + Data32Channel24, + Data32Channel16, + Data32Channel8, + Data16Channel16, + Data16Channel8, + Data8Channel8, +} + +#[cfg(any(esp32, esp32s2))] +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(any(esp32, esp32s2))] +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, + } + } +} + +pub struct PinsBclkWsDout { + pub bclk: B, + pub ws: W, + pub dout: DO, +} + +impl I2sTxPins for PinsBclkWsDout +where + B: OutputPin, + W: OutputPin, + DO: OutputPin, +{ + fn configure(&mut self, instance: &mut I) + where + I: RegisterAccess, + { + self.bclk + .set_to_push_pull_output() + .connect_peripheral_to_output(instance.bclk_signal()); + + self.ws + .set_to_push_pull_output() + .connect_peripheral_to_output(instance.ws_signal()); + + self.dout + .set_to_push_pull_output() + .connect_peripheral_to_output(instance.dout_signal()); + } +} + +pub struct PinsBclkWsDin { + pub bclk: B, + pub ws: W, + pub din: DI, +} + +impl I2sRxPins for PinsBclkWsDin +where + B: OutputPin, + W: OutputPin, + DI: InputPin, +{ + fn configure(&mut self, instance: &mut I) + where + I: RegisterAccess, + { + self.bclk + .set_to_push_pull_output() + .connect_peripheral_to_output(instance.bclk_rx_signal()); + + self.ws + .set_to_push_pull_output() + .connect_peripheral_to_output(instance.ws_rx_signal()); + + self.din + .set_to_input() + .connect_input_to_peripheral(instance.din_signal()); + } +} + +#[cfg(not(esp32))] +pub struct MclkPin { + pub mclk: M, +} + +#[cfg(not(esp32))] +impl I2sMclkPin for MclkPin +where + M: OutputPin, +{ + fn configure(&mut self, instance: &mut I) + where + I: RegisterAccess, + { + self.mclk + .set_to_push_pull_output() + .connect_peripheral_to_output(instance.mclk_signal()); + } +} + +pub struct NoMclk {} + +impl I2sMclkPin for NoMclk { + fn configure(&mut self, _instance: &mut I) + where + I: RegisterAccess, + { + // nothing to do + } +} + +/// An in-progress DMA write transfer. +pub struct I2sWriteDmaTransfer +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, +{ + i2s_tx: I2sTx, + buffer: BUFFER, +} + +impl I2sWriteDmaTransfer +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, +{ + /// Amount of bytes which can be pushed + pub fn available(&mut self) -> usize { + self.i2s_tx.tx_channel.available() + } + + pub fn push(&mut self, data: &[u8]) -> Result { + Ok(self.i2s_tx.tx_channel.push(data)?) + } +} + +impl DmaTransfer> + for I2sWriteDmaTransfer +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, +{ + /// Wait for the DMA transfer to complete and return the buffers and the + /// I2sTx instance. + fn wait(self) -> (BUFFER, I2sTx) { + self.i2s_tx.wait_tx_dma_done().ok(); // waiting for the DMA transfer is not enough + + // `DmaTransfer` needs to have a `Drop` implementation, because we accept + // managed buffers that can free their memory on drop. Because of that + // we can't move out of the `DmaTransfer`'s fields, so we use `ptr::read` + // and `mem::forget`. + // + // NOTE(unsafe) There is no panic branch between getting the resources + // and forgetting `self`. + unsafe { + let buffer = core::ptr::read(&self.buffer); + let payload = core::ptr::read(&self.i2s_tx); + core::mem::forget(self); + (buffer, payload) + } + } +} + +impl Drop for I2sWriteDmaTransfer +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, +{ + fn drop(&mut self) { + self.i2s_tx.wait_tx_dma_done().ok(); + } +} + +pub trait I2sWrite { + fn write(&mut self, words: &[W]) -> Result<(), Error>; +} + +pub trait I2sWriteDma +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, +{ + fn write_dma(self, words: TXBUF) -> Result, Error> + where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, + TXBUF: ReadBuffer; + + fn write_dma_circular( + self, + words: TXBUF, + ) -> Result, Error> + where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, + TXBUF: ReadBuffer; +} + +/// An in-progress DMA read transfer. +pub struct I2sReadDmaTransfer +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, +{ + i2s_rx: I2sRx, + buffer: BUFFER, +} + +impl I2sReadDmaTransfer +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, +{ + /// Amount of bytes which can be poped + pub fn available(&mut self) -> usize { + self.i2s_rx.rx_channel.available() + } + + pub fn pop(&mut self, data: &mut [u8]) -> Result { + Ok(self.i2s_rx.rx_channel.pop(data)?) + } +} + +impl DmaTransfer> for I2sReadDmaTransfer +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, +{ + /// Wait for the DMA transfer to complete and return the buffers and the + /// I2sTx instance. + fn wait(self) -> (BUFFER, I2sRx) { + self.i2s_rx.wait_rx_dma_done().ok(); // waiting for the DMA transfer is not enough + + // `DmaTransfer` needs to have a `Drop` implementation, because we accept + // managed buffers that can free their memory on drop. Because of that + // we can't move out of the `DmaTransfer`'s fields, so we use `ptr::read` + // and `mem::forget`. + // + // NOTE(unsafe) There is no panic branch between getting the resources + // and forgetting `self`. + unsafe { + let buffer = core::ptr::read(&self.buffer); + let payload = core::ptr::read(&self.i2s_rx); + core::mem::forget(self); + (buffer, payload) + } + } +} + +impl Drop for I2sReadDmaTransfer +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, +{ + fn drop(&mut self) { + self.i2s_rx.wait_rx_dma_done().ok(); + } +} + +pub trait I2sRead { + fn read(&mut self, words: &mut [W]) -> Result<(), Error>; +} + +pub trait I2sReadDma +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, +{ + fn read_dma(self, words: RXBUF) -> Result, Error> + where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, + RXBUF: WriteBuffer; + + fn read_dma_circular(self, words: RXBUF) -> Result, Error> + where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, + RXBUF: WriteBuffer; +} + +pub struct I2s +where + I: Instance, + T: RegisterAccess + Clone, + P: I2sMclkPin, + TX: Tx, + RX: Rx, +{ + _peripheral: I, + _register_access: T, + _pins: P, + pub i2s_tx: TxCreator, + pub i2s_rx: RxCreator, +} + +impl I2s +where + I: Instance, + T: RegisterAccess + Clone, + P: I2sMclkPin, + TX: Tx, + RX: Rx, +{ + fn new_internal( + i2s: I, + mut pins: P, + standard: Standard, + data_format: DataFormat, + sample_rate: impl Into, + mut channel: Channel, + peripheral_clock_control: &mut PeripheralClockControl, + clocks: &Clocks, + ) -> Self + where + IP: I2sPeripheral, + { + // on ESP32-C3 / ESP32-S3 and later RX and TX are independent and + // could be configured totally independently but for now handle all + // the targets the same and force same configuration for both, TX and RX + + let mut register_access = i2s.register_access(); + + channel.tx.init_channel(); + peripheral_clock_control.enable(register_access.get_peripheral()); + pins.configure(&mut register_access); + register_access.set_clock(calculate_clock( + sample_rate, + 2, + data_format.channel_bits(), + clocks, + )); + register_access.configure(&standard, &data_format); + register_access.set_master(); + register_access.update(); + + Self { + _peripheral: i2s, + _register_access: register_access.clone(), + _pins: pins, + i2s_tx: TxCreator { + register_access: register_access.clone(), + tx_channel: channel.tx, + }, + i2s_rx: RxCreator { + register_access, + rx_channel: channel.rx, + }, + } + } +} + +pub trait I2s0New +where + I: Instance, + T: RegisterAccess + Clone, + P: I2sMclkPin, + TX: Tx, + RX: Rx, + IP: I2sPeripheral + I2s0Peripheral, + RX: Rx, +{ + fn new( + i2s: I, + pins: P, + standard: Standard, + data_format: DataFormat, + sample_rate: impl Into, + channel: Channel, + peripheral_clock_control: &mut PeripheralClockControl, + clocks: &Clocks, + ) -> Self; +} + +impl I2s0New for I2s +where + I: Instance + I2s0Instance, + T: RegisterAccess + Clone, + P: I2sMclkPin, + TX: Tx, + RX: Rx, + IP: I2sPeripheral + I2s0Peripheral, +{ + fn new( + i2s: I, + pins: P, + standard: Standard, + data_format: DataFormat, + sample_rate: impl Into, + channel: Channel, + peripheral_clock_control: &mut PeripheralClockControl, + clocks: &Clocks, + ) -> Self { + Self::new_internal( + i2s, + pins, + standard, + data_format, + sample_rate, + channel, + peripheral_clock_control, + clocks, + ) + } +} + +#[cfg(any(esp32s3))] +pub trait I2s1New +where + I: Instance, + T: RegisterAccess + Clone, + P: I2sMclkPin, + TX: Tx, + RX: Rx, + IP: I2sPeripheral + I2s1Peripheral, + RX: Rx, +{ + fn new( + i2s: I, + pins: P, + standard: Standard, + data_format: DataFormat, + sample_rate: impl Into, + channel: Channel, + peripheral_clock_control: &mut PeripheralClockControl, + clocks: &Clocks, + ) -> Self; +} + +#[cfg(any(esp32s3))] +impl I2s1New for I2s +where + I: Instance + I2s1Instance, + T: RegisterAccess + Clone, + P: I2sMclkPin, + TX: Tx, + RX: Rx, + IP: I2sPeripheral + I2s1Peripheral, +{ + fn new( + i2s: I, + pins: P, + standard: Standard, + data_format: DataFormat, + sample_rate: impl Into, + channel: Channel, + peripheral_clock_control: &mut PeripheralClockControl, + clocks: &Clocks, + ) -> Self { + Self::new_internal( + i2s, + pins, + standard, + data_format, + sample_rate, + channel, + peripheral_clock_control, + clocks, + ) + } +} + +pub struct I2sTx +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, +{ + register_access: T, + _pins: P, + tx_channel: TX, +} + +impl I2sTx +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, +{ + fn new(mut register_access: T, mut pins: P, tx_channel: TX) -> Self { + pins.configure(&mut register_access); + + Self { + register_access, + _pins: pins, + tx_channel, + } + } + + fn write_bytes(&mut self, data: &[u8]) -> Result<(), Error> { + let ptr = data as *const _ as *const u8; + + // Reset TX unit and TX FIFO + self.register_access.reset_tx(); + + // Enable corresponding interrupts if needed + + // configure DMA outlink + self.tx_channel.prepare_transfer( + self.register_access.get_dma_peripheral(), + false, + ptr, + data.len(), + )?; + + // set I2S_TX_STOP_EN if needed + + // start: set I2S_TX_START + self.register_access.tx_start(); + + // wait until I2S_TX_IDLE is 1 + self.register_access.wait_for_tx_done(); + + Ok(()) + } + + fn start_tx_transfer( + mut self, + words: TXBUF, + circular: bool, + ) -> Result, Error> + where + TXBUF: ReadBuffer, + { + let (ptr, len) = unsafe { words.read_buffer() }; + + // Reset TX unit and TX FIFO + self.register_access.reset_tx(); + + // Enable corresponding interrupts if needed + + // configure DMA outlink + self.tx_channel.prepare_transfer( + self.register_access.get_dma_peripheral(), + circular, + ptr, + len, + )?; + + // set I2S_TX_STOP_EN if needed + + // start: set I2S_TX_START + self.register_access.tx_start(); + + Ok(I2sWriteDmaTransfer { + i2s_tx: self, + buffer: words, + }) + } + + fn wait_tx_dma_done(&self) -> Result<(), Error> { + // wait until I2S_TX_IDLE is 1 + self.register_access.wait_for_tx_done(); + + Ok(()) + } +} + +impl I2sWrite for I2sTx +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, + W: AcceptedWord, +{ + fn write(&mut self, words: &[W]) -> Result<(), Error> { + self.write_bytes(unsafe { + core::slice::from_raw_parts( + words as *const _ as *const u8, + words.len() * core::mem::size_of::(), + ) + }) + } +} + +impl I2sWriteDma for I2sTx +where + T: RegisterAccess, + P: I2sTxPins, + TX: Tx, +{ + fn write_dma(self, words: TXBUF) -> Result, Error> + where + TXBUF: ReadBuffer, + { + self.start_tx_transfer(words, false) + } + + fn write_dma_circular(self, words: TXBUF) -> Result, Error> + where + TXBUF: ReadBuffer, + { + self.start_tx_transfer(words, true) + } +} + +pub struct I2sRx +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, +{ + register_access: T, + _pins: P, + rx_channel: RX, +} + +impl I2sRx +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, +{ + fn new(mut register_access: T, mut pins: P, rx_channel: RX) -> Self { + pins.configure(&mut register_access); + + Self { + register_access, + _pins: pins, + rx_channel, + } + } + + fn read_bytes(&mut self, data: &mut [u8]) -> Result<(), Error> { + let ptr = data as *mut _ as *mut u8; + + // Reset RX unit and RX FIFO + self.register_access.reset_rx(); + + // Enable corresponding interrupts if needed + + // configure DMA outlink + self.rx_channel.prepare_transfer( + false, + self.register_access.get_dma_peripheral(), + ptr, + data.len(), + )?; + + // set I2S_TX_STOP_EN if needed + + // start: set I2S_TX_START + self.register_access.rx_start(data.len() - 1); + + // wait until I2S_TX_IDLE is 1 + self.register_access.wait_for_rx_done(); + + Ok(()) + } + + fn start_rx_transfer( + mut self, + mut words: RXBUF, + circular: bool, + ) -> Result, Error> + where + RXBUF: WriteBuffer, + { + let (ptr, len) = unsafe { words.write_buffer() }; + + if len % 4 != 0 { + return Err(Error::IllegalArgument); + } + + // Reset TX unit and TX FIFO + self.register_access.reset_rx(); + + // Enable corresponding interrupts if needed + + // configure DMA outlink + self.rx_channel.prepare_transfer( + circular, + self.register_access.get_dma_peripheral(), + ptr, + len, + )?; + + // set I2S_TX_STOP_EN if needed + + // start: set I2S_RX_START + #[cfg(not(esp32))] + self.register_access.rx_start(len - 1); + + #[cfg(esp32)] + self.register_access.rx_start(len); + + Ok(I2sReadDmaTransfer { + i2s_rx: self, + buffer: words, + }) + } + + fn wait_rx_dma_done(&self) -> Result<(), Error> { + self.register_access.wait_for_rx_done(); + + Ok(()) + } +} + +impl I2sRead for I2sRx +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, + W: AcceptedWord, +{ + fn read(&mut self, words: &mut [W]) -> Result<(), Error> { + if words.len() * core::mem::size_of::() > 4096 || words.len() == 0 { + return Err(Error::IllegalArgument); + } + + self.read_bytes(unsafe { + core::slice::from_raw_parts_mut( + words as *mut _ as *mut u8, + words.len() * core::mem::size_of::(), + ) + }) + } +} + +impl I2sReadDma for I2sRx +where + T: RegisterAccess, + P: I2sRxPins, + RX: Rx, +{ + fn read_dma(self, words: RXBUF) -> Result, Error> + where + RXBUF: WriteBuffer, + { + self.start_rx_transfer(words, false) + } + + fn read_dma_circular(self, words: RXBUF) -> Result, Error> + where + RXBUF: WriteBuffer, + { + self.start_rx_transfer(words, true) + } +} + +mod private { + use fugit::HertzU32; + + use super::{DataFormat, I2sRx, I2sTx, Standard}; + #[cfg(any(esp32c3, esp32s2))] + use crate::pac::i2s::RegisterBlock; + // on ESP32-S3 I2S1 doesn't support all features - use that to avoid using those features + // by accident + #[cfg(any(esp32s3, esp32))] + use crate::pac::i2s1::RegisterBlock; + #[cfg(any(esp32c3, esp32s2))] + use crate::pac::I2S; + #[cfg(any(esp32s3, esp32))] + use crate::pac::I2S0 as I2S; + use crate::{ + clock::Clocks, + dma::{ + private::{Rx, Tx}, + DmaPeripheral, + }, + system::Peripheral, + types::{InputSignal, OutputSignal}, + }; + + pub trait I2sTxPins { + fn configure(&mut self, instance: &mut I) + where + I: RegisterAccess; + } + + pub trait I2sRxPins { + fn configure(&mut self, instance: &mut I) + where + I: RegisterAccess; + } + + pub trait I2sMclkPin { + fn configure(&mut self, instance: &mut I) + where + I: RegisterAccess; + } + + pub struct TxCreator + where + T: RegisterAccess + Clone, + TX: Tx, + { + pub register_access: T, + pub tx_channel: TX, + } + + impl TxCreator + where + T: RegisterAccess + Clone, + TX: Tx, + { + pub fn with_pins

(self, mut pins: P) -> I2sTx + where + P: I2sTxPins, + { + let mut register_access = self.register_access.clone(); + pins.configure(&mut register_access); + + I2sTx::new(self.register_access, pins, self.tx_channel) + } + } + + pub struct RxCreator + where + T: RegisterAccess + Clone, + RX: Rx, + { + pub register_access: T, + pub rx_channel: RX, + } + + impl RxCreator + where + T: RegisterAccess + Clone, + RX: Rx, + { + pub fn with_pins

(self, mut pins: P) -> I2sRx + where + P: I2sRxPins, + { + let mut register_access = self.register_access.clone(); + pins.configure(&mut register_access); + + I2sRx::new(self.register_access, pins, self.rx_channel) + } + } + + pub trait I2s0Instance {} + + pub trait I2s1Instance {} + + pub trait Instance + where + R: RegisterAccess, + { + fn register_access(&self) -> R; + } + + impl Instance for I2S { + fn register_access(&self) -> I2sPeripheral0 { + I2sPeripheral0 {} + } + } + + impl I2s0Instance for I2S {} + + #[cfg(esp32s3)] + impl Instance for crate::pac::I2S1 { + fn register_access(&self) -> I2sPeripheral1 { + I2sPeripheral1 {} + } + } + + #[cfg(esp32s3)] + impl I2s1Instance for crate::pac::I2S1 {} + + pub trait Signals { + fn get_peripheral(&self) -> Peripheral; + + fn get_dma_peripheral(&self) -> DmaPeripheral; + + 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 trait RegBlock { + fn register_block(&self) -> &'static RegisterBlock; + } + + #[cfg(any(esp32, esp32s2))] + pub trait RegisterAccess: Signals + RegBlock { + fn set_clock(&self, clock_settings: I2sClockDividers) { + let i2s = self.register_block(); + + i2s.clkm_conf.modify(|r, w| unsafe { + w.bits(r.bits() | (2 << 21)) // select PLL_160M + }); + + #[cfg(esp32)] + i2s.clkm_conf.modify(|_, w| w.clka_ena().clear_bit()); + + i2s.clkm_conf.modify(|_, w| { + w.clk_en() + .set_bit() + .clkm_div_num() + .variant(clock_settings.mclk_divider as u8) + }); + + i2s.sample_rate_conf.modify(|_, w| { + w.tx_bck_div_num() + .variant(clock_settings.bclk_divider as u8) + .rx_bck_div_num() + .variant(clock_settings.bclk_divider as u8) + }); + + i2s.clkm_conf + .modify(|_, w| w.clkm_div_b().variant(0).clkm_div_a().variant(0)); + } + + fn configure(&self, _standard: &Standard, data_format: &DataFormat) { + let i2s = self.register_block(); + + let fifo_mod = match data_format { + DataFormat::Data32Channel32 => 2, + DataFormat::Data16Channel16 => 0, + }; + + i2s.sample_rate_conf + .modify(|_, w| w.tx_bits_mod().variant(data_format.channel_bits())); + i2s.sample_rate_conf + .modify(|_, w| w.rx_bits_mod().variant(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() + .variant(false) //?? + .rx_short_sync() + .variant(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() + }); + + i2s.fifo_conf.modify(|_, w| { + w.tx_fifo_mod() + .variant(fifo_mod) + .tx_fifo_mod_force_en() + .set_bit() + .dscr_en() + .set_bit() + .rx_fifo_mod() + .variant(fifo_mod) + .rx_fifo_mod_force_en() + .set_bit() + }); + + i2s.conf_chan + .modify(|_, w| w.tx_chan_mod().variant(0).rx_chan_mod().variant(0)); // for now only stereo + + i2s.conf1 + .modify(|_, w| w.tx_pcm_bypass().set_bit().rx_pcm_bypass().set_bit()); + + i2s.pd_conf + .modify(|_, w| w.fifo_force_pu().set_bit().fifo_force_pd().clear_bit()); + + i2s.conf2 + .modify(|_, w| w.camera_en().clear_bit().lcd_en().clear_bit()); + } + + fn set_master(&self) { + let i2s = self.register_block(); + i2s.conf + .modify(|_, w| w.rx_slave_mod().clear_bit().tx_slave_mod().clear_bit()); + } + + fn update(&self) { + // nothing to do + } + + fn reset_tx(&self) { + 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.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_int_clr() + .set_bit() + .out_total_eof_int_clr() + .set_bit() + }); + } + + fn tx_start(&self) { + let i2s = self.register_block(); + i2s.conf.modify(|_, w| w.tx_start().set_bit()); + } + + fn wait_for_tx_done(&self) { + let i2s = self.register_block(); + while i2s.state.read().tx_idle().bit_is_clear() { + // wait + } + + i2s.conf.modify(|_, w| w.tx_start().clear_bit()); + } + + fn reset_rx(&self) { + 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.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_int_clr().set_bit().in_suc_eof_int_clr().set_bit()); + } + + fn rx_start(&self, len: usize) { + let i2s = self.register_block(); + + i2s.int_clr.write(|w| w.in_suc_eof_int_clr().set_bit()); + + #[cfg(not(esp32))] + i2s.rxeof_num + .modify(|_, w| w.rx_eof_num().variant(len as u32)); + + // On ESP32, the eof_num count in words. + #[cfg(esp32)] + i2s.rxeof_num + .modify(|_, w| w.rx_eof_num().variant((len / 4) as u32)); + + i2s.conf.modify(|_, w| w.rx_start().set_bit()); + } + + fn wait_for_rx_done(&self) { + let i2s = self.register_block(); + while i2s.int_raw.read().in_suc_eof_int_raw().bit_is_clear() { + // wait + } + + i2s.int_clr.write(|w| w.in_suc_eof_int_clr().set_bit()); + } + } + + #[cfg(any(esp32c3, esp32s3))] + pub trait RegisterAccess: Signals + RegBlock { + fn set_clock(&self, clock_settings: I2sClockDividers) { + let i2s = self.register_block(); + i2s.tx_clkm_conf.modify(|_, w| { + w.clk_en() + .set_bit() + .tx_clk_active() + .set_bit() + .tx_clk_sel() + .variant(2) // for now fixed at 160MHz + .tx_clkm_div_num() + .variant(clock_settings.mclk_divider as u8) + }); + + i2s.tx_conf1.modify(|_, w| { + w.tx_bck_div_num() + .variant(clock_settings.bclk_divider as u8) + }); + + i2s.tx_clkm_div_conf.modify(|_, w| { + w.tx_clkm_div_x() + .variant(0) + .tx_clkm_div_y() + .variant(1) + .tx_clkm_div_yn1() + .variant(false) + .tx_clkm_div_z() + .variant(0) + }); + + i2s.rx_clkm_conf.modify(|_, w| { + w.rx_clk_active() + .set_bit() + .rx_clk_sel() + .variant(2) // for now fixed at 160MHz + .rx_clkm_div_num() + .variant(clock_settings.mclk_divider as u8) + }); + + i2s.rx_conf1.modify(|_, w| { + w.rx_bck_div_num() + .variant(clock_settings.bclk_divider as u8) + }); + + i2s.rx_clkm_div_conf.modify(|_, w| { + w.rx_clkm_div_x() + .variant(0) + .rx_clkm_div_y() + .variant(1) + .rx_clkm_div_yn1() + .variant(false) + .rx_clkm_div_z() + .variant(0) + }); + } + + fn configure(&self, _standard: &Standard, data_format: &DataFormat) { + let i2s = self.register_block(); + i2s.tx_conf1.modify(|_, w| { + w.tx_tdm_ws_width() + .variant(data_format.channel_bits() - 1) + .tx_bits_mod() + .variant(data_format.data_bits() - 1) + .tx_tdm_chan_bits() + .variant(data_format.channel_bits() - 1) + .tx_half_sample_bits() + .variant(data_format.channel_bits() - 1) + .tx_msb_shift() + .set_bit() + }); + + i2s.tx_conf.modify(|_, w| { + 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() + .variant(0) + }); + + i2s.tx_tdm_ctrl.modify(|_, w| { + w.tx_tdm_tot_chan_num() + .variant(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() + }); + + i2s.rx_conf1.modify(|_, w| { + w.rx_tdm_ws_width() + .variant(data_format.channel_bits() - 1) + .rx_bits_mod() + .variant(data_format.data_bits() - 1) + .rx_tdm_chan_bits() + .variant(data_format.channel_bits() - 1) + .rx_half_sample_bits() + .variant(data_format.channel_bits() - 1) + }); + + i2s.rx_conf.modify(|_, w| { + w.rx_mono() + .clear_bit() + .rx_mono_fst_vld() + .set_bit() + .rx_stop_mode() + .variant(2) + .rx_tdm_en() + .set_bit() + .rx_pdm_en() + .clear_bit() + .rx_pcm_bypass() + .set_bit() + }); + + i2s.rx_tdm_ctrl.modify(|_, w| { + w.rx_tdm_tot_chan_num() + .variant(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() + }); + } + + fn set_master(&self) { + let i2s = self.register_block(); + i2s.tx_conf.modify(|_, w| w.tx_slave_mod().clear_bit()); + i2s.rx_conf.modify(|_, w| w.rx_slave_mod().clear_bit()); + } + + fn update(&self) { + let i2s = self.register_block(); + i2s.tx_conf.modify(|_, w| w.tx_update().clear_bit()); + i2s.tx_conf.modify(|_, w| w.tx_update().set_bit()); + + i2s.rx_conf.modify(|_, w| w.rx_update().clear_bit()); + i2s.rx_conf.modify(|_, w| w.rx_update().set_bit()); + } + + fn reset_tx(&self) { + 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.int_clr + .write(|w| w.tx_done_int_clr().set_bit().tx_hung_int_clr().set_bit()); + } + + fn tx_start(&self) { + let i2s = self.register_block(); + i2s.tx_conf.modify(|_, w| w.tx_start().set_bit()); + } + + fn wait_for_tx_done(&self) { + let i2s = self.register_block(); + while i2s.state.read().tx_idle().bit_is_clear() { + // wait + } + + i2s.tx_conf.modify(|_, w| w.tx_start().clear_bit()); + } + + fn reset_rx(&self) { + 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.int_clr + .write(|w| w.rx_done_int_clr().set_bit().rx_hung_int_clr().set_bit()); + } + + fn rx_start(&self, len: usize) { + let i2s = self.register_block(); + i2s.rxeof_num.write(|w| w.rx_eof_num().variant(len as u16)); + i2s.rx_conf.modify(|_, w| w.rx_start().set_bit()); + } + + fn wait_for_rx_done(&self) { + let i2s = self.register_block(); + while i2s.int_raw.read().rx_done_int_raw().bit_is_clear() { + // wait + } + + i2s.int_clr.write(|w| w.rx_done_int_clr().set_bit()); + } + } + + #[derive(Clone)] + pub struct I2sPeripheral0 {} + + #[cfg(any(esp32s3, esp32))] + #[derive(Clone)] + pub struct I2sPeripheral1 {} + + #[cfg(esp32c3)] + impl Signals for I2sPeripheral0 { + fn get_peripheral(&self) -> Peripheral { + Peripheral::I2s0 + } + + fn get_dma_peripheral(&self) -> DmaPeripheral { + DmaPeripheral::I2s + } + + fn mclk_signal(&self) -> OutputSignal { + OutputSignal::I2S_MCLK + } + + fn bclk_signal(&self) -> OutputSignal { + OutputSignal::I2SO_BCK + } + + fn ws_signal(&self) -> OutputSignal { + OutputSignal::I2SO_WS + } + + fn dout_signal(&self) -> OutputSignal { + OutputSignal::I2SI_SD + } + + fn bclk_rx_signal(&self) -> OutputSignal { + OutputSignal::I2SI_BCK + } + + fn ws_rx_signal(&self) -> OutputSignal { + OutputSignal::I2SI_WS + } + + fn din_signal(&self) -> InputSignal { + InputSignal::I2SI_SD + } + } + + #[cfg(esp32s3)] + impl Signals for I2sPeripheral0 { + fn get_peripheral(&self) -> Peripheral { + Peripheral::I2s0 + } + + fn get_dma_peripheral(&self) -> DmaPeripheral { + DmaPeripheral::I2s0 + } + + fn mclk_signal(&self) -> OutputSignal { + OutputSignal::I2S0_MCLK + } + + fn bclk_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_BCK + } + + fn ws_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_WS + } + + fn dout_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_SD + } + + fn bclk_rx_signal(&self) -> OutputSignal { + OutputSignal::I2S0I_BCK + } + + fn ws_rx_signal(&self) -> OutputSignal { + OutputSignal::I2S0I_WS + } + + fn din_signal(&self) -> InputSignal { + InputSignal::I2S0I_SD + } + } + + #[cfg(esp32s3)] + impl Signals for I2sPeripheral1 { + fn get_peripheral(&self) -> Peripheral { + Peripheral::I2s1 + } + + fn get_dma_peripheral(&self) -> DmaPeripheral { + DmaPeripheral::I2s1 + } + + 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 { + 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 { + InputSignal::I2S1I_SD + } + } + + #[cfg(esp32)] + impl Signals for I2sPeripheral0 { + fn get_peripheral(&self) -> Peripheral { + Peripheral::I2s0 + } + + fn get_dma_peripheral(&self) -> DmaPeripheral { + DmaPeripheral::I2s0 + } + + fn mclk_signal(&self) -> OutputSignal { + panic!("MCLK currently not supported on ESP32"); + } + + fn bclk_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_BCK + } + + fn ws_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_WS + } + + fn dout_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_DATA_23 + } + + fn bclk_rx_signal(&self) -> OutputSignal { + OutputSignal::I2S0I_BCK + } + + fn ws_rx_signal(&self) -> OutputSignal { + OutputSignal::I2S0I_WS + } + + fn din_signal(&self) -> InputSignal { + InputSignal::I2S0I_DATA_15 + } + } + + #[cfg(esp32)] + impl Signals for I2sPeripheral1 { + fn get_peripheral(&self) -> Peripheral { + Peripheral::I2s1 + } + + fn get_dma_peripheral(&self) -> DmaPeripheral { + DmaPeripheral::I2s1 + } + + fn mclk_signal(&self) -> OutputSignal { + panic!("MCLK currently not supported on ESP32"); + } + + fn bclk_signal(&self) -> OutputSignal { + OutputSignal::I2S1O_BCK + } + + fn ws_signal(&self) -> OutputSignal { + OutputSignal::I2S1O_WS + } + + fn dout_signal(&self) -> OutputSignal { + OutputSignal::I2S1O_DATA_23 + } + + fn bclk_rx_signal(&self) -> OutputSignal { + OutputSignal::I2S1I_BCK + } + + fn ws_rx_signal(&self) -> OutputSignal { + OutputSignal::I2S1I_WS + } + + fn din_signal(&self) -> InputSignal { + InputSignal::I2S1I_DATA_15 + } + } + + #[cfg(esp32s2)] + impl Signals for I2sPeripheral0 { + fn get_peripheral(&self) -> Peripheral { + Peripheral::I2s0 + } + + fn get_dma_peripheral(&self) -> DmaPeripheral { + DmaPeripheral::I2s0 + } + + fn mclk_signal(&self) -> OutputSignal { + OutputSignal::CLK_I2S + } + + fn bclk_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_BCK + } + + fn ws_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_WS + } + + fn dout_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_DATA_OUT23 + } + + fn bclk_rx_signal(&self) -> OutputSignal { + OutputSignal::I2S0I_BCK + } + + fn ws_rx_signal(&self) -> OutputSignal { + OutputSignal::I2S0I_WS + } + + fn din_signal(&self) -> InputSignal { + InputSignal::I2S0I_DATA_IN15 + } + } + + impl RegBlock for I2sPeripheral0 { + fn register_block(&self) -> &'static RegisterBlock { + unsafe { core::mem::transmute(I2S::PTR) } + } + } + + #[cfg(any(esp32s3, esp32))] + impl RegBlock for I2sPeripheral1 { + fn register_block(&self) -> &'static RegisterBlock { + unsafe { core::mem::transmute(crate::pac::I2S1::PTR) } + } + } + + impl RegisterAccess for I2sPeripheral0 {} + + #[cfg(any(esp32s3, esp32))] + impl RegisterAccess for I2sPeripheral1 {} + + pub struct I2sClockDividers { + mclk_divider: u32, + bclk_divider: u32, + } + + pub fn calculate_clock( + sample_rate: impl Into, + channels: u8, + data_bits: u8, + _clocks: &Clocks, + ) -> I2sClockDividers { + // in esp-idf the functions looks not only different for every chip but + // also for TX/RX and for different modes + + let mclk_multiple = 256; + let sclk = 160_000_000; // for now it's fixed PLL_160M_CLK + + let rate_hz: HertzU32 = sample_rate.into(); + let rate = rate_hz.raw(); + + let bclk = rate * channels as u32 * data_bits as u32; + let mclk = rate * mclk_multiple; + let bclk_divider = mclk / bclk; + let mclk_divider = sclk / mclk; + + // this way we won't exactly match the desired frequency - for that we need + // to calculate clkm div_z, div_y, div_x, div_yn1 + + I2sClockDividers { + mclk_divider, + bclk_divider, + } + } +} diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index c4e474ce7b2..d5add7f07f9 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -54,6 +54,10 @@ pub mod delay; pub mod dma; pub mod gpio; pub mod i2c; + +#[cfg(any(esp32c3, esp32s3, esp32, esp32s2))] +pub mod i2s; + pub mod ledc; #[cfg(any(esp32s2, esp32s3))] pub mod otg_fs; diff --git a/esp-hal-common/src/spi.rs b/esp-hal-common/src/spi.rs index b03c04876a2..9ab50705e8f 100644 --- a/esp-hal-common/src/spi.rs +++ b/esp-hal-common/src/spi.rs @@ -1000,8 +1000,18 @@ where self.enable_dma(); self.update(); - tx.prepare_transfer(self.dma_peripheral(), write_buffer_ptr, write_buffer_len)?; - rx.prepare_transfer(self.dma_peripheral(), read_buffer_ptr, read_buffer_len)?; + tx.prepare_transfer( + self.dma_peripheral(), + false, + write_buffer_ptr, + write_buffer_len, + )?; + rx.prepare_transfer( + false, + self.dma_peripheral(), + read_buffer_ptr, + read_buffer_len, + )?; self.clear_dma_interrupts(); @@ -1033,7 +1043,7 @@ where self.enable_dma(); self.update(); - tx.prepare_transfer(self.dma_peripheral(), ptr, len)?; + tx.prepare_transfer(self.dma_peripheral(), false, ptr, len)?; self.clear_dma_interrupts(); @@ -1054,7 +1064,7 @@ where self.enable_dma(); self.update(); - rx.prepare_transfer(self.dma_peripheral(), ptr, len)?; + rx.prepare_transfer(false, self.dma_peripheral(), ptr, len)?; self.clear_dma_interrupts(); diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index fa5e5820c8e..852bbe292af 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -34,6 +34,10 @@ pub enum Peripheral { Gdma, #[cfg(any(esp32, esp32s2))] Dma, + #[cfg(not(esp32c2))] + I2s0, + #[cfg(not(any(esp32c2, esp32s2, esp32c3)))] + I2s1, #[cfg(any(esp32s2, esp32s3))] Usb, } @@ -122,6 +126,22 @@ impl PeripheralClockControl { perip_clk_en0.modify(|_, w| w.spi3_dma_clk_en().set_bit()); perip_rst_en0.modify(|_, w| w.spi3_dma_rst().clear_bit()); } + #[cfg(esp32c3)] + Peripheral::I2s0 => { + // on ESP32-C3 note that i2s1_clk_en / rst is really I2s0 + perip_clk_en0.modify(|_, w| w.i2s1_clk_en().set_bit()); + perip_rst_en0.modify(|_, w| w.i2s1_rst().clear_bit()); + } + #[cfg(any(esp32s3, esp32, esp32s2))] + Peripheral::I2s0 => { + perip_clk_en0.modify(|_, w| w.i2s0_clk_en().set_bit()); + perip_rst_en0.modify(|_, w| w.i2s0_rst().clear_bit()); + } + #[cfg(any(esp32s3, esp32))] + Peripheral::I2s1 => { + perip_clk_en0.modify(|_, w| w.i2s1_clk_en().set_bit()); + perip_rst_en0.modify(|_, w| w.i2s1_rst().clear_bit()); + } #[cfg(any(esp32s2, esp32s3))] Peripheral::Usb => { perip_clk_en0.modify(|_, w| w.usb_clk_en().set_bit()); diff --git a/esp32-hal/examples/i2s_read.rs b/esp32-hal/examples/i2s_read.rs new file mode 100644 index 00000000000..a553528883c --- /dev/null +++ b/esp32-hal/examples/i2s_read.rs @@ -0,0 +1,94 @@ +//! This shows how to continously receive data via I2S +//! +//! Pins used +//! BCLK GPIO12 +//! WS GPIO13 +//! DIN GPIO17 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] + +use esp32_hal::{ + clock::ClockControl, + dma::{DmaPriority}, + pdma::Dma, + i2s::{DataFormat, I2s, NoMclk, Standard, I2s0New, PinsBclkWsDin, I2sReadDma}, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use esp_println::println; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt = timer_group0.wdt; + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + + // Disable MWDT and RWDT (Watchdog) flash boot protection + wdt.disable(); + rtc.rwdt.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.i2s0channel; + + let mut tx_descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S0, + NoMclk {}, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin { + bclk: io.pins.gpio12, + ws: io.pins.gpio13, + din: io.pins.gpio17, + }); + + let buffer = dma_buffer(); + + let mut transfer = i2s_rx.read_dma_circular(buffer).unwrap(); + println!("Started transfer"); + + loop { + let avail = transfer.available(); + + if avail > 0 { + let mut rcv = [0u8; 5000]; + transfer.pop(&mut rcv[..avail]).unwrap(); + println!("Received {:x?}...", &rcv[..30]); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32-hal/examples/i2s_sound.rs b/esp32-hal/examples/i2s_sound.rs new file mode 100644 index 00000000000..118a6b42792 --- /dev/null +++ b/esp32-hal/examples/i2s_sound.rs @@ -0,0 +1,136 @@ +//! This shows how to transmit data continously via I2S +//! +//! Pins used +//! BCLK GPIO12 +//! WS GPIO13 +//! DOUT GPIO14 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO12 | +//! | DIN | GPIO14 | +//! | LRCK | GPIO13 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] + +use esp32_hal::{ + clock::ClockControl, + dma::DmaPriority, + pdma::Dma, + i2s::{DataFormat, I2s, I2sWriteDma, NoMclk, PinsBclkWsDout, Standard, I2s0New}, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use xtensa_lx_rt::entry; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt = timer_group0.wdt; + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + + // Disable MWDT and RWDT (Watchdog) flash boot protection + wdt.disable(); + rtc.rwdt.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.i2s0channel; + + let mut tx_descriptors = [0u32; 20 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S0, + NoMclk {}, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout { + bclk: io.pins.gpio12, + ws: io.pins.gpio13, + dout: io.pins.gpio14, + }); + + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + let mut transfer = i2s_tx.write_dma_circular(buffer).unwrap(); + + loop { + let avail = transfer.available(); + if avail > 0 { + let avail = usize::min(10000, avail); + for bidx in 0..avail { + filler[bidx] = data[idx]; + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + transfer.push(&filler[0..avail]).unwrap(); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index 04a4afa0a04..51312de2f7d 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -10,6 +10,7 @@ pub use esp_hal_common::{ efuse, gpio as gpio_types, i2c, + i2s, interrupt, ledc, macros, diff --git a/esp32c3-hal/examples/i2s_read.rs b/esp32c3-hal/examples/i2s_read.rs new file mode 100644 index 00000000000..ded6e994361 --- /dev/null +++ b/esp32c3-hal/examples/i2s_read.rs @@ -0,0 +1,101 @@ +//! This shows how to continously receive data via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO5 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] + +use esp32c3_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + i2s::{DataFormat, I2s, I2sReadDma, MclkPin, PinsBclkWsDin, Standard, I2s0New}, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use esp_println::println; +use riscv_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut tx_descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S, + MclkPin { + mclk: io.pins.gpio4, + }, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin { + bclk: io.pins.gpio1, + ws: io.pins.gpio2, + din: io.pins.gpio5, + }); + + let buffer = dma_buffer(); + + let mut transfer = i2s_rx.read_dma_circular(buffer).unwrap(); + println!("Started transfer"); + + loop { + let avail = transfer.available(); + + if avail > 0 { + let mut rcv = [0u8; 5000]; + transfer.pop(&mut rcv[..avail]).unwrap(); + println!("Received {:x?}...", &rcv[..30]); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32c3-hal/examples/i2s_sound.rs b/esp32c3-hal/examples/i2s_sound.rs new file mode 100644 index 00000000000..8b084506940 --- /dev/null +++ b/esp32c3-hal/examples/i2s_sound.rs @@ -0,0 +1,143 @@ +//! This shows how to transmit data continously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] + +use esp32c3_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + i2s::{DataFormat, I2s, I2sWriteDma, MclkPin, PinsBclkWsDout, Standard, I2s0New}, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use riscv_rt::entry; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut tx_descriptors = [0u32; 20 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S, + MclkPin { + mclk: io.pins.gpio4, + }, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout { + bclk: io.pins.gpio1, + ws: io.pins.gpio2, + dout: io.pins.gpio3, + }); + + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + + let mut transfer = i2s_tx.write_dma_circular(buffer).unwrap(); + loop { + let avail = transfer.available(); + if avail > 0 { + let avail = usize::min(10000, avail); + for bidx in 0..avail { + filler[bidx] = data[idx]; + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + transfer.push(&filler[0..avail]).unwrap(); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index 7e1944ad651..7d9921d3b57 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -13,6 +13,7 @@ pub use esp_hal_common::{ efuse, gpio as gpio_types, i2c, + i2s, interrupt, ledc, macros, diff --git a/esp32s2-hal/examples/i2s_read.rs b/esp32s2-hal/examples/i2s_read.rs new file mode 100644 index 00000000000..ccc6ca78b40 --- /dev/null +++ b/esp32s2-hal/examples/i2s_read.rs @@ -0,0 +1,94 @@ +//! This shows how to continously receive data via I2S +//! +//! Pins used +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO5 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] + +use esp32s2_hal::{ + clock::ClockControl, + dma::{DmaPriority}, + pdma::Dma, + i2s::{DataFormat, I2s, NoMclk, Standard, I2s0New, PinsBclkWsDin, I2sReadDma}, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use esp_println::println; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt = timer_group0.wdt; + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + + // Disable MWDT and RWDT (Watchdog) flash boot protection + wdt.disable(); + rtc.rwdt.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.i2s0channel; + + let mut tx_descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S, + NoMclk {}, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin { + bclk: io.pins.gpio1, + ws: io.pins.gpio2, + din: io.pins.gpio5, + }); + + let buffer = dma_buffer(); + + let mut transfer = i2s_rx.read_dma_circular(buffer).unwrap(); + println!("Started transfer"); + + loop { + let avail = transfer.available(); + + if avail > 0 { + let mut rcv = [0u8; 4092 * 4]; + transfer.pop(&mut rcv[..avail]).unwrap(); + println!("Received {:x?}...", &rcv[..30]); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32s2-hal/examples/i2s_sound.rs b/esp32s2-hal/examples/i2s_sound.rs new file mode 100644 index 00000000000..21cb3216e03 --- /dev/null +++ b/esp32s2-hal/examples/i2s_sound.rs @@ -0,0 +1,139 @@ +//! This shows how to transmit data continously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] + +use esp32s2_hal::{ + clock::ClockControl, + dma::DmaPriority, + pdma::Dma, + i2s::{DataFormat, I2s, I2sWriteDma, MclkPin, PinsBclkWsDout, Standard, I2s0New}, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use xtensa_lx_rt::entry; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt = timer_group0.wdt; + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + + // Disable watchdog timer + wdt.disable(); + rtc.rwdt.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.i2s0channel; + + let mut tx_descriptors = [0u32; 20 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S, + MclkPin { + mclk: io.pins.gpio4, + }, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout { + bclk: io.pins.gpio1, + ws: io.pins.gpio2, + dout: io.pins.gpio3, + }); + + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + + let mut transfer = i2s_tx.write_dma_circular(buffer).unwrap(); + loop { + let avail = transfer.available(); + if avail > 0 { + let avail = usize::min(10000, avail); + for bidx in 0..avail { + filler[bidx] = data[idx]; + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + transfer.push(&filler[0..avail]).unwrap(); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32s2-hal/src/lib.rs b/esp32s2-hal/src/lib.rs index 5726ab3350a..ffb1bd5727a 100644 --- a/esp32s2-hal/src/lib.rs +++ b/esp32s2-hal/src/lib.rs @@ -8,6 +8,7 @@ pub use esp_hal_common::{ dma::pdma, efuse, gpio as gpio_types, + i2s, i2c::{self, I2C}, interrupt, ledc, diff --git a/esp32s3-hal/examples/i2s_read.rs b/esp32s3-hal/examples/i2s_read.rs new file mode 100644 index 00000000000..73dc48c219a --- /dev/null +++ b/esp32s3-hal/examples/i2s_read.rs @@ -0,0 +1,101 @@ +//! This shows how to continously receive data via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DIN GPIO5 +//! +//! Without an additional I2S source device you can connect 3V3 or GND to DIN to +//! read 0 or 0xFF or connect DIN to WS to read two different values +//! +//! You can also inspect the MCLK, BCLK and WS with a logic analyzer + +#![no_std] +#![no_main] + +use esp32s3_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + i2s::{DataFormat, I2s, I2sReadDma, MclkPin, PinsBclkWsDin, Standard, I2s0New}, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use esp_println::println; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut tx_descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin { + mclk: io.pins.gpio4, + }, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_rx = i2s.i2s_rx.with_pins(PinsBclkWsDin { + bclk: io.pins.gpio1, + ws: io.pins.gpio2, + din: io.pins.gpio5, + }); + + let buffer = dma_buffer(); + + let mut transfer = i2s_rx.read_dma_circular(buffer).unwrap(); + println!("Started transfer"); + + loop { + let avail = transfer.available(); + + if avail > 0 { + let mut rcv = [0u8; 5000]; + transfer.pop(&mut rcv[..avail]).unwrap(); + println!("Received {:x?}...", &rcv[..30]); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 4092 * 4] { + static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4]; + unsafe { &mut BUFFER } +} diff --git a/esp32s3-hal/examples/i2s_sound.rs b/esp32s3-hal/examples/i2s_sound.rs new file mode 100644 index 00000000000..6f790ddc780 --- /dev/null +++ b/esp32s3-hal/examples/i2s_sound.rs @@ -0,0 +1,143 @@ +//! This shows how to transmit data continously via I2S +//! +//! Pins used +//! MCLK GPIO4 +//! BCLK GPIO1 +//! WS GPIO2 +//! DOUT GPIO3 +//! +//! Without an additional I2S sink device you can inspect the MCLK, BCLK, WS and +//! DOUT with a logic analyzer +//! +//! You can also connect e.g. a PCM510x to hear an annoying loud sine tone (full +//! scale), so turn down the volume before running this example. +//! +//! Wiring is like this +//! +//! | Pin | Connected to | +//! |-------|-----------------| +//! | BCK | GPIO1 | +//! | DIN | GPIO3 | +//! | LRCK | GPIO2 | +//! | SCK | Gnd | +//! | GND | Gnd | +//! | VIN | +3V3 | +//! | FLT | Gnd | +//! | FMT | Gnd | +//! | DEMP | Gnd | +//! | XSMT | +3V3 | + +#![no_std] +#![no_main] + +use esp32s3_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + i2s::{DataFormat, I2s, I2sWriteDma, MclkPin, PinsBclkWsDout, Standard, I2s0New}, + pac::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use xtensa_lx_rt::entry; + +const SINE: [i16; 64] = [ + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, 23169, 25329, 27244, 28897, 30272, 31356, + 32137, 32609, 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, 23169, 20787, 18204, + 15446, 12539, 9511, 6392, 3211, 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, -23169, + -25329, -27244, -28897, -30272, -31356, -32137, -32609, -32767, -32609, -32137, -31356, -30272, + -28897, -27244, -25329, -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +]; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut tx_descriptors = [0u32; 20 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let i2s = I2s::new( + peripherals.I2S0, + MclkPin { + mclk: io.pins.gpio4, + }, + Standard::Philips, + DataFormat::Data16Channel16, + 44100u32.Hz(), + dma_channel.configure( + false, + &mut tx_descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + ), + &mut system.peripheral_clock_control, + &clocks, + ); + + let i2s_tx = i2s.i2s_tx.with_pins(PinsBclkWsDout { + bclk: io.pins.gpio1, + ws: io.pins.gpio2, + dout: io.pins.gpio3, + }); + + let data = + unsafe { core::slice::from_raw_parts(&SINE as *const _ as *const u8, SINE.len() * 2) }; + + let buffer = dma_buffer(); + let mut idx = 0; + for i in 0..usize::min(data.len(), buffer.len()) { + buffer[i] = data[idx]; + + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + + let mut filler = [0u8; 10000]; + + let mut transfer = i2s_tx.write_dma_circular(buffer).unwrap(); + loop { + let avail = transfer.available(); + if avail > 0 { + let avail = usize::min(10000, avail); + for bidx in 0..avail { + filler[bidx] = data[idx]; + idx += 1; + + if idx >= data.len() { + idx = 0; + } + } + transfer.push(&filler[0..avail]).unwrap(); + } + } +} + +fn dma_buffer() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32s3-hal/src/lib.rs b/esp32s3-hal/src/lib.rs index 340bfcd6b22..8156e3763b4 100644 --- a/esp32s3-hal/src/lib.rs +++ b/esp32s3-hal/src/lib.rs @@ -11,6 +11,7 @@ pub use esp_hal_common::{ efuse, gpio as gpio_types, i2c, + i2s, interrupt, ledc, macros,