diff --git a/cpu/atmega_common/periph/timer.c b/cpu/atmega_common/periph/timer.c index 81fb145c18..dac8c57af8 100644 --- a/cpu/atmega_common/periph/timer.c +++ b/cpu/atmega_common/periph/timer.c @@ -224,7 +224,12 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags /* disable CTC mode */ ctx[tim].mode &= (1 << 3); } - ctx[tim].dev->CRB = ctx[tim].mode; + /* enable timer or stop it */ + if (flags & TIM_FLAG_SET_STOPPED) { + ctx[tim].dev->CRB = 0; + } else { + ctx[tim].dev->CRB = ctx[tim].mode; + } } else { assert((flags & TIM_FLAG_RESET_ON_MATCH) == 0); res = -1; diff --git a/cpu/gd32v/periph/timer.c b/cpu/gd32v/periph/timer.c index 3a60082291..13b5871eab 100644 --- a/cpu/gd32v/periph/timer.c +++ b/cpu/gd32v/periph/timer.c @@ -164,6 +164,10 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, return -1; } + if (flags & TIM_FLAG_SET_STOPPED) { + timer_stop(tim); + } + clear_oneshot(tim, channel); if (flags & TIM_FLAG_RESET_ON_SET) { diff --git a/cpu/lpc23xx/periph/timer.c b/cpu/lpc23xx/periph/timer.c index cb36ede224..ce7bb24c82 100644 --- a/cpu/lpc23xx/periph/timer.c +++ b/cpu/lpc23xx/periph/timer.c @@ -208,6 +208,7 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags return -1; } + bool stop = flags & TIM_FLAG_SET_STOPPED; lpc23xx_timer_t *dev = get_dev(tim); if (flags & TIM_FLAG_RESET_ON_SET) { @@ -218,7 +219,12 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags * (reason: TCR is volatile control register. Bit 2 will put the timer into Reset Bit 1 will control if the timer is running) */ - dev->TCR = 1; + if (!stop) { + dev->TCR = 1; + } + } else if (stop) { + /* stop the timer */ + dev->TCR = 0; } clear_oneshot(tim, channel); diff --git a/cpu/native/periph/timer.c b/cpu/native/periph/timer.c index e4dc14baa1..f34d01c30d 100644 --- a/cpu/native/periph/timer.c +++ b/cpu/native/periph/timer.c @@ -105,7 +105,7 @@ int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg) return 0; } -static void do_timer_set(tim_t dev, unsigned int offset, bool periodic) +static void do_timer_set(unsigned int offset, bool periodic) { DEBUG("%s\n", __func__); @@ -121,8 +121,6 @@ static void do_timer_set(tim_t dev, unsigned int offset, bool periodic) } DEBUG("timer_set(): setting %lu.%06lu\n", itv.it_value.tv_sec, itv.it_value.tv_usec); - - timer_start(dev); } int timer_set(tim_t dev, int channel, unsigned int offset) @@ -137,7 +135,8 @@ int timer_set(tim_t dev, int channel, unsigned int offset) offset = NATIVE_TIMER_MIN_RES; } - do_timer_set(dev, offset, false); + do_timer_set(offset, false); + timer_start(dev); return 0; } @@ -156,7 +155,11 @@ int timer_set_periodic(tim_t dev, int channel, unsigned int value, uint8_t flags return -1; } - do_timer_set(dev, value, true); + do_timer_set(value, true); + + if (!(flags & TIM_FLAG_SET_STOPPED)) { + timer_start(dev); + } return 0; } @@ -165,7 +168,8 @@ int timer_clear(tim_t dev, int channel) { (void)channel; - do_timer_set(dev, 0, false); + do_timer_set(0, false); + timer_start(dev); return 0; } diff --git a/cpu/nrf5x_common/periph/timer.c b/cpu/nrf5x_common/periph/timer.c index b37939891f..2ad3559d88 100644 --- a/cpu/nrf5x_common/periph/timer.c +++ b/cpu/nrf5x_common/periph/timer.c @@ -114,6 +114,9 @@ int timer_set_periodic(tim_t tim, int chan, unsigned int value, uint8_t flags) return -1; } + /* stop timer to avoid race condition */ + dev(tim)->TASKS_STOP = 1; + ctx[tim].flags |= (1 << chan); ctx[tim].is_periodic |= (1 << chan); dev(tim)->CC[chan] = value; @@ -125,7 +128,10 @@ int timer_set_periodic(tim_t tim, int chan, unsigned int value, uint8_t flags) } dev(tim)->INTENSET = (TIMER_INTENSET_COMPARE0_Msk << chan); - dev(tim)->TASKS_START = 1; + /* re-start timer */ + if (!(flags & TIM_FLAG_SET_STOPPED)) { + dev(tim)->TASKS_START = 1; + } return 0; } diff --git a/cpu/rpx0xx/periph/timer.c b/cpu/rpx0xx/periph/timer.c index 2476bf9c55..e47a6c4bda 100644 --- a/cpu/rpx0xx/periph/timer.c +++ b/cpu/rpx0xx/periph/timer.c @@ -183,6 +183,9 @@ int timer_set_periodic(tim_t dev, int channel, unsigned int value, uint8_t flags if (channel < 0 || channel >= timer_config[dev].ch_numof) { return -EINVAL; } + if (flags & TIM_FLAG_SET_STOPPED) { + timer_stop(dev); + } if (flags & TIM_FLAG_RESET_ON_SET) { _timer_reset(dev); } diff --git a/cpu/sam0_common/periph/timer.c b/cpu/sam0_common/periph/timer.c index e1064e904e..7c480ac1cb 100644 --- a/cpu/sam0_common/periph/timer.c +++ b/cpu/sam0_common/periph/timer.c @@ -110,27 +110,21 @@ static uint8_t _get_prescaler(uint32_t freq_out, uint32_t freq_in) /* TOP value is CC0 */ static inline void _set_mfrq(tim_t tim) { - timer_stop(tim); - wait_synchronization(tim); #ifdef TC_WAVE_WAVEGEN_MFRQ dev(tim)->WAVE.reg = TC_WAVE_WAVEGEN_MFRQ; #else dev(tim)->CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_MFRQ_Val; #endif - timer_start(tim); } /* TOP value is MAX timer value */ static inline void _set_nfrq(tim_t tim) { - timer_stop(tim); - wait_synchronization(tim); #ifdef TC_WAVE_WAVEGEN_NFRQ dev(tim)->WAVE.reg = TC_WAVE_WAVEGEN_NFRQ; #else dev(tim)->CTRLA.bit.WAVEGEN = TC_CTRLA_WAVEGEN_NFRQ_Val; #endif - timer_start(tim); } /** @@ -242,9 +236,12 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags { DEBUG("Setting timer %i channel %i to %i (repeating)\n", tim, channel, value); + timer_stop(tim); + /* set timeout value */ switch (channel) { case 0: + /* clear interrupt */ dev(tim)->INTFLAG.reg = TC_INTFLAG_MC0; if (flags & TIM_FLAG_RESET_ON_MATCH) { @@ -276,6 +273,10 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags dev(tim)->COUNT.reg = 0; } + if (!(flags & TIM_FLAG_SET_STOPPED)) { + timer_start(tim); + } + clear_oneshot(tim, channel); return 0; @@ -328,10 +329,12 @@ unsigned int timer_read(tim_t tim) void timer_stop(tim_t tim) { dev(tim)->CTRLA.bit.ENABLE = 0; + wait_synchronization(tim); } void timer_start(tim_t tim) { + wait_synchronization(tim); dev(tim)->CTRLA.bit.ENABLE = 1; } diff --git a/cpu/stm32/periph/timer.c b/cpu/stm32/periph/timer.c index 64d929b40e..a09c601c94 100644 --- a/cpu/stm32/periph/timer.c +++ b/cpu/stm32/periph/timer.c @@ -149,6 +149,10 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags clear_oneshot(tim, channel); + if (flags & TIM_FLAG_SET_STOPPED) { + timer_stop(tim); + } + if (flags & TIM_FLAG_RESET_ON_SET) { /* setting COUNT gives us an interrupt on all channels */ unsigned state = irq_disable(); diff --git a/drivers/dose/dose.c b/drivers/dose/dose.c index 58f9a64fdf..24a3ce33af 100644 --- a/drivers/dose/dose.c +++ b/drivers/dose/dose.c @@ -177,8 +177,8 @@ static void _dose_watchdog_cb(void *arg, int chan) static void _watchdog_init(unsigned timeout_us) { timer_init(DOSE_TIMER_DEV, US_PER_SEC, _dose_watchdog_cb, NULL); - timer_set_periodic(DOSE_TIMER_DEV, 0, timeout_us, TIM_FLAG_RESET_ON_MATCH); - timer_stop(DOSE_TIMER_DEV); + timer_set_periodic(DOSE_TIMER_DEV, 0, timeout_us, + TIM_FLAG_RESET_ON_MATCH | TIM_FLAG_SET_STOPPED); } #else static inline void _watchdog_start(void) {} diff --git a/drivers/include/periph/timer.h b/drivers/include/periph/timer.h index c1068deaa7..6b21d6885f 100644 --- a/drivers/include/periph/timer.h +++ b/drivers/include/periph/timer.h @@ -89,6 +89,16 @@ typedef uint_fast8_t tim_t; #define TIM_FLAG_RESET_ON_MATCH (0x02) #endif +/** + * @brief Keep the timer stopped after setting alarm. + * + * When set, the timer will remained stopped after a timer_set_periodic() and + * can be started manually with timer_start(). + */ +#ifndef TIM_FLAG_SET_STOPPED +#define TIM_FLAG_SET_STOPPED (0x04) +#endif + /** * @brief Signature of event callback functions triggered from interrupts * diff --git a/tests/periph_timer_periodic/main.c b/tests/periph_timer_periodic/main.c index 42b4d014bf..5b376ce4f0 100644 --- a/tests/periph_timer_periodic/main.c +++ b/tests/periph_timer_periodic/main.c @@ -93,6 +93,18 @@ static const char* _print_ok(int chan, bool *succeeded) return "ERROR"; } +static void _cb_set_stopped(void *arg, int chan) +{ + (void)chan; + + bool *succeeded = arg; + + *succeeded = false; + puts("TIM_FLAG_SET_STOPPED failed"); + + timer_stop(TIMER_CYCL); +} + int main(void) { mutex_t lock = MUTEX_INIT; @@ -144,6 +156,12 @@ int main(void) } } + expect(timer_init(TIMER_CYCL, timer_hz, _cb_set_stopped, &succeeded) == 0); + timer_set_periodic(TIMER_CYCL, 0, 25, TIM_FLAG_RESET_ON_SET | TIM_FLAG_SET_STOPPED); + + /* busy wait */ + for (volatile uint32_t i = 0; i < CLOCK_CORECLOCK / 10; ++i) {} + if (succeeded) { puts("TEST SUCCEEDED"); } else {