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