Преглед изворни кода

add example deep sleep wake stub

jiangguangming пре 3 година
родитељ
комит
51ec91c637

+ 4 - 0
components/esp_hw_support/CMakeLists.txt

@@ -79,6 +79,10 @@ if(NOT BOOTLOADER_BUILD)
         list(APPEND srcs "mspi_timing_tuning.c" "port/${target}/mspi_timing_config.c")
     endif()
 
+    if(CONFIG_SOC_RTC_FAST_MEM_SUPPORTED)
+        list(APPEND srcs "sleep_wake_stub.c")
+    endif()
+
     if(CONFIG_IDF_TARGET_ESP32H2)
         list(REMOVE_ITEM srcs
                 "adc_share_hw_ctrl.c" # TODO: IDF-6215

+ 68 - 0
components/esp_hw_support/include/esp_wake_stub.h

@@ -0,0 +1,68 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include "esp_log.h"
+#include "esp_sleep.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTC_STR(str) (__extension__({static const RTC_RODATA_ATTR char _fmt[] = (str); (const char *)&_fmt;}))
+#define RTC_LOG_FORMAT(letter, format)  LOG_COLOR_ ## letter format LOG_RESET_COLOR "\n"
+
+#define ESP_RTC_LOG( level, format, ... )  if (LOG_LOCAL_LEVEL >= level) { esp_rom_printf(RTC_STR(format), ##__VA_ARGS__); \
+                                                                            esp_wake_stub_uart_tx_wait_idle(0); }
+
+#define ESP_RTC_LOGE( format, ... )  ESP_RTC_LOG(ESP_LOG_ERROR, RTC_LOG_FORMAT(E, format), ##__VA_ARGS__)
+#define ESP_RTC_LOGW( format, ... )  ESP_RTC_LOG(ESP_LOG_WARN, RTC_LOG_FORMAT(W, format), ##__VA_ARGS__)
+#define ESP_RTC_LOGI( format, ... )  ESP_RTC_LOG(ESP_LOG_INFO, RTC_LOG_FORMAT(I, format), ##__VA_ARGS__)
+#define ESP_RTC_LOGD( format, ... )  ESP_RTC_LOG(ESP_LOG_DEBUG, RTC_LOG_FORMAT(D, format), ##__VA_ARGS__)
+#define ESP_RTC_LOGV( format, ... )  ESP_RTC_LOG(ESP_LOG_VERBOSE, RTC_LOG_FORMAT(V, format), ##__VA_ARGS__)
+
+/**
+ * @brief Enter deep-sleep mode from deep sleep wake stub code
+ *
+ * This should be called from the wake stub code.
+ *
+ * @param new_stub  new wake stub function will be set
+ */
+void esp_wake_stub_sleep(esp_deep_sleep_wake_stub_fn_t new_stub);
+
+/**
+ * @brief Wait while uart transmission is in progress
+ *
+ * This function is waiting while uart transmission is not completed,
+ * and this function should be called from the wake stub code.
+ *
+ * @param uart_no  UART port to wait idle
+ */
+void esp_wake_stub_uart_tx_wait_idle(uint8_t uart_no);
+
+/**
+ * @brief Set wakeup time from deep sleep stub.
+ *
+ * This should be called from the wake stub code.
+ *
+ * @param time_in_us  wakeup time in us
+ */
+void esp_wake_stub_set_wakeup_time(uint64_t time_in_us);
+
+/**
+ * @brief Get wakeup cause from deep sleep stub.
+ *
+ * This should be called from the wake stub code.
+ *
+ * @return wakeup casue value
+ */
+uint32_t esp_wake_stub_get_wakeup_cause(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 81 - 0
components/esp_hw_support/sleep_wake_stub.c

@@ -0,0 +1,81 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <sys/lock.h>
+#include <sys/param.h>
+
+#include "esp_attr.h"
+#include "esp_sleep.h"
+
+#include "soc/soc.h"
+#include "soc/rtc.h"
+#include "soc/soc_caps.h"
+#include "hal/rtc_cntl_ll.h"
+#include "hal/uart_ll.h"
+
+#include "sdkconfig.h"
+#include "esp_rom_uart.h"
+#include "esp_rom_sys.h"
+
+#ifdef CONFIG_IDF_TARGET_ESP32
+#include "esp32/rom/rtc.h"
+#elif CONFIG_IDF_TARGET_ESP32S2
+#include "esp32s2/rom/rtc.h"
+#elif CONFIG_IDF_TARGET_ESP32S3
+#include "esp32s3/rom/rtc.h"
+#elif CONFIG_IDF_TARGET_ESP32C3
+#include "esp32c3/rom/rtc.h"
+#elif CONFIG_IDF_TARGET_ESP32H4
+#include "esp32h4/rom/rtc.h"
+#elif CONFIG_IDF_TARGET_ESP32C6
+#include "esp32c6/rom/rtc.h"
+#elif CONFIG_IDF_TARGET_ESP32H2
+#include "esp32h2/rom/rtc.h"
+#endif
+
+void RTC_IRAM_ATTR esp_wake_stub_sleep(esp_deep_sleep_wake_stub_fn_t new_stub)
+{
+#if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY
+    extern char _rtc_text_start[];
+    #if CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM
+    extern char _rtc_noinit_end[];
+    size_t rtc_fast_length = (size_t)_rtc_noinit_end - (size_t)_rtc_text_start;
+    #else
+    extern char _rtc_force_fast_end[];
+    size_t rtc_fast_length = (size_t)_rtc_force_fast_end - (size_t)_rtc_text_start;
+    #endif // CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM
+    esp_rom_set_rtc_wake_addr((esp_rom_wake_func_t)new_stub, rtc_fast_length);
+#else
+    // Set the pointer of the wake stub function.
+    REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)new_stub);
+    set_rtc_memory_crc();
+#endif // SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_MEM
+
+    // Go to sleep.
+    rtc_cntl_ll_sleep_enable();
+    // A few CPU cycles may be necessary for the sleep to start...
+    while (true) {};
+    // never reaches here.
+}
+
+void RTC_IRAM_ATTR esp_wake_stub_uart_tx_wait_idle(uint8_t uart_no)
+{
+    while (!uart_ll_is_tx_idle(UART_LL_GET_HW(uart_no))) {};
+}
+
+void RTC_IRAM_ATTR esp_wake_stub_set_wakeup_time(uint64_t time_in_us)
+{
+    uint64_t rtc_count_delta = rtc_cntl_ll_time_to_count(time_in_us);
+    uint64_t rtc_curr_count = rtc_cntl_ll_get_rtc_time();
+    rtc_cntl_ll_set_wakeup_timer(rtc_curr_count + rtc_count_delta);
+}
+
+uint32_t RTC_IRAM_ATTR esp_wake_stub_get_wakeup_cause(void)
+{
+    return rtc_cntl_ll_get_wakeup_cause();
+}

