diff --git a/cpu/sam0_common/Makefile.dep b/cpu/sam0_common/Makefile.dep index b286abfdfa..2dc27cd379 100644 --- a/cpu/sam0_common/Makefile.dep +++ b/cpu/sam0_common/Makefile.dep @@ -5,6 +5,9 @@ endif # All SAM0 based CPUs provide PM USEMODULE += pm_layered +# the timer implements timer_set_periodic() +FEATURES_PROVIDED += periph_timer_periodic + # include sam0 common periph drivers USEMODULE += sam0_common_periph diff --git a/cpu/sam0_common/periph/timer.c b/cpu/sam0_common/periph/timer.c index 775360c3a6..6e86a8ff9e 100644 --- a/cpu/sam0_common/periph/timer.c +++ b/cpu/sam0_common/periph/timer.c @@ -37,6 +37,23 @@ */ static timer_isr_ctx_t config[TIMER_NUMOF]; +static uint32_t _oneshot; + +static inline void set_oneshot(tim_t tim, int chan) +{ + _oneshot |= (1 << chan) << (TIMER_CHANNELS * tim); +} + +static inline void clear_oneshot(tim_t tim, int chan) +{ + _oneshot &= ~((1 << chan) << (TIMER_CHANNELS * tim)); +} + +static inline bool is_oneshot(tim_t tim, int chan) +{ + return _oneshot & ((1 << chan) << (TIMER_CHANNELS * tim)); +} + static inline TcCount32 *dev(tim_t tim) { return &timer_config[tim].dev->COUNT32; @@ -89,6 +106,32 @@ static uint8_t _get_prescaler(unsigned long freq_out, unsigned long freq_in) return scale; } +/* 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); +} + /** * @brief Setup the given timer */ @@ -187,7 +230,52 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value) break; default: return -1; - } + } + + set_oneshot(tim, channel); + + return 0; +} + +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); + + /* set timeout value */ + switch (channel) { + case 0: + dev(tim)->INTFLAG.reg = TC_INTFLAG_MC0; + + if (flags & TIM_FLAG_RESET_ON_MATCH) { + _set_mfrq(tim); + } else { + _set_nfrq(tim); + } + + _set_cc(tim, 0, value); + dev(tim)->INTENSET.reg = TC_INTENSET_MC0; + break; + case 1: + + /* only CC0 can be used to set TOP */ + if (flags & TIM_FLAG_RESET_ON_MATCH) { + assert(0); + return -1; + } + + dev(tim)->INTFLAG.reg = TC_INTFLAG_MC1; + _set_cc(tim, 1, value); + dev(tim)->INTENSET.reg = TC_INTENSET_MC1; + break; + default: + return -1; + } + + if (flags & TIM_FLAG_RESET_ON_SET) { + dev(tim)->COUNT.reg = 0; + } + + clear_oneshot(tim, channel); return 0; } @@ -255,13 +343,22 @@ static inline void timer_isr(tim_t tim) tc->INTFLAG.reg = status; if ((status & TC_INTFLAG_MC0) && tc->INTENSET.bit.MC0) { - tc->INTENCLR.reg = TC_INTENCLR_MC0; + + if (is_oneshot(tim, 0)) { + tc->INTENCLR.reg = TC_INTENCLR_MC0; + } + if (config[tim].cb) { config[tim].cb(config[tim].arg, 0); } } + if ((status & TC_INTFLAG_MC1) && tc->INTENSET.bit.MC1) { - tc->INTENCLR.reg = TC_INTENCLR_MC1; + + if (is_oneshot(tim, 1)) { + tc->INTENCLR.reg = TC_INTENCLR_MC1; + } + if (config[tim].cb) { config[tim].cb(config[tim].arg, 1); }