diff --git a/cpu/kinetis/periph/timer.c b/cpu/kinetis/periph/timer.c index b2ad14cdb64ab7a76167dbe28f42337506687493..8c9befe7f0c63324223b12a86a11c21426eace9e 100644 --- a/cpu/kinetis/periph/timer.c +++ b/cpu/kinetis/periph/timer.c @@ -151,34 +151,6 @@ static inline void pit_start(uint8_t dev); static inline void pit_stop(uint8_t dev); static inline void pit_irq_handler(tim_t dev); -static inline void _pit_set_cb_config(uint8_t dev, timer_cb_t cb, void *arg) -{ - /* set callback function */ - pit[dev].isr_ctx.cb = cb; - pit[dev].isr_ctx.arg = arg; -} - -/** use channel n-1 as prescaler */ -static inline void _pit_set_prescaler(uint8_t ch, uint32_t freq) -{ - /* Disable channel completely */ - PIT->CHANNEL[ch].TCTRL = 0x0; - PIT->CHANNEL[ch].LDVAL = (PIT_BASECLOCK / freq) - 1; - /* Start the prescaler counter immediately */ - PIT->CHANNEL[ch].TCTRL = (PIT_TCTRL_TEN_MASK); -} - -static inline void _pit_set_counter(uint8_t dev) -{ - const uint8_t ch = pit_config[dev].count_ch; - /* Disable channel completely */ - PIT->CHANNEL[ch].TCTRL = 0x0; - PIT->CHANNEL[ch].LDVAL = pit[dev].ldval; - PIT->CHANNEL[ch].TFLG = PIT_TFLG_TIF_MASK; - /* Restore previous timer state */ - PIT->CHANNEL[ch].TCTRL = pit[dev].tctrl; -} - static inline int pit_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *arg) { /* Turn on module clock gate */ @@ -188,32 +160,38 @@ static inline int pit_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *arg) /* Disable IRQs to avoid race with ISR */ unsigned int mask = irq_disable(); - + uint8_t count_ch = pit_config[dev].count_ch; /* Clear configuration */ - PIT->CHANNEL[pit_config[dev].count_ch].TCTRL = 0; + PIT->CHANNEL[count_ch].TCTRL = 0; /* Freeze timers during debug break, resume normal operations (clear MDIS) */ PIT->MCR = PIT_MCR_FRZ_MASK; - _pit_set_cb_config(dev, cb, arg); + /* set callback function */ + pit[dev].isr_ctx.cb = cb; + pit[dev].isr_ctx.arg = arg; /* Clear IRQ flag */ - PIT->CHANNEL[pit_config[dev].count_ch].TFLG = PIT_TFLG_TIF_MASK; + PIT->CHANNEL[count_ch].TFLG = PIT_TFLG_TIF_MASK; #if KINETIS_PIT_COMBINED_IRQ /* One IRQ for all channels */ /* NVIC_ClearPendingIRQ(PIT_IRQn); */ /* does it make sense to clear this IRQ flag? */ NVIC_EnableIRQ(PIT_IRQn); #else /* Refactor the below lines if there are any CPUs where the PIT IRQs are not sequential */ - NVIC_ClearPendingIRQ(PIT0_IRQn + pit_config[dev].count_ch); - NVIC_EnableIRQ(PIT0_IRQn + pit_config[dev].count_ch); + NVIC_ClearPendingIRQ(PIT0_IRQn + count_ch); + NVIC_EnableIRQ(PIT0_IRQn + count_ch); #endif /* Reset up-counter */ pit[dev].count = PIT_MAX_VALUE; - pit[dev].ldval = PIT_MAX_VALUE; - pit[dev].tctrl = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; - _pit_set_prescaler(pit_config[dev].prescaler_ch, freq); - _pit_set_counter(dev); + PIT->CHANNEL[count_ch].LDVAL = PIT_MAX_VALUE; + /* Disable prescaler channel */ + PIT->CHANNEL[pit_config[dev].prescaler_ch].TCTRL = 0x0; + /* Load prescaler value */ + PIT->CHANNEL[pit_config[dev].prescaler_ch].LDVAL = (PIT_BASECLOCK / freq) - 1; + /* Start the prescaler counter */ + PIT->CHANNEL[pit_config[dev].prescaler_ch].TCTRL = (PIT_TCTRL_TEN_MASK); + PIT->CHANNEL[count_ch].TCTRL = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; irq_restore(mask); return 0; @@ -224,17 +202,18 @@ static inline int pit_set(uint8_t dev, uint32_t timeout) const uint8_t ch = pit_config[dev].count_ch; /* Disable IRQs to minimize the number of lost ticks */ unsigned int mask = irq_disable(); - pit[dev].ldval = timeout; - pit[dev].tctrl = PIT_TCTRL_TIE_MASK | PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; + /* Subtract if there was anything left on the counter */ + pit[dev].count -= PIT->CHANNEL[ch].CVAL; + /* Set new timeout */ + PIT->CHANNEL[ch].TCTRL = 0; + PIT->CHANNEL[ch].LDVAL = timeout; + PIT->CHANNEL[ch].TFLG = PIT_TFLG_TIF_MASK; + PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_TIE_MASK | PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; /* Add the new timeout offset to the up-counter */ pit[dev].count += timeout; - if ((PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TEN_MASK) != 0) { - /* Timer is currently running */ - uint32_t cval = PIT->CHANNEL[ch].CVAL; - /* Subtract if there was anything left on the counter */ - pit[dev].count -= cval; - _pit_set_counter(dev); - } + /* Set the timer to reload the maximum value to be able to count the number + * of overflow ticks inside the ISR */ + PIT->CHANNEL[ch].LDVAL = PIT_MAX_VALUE; irq_restore(mask); return 0; } @@ -244,16 +223,18 @@ static inline int pit_set_absolute(uint8_t dev, uint32_t target) uint8_t ch = pit_config[dev].count_ch; /* Disable IRQs to minimize the number of lost ticks */ unsigned int mask = irq_disable(); - uint32_t now = pit_read(dev); + uint32_t now = pit[dev].count - PIT->CHANNEL[ch].CVAL; uint32_t offset = target - now; - pit[dev].ldval = offset; - pit[dev].tctrl = PIT_TCTRL_TIE_MASK | PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; + /* Set new timeout */ + PIT->CHANNEL[ch].TCTRL = 0; + PIT->CHANNEL[ch].LDVAL = offset; + PIT->CHANNEL[ch].TFLG = PIT_TFLG_TIF_MASK; + PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_TIE_MASK | PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; /* Set the new target time in the up-counter */ pit[dev].count = target; - if ((PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TEN_MASK) != 0) { - _pit_set_counter(dev); - } - + /* Set the timer to reload the maximum value to be able to count the number + * of overflow ticks inside the ISR */ + PIT->CHANNEL[ch].LDVAL = PIT_MAX_VALUE; irq_restore(mask); return 0; } @@ -263,78 +244,61 @@ static inline int pit_clear(uint8_t dev) uint8_t ch = pit_config[dev].count_ch; /* Disable IRQs to minimize the number of lost ticks */ unsigned int mask = irq_disable(); - - pit[dev].ldval = PIT_MAX_VALUE; - pit[dev].tctrl = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; - /* pit[dev].count += PIT_MAX_VALUE + 1; */ /* == 0 (mod 2**32) */ - - if ((PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TEN_MASK) != 0) { - /* Timer is currently running */ - uint32_t cval = PIT->CHANNEL[ch].CVAL; - /* Subtract if there was anything left on the counter */ - pit[dev].count -= cval; - /* Set a long timeout */ - _pit_set_counter(ch); - } - + /* Subtract if there was anything left on the counter */ + pit[dev].count -= PIT->CHANNEL[ch].CVAL; + /* No need to add PIT_MAX_VALUE + 1 to the counter because of modulo 2**32 */ + /* Set a long timeout */ + PIT->CHANNEL[ch].TCTRL = 0; + PIT->CHANNEL[ch].LDVAL = PIT_MAX_VALUE; + PIT->CHANNEL[ch].TFLG = PIT_TFLG_TIF_MASK; + PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; irq_restore(mask); return 0; } +/* CVAL is unreliable if the timer is not enabled (TCTRL_TEN bit clear), + * by stopping the prescaler instead of the counter channel we avoid this issue, + * and additionally do not need to worry about saving the control registers or + * recomputing the target time when starting the timer */ static inline uint32_t pit_read(uint8_t dev) { uint8_t ch = pit_config[dev].count_ch; - if ((PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TEN_MASK) != 0) { - /* Timer running */ - return pit[dev].count - PIT->CHANNEL[ch].CVAL; - } - else { - /* Timer stopped */ - return pit[dev].count; - } + return pit[dev].count - PIT->CHANNEL[ch].CVAL; } static inline void pit_start(uint8_t dev) { - uint8_t ch = pit_config[dev].count_ch; - if ((PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TEN_MASK) != 0) { - /* Already running */ - return; - } - PIT->CHANNEL[ch].LDVAL = pit[dev].ldval; - pit[dev].count += pit[dev].ldval; - PIT->CHANNEL[ch].TCTRL = pit[dev].tctrl; + uint8_t ch = pit_config[dev].prescaler_ch; + PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_TEN_MASK; } static inline void pit_stop(uint8_t dev) { - uint8_t ch = pit_config[dev].count_ch; - if ((PIT->CHANNEL[ch].TCTRL & PIT_TCTRL_TEN_MASK) == 0) { - /* Already stopped */ - return; - } - uint32_t cval = PIT->CHANNEL[ch].CVAL; - pit[dev].tctrl = PIT->CHANNEL[ch].TCTRL; + uint8_t ch = pit_config[dev].prescaler_ch; PIT->CHANNEL[ch].TCTRL = 0; - pit[dev].count -= cval; - pit[dev].ldval = cval; } static inline void pit_irq_handler(tim_t dev) { uint8_t ch = pit_config[_pit_index(dev)].count_ch; pit_t *pit_ctx = &pit[_pit_index(dev)]; - pit_ctx->ldval = PIT_MAX_VALUE; - pit_ctx->count += PIT_MAX_VALUE; - pit_ctx->tctrl = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; - _pit_set_counter(_pit_index(dev)); + if (!PIT->CHANNEL[ch].TFLG) { + DEBUG("PIT%u!TFLG\n", (unsigned)dev); + return; + } + /* Add the overflow amount to the counter before resetting */ + /* (this may be > 0 if the IRQ handler was delayed e.g. by irq_disable etc.) */ + pit_ctx->count += PIT->CHANNEL[ch].LDVAL - PIT->CHANNEL[ch].CVAL; + /* inline pit_clear */ + PIT->CHANNEL[ch].TCTRL = 0; + PIT->CHANNEL[ch].LDVAL = PIT_MAX_VALUE; + PIT->CHANNEL[ch].TFLG = PIT_TFLG_TIF_MASK; + PIT->CHANNEL[ch].TCTRL = PIT_TCTRL_CHN_MASK | PIT_TCTRL_TEN_MASK; if (pit_ctx->isr_ctx.cb != NULL) { pit_ctx->isr_ctx.cb(pit_ctx->isr_ctx.arg, 0); } - PIT->CHANNEL[ch].TFLG = PIT_TFLG_TIF_MASK; - cortexm_isr_end(); }