+ 6 - 0
examples/system/.build-test-rules.yml

@@ -42,6 +42,12 @@ examples/system/deep_sleep:
       temporary: true
       reason: target(s) not supported yet
 
+examples/system/deep_sleep_wake_stub:
+  disable:
+    - if: IDF_TARGET in ["esp32c2", "esp32c6", "esp32h2"]
+      temporary: true
+      reason: target(s) is not supported yet
+
 examples/system/efuse:
   disable_test:
     - if: IDF_TARGET == "esp32s3"

+ 6 - 0
examples/system/deep_sleep_wake_stub/CMakeLists.txt

@@ -0,0 +1,6 @@
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(deep_sleep_wake_stub)

+ 75 - 0
examples/system/deep_sleep_wake_stub/README.md

@@ -0,0 +1,75 @@
+| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |
+
+# Deep Sleep Wake Stub Example
+
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
+
+The [Deep-sleep wake stub](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/deep-sleep-stub.html) is used to RTC fast boot mode that avoid the SPI flash booting, thus speeding up the wakeup process. This example demonstrates how to implement the wake stub.
+
+In this example, the `CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` Kconfig option is used, which allows you to reduce the boot time of the bootloader during waking up from deep sleep. The bootloader stores in rtc memory the address of a running partition and uses it when it wakes up. This example allows you to skip all image checks and speed up the boot.
+
+## How to use example
+
+### Hardware Required
+
+This example should be able to run on any commonly available ESP32/ESP32-S2/ESP32-S3/ESP32-C3 development board without any extra hardware if only **Timer** wake up sources are used.
+
+### Configure the project
+
+
+```
+idf.py menuconfig
+```
+
+* **Wake up time** can be configured via `Example configuration > Wake up interval in seconds`
+Wake up sources that are unused or unconnected should be disabled in configuration to prevent inadvertent triggering of wake up as a result of floating pins.
+
+
+### Build and Flash
+
+Build the project and flash it to the board, then run monitor tool to view serial output:
+
+```
+idf.py -p PORT flash monitor
+```
+
+(Replace PORT with the name of the serial port to use.)
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
+
+## Example Output
+
+On initial startup, this example will detect that this is the first boot and output the following log:
+
+```
+...
+I (309) cpu_start: Starting scheduler on PRO CPU.
+I (0) cpu_start: Starting scheduler on APP CPU.
+Not a deep sleep reset
+Enabling timer wakeup, 10s
+Entering deep sleep
+```
+
+The ESP chips will then enter deep sleep. When a timer wake up occurs, if deep sleep wake stub enabled, the ESP chips will boot from RTC memory and execute stub code. The output log such as the following:
+
+```
+...
+ESP-ROM:esp32s3-20210327
+Build:Mar 27 2021
+rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT)
+wake stub: wakeup count is 1, wakeup cause is 8
+wake stub: going to deep sleep
+ESP-ROM:esp32s3-20210327
+Build:Mar 27 2021
+rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT)
+wake stub: wakeup count is 2, wakeup cause is 8
+wake stub: going to deep sleep
+ESP-ROM:esp32s3-20210327
+Build:Mar 27 2021
+rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT)
+wake stub: wakeup count is 3, wakeup cause is 8
+wake stub: going to deep sleep
+```

