1mod clock;
7#[cfg(not(i2s_version = "1"))]
8mod hp_filter;
9#[cfg_attr(i2s_version = "1", path = "regs_v1.rs")]
10#[cfg_attr(i2s_version = "2", path = "regs_v2.rs")]
11#[cfg_attr(i2s_version = "3", path = "regs_v2.rs")]
12mod ll;
13
14use super::master::{ConfigError, Instance};
15use crate::{i2s::master::Info, time::Rate};
16
17#[non_exhaustive]
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21pub enum PdmError {
22 UnsupportedInstance,
24 PcmFormatUnsupported,
26 InvalidClock,
28 InvalidSlotMask,
30 DirectionMissing,
32 DuplexUnsupported,
34 InvalidLine,
36}
37
38impl core::error::Error for PdmError {}
39
40impl core::fmt::Display for PdmError {
41 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42 match self {
43 Self::UnsupportedInstance => {
44 write!(f, "PDM mode is not supported on this I2S instance")
45 }
46 Self::PcmFormatUnsupported => write!(
47 f,
48 "PCM PDM format is not supported on this I2S instance; use raw PDM format"
49 ),
50 Self::InvalidClock => write!(f, "PDM clock configuration is out of supported range"),
51 Self::InvalidSlotMask => {
52 write!(f, "PDM slot mask must select at least one active slot")
53 }
54 Self::DirectionMissing => {
55 write!(f, "PDM configuration must include TX and/or RX settings")
56 }
57 Self::DuplexUnsupported => {
58 write!(f, "PDM full duplex (TX and RX together) is not supported")
59 }
60 Self::InvalidLine => write!(f, "PDM data line index is not available on this chip"),
61 }
62 }
63}
64
65pub trait PdmInstance: Instance {}
67
68for_each_i2s! {
69 (
70 $instance:ident, $sys:ident, $mclk:ident,
71 $bclk:ident, $ws:ident, $bclk_rx:ident, $ws_rx:ident,
72 $dout:tt, $din:tt, true, $pdm_rx:literal, $pcm2pdm:literal, $pdm2pcm:literal
73 ) => {
74 impl PdmInstance for crate::peripherals::$instance<'_> {}
75 };
76 (
77 $instance:ident, $sys:ident, $mclk:ident,
78 $bclk:ident, $ws:ident, $bclk_rx:ident, $ws_rx:ident,
79 $dout:tt, $din:tt, false, true, $pcm2pdm:literal, $pdm2pcm:literal
80 ) => {
81 impl PdmInstance for crate::peripherals::$instance<'_> {}
82 };
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
87#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88pub enum PdmDataFormat {
89 #[default]
91 Pcm,
92 Raw,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub enum PdmSlotMode {
100 #[default]
102 Mono,
103 Stereo,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
109#[cfg_attr(feature = "defmt", derive(defmt::Format))]
110pub struct PdmSlotMask(u16);
111
112impl PdmSlotMask {
113 pub const LEFT: Self = Self(1 << 1);
115 pub const RIGHT: Self = Self(1 << 0);
117 pub const BOTH: Self = Self(0b11);
119
120 #[cfg(all(i2s_supports_pdm_rx, not(esp32)))]
121 pub const LINE0_LEFT: Self = Self(1 << 1);
123 #[cfg(all(i2s_supports_pdm_rx, not(esp32)))]
124 pub const LINE0_RIGHT: Self = Self(1 << 0);
126 #[cfg(all(i2s_supports_pdm_rx, esp32s3))]
127 pub const LINE1_LEFT: Self = Self(1 << 3);
129 #[cfg(all(i2s_supports_pdm_rx, esp32s3))]
130 pub const LINE1_RIGHT: Self = Self(1 << 2);
132 #[cfg(all(i2s_supports_pdm_rx, esp32s3))]
133 pub const LINE2_LEFT: Self = Self(1 << 5);
135 #[cfg(all(i2s_supports_pdm_rx, esp32s3))]
136 pub const LINE2_RIGHT: Self = Self(1 << 4);
138 #[cfg(all(i2s_supports_pdm_rx, esp32s3))]
139 pub const LINE3_LEFT: Self = Self(1 << 7);
141 #[cfg(all(i2s_supports_pdm_rx, esp32s3))]
142 pub const LINE3_RIGHT: Self = Self(1 << 6);
144
145 pub const fn from_bits(bits: u16) -> Self {
147 Self(bits)
148 }
149
150 pub const fn bits(self) -> u16 {
152 self.0
153 }
154
155 pub fn for_mode(mode: PdmSlotMode) -> Self {
157 match mode {
158 PdmSlotMode::Mono => Self::LEFT,
159 PdmSlotMode::Stereo => Self::BOTH,
160 }
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
166#[cfg_attr(feature = "defmt", derive(defmt::Format))]
167pub enum PdmDownsampleRate {
168 #[default]
170 Dsr8s,
171 Dsr16s,
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
177#[cfg_attr(feature = "defmt", derive(defmt::Format))]
178pub enum PdmSigScaling {
179 Div2,
181 #[default]
183 Mul1,
184 Mul2,
186 Mul4,
188}
189
190impl PdmSigScaling {
191 pub(crate) fn to_register(self) -> u8 {
192 match self {
193 Self::Div2 => 0,
194 Self::Mul1 => 1,
195 Self::Mul2 => 2,
196 Self::Mul4 => 3,
197 }
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
203#[cfg_attr(feature = "defmt", derive(defmt::Format))]
204pub enum PdmTxLineMode {
205 #[default]
207 OneLineCodec,
208 OneLineDac,
210 TwoLineDac,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
216#[cfg_attr(feature = "defmt", derive(defmt::Format))]
217pub struct PdmTxClockConfig {
218 pub sample_rate: Rate,
220 pub up_sample_fp: u32,
222 pub up_sample_fs: u32,
224 pub bclk_div: u32,
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
230#[cfg_attr(feature = "defmt", derive(defmt::Format))]
231#[non_exhaustive]
232pub struct PdmTxSlotConfig {
233 pub slot_mode: PdmSlotMode,
235 pub data_format: PdmDataFormat,
237 pub sd_prescale: u8,
239 pub sd_scale: PdmSigScaling,
241 pub hp_scale: PdmSigScaling,
243 pub lp_scale: PdmSigScaling,
245 pub sinc_scale: PdmSigScaling,
247 #[cfg(not(i2s_version = "1"))]
248 pub line_mode: PdmTxLineMode,
250 pub hp_en: bool,
252 pub hp_cut_off_freq_hz: f32,
254 #[cfg(not(i2s_version = "1"))]
255 pub sd_dither: u8,
257 #[cfg(not(i2s_version = "1"))]
258 pub sd_dither2: u8,
260 #[cfg(i2s_version = "1")]
261 pub slot_mask: PdmSlotMask,
263}
264
265impl PdmTxSlotConfig {
266 fn codec_pcm_default(mode: PdmSlotMode) -> Self {
267 Self {
268 slot_mode: mode,
269 data_format: PdmDataFormat::Pcm,
270 sd_prescale: 0,
271 sd_scale: PdmSigScaling::Mul1,
272 hp_scale: PdmSigScaling::Div2,
273 lp_scale: PdmSigScaling::Mul1,
274 sinc_scale: PdmSigScaling::Mul1,
275 #[cfg(not(i2s_version = "1"))]
276 line_mode: PdmTxLineMode::OneLineCodec,
277 hp_en: true,
278 hp_cut_off_freq_hz: 35.5,
279 #[cfg(not(i2s_version = "1"))]
280 sd_dither: 0,
281 #[cfg(not(i2s_version = "1"))]
282 sd_dither2: 1,
283 #[cfg(i2s_version = "1")]
284 slot_mask: PdmSlotMask::BOTH,
285 }
286 }
287
288 #[cfg(not(i2s_version = "1"))]
289 fn dac_pcm_default(mode: PdmSlotMode) -> Self {
290 let mut cfg = Self::codec_pcm_default(mode);
291 cfg.hp_scale = PdmSigScaling::Mul1;
292 cfg.lp_scale = PdmSigScaling::Mul1;
293 cfg.sinc_scale = PdmSigScaling::Mul1;
294 #[cfg(not(i2s_version = "1"))]
295 {
296 cfg.line_mode = if mode == PdmSlotMode::Mono {
297 PdmTxLineMode::OneLineDac
298 } else {
299 PdmTxLineMode::TwoLineDac
300 };
301 }
302 cfg
303 }
304
305 fn raw_default(mode: PdmSlotMode) -> Self {
306 let mut cfg = Self::codec_pcm_default(mode);
307 cfg.data_format = PdmDataFormat::Raw;
308 cfg.hp_en = false;
309 cfg
310 }
311}
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
315#[cfg_attr(feature = "defmt", derive(defmt::Format))]
316pub struct PdmRxClockConfig {
317 pub sample_rate: Rate,
319
320 pub downsample_rate: PdmDownsampleRate,
322
323 pub bclk_div: u32,
325}
326
327#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
329#[cfg_attr(feature = "defmt", derive(defmt::Format))]
330#[non_exhaustive]
331pub struct PdmRxSlotConfig {
332 pub slot_mode: PdmSlotMode,
334
335 pub slot_mask: PdmSlotMask,
337
338 pub data_format: PdmDataFormat,
340
341 #[cfg(i2s_supports_pdm_rx_hp_filter)]
342 pub hp_en: bool,
344
345 #[cfg(i2s_supports_pdm_rx_hp_filter)]
346 pub hp_cut_off_freq_hz: f32,
348
349 #[cfg(i2s_supports_pdm_rx_hp_filter)]
350 pub amplify_num: u32,
352}
353
354impl PdmRxSlotConfig {
355 fn pcm_default(mode: PdmSlotMode) -> Self {
356 Self {
357 slot_mode: mode,
358 slot_mask: PdmSlotMask::for_mode(mode),
359 data_format: PdmDataFormat::Pcm,
360 #[cfg(i2s_supports_pdm_rx_hp_filter)]
361 hp_en: true,
362 #[cfg(i2s_supports_pdm_rx_hp_filter)]
363 hp_cut_off_freq_hz: 35.5,
364 #[cfg(i2s_supports_pdm_rx_hp_filter)]
365 amplify_num: 1,
366 }
367 }
368
369 fn raw_default(mode: PdmSlotMode) -> Self {
370 let mut cfg = Self::pcm_default(mode);
371 cfg.data_format = PdmDataFormat::Raw;
372 #[cfg(i2s_supports_pdm_rx_hp_filter)]
373 {
374 cfg.hp_en = false;
375 }
376 cfg
377 }
378}
379
380#[derive(Debug, Clone, Copy, PartialEq)]
382#[cfg_attr(feature = "defmt", derive(defmt::Format))]
383pub struct PdmTxConfig {
384 pub clock: PdmTxClockConfig,
386
387 pub slot: PdmTxSlotConfig,
389}
390
391impl PdmTxConfig {
392 pub fn new_codec_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
394 Self {
395 clock: PdmTxClockConfig::codec_default(sample_rate),
396 slot: default_tx_slot(mode),
397 }
398 }
399
400 pub fn new_raw_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
402 Self {
403 clock: PdmTxClockConfig::codec_default(sample_rate),
404 slot: PdmTxSlotConfig::raw_default(mode),
405 }
406 }
407
408 #[cfg(not(i2s_version = "1"))]
410 pub fn new_dac_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
411 Self {
412 clock: PdmTxClockConfig::dac_default(sample_rate),
413 slot: PdmTxSlotConfig::dac_pcm_default(mode),
414 }
415 }
416
417 pub fn validate(&self, info: &Info) -> Result<(), PdmError> {
419 if self.slot.data_format == PdmDataFormat::Pcm && !info.pcm2pdm {
420 return Err(PdmError::PcmFormatUnsupported);
421 }
422 if self.clock.up_sample_fs > 480 {
423 return Err(PdmError::InvalidClock);
424 }
425 Ok(())
426 }
427}
428
429fn default_tx_slot(mode: PdmSlotMode) -> PdmTxSlotConfig {
430 cfg_select! {
431 i2s_supports_pcm2pdm => PdmTxSlotConfig::codec_pcm_default(mode),
432 _ => PdmTxSlotConfig::raw_default(mode),
433 }
434}
435
436#[derive(Debug, Clone, Copy, PartialEq)]
438#[cfg_attr(feature = "defmt", derive(defmt::Format))]
439pub struct PdmRxConfig {
440 pub clock: PdmRxClockConfig,
442 pub slot: PdmRxSlotConfig,
444}
445
446impl PdmRxConfig {
447 pub fn new_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
449 Self {
450 clock: PdmRxClockConfig::default(sample_rate),
451 slot: default_rx_slot(mode),
452 }
453 }
454
455 pub fn new_pcm_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
457 Self {
458 clock: PdmRxClockConfig::default(sample_rate),
459 slot: PdmRxSlotConfig::pcm_default(mode),
460 }
461 }
462
463 pub fn new_raw_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
465 Self {
466 clock: PdmRxClockConfig::default(sample_rate),
467 slot: PdmRxSlotConfig::raw_default(mode),
468 }
469 }
470
471 pub fn validate(&self, info: &Info) -> Result<(), PdmError> {
473 if self.slot.data_format == PdmDataFormat::Pcm && !info.pdm2pcm {
474 return Err(PdmError::PcmFormatUnsupported);
475 }
476 if self.slot.slot_mask.bits() == 0 {
477 return Err(PdmError::InvalidSlotMask);
478 }
479 Ok(())
480 }
481}
482
483fn default_rx_slot(mode: PdmSlotMode) -> PdmRxSlotConfig {
484 cfg_select! {
485 i2s_supports_pdm2pcm => PdmRxSlotConfig::pcm_default(mode),
486 _ => PdmRxSlotConfig::raw_default(mode),
487 }
488}
489
490#[derive(Debug, Clone, Copy, PartialEq)]
492#[cfg_attr(feature = "defmt", derive(defmt::Format))]
493pub struct PdmConfig {
494 pub tx: Option<PdmTxConfig>,
496 pub rx: Option<PdmRxConfig>,
498}
499
500impl PdmConfig {
501 #[cfg(i2s_supports_pdm_tx)]
503 pub fn tx_only(tx: PdmTxConfig) -> Self {
504 Self {
505 tx: Some(tx),
506 rx: None,
507 }
508 }
509
510 #[cfg(i2s_supports_pdm_rx)]
512 pub fn rx_only(rx: PdmRxConfig) -> Self {
513 Self {
514 tx: None,
515 rx: Some(rx),
516 }
517 }
518
519 pub fn validate(&self, info: &Info) -> Result<(), PdmError> {
522 if self.tx.is_none() && self.rx.is_none() {
523 return Err(PdmError::DirectionMissing);
524 }
525 if self.tx.is_some() && self.rx.is_some() {
526 return Err(PdmError::DuplexUnsupported);
527 }
528 if self.tx.is_some() && !info.pdm_tx {
529 return Err(PdmError::UnsupportedInstance);
530 }
531 if self.rx.is_some() && !info.pdm_rx {
532 return Err(PdmError::UnsupportedInstance);
533 }
534 if let Some(tx) = &self.tx {
535 tx.validate(info)?;
536 }
537 if let Some(rx) = &self.rx {
538 rx.validate(info)?;
539 }
540 Ok(())
541 }
542}
543
544pub(crate) fn configure_pdm(i2s: &Info, config: &PdmConfig) -> Result<(), ConfigError> {
545 ll::configure_pdm(i2s, config).map_err(ConfigError::Pdm)
546}