timer.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. * timer.c
  3. *
  4. * Deadline-based timer callbacks.
  5. *
  6. * Written & released by Keir Fraser <keir.xen@gmail.com>
  7. *
  8. * This is free and unencumbered software released into the public domain.
  9. * See the file COPYING for more details, or visit <http://unlicense.org>.
  10. */
  11. /* TIM1_UP: IRQ 25. */
  12. void IRQ_25(void) __attribute__((alias("IRQ_timer")));
  13. #define TIMER_IRQ 25
  14. #define tim tim1
  15. /* IRQ only on counter overflow, one-time enable. */
  16. #define TIM_CR1 (TIM_CR1_URS | TIM_CR1_OPM)
  17. /* Empirically-determined offset applied to timer deadlines to counteract the
  18. * latency incurred by reprogram_timer() and IRQ_timer(). */
  19. #define SLACK_TICKS 12
  20. #define TIMER_INACTIVE ((struct timer *)1ul)
  21. static struct timer *head;
  22. static void reprogram_timer(int32_t delta)
  23. {
  24. tim->cr1 = TIM_CR1;
  25. if (delta < 0x10000) {
  26. /* Fine-grained deadline (sub-microsecond accurate) */
  27. tim->psc = SYSCLK_MHZ/TIME_MHZ-1;
  28. tim->arr = (delta <= SLACK_TICKS) ? 1 : delta-SLACK_TICKS;
  29. } else {
  30. /* Coarse-grained deadline, fires in time to set a shorter,
  31. * fine-grained deadline. */
  32. tim->psc = sysclk_us(100)-1;
  33. tim->arr = min_t(uint32_t, 0xffffu,
  34. delta/time_us(100)-50); /* 5ms early */
  35. }
  36. tim->egr = TIM_EGR_UG; /* update CNT, PSC, ARR */
  37. tim->sr = 0; /* dummy write, gives hardware time to process EGR.UG=1 */
  38. tim->cr1 = TIM_CR1 | TIM_CR1_CEN;
  39. }
  40. void timer_init(struct timer *timer, void (*cb_fn)(void *), void *cb_dat)
  41. {
  42. timer->cb_fn = cb_fn;
  43. timer->cb_dat = cb_dat;
  44. timer->next = TIMER_INACTIVE;
  45. }
  46. static bool_t timer_is_active(struct timer *timer)
  47. {
  48. return timer->next != TIMER_INACTIVE;
  49. }
  50. static void _timer_cancel(struct timer *timer)
  51. {
  52. struct timer *t, **pprev;
  53. if (!timer_is_active(timer))
  54. return;
  55. for (pprev = &head; (t = *pprev) != timer; pprev = &t->next)
  56. continue;
  57. *pprev = t->next;
  58. t->next = TIMER_INACTIVE;
  59. }
  60. void timer_set(struct timer *timer, time_t deadline)
  61. {
  62. struct timer *t, **pprev;
  63. time_t now;
  64. int32_t delta;
  65. uint32_t oldpri;
  66. oldpri = IRQ_save(TIMER_IRQ_PRI);
  67. _timer_cancel(timer);
  68. timer->deadline = deadline;
  69. now = time_now();
  70. delta = time_diff(now, deadline);
  71. for (pprev = &head; (t = *pprev) != NULL; pprev = &t->next)
  72. if (delta <= time_diff(now, t->deadline))
  73. break;
  74. timer->next = *pprev;
  75. *pprev = timer;
  76. if (head == timer)
  77. reprogram_timer(delta);
  78. IRQ_restore(oldpri);
  79. }
  80. void timer_cancel(struct timer *timer)
  81. {
  82. uint32_t oldpri;
  83. oldpri = IRQ_save(TIMER_IRQ_PRI);
  84. _timer_cancel(timer);
  85. IRQ_restore(oldpri);
  86. }
  87. void timers_init(void)
  88. {
  89. tim->cr2 = 0;
  90. tim->dier = TIM_DIER_UIE;
  91. IRQx_set_prio(TIMER_IRQ, TIMER_IRQ_PRI);
  92. IRQx_enable(TIMER_IRQ);
  93. }
  94. static void IRQ_timer(void)
  95. {
  96. struct timer *t;
  97. int32_t delta;
  98. tim->sr = 0;
  99. while ((t = head) != NULL) {
  100. if ((delta = time_diff(time_now(), t->deadline)) > SLACK_TICKS) {
  101. reprogram_timer(delta);
  102. break;
  103. }
  104. head = t->next;
  105. t->next = TIMER_INACTIVE;
  106. (*t->cb_fn)(t->cb_dat);
  107. }
  108. }
  109. /*
  110. * Local variables:
  111. * mode: C
  112. * c-file-style: "Linux"
  113. * c-basic-offset: 4
  114. * tab-width: 4
  115. * indent-tabs-mode: nil
  116. * End:
  117. */