• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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