components/driver/i2s.c
/**
 * @brief     APLL calculate function, was described by following:
 *            APLL Output frequency is given by the formula:
 *
 *            apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + 2) * 2)
 *            apll_freq = fout / ((o_div + 2) * 2)
 *
 *            The dividend in this expression should be in the range of 240 - 600 MHz.
 *            In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0.
 *            * sdm0  frequency adjustment parameter, 0..255
 *            * sdm1  frequency adjustment parameter, 0..255
 *            * sdm2  frequency adjustment parameter, 0..63
 *            * o_div  frequency divider, 0..31
 *
 *            The most accurate way to find the sdm0..2 and odir parameters is to loop through them all,
 *            then apply the above formula, finding the closest frequency to the desired one.
 *            But 256*256*64*32 = 134,217,728 loops are too slow with ESP32
 *            1. We will choose the parameters with the highest level of change,
 *               With 350MHz<fout<500MHz, we limit the sdm2 from 4 to 9,
 *               Take average frequency close to the desired frequency, and select sdm2
 *            2. Next, we look for sequences of less influential and more detailed parameters,
 *               also by taking the average of the largest and smallest frequencies closer to the desired frequency.
 *            3. And finally, loop through all the most detailed of the parameters, finding the best desired frequency
 *
 * @param[in]       rate             The I2S Frequency (MCLK)
 * @param[in]       bits_per_sample  The bits per sample
 * @param[out]      sdm0             The sdm 0
 * @param[out]      sdm1             The sdm 1
 * @param[out]      sdm2             The sdm 2
 * @param[out]      odir             The odir
 */
@brief     APLL calculate function, was described by following:
APLL Output frequency is given by the formula.



The dividend in this expression should be in the range of 240 - 600 MHz.
In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0.
sdm0  frequency adjustment parameter, 0..255
sdm1  frequency adjustment parameter, 0..255
sdm2  frequency adjustment parameter, 0..63
o_div  frequency divider, 0..31

The most accurate way to find the sdm0..2 and odir parameters is to loop through them all,
then apply the above formula, finding the closest frequency to the desired one.


