mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
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:
@@ -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
|
||||
Reference in New Issue
Block a user