esp-idf-hal/src/i2s.rs
ivmarkov 8cc193aa35
TWAI driver async hack; examples cleanup (#292)
* Hacky async TWAI driver

* Async TWAI: Custom priority and pin to core

* Async TWAI: Proper configuration of alerts

* Do not refer to esp-idf-sys as it is not an explicit dependency anymore
2023-08-27 10:47:18 +03:00

1241 lines
41 KiB
Rust

//! Driver for the Inter-IC Sound (I2S) peripheral(s).
use core::{ffi::c_void, marker::PhantomData, mem::MaybeUninit};
use esp_idf_sys::{esp, i2s_port_t, EspError, TickType_t};
#[cfg(not(esp_idf_version_major = "4"))]
use {
core::ptr::null_mut,
esp_idf_sys::{
i2s_chan_config_t, i2s_chan_handle_t, i2s_channel_disable, i2s_channel_enable,
i2s_channel_read, i2s_channel_register_event_callback, i2s_channel_write, i2s_del_channel,
i2s_event_callbacks_t, i2s_event_data_t, i2s_new_channel,
},
};
#[cfg(esp_idf_version_major = "4")]
use esp_idf_sys::{
i2s_config_t, i2s_driver_install, i2s_driver_uninstall, i2s_read, i2s_start, i2s_stop,
i2s_write,
};
#[cfg(not(esp_idf_version_major = "4"))]
use crate::private::notification::Notification;
// For v5+, we rely configuration options for PDM/TDM support.
// For v4, we have to examine the chip type.
#[cfg(any(
all(
not(esp_idf_version_major = "4"),
any(esp_idf_soc_i2s_supports_pdm_rx, esp_idf_soc_i2s_supports_pdm_tx)
),
all(esp_idf_version_major = "4", any(esp32, esp32s3, esp32c3, esp32c6))
))]
mod pdm;
mod std;
#[cfg(any(
all(not(esp_idf_version_major = "4"), esp_idf_soc_i2s_supports_tdm),
all(esp_idf_version_major = "4", any(esp32s3, esp32c3, esp32c6))
))]
mod tdm;
pub type I2sConfig = config::Config;
/// I2S configuration
pub mod config {
#[cfg(any(
all(
not(esp_idf_version_major = "4"),
any(esp_idf_soc_i2s_supports_pdm_rx, esp_idf_soc_i2s_supports_pdm_tx)
),
all(esp_idf_version_major = "4", any(esp32, esp32s3, esp32c3, esp32c6))
))]
pub use super::pdm::config::*;
pub use super::std::config::*;
#[cfg(any(
all(not(esp_idf_version_major = "4"), esp_idf_soc_i2s_supports_tdm),
all(esp_idf_version_major = "4", any(esp32s3, esp32c3, esp32c6))
))]
pub use super::tdm::config::*;
use core::convert::TryFrom;
use esp_idf_sys::{
i2s_mclk_multiple_t, i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_128,
i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_256, i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_384,
EspError, ESP_ERR_INVALID_ARG,
};
#[cfg(not(esp_idf_version_major = "4"))]
use esp_idf_sys::{
i2s_chan_config_t, i2s_clock_src_t, i2s_data_bit_width_t,
i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_512, i2s_port_t, i2s_role_t, i2s_slot_bit_width_t,
i2s_slot_mode_t,
};
#[cfg(esp_idf_version_major = "4")]
use esp_idf_sys::{
i2s_bits_per_chan_t, i2s_bits_per_sample_t, i2s_mode_t, i2s_mode_t_I2S_MODE_MASTER,
i2s_mode_t_I2S_MODE_SLAVE,
};
/// I2S clock source
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum ClockSource {
/// Use PLL_F160M as the source clock
Pll160M,
/// Use APLL as the source clock
#[cfg(any(esp32, esp32s2))]
Apll,
}
impl Default for ClockSource {
#[inline(always)]
fn default() -> Self {
Self::Pll160M
}
}
impl ClockSource {
#[cfg(not(esp_idf_version_major = "4"))]
#[allow(clippy::unnecessary_cast)]
pub(super) fn as_sdk(&self) -> i2s_clock_src_t {
match self {
Self::Pll160M => core::convert::TryInto::try_into(
esp_idf_sys::soc_module_clk_t_SOC_MOD_CLK_PLL_F160M,
)
.unwrap(),
#[cfg(any(esp32, esp32s2))]
Self::Apll => esp_idf_sys::soc_module_clk_t_SOC_MOD_CLK_APLL,
}
}
}
/// I2S controller channel configuration.
///
/// To create a custom configuration, use the builder pattern built-in to this struct. For example:
/// ```
/// use esp_idf_hal::i2s::config::{ChannelOpen, Config, Role};
/// let config = Config::default().role(Role::Target).channels(ChannelOpen::Rx);
/// ```
///
/// The default configuration is:
/// * Role ([Config::role]): [Role::Controller] (master)
/// * DMA buffer number/descriptor number ([Config::dma_desc]): 6
/// * I2S frames in one DMA buffer ([Config::dma_frame]): 240
/// * Auto clear ([Config::auto_clear]): false
#[derive(Clone)]
pub struct Config {
/// The role of this channel: controller (master) or target (slave)
pub(super) role: Role,
/// The DMA buffer number to use (also the DMA descriptor number).
pub(super) dma_desc: u32,
/// The number of I2S frames in one DMA buffer.
pub(super) frames: u32,
/// If true, the transmit buffer will be automatically cleared upon sending.
pub(super) auto_clear: bool,
}
impl Default for Config {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl Config {
#[inline(always)]
/// Create a new Config
pub const fn new() -> Self {
Self {
role: Role::Controller,
dma_desc: 6,
frames: 240,
auto_clear: false,
}
}
/// Set the role of this channel: controller (master) or target (slave)
#[must_use]
#[inline(always)]
pub fn role(mut self, role: Role) -> Self {
self.role = role;
self
}
/// Set the DMA buffer to use.
#[must_use]
#[inline(always)]
pub fn dma_desc(mut self, dma_desc: u32) -> Self {
self.dma_desc = dma_desc;
self
}
/// Set the number of I2S frames in one DMA buffer.
#[must_use]
#[inline(always)]
pub fn frames(mut self, frames: u32) -> Self {
self.frames = frames;
self
}
/// Set if the transmit buffer will be automatically cleared upon sending.
#[must_use]
#[inline(always)]
pub fn auto_clear(mut self, auto_clear: bool) -> Self {
self.auto_clear = auto_clear;
self
}
/// Convert to the ESP-IDF SDK `i2s_chan_config_t` representation.
#[cfg(not(esp_idf_version_major = "4"))]
#[inline(always)]
pub(super) fn as_sdk(&self, id: i2s_port_t) -> i2s_chan_config_t {
i2s_chan_config_t {
id,
role: self.role.as_sdk(),
dma_desc_num: self.dma_desc,
dma_frame_num: self.frames,
auto_clear: self.auto_clear,
}
}
}
/// Available data bit width in one slot.
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub enum DataBitWidth {
/// Channel data bit width is 8 bits.
Bits8,
/// Channel data bit width is 16 bits.
Bits16,
/// Channel data bit width is 24 bits.
Bits24,
/// Channel data bit width is 32 bits.
Bits32,
}
impl From<DataBitWidth> for u32 {
#[inline(always)]
fn from(value: DataBitWidth) -> Self {
match value {
DataBitWidth::Bits8 => 8,
DataBitWidth::Bits16 => 16,
DataBitWidth::Bits24 => 24,
DataBitWidth::Bits32 => 32,
}
}
}
impl DataBitWidth {
/// Convert to the ESP-IDF SDK `i2s_data_bit_width_t` representation.
#[cfg(not(esp_idf_version_major = "4"))]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_data_bit_width_t {
match self {
Self::Bits8 => 8,
Self::Bits16 => 16,
Self::Bits24 => 24,
Self::Bits32 => 32,
}
}
/// Convert to the ESP-IDF SDK `i2s_bits_per_sample_t` representation.
#[cfg(esp_idf_version_major = "4")]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_bits_per_sample_t {
match self {
Self::Bits8 => 8,
Self::Bits16 => 16,
Self::Bits24 => 24,
Self::Bits32 => 32,
}
}
}
impl TryFrom<usize> for DataBitWidth {
type Error = EspError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
match value {
8 => Ok(Self::Bits8),
16 => Ok(Self::Bits16),
24 => Ok(Self::Bits24),
32 => Ok(Self::Bits32),
_ => Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()),
}
}
}
/// The multiple of MCLK to the sample rate.
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum MclkMultiple {
/// MCLK = sample rate * 128
M128,
/// MCLK = sample rate * 256
M256,
/// MCLK = sample rate * 384
M384,
/// MCLK = sample rate * 512
#[cfg(not(esp_idf_version_major = "4"))]
M512,
}
impl MclkMultiple {
/// Convert to the ESP-IDF SDK `i2s_mclk_multiple_t` representation.
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_mclk_multiple_t {
match self {
Self::M128 => i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_128,
Self::M256 => i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_256,
Self::M384 => i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_384,
#[cfg(not(esp_idf_version_major = "4"))]
Self::M512 => i2s_mclk_multiple_t_I2S_MCLK_MULTIPLE_512,
}
}
}
impl From<MclkMultiple> for u32 {
#[inline(always)]
fn from(mclk_multiple: MclkMultiple) -> Self {
match mclk_multiple {
MclkMultiple::M128 => 128,
MclkMultiple::M256 => 256,
MclkMultiple::M384 => 384,
#[cfg(not(esp_idf_version_major = "4"))]
MclkMultiple::M512 => 512,
}
}
}
/// I2S channel operating role
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Role {
/// Controller (master)
Controller,
/// Target (slave)
Target,
}
impl Default for Role {
#[inline(always)]
fn default() -> Self {
Self::Controller
}
}
/// I2S peripheral in controller (master) role, bclk and ws signal will be set to output.
#[cfg(not(esp_idf_version_major = "4"))]
const I2S_ROLE_CONTROLLER: i2s_role_t = 0;
/// I2S peripheral in target (slave) role, bclk and ws signal will be set to input.
#[cfg(not(esp_idf_version_major = "4"))]
const I2S_ROLE_TARGET: i2s_role_t = 1;
impl Role {
/// Convert to the ESP-IDF SDK `i2s_role_t` representation.
#[cfg(not(esp_idf_version_major = "4"))]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_role_t {
match self {
Self::Controller => I2S_ROLE_CONTROLLER,
Self::Target => I2S_ROLE_TARGET,
}
}
/// Convert to the ESP-IDF SDK `i2s_mode_t` representation.
#[cfg(esp_idf_version_major = "4")]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_mode_t {
match self {
Self::Controller => i2s_mode_t_I2S_MODE_MASTER,
Self::Target => i2s_mode_t_I2S_MODE_SLAVE,
}
}
}
/// The total slot bit width in one slot.
///
/// This is not necessarily the number of data bits in one slot. A slot may have additional bits padded
/// to fill out the slot.
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum SlotBitWidth {
/// Slot bit width is automatically set to the data bit width.
Auto,
/// Slot bit width is 8 bits.
Bits8,
/// Slot bit width is 16 bits.
Bits16,
/// Slot bit width is 24 bits.
Bits24,
/// Slot bit width is 32 bits.
Bits32,
}
impl Default for SlotBitWidth {
#[inline(always)]
fn default() -> Self {
Self::Auto
}
}
#[cfg(not(esp_idf_version_major = "4"))]
type SlotBitWidthSdkType = i2s_slot_bit_width_t;
#[cfg(esp_idf_version_major = "4")]
type SlotBitWidthSdkType = i2s_bits_per_chan_t;
impl SlotBitWidth {
/// Convert this to the ESP-IDF SDK `i2s_slot_bit_width_t`/`i2s_bits_per_chan_t` representation.
#[inline(always)]
pub(super) fn as_sdk(&self) -> SlotBitWidthSdkType {
match self {
Self::Auto => 0,
Self::Bits8 => 8,
Self::Bits16 => 16,
Self::Bits24 => 24,
Self::Bits32 => 32,
}
}
}
impl TryFrom<u32> for SlotBitWidth {
type Error = EspError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Auto),
8 => Ok(Self::Bits8),
16 => Ok(Self::Bits16),
24 => Ok(Self::Bits24),
32 => Ok(Self::Bits32),
_ => Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()),
}
}
}
/// I2S channel slot mode.
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum SlotMode {
/// Mono mode:
/// * When transmitting, transmit the same data in all slots.
/// * When receiving, only receive data from the first slot.
Mono,
/// Stereo mode:
/// * When transmitting, transmit different data in each slot.
/// * When receiving, receive data from all slots.
Stereo,
}
impl Default for SlotMode {
#[inline(always)]
fn default() -> Self {
Self::Stereo
}
}
impl SlotMode {
/// Convert this to the ESP-IDF SDK `i2s_slot_mode_t` representation.
#[cfg(not(esp_idf_version_major = "4"))]
#[inline(always)]
pub(super) fn as_sdk(&self) -> i2s_slot_mode_t {
match self {
Self::Mono => 1,
Self::Stereo => 2,
}
}
}
}
pub trait I2s: Send {
fn port() -> i2s_port_t;
}
pub trait I2sPort {
/// Returns the I2S port number of this driver.
fn port(&self) -> i2s_port_t;
}
/// Functions for receive channels.
/// Marker trait indicating that a driver supports the [I2sRx] trait.
pub trait I2sRxSupported {}
/// Concrete implementation of [I2sRxSupported] for use in clients.
///
/// Example usage:
/// ```
/// use esp_idf_hal::i2s::{config::{StdModeConfig, DataBitWidth}, gpio::*};
/// let std_config = StdModeConfig::philips(48000, DataBitWidth::Bits16);
/// let periperhals = Peripherals::take().unwrap();
/// let bclk = peripherals.pins.gpio1;
/// let din = peripherals.pins.gpio4;
/// let mclk = AnyIOPin::none();
/// let ws = peripherals.pins.gpio2;
/// let i2s = I2sStdModeDriver::<I2sRx>::new_rx(periperhals.i2s0, std_config, bclk, Some(din), mclk, ws, None).unwrap();
/// ```
pub struct I2sRx {}
impl I2sRxSupported for I2sRx {}
/// Marker trait indicating that a driver supports the [I2sTx] trait.
pub trait I2sTxSupported {}
/// Concrete implementation of [I2sTxSupported] for use in clients.
///
/// Example usage:
/// ```
/// use esp_idf_hal::i2s::{config::{StdModeConfig, DataBitWidth}, gpio::*};
/// let std_config = StdModeConfig::philips(48000, DataBitWidth::Bits16);
/// let periperhals = Peripherals::take().unwrap();
/// let bclk = peripherals.pins.gpio1;
/// let dout = peripherals.pins.gpio6;
/// let mclk = AnyIOPin::none();
/// let ws = peripherals.pins.gpio2;
/// let i2s = I2sStdModeDriver::<I2sTx>::new_tx(periperhals.i2s0, std_config, bclk, Some(dout), mclk, ws, None).unwrap();
/// ```
pub struct I2sTx {}
impl I2sTxSupported for I2sTx {}
/// Concrete implementation of both [I2sRxSupported] and [I2sTxSupported] for use in clients.
///
/// Example usage:
/// ```
/// use esp_idf_hal::i2s::{config::{StdModeConfig, DataBitWidth}, gpio::*, peripherals::Peripherals};
/// let std_config = StdModeConfig::philips(48000, DataBitWidth::Bits16);
/// let periperhals = Peripherals::take().unwrap();
/// let bclk = peripherals.pins.gpio1;
/// let din = peripherals.pins.gpio4;
/// let dout = peripherals.pins.gpio6;
/// let mclk = AnyIOPin::none();
/// let ws = peripherals.pins.gpio2;
/// let i2s = I2sStdModeDriver::<I2sBiDir>::new_bidir(periperhals.i2s0, std_config, bclk, Some(din), Some(dout), mclk, ws, None, None).unwrap();
/// ```
pub struct I2sBiDir {}
impl I2sRxSupported for I2sBiDir {}
impl I2sTxSupported for I2sBiDir {}
/// The I2S driver.
pub struct I2sDriver<'d, Dir> {
/// The Rx channel, possibly null.
#[cfg(not(esp_idf_version_major = "4"))]
rx_handle: i2s_chan_handle_t,
/// The Tx channel, possibly null.
#[cfg(not(esp_idf_version_major = "4"))]
tx_handle: i2s_chan_handle_t,
/// The I2S peripheral number. Either 0 or 1 (ESP32 and ESP32S3 only).
port: u8,
/// Driver lifetime -- mimics the lifetime of the peripheral.
_p: PhantomData<&'d ()>,
/// Directionality -- mimics the directionality of the peripheral.
_dir: PhantomData<Dir>,
}
impl<'d, Dir> I2sDriver<'d, Dir> {
/// Create a new standard mode driver for the given I2S peripheral with both the receive and transmit channels open.
#[cfg(not(esp_idf_version_major = "4"))]
fn internal_new<I2S: I2s>(
config: &i2s_chan_config_t,
rx: bool,
tx: bool,
) -> Result<Self, EspError> {
let port = I2S::port();
let mut rx_handle: i2s_chan_handle_t = null_mut();
let mut tx_handle: i2s_chan_handle_t = null_mut();
unsafe {
esp!(i2s_new_channel(
config,
if tx {
&mut tx_handle as _
} else {
core::ptr::null_mut()
},
if rx {
&mut rx_handle as _
} else {
core::ptr::null_mut()
},
))?
};
let mut this = Self {
port: port as u8,
rx_handle,
tx_handle,
_p: PhantomData,
_dir: PhantomData,
};
this.subscribe_channel(this.rx_handle)?;
this.subscribe_channel(this.tx_handle)?;
Ok(this)
}
#[cfg(esp_idf_version_major = "4")]
#[allow(clippy::too_many_arguments)]
pub fn internal_new<I2S: I2s>(config: &i2s_config_t) -> Result<Self, EspError> {
let port = I2S::port();
unsafe {
esp!(i2s_driver_install(port, config, 0, core::ptr::null_mut()))?;
}
Ok(Self {
port: port as u8,
_p: PhantomData,
_dir: PhantomData,
})
}
#[cfg(not(esp_idf_version_major = "4"))]
fn subscribe_channel(&mut self, handle: i2s_chan_handle_t) -> Result<(), EspError> {
if !handle.is_null() {
let callbacks = i2s_event_callbacks_t {
on_recv: Some(dispatch_recv),
on_recv_q_ovf: Some(dispatch_recv),
on_sent: Some(dispatch_send),
on_send_q_ovf: Some(dispatch_send),
};
// Safety: chan_handle is a valid pointer to an i2s_chan_handle_t and callbacks is initialized.
esp!(unsafe {
i2s_channel_register_event_callback(
handle,
&callbacks,
self.port as u32 as *mut core::ffi::c_void,
)
})?;
}
Ok(())
}
#[cfg(not(esp_idf_version_major = "4"))]
fn unsubscribe_channel(&mut self, handle: i2s_chan_handle_t) -> Result<(), EspError> {
if !handle.is_null() {
let callbacks = i2s_event_callbacks_t {
on_recv: None,
on_recv_q_ovf: None,
on_sent: None,
on_send_q_ovf: None,
};
// Safety: chan_handle is a valid pointer to an i2s_chan_handle_t and callbacks is initialized.
esp!(unsafe {
i2s_channel_register_event_callback(
handle,
&callbacks,
self.port as u32 as *mut core::ffi::c_void,
)
})?;
}
Ok(())
}
#[cfg(not(esp_idf_version_major = "4"))]
fn del_channel(&mut self, handle: i2s_chan_handle_t) -> Result<(), EspError> {
if !handle.is_null() {
let callbacks = i2s_event_callbacks_t {
on_recv: None,
on_recv_q_ovf: None,
on_sent: None,
on_send_q_ovf: None,
};
// Safety: chan_handle is a valid pointer to an i2s_chan_handle_t and callbacks is initialized.
esp!(unsafe {
i2s_channel_register_event_callback(handle, &callbacks, core::ptr::null_mut())
})?;
// Safety: chan_handle is a valid, non-null i2s_chan_handle_t.
esp!(unsafe { i2s_del_channel(handle) })?;
}
Ok(())
}
fn remap_result(
result: Result<(), EspError>,
bytes_processed: usize,
) -> Result<usize, EspError> {
match result {
Ok(_) => Ok(bytes_processed),
Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT && bytes_processed > 0 => {
Ok(bytes_processed)
}
Err(err) => Err(err),
}
}
}
/// Functions for receive channels.
impl<'d, Dir> I2sDriver<'d, Dir>
where
Dir: I2sRxSupported,
{
/// Enable the I2S receive channel.
///
/// # Note
/// This can only be called when the channel is in the `READY` state: initialized but not yet started from a driver
/// constructor, or disabled from the `RUNNING` state via [I2sRxChannel::rx_disable]. The channel will enter the
/// `RUNNING` state if it is enabled successfully.
///
/// Enabling the channel will start I2S communications on the hardware. BCLK and WS signals will be generated if
/// this is a controller. MCLK will be generated once initialization is finished.
///
/// # Errors
/// This will return an [EspError] with `ESP_ERR_INVALID_STATE` if the channel is not in the `READY` state.
#[cfg(esp_idf_version_major = "4")]
pub fn rx_enable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_start(self.port as _)) }
}
/// Enable the I2S receive channel.
///
/// # Note
/// This can only be called when the channel is in the `READY` state: initialized but not yet started from a driver
/// constructor, or disabled from the `RUNNING` state via [I2sRxChannel::rx_disable]. The channel will enter the
/// `RUNNING` state if it is enabled successfully.
///
/// Enabling the channel will start I2S communications on the hardware. BCLK and WS signals will be generated if
/// this is a controller. MCLK will be generated once initialization is finished.
///
/// # Errors
/// This will return an [EspError] with `ESP_ERR_INVALID_STATE` if the channel is not in the `READY` state.
#[cfg(not(esp_idf_version_major = "4"))]
pub fn rx_enable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_channel_enable(self.rx_handle)) }
}
/// Disable the I2S receive channel.
///
/// # Note
/// This can only be called when the channel is in the `RUNNING` state: the channel has been previously enabled
/// via a call to [I2sRxChannel::rx_enable]. The channel will enter the `READY` state if it is disabled
/// successfully.
///
/// Disabling the channel will stop I2S communications on the hardware. BCLK and WS signals will stop being
/// generated if this is a controller. MCLK will continue to be generated.
///
/// # Errors
/// This will return an [EspError] with `ESP_ERR_INVALID_STATE` if the channel is not in the `RUNNING` state.
#[cfg(esp_idf_version_major = "4")]
pub fn rx_disable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_stop(self.port as _)) }
}
/// Disable the I2S receive channel.
///
/// # Note
/// This can only be called when the channel is in the `RUNNING` state: the channel has been previously enabled
/// via a call to [I2sRxChannel::rx_enable]. The channel will enter the `READY` state if it is disabled
/// successfully.
///
/// Disabling the channel will stop I2S communications on the hardware. BCLK and WS signals will stop being
/// generated if this is a controller. MCLK will continue to be generated.
///
/// # Errors
/// This will return an [EspError] with `ESP_ERR_INVALID_STATE` if the channel is not in the `RUNNING` state.
#[cfg(not(esp_idf_version_major = "4"))]
pub fn rx_disable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_channel_disable(self.rx_handle)) }
}
/// Read data from the channel asynchronously.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes read, or an [EspError] if an error occurred.
#[cfg(not(esp_idf_version_major = "4"))]
pub async fn read_async(&mut self, buffer: &mut [u8]) -> Result<usize, EspError> {
loop {
match self.read(buffer, crate::delay::NON_BLOCK) {
Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => {
RECV_NOTIFIER[self.port as usize].wait().await;
}
other => break other,
}
}
}
/// Read data from the channel.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes read, or an [EspError] if an error occurred.
#[cfg(esp_idf_version_major = "4")]
pub fn read(&mut self, buffer: &mut [u8], timeout: TickType_t) -> Result<usize, EspError> {
if buffer.is_empty() {
Ok(0)
} else {
let mut bytes_read: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_read(
self.port as _,
buffer.as_mut_ptr() as *mut c_void,
buffer.len(),
&mut bytes_read,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_read,
)
}
}
/// Read data from the channel.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes read, or an [EspError] if an error occurred.
#[cfg(not(esp_idf_version_major = "4"))]
pub fn read(&mut self, buffer: &mut [u8], timeout: TickType_t) -> Result<usize, EspError> {
if buffer.is_empty() {
Ok(0)
} else {
let mut bytes_read: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_channel_read(
self.rx_handle,
buffer.as_mut_ptr() as *mut c_void,
buffer.len(),
&mut bytes_read,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_read,
)
}
}
/// Read data from the channel into an uninitalized buffer asynchronously.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes read, or an [EspError] if an error occurred.
///
/// # Safety
/// Upon a successful return with `Ok(n_read)`, `buffer[..n_read]` will be initialized.
#[cfg(not(esp_idf_version_major = "4"))]
pub async fn read_uninit_async(
&mut self,
buffer: &mut [MaybeUninit<u8>],
) -> Result<usize, EspError> {
loop {
match self.read_uninit(buffer, crate::delay::NON_BLOCK) {
Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => {
RECV_NOTIFIER[self.port as usize].wait().await;
}
other => break other,
}
}
}
/// Read data from the channel into an uninitalized buffer.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes read, or an [EspError] if an error occurred.
///
/// # Safety
/// Upon a successful return with `Ok(n_read)`, `buffer[..n_read]` will be initialized.
#[cfg(esp_idf_version_major = "4")]
pub fn read_uninit(
&mut self,
buffer: &mut [MaybeUninit<u8>],
timeout: TickType_t,
) -> Result<usize, EspError> {
if buffer.is_empty() {
Ok(0)
} else {
let mut bytes_read: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_read(
self.port as _,
buffer.as_mut_ptr() as *mut c_void,
buffer.len(),
&mut bytes_read,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_read,
)
}
}
/// Read data from the channel into an uninitalized buffer.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes read, or an [EspError] if an error occurred.
///
/// # Safety
/// Upon a successful return with `Ok(n_read)`, `buffer[..n_read]` will be initialized.
#[cfg(not(esp_idf_version_major = "4"))]
pub fn read_uninit(
&mut self,
buffer: &mut [MaybeUninit<u8>],
timeout: TickType_t,
) -> Result<usize, EspError> {
if buffer.is_empty() {
Ok(0)
} else {
let mut bytes_read: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_channel_read(
self.rx_handle,
buffer.as_mut_ptr() as *mut c_void,
buffer.len(),
&mut bytes_read,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_read,
)
}
}
}
/// Functions for transmit channels.
impl<'d, Dir> I2sDriver<'d, Dir>
where
Dir: I2sTxSupported,
{
/// Enable the I2S transmit channel.
///
/// # Note
/// This can only be called when the channel is in the `READY` state: initialized but not yet started from a driver
/// constructor, or disabled from the `RUNNING` state via [I2sTxChannel::tx_disable]. The channel will enter the
/// `RUNNING` state if it is enabled successfully.
///
/// Enabling the channel will start I2S communications on the hardware. BCLK and WS signals will be generated if
/// this is a controller. MCLK will be generated once initialization is finished.
///
/// # Errors
/// This will return an [EspError] with `ESP_ERR_INVALID_STATE` if the channel is not in the `READY` state.
#[cfg(esp_idf_version_major = "4")]
pub fn tx_enable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_start(self.port as _)) }
}
/// Enable the I2S transmit channel.
///
/// # Note
/// This can only be called when the channel is in the `READY` state: initialized but not yet started from a driver
/// constructor, or disabled from the `RUNNING` state via [I2sTxChannel::tx_disable]. The channel will enter the
/// `RUNNING` state if it is enabled successfully.
///
/// Enabling the channel will start I2S communications on the hardware. BCLK and WS signals will be generated if
/// this is a controller. MCLK will be generated once initialization is finished.
///
/// # Errors
/// This will return an [EspError] with `ESP_ERR_INVALID_STATE` if the channel is not in the `READY` state.
#[cfg(not(esp_idf_version_major = "4"))]
pub fn tx_enable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_channel_enable(self.tx_handle)) }
}
/// Disable the I2S transmit channel.
///
/// # Note
/// This can only be called when the channel is in the `RUNNING` state: the channel has been previously enabled
/// via a call to [I2sTxChannel::tx_enable]. The channel will enter the `READY` state if it is disabled
/// successfully.
///
/// Disabling the channel will stop I2S communications on the hardware. BCLK and WS signals will stop being
/// generated if this is a controller. MCLK will continue to be generated.
///
/// # Errors
/// This will return an [EspError] with `ESP_ERR_INVALID_STATE` if the channel is not in the `RUNNING` state.
#[cfg(esp_idf_version_major = "4")]
pub fn tx_disable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_stop(self.port())) }
}
/// Disable the I2S transmit channel.
///
/// # Note
/// This can only be called when the channel is in the `RUNNING` state: the channel has been previously enabled
/// via a call to [I2sTxChannel::tx_enable]. The channel will enter the `READY` state if it is disabled
/// successfully.
///
/// Disabling the channel will stop I2S communications on the hardware. BCLK and WS signals will stop being
/// generated if this is a controller. MCLK will continue to be generated.
///
/// # Errors
/// This will return an [EspError] with `ESP_ERR_INVALID_STATE` if the channel is not in the `RUNNING` state.
#[cfg(not(esp_idf_version_major = "4"))]
pub fn tx_disable(&mut self) -> Result<(), EspError> {
unsafe { esp!(i2s_channel_disable(self.tx_handle)) }
}
/// Preload data into the transmit channel DMA buffer.
///
/// This may be called only when the channel is in the `READY` state: initialized but not yet started.
///
/// This is used to preload data into the DMA buffer so that valid data can be transmitted immediately after the
/// channel is enabled via [I2sTxChannel::tx_enable]. If this function is not called before enabling the channel,
/// empty data will be transmitted.
///
/// This function can be called multiple times before enabling the channel. Additional calls will concatenate the
/// data to the end of the buffer until the buffer is full.
///
/// # Returns
/// This returns the number of bytes that have been loaded into the buffer. If this is less than the length of
/// the data provided, the buffer is full and no more data can be loaded.
#[cfg(all(
not(esp_idf_version_major = "4"),
not(all(esp_idf_version_major = "5", esp_idf_version_minor = "0"))
))]
pub fn preload_data(&mut self, data: &[u8]) -> Result<usize, EspError> {
let mut bytes_loaded: usize = 0;
unsafe {
esp!(esp_idf_sys::i2s_channel_preload_data(
self.tx_handle,
data.as_ptr() as *const c_void,
data.len(),
&mut bytes_loaded as *mut usize
))?;
}
Ok(bytes_loaded)
}
/// Write data to the channel asynchronously.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes sent. This may be less than the length of the data provided.
#[cfg(not(esp_idf_version_major = "4"))]
pub async fn write_async(&mut self, data: &[u8]) -> Result<usize, EspError> {
loop {
match self.write(data, crate::delay::NON_BLOCK) {
Err(err) if err.code() == esp_idf_sys::ESP_ERR_TIMEOUT => {
SEND_NOTIFIER[self.port as usize].wait().await;
}
other => break other,
}
}
}
/// Write all data to the channel asynchronously.
///
/// This may be called only when the channel is in the `RUNNING` state.
#[cfg(not(esp_idf_version_major = "4"))]
pub async fn write_all_async(&mut self, data: &[u8]) -> Result<(), EspError> {
let mut offset = 0;
while offset < data.len() {
offset += self.write_async(&data[offset..]).await?;
}
Ok(())
}
/// Write data to the channel.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes sent. This may be less than the length of the data provided.
#[cfg(esp_idf_version_major = "4")]
pub fn write(&mut self, data: &[u8], timeout: TickType_t) -> Result<usize, EspError> {
if data.is_empty() {
Ok(0)
} else {
let mut bytes_written: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_write(
self.port(),
data.as_ptr() as *mut c_void,
data.len(),
&mut bytes_written,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_written,
)
}
}
/// Write data to the channel.
///
/// This may be called only when the channel is in the `RUNNING` state.
///
/// # Returns
/// This returns the number of bytes sent. This may be less than the length of the data provided.
#[cfg(not(esp_idf_version_major = "4"))]
pub fn write(&mut self, data: &[u8], timeout: TickType_t) -> Result<usize, EspError> {
if data.is_empty() {
Ok(0)
} else {
let mut bytes_written: usize = 0;
Self::remap_result(
unsafe {
esp!(i2s_channel_write(
self.tx_handle,
data.as_ptr() as *mut c_void,
data.len(),
&mut bytes_written,
crate::delay::TickType(timeout).as_millis_u32(),
))
},
bytes_written,
)
}
}
/// Write all data to the channel.
///
/// This may be called only when the channel is in the `RUNNING` state.
pub fn write_all(&mut self, data: &[u8], timeout: TickType_t) -> Result<(), EspError> {
let mut offset = 0;
while offset < data.len() {
offset += self.write(&data[offset..], timeout)?;
}
Ok(())
}
}
impl<'d, Dir> Drop for I2sDriver<'d, Dir> {
fn drop(&mut self) {
#[cfg(esp_idf_version_major = "4")]
{
let _ = unsafe { esp!(i2s_stop(self.port as _)) };
esp!(unsafe { i2s_driver_uninstall(self.port as _) }).unwrap();
}
#[cfg(not(esp_idf_version_major = "4"))]
{
if !self.rx_handle.is_null() {
let _ = unsafe { esp!(i2s_channel_disable(self.rx_handle)) };
}
if !self.tx_handle.is_null() {
let _ = unsafe { esp!(i2s_channel_disable(self.tx_handle)) };
}
self.unsubscribe_channel(self.rx_handle).unwrap();
self.unsubscribe_channel(self.tx_handle).unwrap();
if !self.rx_handle.is_null() {
self.del_channel(self.rx_handle).unwrap();
}
if !self.tx_handle.is_null() {
self.del_channel(self.tx_handle).unwrap();
}
}
}
}
unsafe impl<'d, Dir> Send for I2sDriver<'d, Dir> {}
impl<'d, Dir> I2sPort for I2sDriver<'d, Dir> {
fn port(&self) -> i2s_port_t {
self.port as _
}
}
/// C-facing ISR dispatcher for on_send_* callbacks.
#[cfg(not(esp_idf_version_major = "4"))]
unsafe extern "C" fn dispatch_send(
_handle: i2s_chan_handle_t,
_raw_event: *mut i2s_event_data_t,
user_ctx: *mut c_void,
) -> bool {
let port = user_ctx as u32 as i2s_port_t;
SEND_NOTIFIER[port as usize].notify()
}
/// C-facing ISR dispatcher for on_recv_* callbacks.
#[cfg(not(esp_idf_version_major = "4"))]
unsafe extern "C" fn dispatch_recv(
_handle: i2s_chan_handle_t,
_raw_event: *mut i2s_event_data_t,
user_ctx: *mut c_void,
) -> bool {
let port = user_ctx as u32 as i2s_port_t;
RECV_NOTIFIER[port as usize].notify()
}
macro_rules! impl_i2s {
($i2s:ident: $port:expr) => {
crate::impl_peripheral!($i2s);
impl I2s for $i2s {
#[inline(always)]
fn port() -> i2s_port_t {
$port
}
}
};
}
impl_i2s!(I2S0: 0);
#[cfg(any(esp32, esp32s3))]
impl_i2s!(I2S1: 1);
#[cfg(not(esp_idf_version_major = "4"))]
#[cfg(not(any(esp32, esp32s3)))]
static SEND_NOTIFIER: [Notification; 1] = [Notification::new()];
#[cfg(not(esp_idf_version_major = "4"))]
#[cfg(not(any(esp32, esp32s3)))]
static RECV_NOTIFIER: [Notification; 1] = [Notification::new()];
#[cfg(not(esp_idf_version_major = "4"))]
#[cfg(any(esp32, esp32s3))]
static SEND_NOTIFIER: [Notification; 2] = [Notification::new(), Notification::new()];
#[cfg(not(esp_idf_version_major = "4"))]
#[cfg(any(esp32, esp32s3))]
static RECV_NOTIFIER: [Notification; 2] = [Notification::new(), Notification::new()];