1 // Copyright 2017 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <sys/param.h>
16 #include <string.h>
17 #include "soc/soc.h"
18 #include "esp_types.h"
19 #include "esp_attr.h"
20 #include "esp_err.h"
21 #include "esp_task.h"
22 #include "esp_log.h"
23 #include "esp_osal/esp_osal.h"
24 #include "esp_osal/task.h"
25 #include "esp_osal/semphr.h"
26 #include "soc/spinlock.h"
27 #include "esp_timer.h"
28 #include "esp_timer_impl.h"
29
30 #include "esp_private/startup_internal.h"
31 #include "esp_private/esp_timer_private.h"
32 #include "esp_private/system_internal.h"
33
34 #if CONFIG_IDF_TARGET_ESP32
35 #include "esp32/rtc.h"
36 #elif CONFIG_IDF_TARGET_ESP32S2
37 #include "esp32s2/rtc.h"
38 #elif CONFIG_IDF_TARGET_ESP32S3
39 #include "esp32s3/rtc.h"
40 #elif CONFIG_IDF_TARGET_ESP32C3
41 #include "esp32c3/rtc.h"
42 #endif
43
44 #include "sdkconfig.h"
45
46 #ifdef CONFIG_ESP_TIMER_PROFILING
47 #define WITH_PROFILING 1
48 #endif
49
50 #ifndef NDEBUG
51 // Enable built-in checks in queue.h in debug builds
52 #define INVARIANTS
53 #endif
54 #include "sys/queue.h"
55
56 #define EVENT_ID_DELETE_TIMER 0xF0DE1E1E
57
58 typedef enum {
59 FL_DISPATCH_METHOD = (1 << 0), //!< 0=Callback is called from timer task, 1=Callback is called from timer ISR
60 FL_SKIP_UNHANDLED_EVENTS = (1 << 1), //!< 0=NOT skip unhandled events for periodic timers, 1=Skip unhandled events for periodic timers
61 } flags_t;
62
63 struct esp_timer {
64 uint64_t alarm;
65 uint64_t period:56;
66 flags_t flags:8;
67 union {
68 esp_timer_cb_t callback;
69 uint32_t event_id;
70 };
71 void* arg;
72 #if WITH_PROFILING
73 const char* name;
74 size_t times_triggered;
75 size_t times_armed;
76 size_t times_skipped;
77 uint64_t total_callback_run_time;
78 #endif // WITH_PROFILING
79 LIST_ENTRY(esp_timer) list_entry;
80 };
81
82 static inline bool is_initialized(void);
83 static esp_err_t timer_insert(esp_timer_handle_t timer);
84 static esp_err_t timer_remove(esp_timer_handle_t timer);
85 static bool timer_armed(esp_timer_handle_t timer);
86 static void timer_list_lock(void);
87 static void timer_list_unlock(void);
88
89 #if WITH_PROFILING
90 static void timer_insert_inactive(esp_timer_handle_t timer);
91 static void timer_remove_inactive(esp_timer_handle_t timer);
92 #endif // WITH_PROFILING
93
94 __attribute__((unused)) static const char* TAG = "esp_timer";
95
96 // list of currently armed timers
97 static LIST_HEAD(esp_timer_list, esp_timer) s_timers =
98 LIST_HEAD_INITIALIZER(s_timers);
99 #if WITH_PROFILING
100 // list of unarmed timers, used only to be able to dump statistics about
101 // all the timers
102 static LIST_HEAD(esp_inactive_timer_list, esp_timer) s_inactive_timers =
103 LIST_HEAD_INITIALIZER(s_timers);
104 #endif
105 // task used to dispatch timer callbacks
106 static int s_timer_task = -1;
107 static EVENT_CB_S s_timer_event = {0};
108
109 // lock protecting s_timers, s_inactive_timers
110 static portMUX_TYPE s_timer_lock = portMUX_INITIALIZER_UNLOCKED;
111
112
esp_timer_create(const esp_timer_create_args_t * args,esp_timer_handle_t * out_handle)113 esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
114 esp_timer_handle_t* out_handle)
115 {
116 if (!is_initialized()) {
117 return ESP_ERR_INVALID_STATE;
118 }
119 if (args == NULL || args->callback == NULL || out_handle == NULL) {
120 return ESP_ERR_INVALID_ARG;
121 }
122 esp_timer_handle_t result = (esp_timer_handle_t) calloc(1, sizeof(*result));
123 if (result == NULL) {
124 return ESP_ERR_NO_MEM;
125 }
126 result->callback = args->callback;
127 result->arg = args->arg;
128 result->flags = (args->dispatch_method ? FL_DISPATCH_METHOD : 0) |
129 (args->skip_unhandled_events ? FL_SKIP_UNHANDLED_EVENTS : 0);
130 #if WITH_PROFILING
131 result->name = args->name;
132 timer_insert_inactive(result);
133 #endif
134 *out_handle = result;
135 return ESP_OK;
136 }
137
esp_timer_start_once(esp_timer_handle_t timer,uint64_t timeout_us)138 esp_err_t IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
139 {
140 if (timer == NULL) {
141 return ESP_ERR_INVALID_ARG;
142 }
143 if (!is_initialized() || timer_armed(timer)) {
144 return ESP_ERR_INVALID_STATE;
145 }
146 timer_list_lock();
147 timer->alarm = esp_timer_get_time() + timeout_us;
148 timer->period = 0;
149 #if WITH_PROFILING
150 timer->times_armed++;
151 #endif
152 esp_err_t err = timer_insert(timer);
153 timer_list_unlock();
154 return err;
155 }
156
esp_timer_start_periodic(esp_timer_handle_t timer,uint64_t period_us)157 esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
158 {
159 if (timer == NULL) {
160 return ESP_ERR_INVALID_ARG;
161 }
162 if (!is_initialized() || timer_armed(timer)) {
163 return ESP_ERR_INVALID_STATE;
164 }
165 timer_list_lock();
166 period_us = MAX(period_us, esp_timer_impl_get_min_period_us());
167 timer->alarm = esp_timer_get_time() + period_us;
168 timer->period = period_us;
169 #if WITH_PROFILING
170 timer->times_armed++;
171 timer->times_skipped = 0;
172 #endif
173 esp_err_t err = timer_insert(timer);
174 timer_list_unlock();
175 return err;
176 }
177
esp_timer_stop(esp_timer_handle_t timer)178 esp_err_t IRAM_ATTR esp_timer_stop(esp_timer_handle_t timer)
179 {
180 if (timer == NULL) {
181 return ESP_ERR_INVALID_ARG;
182 }
183 if (!is_initialized() || !timer_armed(timer)) {
184 return ESP_ERR_INVALID_STATE;
185 }
186 return timer_remove(timer);
187 }
188
esp_timer_delete(esp_timer_handle_t timer)189 esp_err_t esp_timer_delete(esp_timer_handle_t timer)
190 {
191 if (timer == NULL) {
192 return ESP_ERR_INVALID_ARG;
193 }
194 if (timer_armed(timer)) {
195 return ESP_ERR_INVALID_STATE;
196 }
197 timer_list_lock();
198 timer->event_id = EVENT_ID_DELETE_TIMER;
199 timer->alarm = esp_timer_get_time();
200 timer->period = 0;
201 timer_insert(timer);
202 timer_list_unlock();
203 return ESP_OK;
204 }
205
timer_insert(esp_timer_handle_t timer)206 static IRAM_ATTR esp_err_t timer_insert(esp_timer_handle_t timer)
207 {
208 #if WITH_PROFILING
209 timer_remove_inactive(timer);
210 #endif
211 esp_timer_handle_t it, last = NULL;
212 if (LIST_FIRST(&s_timers) == NULL) {
213 LIST_INSERT_HEAD(&s_timers, timer, list_entry);
214 } else {
215 LIST_FOREACH(it, &s_timers, list_entry) {
216 if (timer->alarm < it->alarm) {
217 LIST_INSERT_BEFORE(it, timer, list_entry);
218 break;
219 }
220 last = it;
221 }
222 if (it == NULL) {
223 assert(last);
224 LIST_INSERT_AFTER(last, timer, list_entry);
225 }
226 }
227 if (timer == LIST_FIRST(&s_timers)) {
228 esp_timer_impl_set_alarm(timer->alarm);
229 }
230 return ESP_OK;
231 }
232
timer_remove(esp_timer_handle_t timer)233 static IRAM_ATTR esp_err_t timer_remove(esp_timer_handle_t timer)
234 {
235 timer_list_lock();
236 LIST_REMOVE(timer, list_entry);
237 timer->alarm = 0;
238 timer->period = 0;
239 #if WITH_PROFILING
240 timer_insert_inactive(timer);
241 #endif
242 timer_list_unlock();
243 return ESP_OK;
244 }
245
246 #if WITH_PROFILING
247
timer_insert_inactive(esp_timer_handle_t timer)248 static IRAM_ATTR void timer_insert_inactive(esp_timer_handle_t timer)
249 {
250 /* May be locked or not, depending on where this is called from.
251 * Lock recursively.
252 */
253 timer_list_lock();
254 esp_timer_handle_t head = LIST_FIRST(&s_inactive_timers);
255 if (head == NULL) {
256 LIST_INSERT_HEAD(&s_inactive_timers, timer, list_entry);
257 } else {
258 /* Insert as head element as this is the fastest thing to do.
259 * Removal is O(1) anyway.
260 */
261 LIST_INSERT_BEFORE(head, timer, list_entry);
262 }
263 timer_list_unlock();
264 }
265
timer_remove_inactive(esp_timer_handle_t timer)266 static IRAM_ATTR void timer_remove_inactive(esp_timer_handle_t timer)
267 {
268 timer_list_lock();
269 LIST_REMOVE(timer, list_entry);
270 timer_list_unlock();
271 }
272
273 #endif // WITH_PROFILING
274
timer_armed(esp_timer_handle_t timer)275 static IRAM_ATTR bool timer_armed(esp_timer_handle_t timer)
276 {
277 return timer->alarm > 0;
278 }
279
timer_list_lock(void)280 static IRAM_ATTR void timer_list_lock(void)
281 {
282 portENTER_CRITICAL_SAFE(&s_timer_lock);
283 }
284
timer_list_unlock(void)285 static IRAM_ATTR void timer_list_unlock(void)
286 {
287 portEXIT_CRITICAL_SAFE(&s_timer_lock);
288 }
289
timer_process_alarm(esp_timer_dispatch_t dispatch_method)290 static void timer_process_alarm(esp_timer_dispatch_t dispatch_method)
291 {
292 /* unused, provision to allow running callbacks from ISR */
293 (void) dispatch_method;
294
295 timer_list_lock();
296 esp_timer_handle_t it;
297 while (1) {
298 it = LIST_FIRST(&s_timers);
299 int64_t now = esp_timer_impl_get_time();
300 if (it == NULL || it->alarm > now) {
301 break;
302 }
303 LIST_REMOVE(it, list_entry);
304 if (it->event_id == EVENT_ID_DELETE_TIMER) {
305 free(it);
306 it = NULL;
307 } else {
308 if (it->period > 0) {
309 int skipped = (now - it->alarm) / it->period;
310 if ((it->flags & FL_SKIP_UNHANDLED_EVENTS) && (skipped > 1)) {
311 it->alarm = now + it->period;
312 #if WITH_PROFILING
313 it->times_skipped += skipped;
314 #endif
315 } else {
316 it->alarm += it->period;
317 }
318 timer_insert(it);
319 } else {
320 it->alarm = 0;
321 #if WITH_PROFILING
322 timer_insert_inactive(it);
323 #endif
324 }
325 #if WITH_PROFILING
326 uint64_t callback_start = now;
327 #endif
328 esp_timer_cb_t callback = it->callback;
329 void* arg = it->arg;
330 timer_list_unlock();
331 (*callback)(arg);
332 timer_list_lock();
333 #if WITH_PROFILING
334 it->times_triggered++;
335 it->total_callback_run_time += esp_timer_impl_get_time() - callback_start;
336 #endif
337 }
338 }
339 if (it) {
340 esp_timer_impl_set_alarm(it->alarm);
341 }
342 timer_list_unlock();
343 }
344
timer_task(void * arg)345 static void timer_task(void* arg)
346 {
347 s_timer_task = LOS_CurTaskIDGet();
348 while (true){
349 // ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
350 // LOS_TaskSuspend(s_timer_task);
351 LOS_EventRead(&s_timer_event,0x01,LOS_WAITMODE_AND | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER);
352 // all deferred events are processed at a time
353 timer_process_alarm(ESP_TIMER_TASK);
354 }
355 }
356
timer_alarm_handler(void * arg)357 static void IRAM_ATTR timer_alarm_handler(void* arg)
358 {
359 if(-1 != s_timer_task) {
360 // if(s_timer_task != LOS_CurTaskIDGet()) LOS_TaskResume(s_timer_task);
361 LOS_EventWrite(&s_timer_event,0x01);
362 portYIELD_FROM_ISR();
363 }
364 }
365
is_initialized(void)366 static IRAM_ATTR inline bool is_initialized(void)
367 {
368 return s_timer_task != -1;
369 }
370
esp_timer_init(void)371 esp_err_t esp_timer_init(void)
372 {
373 esp_err_t err;
374 if (is_initialized()) {
375 return ESP_ERR_INVALID_STATE;
376 }
377
378 #if 0
379 int ret = xTaskCreatePinnedToCore(&timer_task, "esp_timer",
380 ESP_TASK_TIMER_STACK, NULL, ESP_TASK_TIMER_PRIO, &s_timer_task, PRO_CPU_NUM);
381 if (ret != pdPASS) {
382 err = ESP_ERR_NO_MEM;
383 goto out;
384 }
385 #else
386 {
387 UINT32 ret;
388 TSK_INIT_PARAM_S attr;
389 memset(&attr,0,sizeof(attr));
390 attr.pfnTaskEntry = (TSK_ENTRY_FUNC)timer_task;
391 attr.usTaskPrio = configMAX_PRIORITIES - 1 - ESP_TASK_TIMER_PRIO;
392 attr.uwArg = 0;
393 attr.uwStackSize = ESP_TASK_TIMER_STACK + 32;
394 attr.pcName = "esp_timer";
395 memset(&s_timer_event,0,sizeof(s_timer_event));
396 LOS_EventInit(&s_timer_event);
397 ret = LOS_TaskCreate((UINT32 *)&s_timer_task, &attr);
398 if (ret != LOS_OK) {
399 err = ESP_ERR_NO_MEM;
400 goto out;
401 }
402 }
403 #endif
404
405 err = esp_timer_impl_init(&timer_alarm_handler);
406 if (err != ESP_OK) {
407 goto out;
408 }
409
410 #if CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER
411 // [refactor-todo] this logic, "esp_rtc_get_time_us() - g_startup_time", is also
412 // the weak definition of esp_system_get_time; find a way to remove this duplication.
413 esp_timer_private_advance(esp_rtc_get_time_us() - g_startup_time);
414 #endif
415
416 return ESP_OK;
417
418 out:
419 ESP_LOGE(TAG, "esp_timer_init fail!!!");
420 if (-1 != s_timer_task) {
421 LOS_TaskDelete(s_timer_task);
422 s_timer_task = -1;
423 LOS_EventDestroy(&s_timer_event);
424 }
425
426 return ESP_ERR_NO_MEM;
427 }
428
esp_timer_deinit(void)429 esp_err_t esp_timer_deinit(void)
430 {
431 if (!is_initialized()) {
432 return ESP_ERR_INVALID_STATE;
433 }
434
435 /* Check if there are any active timers */
436 if (!LIST_EMPTY(&s_timers)) {
437 return ESP_ERR_INVALID_STATE;
438 }
439
440 /* We can only check if there are any timers which are not deleted if
441 * profiling is enabled.
442 */
443 #if WITH_PROFILING
444 if (!LIST_EMPTY(&s_inactive_timers)) {
445 return ESP_ERR_INVALID_STATE;
446 }
447 #endif
448
449 esp_timer_impl_deinit();
450
451 if(-1 != s_timer_task) {
452 LOS_TaskDelete(s_timer_task);
453 LOS_EventDestroy(&s_timer_event);
454 }
455 s_timer_task = -1;
456 return ESP_OK;
457 }
458
print_timer_info(esp_timer_handle_t t,char ** dst,size_t * dst_size)459 static void print_timer_info(esp_timer_handle_t t, char** dst, size_t* dst_size)
460 {
461 #if WITH_PROFILING
462 size_t cb;
463 // name is optional, might be missed.
464 if (t->name) {
465 cb = snprintf(*dst, *dst_size, "%-12s ", t->name);
466 } else {
467 cb = snprintf(*dst, *dst_size, "timer@%p ", t);
468 }
469 cb += snprintf(*dst + cb, *dst_size + cb, "%12lld %12lld %9d %9d %6d %12lld\n",
470 (uint64_t)t->period, t->alarm, t->times_armed,
471 t->times_triggered, t->times_skipped, t->total_callback_run_time);
472 /* keep this in sync with the format string, used in esp_timer_dump */
473 #define TIMER_INFO_LINE_LEN 90
474 #else
475 size_t cb = snprintf(*dst, *dst_size, "timer@%p %12lld %12lld\n", t, (uint64_t)t->period, t->alarm);
476 #define TIMER_INFO_LINE_LEN 46
477 #endif
478 *dst += cb;
479 *dst_size -= cb;
480 }
481
482
esp_timer_dump(FILE * stream)483 esp_err_t esp_timer_dump(FILE* stream)
484 {
485 /* Since timer lock is a critical section, we don't want to print directly
486 * to stdout, since that may cause a deadlock if stdout is interrupt-driven
487 * (via the UART driver). Allocate sufficiently large chunk of memory first,
488 * print to it, then dump this memory to stdout.
489 */
490
491 esp_timer_handle_t it;
492
493 /* First count the number of timers */
494 size_t timer_count = 0;
495 timer_list_lock();
496 LIST_FOREACH(it, &s_timers, list_entry) {
497 ++timer_count;
498 }
499 #if WITH_PROFILING
500 LIST_FOREACH(it, &s_inactive_timers, list_entry) {
501 ++timer_count;
502 }
503 #endif
504 timer_list_unlock();
505
506 /* Allocate the memory for this number of timers. Since we have unlocked,
507 * we may find that there are more timers. There's no bulletproof solution
508 * for this (can't allocate from a critical section), but we allocate
509 * slightly more and the output will be truncated if that is not enough.
510 */
511 size_t buf_size = TIMER_INFO_LINE_LEN * (timer_count + 3);
512 char* print_buf = calloc(1, buf_size + 1);
513 if (print_buf == NULL) {
514 return ESP_ERR_NO_MEM;
515 }
516
517 /* Print to the buffer */
518 timer_list_lock();
519 char* pos = print_buf;
520 LIST_FOREACH(it, &s_timers, list_entry) {
521 print_timer_info(it, &pos, &buf_size);
522 }
523 #if WITH_PROFILING
524 LIST_FOREACH(it, &s_inactive_timers, list_entry) {
525 print_timer_info(it, &pos, &buf_size);
526 }
527 #endif
528 timer_list_unlock();
529
530 /* Print the buffer */
531 fputs(print_buf, stream);
532
533 free(print_buf);
534 return ESP_OK;
535 }
536
esp_timer_get_next_alarm(void)537 int64_t IRAM_ATTR esp_timer_get_next_alarm(void)
538 {
539 int64_t next_alarm = INT64_MAX;
540 timer_list_lock();
541 esp_timer_handle_t it = LIST_FIRST(&s_timers);
542 if (it) {
543 next_alarm = it->alarm;
544 }
545 timer_list_unlock();
546 return next_alarm;
547 }
548
549 // Provides strong definition for system time functions relied upon
550 // by core components.
551 #if CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER
esp_system_get_time(void)552 int64_t IRAM_ATTR esp_system_get_time(void)
553 {
554 return esp_timer_get_time();
555 }
556
esp_system_get_time_resolution(void)557 uint32_t IRAM_ATTR esp_system_get_time_resolution(void)
558 {
559 return 1000;
560 }
561 #endif
562
esp_timer_is_active(esp_timer_handle_t timer)563 bool esp_timer_is_active(esp_timer_handle_t timer)
564 {
565 return timer_armed(timer);
566 }
567