diff --git a/cpu/stm32/Kconfig b/cpu/stm32/Kconfig index a72ff254eb..e7880ad381 100644 --- a/cpu/stm32/Kconfig +++ b/cpu/stm32/Kconfig @@ -13,6 +13,7 @@ config CPU_STM32 select HAS_PERIPH_GPIO select HAS_PERIPH_GPIO_IRQ select HAS_PUF_SRAM + select HAS_PERIPH_TIMER_PERIODIC select HAS_PERIPH_UART_MODECFG select HAS_PERIPH_UART_NONBLOCKING select HAS_PERIPH_WDT diff --git a/cpu/stm32/Makefile.features b/cpu/stm32/Makefile.features index 708f5869d9..91c7bee51a 100644 --- a/cpu/stm32/Makefile.features +++ b/cpu/stm32/Makefile.features @@ -5,6 +5,7 @@ FEATURES_PROVIDED += cpu_stm32$(CPU_FAM) FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += puf_sram +FEATURES_PROVIDED += periph_timer_periodic FEATURES_PROVIDED += periph_uart_modecfg FEATURES_PROVIDED += periph_uart_nonblocking FEATURES_PROVIDED += periph_wdt diff --git a/cpu/stm32/periph/timer.c b/cpu/stm32/periph/timer.c index 3444b3a92a..4e14305200 100644 --- a/cpu/stm32/periph/timer.c +++ b/cpu/stm32/periph/timer.c @@ -36,6 +36,57 @@ static inline TIM_TypeDef *dev(tim_t tim) return timer_config[tim].dev; } +#ifdef MODULE_PERIPH_TIMER_PERIODIC + +/** + * @brief Helper macro to get channel bit in timer/channel bitmap + */ +#define CHAN_BIT(tim, chan) (1 << chan) << (TIMER_CHANNEL_NUMOF * (tim & 1)) + +/** + * @brief Bitmap for compare channel disable after match + */ +static uint8_t _oneshot[(TIMER_NUMOF+1)/2]; + +/** + * @brief Clear interrupt enable after the interrupt has fired + */ +static inline void set_oneshot(tim_t tim, int chan) +{ + _oneshot[tim >> 1] |= CHAN_BIT(tim, chan); +} + +/** + * @brief Enable interrupt with every wrap-around of the timer + */ +static inline void clear_oneshot(tim_t tim, int chan) +{ + _oneshot[tim >> 1] &= ~CHAN_BIT(tim, chan); +} + +static inline bool is_oneshot(tim_t tim, int chan) +{ + return _oneshot[tim >> 1] & CHAN_BIT(tim, chan); +} + +#else /* !MODULE_PERIPH_TIMER_PERIODIC */ + +static inline void set_oneshot(tim_t tim, int chan) +{ + (void)tim; + (void)chan; +} + +static inline bool is_oneshot(tim_t tim, int chan) +{ + (void)tim; + (void)chan; + + return true; +} + +#endif /* MODULE_PERIPH_TIMER_PERIODIC */ + int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg) { /* check if device is valid */ @@ -74,13 +125,53 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value) return -1; } + set_oneshot(tim, channel); + TIM_CHAN(tim, channel) = (value & timer_config[tim].max); - dev(tim)->SR &= ~(TIM_SR_CC1IF << channel); + +#ifdef MODULE_PERIPH_TIMER_PERIODIC + if (dev(tim)->ARR == TIM_CHAN(tim, channel)) { + dev(tim)->ARR = timer_config[tim].max; + } +#endif + dev(tim)->DIER |= (TIM_DIER_CC1IE << channel); return 0; } +#ifdef MODULE_PERIPH_TIMER_PERIODIC +int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags) +{ + if (channel >= (int)TIMER_CHANNEL_NUMOF) { + return -1; + } + + clear_oneshot(tim, channel); + + if (flags & TIM_FLAG_RESET_ON_SET) { + /* setting COUNT gives us an interrupt on all channels */ + unsigned state = irq_disable(); + dev(tim)->CNT = 0; + + /* wait for the interrupt & clear it */ + while(dev(tim)->SR == 0) {} + dev(tim)->SR = 0; + + irq_restore(state); + } + + TIM_CHAN(tim, channel) = value; + dev(tim)->DIER |= (TIM_DIER_CC1IE << channel); + + if (flags & TIM_FLAG_RESET_ON_MATCH) { + dev(tim)->ARR = value; + } + + return 0; +} +#endif /* MODULE_PERIPH_TIMER_PERIODIC */ + int timer_clear(tim_t tim, int channel) { if (channel >= (int)TIMER_CHANNEL_NUMOF) { @@ -88,6 +179,13 @@ int timer_clear(tim_t tim, int channel) } dev(tim)->DIER &= ~(TIM_DIER_CC1IE << channel); + +#ifdef MODULE_PERIPH_TIMER_PERIODIC + if (dev(tim)->ARR == TIM_CHAN(tim, channel)) { + dev(tim)->ARR = timer_config[tim].max; + } +#endif + return 0; } @@ -108,13 +206,30 @@ void timer_stop(tim_t tim) static inline void irq_handler(tim_t tim) { - uint32_t status = (dev(tim)->SR & dev(tim)->DIER); + uint32_t top = dev(tim)->ARR; + uint32_t status = dev(tim)->SR & dev(tim)->DIER; + dev(tim)->SR = 0; - for (unsigned int i = 0; i < TIMER_CHANNEL_NUMOF; i++) { - if (status & (TIM_SR_CC1IF << i)) { - dev(tim)->DIER &= ~(TIM_DIER_CC1IE << i); - isr_ctx[tim].cb(isr_ctx[tim].arg, i); + for (unsigned int i = 0; status; i++) { + uint32_t msk = TIM_SR_CC1IF << i; + + /* check if interrupt flag is set */ + if ((status & msk) == 0) { + continue; } + status &= ~msk; + + /* interrupt flag gets set for all channels > ARR */ + if (TIM_CHAN(tim, i) > top) { + continue; + } + + /* disable Interrupt */ + if (is_oneshot(tim, i)) { + dev(tim)->DIER &= ~msk; + } + + isr_ctx[tim].cb(isr_ctx[tim].arg, i); } cortexm_isr_end(); }