mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-02 06:40:47 +00:00
Preliminary I2S Implementation
This commit is contained in:
parent
d9135350e7
commit
4afa8fda92
@ -119,6 +119,38 @@ macro_rules! impl_channel {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn last_out_dscr_address() -> usize {
|
||||||
|
let dma = unsafe { &*crate::pac::DMA::PTR };
|
||||||
|
dma.[<out_eof_des_addr_ch $num>].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.[<int_raw_ch $num>].read().out_eof().bit();
|
||||||
|
#[cfg(esp32s3)]
|
||||||
|
let ret = dma.[<out_int_raw_ch $num>].read().out_eof().bit();
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_out_eof_interrupt() {
|
||||||
|
let dma = unsafe { &*crate::pac::DMA::PTR };
|
||||||
|
|
||||||
|
#[cfg(not(esp32s3))]
|
||||||
|
dma.[<int_clr_ch $num>].write(|w| {
|
||||||
|
w.out_eof()
|
||||||
|
.set_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(esp32s3)]
|
||||||
|
dma.[<out_int_clr_ch $num>].write(|w| {
|
||||||
|
w.out_eof()
|
||||||
|
.set_bit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn set_in_burstmode(burst_mode: bool) {
|
fn set_in_burstmode(burst_mode: bool) {
|
||||||
let dma = unsafe { &*crate::pac::DMA::PTR };
|
let dma = unsafe { &*crate::pac::DMA::PTR };
|
||||||
|
|
||||||
@ -225,6 +257,11 @@ macro_rules! impl_channel {
|
|||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn last_in_dscr_address() -> usize {
|
||||||
|
let dma = unsafe { &*crate::pac::DMA::PTR };
|
||||||
|
dma.[<in_dscr_bf0_ch $num>].read().inlink_dscr_bf0().bits() as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct [<Channel $num TxImpl>] {}
|
pub struct [<Channel $num TxImpl>] {}
|
||||||
@ -252,6 +289,12 @@ macro_rules! impl_channel {
|
|||||||
descriptors: tx_descriptors,
|
descriptors: tx_descriptors,
|
||||||
burst_mode,
|
burst_mode,
|
||||||
tx_impl: tx_impl,
|
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(),
|
_phantom: PhantomData::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -262,6 +305,12 @@ macro_rules! impl_channel {
|
|||||||
descriptors: rx_descriptors,
|
descriptors: rx_descriptors,
|
||||||
burst_mode,
|
burst_mode,
|
||||||
rx_impl: rx_impl,
|
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(),
|
_phantom: PhantomData::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -279,6 +328,9 @@ macro_rules! impl_channel {
|
|||||||
// with GDMA every channel can be used for any peripheral
|
// with GDMA every channel can be used for any peripheral
|
||||||
impl SpiPeripheral for [<SuitablePeripheral $num>] {}
|
impl SpiPeripheral for [<SuitablePeripheral $num>] {}
|
||||||
impl Spi2Peripheral for [<SuitablePeripheral $num>] {}
|
impl Spi2Peripheral for [<SuitablePeripheral $num>] {}
|
||||||
|
impl I2sPeripheral for [<SuitablePeripheral $num>] {}
|
||||||
|
impl I2s0Peripheral for [<SuitablePeripheral $num>] {}
|
||||||
|
impl I2s1Peripheral for [<SuitablePeripheral $num>] {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ pub mod gdma;
|
|||||||
#[cfg(pdma)]
|
#[cfg(pdma)]
|
||||||
pub mod pdma;
|
pub mod pdma;
|
||||||
|
|
||||||
|
const CHUNK_SIZE: usize = 4092;
|
||||||
|
|
||||||
/// DMA Errors
|
/// DMA Errors
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum DmaError {
|
pub enum DmaError {
|
||||||
@ -16,6 +18,9 @@ pub enum DmaError {
|
|||||||
OutOfDescriptors,
|
OutOfDescriptors,
|
||||||
InvalidDescriptorSize,
|
InvalidDescriptorSize,
|
||||||
DescriptorError,
|
DescriptorError,
|
||||||
|
Overflow,
|
||||||
|
Exhausted,
|
||||||
|
BufferTooSmall,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DMA Priorities
|
/// DMA Priorities
|
||||||
@ -51,9 +56,9 @@ pub enum DmaPeripheral {
|
|||||||
Spi3 = 1,
|
Spi3 = 1,
|
||||||
#[cfg(any(esp32c3, esp32s3))]
|
#[cfg(any(esp32c3, esp32s3))]
|
||||||
Uhci0 = 2,
|
Uhci0 = 2,
|
||||||
#[cfg(any(esp32c3, esp32s3))]
|
#[cfg(any(esp32, esp32s2, esp32c3, esp32s3))]
|
||||||
I2s0 = 3,
|
I2s0 = 3,
|
||||||
#[cfg(esp32s3)]
|
#[cfg(any(esp32, esp32s3))]
|
||||||
I2s1 = 4,
|
I2s1 = 4,
|
||||||
#[cfg(esp32s3)]
|
#[cfg(esp32s3)]
|
||||||
LcdCam = 5,
|
LcdCam = 5,
|
||||||
@ -89,7 +94,6 @@ trait DmaLinkedListDw0 {
|
|||||||
fn set_err_eof(&mut self, err_eof: bool);
|
fn set_err_eof(&mut self, err_eof: bool);
|
||||||
#[cfg(not(esp32))]
|
#[cfg(not(esp32))]
|
||||||
fn get_err_eof(&mut self) -> bool;
|
fn get_err_eof(&mut self) -> bool;
|
||||||
#[cfg(not(esp32))]
|
|
||||||
fn set_suc_eof(&mut self, suc_eof: bool);
|
fn set_suc_eof(&mut self, suc_eof: bool);
|
||||||
fn get_suc_eof(&mut self) -> bool;
|
fn get_suc_eof(&mut self) -> bool;
|
||||||
fn set_owner(&mut self, owner: Owner);
|
fn set_owner(&mut self, owner: Owner);
|
||||||
@ -134,7 +138,6 @@ impl DmaLinkedListDw0 for &mut u32 {
|
|||||||
((**self & (mask << bit_s)) >> bit_s) != 0
|
((**self & (mask << bit_s)) >> bit_s) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(esp32))]
|
|
||||||
fn set_suc_eof(&mut self, suc_eof: bool) {
|
fn set_suc_eof(&mut self, suc_eof: bool) {
|
||||||
let mask = 0b1;
|
let mask = 0b1;
|
||||||
let bit_s = 30;
|
let bit_s = 30;
|
||||||
@ -176,6 +179,15 @@ pub(crate) mod private {
|
|||||||
#[cfg(any(esp32, esp32s2, esp32s3))]
|
#[cfg(any(esp32, esp32s2, esp32s3))]
|
||||||
pub trait Spi3Peripheral: SpiPeripheral + PeripheralMarker {}
|
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
|
/// DMA Rx
|
||||||
///
|
///
|
||||||
/// The functions here are not meant to be used outside the HAL and will be
|
/// The functions here are not meant to be used outside the HAL and will be
|
||||||
@ -187,12 +199,17 @@ pub(crate) mod private {
|
|||||||
|
|
||||||
fn prepare_transfer(
|
fn prepare_transfer(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
circular: bool,
|
||||||
peri: DmaPeripheral,
|
peri: DmaPeripheral,
|
||||||
data: *mut u8,
|
data: *mut u8,
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> Result<(), DmaError>;
|
) -> Result<(), DmaError>;
|
||||||
|
|
||||||
fn is_done(&mut self) -> bool;
|
fn is_done(&mut self) -> bool;
|
||||||
|
|
||||||
|
fn available(&mut self) -> usize;
|
||||||
|
|
||||||
|
fn pop(&mut self, data: &mut [u8]) -> Result<usize, DmaError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RxChannel<R>
|
pub trait RxChannel<R>
|
||||||
@ -207,6 +224,7 @@ pub(crate) mod private {
|
|||||||
fn prepare_transfer(
|
fn prepare_transfer(
|
||||||
&mut self,
|
&mut self,
|
||||||
descriptors: &mut [u32],
|
descriptors: &mut [u32],
|
||||||
|
circular: bool,
|
||||||
peri: DmaPeripheral,
|
peri: DmaPeripheral,
|
||||||
data: *mut u8,
|
data: *mut u8,
|
||||||
len: usize,
|
len: usize,
|
||||||
@ -220,16 +238,14 @@ pub(crate) mod private {
|
|||||||
let mut processed = 0;
|
let mut processed = 0;
|
||||||
let mut descr = 0;
|
let mut descr = 0;
|
||||||
loop {
|
loop {
|
||||||
let chunk_size = usize::min(4092, len - processed);
|
let chunk_size = usize::min(CHUNK_SIZE, len - processed);
|
||||||
let last = processed + chunk_size >= len;
|
let last = processed + chunk_size >= len;
|
||||||
|
|
||||||
descriptors[descr + 1] = data as u32 + processed as u32;
|
descriptors[descr + 1] = data as u32 + processed as u32;
|
||||||
|
|
||||||
let mut dw0 = &mut descriptors[descr];
|
let mut dw0 = &mut descriptors[descr];
|
||||||
|
|
||||||
#[cfg(not(esp32))]
|
dw0.set_suc_eof(true);
|
||||||
dw0.set_suc_eof(last);
|
|
||||||
|
|
||||||
dw0.set_owner(Owner::Dma);
|
dw0.set_owner(Owner::Dma);
|
||||||
dw0.set_size(chunk_size as u16); // align to 32 bits?
|
dw0.set_size(chunk_size as u16); // align to 32 bits?
|
||||||
dw0.set_length(0); // actual size of the data!?
|
dw0.set_length(0); // actual size of the data!?
|
||||||
@ -238,7 +254,11 @@ pub(crate) mod private {
|
|||||||
descriptors[descr + 2] =
|
descriptors[descr + 2] =
|
||||||
(&descriptors[descr + 3]) as *const _ as *const () as u32;
|
(&descriptors[descr + 3]) as *const _ as *const () as u32;
|
||||||
} else {
|
} else {
|
||||||
descriptors[descr + 2] = 0;
|
descriptors[descr + 2] = if circular {
|
||||||
|
descriptors.as_ptr() as *const () as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
processed += chunk_size;
|
processed += chunk_size;
|
||||||
@ -265,6 +285,10 @@ pub(crate) mod private {
|
|||||||
fn is_done(&mut self) -> bool {
|
fn is_done(&mut self) -> bool {
|
||||||
R::is_in_done()
|
R::is_in_done()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn last_in_dscr_address(&self) -> usize {
|
||||||
|
R::last_in_dscr_address()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChannelRx<'a, T, R>
|
pub struct ChannelRx<'a, T, R>
|
||||||
@ -275,6 +299,12 @@ pub(crate) mod private {
|
|||||||
pub descriptors: &'a mut [u32],
|
pub descriptors: &'a mut [u32],
|
||||||
pub burst_mode: bool,
|
pub burst_mode: bool,
|
||||||
pub rx_impl: T,
|
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<R>,
|
pub _phantom: PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,6 +319,7 @@ pub(crate) mod private {
|
|||||||
|
|
||||||
fn prepare_transfer(
|
fn prepare_transfer(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
circular: bool,
|
||||||
peri: DmaPeripheral,
|
peri: DmaPeripheral,
|
||||||
data: *mut u8,
|
data: *mut u8,
|
||||||
len: usize,
|
len: usize,
|
||||||
@ -297,7 +328,7 @@ pub(crate) mod private {
|
|||||||
return Err(DmaError::InvalidDescriptorSize);
|
return Err(DmaError::InvalidDescriptorSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.descriptors.len() / 3 < len / 4092 {
|
if self.descriptors.len() / 3 < len / CHUNK_SIZE {
|
||||||
return Err(DmaError::OutOfDescriptors);
|
return Err(DmaError::OutOfDescriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,8 +336,19 @@ pub(crate) mod private {
|
|||||||
return Err(DmaError::InvalidAlignment);
|
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
|
self.rx_impl
|
||||||
.prepare_transfer(self.descriptors, peri, data, len)?;
|
.prepare_transfer(self.descriptors, circular, peri, data, len)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,6 +359,118 @@ pub(crate) mod private {
|
|||||||
fn init_channel(&mut self) {
|
fn init_channel(&mut self) {
|
||||||
R::init_channel();
|
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<usize, super::DmaError> {
|
||||||
|
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
|
/// DMA Tx
|
||||||
@ -331,11 +485,16 @@ pub(crate) mod private {
|
|||||||
fn prepare_transfer(
|
fn prepare_transfer(
|
||||||
&mut self,
|
&mut self,
|
||||||
peri: DmaPeripheral,
|
peri: DmaPeripheral,
|
||||||
|
circular: bool,
|
||||||
data: *const u8,
|
data: *const u8,
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> Result<(), DmaError>;
|
) -> Result<(), DmaError>;
|
||||||
|
|
||||||
fn is_done(&mut self) -> bool;
|
fn is_done(&mut self) -> bool;
|
||||||
|
|
||||||
|
fn available(&mut self) -> usize;
|
||||||
|
|
||||||
|
fn push(&mut self, data: &[u8]) -> Result<usize, super::DmaError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TxChannel<R>
|
pub trait TxChannel<R>
|
||||||
@ -350,6 +509,7 @@ pub(crate) mod private {
|
|||||||
fn prepare_transfer(
|
fn prepare_transfer(
|
||||||
&mut self,
|
&mut self,
|
||||||
descriptors: &mut [u32],
|
descriptors: &mut [u32],
|
||||||
|
circular: bool,
|
||||||
peri: DmaPeripheral,
|
peri: DmaPeripheral,
|
||||||
data: *const u8,
|
data: *const u8,
|
||||||
len: usize,
|
len: usize,
|
||||||
@ -363,16 +523,14 @@ pub(crate) mod private {
|
|||||||
let mut processed = 0;
|
let mut processed = 0;
|
||||||
let mut descr = 0;
|
let mut descr = 0;
|
||||||
loop {
|
loop {
|
||||||
let chunk_size = usize::min(4092, len - processed);
|
let chunk_size = usize::min(CHUNK_SIZE, len - processed);
|
||||||
let last = processed + chunk_size >= len;
|
let last = processed + chunk_size >= len;
|
||||||
|
|
||||||
descriptors[descr + 1] = data as u32 + processed as u32;
|
descriptors[descr + 1] = data as u32 + processed as u32;
|
||||||
|
|
||||||
let mut dw0 = &mut descriptors[descr];
|
let mut dw0 = &mut descriptors[descr];
|
||||||
|
|
||||||
#[cfg(not(esp32))]
|
dw0.set_suc_eof(true);
|
||||||
dw0.set_suc_eof(last);
|
|
||||||
|
|
||||||
dw0.set_owner(Owner::Dma);
|
dw0.set_owner(Owner::Dma);
|
||||||
dw0.set_size(chunk_size as u16); // align to 32 bits?
|
dw0.set_size(chunk_size as u16); // align to 32 bits?
|
||||||
dw0.set_length(chunk_size as u16); // actual size of the data!?
|
dw0.set_length(chunk_size as u16); // actual size of the data!?
|
||||||
@ -381,7 +539,11 @@ pub(crate) mod private {
|
|||||||
descriptors[descr + 2] =
|
descriptors[descr + 2] =
|
||||||
(&descriptors[descr + 3]) as *const _ as *const () as u32;
|
(&descriptors[descr + 3]) as *const _ as *const () as u32;
|
||||||
} else {
|
} else {
|
||||||
descriptors[descr + 2] = 0;
|
if !circular {
|
||||||
|
descriptors[descr + 2] = 0;
|
||||||
|
} else {
|
||||||
|
descriptors[descr + 2] = descriptors.as_ptr() as u32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processed += chunk_size;
|
processed += chunk_size;
|
||||||
@ -408,6 +570,18 @@ pub(crate) mod private {
|
|||||||
fn is_done(&mut self) -> bool {
|
fn is_done(&mut self) -> bool {
|
||||||
R::is_out_done()
|
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>
|
pub struct ChannelTx<'a, T, R>
|
||||||
@ -419,6 +593,12 @@ pub(crate) mod private {
|
|||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub burst_mode: bool,
|
pub burst_mode: bool,
|
||||||
pub tx_impl: T,
|
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<R>,
|
pub _phantom: PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,6 +618,7 @@ pub(crate) mod private {
|
|||||||
fn prepare_transfer(
|
fn prepare_transfer(
|
||||||
&mut self,
|
&mut self,
|
||||||
peri: DmaPeripheral,
|
peri: DmaPeripheral,
|
||||||
|
circular: bool,
|
||||||
data: *const u8,
|
data: *const u8,
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> Result<(), DmaError> {
|
) -> Result<(), DmaError> {
|
||||||
@ -445,12 +626,23 @@ pub(crate) mod private {
|
|||||||
return Err(DmaError::InvalidDescriptorSize);
|
return Err(DmaError::InvalidDescriptorSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.descriptors.len() / 3 < len / 4092 {
|
if self.descriptors.len() / 3 < len / CHUNK_SIZE {
|
||||||
return Err(DmaError::OutOfDescriptors);
|
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
|
self.tx_impl
|
||||||
.prepare_transfer(self.descriptors, peri, data, len)?;
|
.prepare_transfer(self.descriptors, circular, peri, data, len)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -458,6 +650,113 @@ pub(crate) mod private {
|
|||||||
fn is_done(&mut self) -> bool {
|
fn is_done(&mut self) -> bool {
|
||||||
self.tx_impl.is_done()
|
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<usize, super::DmaError> {
|
||||||
|
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 {
|
pub trait RegisterAccess {
|
||||||
@ -471,6 +770,10 @@ pub(crate) mod private {
|
|||||||
fn set_out_peripheral(peripheral: u8);
|
fn set_out_peripheral(peripheral: u8);
|
||||||
fn start_out();
|
fn start_out();
|
||||||
fn is_out_done() -> bool;
|
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_burstmode(burst_mode: bool);
|
||||||
fn set_in_priority(priority: DmaPriority);
|
fn set_in_priority(priority: DmaPriority);
|
||||||
fn clear_in_interrupts();
|
fn clear_in_interrupts();
|
||||||
@ -480,6 +783,7 @@ pub(crate) mod private {
|
|||||||
fn set_in_peripheral(peripheral: u8);
|
fn set_in_peripheral(peripheral: u8);
|
||||||
fn start_in();
|
fn start_in();
|
||||||
fn is_in_done() -> bool;
|
fn is_in_done() -> bool;
|
||||||
|
fn last_in_dscr_address() -> usize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,24 @@ macro_rules! ImplSpiChannel {
|
|||||||
spi.dma_int_raw.read().out_done_int_raw().bit()
|
spi.dma_int_raw.read().out_done_int_raw().bit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn last_out_dscr_address() -> usize {
|
||||||
|
let spi = unsafe { &*crate::pac::[<SPI $num>]::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::[<SPI $num>]::PTR };
|
||||||
|
spi.dma_int_raw.read().out_eof_int_raw().bit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_out_eof_interrupt() {
|
||||||
|
let spi = unsafe { &*crate::pac::[<SPI $num>]::PTR };
|
||||||
|
spi.dma_int_clr.write(|w| {
|
||||||
|
w.out_eof_int_clr()
|
||||||
|
.set_bit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn set_in_burstmode(burst_mode: bool) {
|
fn set_in_burstmode(burst_mode: bool) {
|
||||||
let spi = unsafe { &*crate::pac::[<SPI $num>]::PTR };
|
let spi = unsafe { &*crate::pac::[<SPI $num>]::PTR };
|
||||||
spi.dma_conf
|
spi.dma_conf
|
||||||
@ -138,6 +156,11 @@ macro_rules! ImplSpiChannel {
|
|||||||
let spi = unsafe { &*crate::pac::[<SPI $num>]::PTR };
|
let spi = unsafe { &*crate::pac::[<SPI $num>]::PTR };
|
||||||
spi.dma_int_raw.read().in_done_int_raw().bit()
|
spi.dma_int_raw.read().in_done_int_raw().bit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn last_in_dscr_address() -> usize {
|
||||||
|
let spi = unsafe { &*crate::pac::[<SPI $num>]::PTR };
|
||||||
|
spi.inlink_dscr_bf0.read().dma_inlink_dscr_bf0().bits() as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct [<Spi $num DmaChannelTxImpl>] {}
|
pub struct [<Spi $num DmaChannelTxImpl>] {}
|
||||||
@ -169,6 +192,12 @@ macro_rules! ImplSpiChannel {
|
|||||||
descriptors: tx_descriptors,
|
descriptors: tx_descriptors,
|
||||||
burst_mode,
|
burst_mode,
|
||||||
tx_impl: tx_impl,
|
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(),
|
_phantom: PhantomData::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,6 +208,12 @@ macro_rules! ImplSpiChannel {
|
|||||||
descriptors: rx_descriptors,
|
descriptors: rx_descriptors,
|
||||||
burst_mode,
|
burst_mode,
|
||||||
rx_impl: rx_impl,
|
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(),
|
_phantom: PhantomData::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -193,7 +228,212 @@ macro_rules! ImplSpiChannel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crate private implementatin details
|
macro_rules! ImplI2sChannel {
|
||||||
|
($num: literal, $peripheral: literal) => {
|
||||||
|
paste::paste! {
|
||||||
|
pub struct [<I2s $num DmaChannel>] {}
|
||||||
|
|
||||||
|
impl RegisterAccess for [<I2s $num DmaChannel>] {
|
||||||
|
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 [<I2s $num DmaChannelTxImpl>] {}
|
||||||
|
|
||||||
|
impl<'a> TxChannel<[<I2s $num DmaChannel>]> for [<I2s $num DmaChannelTxImpl>] {}
|
||||||
|
|
||||||
|
pub struct [<I2s $num DmaChannelRxImpl>] {}
|
||||||
|
|
||||||
|
impl<'a> RxChannel<[<I2s $num DmaChannel>]> for [<I2s $num DmaChannelRxImpl>] {}
|
||||||
|
|
||||||
|
pub struct [<I2s $num DmaChannelCreator>] {}
|
||||||
|
|
||||||
|
impl [<I2s $num DmaChannelCreator>] {
|
||||||
|
pub fn configure<'a>(
|
||||||
|
self,
|
||||||
|
burst_mode: bool,
|
||||||
|
tx_descriptors: &'a mut [u32],
|
||||||
|
rx_descriptors: &'a mut [u32],
|
||||||
|
priority: DmaPriority,
|
||||||
|
) -> Channel<
|
||||||
|
ChannelTx<'a,[<I2s $num DmaChannelTxImpl>], [<I2s $num DmaChannel>]>,
|
||||||
|
ChannelRx<'a,[<I2s $num DmaChannelRxImpl>], [<I2s $num DmaChannel>]>,
|
||||||
|
[<I2s $num DmaSuitablePeripheral>],
|
||||||
|
> {
|
||||||
|
let mut tx_impl = [<I2s $num DmaChannelTxImpl>] {};
|
||||||
|
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 = [<I2s $num DmaChannelRxImpl>] {};
|
||||||
|
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(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Channel {
|
||||||
|
tx: tx_channel,
|
||||||
|
rx: rx_channel,
|
||||||
|
_phantom: PhantomData::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Crate private implementation details
|
||||||
pub(crate) mod private {
|
pub(crate) mod private {
|
||||||
use crate::dma::{private::*, *};
|
use crate::dma::{private::*, *};
|
||||||
|
|
||||||
@ -209,6 +449,25 @@ pub(crate) mod private {
|
|||||||
|
|
||||||
ImplSpiChannel!(2);
|
ImplSpiChannel!(2);
|
||||||
ImplSpiChannel!(3);
|
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
|
/// DMA Peripheral
|
||||||
@ -218,6 +477,9 @@ pub struct Dma {
|
|||||||
_inner: crate::system::Dma,
|
_inner: crate::system::Dma,
|
||||||
pub spi2channel: Spi2DmaChannelCreator,
|
pub spi2channel: Spi2DmaChannelCreator,
|
||||||
pub spi3channel: Spi3DmaChannelCreator,
|
pub spi3channel: Spi3DmaChannelCreator,
|
||||||
|
pub i2s0channel: I2s0DmaChannelCreator,
|
||||||
|
#[cfg(esp32)]
|
||||||
|
pub i2s1channel: I2s1DmaChannelCreator,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dma {
|
impl Dma {
|
||||||
@ -232,6 +494,9 @@ impl Dma {
|
|||||||
_inner: dma,
|
_inner: dma,
|
||||||
spi2channel: Spi2DmaChannelCreator {},
|
spi2channel: Spi2DmaChannelCreator {},
|
||||||
spi3channel: Spi3DmaChannelCreator {},
|
spi3channel: Spi3DmaChannelCreator {},
|
||||||
|
i2s0channel: I2s0DmaChannelCreator {},
|
||||||
|
#[cfg(esp32)]
|
||||||
|
i2s1channel: I2s1DmaChannelCreator {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ pub enum InputSignal {
|
|||||||
SUBSPID = 128,
|
SUBSPID = 128,
|
||||||
SUBSPIHD = 129,
|
SUBSPIHD = 129,
|
||||||
SUBSPIWP = 130,
|
SUBSPIWP = 130,
|
||||||
|
I2S0I_DATA_IN15 = 158,
|
||||||
SUBSPID4 = 167,
|
SUBSPID4 = 167,
|
||||||
SUBSPID5 = 168,
|
SUBSPID5 = 168,
|
||||||
SUBSPID6 = 169,
|
SUBSPID6 = 169,
|
||||||
@ -73,95 +74,97 @@ pub enum InputSignal {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum OutputSignal {
|
pub enum OutputSignal {
|
||||||
SPIQ = 0,
|
SPIQ = 0,
|
||||||
SPID = 1,
|
SPID = 1,
|
||||||
SPIHD = 2,
|
SPIHD = 2,
|
||||||
SPIWP = 3,
|
SPIWP = 3,
|
||||||
SPICLK = 4,
|
SPICLK = 4,
|
||||||
SPICS0 = 5,
|
SPICS0 = 5,
|
||||||
SPICS1 = 6,
|
SPICS1 = 6,
|
||||||
SPID4 = 7,
|
SPID4 = 7,
|
||||||
SPID5 = 8,
|
SPID5 = 8,
|
||||||
SPID6 = 9,
|
SPID6 = 9,
|
||||||
SPID7 = 10,
|
SPID7 = 10,
|
||||||
SPIDQS = 11,
|
SPIDQS = 11,
|
||||||
U0TXD = 14,
|
U0TXD = 14,
|
||||||
U0RTS = 15,
|
U0RTS = 15,
|
||||||
U0DTR = 16,
|
U0DTR = 16,
|
||||||
U1TXD = 17,
|
U1TXD = 17,
|
||||||
U1RTS = 18,
|
U1RTS = 18,
|
||||||
U1DTR = 21,
|
U1DTR = 21,
|
||||||
I2S0O_BCK = 23,
|
I2S0O_BCK = 23,
|
||||||
I2S0O_WS = 25,
|
I2S0O_WS = 25,
|
||||||
I2S0I_BCK = 27,
|
I2S0I_BCK = 27,
|
||||||
I2S0I_WS = 28,
|
I2S0I_WS = 28,
|
||||||
I2CEXT0_SCL = 29,
|
I2CEXT0_SCL = 29,
|
||||||
I2CEXT0_SDA = 30,
|
I2CEXT0_SDA = 30,
|
||||||
SDIO_TOHOST_INT = 31,
|
SDIO_TOHOST_INT = 31,
|
||||||
SPI3_CLK = 72,
|
SPI3_CLK = 72,
|
||||||
SPI3_Q = 73,
|
SPI3_Q = 73,
|
||||||
SPI3_D = 74,
|
SPI3_D = 74,
|
||||||
SPI3_HD = 75,
|
SPI3_HD = 75,
|
||||||
SPI3_CS0 = 76,
|
SPI3_CS0 = 76,
|
||||||
SPI3_CS1 = 77,
|
SPI3_CS1 = 77,
|
||||||
SPI3_CS2 = 78,
|
SPI3_CS2 = 78,
|
||||||
LEDC_LS_SIG0 = 79,
|
LEDC_LS_SIG0 = 79,
|
||||||
LEDC_LS_SIG1 = 80,
|
LEDC_LS_SIG1 = 80,
|
||||||
LEDC_LS_SIG2 = 81,
|
LEDC_LS_SIG2 = 81,
|
||||||
LEDC_LS_SIG3 = 82,
|
LEDC_LS_SIG3 = 82,
|
||||||
LEDC_LS_SIG4 = 83,
|
LEDC_LS_SIG4 = 83,
|
||||||
LEDC_LS_SIG5 = 84,
|
LEDC_LS_SIG5 = 84,
|
||||||
LEDC_LS_SIG6 = 85,
|
LEDC_LS_SIG6 = 85,
|
||||||
LEDC_LS_SIG7 = 86,
|
LEDC_LS_SIG7 = 86,
|
||||||
RMT_SIG_OUT0 = 87,
|
RMT_SIG_OUT0 = 87,
|
||||||
RMT_SIG_OUT1 = 88,
|
RMT_SIG_OUT1 = 88,
|
||||||
RMT_SIG_OUT2 = 89,
|
RMT_SIG_OUT2 = 89,
|
||||||
RMT_SIG_OUT3 = 90,
|
RMT_SIG_OUT3 = 90,
|
||||||
I2CEXT1_SCL = 95,
|
I2CEXT1_SCL = 95,
|
||||||
I2CEXT1_SDA = 96,
|
I2CEXT1_SDA = 96,
|
||||||
GPIO_SD0 = 100,
|
GPIO_SD0 = 100,
|
||||||
GPIO_SD1 = 101,
|
GPIO_SD1 = 101,
|
||||||
GPIO_SD2 = 102,
|
GPIO_SD2 = 102,
|
||||||
GPIO_SD3 = 103,
|
GPIO_SD3 = 103,
|
||||||
GPIO_SD4 = 104,
|
GPIO_SD4 = 104,
|
||||||
GPIO_SD5 = 105,
|
GPIO_SD5 = 105,
|
||||||
GPIO_SD6 = 106,
|
GPIO_SD6 = 106,
|
||||||
GPIO_SD7 = 107,
|
GPIO_SD7 = 107,
|
||||||
FSPICLK = 108,
|
FSPICLK = 108,
|
||||||
FSPIQ = 109,
|
FSPIQ = 109,
|
||||||
FSPID = 110,
|
FSPID = 110,
|
||||||
FSPIHD = 111,
|
FSPIHD = 111,
|
||||||
FSPIWP = 112,
|
FSPIWP = 112,
|
||||||
FSPIIO4 = 113,
|
FSPIIO4 = 113,
|
||||||
FSPIIO5 = 114,
|
FSPIIO5 = 114,
|
||||||
FSPIIO6 = 115,
|
FSPIIO6 = 115,
|
||||||
FSPIIO7 = 116,
|
FSPIIO7 = 116,
|
||||||
FSPICS0 = 117,
|
FSPICS0 = 117,
|
||||||
FSPICS1 = 118,
|
FSPICS1 = 118,
|
||||||
FSPICS2 = 119,
|
FSPICS2 = 119,
|
||||||
FSPICS3 = 120,
|
FSPICS3 = 120,
|
||||||
FSPICS4 = 121,
|
FSPICS4 = 121,
|
||||||
FSPICS5 = 122,
|
FSPICS5 = 122,
|
||||||
SUBSPICLK = 126,
|
SUBSPICLK = 126,
|
||||||
SUBSPIQ = 127,
|
SUBSPIQ = 127,
|
||||||
SUBSPID = 128,
|
SUBSPID = 128,
|
||||||
SUBSPIHD = 129,
|
SUBSPIHD = 129,
|
||||||
SUBSPIWP = 130,
|
SUBSPIWP = 130,
|
||||||
SUBSPICS0 = 131,
|
SUBSPICS0 = 131,
|
||||||
SUBSPICS1 = 132,
|
SUBSPICS1 = 132,
|
||||||
FSPIDQS = 133,
|
FSPIDQS = 133,
|
||||||
FSPI_HSYNC = 134,
|
FSPI_HSYNC = 134,
|
||||||
FSPI_VSYNC = 135,
|
FSPI_VSYNC = 135,
|
||||||
FSPI_DE = 136,
|
FSPI_DE = 136,
|
||||||
FSPICD = 137,
|
FSPICD = 137,
|
||||||
SPI3_CD = 139,
|
SPI3_CD = 139,
|
||||||
SPI3_DQS = 140,
|
SPI3_DQS = 140,
|
||||||
SUBSPID4 = 167,
|
I2S0O_DATA_OUT23 = 166,
|
||||||
SUBSPID5 = 168,
|
SUBSPID4 = 167,
|
||||||
SUBSPID6 = 169,
|
SUBSPID5 = 168,
|
||||||
SUBSPID7 = 170,
|
SUBSPID6 = 169,
|
||||||
SUBSPIDQS = 171,
|
SUBSPID7 = 170,
|
||||||
PCMFSYNC = 209,
|
SUBSPIDQS = 171,
|
||||||
PCMCLK = 210,
|
PCMFSYNC = 209,
|
||||||
GPIO = 256,
|
PCMCLK = 210,
|
||||||
|
CLK_I2S = 251,
|
||||||
|
GPIO = 256,
|
||||||
}
|
}
|
||||||
|
1725
esp-hal-common/src/i2s.rs
Normal file
1725
esp-hal-common/src/i2s.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -56,6 +56,10 @@ pub mod dma;
|
|||||||
pub mod embassy;
|
pub mod embassy;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
|
|
||||||
|
#[cfg(any(esp32c3, esp32s3, esp32, esp32s2))]
|
||||||
|
pub mod i2s;
|
||||||
|
|
||||||
pub mod ledc;
|
pub mod ledc;
|
||||||
#[cfg(usb_otg)]
|
#[cfg(usb_otg)]
|
||||||
pub mod otg_fs;
|
pub mod otg_fs;
|
||||||
|
@ -1000,8 +1000,18 @@ where
|
|||||||
self.enable_dma();
|
self.enable_dma();
|
||||||
self.update();
|
self.update();
|
||||||
|
|
||||||
tx.prepare_transfer(self.dma_peripheral(), write_buffer_ptr, write_buffer_len)?;
|
tx.prepare_transfer(
|
||||||
rx.prepare_transfer(self.dma_peripheral(), read_buffer_ptr, read_buffer_len)?;
|
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();
|
self.clear_dma_interrupts();
|
||||||
|
|
||||||
@ -1033,7 +1043,7 @@ where
|
|||||||
self.enable_dma();
|
self.enable_dma();
|
||||||
self.update();
|
self.update();
|
||||||
|
|
||||||
tx.prepare_transfer(self.dma_peripheral(), ptr, len)?;
|
tx.prepare_transfer(self.dma_peripheral(), false, ptr, len)?;
|
||||||
|
|
||||||
self.clear_dma_interrupts();
|
self.clear_dma_interrupts();
|
||||||
|
|
||||||
@ -1054,7 +1064,7 @@ where
|
|||||||
self.enable_dma();
|
self.enable_dma();
|
||||||
self.update();
|
self.update();
|
||||||
|
|
||||||
rx.prepare_transfer(self.dma_peripheral(), ptr, len)?;
|
rx.prepare_transfer(false, self.dma_peripheral(), ptr, len)?;
|
||||||
|
|
||||||
self.clear_dma_interrupts();
|
self.clear_dma_interrupts();
|
||||||
|
|
||||||
|
@ -34,6 +34,10 @@ pub enum Peripheral {
|
|||||||
Gdma,
|
Gdma,
|
||||||
#[cfg(pdma)]
|
#[cfg(pdma)]
|
||||||
Dma,
|
Dma,
|
||||||
|
#[cfg(not(esp32c2))]
|
||||||
|
I2s0,
|
||||||
|
#[cfg(not(any(esp32c2, esp32s2, esp32c3)))]
|
||||||
|
I2s1,
|
||||||
#[cfg(usb_otg)]
|
#[cfg(usb_otg)]
|
||||||
Usb,
|
Usb,
|
||||||
}
|
}
|
||||||
@ -122,6 +126,22 @@ impl PeripheralClockControl {
|
|||||||
perip_clk_en0.modify(|_, w| w.spi3_dma_clk_en().set_bit());
|
perip_clk_en0.modify(|_, w| w.spi3_dma_clk_en().set_bit());
|
||||||
perip_rst_en0.modify(|_, w| w.spi3_dma_rst().clear_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(usb_otg)]
|
#[cfg(usb_otg)]
|
||||||
Peripheral::Usb => {
|
Peripheral::Usb => {
|
||||||
perip_clk_en0.modify(|_, w| w.usb_clk_en().set_bit());
|
perip_clk_en0.modify(|_, w| w.usb_clk_en().set_bit());
|
||||||
|
94
esp32-hal/examples/i2s_read.rs
Normal file
94
esp32-hal/examples/i2s_read.rs
Normal file
@ -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 }
|
||||||
|
}
|
136
esp32-hal/examples/i2s_sound.rs
Normal file
136
esp32-hal/examples/i2s_sound.rs
Normal file
@ -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 }
|
||||||
|
}
|
@ -10,6 +10,7 @@ pub use esp_hal_common::{
|
|||||||
efuse,
|
efuse,
|
||||||
gpio as gpio_types,
|
gpio as gpio_types,
|
||||||
i2c,
|
i2c,
|
||||||
|
i2s,
|
||||||
interrupt,
|
interrupt,
|
||||||
ledc,
|
ledc,
|
||||||
macros,
|
macros,
|
||||||
|
101
esp32c3-hal/examples/i2s_read.rs
Normal file
101
esp32c3-hal/examples/i2s_read.rs
Normal file
@ -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 }
|
||||||
|
}
|
143
esp32c3-hal/examples/i2s_sound.rs
Normal file
143
esp32c3-hal/examples/i2s_sound.rs
Normal file
@ -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 }
|
||||||
|
}
|
@ -13,6 +13,7 @@ pub use esp_hal_common::{
|
|||||||
efuse,
|
efuse,
|
||||||
gpio as gpio_types,
|
gpio as gpio_types,
|
||||||
i2c,
|
i2c,
|
||||||
|
i2s,
|
||||||
interrupt,
|
interrupt,
|
||||||
ledc,
|
ledc,
|
||||||
macros,
|
macros,
|
||||||
|
94
esp32s2-hal/examples/i2s_read.rs
Normal file
94
esp32s2-hal/examples/i2s_read.rs
Normal file
@ -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 }
|
||||||
|
}
|
139
esp32s2-hal/examples/i2s_sound.rs
Normal file
139
esp32s2-hal/examples/i2s_sound.rs
Normal file
@ -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 }
|
||||||
|
}
|
@ -8,6 +8,7 @@ pub use esp_hal_common::{
|
|||||||
dma::pdma,
|
dma::pdma,
|
||||||
efuse,
|
efuse,
|
||||||
gpio as gpio_types,
|
gpio as gpio_types,
|
||||||
|
i2s,
|
||||||
i2c::{self, I2C},
|
i2c::{self, I2C},
|
||||||
interrupt,
|
interrupt,
|
||||||
ledc,
|
ledc,
|
||||||
|
101
esp32s3-hal/examples/i2s_read.rs
Normal file
101
esp32s3-hal/examples/i2s_read.rs
Normal file
@ -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 }
|
||||||
|
}
|
143
esp32s3-hal/examples/i2s_sound.rs
Normal file
143
esp32s3-hal/examples/i2s_sound.rs
Normal file
@ -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 }
|
||||||
|
}
|
@ -11,6 +11,7 @@ pub use esp_hal_common::{
|
|||||||
efuse,
|
efuse,
|
||||||
gpio as gpio_types,
|
gpio as gpio_types,
|
||||||
i2c,
|
i2c,
|
||||||
|
i2s,
|
||||||
interrupt,
|
interrupt,
|
||||||
ledc,
|
ledc,
|
||||||
macros,
|
macros,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user