Merge branch 'feature/example_deep_sleep_wake_stub_backport_v4.4' into 'release/v4.4'

example: add deepsleep_wake stub example (backport v4.4)

See merge request espressif/esp-idf!23360
This commit is contained in:
Jiang Jiang Jian
2023-06-09 19:11:20 +08:00
32 changed files with 657 additions and 88 deletions
+1 -1
View File
@@ -68,7 +68,7 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
## Example Output
On initial startup, this example will detect that this is the first boot and output the following low:
On initial startup, this example will detect that this is the first boot and output the following log:
```
...
@@ -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.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(deep_sleep_wake_stub)
@@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := deep_sleep_wake_stub
include $(IDF_PATH)/make/project.mk
@@ -0,0 +1,84 @@
| 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 runs on any commonly available development board with a chip listed under "Supported Targets" without requiring 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, wakeup cost 7698 us
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, wakeup cost 7694 us
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, wakeup cost 7693 us
wake stub: going to deep sleep
```
> Note:
> 1. The wakeup time cost printed in this example is not entirely accurate. This is because it does not take into account the hardware initialization time before the CPU starts. For most ESP chips, the initialization time is about 280 us.
> 2. The wake-up time shown in this example may be reduced by disabling the logs printed by ROM code. For most ESP chips, this ROM printing takes about 6100 us. In the product firmware, users can temporarily or permanently turn off ROM printing by calling ``esp_deep_sleep_disable_rom_logging`` or by setting ``menuconfig`` > ``Boot ROM Behavior`` > ``Permanently disable logging`` to speed up the wake-up.(ESP32 does not support suppressing ROM logging through menuconfig, but it can be suppressed by grounding GPIO15)
> 3. Here is a method for roughly estimating optimal wake-up time: Taking ESP32-S3 as an example, the wake-up time from stub printing is about 7700 us. However, by substracting the ROM printing overhead of 6100 us and adding the system initialization overhead of 280 us, the wake-up overhead is estimated to be around 1880 us. Users also can modify the example to configure GPIO wake-up and obtain a more realistic and accurate wake-up time by grabbing GPIO signals with a logic analyzer.
@@ -0,0 +1,32 @@
from __future__ import unicode_literals
import time
import tiny_test_fw
import ttfw_idf
try:
import typing # noqa: F401 # pylint: disable=unused-import
except ImportError:
pass
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32s2', 'esp32c3', 'esp32s3'])
def test_deep_sleep_wake_stub(env, extra_data): # type: (tiny_test_fw.Env, typing.Any) -> None
dut = env.get_dut('deep_sleep', 'examples/system/deep_sleep_wake_stub')
dut.start_app()
dut.expect('Enabling timer wakeup, 10s', timeout=30)
dut.expect('Entering deep sleep', timeout=10)
start_sleep = time.time()
print('Waiting for wakeup...')
dut.expect('wake stub: going to deep sleep')
sleep_time = time.time() - start_sleep
print('Host measured sleep time at {:.2f}s'.format(sleep_time))
assert 8 < sleep_time < 12 # note: high tolerance as measuring time on the host may have some timing skew
if __name__ == '__main__':
test_deep_sleep_wake_stub()
@@ -0,0 +1,3 @@
idf_component_register(SRCS "wake_stub_example_main.c"
"rtc_wake_stub_example.c"
INCLUDE_DIRS ".")
@@ -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
@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
@@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <inttypes.h>
#include "esp_sleep.h"
#include "hal/cpu_hal.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;
// wakeup_time from CPU start to wake stub
static uint32_t wakeup_time;
// wake up stub function stored in RTC memory
void wake_stub_example(void)
{
// Get wakeup time.
extern uint32_t ets_get_cpu_frequency(void);
wakeup_time = cpu_hal_get_cycle_count() / ets_get_cpu_frequency();
// 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, wakeup cost %ld us", s_count, wakeup_cause, wakeup_time);
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);
}
@@ -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
@@ -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();
}
@@ -0,0 +1 @@
CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y