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}