i2c: fix I2C slave clock source selection

In I2C slave mode, clock source can now be changed, according to
the flags specified in `i2c_config_t` structure. Thus, ESP32-S2
can now act as an I2C slave, even with a 400KHz master clock.
This commit is contained in:
Omar Chebib 2021-05-24 10:51:38 +08:00
parent f29d873c54
commit 2575c0d49f
4 changed files with 42 additions and 14 deletions

View File

@ -631,7 +631,7 @@ static esp_err_t i2c_hw_fsm_reset(i2c_port_t i2c_num)
return ESP_OK;
}
static i2c_sclk_t i2c_get_clk_src(const i2c_config_t *i2c_conf)
static i2c_sclk_t i2c_get_clk_src(const uint32_t clk_flags, const uint32_t clk_speed)
{
for (i2c_sclk_t clk = I2C_SCLK_DEFAULT + 1; clk < I2C_SCLK_MAX; clk++) {
#if CONFIG_IDF_TARGET_ESP32S3
@ -639,7 +639,8 @@ static i2c_sclk_t i2c_get_clk_src(const i2c_config_t *i2c_conf)
continue;
}
#endif
if (((i2c_conf->clk_flags & i2c_clk_alloc[clk].character) == i2c_conf->clk_flags) && (i2c_conf->master.clk_speed <= i2c_clk_alloc[clk].clk_freq)) {
if ( ((clk_flags & i2c_clk_alloc[clk].character) == clk_flags) &&
(clk_speed <= i2c_clk_alloc[clk].clk_freq) ) {
return clk;
}
}
@ -648,15 +649,30 @@ static i2c_sclk_t i2c_get_clk_src(const i2c_config_t *i2c_conf)
esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf)
{
i2c_sclk_t src_clk = I2C_SCLK_DEFAULT;
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_NUM_ERROR_STR);
ESP_RETURN_ON_FALSE(i2c_conf != NULL, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_ADDR_ERROR_STR);
ESP_RETURN_ON_FALSE(i2c_conf->mode < I2C_MODE_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_MODE_ERR_STR);
if (i2c_conf->mode == I2C_MODE_MASTER) {
ESP_RETURN_ON_FALSE(i2c_get_clk_src(i2c_conf) != I2C_SCLK_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_CLK_FLAG_ERR_STR);
src_clk = i2c_get_clk_src(i2c_conf->clk_flags, i2c_conf->master.clk_speed);
ESP_RETURN_ON_FALSE(src_clk != I2C_SCLK_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_CLK_FLAG_ERR_STR);
} else {
#if CONFIG_IDF_TARGET_ESP32S2
/* On ESP32-S2, APB clock shall always be used in slave mode as the
* other one, I2C_SCLK_REF_TICK, is too slow, even for sampling a
* 100KHz SCL. */
src_clk = I2C_SCLK_APB;
#else
src_clk = i2c_get_clk_src(i2c_conf->clk_flags, i2c_conf->slave.maximum_speed);
ESP_RETURN_ON_FALSE(src_clk != I2C_SCLK_MAX, ESP_ERR_INVALID_ARG, I2C_TAG, I2C_CLK_FLAG_ERR_STR);
#endif
}
esp_err_t ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num,
i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode);
ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num,
i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode);
if (ret != ESP_OK) {
return ret;
}
@ -666,6 +682,7 @@ esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf)
i2c_hal_clr_intsts_mask(&(i2c_context[i2c_num].hal), I2C_LL_INTR_MASK);
if (i2c_conf->mode == I2C_MODE_SLAVE) { //slave mode
i2c_hal_slave_init(&(i2c_context[i2c_num].hal), i2c_num);
i2c_hal_set_source_clk(&(i2c_context[i2c_num].hal), src_clk);
i2c_hal_set_slave_addr(&(i2c_context[i2c_num].hal), i2c_conf->slave.slave_addr, i2c_conf->slave.addr_10bit_en);
i2c_hal_set_rxfifo_full_thr(&(i2c_context[i2c_num].hal), I2C_FIFO_FULL_THRESH_VAL);
i2c_hal_set_txfifo_empty_thr(&(i2c_context[i2c_num].hal), I2C_FIFO_EMPTY_THRESH_VAL);
@ -678,7 +695,7 @@ esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf)
i2c_hal_master_init(&(i2c_context[i2c_num].hal), i2c_num);
//Default, we enable hardware filter
i2c_hal_set_filter(&(i2c_context[i2c_num].hal), I2C_FILTER_CYC_NUM_DEF);
i2c_hal_set_bus_timing(&(i2c_context[i2c_num].hal), i2c_conf->master.clk_speed, i2c_get_clk_src(i2c_conf));
i2c_hal_set_bus_timing(&(i2c_context[i2c_num].hal), i2c_conf->master.clk_speed, src_clk);
}
I2C_EXIT_CRITICAL(&(i2c_context[i2c_num].spinlock));
return ESP_OK;

View File

@ -74,14 +74,15 @@ typedef struct{
union {
struct {
uint32_t clk_speed; /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */
} master; /*!< I2C master config */
uint32_t clk_speed; /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */
} master; /*!< I2C master config */
struct {
uint8_t addr_10bit_en; /*!< I2C 10bit address mode enable for slave mode */
uint16_t slave_addr; /*!< I2C address for slave mode */
} slave; /*!< I2C slave config */
uint8_t addr_10bit_en; /*!< I2C 10bit address mode enable for slave mode */
uint16_t slave_addr; /*!< I2C address for slave mode */
uint32_t maximum_speed; /*!< I2C expected clock speed from SCL. */
} slave; /*!< I2C slave config */
};
uint32_t clk_flags; /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/
uint32_t clk_flags; /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/
} i2c_config_t;

View File

@ -680,7 +680,7 @@ static inline void i2c_ll_master_clr_rx_it(i2c_dev_t *hw)
}
/**
* @brief
* @brief Enable I2C slave TX interrupt
*
* @param hw Beginning address of the peripheral registers
*
@ -752,7 +752,7 @@ static inline void i2c_ll_slave_clr_rx_it(i2c_dev_t *hw)
}
/**
* @brief Reste I2C master FSM. When the master FSM is stuck, call this function to reset the FSM
* @brief Reset I2C master FSM. When the master FSM is stuck, call this function to reset the FSM
*
* @param hw Beginning address of the peripheral registers
*

View File

@ -110,6 +110,16 @@ typedef struct {
*/
#define i2c_hal_slave_clr_rx_it(hal) i2c_ll_slave_clr_rx_it((hal)->dev)
/**
* @brief Set the source clock. This function is meant to be used in
* slave mode, in order to select a source clock abe to handle
* the expected SCL frequency.
*
* @param hal Context of the HAL layer
* @param src_clk Source clock to use choosen from `i2c_sclk_t` type
*/
#define i2c_hal_set_source_clk(hal, src_clk) i2c_ll_set_source_clk((hal)->dev, src_clk)
/**
* @brief Init the I2C master.
*