diff --git a/src/avr/timer.c b/src/avr/timer.c index e4d7b9425..a7e832bc3 100644 --- a/src/avr/timer.c +++ b/src/avr/timer.c @@ -42,6 +42,14 @@ timer_set_clear(uint16_t next) TIFR1 = 1<<OCF1A; } +static inline void +timer_repeat_set(uint16_t next) +{ + // Timer1B is used to limit the number of timers run from a timer1A irq + OCR1B = next; + TIFR1 = 1<<OCF1B; +} + ISR(TIMER1_COMPA_vect) { sched_timer_kick(); @@ -128,9 +136,8 @@ timer_set_next(uint32_t next) return 1; } -static uint8_t timer_repeat; -#define TIMER_MAX_REPEAT 40 -#define TIMER_MAX_NEXT_REPEAT 15 +#define TIMER_IDLE_REPEAT_TICKS 8000 +#define TIMER_REPEAT_TICKS 3000 #define TIMER_MIN_TRY_TICKS 60 // 40 ticks to exit irq; 20 ticks of progress #define TIMER_DEFER_REPEAT_TICKS 200 @@ -147,11 +154,12 @@ timer_try_set_next(uint32_t target) goto done; // Next timer is in the past or near future - can't reschedule to it - uint8_t tr = timer_repeat-1; - if (likely(tr)) { + if (!(TIFR1 & (1<<OCF1B))) { + // Can run more timers from this irq; briefly allow irqs irq_enable(); - timer_repeat = tr; + asm("nop"); irq_disable(); + while (diff >= 0) { // Next timer is in the near future - wait for time to occur now = timer_get(); @@ -161,13 +169,13 @@ timer_try_set_next(uint32_t target) } return 0; } - - // Too many repeat timers from a single interrupt - force a pause - timer_repeat = TIMER_MAX_NEXT_REPEAT; - next = now + TIMER_DEFER_REPEAT_TICKS; if (diff < (int16_t)(-timer_from_us(1000))) goto fail; + // Too many repeat timers - force a pause so tasks aren't starved + timer_repeat_set(now + TIMER_REPEAT_TICKS); + next = now + TIMER_DEFER_REPEAT_TICKS; + done: timer_set(next); return 1; @@ -175,9 +183,13 @@ fail: shutdown("Rescheduled timer in the past"); } +// Periodic background task that temporarily boosts priority of +// timers. This helps prioritize timers when tasks are idling. static void timer_task(void) { - timer_repeat = TIMER_MAX_REPEAT; + irq_disable(); + timer_repeat_set(timer_get() + TIMER_IDLE_REPEAT_TICKS); + irq_enable(); } DECL_TASK(timer_task);