timer.c 3.8 KB

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