17
17
#include <sys/param.h>
18
18
#include "esp_attr.h"
19
19
#include "esp_sleep.h"
20
+ #include "esp_timer_impl.h"
20
21
#include "esp_log.h"
21
22
#include "esp_clk.h"
22
23
#include "esp_newlib.h"
42
43
// Time from VDD_SDIO power up to first flash read in ROM code
43
44
#define VDD_SDIO_POWERUP_TO_FLASH_READ_US 700
44
45
46
+ // Extra time it takes to enter and exit light sleep and deep sleep
47
+ // For deep sleep, this is until the wake stub runs (not the app).
48
+ #ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
49
+ #define LIGHT_SLEEP_TIME_OVERHEAD_US (650 + 30 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
50
+ #define DEEP_SLEEP_TIME_OVERHEAD_US (650 + 100 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
51
+ #else
52
+ #define LIGHT_SLEEP_TIME_OVERHEAD_US (250 + 30 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
53
+ #define DEEP_SLEEP_TIME_OVERHEAD_US (250 + 100 * 240 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
54
+ #endif // CONFIG_ESP32_RTC_CLOCK_SOURCE
55
+
56
+ // Minimal amount of time we can sleep for
57
+ #define LIGHT_SLEEP_MIN_TIME_US 200
58
+
45
59
#define CHECK_SOURCE (source , value , mask ) ((s_config.wakeup_triggers & mask) && \
46
60
(source == value))
47
61
@@ -56,9 +70,11 @@ typedef struct {
56
70
uint32_t ext1_rtc_gpio_mask : 18 ;
57
71
uint32_t ext0_trigger_level : 1 ;
58
72
uint32_t ext0_rtc_gpio_num : 5 ;
59
- } deep_sleep_config_t ;
73
+ uint32_t sleep_time_adjustment ;
74
+ uint64_t rtc_ticks_at_sleep_start ;
75
+ } sleep_config_t ;
60
76
61
- static deep_sleep_config_t s_config = {
77
+ static sleep_config_t s_config = {
62
78
.pd_options = { ESP_PD_OPTION_AUTO , ESP_PD_OPTION_AUTO , ESP_PD_OPTION_AUTO },
63
79
.wakeup_triggers = 0
64
80
};
@@ -125,12 +141,31 @@ void esp_deep_sleep(uint64_t time_in_us)
125
141
esp_deep_sleep_start ();
126
142
}
127
143
144
+ static void IRAM_ATTR suspend_uarts ()
145
+ {
146
+ for (int i = 0 ; i < 3 ; ++ i ) {
147
+ REG_SET_BIT (UART_FLOW_CONF_REG (i ), UART_FORCE_XOFF );
148
+ uart_tx_wait_idle (i );
149
+ }
150
+ }
151
+
152
+ static void IRAM_ATTR resume_uarts ()
153
+ {
154
+ for (int i = 0 ; i < 3 ; ++ i ) {
155
+ REG_CLR_BIT (UART_FLOW_CONF_REG (i ), UART_FORCE_XOFF );
156
+ REG_SET_BIT (UART_FLOW_CONF_REG (i ), UART_FORCE_XON );
157
+ REG_CLR_BIT (UART_FLOW_CONF_REG (i ), UART_FORCE_XON );
158
+ }
159
+ }
160
+
128
161
static uint32_t IRAM_ATTR esp_sleep_start (uint32_t pd_flags )
129
162
{
130
- // Flush UARTs so that output is not lost due to APB frequency change
131
- uart_tx_wait_idle (0 );
132
- uart_tx_wait_idle (1 );
133
- uart_tx_wait_idle (2 );
163
+ // Stop UART output so that output is not lost due to APB frequency change
164
+ suspend_uarts ();
165
+
166
+ // Save current frequency and switch to XTAL
167
+ rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get ();
168
+ rtc_clk_cpu_freq_set (RTC_CPU_FREQ_XTAL );
134
169
135
170
// Configure pins for external wakeup
136
171
if (s_config .wakeup_triggers & RTC_EXT0_TRIG_EN ) {
@@ -143,20 +178,32 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
143
178
if (s_config .wakeup_triggers & RTC_ULP_TRIG_EN ) {
144
179
SET_PERI_REG_MASK (RTC_CNTL_STATE0_REG , RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN );
145
180
}
181
+
182
+ // Enter sleep
183
+ rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT (pd_flags );
184
+ rtc_sleep_init (config );
185
+
146
186
// Configure timer wakeup
147
187
if ((s_config .wakeup_triggers & RTC_TIMER_TRIG_EN ) &&
148
188
s_config .sleep_duration > 0 ) {
149
189
timer_wakeup_prepare ();
150
190
}
191
+ uint32_t result = rtc_sleep_start (s_config .wakeup_triggers , 0 );
151
192
152
- // Enter sleep
153
- rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT (pd_flags );
154
- rtc_sleep_init (config );
155
- return rtc_sleep_start (s_config .wakeup_triggers , 0 );
193
+ // Restore CPU frequency
194
+ rtc_clk_cpu_freq_set (cpu_freq );
195
+
196
+ // re-enable UART output
197
+ resume_uarts ();
198
+
199
+ return result ;
156
200
}
157
201
158
202
void IRAM_ATTR esp_deep_sleep_start ()
159
203
{
204
+ // record current RTC time
205
+ s_config .rtc_ticks_at_sleep_start = rtc_time_get ();
206
+
160
207
// Configure wake stub
161
208
if (esp_get_deep_sleep_wake_stub () == NULL ) {
162
209
esp_set_deep_sleep_wake_stub (esp_wake_deep_sleep );
@@ -165,8 +212,11 @@ void IRAM_ATTR esp_deep_sleep_start()
165
212
// Decide which power domains can be powered down
166
213
uint32_t pd_flags = get_power_down_flags ();
167
214
215
+ // Correct the sleep time
216
+ s_config .sleep_time_adjustment = DEEP_SLEEP_TIME_OVERHEAD_US ;
217
+
168
218
// Enter sleep
169
- esp_sleep_start (RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | pd_flags );
219
+ esp_sleep_start (RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | RTC_SLEEP_PD_XTAL | pd_flags );
170
220
171
221
// Because RTC is in a slower clock domain than the CPU, it
172
222
// can take several CPU cycles for the sleep mode to start.
@@ -201,11 +251,11 @@ static void rtc_wdt_disable()
201
251
* Placed into IRAM as flash may need some time to be powered on.
202
252
*/
203
253
static esp_err_t esp_light_sleep_inner (uint32_t pd_flags ,
204
- rtc_cpu_freq_t cpu_freq , uint32_t flash_enable_time_us ,
254
+ uint32_t flash_enable_time_us ,
205
255
rtc_vddsdio_config_t vddsdio_config ) IRAM_ATTR __attribute__((noinline ));
206
256
207
257
static esp_err_t esp_light_sleep_inner (uint32_t pd_flags ,
208
- rtc_cpu_freq_t cpu_freq , uint32_t flash_enable_time_us ,
258
+ uint32_t flash_enable_time_us ,
209
259
rtc_vddsdio_config_t vddsdio_config )
210
260
{
211
261
// Enter sleep
@@ -217,9 +267,6 @@ static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
217
267
rtc_vddsdio_set_config (vddsdio_config );
218
268
}
219
269
220
- // Restore CPU frequency
221
- rtc_clk_cpu_freq_set (cpu_freq );
222
-
223
270
// If SPI flash was powered down, wait for it to become ready
224
271
if (pd_flags & RTC_SLEEP_PD_VDDSDIO ) {
225
272
// Wait for the flash chip to start up
@@ -231,53 +278,61 @@ static esp_err_t esp_light_sleep_inner(uint32_t pd_flags,
231
278
esp_err_t esp_light_sleep_start ()
232
279
{
233
280
static portMUX_TYPE light_sleep_lock = portMUX_INITIALIZER_UNLOCKED ;
234
-
235
281
portENTER_CRITICAL (& light_sleep_lock );
236
- int other_cpu = xPortGetCoreID () ? 0 : 1 ;
237
- esp_cpu_stall (other_cpu );
238
-
239
- // Other CPU is stalled, need to disable DPORT protection
240
- esp_dport_access_int_pause ();
282
+ s_config .rtc_ticks_at_sleep_start = rtc_time_get ();
283
+ uint64_t frc_time_at_start = esp_timer_get_time ();
284
+ DPORT_STALL_OTHER_CPU_START ();
241
285
242
286
// Decide which power domains can be powered down
243
287
uint32_t pd_flags = get_power_down_flags ();
244
288
289
+ // Amount of time to subtract from actual sleep time.
290
+ // This is spent on entering and leaving light sleep.
291
+ s_config .sleep_time_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US ;
292
+
245
293
// Decide if VDD_SDIO needs to be powered down;
246
294
// If it needs to be powered down, adjust sleep time.
247
295
const uint32_t flash_enable_time_us = VDD_SDIO_POWERUP_TO_FLASH_READ_US
248
296
+ CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY ;
249
297
250
- // Don't power down VDD_SDIO if pSRAM is used.
251
298
#ifndef CONFIG_SPIRAM_SUPPORT
252
- if (s_config .sleep_duration > FLASH_PD_MIN_SLEEP_TIME_US &&
253
- s_config .sleep_duration > flash_enable_time_us ) {
299
+ const uint32_t vddsdio_pd_sleep_duration = MAX (FLASH_PD_MIN_SLEEP_TIME_US ,
300
+ flash_enable_time_us + LIGHT_SLEEP_TIME_OVERHEAD_US + LIGHT_SLEEP_MIN_TIME_US );
301
+
302
+ if (s_config .sleep_duration > vddsdio_pd_sleep_duration ) {
254
303
pd_flags |= RTC_SLEEP_PD_VDDSDIO ;
255
- s_config .sleep_duration - = flash_enable_time_us ;
304
+ s_config .sleep_time_adjustment + = flash_enable_time_us ;
256
305
}
257
306
#endif //CONFIG_SPIRAM_SUPPORT
307
+
258
308
rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config ();
259
309
260
310
// Safety net: enable WDT in case exit from light sleep fails
261
311
rtc_wdt_enable (1000 );
262
312
263
- // Save current CPU frequency, light sleep will switch to XTAL
264
- rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get ();
265
-
266
313
// Enter sleep, then wait for flash to be ready on wakeup
267
- esp_err_t err = esp_light_sleep_inner (pd_flags , cpu_freq ,
314
+ esp_err_t err = esp_light_sleep_inner (pd_flags ,
268
315
flash_enable_time_us , vddsdio_config );
269
316
270
- // At this point, if FRC1 is used for timekeeping, time will be lagging behind .
271
- // This will update the microsecond count based on RTC timer.
272
- esp_set_time_from_rtc ();
317
+ // FRC1 has been clock gated for the duration of the sleep, correct for that .
318
+ uint64_t rtc_ticks_at_end = rtc_time_get ();
319
+ uint64_t frc_time_at_end = esp_timer_get_time ();
273
320
274
- // However, we do not advance RTOS ticks here; doing so would be rather messy,
275
- // as ticks can only be advanced on CPU0.
276
- // If this is needed by the application, automatic light sleep (tickless idle)
277
- // will handle that better.
321
+ uint64_t rtc_time_diff = rtc_time_slowclk_to_us (rtc_ticks_at_end - s_config .rtc_ticks_at_sleep_start ,
322
+ esp_clk_slowclk_cal_get ());
323
+ uint64_t frc_time_diff = frc_time_at_end - frc_time_at_start ;
324
+
325
+ int64_t time_diff = rtc_time_diff - frc_time_diff ;
326
+ /* Small negative values (up to 1 RTC_SLOW clock period) are possible,
327
+ * for very small values of sleep_duration. Ignore those to keep esp_timer
328
+ * monotonic.
329
+ */
330
+ if (time_diff > 0 ) {
331
+ esp_timer_impl_advance (time_diff );
332
+ }
333
+ esp_set_time_from_rtc ();
278
334
279
- esp_cpu_unstall (other_cpu );
280
- esp_dport_access_int_resume ();
335
+ DPORT_STALL_OTHER_CPU_END ();
281
336
rtc_wdt_disable ();
282
337
portEXIT_CRITICAL (& light_sleep_lock );
283
338
return err ;
@@ -343,9 +398,13 @@ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us)
343
398
static void timer_wakeup_prepare ()
344
399
{
345
400
uint32_t period = esp_clk_slowclk_cal_get ();
346
- uint64_t rtc_count_delta = rtc_time_us_to_slowclk (s_config .sleep_duration , period );
347
- uint64_t cur_rtc_count = rtc_time_get ();
348
- rtc_sleep_set_wakeup_time (cur_rtc_count + rtc_count_delta );
401
+ int64_t sleep_duration = (int64_t ) s_config .sleep_duration - (int64_t ) s_config .sleep_time_adjustment ;
402
+ if (sleep_duration < 0 ) {
403
+ sleep_duration = 0 ;
404
+ }
405
+ int64_t rtc_count_delta = rtc_time_us_to_slowclk (sleep_duration , period );
406
+
407
+ rtc_sleep_set_wakeup_time (s_config .rtc_ticks_at_sleep_start + rtc_count_delta );
349
408
}
350
409
351
410
esp_err_t esp_sleep_enable_touchpad_wakeup ()
@@ -561,6 +620,10 @@ static uint32_t get_power_down_flags()
561
620
}
562
621
}
563
622
623
+ if (s_config .pd_options [ESP_PD_DOMAIN_XTAL ] == ESP_PD_OPTION_AUTO ) {
624
+ s_config .pd_options [ESP_PD_DOMAIN_XTAL ] = ESP_PD_OPTION_OFF ;
625
+ }
626
+
564
627
const char * option_str [] = {"OFF" , "ON" , "AUTO(OFF)" /* Auto works as OFF */ };
565
628
ESP_LOGD (TAG , "RTC_PERIPH: %s, RTC_SLOW_MEM: %s, RTC_FAST_MEM: %s" ,
566
629
option_str [s_config .pd_options [ESP_PD_DOMAIN_RTC_PERIPH ]],
@@ -578,5 +641,8 @@ static uint32_t get_power_down_flags()
578
641
if (s_config .pd_options [ESP_PD_DOMAIN_RTC_PERIPH ] != ESP_PD_OPTION_ON ) {
579
642
pd_flags |= RTC_SLEEP_PD_RTC_PERIPH ;
580
643
}
644
+ if (s_config .pd_options [ESP_PD_DOMAIN_XTAL ] != ESP_PD_OPTION_ON ) {
645
+ pd_flags |= RTC_SLEEP_PD_XTAL ;
646
+ }
581
647
return pd_flags ;
582
648
}
0 commit comments