timer.c 3.5 KB

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