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 pub clock_source: crate::i2s::master::I2sClockSource,
228}
229
230#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
232#[cfg_attr(feature = "defmt", derive(defmt::Format))]
233#[non_exhaustive]
234pub struct PdmTxSlotConfig {
235 pub slot_mode: PdmSlotMode,
237 pub data_format: PdmDataFormat,
239 pub sd_prescale: u8,
241 pub sd_scale: PdmSigScaling,
243 pub hp_scale: PdmSigScaling,
245 pub lp_scale: PdmSigScaling,
247 pub sinc_scale: PdmSigScaling,
249 #[cfg(not(i2s_version = "1"))]
250 pub line_mode: PdmTxLineMode,
252 pub hp_en: bool,
254 pub hp_cut_off_freq_hz: f32,
256 #[cfg(not(i2s_version = "1"))]
257 pub sd_dither: u8,
259 #[cfg(not(i2s_version = "1"))]
260 pub sd_dither2: u8,
262 #[cfg(i2s_version = "1")]
263 pub slot_mask: PdmSlotMask,
265}
266
267impl PdmTxSlotConfig {
268 fn codec_pcm_default(mode: PdmSlotMode) -> Self {
269 Self {
270 slot_mode: mode,
271 data_format: PdmDataFormat::Pcm,
272 sd_prescale: 0,
273 sd_scale: PdmSigScaling::Mul1,
274 hp_scale: PdmSigScaling::Div2,
275 lp_scale: PdmSigScaling::Mul1,
276 sinc_scale: PdmSigScaling::Mul1,
277 #[cfg(not(i2s_version = "1"))]
278 line_mode: PdmTxLineMode::OneLineCodec,
279 hp_en: true,
280 hp_cut_off_freq_hz: 35.5,
281 #[cfg(not(i2s_version = "1"))]
282 sd_dither: 0,
283 #[cfg(not(i2s_version = "1"))]
284 sd_dither2: 1,
285 #[cfg(i2s_version = "1")]
286 slot_mask: PdmSlotMask::BOTH,
287 }
288 }
289
290 #[cfg(not(i2s_version = "1"))]
291 fn dac_pcm_default(mode: PdmSlotMode) -> Self {
292 let mut cfg = Self::codec_pcm_default(mode);
293 cfg.hp_scale = PdmSigScaling::Mul1;
294 cfg.lp_scale = PdmSigScaling::Mul1;
295 cfg.sinc_scale = PdmSigScaling::Mul1;
296 #[cfg(not(i2s_version = "1"))]
297 {
298 cfg.line_mode = if mode == PdmSlotMode::Mono {
299 PdmTxLineMode::OneLineDac
300 } else {
301 PdmTxLineMode::TwoLineDac
302 };
303 }
304 cfg
305 }
306
307 fn raw_default(mode: PdmSlotMode) -> Self {
308 let mut cfg = Self::codec_pcm_default(mode);
309 cfg.data_format = PdmDataFormat::Raw;
310 cfg.hp_en = false;
311 cfg
312 }
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
317#[cfg_attr(feature = "defmt", derive(defmt::Format))]
318pub struct PdmRxClockConfig {
319 pub sample_rate: Rate,
321
322 pub downsample_rate: PdmDownsampleRate,
324
325 pub bclk_div: u32,
327 pub clock_source: crate::i2s::master::I2sClockSource,
329}
330
331#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
333#[cfg_attr(feature = "defmt", derive(defmt::Format))]
334#[non_exhaustive]
335pub struct PdmRxSlotConfig {
336 pub slot_mode: PdmSlotMode,
338
339 pub slot_mask: PdmSlotMask,
341
342 pub data_format: PdmDataFormat,
344
345 #[cfg(i2s_supports_pdm_rx_hp_filter)]
346 pub hp_en: bool,
348
349 #[cfg(i2s_supports_pdm_rx_hp_filter)]
350 pub hp_cut_off_freq_hz: f32,
352
353 #[cfg(i2s_supports_pdm_rx_hp_filter)]
354 pub amplify_num: u32,
356}
357
358impl PdmRxSlotConfig {
359 fn pcm_default(mode: PdmSlotMode) -> Self {
360 Self {
361 slot_mode: mode,
362 slot_mask: PdmSlotMask::for_mode(mode),
363 data_format: PdmDataFormat::Pcm,
364 #[cfg(i2s_supports_pdm_rx_hp_filter)]
365 hp_en: true,
366 #[cfg(i2s_supports_pdm_rx_hp_filter)]
367 hp_cut_off_freq_hz: 35.5,
368 #[cfg(i2s_supports_pdm_rx_hp_filter)]
369 amplify_num: 1,
370 }
371 }
372
373 fn raw_default(mode: PdmSlotMode) -> Self {
374 let mut cfg = Self::pcm_default(mode);
375 cfg.data_format = PdmDataFormat::Raw;
376 #[cfg(i2s_supports_pdm_rx_hp_filter)]
377 {
378 cfg.hp_en = false;
379 }
380 cfg
381 }
382}
383
384#[derive(Debug, Clone, Copy, PartialEq)]
386#[cfg_attr(feature = "defmt", derive(defmt::Format))]
387pub struct PdmTxConfig {
388 pub clock: PdmTxClockConfig,
390
391 pub slot: PdmTxSlotConfig,
393}
394
395impl PdmTxConfig {
396 pub fn new_codec_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
398 Self {
399 clock: PdmTxClockConfig::codec_default(sample_rate),
400 slot: default_tx_slot(mode),
401 }
402 }
403
404 pub fn new_raw_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
406 Self {
407 clock: PdmTxClockConfig::codec_default(sample_rate),
408 slot: PdmTxSlotConfig::raw_default(mode),
409 }
410 }
411
412 #[cfg(not(i2s_version = "1"))]
414 pub fn new_dac_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
415 Self {
416 clock: PdmTxClockConfig::dac_default(sample_rate),
417 slot: PdmTxSlotConfig::dac_pcm_default(mode),
418 }
419 }
420
421 pub fn validate(&self, info: &Info) -> Result<(), PdmError> {
423 if self.slot.data_format == PdmDataFormat::Pcm && !info.pcm2pdm {
424 return Err(PdmError::PcmFormatUnsupported);
425 }
426 if self.clock.up_sample_fs > 480 {
427 return Err(PdmError::InvalidClock);
428 }
429 Ok(())
430 }
431}
432
433fn default_tx_slot(mode: PdmSlotMode) -> PdmTxSlotConfig {
434 cfg_select! {
435 i2s_supports_pcm2pdm => PdmTxSlotConfig::codec_pcm_default(mode),
436 _ => PdmTxSlotConfig::raw_default(mode),
437 }
438}
439
440#[derive(Debug, Clone, Copy, PartialEq)]
442#[cfg_attr(feature = "defmt", derive(defmt::Format))]
443pub struct PdmRxConfig {
444 pub clock: PdmRxClockConfig,
446 pub slot: PdmRxSlotConfig,
448}
449
450impl PdmRxConfig {
451 pub fn new_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
453 Self {
454 clock: PdmRxClockConfig::default(sample_rate),
455 slot: default_rx_slot(mode),
456 }
457 }
458
459 pub fn new_pcm_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
461 Self {
462 clock: PdmRxClockConfig::default(sample_rate),
463 slot: PdmRxSlotConfig::pcm_default(mode),
464 }
465 }
466
467 pub fn new_raw_default(sample_rate: Rate, mode: PdmSlotMode) -> Self {
469 Self {
470 clock: PdmRxClockConfig::default(sample_rate),
471 slot: PdmRxSlotConfig::raw_default(mode),
472 }
473 }
474
475 pub fn validate(&self, info: &Info) -> Result<(), PdmError> {
477 if self.slot.data_format == PdmDataFormat::Pcm && !info.pdm2pcm {
478 return Err(PdmError::PcmFormatUnsupported);
479 }
480 if self.slot.slot_mask.bits() == 0 {
481 return Err(PdmError::InvalidSlotMask);
482 }
483 Ok(())
484 }
485}
486
487fn default_rx_slot(mode: PdmSlotMode) -> PdmRxSlotConfig {
488 cfg_select! {
489 i2s_supports_pdm2pcm => PdmRxSlotConfig::pcm_default(mode),
490 _ => PdmRxSlotConfig::raw_default(mode),
491 }
492}
493
494#[derive(Debug, Clone, Copy, PartialEq)]
496#[cfg_attr(feature = "defmt", derive(defmt::Format))]
497pub struct PdmConfig {
498 pub tx: Option<PdmTxConfig>,
500 pub rx: Option<PdmRxConfig>,
502 #[cfg(not(i2s_version = "1"))]
504 pub mclk_out: crate::i2s::master::MclkOut,
505}
506
507impl PdmConfig {
508 #[cfg(i2s_supports_pdm_tx)]
510 pub fn tx_only(tx: PdmTxConfig) -> Self {
511 Self {
512 tx: Some(tx),
513 rx: None,
514 #[cfg(not(i2s_version = "1"))]
515 mclk_out: crate::i2s::master::MclkOut::Tx,
516 }
517 }
518
519 #[cfg(i2s_supports_pdm_rx)]
521 pub fn rx_only(rx: PdmRxConfig) -> Self {
522 Self {
523 tx: None,
524 rx: Some(rx),
525 #[cfg(not(i2s_version = "1"))]
526 mclk_out: crate::i2s::master::MclkOut::Rx,
527 }
528 }
529
530 pub fn validate(&self, info: &Info) -> Result<(), PdmError> {
533 if self.tx.is_none() && self.rx.is_none() {
534 return Err(PdmError::DirectionMissing);
535 }
536 if self.tx.is_some() && self.rx.is_some() {
537 return Err(PdmError::DuplexUnsupported);
538 }
539 if self.tx.is_some() && !info.pdm_tx {
540 return Err(PdmError::UnsupportedInstance);
541 }
542 if self.rx.is_some() && !info.pdm_rx {
543 return Err(PdmError::UnsupportedInstance);
544 }
545 if let Some(tx) = &self.tx {
546 tx.validate(info)?;
547 }
548 if let Some(rx) = &self.rx {
549 rx.validate(info)?;
550 }
551 Ok(())
552 }
553}
554
555pub(crate) fn configure_pdm(i2s: &Info, config: &PdmConfig) -> Result<(), ConfigError> {
556 ll::configure_pdm(i2s, config).map_err(ConfigError::Pdm)
557}