+ 3 - 0
examples/system/deep_sleep_wake_stub/main/CMakeLists.txt

@@ -0,0 +1,3 @@
+idf_component_register(SRCS "wake_stub_example_main.c"
+                            "rtc_wake_stub_example.c"
+                    INCLUDE_DIRS ".")

+ 10 - 0
examples/system/deep_sleep_wake_stub/main/Kconfig.projbuild

@@ -0,0 +1,10 @@
+menu "Example Configuration"
+
+    config WAKE_UP_TIME
+        int "Wake up interval in seconds"
+        default 10
+        range 1 60
+        help
+            Configurable wake up interval in seconds.
+
+endmenu

+ 68 - 0
examples/system/deep_sleep_wake_stub/main/rtc_wake_stub_example.c

@@ -0,0 +1,68 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#include <inttypes.h>
+#include "esp_sleep.h"
+#include "esp_wake_stub.h"
+#include "sdkconfig.h"
+
+/*
+ * Deep sleep wake stub function is a piece of code that will be loaded into 'RTC Fast Memory'.
+ * The first way is to use the RTC_IRAM_ATTR attribute to place a function into RTC memory,
+ * The second way is to place the function into any source file whose name starts with rtc_wake_stub.
+ * Files names rtc_wake_stub* have their contents automatically put into RTC memory by the linker.
+ *
+ * First, call esp_set_deep_sleep_wake_stub to set the wake stub function as the RTC stub entry,
+ * The wake stub function runs immediately as soon as the chip wakes up - before any normal
+ * initialisation, bootloader, or ESP-IDF code has run. After the wake stub runs, the SoC
+ * can go back to sleep or continue to start ESP-IDF normally.
+ *
+ * Wake stub code must be carefully written, there are some rules for wake stub:
+ * 1) The wake stub code can only access data loaded in RTC memory.
+ * 2) The wake stub code can only call functions implemented in ROM or loaded into RTC Fast Memory.
+ * 3) RTC memory must include any read-only data (.rodata) used by the wake stub.
+ */
+
+// counter value, stored in RTC memory
+static uint32_t s_count = 0;
+static const uint32_t s_max_count = 20;
+
+// wakeup_cause stored in RTC memory
+static uint32_t wakeup_cause;
+
+// wake up stub function stored in RTC memory
+void wake_stub_example(void)
+{
+    // Get wakeup cause.
+    wakeup_cause = esp_wake_stub_get_wakeup_cause();
+    // Increment the counter.
+    s_count++;
+    // Print the counter value and wakeup cause.
+    ESP_RTC_LOGI("wake stub: wakeup count is %d, wakeup cause is %d", s_count, wakeup_cause);
+
+    if (s_count >= s_max_count) {
+        // Reset s_count
+        s_count = 0;
+
+        // Set the default wake stub.
+        // There is a default version of this function provided in esp-idf.
+        esp_default_wake_deep_sleep();
+
+        // Return from the wake stub function to continue
+        // booting the firmware.
+        return;
+    }
+    // s_count is < s_max_count, go back to deep sleep.
+
+    // Set wakeup time in stub, if need to check GPIOs or read some sensor periodically in the stub.
+    esp_wake_stub_set_wakeup_time(CONFIG_WAKE_UP_TIME*1000000);
+
+    // Print status.
+    ESP_RTC_LOGI("wake stub: going to deep sleep");
+
+    // Set stub entry, then going to deep sleep again.
+    esp_wake_stub_sleep(&wake_stub_example);
+}

