esp_timer.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /*
  2. * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <sys/param.h>
  7. #include <string.h>
  8. #include "soc/soc.h"
  9. #include "esp_types.h"
  10. #include "esp_attr.h"
  11. #include "esp_err.h"
  12. #include "esp_task.h"
  13. #include "esp_log.h"
  14. #include "freertos/FreeRTOS.h"
  15. #include "freertos/task.h"
  16. #include "freertos/semphr.h"
  17. #include "soc/spinlock.h"
  18. #include "esp_timer.h"
  19. #include "esp_timer_impl.h"
  20. #include "esp_private/startup_internal.h"
  21. #include "esp_private/esp_timer_private.h"
  22. #include "esp_private/system_internal.h"
  23. #if CONFIG_IDF_TARGET_ESP32
  24. #include "esp32/rtc.h"
  25. #elif CONFIG_IDF_TARGET_ESP32S2
  26. #include "esp32s2/rtc.h"
  27. #elif CONFIG_IDF_TARGET_ESP32S3
  28. #include "esp32s3/rtc.h"
  29. #elif CONFIG_IDF_TARGET_ESP32C3
  30. #include "esp32c3/rtc.h"
  31. #endif
  32. #include "sdkconfig.h"
  33. #ifdef CONFIG_ESP_TIMER_PROFILING
  34. #define WITH_PROFILING 1
  35. #endif
  36. #ifndef NDEBUG
  37. // Enable built-in checks in queue.h in debug builds
  38. #define INVARIANTS
  39. #endif
  40. #include "sys/queue.h"
  41. #define EVENT_ID_DELETE_TIMER 0xF0DE1E1E
  42. typedef enum {
  43. FL_DISPATCH_METHOD = (1 << 0), //!< 0=Callback is called from timer task, 1=Callback is called from timer ISR
  44. FL_SKIP_UNHANDLED_EVENTS = (1 << 1), //!< 0=NOT skip unhandled events for periodic timers, 1=Skip unhandled events for periodic timers
  45. } flags_t;
  46. struct esp_timer {
  47. uint64_t alarm;
  48. uint64_t period:56;
  49. flags_t flags:8;
  50. union {
  51. esp_timer_cb_t callback;
  52. uint32_t event_id;
  53. };
  54. void* arg;
  55. #if WITH_PROFILING
  56. const char* name;
  57. size_t times_triggered;
  58. size_t times_armed;
  59. size_t times_skipped;
  60. uint64_t total_callback_run_time;
  61. #endif // WITH_PROFILING
  62. LIST_ENTRY(esp_timer) list_entry;
  63. };
  64. static inline bool is_initialized(void);
  65. static esp_err_t timer_insert(esp_timer_handle_t timer);
  66. static esp_err_t timer_remove(esp_timer_handle_t timer);
  67. static bool timer_armed(esp_timer_handle_t timer);
  68. static void timer_list_lock(void);
  69. static void timer_list_unlock(void);
  70. #if WITH_PROFILING
  71. static void timer_insert_inactive(esp_timer_handle_t timer);
  72. static void timer_remove_inactive(esp_timer_handle_t timer);
  73. #endif // WITH_PROFILING
  74. __attribute__((unused)) static const char* TAG = "esp_timer";
  75. // list of currently armed timers
  76. static LIST_HEAD(esp_timer_list, esp_timer) s_timers =
  77. LIST_HEAD_INITIALIZER(s_timers);
  78. #if WITH_PROFILING
  79. // list of unarmed timers, used only to be able to dump statistics about
  80. // all the timers
  81. static LIST_HEAD(esp_inactive_timer_list, esp_timer) s_inactive_timers =
  82. LIST_HEAD_INITIALIZER(s_timers);
  83. #endif
  84. // task used to dispatch timer callbacks
  85. static TaskHandle_t s_timer_task;
  86. // lock protecting s_timers, s_inactive_timers
  87. static portMUX_TYPE s_timer_lock = portMUX_INITIALIZER_UNLOCKED;
  88. esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
  89. esp_timer_handle_t* out_handle)
  90. {
  91. if (!is_initialized()) {
  92. return ESP_ERR_INVALID_STATE;
  93. }
  94. if (args == NULL || args->callback == NULL || out_handle == NULL) {
  95. return ESP_ERR_INVALID_ARG;
  96. }
  97. esp_timer_handle_t result = (esp_timer_handle_t) calloc(1, sizeof(*result));
  98. if (result == NULL) {
  99. return ESP_ERR_NO_MEM;
  100. }
  101. result->callback = args->callback;
  102. result->arg = args->arg;
  103. result->flags = (args->dispatch_method ? FL_DISPATCH_METHOD : 0) |
  104. (args->skip_unhandled_events ? FL_SKIP_UNHANDLED_EVENTS : 0);
  105. #if WITH_PROFILING
  106. result->name = args->name;
  107. timer_insert_inactive(result);
  108. #endif
  109. *out_handle = result;
  110. return ESP_OK;
  111. }
  112. esp_err_t IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
  113. {
  114. if (timer == NULL) {
  115. return ESP_ERR_INVALID_ARG;
  116. }
  117. if (!is_initialized() || timer_armed(timer)) {
  118. return ESP_ERR_INVALID_STATE;
  119. }
  120. timer_list_lock();
  121. timer->alarm = esp_timer_get_time() + timeout_us;
  122. timer->period = 0;
  123. #if WITH_PROFILING
  124. timer->times_armed++;
  125. #endif
  126. esp_err_t err = timer_insert(timer);
  127. timer_list_unlock();
  128. return err;
  129. }
  130. esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
  131. {
  132. if (timer == NULL) {
  133. return ESP_ERR_INVALID_ARG;
  134. }
  135. if (!is_initialized() || timer_armed(timer)) {
  136. return ESP_ERR_INVALID_STATE;
  137. }
  138. timer_list_lock();
  139. period_us = MAX(period_us, esp_timer_impl_get_min_period_us());
  140. timer->alarm = esp_timer_get_time() + period_us;
  141. timer->period = period_us;
  142. #if WITH_PROFILING
  143. timer->times_armed++;
  144. timer->times_skipped = 0;
  145. #endif
  146. esp_err_t err = timer_insert(timer);
  147. timer_list_unlock();
  148. return err;
  149. }
  150. esp_err_t IRAM_ATTR esp_timer_stop(esp_timer_handle_t timer)
  151. {
  152. if (timer == NULL) {
  153. return ESP_ERR_INVALID_ARG;
  154. }
  155. if (!is_initialized() || !timer_armed(timer)) {
  156. return ESP_ERR_INVALID_STATE;
  157. }
  158. return timer_remove(timer);
  159. }
  160. esp_err_t esp_timer_delete(esp_timer_handle_t timer)
  161. {
  162. if (timer == NULL) {
  163. return ESP_ERR_INVALID_ARG;
  164. }
  165. if (timer_armed(timer)) {
  166. return ESP_ERR_INVALID_STATE;
  167. }
  168. timer_list_lock();
  169. timer->event_id = EVENT_ID_DELETE_TIMER;
  170. timer->alarm = esp_timer_get_time();
  171. timer->period = 0;
  172. timer_insert(timer);
  173. timer_list_unlock();
  174. return ESP_OK;
  175. }
  176. static IRAM_ATTR esp_err_t timer_insert(esp_timer_handle_t timer)
  177. {
  178. #if WITH_PROFILING
  179. timer_remove_inactive(timer);
  180. #endif
  181. esp_timer_handle_t it, last = NULL;
  182. if (LIST_FIRST(&s_timers) == NULL) {
  183. LIST_INSERT_HEAD(&s_timers, timer, list_entry);
  184. } else {
  185. LIST_FOREACH(it, &s_timers, list_entry) {
  186. if (timer->alarm < it->alarm) {
  187. LIST_INSERT_BEFORE(it, timer, list_entry);
  188. break;
  189. }
  190. last = it;
  191. }
  192. if (it == NULL) {
  193. assert(last);
  194. LIST_INSERT_AFTER(last, timer, list_entry);
  195. }
  196. }
  197. if (timer == LIST_FIRST(&s_timers)) {
  198. esp_timer_impl_set_alarm(timer->alarm);
  199. }
  200. return ESP_OK;
  201. }
  202. static IRAM_ATTR esp_err_t timer_remove(esp_timer_handle_t timer)
  203. {
  204. timer_list_lock();
  205. LIST_REMOVE(timer, list_entry);
  206. timer->alarm = 0;
  207. timer->period = 0;
  208. #if WITH_PROFILING
  209. timer_insert_inactive(timer);
  210. #endif
  211. timer_list_unlock();
  212. return ESP_OK;
  213. }
  214. #if WITH_PROFILING
  215. static IRAM_ATTR void timer_insert_inactive(esp_timer_handle_t timer)
  216. {
  217. /* May be locked or not, depending on where this is called from.
  218. * Lock recursively.
  219. */
  220. timer_list_lock();
  221. esp_timer_handle_t head = LIST_FIRST(&s_inactive_timers);
  222. if (head == NULL) {
  223. LIST_INSERT_HEAD(&s_inactive_timers, timer, list_entry);
  224. } else {
  225. /* Insert as head element as this is the fastest thing to do.
  226. * Removal is O(1) anyway.
  227. */
  228. LIST_INSERT_BEFORE(head, timer, list_entry);
  229. }
  230. timer_list_unlock();
  231. }
  232. static IRAM_ATTR void timer_remove_inactive(esp_timer_handle_t timer)
  233. {
  234. timer_list_lock();
  235. LIST_REMOVE(timer, list_entry);
  236. timer_list_unlock();
  237. }
  238. #endif // WITH_PROFILING
  239. static IRAM_ATTR bool timer_armed(esp_timer_handle_t timer)
  240. {
  241. return timer->alarm > 0;
  242. }
  243. static IRAM_ATTR void timer_list_lock(void)
  244. {
  245. portENTER_CRITICAL_SAFE(&s_timer_lock);
  246. }
  247. static IRAM_ATTR void timer_list_unlock(void)
  248. {
  249. portEXIT_CRITICAL_SAFE(&s_timer_lock);
  250. }
  251. static void timer_process_alarm(esp_timer_dispatch_t dispatch_method)
  252. {
  253. /* unused, provision to allow running callbacks from ISR */
  254. (void) dispatch_method;
  255. timer_list_lock();
  256. esp_timer_handle_t it;
  257. while (1) {
  258. it = LIST_FIRST(&s_timers);
  259. int64_t now = esp_timer_impl_get_time();
  260. if (it == NULL || it->alarm > now) {
  261. break;
  262. }
  263. LIST_REMOVE(it, list_entry);
  264. if (it->event_id == EVENT_ID_DELETE_TIMER) {
  265. free(it);
  266. it = NULL;
  267. } else {
  268. if (it->period > 0) {
  269. int skipped = (now - it->alarm) / it->period;
  270. if ((it->flags & FL_SKIP_UNHANDLED_EVENTS) && (skipped > 1)) {
  271. it->alarm = now + it->period;
  272. #if WITH_PROFILING
  273. it->times_skipped += skipped;
  274. #endif
  275. } else {
  276. it->alarm += it->period;
  277. }
  278. timer_insert(it);
  279. } else {
  280. it->alarm = 0;
  281. #if WITH_PROFILING
  282. timer_insert_inactive(it);
  283. #endif
  284. }
  285. #if WITH_PROFILING
  286. uint64_t callback_start = now;
  287. #endif
  288. esp_timer_cb_t callback = it->callback;
  289. void* arg = it->arg;
  290. timer_list_unlock();
  291. (*callback)(arg);
  292. timer_list_lock();
  293. #if WITH_PROFILING
  294. it->times_triggered++;
  295. it->total_callback_run_time += esp_timer_impl_get_time() - callback_start;
  296. #endif
  297. }
  298. }
  299. if (it) {
  300. esp_timer_impl_set_alarm(it->alarm);
  301. }
  302. timer_list_unlock();
  303. }
  304. static void timer_task(void* arg)
  305. {
  306. while (true){
  307. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  308. // all deferred events are processed at a time
  309. timer_process_alarm(ESP_TIMER_TASK);
  310. }
  311. }
  312. static void IRAM_ATTR timer_alarm_handler(void* arg)
  313. {
  314. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  315. vTaskNotifyGiveFromISR(s_timer_task, &xHigherPriorityTaskWoken);
  316. if (xHigherPriorityTaskWoken == pdTRUE) {
  317. portYIELD_FROM_ISR();
  318. }
  319. }
  320. static IRAM_ATTR inline bool is_initialized(void)
  321. {
  322. return s_timer_task != NULL;
  323. }
  324. esp_err_t esp_timer_early_init(void)
  325. {
  326. esp_timer_impl_early_init();
  327. #if CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER
  328. esp_timer_impl_init_system_time();
  329. #endif
  330. return ESP_OK;
  331. }
  332. esp_err_t esp_timer_init(void)
  333. {
  334. esp_err_t err;
  335. if (is_initialized()) {
  336. return ESP_ERR_INVALID_STATE;
  337. }
  338. int ret = xTaskCreatePinnedToCore(&timer_task, "esp_timer",
  339. ESP_TASK_TIMER_STACK, NULL, ESP_TASK_TIMER_PRIO, &s_timer_task, PRO_CPU_NUM);
  340. if (ret != pdPASS) {
  341. err = ESP_ERR_NO_MEM;
  342. goto out;
  343. }
  344. err = esp_timer_impl_init(&timer_alarm_handler);
  345. if (err != ESP_OK) {
  346. goto out;
  347. }
  348. return ESP_OK;
  349. out:
  350. if (s_timer_task) {
  351. vTaskDelete(s_timer_task);
  352. s_timer_task = NULL;
  353. }
  354. return ESP_ERR_NO_MEM;
  355. }
  356. esp_err_t esp_timer_deinit(void)
  357. {
  358. if (!is_initialized()) {
  359. return ESP_ERR_INVALID_STATE;
  360. }
  361. /* Check if there are any active timers */
  362. if (!LIST_EMPTY(&s_timers)) {
  363. return ESP_ERR_INVALID_STATE;
  364. }
  365. /* We can only check if there are any timers which are not deleted if
  366. * profiling is enabled.
  367. */
  368. #if WITH_PROFILING
  369. if (!LIST_EMPTY(&s_inactive_timers)) {
  370. return ESP_ERR_INVALID_STATE;
  371. }
  372. #endif
  373. esp_timer_impl_deinit();
  374. vTaskDelete(s_timer_task);
  375. s_timer_task = NULL;
  376. return ESP_OK;
  377. }
  378. static void print_timer_info(esp_timer_handle_t t, char** dst, size_t* dst_size)
  379. {
  380. #if WITH_PROFILING
  381. size_t cb;
  382. // name is optional, might be missed.
  383. if (t->name) {
  384. cb = snprintf(*dst, *dst_size, "%-12s ", t->name);
  385. } else {
  386. cb = snprintf(*dst, *dst_size, "timer@%p ", t);
  387. }
  388. cb += snprintf(*dst + cb, *dst_size + cb, "%12lld %12lld %9d %9d %6d %12lld\n",
  389. (uint64_t)t->period, t->alarm, t->times_armed,
  390. t->times_triggered, t->times_skipped, t->total_callback_run_time);
  391. /* keep this in sync with the format string, used in esp_timer_dump */
  392. #define TIMER_INFO_LINE_LEN 90
  393. #else
  394. size_t cb = snprintf(*dst, *dst_size, "timer@%p %12lld %12lld\n", t, (uint64_t)t->period, t->alarm);
  395. #define TIMER_INFO_LINE_LEN 46
  396. #endif
  397. *dst += cb;
  398. *dst_size -= cb;
  399. }
  400. esp_err_t esp_timer_dump(FILE* stream)
  401. {
  402. /* Since timer lock is a critical section, we don't want to print directly
  403. * to stdout, since that may cause a deadlock if stdout is interrupt-driven
  404. * (via the UART driver). Allocate sufficiently large chunk of memory first,
  405. * print to it, then dump this memory to stdout.
  406. */
  407. esp_timer_handle_t it;
  408. /* First count the number of timers */
  409. size_t timer_count = 0;
  410. timer_list_lock();
  411. LIST_FOREACH(it, &s_timers, list_entry) {
  412. ++timer_count;
  413. }
  414. #if WITH_PROFILING
  415. LIST_FOREACH(it, &s_inactive_timers, list_entry) {
  416. ++timer_count;
  417. }
  418. #endif
  419. timer_list_unlock();
  420. /* Allocate the memory for this number of timers. Since we have unlocked,
  421. * we may find that there are more timers. There's no bulletproof solution
  422. * for this (can't allocate from a critical section), but we allocate
  423. * slightly more and the output will be truncated if that is not enough.
  424. */
  425. size_t buf_size = TIMER_INFO_LINE_LEN * (timer_count + 3);
  426. char* print_buf = calloc(1, buf_size + 1);
  427. if (print_buf == NULL) {
  428. return ESP_ERR_NO_MEM;
  429. }
  430. /* Print to the buffer */
  431. timer_list_lock();
  432. char* pos = print_buf;
  433. LIST_FOREACH(it, &s_timers, list_entry) {
  434. print_timer_info(it, &pos, &buf_size);
  435. }
  436. #if WITH_PROFILING
  437. LIST_FOREACH(it, &s_inactive_timers, list_entry) {
  438. print_timer_info(it, &pos, &buf_size);
  439. }
  440. #endif
  441. timer_list_unlock();
  442. /* Print the buffer */
  443. fputs(print_buf, stream);
  444. free(print_buf);
  445. return ESP_OK;
  446. }
  447. int64_t IRAM_ATTR esp_timer_get_next_alarm(void)
  448. {
  449. int64_t next_alarm = INT64_MAX;
  450. timer_list_lock();
  451. esp_timer_handle_t it = LIST_FIRST(&s_timers);
  452. if (it) {
  453. next_alarm = it->alarm;
  454. }
  455. timer_list_unlock();
  456. return next_alarm;
  457. }
  458. bool esp_timer_is_active(esp_timer_handle_t timer)
  459. {
  460. return timer_armed(timer);
  461. }