From b8cc222e765d22dc2c2f3a4f361e4a0977fe93ee Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 9 Dec 2022 19:13:10 +0100 Subject: [PATCH] cpu/stm32/periph_timer: implement timer_set() The fallback implementation of timer_set() in `drivers/periph_common` is known to fail on short relative sets. This adds a robust implementation. --- cpu/stm32/include/periph/cpu_timer.h | 5 ++++ cpu/stm32/periph/timer.c | 41 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/cpu/stm32/include/periph/cpu_timer.h b/cpu/stm32/include/periph/cpu_timer.h index 9d22a99969..83ed1c6821 100644 --- a/cpu/stm32/include/periph/cpu_timer.h +++ b/cpu/stm32/include/periph/cpu_timer.h @@ -34,6 +34,11 @@ extern "C" { */ #define TIMER_CHANNEL_NUMOF (4U) +/** + * @brief The driver provides a relative set function + */ +#define PERIPH_TIMER_PROVIDES_SET + /** * @brief Define a macro for accessing a timer channel */ diff --git a/cpu/stm32/periph/timer.c b/cpu/stm32/periph/timer.c index fdb45a217f..0118f51d2a 100644 --- a/cpu/stm32/periph/timer.c +++ b/cpu/stm32/periph/timer.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "periph/timer.h" +#include /** * @brief Interrupt context for each configured timer @@ -146,6 +147,46 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value) return 0; } +int timer_set(tim_t tim, int channel, unsigned int timeout) +{ + if (channel >= (int)TIMER_CHANNEL_NUMOF) { + return -1; + } + + unsigned irqstate = irq_disable(); + set_oneshot(tim, channel); + + /* clear spurious IRQs */ + dev(tim)->SR &= ~(TIM_SR_CC1IF << channel); + + unsigned value = (dev(tim)->CNT + timeout) & timer_config[tim].max; + TIM_CHAN(tim, channel) = value; + + /* enable IRQ */ + 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 + + /* calculate time till timeout */ + value = (value - dev(tim)->CNT) & timer_config[tim].max; + + if (value > timeout) { + /* time till timeout is larger than requested --> timer already expired + * ==> let's make sure we have an IRQ pending :) */ + dev(tim)->CR1 &= ~(TIM_CR1_CEN); + TIM_CHAN(tim, channel) = dev(tim)->CNT; + dev(tim)->CR1 |= TIM_CR1_CEN; + } + + irq_restore(irqstate); + + return 0; +} + #ifdef MODULE_PERIPH_TIMER_PERIODIC int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags) {