@brief APLL calculate function was described by following : APLL Output frequency is given by the formula . The dividend in this expression should be in the range of 240 - 600 MHz . In rev . 0 of ESP32 sdm0 and sdm1 are unused and always set to 0 . sdm0 frequency adjustment parameter 0 .. 255 sdm1 frequency adjustment parameter 0 .. 255 sdm2 frequency adjustment parameter 0 .. 63 o_div frequency divider 0 .. 31 The most accurate way to find the sdm0 .. 2 and odir parameters is to loop through them all then apply the above formula finding the closest frequency to the desired one .
static void i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm0, int *sdm1, int *sdm2, int *odir)
{
    int _odir, _sdm0, _sdm1, _sdm2;
    float avg;
    float min_rate, max_rate, min_diff;
    *sdm0 = 0;
    *sdm1 = 0;
    *sdm2 = 0;
    *odir = 0;
    min_diff = SOC_I2S_APLL_MAX_FREQ;
    for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) {
        max_rate = i2s_apll_get_freq(bits_per_sample, 255, 255, _sdm2, 0);
        min_rate = i2s_apll_get_freq(bits_per_sample, 0, 0, _sdm2, 31);
        avg = (max_rate + min_rate) / 2;
        if (abs(avg - rate) < min_diff) {
            min_diff = abs(avg - rate);
            *sdm2 = _sdm2;
        }
    }
    min_diff = SOC_I2S_APLL_MAX_FREQ;
    for (_odir = 0; _odir < 32; _odir ++) {
        max_rate = i2s_apll_get_freq(bits_per_sample, 255, 255, *sdm2, _odir);
        min_rate = i2s_apll_get_freq(bits_per_sample, 0, 0, *sdm2, _odir);
        avg = (max_rate + min_rate) / 2;
        if (abs(avg - rate) < min_diff) {
            min_diff = abs(avg - rate);
            *odir = _odir;
        }
    }
    min_diff = SOC_I2S_APLL_MAX_FREQ;
    for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) {
        max_rate = i2s_apll_get_freq(bits_per_sample, 255, 255, _sdm2, *odir);
        min_rate = i2s_apll_get_freq(bits_per_sample, 0, 0, _sdm2, *odir);
        avg = (max_rate + min_rate) / 2;
        if (abs(avg - rate) < min_diff) {
            min_diff = abs(avg - rate);
            *sdm2 = _sdm2;
        }
    }
    min_diff = SOC_I2S_APLL_MAX_FREQ;
    for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) {
        max_rate = i2s_apll_get_freq(bits_per_sample, 255, _sdm1, *sdm2, *odir);
        min_rate = i2s_apll_get_freq(bits_per_sample, 0, _sdm1, *sdm2, *odir);
        avg = (max_rate + min_rate) / 2;
        if (abs(avg - rate) < min_diff) {
            min_diff = abs(avg - rate);
            *sdm1 = _sdm1;
        }
    }
    min_diff = SOC_I2S_APLL_MAX_FREQ;
    for (_sdm0 = 0; _sdm0 < 256; _sdm0 ++) {
        avg = i2s_apll_get_freq(bits_per_sample, _sdm0, *sdm1, *sdm2, *odir);
        if (abs(avg - rate) < min_diff) {
            min_diff = abs(avg - rate);
            *sdm0 = _sdm0;
        }
    }
}
static void i2s_apll_calculate_fi2s ( int rate , int bits_per_sample , int * sdm0 , int * sdm1 , int * sdm2 , int * odir ) { int _odir , _sdm0 , _sdm1 , _sdm2 ; float avg ; float min_rate , max_rate , min_diff ; * sdm0 = 0 ; * sdm1 = 0 ; * sdm2 = 0 ; * odir = 0 ; min_diff = SOC_I2S_APLL_MAX_FREQ ; for ( _sdm2 = 4 ; _sdm2 < 9 ; _sdm2 ++ ) { max_rate = i2s_apll_get_freq ( bits_per_sample , 255 , 255 , _sdm2 , 0 ) ; min_rate = i2s_apll_get_freq ( bits_per_sample , 0 , 0 , _sdm2 , 31 ) ; avg = ( max_rate + min_rate ) / 2 ; if ( abs ( avg - rate ) < min_diff ) { min_diff = abs ( avg - rate ) ; * sdm2 = _sdm2 ; } } min_diff = SOC_I2S_APLL_MAX_FREQ ; for ( _odir = 0 ; _odir < 32 ; _odir ++ ) { max_rate = i2s_apll_get_freq ( bits_per_sample , 255 , 255 , * sdm2 , _odir ) ; min_rate = i2s_apll_get_freq ( bits_per_sample , 0 , 0 , * sdm2 , _odir ) ; avg = ( max_rate + min_rate ) / 2 ; if ( abs ( avg - rate ) < min_diff ) { min_diff = abs ( avg - rate ) ; * odir = _odir ; } } min_diff = SOC_I2S_APLL_MAX_FREQ ; for ( _sdm2 = 4 ; _sdm2 < 9 ; _sdm2 ++ ) { max_rate = i2s_apll_get_freq ( bits_per_sample , 255 , 255 , _sdm2 , * odir ) ; min_rate = i2s_apll_get_freq ( bits_per_sample , 0 , 0 , _sdm2 , * odir ) ; avg = ( max_rate + min_rate ) / 2 ; if ( abs ( avg - rate ) < min_diff ) { min_diff = abs ( avg - rate ) ; * sdm2 = _sdm2 ; } } min_diff = SOC_I2S_APLL_MAX_FREQ ; for ( _sdm1 = 0 ; _sdm1 < 256 ; _sdm1 ++ ) { max_rate = i2s_apll_get_freq ( bits_per_sample , 255 , _sdm1 , * sdm2 , * odir ) ; min_rate = i2s_apll_get_freq ( bits_per_sample , 0 , _sdm1 , * sdm2 , * odir ) ; avg = ( max_rate + min_rate ) / 2 ; if ( abs ( avg - rate ) < min_diff ) { min_diff = abs ( avg - rate ) ; * sdm1 = _sdm1 ; } } min_diff = SOC_I2S_APLL_MAX_FREQ ; for ( _sdm0 = 0 ; _sdm0 < 256 ; _sdm0 ++ ) { avg = i2s_apll_get_freq ( bits_per_sample , _sdm0 , * sdm1 , * sdm2 , * odir ) ; if ( abs ( avg - rate ) < min_diff ) { min_diff = abs ( avg - rate ) ; * sdm0 = _sdm0 ; } } }
@brief     APLL calculate function, was described by following:
            APLL Output frequency is given by the formula:
@brief APLL calculate function was described by following : APLL Output frequency is given by the formula :