From 88e4c0602889333f8e81689c7026a8cb7f2e5a4e Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Mon, 27 Mar 2023 16:22:43 +0200 Subject: [PATCH] ulp-riscv-i2c: Updated ULP RISC-V I2C driver to abort when met with errors The current ULP RISC-V RTC I2C driver got stuck in an infinite loop if there is a I2C transaction error. This commit amends the driver flow to abort the read/write operation if met with errors. It also adds a loop timeout to avoid getting stuck in an infinite loop.The commit also updates the default bus timing parameters for RTC I2C to be faster. This commit also adds documentation help to guide users when they meet with issues while working with the RTC I2C driver on the ULP RISC-V coprocessor. --- .../ulp/ulp_riscv/include/ulp_riscv_i2c.h | 59 +++++-- .../ulp_core/include/ulp_riscv_utils.h | 1 + .../ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c | 83 ++++++++-- components/ulp/ulp_riscv/ulp_riscv_i2c.c | 155 +++++++++++++----- docs/doxygen/Doxyfile | 1 + docs/en/api-reference/system/ulp-risc-v.rst | 33 ++++ .../i2c/main/ulp_riscv_rtc_i2c_example_main.c | 8 +- 7 files changed, 259 insertions(+), 81 deletions(-) diff --git a/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h b/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h index 36ef871667..4ee4482a6a 100644 --- a/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h +++ b/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,40 +15,67 @@ extern "C" { #include "hal/gpio_types.h" #include "esp_err.h" +/** + * @brief ULP RISC-V RTC I2C pin config + */ typedef struct { - uint32_t sda_io_num; // GPIO pin for SDA signal. Only GPIO#1 or GPIO#3 can be used as the SDA pin. - uint32_t scl_io_num; // GPIO pin for SCL signal. Only GPIO#0 or GPIO#2 can be used as the SCL pin. - bool sda_pullup_en; // SDA line enable internal pullup. Can be configured if external pullup is not used. - bool scl_pullup_en; // SCL line enable internal pullup. Can be configured if external pullup is not used. + uint32_t sda_io_num; /*!< GPIO pin for SDA signal. Only GPIO#1 or GPIO#3 can be used as the SDA pin. */ + uint32_t scl_io_num; /*!< GPIO pin for SCL signal. Only GPIO#0 or GPIO#2 can be used as the SCL pin. */ + bool sda_pullup_en; /*!< SDA line enable internal pullup. Can be configured if external pullup is not used. */ + bool scl_pullup_en; /*!< SCL line enable internal pullup. Can be configured if external pullup is not used. */ } ulp_riscv_i2c_pin_cfg_t; +/** + * @brief ULP RISC-V RTC I2C timing config + */ typedef struct { - uint32_t scl_low_period; // SCL low period - uint32_t scl_high_period; // SCL high period - uint32_t sda_duty_period; // Period between the SDA switch and the falling edge of SCL - uint32_t scl_start_period; // Waiting time after the START condition - uint32_t scl_stop_period; // Waiting time before the END condition - uint32_t i2c_trans_timeout; // I2C transaction timeout + float scl_low_period; /*!< SCL low period in micro seconds */ + float scl_high_period; /*!< SCL high period in micro seconds */ + float sda_duty_period; /*!< Period between the SDA switch and the falling edge of SCL in micro seconds */ + float scl_start_period; /*!< Waiting time after the START condition in micro seconds */ + float scl_stop_period; /*!< Waiting time before the END condition in micro seconds */ + float i2c_trans_timeout; /*!< I2C transaction timeout in micro seconds */ } ulp_riscv_i2c_timing_cfg_t; +/** + * @brief ULP RISC-V RTC I2C init parameters + */ typedef struct { - ulp_riscv_i2c_pin_cfg_t i2c_pin_cfg; // RTC I2C pin configuration - ulp_riscv_i2c_timing_cfg_t i2c_timing_cfg; // RTC I2C timing configuration + ulp_riscv_i2c_pin_cfg_t i2c_pin_cfg; /*!< RTC I2C pin configuration */ + ulp_riscv_i2c_timing_cfg_t i2c_timing_cfg; /*!< RTC I2C timing configuration */ } ulp_riscv_i2c_cfg_t; -/* Nominal default GPIO settings and timing parametes */ -#define ULP_RISCV_I2C_DEFAULT_CONFIG() \ - { \ +/* Default RTC I2C GPIO settings */ +#define ULP_RISCV_I2C_DEFAULT_GPIO_CONFIG() \ .i2c_pin_cfg.sda_io_num = GPIO_NUM_3, \ .i2c_pin_cfg.scl_io_num = GPIO_NUM_2, \ .i2c_pin_cfg.sda_pullup_en = true, \ .i2c_pin_cfg.scl_pullup_en = true, \ + +/* Nominal I2C bus timing parameters for I2C fast mode. Max SCL freq of 400 KHz. */ +#define ULP_RISCV_I2C_FAST_MODE_CONFIG() \ + .i2c_timing_cfg.scl_low_period = 1.4, \ + .i2c_timing_cfg.scl_high_period = 0.3, \ + .i2c_timing_cfg.sda_duty_period = 1, \ + .i2c_timing_cfg.scl_start_period = 2, \ + .i2c_timing_cfg.scl_stop_period = 1.3, \ + .i2c_timing_cfg.i2c_trans_timeout = 20, \ + +/* Nominal I2C bus timing parameters for I2C standard mode. Max SCL freq of 100 KHz. */ +#define ULP_RISCV_I2C_STANDARD_MODE_CONFIG() \ .i2c_timing_cfg.scl_low_period = 5, \ .i2c_timing_cfg.scl_high_period = 5, \ .i2c_timing_cfg.sda_duty_period = 2, \ .i2c_timing_cfg.scl_start_period = 3, \ .i2c_timing_cfg.scl_stop_period = 6, \ .i2c_timing_cfg.i2c_trans_timeout = 20, \ + +/* Default RTC I2C configuration settings. Uses I2C fast mode. */ +//TODO: Move to smaller units of time in the future like nano seconds to avoid floating point operations. +#define ULP_RISCV_I2C_DEFAULT_CONFIG() \ + { \ + ULP_RISCV_I2C_DEFAULT_GPIO_CONFIG() \ + ULP_RISCV_I2C_FAST_MODE_CONFIG() \ } /** diff --git a/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_utils.h b/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_utils.h index a53b949b2b..20eebb6282 100644 --- a/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_utils.h +++ b/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_utils.h @@ -10,6 +10,7 @@ extern "C" { #endif +#include "sdkconfig.h" #include #include "ulp_riscv_register_ops.h" diff --git a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c index de3f787c57..bfde231551 100644 --- a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c +++ b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -29,6 +29,9 @@ #define ULP_I2C_CMD_END I2C_LL_CMD_END /*!i2c_timing_cfg.scl_low_period); - uint32_t scl_high_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_high_period); - uint32_t sda_duty_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.sda_duty_period); - uint32_t scl_start_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_start_period); - uint32_t scl_stop_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_stop_period); - uint32_t i2c_trans_timeout = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.i2c_trans_timeout); - uint32_t setup_time_start = (cfg->i2c_timing_cfg.scl_high_period + cfg->i2c_timing_cfg.sda_duty_period); - uint32_t hold_time_start = (cfg->i2c_timing_cfg.scl_start_period - cfg->i2c_timing_cfg.sda_duty_period); - uint32_t setup_time_data = (cfg->i2c_timing_cfg.scl_low_period - cfg->i2c_timing_cfg.sda_duty_period); + float scl_low_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_low_period); + float scl_high_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_high_period); + float sda_duty_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.sda_duty_period); + float scl_start_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_start_period); + float scl_stop_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_stop_period); + float i2c_trans_timeout = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.i2c_trans_timeout); + float setup_time_start = (cfg->i2c_timing_cfg.scl_high_period + cfg->i2c_timing_cfg.sda_duty_period); + float hold_time_start = (cfg->i2c_timing_cfg.scl_start_period - cfg->i2c_timing_cfg.sda_duty_period); + float setup_time_data = (cfg->i2c_timing_cfg.scl_low_period - cfg->i2c_timing_cfg.sda_duty_period); /* Verify timing constraints */ - ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_low_period > 1.3, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL low period cannot be less than 1.3 micro seconds"); - ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_high_period > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL high period cannot be less than 0.6 micro seconds"); - ESP_RETURN_ON_FALSE((float)setup_time_start > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds"); - ESP_RETURN_ON_FALSE((float)hold_time_start > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be less than 0.6 micro seconds"); - ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_stop_period > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds"); - ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.sda_duty_period < 3.45, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be greater than 3.45 micro seconds"); - ESP_RETURN_ON_FALSE((float)(setup_time_data * 1000) > 250, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data setup time cannot be less than 250 nano seconds"); + ESP_RETURN_ON_FALSE(cfg->i2c_timing_cfg.scl_low_period >= 1.3f, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL low period cannot be less than 1.3 micro seconds"); + // TODO: As per specs, SCL high period must be greater than 0.6 micro seconds but after tests it is found that we can have a the period as 0.3 micro seconds to + // achieve performance close to I2C fast mode. Therefore, this criteria is relaxed. + ESP_RETURN_ON_FALSE(cfg->i2c_timing_cfg.scl_high_period >= 0.3f, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL high period cannot be less than 0.3 micro seconds"); + ESP_RETURN_ON_FALSE(setup_time_start >= 0.6f, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds"); + ESP_RETURN_ON_FALSE(hold_time_start >= 0.6f, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be less than 0.6 micro seconds"); + ESP_RETURN_ON_FALSE(cfg->i2c_timing_cfg.scl_stop_period >= 0.6f, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds"); + ESP_RETURN_ON_FALSE(cfg->i2c_timing_cfg.sda_duty_period <= 3.45f, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be greater than 3.45 micro seconds"); + ESP_RETURN_ON_FALSE((setup_time_data * 1000) >= 250, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data setup time cannot be less than 250 nano seconds"); /* Verify filtering constrains * @@ -228,6 +233,41 @@ static void ulp_riscv_i2c_format_cmd(uint32_t cmd_idx, uint8_t op_code, uint8_t #endif // CONFIG_IDF_TARGET_ESP32S2 } +static inline esp_err_t ulp_riscv_i2c_wait_for_interrupt(uint32_t timeout) +{ + uint32_t status = 0; + uint32_t to = 0; + esp_err_t ret = ESP_ERR_TIMEOUT; + + while (to < timeout) { + status = READ_PERI_REG(RTC_I2C_INT_ST_REG); + + /* Return ESP_OK if Tx or Rx data interrupt bits are set. */ + if ((status & RTC_I2C_TX_DATA_INT_ST) || + (status & RTC_I2C_RX_DATA_INT_ST)) { + ret = ESP_OK; + break; + /* In case of error status, break and return ESP_FAIL */ +#if CONFIG_IDF_TARGET_ESP32S2 + } else if ((status & RTC_I2C_TIMEOUT_INT_ST) || +#elif CONFIG_IDF_TARGET_ESP32S3 + } else if ((status & RTC_I2C_TIME_OUT_INT_ST) || +#endif // CONFIG_IDF_TARGET_ESP32S2 + (status & RTC_I2C_ACK_ERR_INT_ST) || + (status & RTC_I2C_ARBITRATION_LOST_INT_ST)) { + ret = ESP_FAIL; + break; + } + + vTaskDelay(1); + + /* Loop timeout. If this expires, we return ESP_ERR_TIMEOUT */ + to++; + } + + return ret; +} + void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr) { CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_ADDR_MASK); @@ -261,6 +301,7 @@ void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size) { uint32_t i = 0; uint32_t cmd_idx = 0; + esp_err_t ret = ESP_OK; if (size == 0) { // Quietly return @@ -293,34 +334,40 @@ void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size) /* Configure the RTC I2C controller in read mode */ SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 0, 27); - /* Enable Rx data interrupt */ - SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA); - /* Start RTC I2C transmission */ SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE); SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START); for (i = 0; i < size; i++) { - /* Poll for RTC I2C Rx Data interrupt bit to be set */ - while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_RX_DATA_INT_ST)) { - /* Minimal delay to avoid hogging the CPU */ - vTaskDelay(1); - } - - /* Read the data - * - * Unfortunately, the RTC I2C has no fifo buffer to help us with reading and storing - * multiple bytes of data. Therefore, we need to read one byte at a time and clear the - * Rx interrupt to get ready for the next byte. + /* Poll for RTC I2C Rx Data interrupt bit to be set. + * Set a loop timeout of 500 iterations to bail in case of any driver + * and/or hardware errors. */ + ret = ulp_riscv_i2c_wait_for_interrupt(ULP_RISCV_I2C_RW_TIMEOUT); + + if (ret == ESP_OK) { + /* Read the data + * + * Unfortunately, the RTC I2C has no fifo buffer to help us with reading and storing + * multiple bytes of data. Therefore, we need to read one byte at a time and clear the + * Rx interrupt to get ready for the next byte. + */ #if CONFIG_IDF_TARGET_ESP32S2 - data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_RDATA); + data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_RDATA); #elif CONFIG_IDF_TARGET_ESP32S3 - data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_I2C_RDATA); + data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_I2C_RDATA); #endif // CONFIG_IDF_TARGET_ESP32S2 - /* Clear the Rx data interrupt bit */ - SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_RX_DATA_INT_CLR); + /* Clear the Rx data interrupt bit */ + SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_RX_DATA_INT_CLR); + } else { + ESP_LOGE(RTCI2C_TAG, "Read Failed!"); + uint32_t status = READ_PERI_REG(RTC_I2C_INT_RAW_REG); + ESP_LOGE(RTCI2C_TAG, "RTC I2C Interrupt Raw Reg 0x%"PRIx32"", status); + ESP_LOGE(RTCI2C_TAG, "RTC I2C Status Reg 0x%"PRIx32"", READ_PERI_REG(RTC_I2C_STATUS_REG)); + SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, status); + break; + } } /* Clear the RTC I2C transmission bits */ @@ -349,6 +396,7 @@ void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size) { uint32_t i = 0; uint32_t cmd_idx = 0; + esp_err_t ret = ESP_OK; if (size == 0) { // Quietly return @@ -367,9 +415,6 @@ void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size) /* Configure the RTC I2C controller in write mode */ SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 1, 27); - /* Enable Tx data interrupt */ - SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_TX_DATA_INT_ENA); - for (i = 0; i < size; i++) { /* Write the data to be transmitted */ CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_MASTER_TX_DATA_MASK); @@ -381,14 +426,23 @@ void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size) SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START); } - /* Poll for RTC I2C Tx Data interrupt bit to be set */ - while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_TX_DATA_INT_ST)) { - /* Minimal delay to avoid hogging the CPU */ - vTaskDelay(1); - } + /* Poll for RTC I2C Tx Data interrupt bit to be set. + * Set a loop timeout of 500 iterations to bail in case of any driver + * and/or hardware errors. + */ + ret = ulp_riscv_i2c_wait_for_interrupt(ULP_RISCV_I2C_RW_TIMEOUT); - /* Clear the Tx data interrupt bit */ - SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_TX_DATA_INT_CLR); + if (ret == ESP_OK) { + /* Clear the Tx data interrupt bit */ + SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_TX_DATA_INT_CLR); + } else { + ESP_LOGE(RTCI2C_TAG, "Write Failed!"); + uint32_t status = READ_PERI_REG(RTC_I2C_INT_RAW_REG); + ESP_LOGE(RTCI2C_TAG, "RTC I2C Interrupt Raw Reg 0x%"PRIx32"", status); + ESP_LOGE(RTCI2C_TAG, "RTC I2C Status Reg 0x%"PRIx32"", READ_PERI_REG(RTC_I2C_STATUS_REG)); + SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, status); + break; + } } /* Clear the RTC I2C transmission bits */ @@ -441,5 +495,16 @@ esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg) /* Configure RTC I2C timing paramters */ ESP_RETURN_ON_ERROR(i2c_set_timing(cfg), RTCI2C_TAG, "Failed to configure RTC I2C timing"); + /* Enable RTC I2C interrupts */ + SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA | + RTC_I2C_TX_DATA_INT_ENA | + RTC_I2C_ARBITRATION_LOST_INT_ENA | + RTC_I2C_ACK_ERR_INT_ENA | +#if CONFIG_IDF_TARGET_ESP32S2 + RTC_I2C_TIMEOUT_INT_ENA); +#elif CONFIG_IDF_TARGET_ESP32S3 + RTC_I2C_TIME_OUT_INT_ENA); +#endif // CONFIG_IDF_TARGET_ESP32S2 + return ESP_OK; } diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 0e12089805..284ede346c 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -266,6 +266,7 @@ INPUT = \ $(PROJECT_PATH)/components/ulp/ulp_fsm/include/ulp_fsm_common.h \ $(PROJECT_PATH)/components/ulp/ulp_riscv/include/ulp_riscv_lock.h \ $(PROJECT_PATH)/components/ulp/ulp_riscv/include/ulp_riscv.h \ + $(PROJECT_PATH)/components/ulp/ulp_riscv/include/ulp_riscv_i2c.h \ $(PROJECT_PATH)/components/ulp/ulp_riscv/shared/include/ulp_riscv_lock_shared.h \ $(PROJECT_PATH)/components/usb/include/usb/usb_helpers.h \ $(PROJECT_PATH)/components/usb/include/usb/usb_host.h \ diff --git a/docs/en/api-reference/system/ulp-risc-v.rst b/docs/en/api-reference/system/ulp-risc-v.rst index b90c914185..dfedb76fe3 100644 --- a/docs/en/api-reference/system/ulp-risc-v.rst +++ b/docs/en/api-reference/system/ulp-risc-v.rst @@ -150,6 +150,37 @@ The program runs until the field ``RTC_CNTL_COCPU_DONE`` in register ``RTC_CNTL_ To disable the timer (effectively preventing the ULP program from running again), please clear the ``RTC_CNTL_ULP_CP_SLP_TIMER_EN`` bit in the ``RTC_CNTL_ULP_CP_TIMER_REG`` register. This can be done both from the ULP code and from the main program. +ULP RISC-V Peripheral Support +------------------------------ + +To enhance the capabilities of the ULP RISC-V coprocessor, it has access to peripherals which operate in the low-power (RTC) domain. The ULP RISC-V coprocessor can interact with these peripherals when the main CPU is in sleep-mode and can wakeup the main CPU once a wakeup condition is reached. The following peripherals are supported: + +RTC I2C +^^^^^^^^ + +The RTC I2C controller provides I2C Master functionality in the RTC domain. The ULP RISC-V coprocessor can read from or write to I2C slave devices using this controller. To use the RTC I2C peripheral call the :cpp:func:`ulp_riscv_i2c_master_init` from your application running on the main core before initializing the ULP RISC-V core and going in to sleep. + +Once the RTC I2C controller is initialized, the I2C slave device address must be programmed via the :cpp:func:`ulp_riscv_i2c_master_set_slave_addr` API before any read or write operation is performed. + +.. note:: The RTC I2C peripheral always expects a slave sub-register address to be programmed via the :cpp:func:`ulp_riscv_i2c_master_set_slave_reg_addr` API. If it is not, the I2C peripheral uses the ``SENS_SAR_I2C_CTRL_REG[18:11]`` as the sub register address for the subsequent read or write operations. This could make the RTC I2C peripheral incompatible with certain I2C devices or sensors which do not need any sub-register to be programmed. + +.. note:: There is no hardware atomicity protection in accessing the RTC I2C peripheral between the main CPU and the ULP RISC-V core. Therefore, care must be taken that both cores are not accessing the peripheral simultaneously. + +In case your RTC I2C based ULP RISC-V program is not working as expected, the following sanity checks can help in debugging the issue: + + * Incorrect SDA/SCL pin selection: The SDA pin can only be setup as GPIO1 or GPIO3 and SCL pin can only be setup as GPIO0 or GPIO2. Make sure that the pin configuration is correct. + + * Incorrect I2C timing parameters: The RTC I2C bus timing configuration is limited by the I2C standard bus specification. Any timining parameters which violate the standard I2C bus specifications would result in an error. For details on the timing parameters, please read the standard I2C bus specifications. + + * If the I2C slave device or sensor does not require a sub-register address to be programmed, it may not be compatible with the RTC I2C peripheral. Please refer the notes above. + + * If the RTC driver reports a `Write Failed!` or `Read Failed!` error log when run from the main CPU then make sure: + + * The I2C slave device or sensor works correctly with the standard I2C master on Espressif SoCs. This would rule out any problems with the I2C slave device itself. + * If the RTC I2C interrupt status log reports a `TIMEOUT` error or `ACK` error, it could typically mean that the I2C device did not respond to a `START` condition sent out by the RTC I2C controller. This could happen if the I2C slave device is not connected properly to the controller pins or if the I2C slave device is in a bad state. Make sure that the I2C slave device is in a good state and connected properly before continuing. + * If the RTC I2C interrupt log does not report any error status, it could mean that the driver is not fast enough in receiving data from the I2C slave device. This could happen as the RTC I2C controller does not have a Tx/Rx FIFO to store multiple bytes of data but rather, it depends on single byte transmissions using an interrupt status polling mechanism. This could be mitigated to some extent by making sure that the SCL clock of the peripheral is running with as fast as possible. This can be tweaked by configuring the SCL low period and SCL high period values in the initialization config parameters for the peripheral. + +* Other methods of debugging problems would be to ensure that the RTC I2C controller is operational *only* from the main CPU *without* any ULP RISC-V code interfering and *without* any sleepmode being activated. This is the basic configuration under which the RTC I2C peripheral must work. This way you can rule out any potential issues due to the ULP or sleep modes. Debugging Your ULP RISC-V Program ---------------------------------- @@ -170,6 +201,7 @@ Application Examples * ULP RISC-V Coprocessor polls GPIO while main CPU is in deep sleep: :example:`system/ulp_riscv/gpio`. * ULP RISC-V Coprocessor uses bit-banged UART driver to print: :example:`system/ulp_riscv/uart_print`. * ULP RISC-V Coprocessor reads external temperature sensor while main CPU is in deep sleep: :example:`system/ulp_riscv/ds18b20_onewire`. +* ULP RISC-V Coprocessor reads external I2C temperature and humidity sensor (BMP180) while the main CPU is in deep sleep and wakes up the main CPU once a threshold is met: :example:`system/ulp_riscv/i2c`. API Reference ------------- @@ -177,3 +209,4 @@ API Reference .. include-build-file:: inc/ulp_riscv.inc .. include-build-file:: inc/ulp_riscv_lock_shared.inc .. include-build-file:: inc/ulp_riscv_lock.inc +.. include-build-file:: inc/ulp_riscv_i2c.inc diff --git a/examples/system/ulp_riscv/i2c/main/ulp_riscv_rtc_i2c_example_main.c b/examples/system/ulp_riscv/i2c/main/ulp_riscv_rtc_i2c_example_main.c index 35d92db27d..55e58694ef 100644 --- a/examples/system/ulp_riscv/i2c/main/ulp_riscv_rtc_i2c_example_main.c +++ b/examples/system/ulp_riscv/i2c/main/ulp_riscv_rtc_i2c_example_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -162,7 +162,11 @@ static void init_i2c(void) /* Configure RTC I2C */ printf("Initializing RTC I2C ...\n"); ulp_riscv_i2c_cfg_t i2c_cfg = ULP_RISCV_I2C_DEFAULT_CONFIG(); - ulp_riscv_i2c_master_init(&i2c_cfg); + esp_err_t ret = ulp_riscv_i2c_master_init(&i2c_cfg); + if (ret!= ESP_OK) { + printf("ERROR: Failed to initialize RTC I2C. Aborting...\n"); + abort(); + } } static void bmp180_read16(uint16_t *data_out, uint32_t reg_msb, uint32_t reg_lsb)