fix(lp-i2c): Fixed the generation of spurious I2C start with lp-i2c

This commit fixes an issue with LP I2C and RTC I2C where in the
peripherals generated a spurious I2C start condition when initialized.
This caused some sensors to not respond properly to the following read
or write request.

Closes https://github.com/espressif/esp-idf/issues/14043
Closes https://github.com/espressif/esp-idf/issues/11608
This commit is contained in:
Sudeep Mohanty 2024-07-11 11:18:28 +02:00
parent 17a18e8849
commit 2d331c0413
2 changed files with 29 additions and 12 deletions

View File

@ -45,9 +45,10 @@ static esp_err_t lp_i2c_gpio_is_cfg_valid(gpio_num_t sda_io_num, gpio_num_t scl_
static esp_err_t lp_i2c_configure_io(gpio_num_t io_num, bool pullup_en) static esp_err_t lp_i2c_configure_io(gpio_num_t io_num, bool pullup_en)
{ {
/* Set the IO pin to high to avoid them from toggling from Low to High state during initialization. This can register a spurious I2C start condition. */
ESP_RETURN_ON_ERROR(rtc_gpio_set_level(io_num, 1), LPI2C_TAG, "LP GPIO failed to set level to high for %d", io_num);
/* Initialize IO Pin */ /* Initialize IO Pin */
ESP_RETURN_ON_ERROR(rtc_gpio_init(io_num), LPI2C_TAG, "LP GPIO Init failed for GPIO %d", io_num); ESP_RETURN_ON_ERROR(rtc_gpio_init(io_num), LPI2C_TAG, "LP GPIO Init failed for GPIO %d", io_num);
/* Set direction to input+output open-drain mode */ /* Set direction to input+output open-drain mode */
ESP_RETURN_ON_ERROR(rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_INPUT_OUTPUT_OD), LPI2C_TAG, "LP GPIO Set direction failed for %d", io_num); ESP_RETURN_ON_ERROR(rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_INPUT_OUTPUT_OD), LPI2C_TAG, "LP GPIO Set direction failed for %d", io_num);
/* Disable pulldown on the io pin */ /* Disable pulldown on the io pin */
@ -72,12 +73,16 @@ static esp_err_t lp_i2c_set_pin(const lp_core_i2c_cfg_t *cfg)
/* Verify that the LP I2C GPIOs are valid */ /* Verify that the LP I2C GPIOs are valid */
ESP_RETURN_ON_ERROR(lp_i2c_gpio_is_cfg_valid(sda_io_num, scl_io_num), LPI2C_TAG, "LP I2C GPIO config invalid"); ESP_RETURN_ON_ERROR(lp_i2c_gpio_is_cfg_valid(sda_io_num, scl_io_num), LPI2C_TAG, "LP I2C GPIO config invalid");
/* Initialize SDA Pin */ // NOTE: We always initialize the SCL pin first, then the SDA pin.
ESP_RETURN_ON_ERROR(lp_i2c_configure_io(sda_io_num, sda_pullup_en), LPI2C_TAG, "LP I2C SDA pin config failed"); // This order of initialization is important to avoid any spurious
// I2C start conditions on the bus.
/* Initialize SCL Pin */ /* Initialize SCL Pin */
ESP_RETURN_ON_ERROR(lp_i2c_configure_io(scl_io_num, scl_pullup_en), LPI2C_TAG, "LP I2C SCL pin config failed"); ESP_RETURN_ON_ERROR(lp_i2c_configure_io(scl_io_num, scl_pullup_en), LPI2C_TAG, "LP I2C SCL pin config failed");
/* Initialize SDA Pin */
ESP_RETURN_ON_ERROR(lp_i2c_configure_io(sda_io_num, sda_pullup_en), LPI2C_TAG, "LP I2C SDA pin config failed");
/* Select LP I2C function for the SDA Pin */ /* Select LP I2C function for the SDA Pin */
lp_io_dev->gpio[sda_io_num].mcu_sel = 1; lp_io_dev->gpio[sda_io_num].mcu_sel = 1;
@ -103,7 +108,7 @@ static esp_err_t lp_i2c_config_clk(const lp_core_i2c_cfg_t *cfg)
} }
} }
/* Fetch the clock source fequency */ /* Fetch the clock source frequency */
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(source_clk, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &source_freq), LPI2C_TAG, "Invalid LP I2C source clock selected"); ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(source_clk, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &source_freq), LPI2C_TAG, "Invalid LP I2C source clock selected");
/* Verify that the I2C_SCLK operates at a frequency 20 times larger than the requested SCL bus frequency */ /* Verify that the I2C_SCLK operates at a frequency 20 times larger than the requested SCL bus frequency */
@ -141,6 +146,9 @@ esp_err_t lp_core_i2c_master_init(i2c_port_t lp_i2c_num, const lp_core_i2c_cfg_t
/* Initialize LP I2C HAL */ /* Initialize LP I2C HAL */
i2c_hal_init(&i2c_hal, lp_i2c_num); i2c_hal_init(&i2c_hal, lp_i2c_num);
/* Clear any pending interrupts */
i2c_ll_clear_intr_mask(i2c_hal.dev, UINT32_MAX);
/* Initialize LP I2C Master mode */ /* Initialize LP I2C Master mode */
i2c_hal_master_init(&i2c_hal); i2c_hal_master_init(&i2c_hal);

View File

@ -72,6 +72,8 @@ static esp_err_t i2c_gpio_is_cfg_valid(gpio_num_t sda_io_num, gpio_num_t scl_io_
static esp_err_t i2c_configure_io(gpio_num_t io_num, bool pullup_en) static esp_err_t i2c_configure_io(gpio_num_t io_num, bool pullup_en)
{ {
/* Set the IO pin to high to avoid them from toggling from Low to High state during initialization. This can register a spurious I2C start condition. */
ESP_RETURN_ON_ERROR(rtc_gpio_set_level(io_num, 1), RTCI2C_TAG, "RTC GPIO failed to set level to high for %d", io_num);
/* Initialize IO Pin */ /* Initialize IO Pin */
ESP_RETURN_ON_ERROR(rtc_gpio_init(io_num), RTCI2C_TAG, "RTC GPIO Init failed for GPIO %d", io_num); ESP_RETURN_ON_ERROR(rtc_gpio_init(io_num), RTCI2C_TAG, "RTC GPIO Init failed for GPIO %d", io_num);
/* Set direction to input+output */ /* Set direction to input+output */
@ -98,12 +100,16 @@ static esp_err_t i2c_set_pin(const ulp_riscv_i2c_cfg_t *cfg)
/* Verify that the I2C GPIOs are valid */ /* Verify that the I2C GPIOs are valid */
ESP_RETURN_ON_ERROR(i2c_gpio_is_cfg_valid(sda_io_num, scl_io_num), RTCI2C_TAG, "RTC I2C GPIO config invalid"); ESP_RETURN_ON_ERROR(i2c_gpio_is_cfg_valid(sda_io_num, scl_io_num), RTCI2C_TAG, "RTC I2C GPIO config invalid");
/* Initialize SDA Pin */ // NOTE: We always initialize the SCL pin first, then the SDA pin.
ESP_RETURN_ON_ERROR(i2c_configure_io(sda_io_num, sda_pullup_en), RTCI2C_TAG, "RTC I2C SDA pin config failed"); // This order of initialization is important to avoid any spurious
// I2C start conditions on the bus.
/* Initialize SCL Pin */ /* Initialize SCL Pin */
ESP_RETURN_ON_ERROR(i2c_configure_io(scl_io_num, scl_pullup_en), RTCI2C_TAG, "RTC I2C SCL pin config failed"); ESP_RETURN_ON_ERROR(i2c_configure_io(scl_io_num, scl_pullup_en), RTCI2C_TAG, "RTC I2C SCL pin config failed");
/* Initialize SDA Pin */
ESP_RETURN_ON_ERROR(i2c_configure_io(sda_io_num, sda_pullup_en), RTCI2C_TAG, "RTC I2C SDA pin config failed");
/* Route SDA IO signal to the RTC subsystem */ /* Route SDA IO signal to the RTC subsystem */
rtc_io_dev->touch_pad[sda_io_num].mux_sel = 1; rtc_io_dev->touch_pad[sda_io_num].mux_sel = 1;
@ -471,6 +477,12 @@ esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg)
WRITE_PERI_REG(RTC_I2C_CTRL_REG, 0); WRITE_PERI_REG(RTC_I2C_CTRL_REG, 0);
WRITE_PERI_REG(SENS_SAR_I2C_CTRL_REG, 0); WRITE_PERI_REG(SENS_SAR_I2C_CTRL_REG, 0);
/* Verify that the input cfg param is valid */
ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "RTC I2C configuration is NULL");
/* Configure RTC I2C GPIOs */
ESP_RETURN_ON_ERROR(i2c_set_pin(cfg), RTCI2C_TAG, "Failed to configure RTC I2C GPIOs");
/* Reset RTC I2C */ /* Reset RTC I2C */
#if CONFIG_IDF_TARGET_ESP32S2 #if CONFIG_IDF_TARGET_ESP32S2
i2c_dev->ctrl.i2c_reset = 1; i2c_dev->ctrl.i2c_reset = 1;
@ -484,12 +496,6 @@ esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg)
CLEAR_PERI_REG_MASK(SENS_SAR_PERI_RESET_CONF_REG, SENS_RTC_I2C_RESET); CLEAR_PERI_REG_MASK(SENS_SAR_PERI_RESET_CONF_REG, SENS_RTC_I2C_RESET);
#endif // CONFIG_IDF_TARGET_ESP32S2 #endif // CONFIG_IDF_TARGET_ESP32S2
/* Verify that the input cfg param is valid */
ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "RTC I2C configuration is NULL");
/* Configure RTC I2C GPIOs */
ESP_RETURN_ON_ERROR(i2c_set_pin(cfg), RTCI2C_TAG, "Failed to configure RTC I2C GPIOs");
/* Enable internal open-drain mode for SDA and SCL lines */ /* Enable internal open-drain mode for SDA and SCL lines */
#if CONFIG_IDF_TARGET_ESP32S2 #if CONFIG_IDF_TARGET_ESP32S2
i2c_dev->ctrl.sda_force_out = 0; i2c_dev->ctrl.sda_force_out = 0;
@ -519,6 +525,9 @@ esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg)
/* Configure RTC I2C timing parameters */ /* Configure RTC I2C timing parameters */
ESP_RETURN_ON_ERROR(i2c_set_timing(cfg), RTCI2C_TAG, "Failed to configure RTC I2C timing"); ESP_RETURN_ON_ERROR(i2c_set_timing(cfg), RTCI2C_TAG, "Failed to configure RTC I2C timing");
/* Clear any pending interrupts */
WRITE_PERI_REG(RTC_I2C_INT_CLR_REG, UINT32_MAX);
/* Enable RTC I2C interrupts */ /* Enable RTC I2C interrupts */
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA | SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA |
RTC_I2C_TX_DATA_INT_ENA | RTC_I2C_TX_DATA_INT_ENA |