mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-10 04:43:33 +00:00
I2C: Make I2C clock frequency accurate
This commit is contained in:
@@ -110,12 +110,12 @@ static inline void i2c_ll_cal_bus_clk(uint32_t source_clk, uint32_t bus_freq, i2
|
||||
clk_cal->clkm_div = clkm_div;
|
||||
clk_cal->scl_low = half_cycle;
|
||||
// default, scl_wait_high < scl_high
|
||||
int scl_wait_high = (bus_freq <= 50000) ? 0 : (half_cycle / 8); // compensate the time when freq > 50K
|
||||
clk_cal->scl_wait_high = scl_wait_high;
|
||||
clk_cal->scl_high = half_cycle - scl_wait_high;
|
||||
// Make 80KHz as a boundary here, because when working at lower frequency, too much scl_wait_high will faster the frequency
|
||||
// according to some hardware behaviors.
|
||||
clk_cal->scl_wait_high = (bus_freq >= 80*1000) ? (half_cycle / 2 - 2) : (half_cycle / 4);
|
||||
clk_cal->scl_high = half_cycle - clk_cal->scl_wait_high;
|
||||
clk_cal->sda_hold = half_cycle / 4;
|
||||
// scl_wait_high < sda_sample <= scl_high
|
||||
clk_cal->sda_sample = half_cycle / 2;
|
||||
clk_cal->sda_sample = half_cycle / 2 + clk_cal->scl_wait_high;
|
||||
clk_cal->setup = half_cycle;
|
||||
clk_cal->hold = half_cycle;
|
||||
//default we set the timeout value to about 10 bus cycles
|
||||
@@ -146,9 +146,14 @@ static inline void i2c_ll_update(i2c_dev_t *hw)
|
||||
static inline void i2c_ll_set_bus_timing(i2c_dev_t *hw, i2c_clk_cal_t *bus_cfg)
|
||||
{
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clk_conf, sclk_div_num, bus_cfg->clkm_div - 1);
|
||||
//scl period
|
||||
hw->scl_low_period.scl_low_period = bus_cfg->scl_low - 2;
|
||||
hw->scl_high_period.scl_high_period = bus_cfg->scl_high - 3;
|
||||
/* According to the Technical Reference Manual, the following timings must be subtracted by 1.
|
||||
* However, according to the practical measurement and some hardware behaviour, if wait_high_period and scl_high minus one.
|
||||
* The SCL frequency would be a little higher than expected. Therefore, the solution
|
||||
* here is not to minus scl_high as well as scl_wait high, and the frequency will be absolutely accurate to all frequency
|
||||
* to some extent. */
|
||||
hw->scl_low_period.scl_low_period = bus_cfg->scl_low - 1;
|
||||
hw->scl_high_period.scl_high_period = bus_cfg->scl_high;
|
||||
hw->scl_high_period.scl_wait_high_period = bus_cfg->scl_wait_high;
|
||||
//sda sample
|
||||
hw->sda_hold.sda_hold_time = bus_cfg->sda_hold - 1;
|
||||
hw->sda_sample.sda_sample_time = bus_cfg->sda_sample - 1;
|
||||
@@ -776,6 +781,39 @@ static inline void i2c_ll_master_init(i2c_dev_t *hw)
|
||||
hw->ctr.val = ctrl_reg.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure I2C SCL timing
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers
|
||||
* @param high_period The I2C SCL hight period (in core clock cycle, hight_period > 2)
|
||||
* @param low_period The I2C SCL low period (in core clock cycle, low_period > 1)
|
||||
* @param wait_high_period The I2C SCL wait rising edge period.
|
||||
*
|
||||
* @return None.
|
||||
*/
|
||||
static inline void i2c_ll_set_scl_clk_timing(i2c_dev_t *hw, int high_period, int low_period, int wait_high_period)
|
||||
{
|
||||
hw->scl_low_period.scl_low_period = low_period;
|
||||
hw->scl_high_period.scl_high_period = high_period;
|
||||
hw->scl_high_period.scl_wait_high_period = wait_high_period;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get I2C SCL timing configuration
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers
|
||||
* @param high_period Pointer to accept the SCL high period
|
||||
* @param low_period Pointer to accept the SCL low period
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static inline void i2c_ll_get_scl_clk_timing(i2c_dev_t *hw, int *high_period, int *low_period, int *wait_high_period)
|
||||
{
|
||||
*high_period = hw->scl_high_period.scl_high_period;
|
||||
*wait_high_period = hw->scl_high_period.scl_wait_high_period;
|
||||
*low_period = hw->scl_low_period.scl_low_period;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user