+ 17 - 0
examples/system/deep_sleep_wake_stub/main/rtc_wake_stub_example.h

@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void wake_stub_example(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 53 - 0
examples/system/deep_sleep_wake_stub/main/wake_stub_example_main.c

@@ -0,0 +1,53 @@
+/*
+ * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include "sdkconfig.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_sleep.h"
+#include "esp_wake_stub.h"
+#include "driver/rtc_io.h"
+#include "rtc_wake_stub_example.h"
+
+// sleep_enter_time stored in RTC memory
+static RTC_DATA_ATTR struct timeval sleep_enter_time;
+
+void app_main(void)
+{
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000;
+
+    if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
+        printf("Wake up from timer. Time spent in deep sleep: %dms\n", sleep_time_ms);
+    }
+
+    vTaskDelay(1000 / portTICK_PERIOD_MS);
+
+    const int wakeup_time_sec = CONFIG_WAKE_UP_TIME;
+    printf("Enabling timer wakeup, %ds\n", wakeup_time_sec);
+    esp_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000);
+
+#if CONFIG_IDF_TARGET_ESP32
+    // Isolate GPIO12 pin from external circuits. This is needed for modules
+    // which have an external pull-up resistor on GPIO12 (such as ESP32-WROVER)
+    // to minimize current consumption.
+    rtc_gpio_isolate(GPIO_NUM_12);
+#endif
+
+    // Set the wake stub function
+    esp_set_deep_sleep_wake_stub(&wake_stub_example);
+
+    printf("Entering deep sleep\n");
+    gettimeofday(&sleep_enter_time, NULL);
+
+    esp_deep_sleep_start();
+}

+ 1 - 0
examples/system/deep_sleep_wake_stub/sdkconfig.defaults

@@ -0,0 +1 @@
+CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y