diff --git a/esp-hal-common/src/dma/gdma.rs b/esp-hal-common/src/dma/gdma.rs index a33380078a9..57fe18a734d 100644 --- a/esp-hal-common/src/dma/gdma.rs +++ b/esp-hal-common/src/dma/gdma.rs @@ -305,12 +305,10 @@ 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, + read_buffer_start: core::ptr::null(), _phantom: PhantomData::default(), }; diff --git a/esp-hal-common/src/dma/mod.rs b/esp-hal-common/src/dma/mod.rs index 33df76b72ac..1ae3d0d79f7 100644 --- a/esp-hal-common/src/dma/mod.rs +++ b/esp-hal-common/src/dma/mod.rs @@ -72,6 +72,7 @@ pub enum DmaPeripheral { Rmt = 9, } +#[derive(PartialEq, PartialOrd)] enum Owner { Cpu = 0, Dma = 1, @@ -210,6 +211,8 @@ pub(crate) mod private { fn available(&mut self) -> usize; fn pop(&mut self, data: &mut [u8]) -> Result; + + fn drain_buffer(&mut self, dst: &mut [u8]) -> Result; } pub trait RxChannel @@ -245,7 +248,7 @@ pub(crate) mod private { let mut dw0 = &mut descriptors[descr]; - dw0.set_suc_eof(true); + dw0.set_suc_eof(false); 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!? @@ -299,12 +302,10 @@ 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 read_buffer_start: *const u8, pub _phantom: PhantomData, } @@ -340,12 +341,10 @@ pub(crate) mod private { 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.read_buffer_start = data; self.rx_impl .prepare_transfer(self.descriptors, circular, peri, data, len)?; @@ -361,65 +360,47 @@ pub(crate) mod private { } 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 self.last_seen_handled_descriptor_ptr.is_null() { + self.last_seen_handled_descriptor_ptr = self.descriptors.as_mut_ptr(); + return 0; + } - if descr_address >= self.last_seen_handled_descriptor_ptr { - let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; + if self.available != 0 { + return self.available; + } - 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; + let descr_address = self.last_seen_handled_descriptor_ptr as *mut u32; + let mut dw0 = unsafe { &mut descr_address.read_volatile() }; - unsafe { - loop { - if ptr.offset(2).read_volatile() == 0 { - break; - } + if dw0.get_owner() == Owner::Cpu && dw0.get_length() != 0 { + let descriptor_buffer = + unsafe { descr_address.offset(1).read_volatile() } as *const u8; + let next_descriptor = + unsafe { descr_address.offset(2).read_volatile() } as *const u32; - let mut dw0 = &mut ptr.read_volatile(); - self.available += dw0.get_length() as usize; - ptr = ptr.offset(3); - } - } - } + self.read_buffer_start = descriptor_buffer; + self.available = dw0.get_length() as usize; - 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 - } - } + dw0.set_owner(Owner::Dma); + dw0.set_length(0); + dw0.set_suc_eof(false); + + unsafe { + descr_address.write_volatile(*dw0); } - self.last_seen_handled_descriptor_ptr = descr_address; - } else { - self.last_seen_handled_descriptor_ptr = last_dscr; + if !next_descriptor.is_null() { + self.last_seen_handled_descriptor_ptr = next_descriptor; + } else { + self.last_seen_handled_descriptor_ptr = self.descriptors.as_ptr(); + } } self.available } fn pop(&mut self, data: &mut [u8]) -> Result { - let avail = self.available(); + let avail = self.available; if avail < data.len() { return Err(super::DmaError::Exhausted); @@ -427,49 +408,41 @@ pub(crate) mod private { 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); + let src = self.read_buffer_start; + let count = self.available; 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); - } - } + self.available = 0; + Ok(data.len()) + } - let mut forward = data.len(); + fn drain_buffer(&mut self, dst: &mut [u8]) -> Result { + let mut len: usize = 0; + let mut dscr = self.descriptors.as_ptr() as *mut u32; loop { + let mut dw0 = unsafe { &mut dscr.read_volatile() }; + let buffer_ptr = unsafe { dscr.offset(1).read_volatile() } as *const u8; + let next_dscr = unsafe { dscr.offset(2).read_volatile() } as *const u8; + let chunk_len = dw0.get_length() as usize; 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; - } + core::ptr::copy_nonoverlapping( + buffer_ptr, + dst.as_mut_ptr().offset(len as isize), + chunk_len, + ) + }; - forward -= segment_len; + len += chunk_len; - if forward == 0 { - break; - } + if next_dscr.is_null() { + break; } - } - self.read_offset = (self.read_offset + data.len()) % self.buffer_len; - self.available -= data.len(); + dscr = unsafe { dscr.offset(3) }; + } - Ok(data.len()) + Ok(len) } } diff --git a/esp-hal-common/src/dma/pdma.rs b/esp-hal-common/src/dma/pdma.rs index 4979cb1ea35..21798345bba 100644 --- a/esp-hal-common/src/dma/pdma.rs +++ b/esp-hal-common/src/dma/pdma.rs @@ -208,12 +208,10 @@ 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, + read_buffer_start: core::ptr::null(), _phantom: PhantomData::default(), }; @@ -413,12 +411,10 @@ macro_rules! ImplI2sChannel { 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, + read_buffer_start: core::ptr::null(), _phantom: PhantomData::default(), }; diff --git a/esp-hal-common/src/i2s.rs b/esp-hal-common/src/i2s.rs index a87d7c96bdc..af574c483b6 100644 --- a/esp-hal-common/src/i2s.rs +++ b/esp-hal-common/src/i2s.rs @@ -18,6 +18,14 @@ use crate::{ OutputPin, }; +#[cfg(any(esp32, esp32s2, esp32s3))] +const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 6; + +#[cfg(any(esp32c3))] +const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 9; + +const I2S_LL_MCLK_DIVIDER_MAX: usize = (1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1; + trait AcceptedWord {} impl AcceptedWord for u8 {} impl AcceptedWord for u16 {} @@ -26,6 +34,7 @@ impl AcceptedWord for i8 {} impl AcceptedWord for i16 {} impl AcceptedWord for i32 {} +/// I2S Error #[derive(Debug, Clone, Copy)] pub enum Error { Unknown, @@ -39,12 +48,14 @@ impl From for Error { } } +/// Supported standards. pub enum Standard { Philips, // Tdm, // Pdm, } +/// Supported data formats #[cfg(not(any(esp32, esp32s2)))] pub enum DataFormat { Data32Channel32, @@ -56,6 +67,7 @@ pub enum DataFormat { Data8Channel8, } +/// Supported data formats #[cfg(any(esp32, esp32s2))] pub enum DataFormat { Data32Channel32, @@ -106,6 +118,7 @@ impl DataFormat { } } +/// Pins to use for I2S tx pub struct PinsBclkWsDout { pub bclk: B, pub ws: W, @@ -136,6 +149,7 @@ where } } +/// Pins to use for I2S rx pub struct PinsBclkWsDin { pub bclk: B, pub ws: W, @@ -166,6 +180,7 @@ where } } +/// MCLK pin to use #[cfg(not(esp32))] pub struct MclkPin { pub mclk: M, @@ -186,6 +201,7 @@ where } } +/// No MCLK pin pub struct NoMclk {} impl I2sMclkPin for NoMclk { @@ -214,11 +230,14 @@ where P: I2sTxPins, TX: Tx, { - /// Amount of bytes which can be pushed + /// Amount of bytes which can be pushed. + /// Only useful for circular DMA transfers pub fn available(&mut self) -> usize { self.i2s_tx.tx_channel.available() } + /// Push bytes into the DMA buffer. + /// Only useful for circular DMA transfers pub fn push(&mut self, data: &[u8]) -> Result { Ok(self.i2s_tx.tx_channel.push(data)?) } @@ -263,16 +282,21 @@ where } } +/// Blocking I2s Write pub trait I2sWrite { fn write(&mut self, words: &[W]) -> Result<(), Error>; } +/// Initiate a DMA tx transfer pub trait I2sWriteDma where T: RegisterAccess, P: I2sTxPins, TX: Tx, { + /// Write I2S. + /// Returns [I2sWriteDmaTransfer] which represents the in-ptrogress DMA + /// transfer fn write_dma(self, words: TXBUF) -> Result, Error> where T: RegisterAccess, @@ -280,6 +304,8 @@ where TX: Tx, TXBUF: ReadBuffer; + /// Continously write to I2S. Returns [I2sWriteDmaTransfer] which represents + /// the in-ptrogress DMA transfer fn write_dma_circular( self, words: TXBUF, @@ -316,6 +342,30 @@ where pub fn pop(&mut self, data: &mut [u8]) -> Result { Ok(self.i2s_rx.rx_channel.pop(data)?) } + + /// Wait for the DMA transfer to complete and return the buffers and the + /// I2sTx instance after copying the read data to the given buffer. + /// Length of the received data is returned at the third element of the + /// tuple. + pub fn wait_receive(mut self, dst: &mut [u8]) -> (BUFFER, I2sRx, usize) { + self.i2s_rx.wait_rx_dma_done().ok(); // waiting for the DMA transfer is not enough + + let len = self.i2s_rx.rx_channel.drain_buffer(dst).unwrap(); + + // `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, len) + } + } } impl DmaTransfer> for I2sReadDmaTransfer @@ -356,16 +406,21 @@ where } } +/// Blocking I2S Read pub trait I2sRead { fn read(&mut self, words: &mut [W]) -> Result<(), Error>; } +/// Initate a DMA rx transfer pub trait I2sReadDma where T: RegisterAccess, P: I2sRxPins, RX: Rx, { + /// Read I2S. + /// Returns [I2sReadDmaTransfer] which represents the in-ptrogress DMA + /// transfer fn read_dma(self, words: RXBUF) -> Result, Error> where T: RegisterAccess, @@ -373,6 +428,9 @@ where RX: Rx, RXBUF: WriteBuffer; + /// Continously read from I2S. + /// Returns [I2sReadDmaTransfer] which represents the in-ptrogress DMA + /// transfer fn read_dma_circular(self, words: RXBUF) -> Result, Error> where T: RegisterAccess, @@ -381,6 +439,7 @@ where RXBUF: WriteBuffer; } +/// Instance of the I2S peripheral driver pub struct I2s where I: Instance, @@ -452,6 +511,7 @@ where } } +/// Construct a new I2S peripheral driver instance for the first I2S peripheral pub trait I2s0New where I: Instance, @@ -506,6 +566,7 @@ where } } +/// Construct a new I2S peripheral driver instance for the second I2S peripheral #[cfg(any(esp32s3))] pub trait I2s1New where @@ -562,6 +623,7 @@ where } } +/// I2S TX channel pub struct I2sTx where T: RegisterAccess, @@ -696,6 +758,7 @@ where } } +/// I2S RX channel pub struct I2sRx where T: RegisterAccess, @@ -844,7 +907,7 @@ where mod private { use fugit::HertzU32; - use super::{DataFormat, I2sRx, I2sTx, Standard}; + use super::{DataFormat, I2sRx, I2sTx, Standard, I2S_LL_MCLK_DIVIDER_MAX}; #[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 @@ -1005,15 +1068,19 @@ mod private { .variant(clock_settings.mclk_divider as u8) }); + i2s.clkm_conf.modify(|_, w| { + w.clkm_div_a() + .variant(clock_settings.denominator as u8) + .clkm_div_b() + .variant(clock_settings.numerator 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) { @@ -1171,6 +1238,59 @@ mod private { pub trait RegisterAccess: Signals + RegBlock { fn set_clock(&self, clock_settings: I2sClockDividers) { let i2s = self.register_block(); + + let clkm_div_x: u32; + let clkm_div_y: u32; + let clkm_div_z: u32; + let clkm_div_yn1: u32; + + if clock_settings.denominator == 0 || clock_settings.numerator == 0 { + clkm_div_x = 0; + clkm_div_y = 0; + clkm_div_z = 0; + clkm_div_yn1 = 1; + } else { + if clock_settings.numerator > clock_settings.denominator / 2 { + clkm_div_x = clock_settings + .denominator + .overflowing_div( + clock_settings + .denominator + .overflowing_sub(clock_settings.numerator) + .0, + ) + .0 + .overflowing_sub(1) + .0; + clkm_div_y = clock_settings.denominator + % (clock_settings + .denominator + .overflowing_sub(clock_settings.numerator) + .0); + clkm_div_z = clock_settings + .denominator + .overflowing_sub(clock_settings.numerator) + .0; + clkm_div_yn1 = 1; + } else { + clkm_div_x = clock_settings.denominator / clock_settings.numerator - 1; + clkm_div_y = clock_settings.denominator % clock_settings.numerator; + clkm_div_z = clock_settings.numerator; + clkm_div_yn1 = 0; + } + } + + i2s.tx_clkm_div_conf.modify(|_, w| { + w.tx_clkm_div_x() + .variant(clkm_div_x as u16) + .tx_clkm_div_y() + .variant(clkm_div_y as u16) + .tx_clkm_div_yn1() + .variant(if clkm_div_yn1 != 0 { true } else { false }) + .tx_clkm_div_z() + .variant(clkm_div_z as u16) + }); + i2s.tx_clkm_conf.modify(|_, w| { w.clk_en() .set_bit() @@ -1184,18 +1304,18 @@ mod private { i2s.tx_conf1.modify(|_, w| { w.tx_bck_div_num() - .variant(clock_settings.bclk_divider as u8) + .variant((clock_settings.bclk_divider - 1) 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_div_conf.modify(|_, w| { + w.rx_clkm_div_x() + .variant(clkm_div_x as u16) + .rx_clkm_div_y() + .variant(clkm_div_y as u16) + .rx_clkm_div_yn1() + .variant(if clkm_div_yn1 != 0 { true } else { false }) + .rx_clkm_div_z() + .variant(clkm_div_z as u16) }); i2s.rx_clkm_conf.modify(|_, w| { @@ -1205,22 +1325,13 @@ mod private { .variant(2) // for now fixed at 160MHz .rx_clkm_div_num() .variant(clock_settings.mclk_divider as u8) + .mclk_sel() + .variant(true) }); 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) + .variant((clock_settings.bclk_divider - 1) as u8) }); } @@ -1308,6 +1419,8 @@ mod private { .variant(data_format.channel_bits() - 1) .rx_half_sample_bits() .variant(data_format.channel_bits() - 1) + .rx_msb_shift() + .set_bit() }); i2s.rx_conf.modify(|_, w| { @@ -1323,6 +1436,10 @@ mod private { .clear_bit() .rx_pcm_bypass() .set_bit() + .rx_big_endian() + .clear_bit() + .rx_bit_order() + .clear_bit() }); i2s.rx_tdm_ctrl.modify(|_, w| { @@ -1692,6 +1809,8 @@ mod private { pub struct I2sClockDividers { mclk_divider: u32, bclk_divider: u32, + denominator: u32, + numerator: u32, } pub fn calculate_clock( @@ -1700,8 +1819,10 @@ mod private { 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 + // this loosely corresponds to `i2s_std_calculate_clock` and + // `i2s_ll_tx_set_mclk` in esp-idf + // + // main difference is we are using fixed-point arithmetic here let mclk_multiple = 256; let sclk = 160_000_000; // for now it's fixed PLL_160M_CLK @@ -1712,14 +1833,50 @@ mod private { 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 + 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; + + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / + // 126.0 + if decimal > 1250000 / 126 { + mclk_divider += 1; + } else { + let mut min: u32 = !0; + + for a in 2..=I2S_LL_MCLK_DIVIDER_MAX { + let b = (a as u64) * (freq_diff as u64 * 10000u64 / mclk as u64) + 5000; + ma = ((freq_diff as u64 * 10000u64 * a as u64) / 10000) as u32; + mb = (mclk as u64 * (b / 10000)) as u32; + + if ma == mb { + denominator = a as u32; + numerator = (b / 10000) as u32; + break; + } + + if mb.abs_diff(ma) < min { + denominator = a as u32; + numerator = b as u32; + min = mb.abs_diff(ma); + } + } + } + } I2sClockDividers { mclk_divider, bclk_divider, + denominator, + numerator, } } }