Skip to main content

esp_idf_hal/i2s/
std.rs

1//! Standard mode driver for the ESP32 I2S peripheral.
2//!
3//! # Microcontroller support for Standard mode
4//!
5//! | Microcontroller    | Standard Rx     | Standard Tx     |
6//! |--------------------|-----------------|-----------------|
7//! | ESP32              | I2S0, I2S1      | I2S0, I2S11     |
8//! | ESP32-S2           | I2S0            | I2S0            |
9//! | ESP32-S3           | I2S0, I2S1      | I2S0, I2S1      |
10//! | ESP32-C2 (ESP8684) | _not supported_ | _not supported_ |
11//! | ESP32-C3           | I2S0            | I2S0            |
12//! | ESP32-C6           | I2S0            | I2S0            |
13//! | ESP32-H2           | I2S0            | I2S0            |
14
15use super::*;
16use crate::gpio::*;
17
18use esp_idf_sys::*;
19
20pub(super) mod config {
21    #[allow(unused)]
22    use crate::{gpio::*, i2s::config::*};
23    use esp_idf_sys::*;
24
25    /// Standard mode configuration for the I2S peripheral.
26    pub struct StdConfig {
27        /// The base channel configuration.
28        #[allow(dead_code)]
29        pub(super) channel_cfg: Config,
30
31        /// Standard mode channel clock configuration.
32        #[allow(dead_code)]
33        clk_cfg: StdClkConfig,
34
35        /// Standard mode channel slot configuration.
36        #[allow(dead_code)]
37        slot_cfg: StdSlotConfig,
38
39        /// Standard mode channel GPIO configuration.
40        #[cfg(not(esp_idf_version_major = "4"))]
41        #[allow(dead_code)]
42        gpio_cfg: StdGpioConfig,
43    }
44
45    impl StdConfig {
46        /// Create a new standard mode channel configuration from the given clock configuration, slot configuration,
47        /// and GPIO configuration.
48        pub fn new(
49            channel_cfg: Config,
50            clk_cfg: StdClkConfig,
51            slot_cfg: StdSlotConfig,
52            #[cfg(not(esp_idf_version_major = "4"))] gpio_cfg: StdGpioConfig,
53        ) -> Self {
54            Self {
55                channel_cfg,
56                clk_cfg,
57                slot_cfg,
58                #[cfg(not(esp_idf_version_major = "4"))]
59                gpio_cfg,
60            }
61        }
62
63        /// Create a new standard mode channel configuration for the Philips I2S protocol with the specified sample
64        /// rate and bits per sample, in stereo mode, with MCLK set to 256 times the sample rate.
65        #[inline(always)]
66        pub fn philips(sample_rate_hz: u32, bits_per_sample: DataBitWidth) -> Self {
67            Self {
68                channel_cfg: Config::default(),
69                clk_cfg: StdClkConfig::from_sample_rate_hz(sample_rate_hz),
70                slot_cfg: StdSlotConfig::philips_slot_default(bits_per_sample, SlotMode::Stereo),
71                #[cfg(not(esp_idf_version_major = "4"))]
72                gpio_cfg: StdGpioConfig::default(),
73            }
74        }
75
76        /// Create a new standard mode channel configuration for the PCM I2S protocol with the specified sample rate
77        /// and bits per sample, in stereo mode, with MCLK set to 256 times the sample rate.
78        #[inline(always)]
79        pub fn pcm(sample_rate_hz: u32, bits_per_sample: DataBitWidth) -> Self {
80            Self {
81                channel_cfg: Config::default(),
82                clk_cfg: StdClkConfig::from_sample_rate_hz(sample_rate_hz),
83                slot_cfg: StdSlotConfig::pcm_slot_default(bits_per_sample, SlotMode::Stereo),
84                #[cfg(not(esp_idf_version_major = "4"))]
85                gpio_cfg: StdGpioConfig::default(),
86            }
87        }
88
89        /// Create a new standard mode channel configuration for the MSB I2S protocol with the specified sample rate
90        /// and bits per sample, in stereo mode, with MCLK set to 256 times the sample rate.
91        #[inline(always)]
92        pub fn msb(sample_rate_hz: u32, bits_per_sample: DataBitWidth) -> Self {
93            Self {
94                channel_cfg: Config::default(),
95                clk_cfg: StdClkConfig::from_sample_rate_hz(sample_rate_hz),
96                slot_cfg: StdSlotConfig::msb_slot_default(bits_per_sample, SlotMode::Stereo),
97                #[cfg(not(esp_idf_version_major = "4"))]
98                gpio_cfg: StdGpioConfig::default(),
99            }
100        }
101
102        /// Convert to the ESP-IDF SDK `i2s_std_config_t` representation.
103        #[cfg(not(esp_idf_version_major = "4"))]
104        #[inline(always)]
105        pub(crate) fn as_sdk<'d>(
106            &self,
107            bclk: impl InputPin + OutputPin + 'd,
108            din: Option<impl InputPin + 'd>,
109            dout: Option<impl OutputPin + 'd>,
110            mclk: Option<impl InputPin + OutputPin + 'd>,
111            ws: impl InputPin + OutputPin + 'd,
112        ) -> i2s_std_config_t {
113            i2s_std_config_t {
114                clk_cfg: self.clk_cfg.as_sdk(),
115                slot_cfg: self.slot_cfg.as_sdk(),
116                gpio_cfg: self.gpio_cfg.as_sdk(bclk, din, dout, mclk, ws),
117            }
118        }
119
120        /// Convert to the ESP-IDF SDK `i2s_driver_config_t` representation.
121        ///
122        /// # Note
123        /// The mode field is not fully set by this function. Only the controller/target field is set. Before using,
124        /// the following bits must be considered: `I2S_MODE_TX`, `I2S_MODE_RX`, `I2S_MODE_DAC_BUILT_IN`, and
125        /// `I2S_MODE_ADC_BUILT_IN`. (`I2S_MODE_PDM` should not be used here.)
126        #[cfg(esp_idf_version_major = "4")]
127        pub(crate) fn as_sdk(&self) -> i2s_driver_config_t {
128            let chan_fmt = match self.slot_cfg.slot_mode {
129                SlotMode::Stereo => i2s_channel_fmt_t_I2S_CHANNEL_FMT_RIGHT_LEFT,
130                SlotMode::Mono => match self.slot_cfg.slot_mask {
131                    StdSlotMask::Both => i2s_channel_fmt_t_I2S_CHANNEL_FMT_RIGHT_LEFT,
132                    StdSlotMask::Left => i2s_channel_fmt_t_I2S_CHANNEL_FMT_ONLY_LEFT,
133                    StdSlotMask::Right => i2s_channel_fmt_t_I2S_CHANNEL_FMT_ONLY_RIGHT,
134                },
135            };
136
137            i2s_driver_config_t {
138                mode: self.channel_cfg.role.as_sdk(),
139                sample_rate: self.clk_cfg.sample_rate_hz,
140                bits_per_sample: self.slot_cfg.data_bit_width.as_sdk(),
141                channel_format: chan_fmt,
142                communication_format: self.slot_cfg.comm_fmt.as_sdk(),
143                intr_alloc_flags: 1 << 1, // ESP_INTR_FLAG_LEVEL1
144                dma_buf_count: self.channel_cfg.dma_buffer_count as i32,
145                dma_buf_len: self.channel_cfg.frames_per_buffer as i32,
146                #[cfg(any(esp32, esp32s2))]
147                use_apll: matches!(self.clk_cfg.clk_src, ClockSource::Apll),
148                #[cfg(not(any(esp32, esp32s2)))]
149                use_apll: false,
150                tx_desc_auto_clear: self.channel_cfg.auto_clear,
151                fixed_mclk: 0,
152                mclk_multiple: self.clk_cfg.mclk_multiple.as_sdk(),
153                bits_per_chan: self.slot_cfg.slot_bit_width.as_sdk(),
154
155                // The following are TDM-only fields and are not present on chips that don't support TDM mode.
156                // There's no cfg option for this (it's a constant in esp-idf-sys).
157                #[cfg(not(any(esp32, esp32s2)))]
158                chan_mask: 0,
159                #[cfg(not(any(esp32, esp32s2)))]
160                total_chan: 0,
161                #[cfg(not(any(esp32, esp32s2)))]
162                left_align: self.slot_cfg.left_align,
163                #[cfg(not(any(esp32, esp32s2)))]
164                big_edin: self.slot_cfg.big_endian,
165                #[cfg(not(any(esp32, esp32s2)))]
166                bit_order_msb: !self.slot_cfg.bit_order_lsb,
167                #[cfg(not(any(esp32, esp32s2)))]
168                skip_msk: true,
169            }
170        }
171    }
172
173    /// Standard mode channel clock configuration.
174    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
175    pub struct StdClkConfig {
176        /// I2S sample rate.
177        sample_rate_hz: u32,
178
179        /// Clock source.
180        clk_src: ClockSource,
181
182        /// The multiple of MCLK to the sample rate.
183        mclk_multiple: MclkMultiple,
184    }
185
186    impl StdClkConfig {
187        /// Create a standard clock configuration with the specified rate (in Hz), clock source, and MCLK multiple of
188        /// the sample rate.
189        #[inline(always)]
190        pub fn new(sample_rate_hz: u32, clk_src: ClockSource, mclk_multiple: MclkMultiple) -> Self {
191            Self {
192                sample_rate_hz,
193                clk_src,
194                mclk_multiple,
195            }
196        }
197
198        /// Create a standard clock configuration with the specified rate in Hz. This will set the clock source to
199        /// PLL_F160M and the MCLK multiple to 256 times the sample rate.
200        ///
201        /// # Note
202        /// Set the mclk_multiple to [MclkMultiple::M384] when using 24-bit data width. Otherwise, the sample rate
203        /// might be imprecise since the BCLK division is not an integer.
204        #[inline(always)]
205        pub fn from_sample_rate_hz(rate: u32) -> Self {
206            Self {
207                sample_rate_hz: rate,
208                clk_src: ClockSource::default(),
209                mclk_multiple: MclkMultiple::M256,
210            }
211        }
212
213        /// Set the clock source on this standard clock configuration.
214        #[inline(always)]
215        pub fn clk_src(mut self, clk_src: ClockSource) -> Self {
216            self.clk_src = clk_src;
217            self
218        }
219
220        /// Set the MCLK multiple on this standard clock configuration.
221        #[inline(always)]
222        pub fn mclk_multiple(mut self, mclk_multiple: MclkMultiple) -> Self {
223            self.mclk_multiple = mclk_multiple;
224            self
225        }
226
227        /// Convert to the ESP-IDF SDK `i2s_std_clk_config_t` representation.
228        #[cfg(not(esp_idf_version_major = "4"))]
229        #[allow(clippy::needless_update)]
230        #[inline(always)]
231        pub(crate) fn as_sdk(&self) -> i2s_std_clk_config_t {
232            i2s_std_clk_config_t {
233                sample_rate_hz: self.sample_rate_hz,
234                clk_src: self.clk_src.as_sdk(),
235                mclk_multiple: self.mclk_multiple.as_sdk(),
236                ..Default::default()
237            }
238        }
239    }
240
241    /// The communication format used by the v4 driver.
242    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
243    pub enum StdCommFormat {
244        /// Standard I2S/Philips format.
245        #[default]
246        Philips,
247
248        /// MSB-aligned format (data present at first bit clock).
249        Msb,
250
251        /// PCM short standard. Word select is one bit clock.
252        PcmShort,
253
254        /// PCM long standard. Word select is the same as the data bit width.
255        PcmLong,
256    }
257
258    impl StdCommFormat {
259        #[cfg(esp_idf_version_major = "4")]
260        #[inline(always)]
261        pub(in crate::i2s) fn as_sdk(&self) -> i2s_comm_format_t {
262            match self {
263                Self::Philips => i2s_comm_format_t_I2S_COMM_FORMAT_STAND_I2S,
264                Self::Msb => i2s_comm_format_t_I2S_COMM_FORMAT_STAND_MSB,
265                Self::PcmShort => i2s_comm_format_t_I2S_COMM_FORMAT_PCM_SHORT,
266                Self::PcmLong => i2s_comm_format_t_I2S_COMM_FORMAT_PCM_LONG,
267            }
268        }
269    }
270
271    /// Standard mode GPIO (general purpose input/output) configuration.
272    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
273    pub struct StdGpioConfig {
274        /// Invert the BCLK signal.
275        bclk_invert: bool,
276
277        /// Invert the MCLK signal.
278        mclk_invert: bool,
279
280        /// Invert the WS signal.
281        ws_invert: bool,
282    }
283
284    impl StdGpioConfig {
285        /// Create a new standard mode GPIO configuration with the specified inversion flags for BCLK, MCLK, and WS.
286        pub fn new(bclk_invert: bool, mclk_invert: bool, ws_invert: bool) -> Self {
287            Self {
288                bclk_invert,
289                mclk_invert,
290                ws_invert,
291            }
292        }
293
294        /// Set the BCLK inversion flag on this standard GPIO configuration.
295        #[inline(always)]
296        pub fn bclk_invert(mut self, bclk_invert: bool) -> Self {
297            self.bclk_invert = bclk_invert;
298            self
299        }
300
301        /// Set the MCLK inversion flag on this standard GPIO configuration.
302        #[inline(always)]
303        pub fn mclk_invert(mut self, mclk_invert: bool) -> Self {
304            self.mclk_invert = mclk_invert;
305            self
306        }
307
308        /// Set the WS inversion flag on this standard GPIO configuration.
309        #[inline(always)]
310        pub fn ws_invert(mut self, ws_invert: bool) -> Self {
311            self.ws_invert = ws_invert;
312            self
313        }
314
315        /// Convert to the ESP-IDF SDK `i2s_std_gpio_config_t` representation.
316        #[cfg(not(esp_idf_version_major = "4"))]
317        pub(crate) fn as_sdk<'d>(
318            &self,
319            bclk: impl InputPin + OutputPin + 'd,
320            din: Option<impl InputPin + 'd>,
321            dout: Option<impl OutputPin + 'd>,
322            mclk: Option<impl InputPin + OutputPin + 'd>,
323            ws: impl InputPin + OutputPin + 'd,
324        ) -> i2s_std_gpio_config_t {
325            let invert_flags = i2s_std_gpio_config_t__bindgen_ty_1 {
326                _bitfield_1: i2s_std_gpio_config_t__bindgen_ty_1::new_bitfield_1(
327                    self.mclk_invert as u32,
328                    self.bclk_invert as u32,
329                    self.ws_invert as u32,
330                ),
331                ..Default::default()
332            };
333
334            i2s_std_gpio_config_t {
335                bclk: bclk.pin() as _,
336                din: if let Some(din) = din {
337                    din.pin() as _
338                } else {
339                    -1
340                },
341                dout: if let Some(dout) = dout {
342                    dout.pin() as _
343                } else {
344                    -1
345                },
346                mclk: if let Some(mclk) = mclk {
347                    mclk.pin() as _
348                } else {
349                    -1
350                },
351                ws: ws.pin() as _,
352                invert_flags,
353            }
354        }
355    }
356
357    /// Standard mode channel slot configuration.
358    ///
359    /// To create a slot configuration, use [`StdSlotConfig::philips_slot_default`], [`StdSlotConfig::pcm_slot_default`], or
360    /// [`StdSlotConfig::msb_slot_default`], then customize it as needed.
361    ///
362    /// # Note
363    /// The `slot_mode` and `slot_mask` cause the data to be interpreted in different ways, as noted below.
364    /// WS is the "word select" signal, sometimes called LRCLK (left/right clock).
365    ///
366    /// ## Transmit
367    ///
368    /// Assuming the buffered data contains the following samples (where a sample may be 1, 2, 3, or 4 bytes, depending
369    /// on `data_bit_width`):
370    ///
371    /// | **`d[0]`** | **`d[1]`** | **`d[2]`** | **`d[3]`** | **`d[4]`** | **`d[5]`** | **`d[6]`** | **`d[7]`** |
372    /// |------------|------------|------------|------------|------------|------------|------------|------------|
373    /// |  11        | 12         | 13         | 14         | 15         | 16         | 17         | 18         |
374    ///
375    /// The actual data on the line will be:
376    ///
377    /// <table>
378    ///   <thead>
379    ///     <tr><th><code>slot_mode</code></th><th><code>slot_mask</code></th><th colspan=8>Transmitted Data</th></tr>
380    ///     <tr><th></th><th></th><th>WS Low</th><th>WS High</th><th>WS Low</th><th>WS High</th><th>WS Low</th><th>WS High</th><th>WS Low</th><th>WS High</th></tr>
381    ///   </thead>
382    ///   <tbody>
383    ///     <tr><td rowspan=3><code>Mono</code></td><td><code>Left</code></td> <td>11</td><td><font color="red">0</font></td><td>12</td><td><font color="red">0</font></td><td>13</td><td><font color="red">0</font></td><td>14</td><td><font color="red">0</font></td></tr>
384    ///     <tr>                                    <td><code>Right</code></td><td><font color="red">0</font></td><td>11</td><td><font color="red">0</font></td><td>12</td><td><font color="red">0</font></td><td>13</td><td><font color="red">0</font></td><td>14</td></tr>
385    ///     <tr>                                    <td><code>Both</code></td> <td>11</td><td>11</td><td>12</td><td>12</td><td>13</td><td>13</td><td>14</td><td>14</td></tr>
386    ///     <tr><td rowspan=3><code>Stereo</code></td><td><code>Left</code></td> <td>11</td><td><font color="red">0</font></td><td>13</td><td><font color="red">0</font></td><td>15</td><td><font color="red">0</font></td><td>17</td><td><font color="red">0</font></td></tr>
387    ///     <tr>                                      <td><code>Right</code></td><td><font color="red">0</font></td><td>12</td><td><font color="red">0</font></td><td>14</td><td><font color="red">0</font></td><td>16</td><td><font color="red">0</font></td><td>18</td></tr>
388    ///     <tr>                                      <td><code>Both</code></td> <td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td></tr>
389    ///   </tbody>
390    /// </table>
391    ///
392    ///
393    /// ## Receive
394    ///
395    /// Assuming the received data contains the following samples (where a sample may be 8, 16, 24, or 32 bits, depending on `data_bit_width`):
396    ///
397    /// | **WS Low**  | **WS High** | **WS Low**  | **WS High** | **WS Low**  | **WS High** | **WS Low**  | **WS High** |     |
398    /// |-------------|-------------|-------------|-------------|-------------|-------------|-------------|-------------|-----|
399    /// | 11          | 12          | 13          | 14          | 15          | 16          | 17          | 18          | ... |
400    ///
401    /// The actual data in the buffer will be (1-4 bytes, depending on `data_bit_width`):
402    ///
403    /// <table>
404    ///   <thead>
405    ///     <tr><th><code>slot_mode</code></th><th><code>slot_mask</code></th><th colspan=8>Buffer Contents</th></tr>
406    ///     <tr><th></th><th></th><th><code>d[0]</code></th><th><code>d[1]</code></th><th><code>d[2]</code></th><th><code>d[3]</code></th><th><code>d[4]</code></th><th><code>d[5]</code></th><th><code>d[6]</code></th><th><code>d[7]</code></th></tr>
407    ///   </thead>
408    ///   <tbody>
409    ///     <tr><td rowspan=3><code>Mono</code></td>  <td><code>Left</code></td> <td>11</td><td>13</td><td>15</td><td>17</td><td>19</td><td>21</td><td>23</td><td>25</td></tr>
410    ///     <tr>                                      <td><code>Right</code></td><td>12</td><td>14</td><td>16</td><td>18</td><td>20</td><td>22</td><td>24</td><td>26</td></tr>
411    ///     <tr>                                      <td><code>Both</code></td> <td colspan=8><i>Unspecified behavior</i></td></tr>
412    ///     <tr><td rowspan=2><code>Stereo</code></td><td><i>Any</i></td>        <td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td></tr>
413    ///   </tbody>
414    /// </table>
415    ///
416    /// For details, refer to the
417    /// _ESP-IDF Programming Guide_ details for your specific microcontroller:
418    /// * ESP32: [STD Tx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2s.html#std-tx-mode) / [STD Rx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/i2s.html#std-rx-mode).
419    /// * ESP32-S2: [STD Tx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/i2s.html#std-tx-mode) / [STD Rx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/i2s.html#std-tx-mode)
420    /// * ESP32-S3: [STD Tx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#std-tx-mode) / [STD Rx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#std-tx-mode)
421    /// * ESP32-C3: [STD Tx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/i2s.html#std-tx-mode) / [STD Rx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/i2s.html#std-tx-mode).
422    /// * ESP32-C6: [STD Tx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/peripherals/i2s.html#std-tx-mode) / [STD Rx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/peripherals/i2s.html#std-tx-mode).
423    /// * ESP32-H2: [STD Tx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32h2/api-reference/peripherals/i2s.html#std-tx-mode) / [STD Rx Mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32h2/api-reference/peripherals/i2s.html#std-tx-mode).
424    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
425    pub struct StdSlotConfig {
426        /// I2S sample data bit width (valid data bits per sample).
427        data_bit_width: DataBitWidth,
428
429        /// I2S slot bit width (total bits per slot).
430        slot_bit_width: SlotBitWidth,
431
432        /// Mono or stereo mode operation.
433        slot_mode: SlotMode,
434
435        /// Are we using the left, right, or both data slots?
436        slot_mask: StdSlotMask,
437
438        /// The word select (WS) signal width, in terms of the bit clock (BCK) periods.
439        #[cfg(not(esp_idf_version_major = "4"))]
440        ws_width: u32,
441
442        /// The word select signal polarity; true enables the light lever first.
443        #[cfg(not(esp_idf_version_major = "4"))]
444        ws_polarity: bool,
445
446        /// Set to enable the additional bit-shift needed in Philips mode.
447        #[cfg(not(esp_idf_version_major = "4"))]
448        bit_shift: bool,
449
450        /// ESP32/ESP32S2 only: place the right slot data in the MSB in the FIFO.
451        #[cfg(all(any(esp32, esp32s2), not(esp_idf_version_major = "4")))]
452        msb_right: bool,
453
454        /// The communication format used by the driver.
455        #[cfg(esp_idf_version_major = "4")]
456        comm_fmt: StdCommFormat,
457
458        /// Non-ESP32/ESP32S2: enable left-alignment
459        #[cfg(not(any(esp32, esp32s2)))]
460        left_align: bool,
461
462        /// Non-ESP32/ESP32S2: Enable big-endian.
463        #[cfg(not(any(esp32, esp32s2)))]
464        big_endian: bool,
465
466        /// Non-ESP32/ESP32S2: Enable LSB-first.
467        #[cfg(not(any(esp32, esp32s2)))]
468        bit_order_lsb: bool,
469    }
470
471    impl StdSlotConfig {
472        /// Update the data bit width on this standard slot configuration.
473        #[inline(always)]
474        pub fn data_bit_width(mut self, data_bit_width: DataBitWidth) -> Self {
475            self.data_bit_width = data_bit_width;
476            self
477        }
478
479        /// Update the slot bit width on this standard slot configuration.
480        #[inline(always)]
481        pub fn slot_bit_width(mut self, slot_bit_width: SlotBitWidth) -> Self {
482            self.slot_bit_width = slot_bit_width;
483            self
484        }
485
486        /// Update the slot mode and mask on this standard slot configuration.
487        #[inline(always)]
488        pub fn slot_mode_mask(mut self, slot_mode: SlotMode, slot_mask: StdSlotMask) -> Self {
489            self.slot_mode = slot_mode;
490            self.slot_mask = slot_mask;
491            self
492        }
493
494        /// Update the word select signal width on this standard slot configuration.
495        #[cfg(not(esp_idf_version_major = "4"))]
496        #[inline(always)]
497        pub fn ws_width(mut self, ws_width: u32) -> Self {
498            self.ws_width = ws_width;
499            self
500        }
501
502        /// Update the word select signal polarity on this standard slot configuration.
503        #[cfg(not(esp_idf_version_major = "4"))]
504        #[inline(always)]
505        pub fn ws_polarity(mut self, ws_polarity: bool) -> Self {
506            self.ws_polarity = ws_polarity;
507            self
508        }
509
510        /// Update the bit shift flag on this standard slot configuration.
511        #[cfg(not(esp_idf_version_major = "4"))]
512        #[inline(always)]
513        pub fn bit_shift(mut self, bit_shift: bool) -> Self {
514            self.bit_shift = bit_shift;
515            self
516        }
517
518        /// Update the MSB-right flag on this standard slot configuration.
519        #[cfg(all(any(esp32, esp32s2), not(esp_idf_version_major = "4")))]
520        #[inline(always)]
521        pub fn msb_right(mut self, msb_right: bool) -> Self {
522            self.msb_right = msb_right;
523            self
524        }
525
526        /// Update the communication format on this standard slot configuration.
527        #[cfg(esp_idf_version_major = "4")]
528        #[inline(always)]
529        pub fn comm_fmt(mut self, comm_fmt: StdCommFormat) -> Self {
530            self.comm_fmt = comm_fmt;
531            self
532        }
533
534        /// Update the left-alignment flag on this standard slot configuration.
535        #[cfg(not(any(esp32, esp32s2)))]
536        #[inline(always)]
537        pub fn left_align(mut self, left_align: bool) -> Self {
538            self.left_align = left_align;
539            self
540        }
541
542        /// Update the big-endian flag on this standard slot configuration.
543        #[cfg(not(any(esp32, esp32s2)))]
544        #[inline(always)]
545        pub fn big_endian(mut self, big_endian: bool) -> Self {
546            self.big_endian = big_endian;
547            self
548        }
549
550        /// Update the LSB-first flag on this standard slot configuration.
551        #[cfg(not(any(esp32, esp32s2)))]
552        #[inline(always)]
553        pub fn bit_order_lsb(mut self, bit_order_lsb: bool) -> Self {
554            self.bit_order_lsb = bit_order_lsb;
555            self
556        }
557
558        /// Configure in Philips format in 2 slots.
559        pub fn philips_slot_default(bits_per_sample: DataBitWidth, slot_mode: SlotMode) -> Self {
560            let slot_mask = if slot_mode == SlotMode::Mono {
561                StdSlotMask::Left
562            } else {
563                StdSlotMask::Both
564            };
565
566            Self {
567                data_bit_width: bits_per_sample,
568                slot_bit_width: SlotBitWidth::Auto,
569                slot_mode,
570                slot_mask,
571                #[cfg(not(esp_idf_version_major = "4"))]
572                ws_width: bits_per_sample.into(),
573                #[cfg(not(esp_idf_version_major = "4"))]
574                ws_polarity: false,
575                #[cfg(not(esp_idf_version_major = "4"))]
576                bit_shift: true,
577                #[cfg(all(esp32, not(esp_idf_version_major = "4")))]
578                msb_right: bits_per_sample <= DataBitWidth::Bits16,
579                #[cfg(all(esp32s2, not(esp_idf_version_major = "4")))]
580                msb_right: true,
581                #[cfg(esp_idf_version_major = "4")]
582                comm_fmt: StdCommFormat::Philips,
583                #[cfg(not(any(esp32, esp32s2)))]
584                left_align: false,
585                #[cfg(not(any(esp32, esp32s2)))]
586                big_endian: false,
587                #[cfg(not(any(esp32, esp32s2)))]
588                bit_order_lsb: false,
589            }
590        }
591
592        /// Configure in PCM (short) format in 2 slots.
593        pub fn pcm_slot_default(bits_per_sample: DataBitWidth, slot_mode: SlotMode) -> Self {
594            let slot_mask = if slot_mode == SlotMode::Mono {
595                StdSlotMask::Left
596            } else {
597                StdSlotMask::Both
598            };
599
600            Self {
601                data_bit_width: bits_per_sample,
602                slot_bit_width: SlotBitWidth::Auto,
603                slot_mode,
604                slot_mask,
605                #[cfg(not(esp_idf_version_major = "4"))]
606                ws_width: 1,
607                #[cfg(not(esp_idf_version_major = "4"))]
608                ws_polarity: true,
609                #[cfg(not(esp_idf_version_major = "4"))]
610                bit_shift: true,
611                #[cfg(all(esp32, not(esp_idf_version_major = "4")))]
612                msb_right: bits_per_sample <= DataBitWidth::Bits16,
613                #[cfg(all(esp32s2, not(esp_idf_version_major = "4")))]
614                msb_right: true,
615                #[cfg(esp_idf_version_major = "4")]
616                comm_fmt: StdCommFormat::PcmShort,
617                #[cfg(not(any(esp32, esp32s2)))]
618                left_align: false,
619                #[cfg(not(any(esp32, esp32s2)))]
620                big_endian: false,
621                #[cfg(not(any(esp32, esp32s2)))]
622                bit_order_lsb: false,
623            }
624        }
625
626        /// Configure in MSB format in 2 slots.
627        pub fn msb_slot_default(bits_per_sample: DataBitWidth, slot_mode: SlotMode) -> Self {
628            let slot_mask = if slot_mode == SlotMode::Mono {
629                StdSlotMask::Left
630            } else {
631                StdSlotMask::Both
632            };
633
634            Self {
635                data_bit_width: bits_per_sample,
636                slot_bit_width: SlotBitWidth::Auto,
637                slot_mode,
638                slot_mask,
639                #[cfg(not(esp_idf_version_major = "4"))]
640                ws_width: bits_per_sample.into(),
641                #[cfg(not(esp_idf_version_major = "4"))]
642                ws_polarity: false,
643                #[cfg(not(esp_idf_version_major = "4"))]
644                bit_shift: false,
645                #[cfg(all(esp32, not(esp_idf_version_major = "4")))]
646                msb_right: bits_per_sample <= DataBitWidth::Bits16,
647                #[cfg(all(esp32s2, not(esp_idf_version_major = "4")))]
648                msb_right: true,
649                #[cfg(esp_idf_version_major = "4")]
650                comm_fmt: StdCommFormat::Msb,
651                #[cfg(not(any(esp32, esp32s2)))]
652                left_align: false,
653                #[cfg(not(any(esp32, esp32s2)))]
654                big_endian: false,
655                #[cfg(not(any(esp32, esp32s2)))]
656                bit_order_lsb: false,
657            }
658        }
659
660        /// Convert to the ESP-IDF SDK `i2s_std_slot_config_t` representation.
661        #[cfg(not(esp_idf_version_major = "4"))]
662        pub(crate) fn as_sdk(&self) -> i2s_std_slot_config_t {
663            i2s_std_slot_config_t {
664                data_bit_width: self.data_bit_width.as_sdk(),
665                slot_bit_width: self.slot_bit_width.as_sdk(),
666                slot_mode: self.slot_mode.as_sdk(),
667                slot_mask: self.slot_mask.as_sdk(),
668                ws_width: self.ws_width,
669                ws_pol: self.ws_polarity,
670                bit_shift: self.bit_shift,
671                #[cfg(any(esp32, esp32s2))]
672                msb_right: self.msb_right,
673                #[cfg(not(any(esp32, esp32s2)))]
674                left_align: self.left_align,
675                #[cfg(not(any(esp32, esp32s2)))]
676                big_endian: self.big_endian,
677                #[cfg(not(any(esp32, esp32s2)))]
678                bit_order_lsb: self.bit_order_lsb,
679            }
680        }
681    }
682
683    /// I2S slot selection in standard mode.
684    ///
685    /// The default is `StdSlotMask::Both`.
686    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
687    pub enum StdSlotMask {
688        /// I2S transmits or receives the left slot.
689        Left,
690
691        /// I2S transmits or receives the right slot.
692        Right,
693
694        /// I2S transmits or receives both slots.
695        #[default]
696        Both,
697    }
698
699    impl StdSlotMask {
700        /// Convert to the ESP-IDF SDK `i2s_std_slot_mask_t` representation.
701        #[cfg(not(esp_idf_version_major = "4"))]
702        #[inline(always)]
703        pub(crate) fn as_sdk(&self) -> i2s_std_slot_mask_t {
704            match self {
705                Self::Left => 1 << 0,
706                Self::Right => 1 << 1,
707                Self::Both => (1 << 0) | (1 << 1),
708            }
709        }
710    }
711}
712
713impl<'d, Dir> I2sDriver<'d, Dir> {
714    #[cfg(not(esp_idf_version_major = "4"))]
715    #[allow(clippy::too_many_arguments)]
716    fn internal_new_std<I2S: I2s + 'd>(
717        _i2s: I2S,
718        config: &config::StdConfig,
719        rx: bool,
720        tx: bool,
721        bclk: impl InputPin + OutputPin + 'd,
722        din: Option<impl InputPin + 'd>,
723        dout: Option<impl OutputPin + 'd>,
724        mclk: Option<impl InputPin + OutputPin + 'd>,
725        ws: impl InputPin + OutputPin + 'd,
726    ) -> Result<Self, EspError> {
727        let chan_cfg = config.channel_cfg.as_sdk(I2S::port());
728
729        let this = Self::internal_new::<I2S>(&chan_cfg, rx, tx)?;
730
731        // Create the channel configuration.
732        let std_config = config.as_sdk(bclk, din, dout, mclk, ws);
733
734        if rx {
735            unsafe {
736                // Open the RX channel.
737                esp!(i2s_channel_init_std_mode(this.rx_handle, &std_config))?;
738            }
739        }
740
741        if tx {
742            unsafe {
743                // Open the TX channel.
744                esp!(i2s_channel_init_std_mode(this.tx_handle, &std_config))?;
745            }
746        }
747
748        Ok(this)
749    }
750
751    #[cfg(esp_idf_version_major = "4")]
752    #[allow(clippy::too_many_arguments)]
753    pub fn internal_new_std<I2S: I2s + 'd>(
754        _i2s: I2S,
755        config: &config::StdConfig,
756        rx: bool,
757        tx: bool,
758        bclk: impl InputPin + OutputPin + 'd,
759        din: Option<impl InputPin + 'd>,
760        dout: Option<impl OutputPin + 'd>,
761        mclk: Option<impl InputPin + OutputPin + 'd>,
762        ws: impl InputPin + OutputPin + 'd,
763    ) -> Result<Self, EspError> {
764        let mut driver_cfg = config.as_sdk();
765
766        if rx {
767            driver_cfg.mode |= i2s_mode_t_I2S_MODE_RX;
768        }
769
770        if tx {
771            driver_cfg.mode |= i2s_mode_t_I2S_MODE_TX;
772        }
773
774        let this = Self::internal_new::<I2S>(&driver_cfg)?;
775
776        // Set the pin configuration.
777        let pin_cfg = i2s_pin_config_t {
778            bck_io_num: bclk.pin() as _,
779            data_in_num: din.map(|din| din.pin() as _).unwrap_or(-1),
780            data_out_num: dout.map(|dout| dout.pin() as _).unwrap_or(-1),
781            mck_io_num: mclk.map(|mclk| mclk.pin() as _).unwrap_or(-1),
782            ws_io_num: ws.pin() as _,
783        };
784
785        // Safety: &pin_cfg is a valid pointer to an i2s_pin_config_t.
786        unsafe {
787            esp!(i2s_set_pin(I2S::port(), &pin_cfg))?;
788        }
789
790        Ok(this)
791    }
792}
793
794impl<'d> I2sDriver<'d, I2sBiDir> {
795    /// Create a new standard mode driver for the given I2S peripheral with both the receive and transmit channels open.
796    #[allow(clippy::too_many_arguments)]
797    pub fn new_std_bidir<I2S: I2s + 'd>(
798        i2s: I2S,
799        config: &config::StdConfig,
800        bclk: impl InputPin + OutputPin + 'd,
801        din: impl InputPin + 'd,
802        dout: impl OutputPin + 'd,
803        mclk: Option<impl InputPin + OutputPin + 'd>,
804        ws: impl InputPin + OutputPin + 'd,
805    ) -> Result<Self, EspError> {
806        Self::internal_new_std(
807            i2s,
808            config,
809            true,
810            true,
811            bclk,
812            Some(din),
813            Some(dout),
814            mclk,
815            ws,
816        )
817    }
818}
819
820impl<'d> I2sDriver<'d, I2sRx> {
821    /// Create a new standard mode driver for the given I2S peripheral with only the receive channel open.
822    #[allow(clippy::too_many_arguments)]
823    pub fn new_std_rx<I2S: I2s + 'd>(
824        i2s: I2S,
825        config: &config::StdConfig,
826        bclk: impl InputPin + OutputPin + 'd,
827        din: impl InputPin + 'd,
828        mclk: Option<impl InputPin + OutputPin + 'd>,
829        ws: impl InputPin + OutputPin + 'd,
830    ) -> Result<Self, EspError> {
831        Self::internal_new_std(
832            i2s,
833            config,
834            true,
835            false,
836            bclk,
837            Some(din),
838            AnyIOPin::none(),
839            mclk,
840            ws,
841        )
842    }
843}
844
845impl<'d> I2sDriver<'d, I2sTx> {
846    /// Create a new standard mode driver for the given I2S peripheral with only the transmit channel open.
847    #[allow(clippy::too_many_arguments)]
848    pub fn new_std_tx<I2S: I2s + 'd>(
849        i2s: I2S,
850        config: &config::StdConfig,
851        bclk: impl InputPin + OutputPin + 'd,
852        dout: impl OutputPin + 'd,
853        mclk: Option<impl InputPin + OutputPin + 'd>,
854        ws: impl InputPin + OutputPin + 'd,
855    ) -> Result<Self, EspError> {
856        Self::internal_new_std(
857            i2s,
858            config,
859            false,
860            true,
861            bclk,
862            AnyIOPin::none(),
863            Some(dout),
864            mclk,
865            ws,
866        )
867    }
868}