From: Ivan Grokhotkov Date: Thu, 12 Apr 2018 10:18:45 +0000 (+0800) Subject: freertos,esp32: automatic light sleep support X-Git-Tag: v3.1-beta1~110^2~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=028fbb58e8e97071285111919f91f17bd59a7256;p=esp-idf freertos,esp32: automatic light sleep support --- diff --git a/components/esp32/freertos_hooks.c b/components/esp32/freertos_hooks.c index d2b24e9952..328ef36e26 100644 --- a/components/esp32/freertos_hooks.c +++ b/components/esp32/freertos_hooks.c @@ -59,6 +59,10 @@ void esp_vApplicationIdleHook() #ifdef CONFIG_PM_ENABLE esp_pm_impl_idle_hook(); #endif +} + +extern void esp_vApplicationWaitiHook( void ) +{ asm("waiti 0"); } diff --git a/components/esp32/pm_esp32.c b/components/esp32/pm_esp32.c index 11a4d3c0db..f75b3fe811 100644 --- a/components/esp32/pm_esp32.c +++ b/components/esp32/pm_esp32.c @@ -22,10 +22,12 @@ #include "esp_pm.h" #include "esp_log.h" #include "esp_crosscore_int.h" +#include "esp_clk.h" #include "soc/rtc.h" #include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "freertos/xtensa_timer.h" #include "xtensa/core-macros.h" @@ -39,6 +41,16 @@ */ #define CCOMPARE_UPDATE_TIMEOUT 1000000 +/* When changing CCOMPARE, don't allow changes if the difference is less + * than this. This is to prevent setting CCOMPARE below CCOUNT. + */ +#define CCOMPARE_MIN_CYCLES_IN_FUTURE 1000 + +/* When light sleep is used, wake this number of microseconds earlier than + * the next tick. + */ +#define LIGHT_SLEEP_EARLY_WAKEUP_US 100 + #ifdef CONFIG_PM_PROFILING #define WITH_PROFILING #endif @@ -107,7 +119,7 @@ static const char* s_freq_names[] __attribute__((unused)) = { [RTC_CPU_FREQ_2M] = "2" }; -/* Whether automatic light sleep is enabled. Currently always false */ +/* Whether automatic light sleep is enabled */ static bool s_light_sleep_en = false; /* When configuration is changed, current frequency may not match the @@ -177,9 +189,11 @@ esp_err_t esp_pm_configure(const void* vconfig) #endif const esp_pm_config_esp32_t* config = (const esp_pm_config_esp32_t*) vconfig; +#ifndef CONFIG_FREERTOS_USE_TICKLESS_IDLE if (config->light_sleep_enable) { return ESP_ERR_NOT_SUPPORTED; } +#endif if (config->min_cpu_freq == RTC_CPU_FREQ_2M) { /* Minimal APB frequency to achieve 1MHz REF_TICK frequency is 5 MHz */ @@ -401,10 +415,9 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode) */ static void IRAM_ATTR update_ccompare() { - const uint32_t ccompare_min_cycles_in_future = 1000; uint32_t ccount = XTHAL_GET_CCOUNT(); uint32_t ccompare = XTHAL_GET_CCOMPARE(XT_TIMER_INDEX); - if ((ccompare - ccompare_min_cycles_in_future) - ccount < UINT32_MAX / 2) { + if ((ccompare - CCOMPARE_MIN_CYCLES_IN_FUTURE) - ccount < UINT32_MAX / 2) { uint32_t diff = ccompare - ccount; uint32_t diff_scaled = (diff * s_ccount_mul + s_ccount_div - 1) / s_ccount_div; if (diff_scaled < _xt_tick_divisor) { @@ -453,6 +466,56 @@ void IRAM_ATTR esp_pm_impl_isr_hook() ESP_PM_TRACE_EXIT(ISR_HOOK, core_id); } +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE + +bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime ) +{ + bool result = false; + portENTER_CRITICAL(&s_switch_lock); + if (s_mode == PM_MODE_LIGHT_SLEEP && !s_is_switching) { + /* Calculate how much we can sleep */ + int64_t next_esp_timer_alarm = esp_timer_get_next_alarm(); + int64_t now = esp_timer_get_time(); + int64_t time_until_next_alarm = next_esp_timer_alarm - now; + int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime; + int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm); + if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) { + esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US); +#ifdef CONFIG_PM_TRACE + /* to force tracing GPIOs to keep state */ + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); +#endif + /* Enter sleep */ + int core_id = xPortGetCoreID(); + ESP_PM_TRACE_ENTER(SLEEP, core_id); + int64_t sleep_start = esp_timer_get_time(); + esp_light_sleep_start(); + int64_t slept_us = esp_timer_get_time() - sleep_start; + ESP_PM_TRACE_EXIT(SLEEP, core_id); + + uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL); + if (slept_ticks > 0) { + /* Adjust RTOS tick count based on the amount of time spent in sleep */ + vTaskStepTick(slept_ticks); + + /* Trigger tick interrupt, since sleep time was longer + * than portTICK_PERIOD_MS. Note that setting INTSET does not + * work for timer interrupt, and changing CCOMPARE would clear + * the interrupt flag. + */ + XTHAL_SET_CCOUNT(XTHAL_GET_CCOMPARE(XT_TIMER_INDEX) - 16); + while (!(XTHAL_GET_INTERRUPT() & BIT(XT_TIMER_INTNUM))) { + ; + } + } + result = true; + } + } + portEXIT_CRITICAL(&s_switch_lock); + return result; +} +#endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE + #ifdef WITH_PROFILING void esp_pm_impl_dump_stats(FILE* out) { diff --git a/components/esp32/pm_trace.c b/components/esp32/pm_trace.c index c1f240017e..eddaec3843 100644 --- a/components/esp32/pm_trace.c +++ b/components/esp32/pm_trace.c @@ -27,6 +27,7 @@ static const int DRAM_ATTR s_trace_io[] = { BIT(18), BIT(18), // ESP_PM_TRACE_FREQ_SWITCH BIT(19), BIT(19), // ESP_PM_TRACE_CCOMPARE_UPDATE BIT(25), BIT(26), // ESP_PM_TRACE_ISR_HOOK + BIT(27), BIT(27), // ESP_PM_TRACE_SLEEP }; void esp_pm_trace_init() diff --git a/components/esp32/pm_trace.h b/components/esp32/pm_trace.h index 1aff1ac806..20eebb0d69 100644 --- a/components/esp32/pm_trace.h +++ b/components/esp32/pm_trace.h @@ -22,6 +22,7 @@ typedef enum { ESP_PM_TRACE_FREQ_SWITCH, ESP_PM_TRACE_CCOMPARE_UPDATE, ESP_PM_TRACE_ISR_HOOK, + ESP_PM_TRACE_SLEEP, ESP_PM_TRACE_TYPE_MAX } esp_pm_trace_event_t; diff --git a/components/esp32/test/test_pm.c b/components/esp32/test/test_pm.c index 7033238a04..fa432eddc9 100644 --- a/components/esp32/test/test_pm.c +++ b/components/esp32/test/test_pm.c @@ -7,7 +7,14 @@ #include "esp_clk.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/semphr.h" #include "esp_log.h" +#include "driver/timer.h" +#include "driver/rtc_io.h" +#include "esp32/ulp.h" +#include "soc/rtc_io_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_gpio_channel.h" TEST_CASE("Can dump power management lock stats", "[pm]") { @@ -48,4 +55,256 @@ TEST_CASE("Can switch frequency using esp_pm_configure", "[pm]") switch_freq(orig_freq_mhz); } +#if CONFIG_FREERTOS_USE_TICKLESS_IDLE + +static void light_sleep_enable() +{ + const esp_pm_config_esp32_t pm_config = { + .max_cpu_freq = rtc_clk_cpu_freq_get(), + .min_cpu_freq = RTC_CPU_FREQ_XTAL, + .light_sleep_enable = true + }; + ESP_ERROR_CHECK( esp_pm_configure(&pm_config) ); +} + +static void light_sleep_disable() +{ + const esp_pm_config_esp32_t pm_config = { + .max_cpu_freq = rtc_clk_cpu_freq_get(), + .min_cpu_freq = rtc_clk_cpu_freq_get(), + }; + ESP_ERROR_CHECK( esp_pm_configure(&pm_config) ); +} + +TEST_CASE("Automatic light occurs when tasks are suspended", "[pm]") +{ + /* To figure out if light sleep takes place, use Timer Group timer. + * It will stop working while in light sleep. + */ + timer_config_t config = { + .counter_dir = TIMER_COUNT_UP, + .divider = 80 /* 1 us per tick */ + }; + timer_init(TIMER_GROUP_0, TIMER_0, &config); + timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); + timer_start(TIMER_GROUP_0, TIMER_0); + + light_sleep_enable(); + + for (int ticks_to_delay = CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP; + ticks_to_delay < CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP * 10; + ++ticks_to_delay) { + + /* Wait until next tick */ + vTaskDelay(1); + + /* The following delay should cause light sleep to start */ + uint64_t count_start; + timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &count_start); + vTaskDelay(ticks_to_delay); + uint64_t count_end; + timer_get_counter_value(TIMER_GROUP_0, TIMER_0, &count_end); + + + int timer_diff_us = (int) (count_end - count_start); + const int us_per_tick = 1 * portTICK_PERIOD_MS * 1000; + + printf("%d %d\n", ticks_to_delay * us_per_tick, timer_diff_us); + TEST_ASSERT(timer_diff_us < ticks_to_delay * us_per_tick); + } + + light_sleep_disable(); +} + + +TEST_CASE("Can wake up from automatic light sleep by GPIO", "[pm]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 16 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + + /* Set up GPIO used to wake up RTC */ + const int ext1_wakeup_gpio = 25; + const int ext_rtc_io = RTCIO_GPIO25_CHANNEL; + TEST_ESP_OK(rtc_gpio_init(ext1_wakeup_gpio)); + rtc_gpio_set_direction(ext1_wakeup_gpio, RTC_GPIO_MODE_INPUT_OUTPUT); + rtc_gpio_set_level(ext1_wakeup_gpio, 0); + + /* Enable wakeup */ + TEST_ESP_OK(esp_sleep_enable_ext1_wakeup(1ULL << ext1_wakeup_gpio, ESP_EXT1_WAKEUP_ANY_HIGH)); + + /* To simplify test environment, we'll use a ULP program to set GPIO high */ + ulp_insn_t ulp_code[] = { + I_DELAY(65535), /* about 8ms, given 8MHz ULP clock */ + I_WR_REG_BIT(RTC_CNTL_HOLD_FORCE_REG, RTC_CNTL_PDAC1_HOLD_FORCE_S, 0), + I_WR_REG_BIT(RTC_GPIO_OUT_REG, ext_rtc_io + RTC_GPIO_OUT_DATA_S, 1), + I_DELAY(1000), + I_WR_REG_BIT(RTC_GPIO_OUT_REG, ext_rtc_io + RTC_GPIO_OUT_DATA_S, 0), + I_WR_REG_BIT(RTC_CNTL_HOLD_FORCE_REG, RTC_CNTL_PDAC1_HOLD_FORCE_S, 1), + I_END(), + I_HALT() + }; + TEST_ESP_OK(ulp_set_wakeup_period(0, 1000 /* us */)); + size_t size = sizeof(ulp_code)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, ulp_code, &size)); + + light_sleep_enable(); + + for (int i = 0; i < 10; ++i) { + /* Set GPIO low */ + REG_CLR_BIT(rtc_gpio_desc[ext1_wakeup_gpio].reg, rtc_gpio_desc[ext1_wakeup_gpio].hold_force); + rtc_gpio_set_level(ext1_wakeup_gpio, 0); + REG_SET_BIT(rtc_gpio_desc[ext1_wakeup_gpio].reg, rtc_gpio_desc[ext1_wakeup_gpio].hold_force); + + /* Wait for the next tick */ + vTaskDelay(1); + + /* Start ULP program */ + ulp_run(0); + + const int delay_ms = 200; + const int delay_ticks = delay_ms / portTICK_PERIOD_MS; + + int64_t start_rtc = esp_clk_rtc_time(); + int64_t start_hs = esp_timer_get_time(); + uint32_t start_tick = xTaskGetTickCount(); + /* Will enter sleep here */ + vTaskDelay(delay_ticks); + int64_t end_rtc = esp_clk_rtc_time(); + int64_t end_hs = esp_timer_get_time(); + uint32_t end_tick = xTaskGetTickCount(); + + printf("%lld %lld %u\n", end_rtc - start_rtc, end_hs - start_hs, end_tick - start_tick); + + TEST_ASSERT_INT32_WITHIN(3, delay_ticks, end_tick - start_tick); + TEST_ASSERT_INT32_WITHIN(2 * portTICK_PERIOD_MS * 1000, delay_ms * 1000, end_hs - start_hs); + TEST_ASSERT_INT32_WITHIN(2 * portTICK_PERIOD_MS * 1000, delay_ms * 1000, end_rtc - start_rtc); + } + REG_CLR_BIT(rtc_gpio_desc[ext1_wakeup_gpio].reg, rtc_gpio_desc[ext1_wakeup_gpio].hold_force); + rtc_gpio_deinit(ext1_wakeup_gpio); + + light_sleep_disable(); +} + + +typedef struct { + int delay_us; + int result; + SemaphoreHandle_t done; +} delay_test_arg_t; + +static void test_delay_task(void* p) +{ + delay_test_arg_t* arg = (delay_test_arg_t*) p; + vTaskDelay(1); + + uint64_t start = esp_clk_rtc_time(); + vTaskDelay(arg->delay_us / portTICK_PERIOD_MS / 1000); + uint64_t stop = esp_clk_rtc_time(); + + arg->result = (int) (stop - start); + xSemaphoreGive(arg->done); + vTaskDelete(NULL); +} + +TEST_CASE("vTaskDelay duration is correct with light sleep enabled", "[pm]") +{ + light_sleep_enable(); + + delay_test_arg_t args = { + .done = xSemaphoreCreateBinary() + }; + + const int delays[] = { 10, 20, 50, 100, 150, 200, 250 }; + const int delays_count = sizeof(delays) / sizeof(delays[0]); + + for (int i = 0; i < delays_count; ++i) { + int delay_ms = delays[i]; + args.delay_us = delay_ms * 1000; + + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 0); + TEST_ASSERT( xSemaphoreTake(args.done, delay_ms * 10 / portTICK_PERIOD_MS) ); + printf("CPU0: %d %d\n", args.delay_us, args.result); + TEST_ASSERT_INT32_WITHIN(1000 * portTICK_PERIOD_MS * 2, args.delay_us, args.result); + +#if portNUM_PROCESSORS == 2 + xTaskCreatePinnedToCore(test_delay_task, "", 2048, (void*) &args, 3, NULL, 1); + TEST_ASSERT( xSemaphoreTake(args.done, delay_ms * 10 / portTICK_PERIOD_MS) ); + printf("CPU1: %d %d\n", args.delay_us, args.result); + TEST_ASSERT_INT32_WITHIN(1000 * portTICK_PERIOD_MS * 2, args.delay_us, args.result); +#endif + } + vSemaphoreDelete(args.done); + + light_sleep_disable(); +} + +/* This test is similar to the one in test_esp_timer.c, but since we can't use + * ref_clock, this test uses RTC clock for timing. Also enables automatic + * light sleep. + */ +TEST_CASE("esp_timer produces correct delays with light sleep", "[pm]") +{ + // no, we can't make this a const size_t (§6.7.5.2) +#define NUM_INTERVALS 16 + + typedef struct { + esp_timer_handle_t timer; + size_t cur_interval; + int intervals[NUM_INTERVALS]; + int64_t t_start; + SemaphoreHandle_t done; + } test_args_t; + + void timer_func(void* arg) + { + test_args_t* p_args = (test_args_t*) arg; + int64_t t_end = esp_clk_rtc_time(); + int32_t ms_diff = (t_end - p_args->t_start) / 1000; + printf("timer #%d %dms\n", p_args->cur_interval, ms_diff); + p_args->intervals[p_args->cur_interval++] = ms_diff; + // Deliberately make timer handler run longer. + // We check that this doesn't affect the result. + ets_delay_us(10*1000); + if (p_args->cur_interval == NUM_INTERVALS) { + printf("done\n"); + TEST_ESP_OK(esp_timer_stop(p_args->timer)); + xSemaphoreGive(p_args->done); + } + } + + light_sleep_enable(); + + const int delay_ms = 100; + test_args_t args = {0}; + esp_timer_handle_t timer1; + esp_timer_create_args_t create_args = { + .callback = &timer_func, + .arg = &args, + .name = "timer1", + }; + TEST_ESP_OK(esp_timer_create(&create_args, &timer1)); + + args.timer = timer1; + args.t_start = esp_clk_rtc_time(); + args.done = xSemaphoreCreateBinary(); + TEST_ESP_OK(esp_timer_start_periodic(timer1, delay_ms * 1000)); + + TEST_ASSERT(xSemaphoreTake(args.done, delay_ms * NUM_INTERVALS * 2)); + + TEST_ASSERT_EQUAL_UINT32(NUM_INTERVALS, args.cur_interval); + for (size_t i = 0; i < NUM_INTERVALS; ++i) { + TEST_ASSERT_INT32_WITHIN(portTICK_PERIOD_MS, (i + 1) * delay_ms, args.intervals[i]); + } + + TEST_ESP_OK( esp_timer_dump(stdout) ); + + TEST_ESP_OK( esp_timer_delete(timer1) ); + vSemaphoreDelete(args.done); + + light_sleep_disable(); + +#undef NUM_INTERVALS +} + +#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE + #endif // CONFIG_PM_ENABLE diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index c469d679d9..1a037377c8 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -357,6 +357,31 @@ config FREERTOS_RUN_TIME_STATS_USING_CPU_CLK endchoice +config FREERTOS_USE_TICKLESS_IDLE + bool "Tickless idle support" + depends on PM_ENABLE + default n + help + If power management support is enabled, FreeRTOS will be able to put + the system into light sleep mode when no tasks need to run for a number + of ticks. This number can be set using FREERTOS_IDLE_TIME_BEFORE_SLEEP option. + This feature is also known as "automatic light sleep". + + Note that timers created using esp_timer APIs may prevent the system from + entering sleep mode, even when no tasks need to run. + + If disabled, automatic light sleep support will be disabled. + +config FREERTOS_IDLE_TIME_BEFORE_SLEEP + int "Minimum number of ticks to enter sleep mode for" + depends on FREERTOS_USE_TICKLESS_IDLE + default 3 + range 2 4294967295 + # Minimal value is 2 because of a check in FreeRTOS.h (search configEXPECTED_IDLE_TIME_BEFORE_SLEEP) + help + FreeRTOS will enter light sleep mode if no tasks need to run for this number + of ticks. + menuconfig FREERTOS_DEBUG_INTERNALS bool "Debug FreeRTOS internals" default n diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 37912a66ce..c52bc927cf 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -289,6 +289,10 @@ extern void vPortCleanUpTCB ( void *pxTCB ); #define INCLUDE_eTaskGetState 1 #define configUSE_QUEUE_SETS 1 +#define configUSE_TICKLESS_IDLE CONFIG_FREERTOS_USE_TICKLESS_IDLE +#if configUSE_TICKLESS_IDLE +#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP +#endif //configUSE_TICKLESS_IDLE #define configXT_BOARD 1 /* Board mode */ #define configXT_SIMULATOR 0 diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index 93d2071ba5..df5c071d17 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -365,9 +365,13 @@ typedef struct { #define PRIVILEGED_DATA #endif +extern void esp_vApplicationIdleHook( void ); +extern void esp_vApplicationWaitiHook( void ); void _xt_coproc_release(volatile void * coproc_sa_base); +bool vApplicationSleep( TickType_t xExpectedIdleTime ); +#define portSUPPRESS_TICKS_AND_SLEEP( idleTime ) vApplicationSleep( idleTime ) // porttrace #if configUSE_TRACE_FACILITY_2 diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 980699ffce..fe081975dd 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -2151,6 +2151,23 @@ void vTaskSuspendAll( void ) #if ( configUSE_TICKLESS_IDLE != 0 ) + static BaseType_t xHaveReadyTasks() + { + for (int i = tskIDLE_PRIORITY + 1; i < configMAX_PRIORITIES; ++i) + { + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ i ] ) ) > 0 ) + { + return pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + return pdFALSE; + } + + static TickType_t prvGetExpectedIdleTime( void ) { TickType_t xReturn; @@ -2161,7 +2178,18 @@ void vTaskSuspendAll( void ) { xReturn = 0; } - else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 ) +#if portNUM_PROCESSORS > 1 + /* This function is called from Idle task; in single core case this + * means that no higher priority tasks are ready to run, and we can + * enter sleep. In SMP case, there might be ready tasks waiting for + * the other CPU, so need to check all ready lists. + */ + else if( xHaveReadyTasks() ) + { + xReturn = 0; + } +#endif // portNUM_PROCESSORS > 1 + else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > portNUM_PROCESSORS ) { /* There are other idle priority tasks in the ready state. If time slicing is used then the very next tick interrupt must be @@ -3405,7 +3433,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) #endif /* configUSE_IDLE_HOOK */ { /* Call the esp-idf hook system */ - extern void esp_vApplicationIdleHook( void ); esp_vApplicationIdleHook(); } @@ -3417,6 +3444,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) #if ( configUSE_TICKLESS_IDLE != 0 ) { TickType_t xExpectedIdleTime; + BaseType_t xEnteredSleep = pdFALSE; /* It is not desirable to suspend then resume the scheduler on each iteration of the idle task. Therefore, a preliminary @@ -3427,7 +3455,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { -// vTaskSuspendAll(); taskENTER_CRITICAL(&xTaskQueueMutex); { /* Now the scheduler is suspended, the expected idle @@ -3439,7 +3466,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) { traceLOW_POWER_IDLE_BEGIN(); - portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); + xEnteredSleep = portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); traceLOW_POWER_IDLE_END(); } else @@ -3448,13 +3475,21 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) } } taskEXIT_CRITICAL(&xTaskQueueMutex); -// ( void ) xTaskResumeAll(); } else { mtCOVERAGE_TEST_MARKER(); } + /* It might be possible to enter tickless idle again, so skip + * the fallback sleep hook if tickless idle was successful + */ + if ( !xEnteredSleep ) + { + esp_vApplicationWaitiHook(); + } } + #else + esp_vApplicationWaitiHook(); #endif /* configUSE_TICKLESS_IDLE */ } }