refactor(hal): use hal utils to calculate clock division

This commit is contained in:
laokaiyao 2023-09-11 12:58:38 +08:00
parent 9bec9ccade
commit dd4072a80c
23 changed files with 432 additions and 292 deletions

View File

@ -97,7 +97,7 @@ static esp_err_t s_dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll)
ESP_LOGD(TAG, "[sclk] %"PRIu32" [mclk] %"PRIu32" [mclk_div] %"PRIu32" [bclk] %"PRIu32" [bclk_div] %"PRIu32, sclk, mclk, mclk_div, bclk, bclk_div);
i2s_ll_tx_clk_set_src(s_ddp->periph_dev, is_apll ? I2S_CLK_SRC_APLL : I2S_CLK_SRC_DEFAULT);
i2s_ll_mclk_div_t mclk_div_coeff = {};
hal_utils_clk_div_t mclk_div_coeff = {};
i2s_hal_calc_mclk_precise_division(sclk, mclk, &mclk_div_coeff);
i2s_ll_tx_set_mclk(s_ddp->periph_dev, &mclk_div_coeff);
i2s_ll_tx_set_bck_div_num(s_ddp->periph_dev, bclk_div);

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -18,6 +18,7 @@
#include "hal/spi_ll.h"
#include "hal/dac_ll.h"
#include "hal/adc_ll.h"
#include "hal/hal_utils.h"
#include "soc/lldesc.h"
#include "soc/soc.h"
#include "soc/soc_caps.h"
@ -99,36 +100,21 @@ static esp_err_t s_dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll){
}
ESP_RETURN_ON_FALSE(interval * 256 > total_div, ESP_ERR_INVALID_ARG, TAG, "the DAC frequency is too small");
/* Step 3: Calculate the coefficients of ADC digital controller divider*/
uint32_t fsclk = interval * freq_hz; /* The clock frequency that produced by ADC controller divider */
uint32_t clk_div = digi_ctrl_freq / fsclk;
uint32_t mod = digi_ctrl_freq % fsclk;
uint32_t a = 0;
uint32_t b = 1;
if (mod == 0) {
goto finish;
}
uint32_t min_diff = mod + 1;
for (uint32_t tmp_b = 1; tmp_b < 64; tmp_b++) {
uint32_t tmp_a = (uint32_t)(((mod * b) / (float)fsclk) + 0.5);
uint32_t diff = (uint32_t)abs((int)(mod * tmp_b) - (int)(fsclk * tmp_a));
if (diff == 0) {
a = tmp_a;
b = tmp_b;
goto finish;
}
if (diff < min_diff) {
min_diff = diff;
a = tmp_a;
b = tmp_b;
}
}
/* Step 3: Calculate the coefficients of ADC digital controller divider */
hal_utils_clk_info_t adc_clk_info = {
.src_freq_hz = digi_ctrl_freq / interval,
.exp_freq_hz = freq_hz,
.max_integ = 257,
.min_integ = 1,
.max_fract = 64,
};
hal_utils_clk_div_t adc_clk_div = {};
hal_utils_calc_clk_div_frac_accurate(&adc_clk_info, &adc_clk_div);
finish:
/* Step 4: Set the clock coefficients */
dac_ll_digi_clk_inv(true);
dac_ll_digi_set_trigger_interval(interval); // secondary clock division
adc_ll_digi_controller_clk_div(clk_div - 1, b, a);
adc_ll_digi_controller_clk_div(adc_clk_div.integer - 1, adc_clk_div.denominator, adc_clk_div.numerator);
adc_ll_digi_clk_sel(is_apll ? ADC_DIGI_CLK_SRC_APLL : ADC_DIGI_CLK_SRC_DEFAULT);
return ESP_OK;
}

View File

@ -9,3 +9,4 @@ archive: libhal.a
entries:
if LCD_RGB_ISR_IRAM_SAFE = y:
lcd_hal: lcd_hal_cal_pclk_freq (noflash)
hal_utils: hal_utils_calc_clk_div_frac_fast (noflash)

View File

@ -0,0 +1,7 @@
components/hal/test_apps/hal_utils:
enable:
- if: IDF_TARGET == "linux"
disable:
- if: IDF_TARGET == "linux"
temporary: true
reason: env not ready

View File

