From a143a85822d4b35ca6467011871dd6df2552b468 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Wed, 29 Mar 2023 17:18:14 +0800 Subject: [PATCH] i2s: workaround for inacurate PLL frequency after switching --- .../i2s_test_apps/i2s/main/test_i2s.c | 4 +- .../legacy_i2s_driver/main/test_legacy_i2s.c | 4 +- components/hal/esp32/include/hal/i2s_ll.h | 43 +++++---- components/hal/esp32c3/include/hal/i2s_ll.h | 88 ++++++++++-------- components/hal/esp32c6/include/hal/i2s_ll.h | 88 ++++++++++-------- components/hal/esp32h2/include/hal/i2s_ll.h | 88 ++++++++++-------- components/hal/esp32s2/include/hal/i2s_ll.h | 43 +++++---- components/hal/esp32s3/include/hal/i2s_ll.h | 92 ++++++++++--------- 8 files changed, 242 insertions(+), 208 deletions(-) diff --git a/components/driver/test_apps/i2s_test_apps/i2s/main/test_i2s.c b/components/driver/test_apps/i2s_test_apps/i2s/main/test_i2s.c index 03b86f898f..e9e81cde62 100644 --- a/components/driver/test_apps/i2s_test_apps/i2s/main/test_i2s.c +++ b/components/driver/test_apps/i2s_test_apps/i2s/main/test_i2s.c @@ -746,10 +746,8 @@ static void i2s_test_common_sample_rate(i2s_chan_handle_t rx_chan, i2s_std_clk_c esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0); esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0); - /* Test common sample rate - * Workaround: set 12000 as 12001 to bypass the unknown failure, TODO: IDF-6705 */ const uint32_t test_freq[] = { - 8000, 10001, 11025, 12001, 16000, 22050, + 8000, 10000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 128000,144000,196000}; int real_pulse = 0; diff --git a/components/driver/test_apps/i2s_test_apps/legacy_i2s_driver/main/test_legacy_i2s.c b/components/driver/test_apps/i2s_test_apps/legacy_i2s_driver/main/test_legacy_i2s.c index 54436d2385..1c30ab7cd7 100644 --- a/components/driver/test_apps/i2s_test_apps/legacy_i2s_driver/main/test_legacy_i2s.c +++ b/components/driver/test_apps/i2s_test_apps/legacy_i2s_driver/main/test_legacy_i2s.c @@ -874,10 +874,8 @@ static void i2s_test_common_sample_rate(i2s_port_t id) esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0); esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0); - /* Test common sample rate - * Workaround: set 12000 as 12001 to bypass the unknown failure, TODO: IDF-6705 */ const uint32_t test_freq[] = { - 8000, 10000, 11025, 12001, 16000, 22050, + 8000, 10000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 128000, 144000,196000}; int real_pulse = 0; diff --git a/components/hal/esp32/include/hal/i2s_ll.h b/components/hal/esp32/include/hal/i2s_ll.h index e5b2e9a6e2..05f7ebd0fe 100644 --- a/components/hal/esp32/include/hal/i2s_ll.h +++ b/components/hal/esp32/include/hal/i2s_ll.h @@ -285,6 +285,23 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) hw->sample_rate_conf.tx_bck_div_num = val; } +/** + * @brief Configure I2S module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel + * mclk = sclk / (mclk_div + b/a) + * + * @param hw Peripheral I2S hardware instance address. + * @param mclk_div integer part of the division from sclk to mclk + * @param a Denominator of decimal part + * @param b Numerator of decimal part + */ +static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); + hw->clkm_conf.clkm_div_b = b; + hw->clkm_conf.clkm_div_a = a; +} + /** * @brief Configure I2S TX module clock divider * @note mclk on ESP32 is shared by both TX and RX channel @@ -328,26 +345,12 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); - hw->clkm_conf.clkm_div_b = numerator; - hw->clkm_conf.clkm_div_a = denominator; -} - -/** - * @brief Configure I2S module clock divider - * @note mclk on ESP32 is shared by both TX and RX channel - * mclk = sclk / (mclk_div + b/a) - * - * @param hw Peripheral I2S hardware instance address. - * @param mclk_div integer part of the division from sclk to mclk - * @param a Denominator of decimal part - * @param b Numerator of decimal part - */ -static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b) -{ - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); - hw->clkm_conf.clkm_div_b = b; - hw->clkm_conf.clkm_div_a = a; + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */ + i2s_ll_set_raw_mclk_div(hw, 13, 48, 1); + i2s_ll_set_raw_mclk_div(hw, mclk_div, denominator, numerator); } /** diff --git a/components/hal/esp32c3/include/hal/i2s_ll.h b/components/hal/esp32c3/include/hal/i2s_ll.h index ab1fb68cdc..fd30cd6315 100644 --- a/components/hal/esp32c3/include/hal/i2s_ll.h +++ b/components/hal/esp32c3/include/hal/i2s_ll.h @@ -254,10 +254,12 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) */ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1) { - hw->tx_clkm_div_conf.tx_clkm_div_x = x; - hw->tx_clkm_div_conf.tx_clkm_div_y = y; - hw->tx_clkm_div_conf.tx_clkm_div_z = z; - hw->tx_clkm_div_conf.tx_clkm_div_yn1 = yn1; + typeof(hw->tx_clkm_div_conf) div = {}; + div.tx_clkm_div_x = x; + div.tx_clkm_div_y = y; + div.tx_clkm_div_z = z; + div.tx_clkm_div_yn1 = yn1; + hw->tx_clkm_div_conf.val = div.val; } /** @@ -271,10 +273,12 @@ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t */ static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1) { - hw->rx_clkm_div_conf.rx_clkm_div_x = x; - hw->rx_clkm_div_conf.rx_clkm_div_y = y; - hw->rx_clkm_div_conf.rx_clkm_div_z = z; - hw->rx_clkm_div_conf.rx_clkm_div_yn1 = yn1; + typeof(hw->rx_clkm_div_conf) div = {}; + div.rx_clkm_div_x = x; + div.rx_clkm_div_y = y; + div.rx_clkm_div_z = z; + div.rx_clkm_div_yn1 = yn1; + hw->rx_clkm_div_conf.val = div.val; } /** @@ -319,23 +323,25 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - if (denominator == 0 || numerator == 0) { - hw->tx_clkm_div_conf.tx_clkm_div_x = 0; - hw->tx_clkm_div_conf.tx_clkm_div_y = 0; - hw->tx_clkm_div_conf.tx_clkm_div_z = 0; - } else { - if (numerator > denominator / 2) { - hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / (denominator - numerator) - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % (denominator - numerator); - hw->tx_clkm_div_conf.tx_clkm_div_z = denominator - numerator; - hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; - } else { - hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator; - hw->tx_clkm_div_conf.tx_clkm_div_z = numerator; - hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; - } + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */ + i2s_ll_tx_set_raw_clk_div(hw, 47, 0, 1, 0); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, 13); + + uint32_t div_x = 0; + uint32_t div_y = 0; + uint32_t div_z = 0; + uint32_t div_yn1 = 0; + /* If any of denominator and numerator is 0, set all the coefficients to 0 */ + if (denominator && numerator) { + div_yn1 = numerator * 2 > denominator; + div_z = div_yn1 ? denominator - numerator : numerator; + div_x = denominator / div_z - 1; + div_y = denominator % div_z; } + i2s_ll_tx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1); HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div); } @@ -393,23 +399,25 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - if (denominator == 0 || numerator == 0) { - hw->rx_clkm_div_conf.rx_clkm_div_x = 0; - hw->rx_clkm_div_conf.rx_clkm_div_y = 0; - hw->rx_clkm_div_conf.rx_clkm_div_z = 0; - } else { - if (numerator > denominator / 2) { - hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / (denominator - numerator) - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % (denominator - numerator); - hw->rx_clkm_div_conf.rx_clkm_div_z = denominator - numerator; - hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1; - } else { - hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator; - hw->rx_clkm_div_conf.rx_clkm_div_z = numerator; - hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; - } + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */ + i2s_ll_rx_set_raw_clk_div(hw, 47, 0, 1, 0); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, 13); + + uint32_t div_x = 0; + uint32_t div_y = 0; + uint32_t div_z = 0; + uint32_t div_yn1 = 0; + /* If any of denominator and numerator is 0, set all the coefficients to 0 */ + if (denominator && numerator) { + div_yn1 = numerator * 2 > denominator; + div_z = div_yn1 ? denominator - numerator : numerator; + div_x = denominator / div_z - 1; + div_y = denominator % div_z; } + i2s_ll_rx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1); HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div); } diff --git a/components/hal/esp32c6/include/hal/i2s_ll.h b/components/hal/esp32c6/include/hal/i2s_ll.h index a3a763a2ea..6cc7d9a266 100644 --- a/components/hal/esp32c6/include/hal/i2s_ll.h +++ b/components/hal/esp32c6/include/hal/i2s_ll.h @@ -266,10 +266,12 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1) { (void)hw; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = x; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = y; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = z; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = yn1; + typeof(PCR.i2s_tx_clkm_div_conf) div = {}; + div.i2s_tx_clkm_div_x = x; + div.i2s_tx_clkm_div_y = y; + div.i2s_tx_clkm_div_z = z; + div.i2s_tx_clkm_div_yn1 = yn1; + PCR.i2s_tx_clkm_div_conf.val = div.val; } /** @@ -284,10 +286,12 @@ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1) { (void)hw; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = x; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = y; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = z; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = yn1; + typeof(PCR.i2s_rx_clkm_div_conf) div = {}; + div.i2s_rx_clkm_div_x = x; + div.i2s_rx_clkm_div_y = y; + div.i2s_rx_clkm_div_z = z; + div.i2s_rx_clkm_div_yn1 = yn1; + PCR.i2s_rx_clkm_div_conf.val = div.val; } /** @@ -333,23 +337,25 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - if (denominator == 0 || numerator == 0) { - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = 0; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = 0; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = 0; - } else { - if (numerator > denominator / 2) { - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = denominator / (denominator - numerator) - 1; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = denominator % (denominator - numerator); - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = denominator - numerator; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = 1; - } else { - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = denominator / numerator - 1; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = denominator % numerator; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = numerator; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = 0; - } + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */ + i2s_ll_tx_set_raw_clk_div(hw, 47, 0, 1, 0); + HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_tx_clkm_conf, i2s_tx_clkm_div_num, 13); + + uint32_t div_x = 0; + uint32_t div_y = 0; + uint32_t div_z = 0; + uint32_t div_yn1 = 0; + /* If any of denominator and numerator is 0, set all the coefficients to 0 */ + if (denominator && numerator) { + div_yn1 = numerator * 2 > denominator; + div_z = div_yn1 ? denominator - numerator : numerator; + div_x = denominator / div_z - 1; + div_y = denominator % div_z; } + i2s_ll_tx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1); HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_tx_clkm_conf, i2s_tx_clkm_div_num, mclk_div); } @@ -408,23 +414,25 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - if (denominator == 0 || numerator == 0) { - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = 0; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = 0; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = 0; - } else { - if (numerator > denominator / 2) { - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = denominator / (denominator - numerator) - 1; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = denominator % (denominator - numerator); - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = denominator - numerator; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = 1; - } else { - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = denominator / numerator - 1; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = denominator % numerator; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = numerator; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = 0; - } + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */ + i2s_ll_rx_set_raw_clk_div(hw, 47, 0, 1, 0); + HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_rx_clkm_conf, i2s_rx_clkm_div_num, 13); + + uint32_t div_x = 0; + uint32_t div_y = 0; + uint32_t div_z = 0; + uint32_t div_yn1 = 0; + /* If any of denominator and numerator is 0, set all the coefficients to 0 */ + if (denominator && numerator) { + div_yn1 = numerator * 2 > denominator; + div_z = div_yn1 ? denominator - numerator : numerator; + div_x = denominator / div_z - 1; + div_y = denominator % div_z; } + i2s_ll_rx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1); HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_rx_clkm_conf, i2s_rx_clkm_div_num, mclk_div); } diff --git a/components/hal/esp32h2/include/hal/i2s_ll.h b/components/hal/esp32h2/include/hal/i2s_ll.h index df18b690e7..172562781d 100644 --- a/components/hal/esp32h2/include/hal/i2s_ll.h +++ b/components/hal/esp32h2/include/hal/i2s_ll.h @@ -273,10 +273,12 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1) { (void)hw; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = x; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = y; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = z; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = yn1; + typeof(PCR.i2s_tx_clkm_div_conf) div = {}; + div.i2s_tx_clkm_div_x = x; + div.i2s_tx_clkm_div_y = y; + div.i2s_tx_clkm_div_z = z; + div.i2s_tx_clkm_div_yn1 = yn1; + PCR.i2s_tx_clkm_div_conf.val = div.val; } /** @@ -291,10 +293,12 @@ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1) { (void)hw; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = x; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = y; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = z; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = yn1; + typeof(PCR.i2s_rx_clkm_div_conf) div = {}; + div.i2s_rx_clkm_div_x = x; + div.i2s_rx_clkm_div_y = y; + div.i2s_rx_clkm_div_z = z; + div.i2s_rx_clkm_div_yn1 = yn1; + PCR.i2s_rx_clkm_div_conf.val = div.val; } /** @@ -340,23 +344,25 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - if (denominator == 0 || numerator == 0) { - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = 0; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = 0; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = 0; - } else { - if (numerator > denominator / 2) { - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = denominator / (denominator - numerator) - 1; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = denominator % (denominator - numerator); - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = denominator - numerator; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = 1; - } else { - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = denominator / numerator - 1; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = denominator % numerator; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = numerator; - PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = 0; - } + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @96MHz sclk */ + i2s_ll_tx_set_raw_clk_div(hw, 1, 1, 73, 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_tx_clkm_conf, i2s_tx_clkm_div_num, 8); + + uint32_t div_x = 0; + uint32_t div_y = 0; + uint32_t div_z = 0; + uint32_t div_yn1 = 0; + /* If any of denominator and numerator is 0, set all the coefficients to 0 */ + if (denominator && numerator) { + div_yn1 = numerator * 2 > denominator; + div_z = div_yn1 ? denominator - numerator : numerator; + div_x = denominator / div_z - 1; + div_y = denominator % div_z; } + i2s_ll_tx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1); HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_tx_clkm_conf, i2s_tx_clkm_div_num, mclk_div); } @@ -415,23 +421,25 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - if (denominator == 0 || numerator == 0) { - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = 0; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = 0; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = 0; - } else { - if (numerator > denominator / 2) { - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = denominator / (denominator - numerator) - 1; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = denominator % (denominator - numerator); - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = denominator - numerator; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = 1; - } else { - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = denominator / numerator - 1; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = denominator % numerator; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = numerator; - PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = 0; - } + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @96MHz sclk */ + i2s_ll_rx_set_raw_clk_div(hw, 1, 1, 73, 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_rx_clkm_conf, i2s_rx_clkm_div_num, 8); + + uint32_t div_x = 0; + uint32_t div_y = 0; + uint32_t div_z = 0; + uint32_t div_yn1 = 0; + /* If any of denominator and numerator is 0, set all the coefficients to 0 */ + if (denominator && numerator) { + div_yn1 = numerator * 2 > denominator; + div_z = div_yn1 ? denominator - numerator : numerator; + div_x = denominator / div_z - 1; + div_y = denominator % div_z; } + i2s_ll_rx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1); HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_rx_clkm_conf, i2s_rx_clkm_div_num, mclk_div); } diff --git a/components/hal/esp32s2/include/hal/i2s_ll.h b/components/hal/esp32s2/include/hal/i2s_ll.h index 8a422e7096..54bce163e0 100644 --- a/components/hal/esp32s2/include/hal/i2s_ll.h +++ b/components/hal/esp32s2/include/hal/i2s_ll.h @@ -276,6 +276,23 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) hw->sample_rate_conf.tx_bck_div_num = val; } +/** + * @brief Configure I2S module clock divider + * @note mclk on ESP32 is shared by both TX and RX channel + * mclk = sclk / (mclk_div + b/a) + * + * @param hw Peripheral I2S hardware instance address. + * @param mclk_div integer part of the division from sclk to mclk + * @param a Denominator of decimal part + * @param b Numerator of decimal part + */ +static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); + hw->clkm_conf.clkm_div_b = b; + hw->clkm_conf.clkm_div_a = a; +} + /** * @brief Configure I2S TX module clock divider * @note mclk on ESP32S2 is shared by both TX and RX channel @@ -319,26 +336,12 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); - hw->clkm_conf.clkm_div_b = numerator; - hw->clkm_conf.clkm_div_a = denominator; -} - -/** - * @brief Configure I2S module clock divider - * @note mclk on ESP32 is shared by both TX and RX channel - * mclk = sclk / (mclk_div + b/a) - * - * @param hw Peripheral I2S hardware instance address. - * @param mclk_div integer part of the division from sclk to mclk - * @param a Denominator of decimal part - * @param b Numerator of decimal part - */ -static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b) -{ - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div); - hw->clkm_conf.clkm_div_b = b; - hw->clkm_conf.clkm_div_a = a; + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */ + i2s_ll_set_raw_mclk_div(hw, 13, 48, 1); + i2s_ll_set_raw_mclk_div(hw, mclk_div, denominator, numerator); } /** diff --git a/components/hal/esp32s3/include/hal/i2s_ll.h b/components/hal/esp32s3/include/hal/i2s_ll.h index a30771ade8..16a5d8b683 100644 --- a/components/hal/esp32s3/include/hal/i2s_ll.h +++ b/components/hal/esp32s3/include/hal/i2s_ll.h @@ -254,10 +254,12 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val) */ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1) { - hw->tx_clkm_div_conf.tx_clkm_div_x = x; - hw->tx_clkm_div_conf.tx_clkm_div_y = y; - hw->tx_clkm_div_conf.tx_clkm_div_z = z; - hw->tx_clkm_div_conf.tx_clkm_div_yn1 = yn1; + typeof(hw->tx_clkm_div_conf) div = {}; + div.tx_clkm_div_x = x; + div.tx_clkm_div_y = y; + div.tx_clkm_div_z = z; + div.tx_clkm_div_yn1 = yn1; + hw->tx_clkm_div_conf.val = div.val; } /** @@ -271,10 +273,12 @@ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t */ static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1) { - hw->rx_clkm_div_conf.rx_clkm_div_x = x; - hw->rx_clkm_div_conf.rx_clkm_div_y = y; - hw->rx_clkm_div_conf.rx_clkm_div_z = z; - hw->rx_clkm_div_conf.rx_clkm_div_yn1 = yn1; + typeof(hw->rx_clkm_div_conf) div = {}; + div.rx_clkm_div_x = x; + div.rx_clkm_div_y = y; + div.rx_clkm_div_z = z; + div.rx_clkm_div_yn1 = yn1; + hw->rx_clkm_div_conf.val = div.val; } /** @@ -319,24 +323,26 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div); - if (denominator == 0 || numerator == 0) { - hw->tx_clkm_div_conf.tx_clkm_div_x = 0; - hw->tx_clkm_div_conf.tx_clkm_div_y = 0; - hw->tx_clkm_div_conf.tx_clkm_div_z = 0; - } else { - if (numerator > denominator / 2) { - hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / (denominator - numerator) - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % (denominator - numerator); - hw->tx_clkm_div_conf.tx_clkm_div_z = denominator - numerator; - hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1; - } else { - hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1; - hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator; - hw->tx_clkm_div_conf.tx_clkm_div_z = numerator; - hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0; - } + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */ + i2s_ll_tx_set_raw_clk_div(hw, 47, 0, 1, 0); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, 13); + + uint32_t div_x = 0; + uint32_t div_y = 0; + uint32_t div_z = 0; + uint32_t div_yn1 = 0; + /* If any of denominator and numerator is 0, set all the coefficients to 0 */ + if (denominator && numerator) { + div_yn1 = numerator * 2 > denominator; + div_z = div_yn1 ? denominator - numerator : numerator; + div_x = denominator / div_z - 1; + div_y = denominator % div_z; } + i2s_ll_tx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div); } /** @@ -393,24 +399,26 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl } } finish: - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div); - if (denominator == 0 || numerator == 0) { - hw->rx_clkm_div_conf.rx_clkm_div_x = 0; - hw->rx_clkm_div_conf.rx_clkm_div_y = 0; - hw->rx_clkm_div_conf.rx_clkm_div_z = 0; - } else { - if (numerator > denominator / 2) { - hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / (denominator - numerator) - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % (denominator - numerator); - hw->rx_clkm_div_conf.rx_clkm_div_z = denominator - numerator; - hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1; - } else { - hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1; - hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator; - hw->rx_clkm_div_conf.rx_clkm_div_z = numerator; - hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0; - } + /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate + * Set to particular coefficients first then update to the target coefficients, + * otherwise the clock division might be inaccurate. + * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */ + i2s_ll_rx_set_raw_clk_div(hw, 47, 0, 1, 0); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, 13); + + uint32_t div_x = 0; + uint32_t div_y = 0; + uint32_t div_z = 0; + uint32_t div_yn1 = 0; + /* If any of denominator and numerator is 0, set all the coefficients to 0 */ + if (denominator && numerator) { + div_yn1 = numerator * 2 > denominator; + div_z = div_yn1 ? denominator - numerator : numerator; + div_x = denominator / div_z - 1; + div_y = denominator % div_z; } + i2s_ll_rx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div); } /**