Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using SCL for deep-sleep wakeup breaks I2C after one sleep cycle (IDFGH-9572) #10921

Closed
3 tasks done
tadejg opened this issue Mar 6, 2023 · 4 comments
Closed
3 tasks done
Assignees
Labels
Resolution: Done Issue is done internally Status: Done Issue is done internally

Comments

@tadejg
Copy link

tadejg commented Mar 6, 2023

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

General issue report

We have a product made up of two separate units only connected by a USB cable. USB power pins are used to deliver power to the secondary module and data pins are used as an I2C bus to enable communication between the two. Recently, a need arose to have the secondary module wakeup the ESP32c3 (located on the primary module) from deep sleep. Since our I2C bus is connected to RTC GPIOs 4 (SDA) & 5 (SCL) and we can't add an additional interrupt line between the two modules, we want to use SCL to issue a wakeup interrupt to the ESP.

However, when testing this approach, we ran into an issue with I2C breaking after the first sleep cycle. I have prepared a reproducible example which you can find below. The example expects an I2C device with address 0x0E and register 0x0F set to 0x35. After flashing the firmware it's able to successfully read the register and enter deep-sleep. After it wakes up, the I2C command times out, causing an abort. Device gets stuck in this cycle until a hard reset is issued. If you comment out the line which configures SCL pin for wakeup, firmware works as expected - reads register, enters sleep, wakes up, and continues without error.

We're using ESP32c3 with ESP-IDF v5.0.1

#include "esp_err.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "esp_sleep.h"

#define I2C_SDA 4
#define I2C_SCL 5

#define I2C_SLAVE_ADDRESS 0x0E
#define I2C_WHO_AM_I_REG 0x0F
#define I2C_WHO_AM_I_VAL 0x35

#define I2C_OPERATION_WRITE 0
#define I2C_OPERATION_READ 1

static const char* TAG = "main";

void app_main(void) {
  ESP_LOGI(TAG, "I'm awake");
  i2c_config_t i2c_config = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = I2C_SDA,
    .scl_io_num = I2C_SCL,
    .sda_pullup_en = true,
    .scl_pullup_en = true,
    .master = {.clk_speed = 100000},
    .clk_flags = 0
  };
  ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_config));
  ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, i2c_config.mode, 0, 0, ESP_INTR_FLAG_IRAM));
  i2c_cmd_handle_t handle = i2c_cmd_link_create();
  i2c_master_start(handle);
  i2c_master_write_byte(handle, (I2C_SLAVE_ADDRESS << 1) | I2C_OPERATION_WRITE, true);
  i2c_master_write_byte(handle, I2C_WHO_AM_I_REG, true);
  i2c_master_start(handle);
  i2c_master_write_byte(handle, (I2C_SLAVE_ADDRESS << 1) | I2C_OPERATION_READ, true);
  uint8_t data = 0;
  i2c_master_read_byte(handle, &data, I2C_MASTER_NACK);
  i2c_master_stop(handle);
  esp_err_t err = i2c_master_cmd_begin(I2C_NUM_0, handle, 3 * 1000 / portTICK_PERIOD_MS);
  i2c_cmd_link_delete(handle);
  if (err != ESP_OK) {
    ESP_LOGE(TAG, "I2C error (%s)", esp_err_to_name(err));
    i2c_reset_rx_fifo(I2C_NUM_0);
    i2c_reset_tx_fifo(I2C_NUM_0);
    abort();
  }
  if(data == I2C_WHO_AM_I_VAL) {
    ESP_LOGI(TAG, "WHO_AM_I OK");
  } else {
    ESP_LOGE(TAG, "WHO_AM_I NOT OK");
  }
  ESP_LOGI(TAG, "Waiting 3 seconds...");
  vTaskDelay(3000 / portTICK_PERIOD_MS);
  ESP_ERROR_CHECK(i2c_driver_delete(I2C_NUM_0));

  // This causes I2C to break after the first sleep cycle; if commented out, firmware works without issues
  esp_deep_sleep_enable_gpio_wakeup(1 << I2C_SCL, ESP_GPIO_WAKEUP_GPIO_LOW);

  esp_sleep_enable_timer_wakeup(1000 * 1000 * 5);
  ESP_LOGI(TAG, "Going to sleep for 5 seconds...");
  esp_deep_sleep_start();
}

Output before entering deep sleep:

...
I (257) main: I'm awake
I (257) main: WHO_AM_I OK
I (257) main: Waiting 3 seconds...
I (3257) main: Going to sleep for 5 seconds...

Output after waking up from deep-sleep:

...
I (257) main: I'm awake
E (3257) main: I2C error (ESP_ERR_TIMEOUT)

abort() was called at PC 0x42005e75 on core 0
...

As a workaround, I tried replacing abort() with esp_restart(), but that didn't change anything. It appears the only solution is to pull CHIP_EN low.

@espressif-bot espressif-bot added the Status: Opened Issue is new label Mar 6, 2023
@github-actions github-actions bot changed the title Using SCL for deep-sleep wakeup breaks I2C after one sleep cycle Using SCL for deep-sleep wakeup breaks I2C after one sleep cycle (IDFGH-9572) Mar 6, 2023
@tadejg
Copy link
Author

tadejg commented Mar 6, 2023

UPDATE: While making some changes in an older version of our firmware (built with ESP-IDF v4.4.1), I inadvertently ran into the same issue, but this time with the LEDC component. I was changing it to work on a different board, so I set LEDC to use a GPIO which was previously used for wakeup from deep sleep. LEDC ended up working after a hard reset, but stopped working after device entered deep-sleep and woke back up. The only way to revive it was again to hard reset the chip (pull CHIP_EN low).

So as it turns out, this issue isn't exclusive to I2C. I'll do some tests with regular digital GPIOs to see if those are affected as well.

Additionally, I tried disabling all wakeup sources as soon as the device woke up from deep sleep, but that didn't have any effect.

...
void app_main(void) {

  ESP_ERROR_CHECK(esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL));  // ADDED THIS

  ESP_LOGI(TAG, "I'm awake");
  i2c_config_t i2c_config = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = I2C_SDA,
    .scl_io_num = I2C_SCL,
    .sda_pullup_en = true,
    .scl_pullup_en = true,
    .master = {.clk_speed = 100000},
    .clk_flags = 0
  };
...

@ginkgm
Copy link
Collaborator

ginkgm commented Mar 8, 2023

Hi tadejg,

We have a fix on master (eae70a8) is supposed to fix the issue. Before the backports are ready, is it possible to try running your app on master branch to see if it's the correct fix for your issue?

Thanks,

Michael

@espressif-bot espressif-bot added Status: In Progress Work is in progress and removed Status: Opened Issue is new labels Mar 8, 2023
@tadejg
Copy link
Author

tadejg commented Mar 8, 2023

@ginkgm thank you, switching to master fixed the issue. Any ETA on when the fix will be backported?

@espressif-bot espressif-bot added Status: Reviewing Issue is being reviewed and removed Status: In Progress Work is in progress labels Mar 31, 2023
@AxelLin
Copy link
Contributor

AxelLin commented Oct 3, 2023

Fix for v5.0 branch: 55e040b
v4.4: 9516e80

@espressif-bot espressif-bot added Status: Done Issue is done internally Resolution: Done Issue is done internally and removed Status: Reviewing Issue is being reviewed labels Oct 7, 2023
@songruo songruo closed this as completed Nov 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: Done Issue is done internally Status: Done Issue is done internally
Projects
None yet
Development

No branches or pull requests

5 participants