@ -180,7 +180,7 @@ static void adc_hal_digi_sample_freq_config(adc_hal_dma_ctx_t *hal, adc_continuo
uint32_t bclk_div = 16;
uint32_t bclk = sample_freq_hz * 2;
uint32_t mclk = bclk * bclk_div;
i2s_ll_mclk_div_t mclk_div = {};
hal_utils_clk_div_t mclk_div = {};
i2s_hal_calc_mclk_precise_division(I2S_BASE_CLK, mclk, &mclk_div);
i2s_ll_rx_set_mclk(hal->dev, &mclk_div);
i2s_ll_rx_set_bck_div_num(hal->dev, bclk_div);

View File

@ -19,6 +19,8 @@
#include "soc/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "hal/i2s_types.h"
#include "hal/hal_utils.h"
#ifdef __cplusplus
extern "C" {
@ -30,8 +32,8 @@ extern "C" {
#define I2S_LL_AD_BCK_FACTOR (2)
#define I2S_LL_PDM_BCK_FACTOR (64)
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (6)
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
#define I2S_LL_CLK_FRAC_DIV_N_MAX 256 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the N register is 8 bit-width
#define I2S_LL_CLK_FRAC_DIV_AB_MAX 64 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the a/b register is 6 bit-width
#define I2S_LL_BCK_MAX_PRESCALE (64)
@ -48,16 +50,6 @@ extern "C" {
#define I2S_LL_PLL_F160M_CLK_FREQ (160 * 1000000) // PLL_F160M_CLK: 160MHz
#define I2S_LL_DEFAULT_PLL_CLK_FREQ I2S_LL_PLL_F160M_CLK_FREQ // The default PLL clock frequency while using I2S_CLK_SRC_DEFAULT
/**
* @brief I2S clock configuration structure
* @note Fmclk = Fsclk /(integ+numer/denom)
*/
typedef struct {
uint16_t integ; // Integer part of I2S module clock divider
uint16_t denom; // Denominator part of I2S module clock divider
uint16_t numer; // Numerator part of I2S module clock divider
} i2s_ll_mclk_div_t;
/**
* @brief Enable DMA descriptor owner check
*
@ -312,14 +304,14 @@ static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uin
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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 general idea is to set a value that unlike to calculate from the regular decimal */
i2s_ll_set_raw_mclk_div(hw, 7, 47, 3);
i2s_ll_set_raw_mclk_div(hw, mclk_div->integ, mclk_div->denom, mclk_div->numer);
i2s_ll_set_raw_mclk_div(hw, mclk_div->integer, mclk_div->denominator, mclk_div->numerator);
}
/**
@ -340,7 +332,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
// TX and RX channel on ESP32 shares a same mclk
i2s_ll_tx_set_mclk(hw, mclk_div);

View File

@ -18,6 +18,7 @@
#include "soc/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "hal/i2s_types.h"
#include "hal/hal_utils.h"
#ifdef __cplusplus
@ -29,22 +30,12 @@ extern "C" {
#define I2S_LL_TDM_CH_MASK (0xffff)
#define I2S_LL_PDM_BCK_FACTOR (64)
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9)
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
#define I2S_LL_CLK_FRAC_DIV_N_MAX 256 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the N register is 8 bit-width
#define I2S_LL_CLK_FRAC_DIV_AB_MAX 512 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the a/b register is 9 bit-width
#define I2S_LL_PLL_F160M_CLK_FREQ (160 * 1000000) // PLL_F160M_CLK: 160MHz
#define I2S_LL_DEFAULT_PLL_CLK_FREQ I2S_LL_PLL_F160M_CLK_FREQ // The default PLL clock frequency while using I2S_CLK_SRC_DEFAULT
/**
* @brief I2S clock configuration structure
* @note Fmclk = Fsclk /(integ+numer/denom)
*/
typedef struct {
uint16_t integ; // Integer part of I2S module clock divider
uint16_t denom; // Denominator part of I2S module clock divider
uint16_t numer; // Numerator part of I2S module clock divider
} i2s_ll_mclk_div_t;
/**
* @brief I2S module general init, enable I2S clock.
*
@ -304,7 +295,7 @@ static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t div_int, ui
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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,
@ -317,13 +308,13 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mc
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 (mclk_div->denom && mclk_div->numer) {
div_yn1 = mclk_div->numer * 2 > mclk_div->denom;
div_z = div_yn1 ? mclk_div->denom - mclk_div->numer : mclk_div->numer;
div_x = mclk_div->denom / div_z - 1;
div_y = mclk_div->denom % div_z;
if (mclk_div->denominator && mclk_div->numerator) {
div_yn1 = mclk_div->numerator * 2 > mclk_div->denominator;
div_z = div_yn1 ? mclk_div->denominator - mclk_div->numerator : mclk_div->numerator;
div_x = mclk_div->denominator / div_z - 1;
div_y = mclk_div->denominator % div_z;
}
i2s_ll_tx_set_raw_clk_div(hw, mclk_div->integ, div_x, div_y, div_z, div_yn1);
i2s_ll_tx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
}
/**
@ -344,7 +335,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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,
@ -357,13 +348,13 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mc
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 (mclk_div->denom && mclk_div->numer) {
div_yn1 = mclk_div->numer * 2 > mclk_div->denom;
div_z = div_yn1 ? mclk_div->denom - mclk_div->numer : mclk_div->numer;
div_x = mclk_div->denom / div_z - 1;
div_y = mclk_div->denom % div_z;
if (mclk_div->denominator && mclk_div->numerator) {
div_yn1 = mclk_div->numerator * 2 > mclk_div->denominator;
div_z = div_yn1 ? mclk_div->denominator - mclk_div->numerator : mclk_div->numerator;
div_x = mclk_div->denominator / div_z - 1;
div_y = mclk_div->denominator % div_z;
}
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integ, div_x, div_y, div_z, div_yn1);
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
}
/**

View File

@ -19,6 +19,7 @@
#include "soc/i2s_struct.h"
#include "soc/pcr_struct.h"
#include "hal/i2s_types.h"
#include "hal/hal_utils.h"
#ifdef __cplusplus
@ -30,22 +31,12 @@ extern "C" {
#define I2S_LL_TDM_CH_MASK (0xffff)
#define I2S_LL_PDM_BCK_FACTOR (64)
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9)
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
#define I2S_LL_CLK_FRAC_DIV_N_MAX 256 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the N register is 8 bit-width
#define I2S_LL_CLK_FRAC_DIV_AB_MAX 512 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the a/b register is 9 bit-width
#define I2S_LL_PLL_F160M_CLK_FREQ (160 * 1000000) // PLL_F160M_CLK: 160MHz
#define I2S_LL_DEFAULT_PLL_CLK_FREQ I2S_LL_PLL_F160M_CLK_FREQ // The default PLL clock frequency while using I2S_CLK_SRC_DEFAULT
/**
* @brief I2S clock configuration structure
* @note Fmclk = Fsclk /(integ+numer/denom)
*/
typedef struct {
uint16_t integ; // Integer part of I2S module clock divider
uint16_t denom; // Denominator part of I2S module clock divider
uint16_t numer; // Numerator part of I2S module clock divider
} i2s_ll_mclk_div_t;
/**
* @brief I2S module general init, enable I2S clock.
*
@ -313,7 +304,7 @@ static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t div_int, ui
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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,
@ -326,13 +317,13 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mc
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 (mclk_div->denom && mclk_div->numer) {
div_yn1 = mclk_div->numer * 2 > mclk_div->denom;
div_z = div_yn1 ? mclk_div->denom - mclk_div->numer : mclk_div->numer;
div_x = mclk_div->denom / div_z - 1;
div_y = mclk_div->denom % div_z;
if (mclk_div->denominator && mclk_div->numerator) {
div_yn1 = mclk_div->numerator * 2 > mclk_div->denominator;
div_z = div_yn1 ? mclk_div->denominator - mclk_div->numerator : mclk_div->numerator;
div_x = mclk_div->denominator / div_z - 1;
div_y = mclk_div->denominator % div_z;
}
i2s_ll_tx_set_raw_clk_div(hw, mclk_div->integ, div_x, div_y, div_z, div_yn1);
i2s_ll_tx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
}
/**
@ -353,7 +344,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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,
@ -366,13 +357,13 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mc
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 (mclk_div->denom && mclk_div->numer) {
div_yn1 = mclk_div->numer * 2 > mclk_div->denom;
div_z = div_yn1 ? mclk_div->denom - mclk_div->numer : mclk_div->numer;
div_x = mclk_div->denom / div_z - 1;
div_y = mclk_div->denom % div_z;
if (mclk_div->denominator && mclk_div->numerator) {
div_yn1 = mclk_div->numerator * 2 > mclk_div->denominator;
div_z = div_yn1 ? mclk_div->denominator - mclk_div->numerator : mclk_div->numerator;
div_x = mclk_div->denominator / div_z - 1;
div_y = mclk_div->denominator % div_z;
}
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integ, div_x, div_y, div_z, div_yn1);
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
}
/**

View File

@ -19,6 +19,7 @@
#include "soc/i2s_struct.h"
#include "soc/pcr_struct.h"
#include "hal/i2s_types.h"
#include "hal/hal_utils.h"
#ifdef __cplusplus
@ -30,23 +31,13 @@ extern "C" {
#define I2S_LL_TDM_CH_MASK (0xffff)
#define I2S_LL_PDM_BCK_FACTOR (64)
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9)
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
#define I2S_LL_CLK_FRAC_DIV_N_MAX 256 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the N register is 8 bit-width
#define I2S_LL_CLK_FRAC_DIV_AB_MAX 512 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the a/b register is 9 bit-width
#define I2S_LL_PLL_F96M_CLK_FREQ (96 * 1000000) // PLL_F96M_CLK: 96MHz
#define I2S_LL_PLL_F64M_CLK_FREQ (64 * 1000000) // PLL_F64M_CLK: 64MHz
#define I2S_LL_DEFAULT_PLL_CLK_FREQ I2S_LL_PLL_F96M_CLK_FREQ // The default PLL clock frequency while using I2S_CLK_SRC_DEFAULT
/**
* @brief I2S clock configuration structure
* @note Fmclk = Fsclk /(integ+numer/denom)
*/
typedef struct {
uint16_t integ; // Integer part of I2S module clock divider
uint16_t denom; // Denominator part of I2S module clock divider
uint16_t numer; // Numerator part of I2S module clock divider
} i2s_ll_mclk_div_t;
/**
* @brief I2S module general init, enable I2S clock.
*
@ -320,7 +311,7 @@ static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t div_int, ui
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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,
@ -333,13 +324,13 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mc
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 (mclk_div->denom && mclk_div->numer) {
div_yn1 = mclk_div->numer * 2 > mclk_div->denom;
div_z = div_yn1 ? mclk_div->denom - mclk_div->numer : mclk_div->numer;
div_x = mclk_div->denom / div_z - 1;
div_y = mclk_div->denom % div_z;
if (mclk_div->denominator && mclk_div->numerator) {
div_yn1 = mclk_div->numerator * 2 > mclk_div->denominator;
div_z = div_yn1 ? mclk_div->denominator - mclk_div->numerator : mclk_div->numerator;
div_x = mclk_div->denominator / div_z - 1;
div_y = mclk_div->denominator % div_z;
}
i2s_ll_tx_set_raw_clk_div(hw, mclk_div->integ, div_x, div_y, div_z, div_yn1);
i2s_ll_tx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
}
/**
@ -360,7 +351,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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,
@ -373,13 +364,13 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mc
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 (mclk_div->denom && mclk_div->numer) {
div_yn1 = mclk_div->numer * 2 > mclk_div->denom;
div_z = div_yn1 ? mclk_div->denom - mclk_div->numer : mclk_div->numer;
div_x = mclk_div->denom / div_z - 1;
div_y = mclk_div->denom % div_z;
if (mclk_div->denominator && mclk_div->numerator) {
div_yn1 = mclk_div->numerator * 2 > mclk_div->denominator;
div_z = div_yn1 ? mclk_div->denominator - mclk_div->numerator : mclk_div->numerator;
div_x = mclk_div->denominator / div_z - 1;
div_y = mclk_div->denominator % div_z;
}
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integ, div_x, div_y, div_z, div_yn1);
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
}
/**

View File

@ -19,6 +19,7 @@
#include "soc/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "hal/i2s_types.h"
#include "hal/hal_utils.h"
#ifdef __cplusplus
@ -30,8 +31,9 @@ extern "C" {
#define I2S_LL_BCK_MAX_PRESCALE (64)
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (6)
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
#define I2S_LL_CLK_FRAC_DIV_N_MAX 256 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the N register is 8 bit-width
#define I2S_LL_CLK_FRAC_DIV_AB_MAX 64 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the a/b register is 6 bit-width
#define I2S_LL_EVENT_RX_EOF BIT(9)
#define I2S_LL_EVENT_TX_EOF BIT(12)
@ -45,16 +47,6 @@ extern "C" {
#define I2S_LL_PLL_F160M_CLK_FREQ (160 * 1000000) // PLL_F160M_CLK: 160MHz
#define I2S_LL_DEFAULT_PLL_CLK_FREQ I2S_LL_PLL_F160M_CLK_FREQ // The default PLL clock frequency while using I2S_CLK_SRC_DEFAULT
/**
* @brief I2S clock configuration structure
* @note Fmclk = Fsclk /(integ+numer/denom)
*/
typedef struct {
uint16_t integ; // Integer part of I2S module clock divider
uint16_t denom; // Denominator part of I2S module clock divider
uint16_t numer; // Numerator part of I2S module clock divider
} i2s_ll_mclk_div_t;
/**
* @brief Enable DMA descriptor owner check
*
@ -303,14 +295,14 @@ static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uin
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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 general idea is to set a value that unlike to calculate from the regular decimal */
i2s_ll_set_raw_mclk_div(hw, 7, 47, 3);
i2s_ll_set_raw_mclk_div(hw, mclk_div->integ, mclk_div->denom, mclk_div->numer);
i2s_ll_set_raw_mclk_div(hw, mclk_div->integer, mclk_div->denominator, mclk_div->numerator);
}
/**
@ -331,7 +323,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
// TX and RX channel on ESP32 shares a same mclk
i2s_ll_tx_set_mclk(hw, mclk_div);

View File

@ -18,6 +18,7 @@
#include "soc/i2s_periph.h"
#include "soc/i2s_struct.h"
#include "hal/i2s_types.h"
#include "hal/hal_utils.h"
#ifdef __cplusplus
@ -30,22 +31,12 @@ extern "C" {
#define I2S_LL_TDM_CH_MASK (0xffff)
#define I2S_LL_PDM_BCK_FACTOR (64)
#define I2S_LL_MCLK_DIVIDER_BIT_WIDTH (9)
#define I2S_LL_MCLK_DIVIDER_MAX ((1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1)
#define I2S_LL_CLK_FRAC_DIV_N_MAX 256 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the N register is 8 bit-width
#define I2S_LL_CLK_FRAC_DIV_AB_MAX 512 // I2S_MCLK = I2S_SRC_CLK / (N + b/a), the a/b register is 9 bit-width
#define I2S_LL_PLL_F160M_CLK_FREQ (160 * 1000000) // PLL_F160M_CLK: 160MHz
#define I2S_LL_DEFAULT_PLL_CLK_FREQ I2S_LL_PLL_F160M_CLK_FREQ // The default PLL clock frequency while using I2S_CLK_SRC_DEFAULT
/**
* @brief I2S clock configuration structure
* @note Fmclk = Fsclk /(integ+numer/denom)
*/
typedef struct {
uint16_t integ; // Integer part of I2S module clock divider
uint16_t denom; // Denominator part of I2S module clock divider
uint16_t numer; // Numerator part of I2S module clock divider
} i2s_ll_mclk_div_t;
/**
* @brief I2S module general init, enable I2S clock.
*
@ -304,7 +295,7 @@ static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t div_int, ui
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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,
@ -317,13 +308,13 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mc
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 (mclk_div->denom && mclk_div->numer) {
div_yn1 = mclk_div->numer * 2 > mclk_div->denom;
div_z = div_yn1 ? mclk_div->denom - mclk_div->numer : mclk_div->numer;
div_x = mclk_div->denom / div_z - 1;
div_y = mclk_div->denom % div_z;
if (mclk_div->denominator && mclk_div->numerator) {
div_yn1 = mclk_div->numerator * 2 > mclk_div->denominator;
div_z = div_yn1 ? mclk_div->denominator - mclk_div->numerator : mclk_div->numerator;
div_x = mclk_div->denominator / div_z - 1;
div_y = mclk_div->denominator % div_z;
}
i2s_ll_tx_set_raw_clk_div(hw, mclk_div->integ, div_x, div_y, div_z, div_yn1);
i2s_ll_tx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
}
/**
@ -344,7 +335,7 @@ static inline void i2s_ll_rx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
* @param hw Peripheral I2S hardware instance address.
* @param mclk_div The mclk division coefficients
*/
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mclk_div)
static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *mclk_div)
{
/* 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,
@ -357,13 +348,13 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const i2s_ll_mclk_div_t *mc
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 (mclk_div->denom && mclk_div->numer) {
div_yn1 = mclk_div->numer * 2 > mclk_div->denom;
div_z = div_yn1 ? mclk_div->denom - mclk_div->numer : mclk_div->numer;
div_x = mclk_div->denom / div_z - 1;
div_y = mclk_div->denom % div_z;
if (mclk_div->denominator && mclk_div->numerator) {
div_yn1 = mclk_div->numerator * 2 > mclk_div->denominator;
div_z = div_yn1 ? mclk_div->denominator - mclk_div->numerator : mclk_div->numerator;
div_x = mclk_div->denominator / div_z - 1;
div_y = mclk_div->denominator % div_z;
}
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integ, div_x, div_y, div_z, div_yn1);
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
}

View File

@ -5,6 +5,7 @@
*/
#include "hal/hal_utils.h"
#include "hal/assert.h"
/**
* @brief helper function, calculate the Greatest Common Divisor
@ -31,36 +32,42 @@ static inline uint32_t _sub_abs(uint32_t a, uint32_t b)
return a > b ? a - b : b - a;
}
uint32_t hal_utils_calc_clk_div_fast(const hal_utils_clk_info_t *clk_info, hal_utils_clk_div_t *clk_div)
uint32_t hal_utils_calc_clk_div_frac_fast(const hal_utils_clk_info_t *clk_info, hal_utils_clk_div_t *clk_div)
{
uint32_t div_denom = 1;
HAL_ASSERT(clk_info->max_fract > 2);
uint32_t div_denom = 2;
uint32_t div_numer = 0;
uint32_t div_integ = clk_info->src_freq_hz / clk_info->exp_freq_hz;
uint32_t freq_error = clk_info->src_freq_hz % clk_info->exp_freq_hz;
// fractional divider
if (freq_error) {
// Carry bit if the decimal is greater than 1.0 - 1.0 / ((max_fract - 1) * 2)
if (freq_error < clk_info->exp_freq_hz - clk_info->exp_freq_hz / (clk_info->max_fract - 1) * 2) {
// Calculate the Greatest Common Divisor, time complexity O(log n)
uint32_t gcd = _gcd(clk_info->exp_freq_hz, freq_error);
// divide by the Greatest Common Divisor to get the accurate fraction before normalization
div_denom = clk_info->exp_freq_hz / gcd;
div_numer = freq_error / gcd;
// normalize div_denom and div_numer
uint32_t d = div_denom / clk_info->max_fract + 1;
// divide by the normalization coefficient to get the denominator and numerator within range of clk_info->max_fract
div_denom /= d;
div_numer /= d;
} else {
div_integ++;
}
}
// If the expect frequency is too high or too low to satisfy the integral division range, failed and return 0
if (div_integ < clk_info->min_integ || div_integ > clk_info->max_integ) {
if (div_integ < clk_info->min_integ || div_integ >= clk_info->max_integ || div_integ == 0) {
return 0;
}
// fractional divider
if (freq_error) {
// Calculate the Greatest Common Divisor, time complexity O(log n)
uint32_t gcd = _gcd(clk_info->exp_freq_hz, freq_error);
// divide by the Greatest Common Divisor to get the accurate fraction before normalization
div_denom = clk_info->exp_freq_hz / gcd;
div_numer = freq_error / gcd;
// normalize div_denom and div_numer
uint32_t d = div_denom / clk_info->max_fract + 1;
// divide by the normalization coefficient to get the denominator and numerator within range of clk_info->max_fract
div_denom /= d;
div_numer /= d;
}
// Assign result
clk_div->integ = div_integ;
clk_div->denom = div_denom;
clk_div->numer = div_numer;
clk_div->integer = div_integ;
clk_div->denominator = div_denom;
clk_div->numerator = div_numer;
// Return the actual frequency
if (div_numer) {
@ -70,23 +77,19 @@ uint32_t hal_utils_calc_clk_div_fast(const hal_utils_clk_info_t *clk_info, hal_u
return clk_info->src_freq_hz / div_integ;
}
uint32_t hal_utils_calc_clk_div_accurate(const hal_utils_clk_info_t *clk_info, hal_utils_clk_div_t *clk_div)
uint32_t hal_utils_calc_clk_div_frac_accurate(const hal_utils_clk_info_t *clk_info, hal_utils_clk_div_t *clk_div)
{
uint32_t div_denom = 1;
HAL_ASSERT(clk_info->max_fract > 2);
uint32_t div_denom = 2;
uint32_t div_numer = 0;
uint32_t div_integ = clk_info->src_freq_hz / clk_info->exp_freq_hz;
uint32_t freq_error = clk_info->src_freq_hz % clk_info->exp_freq_hz;
// If the expect frequency is too high to satisfy the minimum integral division, failed and return 0
if (div_integ < clk_info->min_integ) {
return 0;
}
if (freq_error) {
// Carry bit if the decimal is greater than 1.0 - 1.0 / (PARLIO_LL_CLK_DIVIDER_MAX * 2)
if (freq_error < clk_info->exp_freq_hz - clk_info->exp_freq_hz / (clk_info->max_fract * 2)) {
// Carry bit if the decimal is greater than 1.0 - 1.0 / ((max_fract - 1) * 2)
if (freq_error < clk_info->exp_freq_hz - clk_info->exp_freq_hz / (clk_info->max_fract - 1) * 2) {
// Search the closest fraction, time complexity O(n)
for (uint32_t sub = 0, a = 2, b = 0, min = UINT32_MAX; min && a <= clk_info->max_fract; a++) {
for (uint32_t sub = 0, a = 2, b = 0, min = UINT32_MAX; min && a < clk_info->max_fract; a++) {
b = (a * freq_error + clk_info->exp_freq_hz / 2) / clk_info->exp_freq_hz;
sub = _sub_abs(clk_info->exp_freq_hz * b, freq_error * a);
if (sub < min) {
@ -99,15 +102,16 @@ uint32_t hal_utils_calc_clk_div_accurate(const hal_utils_clk_info_t *clk_info, h
div_integ++;
}
}
// If the expect frequency is too low to satisfy the maximum integral division, failed and return 0
if (div_integ > clk_info->max_integ) {
// If the expect frequency is too high or too low to satisfy the integral division range, failed and return 0
if (div_integ < clk_info->min_integ || div_integ >= clk_info->max_integ || div_integ == 0) {
return 0;
}
// Assign result
clk_div->integ = div_integ;
clk_div->denom = div_denom;
clk_div->numer = div_numer;
clk_div->integer = div_integ;
clk_div->denominator = div_denom;
clk_div->numerator = div_numer;
// Return the actual frequency
if (div_numer) {
@ -116,3 +120,22 @@ uint32_t hal_utils_calc_clk_div_accurate(const hal_utils_clk_info_t *clk_info, h
}
return clk_info->src_freq_hz / div_integ;
}
uint32_t hal_utils_calc_clk_div_integer(const hal_utils_clk_info_t *clk_info, uint32_t *int_div)
{
uint32_t div_integ = clk_info->src_freq_hz / clk_info->exp_freq_hz;
uint32_t freq_error = clk_info->src_freq_hz % clk_info->exp_freq_hz;
/* If there is error and always round up,
Or, do the normal rounding and error >= (src/n + src/(n+1)) / 2,
then carry the bit */
if ((freq_error && clk_info->round_opt == HAL_DIV_ROUND_UP) || (clk_info->round_opt == HAL_DIV_ROUND &&
(freq_error >= clk_info->src_freq_hz / (2 * div_integ * (div_integ + 1))))) {
div_integ++;
}
// Assign result
*int_div = div_integ;
// Return the actual frequency
return clk_info->src_freq_hz / div_integ;
}

