123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- /*
- * timer.c
- *
- * Deadline-based timer callbacks.
- *
- * Written & released by Keir Fraser <keir.xen@gmail.com>
- *
- * This is free and unencumbered software released into the public domain.
- * See the file COPYING for more details, or visit <http://unlicense.org>.
- */
- #if STM32F == 1
- void IRQ_25(void) __attribute__((alias("IRQ_timer")));
- #define TIMER_IRQ 25
- #define tim tim1
- #define tim_bits 16
- #elif STM32F == 7
- void IRQ_50(void) __attribute__((alias("IRQ_timer")));
- #define TIMER_IRQ 50
- #define tim tim5 /* 32-bit timer */
- #define tim_bits 32
- #endif
- /* IRQ only on counter overflow, one-time enable. */
- #define TIM_CR1 (TIM_CR1_URS | TIM_CR1_OPM)
- /* Empirically-determined offset applied to timer deadlines to counteract the
- * latency incurred by reprogram_timer() and IRQ_timer(). */
- #define SLACK_TICKS 12
- #define TIMER_INACTIVE ((struct timer *)1ul)
- static struct timer *head;
- static void reprogram_timer(int32_t delta)
- {
- tim->cr1 = TIM_CR1;
- if ((tim_bits == 32) || (delta < 0x10000)) {
- /* Fine-grained deadline (sub-microsecond accurate) */
- tim->psc = SYSCLK_MHZ/TIME_MHZ-1;
- tim->arr = (delta <= SLACK_TICKS) ? 1 : delta-SLACK_TICKS;
- } else {
- /* Coarse-grained deadline, fires in time to set a shorter,
- * fine-grained deadline. */
- tim->psc = sysclk_us(100)-1;
- tim->arr = min_t(uint32_t, 0xffffu,
- delta/time_us(100)-50); /* 5ms early */
- }
- tim->egr = TIM_EGR_UG; /* update CNT, PSC, ARR */
- tim->sr = 0; /* dummy write, gives hardware time to process EGR.UG=1 */
- tim->cr1 = TIM_CR1 | TIM_CR1_CEN;
- }
- void timer_init(struct timer *timer, void (*cb_fn)(void *), void *cb_dat)
- {
- timer->cb_fn = cb_fn;
- timer->cb_dat = cb_dat;
- timer->next = TIMER_INACTIVE;
- }
- static bool_t timer_is_active(struct timer *timer)
- {
- return timer->next != TIMER_INACTIVE;
- }
- static void _timer_cancel(struct timer *timer)
- {
- struct timer *t, **pprev;
- if (!timer_is_active(timer))
- return;
- for (pprev = &head; (t = *pprev) != timer; pprev = &t->next)
- continue;
- *pprev = t->next;
- t->next = TIMER_INACTIVE;
- }
- void timer_set(struct timer *timer, time_t deadline)
- {
- struct timer *t, **pprev;
- time_t now;
- int32_t delta;
- uint32_t oldpri;
- oldpri = IRQ_save(TIMER_IRQ_PRI);
- _timer_cancel(timer);
- timer->deadline = deadline;
- now = time_now();
- delta = time_diff(now, deadline);
- for (pprev = &head; (t = *pprev) != NULL; pprev = &t->next)
- if (delta <= time_diff(now, t->deadline))
- break;
- timer->next = *pprev;
- *pprev = timer;
- if (head == timer)
- reprogram_timer(delta);
- IRQ_restore(oldpri);
- }
- void timer_cancel(struct timer *timer)
- {
- uint32_t oldpri;
- oldpri = IRQ_save(TIMER_IRQ_PRI);
- _timer_cancel(timer);
- IRQ_restore(oldpri);
- }
- void timers_init(void)
- {
- #if STM32F == 7
- rcc->apb1enr |= RCC_APB1ENR_TIM5EN;
- peripheral_clock_delay();
- #endif
- tim->cr2 = 0;
- tim->dier = TIM_DIER_UIE;
- IRQx_set_prio(TIMER_IRQ, TIMER_IRQ_PRI);
- IRQx_enable(TIMER_IRQ);
- }
- static void IRQ_timer(void)
- {
- struct timer *t;
- int32_t delta;
- tim->sr = 0;
- while ((t = head) != NULL) {
- if ((delta = time_diff(time_now(), t->deadline)) > SLACK_TICKS) {
- reprogram_timer(delta);
- break;
- }
- head = t->next;
- t->next = TIMER_INACTIVE;
- (*t->cb_fn)(t->cb_dat);
- }
- }
- /*
- * Local variables:
- * mode: C
- * c-file-style: "Linux"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
|