Skip to main content

esp_hal/i2s/pdm/
clock.rs

1//! PDM clock calculations.
2
3use super::{PdmDownsampleRate, PdmError, PdmRxClockConfig, PdmTxClockConfig};
4use crate::{
5    i2s::master::{private::I2sClockDividers, source_frequency},
6    time::Rate,
7};
8
9pub(crate) const PDM_BCK_FACTOR: u32 = 64;
10pub(crate) const PDM_TX_BCLK_DIV_MIN: u32 = 8;
11pub(crate) const PDM_RX_BCLK_DIV_MIN: u32 = 8;
12
13pub(crate) struct PdmTxClockResult {
14    pub dividers: I2sClockDividers,
15    pub over_sample_ratio: u32,
16}
17
18pub(crate) struct PdmRxClockResult {
19    pub dividers: I2sClockDividers,
20}
21
22fn calculate_mclk_dividers(sclk: u32, mclk: u32) -> Result<I2sClockDividers, PdmError> {
23    // Require `sclk > mclk * 1.99` — use integer math to avoid soft-float.
24    if (sclk as u64) * 100 <= (mclk as u64) * 199 {
25        return Err(PdmError::InvalidClock);
26    }
27
28    let dividers = I2sClockDividers::from_frequencies(sclk, mclk, 0);
29
30    if dividers.mclk_divider >= 256 {
31        return Err(PdmError::InvalidClock);
32    }
33
34    Ok(dividers)
35}
36
37pub(crate) fn calculate_tx_clock(
38    clk: &PdmTxClockConfig,
39    pcm: bool,
40) -> Result<PdmTxClockResult, PdmError> {
41    if clk.up_sample_fs > 480 {
42        return Err(PdmError::InvalidClock);
43    }
44
45    let rate = clk.sample_rate.as_hz();
46    let bclk_div = clk.bclk_div.max(PDM_TX_BCLK_DIV_MIN);
47
48    let (bclk, over_sample_ratio) = if pcm {
49        let over_sample_ratio = clk.up_sample_fp / clk.up_sample_fs.max(1);
50        (rate * PDM_BCK_FACTOR * over_sample_ratio, over_sample_ratio)
51    } else {
52        (rate * 2, 0)
53    };
54
55    let mclk = bclk * bclk_div;
56    let sclk = source_frequency(clk.clock_source);
57    let mut dividers = calculate_mclk_dividers(sclk, mclk)?;
58    dividers.bclk_divider = bclk_div;
59
60    Ok(PdmTxClockResult {
61        dividers,
62        over_sample_ratio,
63    })
64}
65
66const PDM_RX_CLK_LIMIT_COEFF: u32 = 128;
67
68pub(crate) fn calculate_rx_clock(
69    clk: &PdmRxClockConfig,
70    pcm: bool,
71    slot_mask: u16,
72) -> Result<PdmRxClockResult, PdmError> {
73    let rate = clk.sample_rate.as_hz();
74    let dn_sample_factor = PDM_BCK_FACTOR * clk.downsample_rate.factor();
75    let slot_num = slot_mask.count_ones().max(1);
76
77    let bclk = if pcm {
78        rate * dn_sample_factor
79    } else {
80        rate * 2
81    };
82
83    let bclk_limit = (PDM_RX_CLK_LIMIT_COEFF * slot_num).div_ceil(dn_sample_factor);
84    let bclk_div = clk.bclk_div.max(PDM_RX_BCLK_DIV_MIN).max(bclk_limit);
85
86    let mclk = bclk * bclk_div;
87    let sclk = source_frequency(clk.clock_source);
88    let mut dividers = calculate_mclk_dividers(sclk, mclk)?;
89    dividers.bclk_divider = bclk_div;
90
91    Ok(PdmRxClockResult { dividers })
92}
93
94impl PdmDownsampleRate {
95    fn factor(self) -> u32 {
96        match self {
97            PdmDownsampleRate::Dsr8s => 1,
98            PdmDownsampleRate::Dsr16s => 2,
99        }
100    }
101}
102
103impl PdmTxClockConfig {
104    /// Default codec-line PDM TX clock (`I2S_PDM_TX_CLK_DEFAULT_CONFIG`).
105    pub fn codec_default(sample_rate: Rate) -> Self {
106        Self {
107            sample_rate,
108            up_sample_fp: 960,
109            up_sample_fs: 480,
110            bclk_div: PDM_TX_BCLK_DIV_MIN,
111            clock_source: Default::default(),
112        }
113    }
114
115    /// Default DAC-line PDM TX clock (`I2S_PDM_TX_CLK_DAC_DEFAULT_CONFIG`).
116    pub fn dac_default(sample_rate: Rate) -> Self {
117        Self {
118            sample_rate,
119            up_sample_fp: 960,
120            up_sample_fs: sample_rate.as_hz() / 100,
121            bclk_div: 13,
122            clock_source: Default::default(),
123        }
124    }
125}
126
127impl PdmRxClockConfig {
128    /// Default PDM RX clock (`I2S_PDM_RX_CLK_DEFAULT_CONFIG`).
129    pub fn default(sample_rate: Rate) -> Self {
130        Self {
131            sample_rate,
132            downsample_rate: PdmDownsampleRate::Dsr8s,
133            bclk_div: PDM_RX_BCLK_DIV_MIN,
134            clock_source: Default::default(),
135        }
136    }
137}