View File

@ -29,46 +29,18 @@ static const float cut_off_coef[21][3] = {
*
* @param sclk system clock
* @param mclk module clock
* @param integer output the integer part of the division
* @param denominator output the denominator part of the division
* @param numerator output the numerator part of the division
* @param mclk_div output the mclk division coefficients
*/
void i2s_hal_calc_mclk_precise_division(uint32_t sclk, uint32_t mclk, i2s_ll_mclk_div_t *mclk_div)
void i2s_hal_calc_mclk_precise_division(uint32_t sclk, uint32_t mclk, hal_utils_clk_div_t *mclk_div)
{
int ma = 0;
int mb = 0;
int min = INT32_MAX;
uint32_t div_denom = 1;
uint32_t div_numer = 0;
uint32_t div_inter = sclk / mclk;
uint32_t freq_diff = sclk % mclk;
if (freq_diff) {
float decimal = freq_diff / (float)mclk;
// Carry bit if the decimal is greater than 1.0 - 1.0 / (I2S_LL_MCLK_DIVIDER_MAX * 2)
if (decimal <= 1.0 - 1.0 / (float)(I2S_LL_MCLK_DIVIDER_MAX * 2)) {
for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) {
int b = (int)(a * (freq_diff / (double)mclk) + 0.5);
ma = freq_diff * a;
mb = mclk * b;
if (ma == mb) {
div_denom = (uint32_t)a;
div_numer = (uint32_t)b;
break;
}
if (abs(mb - ma) < min) {
div_denom = (uint32_t)a;
div_numer = (uint32_t)b;
min = abs(mb - ma);
}
}
} else {
div_inter++;
}
}
mclk_div->integ = div_inter;
mclk_div->denom = div_denom;
mclk_div->numer = div_numer;
hal_utils_clk_info_t i2s_clk_info = {
.src_freq_hz = sclk,
.exp_freq_hz = mclk,
.max_integ = I2S_LL_CLK_FRAC_DIV_N_MAX,
.min_integ = 1,
.max_fract = I2S_LL_CLK_FRAC_DIV_AB_MAX,
};
hal_utils_calc_clk_div_frac_accurate(&i2s_clk_info, mclk_div);
}
void i2s_hal_init(i2s_hal_context_t *hal, int port_id)
@ -79,7 +51,7 @@ void i2s_hal_init(i2s_hal_context_t *hal, int port_id)
void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src)
{
i2s_ll_mclk_div_t mclk_div = {};
hal_utils_clk_div_t mclk_div = {};
#if SOC_I2S_HW_VERSION_2
i2s_ll_tx_enable_clock(hal->dev);
i2s_ll_mclk_bind_to_tx_clk(hal->dev);
@ -92,7 +64,7 @@ void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *cl
void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src)
{
i2s_ll_mclk_div_t mclk_div = {};
hal_utils_clk_div_t mclk_div = {};
#if SOC_I2S_HW_VERSION_2
i2s_ll_rx_enable_clock(hal->dev);
i2s_ll_mclk_bind_to_rx_clk(hal->dev);

View File

@ -12,6 +12,16 @@
extern "C" {
#endif
/**
* @brief Integer division operation
*
*/
typedef enum {
HAL_DIV_ROUND_DOWN, /*!< Round the division down to the floor integer */
HAL_DIV_ROUND_UP, /*!< Round the division up to the ceiling integer */
HAL_DIV_ROUND, /*!< Round the division to the nearest integer (round up if fraction >= 1/2, round down if fraction < 1/2) */
} hal_utils_div_round_opt_t;
/**
* @brief Clock infomation
*
@ -21,7 +31,11 @@ typedef struct {
uint32_t exp_freq_hz; /*!< Expected output clock frequency, unit: Hz */
uint32_t max_integ; /*!< The max value of the integral part */
uint32_t min_integ; /*!< The min value of the integral part, integer range: [min_integ, max_integ) */
uint32_t max_fract; /*!< The max value of the denominator and numerator, numerator range: [0, max_fract), denominator range: [1, max_fract) */
union {
uint32_t max_fract; /*!< The max value of the denominator and numerator, numerator range: [0, max_fract), denominator range: [1, max_fract)
* Please make sure max_fract > 2 when calculate the division with fractal part */
hal_utils_div_round_opt_t round_opt; /*!< Integer division operation. For the case that doesn't have fractal part, set this field to the to specify the rounding method */
};
} hal_utils_clk_info_t;
/**
@ -29,36 +43,47 @@ typedef struct {
*
*/
typedef struct {
uint32_t integ; /*!< Integer part of division */
uint32_t denom; /*!< Denominator part of division */
uint32_t numer; /*!< Numerator part of division */
uint32_t integer; /*!< Integer part of division */
uint32_t denominator; /*!< Denominator part of division */
uint32_t numerator; /*!< Numerator part of division */
} hal_utils_clk_div_t;
/**
* @brief Calculate the clock division
* @brief Calculate the clock division with fractal part fast
* @note Speed first algorithm, Time complexity O(log n).
* About 8~10 times faster than the accurate algorithm
*
* @param[in] clk_info The clock infomation
* @param[out] clk_div The clock division
* @param[out] clk_div The clock division with integral and fractal part
* @return
* - 0: Failed to get the result because the division is out of range
* - others: The real output clock frequency
*/
uint32_t hal_utils_calc_clk_div_fast(const hal_utils_clk_info_t *clk_info, hal_utils_clk_div_t *clk_div);
uint32_t hal_utils_calc_clk_div_frac_fast(const hal_utils_clk_info_t *clk_info, hal_utils_clk_div_t *clk_div);
/**
* @brief Calculate the clock division
* @brief Calculate the clock division with fractal part accurately
* @note Accuracy first algorithm, Time complexity O(n).
* About 1~hundreds times more accurate than the fast algorithm
*
* @param[in] clk_info The clock infomation
* @param[out] clk_div The clock division
* @param[out] clk_div The clock division with integral and fractal part
* @return
* - 0: Failed to get the result because the division is out of range
* - others: The real output clock frequency
*/
uint32_t hal_utils_calc_clk_div_accurate(const hal_utils_clk_info_t *clk_info, hal_utils_clk_div_t *clk_div);
uint32_t hal_utils_calc_clk_div_frac_accurate(const hal_utils_clk_info_t *clk_info, hal_utils_clk_div_t *clk_div);
/**
* @brief Calculate the clock division without fractal part
*
* @param[in] clk_info The clock infomation
* @param[out] int_div The clock integral division
* @return
* - 0: Failed to get the result because the division is out of range
* - others: The real output clock frequency
*/
uint32_t hal_utils_calc_clk_div_integer(const hal_utils_clk_info_t *clk_info, uint32_t *int_div);
#ifdef __cplusplus
}

View File

@ -130,7 +130,7 @@ void i2s_hal_init(i2s_hal_context_t *hal, int port_id);
* @param mclk module clock
* @param mclk_div mclk division coefficients, including integer part and decimal part
*/
void i2s_hal_calc_mclk_precise_division(uint32_t sclk, uint32_t mclk, i2s_ll_mclk_div_t *mclk_div);
void i2s_hal_calc_mclk_precise_division(uint32_t sclk, uint32_t mclk, hal_utils_clk_div_t *mclk_div);
/**
* @brief Set tx channel clock

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,31 +7,13 @@
#include "hal/lcd_hal.h"
#include "hal/lcd_ll.h"
#include "hal/log.h"
#include "hal/hal_utils.h"
void lcd_hal_init(lcd_hal_context_t *hal, int id)
{
hal->dev = LCD_LL_GET_HW(id);
}
/**
* @brief helper function, calculate the Greatest Common Divisor
* @note gcd(a, b) = gcd(b, a % b)
* @param a bigger value
* @param b smaller value
* @return result of gcd(a, b)
*/
__attribute__((always_inline))
static inline uint32_t _gcd(uint32_t a, uint32_t b)
{
uint32_t c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return b;
}
uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uint32_t expect_pclk_freq_hz, int lcd_clk_flags)
{
// lcd_clk = module_clock_src / (n + b / a)
@ -40,30 +22,19 @@ uint32_t lcd_hal_cal_pclk_freq(lcd_hal_context_t *hal, uint32_t src_freq_hz, uin
if (mo == 1 && !(lcd_clk_flags & LCD_HAL_PCLK_FLAG_ALLOW_EQUAL_SYSCLK)) {
mo = 2;
}
uint32_t n = src_freq_hz / expect_pclk_freq_hz / mo;
uint32_t a = 0;
uint32_t b = 0;
// delta_hz / expect_pclk_freq_hz <==> b / a
uint32_t delta_hz = src_freq_hz / mo - expect_pclk_freq_hz * n;
// fractional divider
if (delta_hz) {
uint32_t gcd = _gcd(expect_pclk_freq_hz, delta_hz);
a = expect_pclk_freq_hz / gcd;
b = delta_hz / gcd;
// normalize div_a and div_b
uint32_t d = a / LCD_LL_CLK_FRAC_DIV_AB_MAX + 1;
a /= d;
b /= d;
}
hal_utils_clk_info_t lcd_clk_info = {
.src_freq_hz = src_freq_hz,
.exp_freq_hz = expect_pclk_freq_hz * mo,
.max_integ = LCD_LL_CLK_FRAC_DIV_N_MAX,
.min_integ = 2,
.max_fract = LCD_LL_CLK_FRAC_DIV_AB_MAX,
};
hal_utils_clk_div_t lcd_clk_div = {};
uint32_t real_freq = hal_utils_calc_clk_div_frac_fast(&lcd_clk_info, &lcd_clk_div);
HAL_EARLY_LOGD("lcd_hal", "n=%"PRIu32",a=%"PRIu32",b=%"PRIu32",mo=%"PRIu32, lcd_clk_div.integer, lcd_clk_div.denominator, lcd_clk_div.numerator, mo);
HAL_EARLY_LOGD("lcd_hal", "n=%"PRIu32",a=%"PRIu32",b=%"PRIu32",mo=%"PRIu32"", n, a, b, mo);
lcd_ll_set_group_clock_coeff(hal->dev, n, a, b);
lcd_ll_set_group_clock_coeff(hal->dev, lcd_clk_div.integer, lcd_clk_div.denominator, lcd_clk_div.numerator);
lcd_ll_set_pixel_clock_prescale(hal->dev, mo);
if (delta_hz) {
return ((uint64_t)src_freq_hz * a) / (n * a + b) / mo;
} else {
return src_freq_hz / n / mo;
}
return real_freq / mo;
}

View File

@ -0,0 +1,10 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
project(test_hal_utils)

View File

@ -0,0 +1,2 @@
| Supported Targets | Linux |
| ----------------- | ----- |

View File

@ -0,0 +1,5 @@
idf_component_register(SRCS "test_app_main.c"
"test_calc_clk_div.c"
INCLUDE_DIRS "."
REQUIRES unity hal
WHOLE_ARCHIVE)

View File

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "unity.h"
#include "unity_test_runner.h"
void app_main(void)
{
/**
* _ _ _ _ _ _ _____ ___ _ ____
* | | | | / \ | | | | | |_ _|_ _| | / ___|
* | |_| | / _ \ | | | | | | | | | || | \___ \
* | _ |/ ___ \| |___ | |_| | | | | || |___ ___) |
* |_| |_/_/ \_\_____| \___/ |_| |___|_____|____/
*/
printf(" _ _ _ _ _ _ _____ ___ _ ____ \r\n");
printf(" | | | | / \\ | | | | | |_ _|_ _| | / ___| \r\n");
printf(" | |_| | / _ \\ | | | | | | | | | || | \\___ \\ \r\n");
printf(" | _ |/ ___ \\| |___ | |_| | | | | || |___ ___) |\r\n");
printf(" |_| |_/_/ \\_\\_____| \\___/ |_| |___|_____|____/ \r\n");
unity_run_menu();
}

View File

@ -0,0 +1,150 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "unity.h"
#include "hal/hal_utils.h"
TEST_CASE("test_integral_division", "[clk_div]")
{
uint32_t int_div = 0;
hal_utils_clk_info_t clk_info = {
.src_freq_hz = 80 * 1000 * 1000,
.exp_freq_hz = 57 * 1000 * 1000,
.max_integ = 256,
.min_integ = 1,
.round_opt = 0, // round down
};
// Round down test
uint32_t real_freq = 0;
real_freq = hal_utils_calc_clk_div_integer(&clk_info, &int_div);
TEST_ASSERT_EQUAL_UINT32(1, int_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.src_freq_hz, real_freq);
// Normal round test
clk_info.round_opt = 2;
real_freq = hal_utils_calc_clk_div_integer(&clk_info, &int_div);
TEST_ASSERT_EQUAL_UINT32(2, int_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.src_freq_hz / 2, real_freq);
clk_info.exp_freq_hz = 60 * 1000 * 1000;
real_freq = hal_utils_calc_clk_div_integer(&clk_info, &int_div);
TEST_ASSERT_EQUAL_UINT32(2, int_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.src_freq_hz / 2, real_freq);
clk_info.exp_freq_hz = 63 * 1000 * 1000;
real_freq = hal_utils_calc_clk_div_integer(&clk_info, &int_div);
TEST_ASSERT_EQUAL_UINT32(1, int_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.src_freq_hz, real_freq);
// Round up test
clk_info.round_opt = 1;
real_freq = hal_utils_calc_clk_div_integer(&clk_info, &int_div);
TEST_ASSERT_EQUAL_UINT32(2, int_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.src_freq_hz / 2, real_freq);
// Integral division
clk_info.exp_freq_hz = 40 * 1000 * 1000;
real_freq = hal_utils_calc_clk_div_integer(&clk_info, &int_div);
TEST_ASSERT_EQUAL_UINT32(2, int_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.src_freq_hz / 2, real_freq);
clk_info.round_opt = 0;
real_freq = hal_utils_calc_clk_div_integer(&clk_info, &int_div);
TEST_ASSERT_EQUAL_UINT32(2, int_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.src_freq_hz / 2, real_freq);
clk_info.round_opt = 2;
real_freq = hal_utils_calc_clk_div_integer(&clk_info, &int_div);
TEST_ASSERT_EQUAL_UINT32(2, int_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.src_freq_hz / 2, real_freq);
}
TEST_CASE("test_fractal_division", "[clk_div]")
{
hal_utils_clk_div_t clk_div = {};
hal_utils_clk_info_t clk_info = {
.src_freq_hz = 160 * 1000 * 1000,
.exp_freq_hz = 16 * 1024 * 1024,
.max_integ = 256,
.min_integ = 1,
.max_fract = 512,
};
uint32_t real_freq = 0;
// Fractal division with error
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(9, clk_div.integer);
TEST_ASSERT_UINT32_WITHIN(clk_info.exp_freq_hz * 0.001, clk_info.exp_freq_hz, real_freq);
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(9, clk_div.integer);
TEST_ASSERT_UINT32_WITHIN(clk_info.exp_freq_hz * 0.0001, clk_info.exp_freq_hz, real_freq);
// Fractal division with no error
clk_info.exp_freq_hz = 50 * 1000 * 1000;
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(3, clk_div.integer);
TEST_ASSERT_EQUAL_UINT32(clk_info.exp_freq_hz, real_freq);
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(3, clk_div.integer);
TEST_ASSERT_EQUAL_UINT32(clk_info.exp_freq_hz, real_freq);
// Integral division
clk_info.exp_freq_hz = 40 * 1000 * 1000;
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(4, clk_div.integer);
TEST_ASSERT_EQUAL_UINT32(0, clk_div.numerator);
TEST_ASSERT_EQUAL_UINT32(clk_info.exp_freq_hz, real_freq);
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(4, clk_div.integer);
TEST_ASSERT_EQUAL_UINT32(0, clk_div.numerator);
TEST_ASSERT_EQUAL_UINT32(clk_info.exp_freq_hz, real_freq);
}
TEST_CASE("test_fault_division", "[clk_div]")
{
hal_utils_clk_div_t clk_div = {};
hal_utils_clk_info_t clk_info = {
.src_freq_hz = 160 * 1000 * 1000,
.exp_freq_hz = 1250 * 1000,
.max_integ = 128,
.min_integ = 2,
.max_fract = 512,
};
uint32_t real_freq = 0;
// Equal to the max integer
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);
TEST_ASSERT_FALSE(real_freq);
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_FALSE(real_freq);
// Exceed the max integer
clk_info.exp_freq_hz = 1000 * 1000;
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);
TEST_ASSERT_FALSE(real_freq);
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_FALSE(real_freq);
// Blow the min integer
clk_info.exp_freq_hz = 125 * 1000 * 1000;
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);
TEST_ASSERT_FALSE(real_freq);
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_FALSE(real_freq);
// Equal to the min integer
clk_info.exp_freq_hz = 80 * 1000 * 1000;
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.exp_freq_hz, real_freq);
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(clk_info.exp_freq_hz, real_freq);
// divide 0 case
clk_info.exp_freq_hz = 200 * 1000 * 1000;
clk_info.min_integ = 0;
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);
TEST_ASSERT_FALSE(real_freq);
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_FALSE(real_freq);
}

View File

@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
@pytest.mark.linux
@pytest.mark.host_test
def test_hal_utils(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="linux"
CONFIG_ESP_TASK_WDT_EN=n