From b8cc222e765d22dc2c2f3a4f361e4a0977fe93ee Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 9 Dec 2022 19:13:10 +0100 Subject: [PATCH 1/2] 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 9d22a99969803..83ed1c68210ec 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 fdb45a217f7f2..0118f51d2aa29 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) { From 0835466498e05482d535f717836330647342902a Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 23 Sep 2022 13:49:45 +0200 Subject: [PATCH 2/2] tests/thread_float: do not overload slow MCUs with IRQs If the regular context switches are triggered too fast, slow MCUs will be able to spent little time on actually progressing in the test. This will scale the IRQ rate with the CPU clock as a crude way too keep load within limits. --- tests/thread_float/main.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/thread_float/main.c b/tests/thread_float/main.c index 12eb44f559020..a41e0266a98d0 100644 --- a/tests/thread_float/main.c +++ b/tests/thread_float/main.c @@ -23,10 +23,14 @@ #include #include -#include "thread.h" +#include "board.h" +#include "clk.h" +#include "macros/units.h" #include "msg.h" +#include "periph_conf.h" +#include "thread.h" +#include "time_units.h" #include "ztimer.h" -#include "timex.h" static char t1_stack[THREAD_STACKSIZE_MAIN]; static char t2_stack[THREAD_STACKSIZE_MAIN]; @@ -36,15 +40,13 @@ static kernel_pid_t p1, p2, p3; static ztimer_t timer; -#define OFFSET (100) - static mutex_t lock = MUTEX_INIT; static void timer_cb(void *arg) { - (void)arg; + uint32_t *timeout = arg; thread_yield(); - ztimer_set(ZTIMER_USEC, &timer, OFFSET); + ztimer_set(ZTIMER_USEC, &timer, *timeout); } static void *thread_1_2_3(void *_arg) @@ -81,6 +83,16 @@ int main(void) const char *t2_name = "t2"; const char *t3_name = "t3"; + /* Let's not overwhelm boards by firing IRQs faster than they can handle and + * give them 50 billion CPU cycles per timeout. + * + * (Note: The `static` is required as this variable will be accessed from + * the ISR, which will occur even after the main thread has exited.) */ + static uint32_t timeout = 0; + /* Note: It must be initialized dynamically, as coreclk() is not + * constant. */ + timeout = 50000000000U / coreclk(); + p1 = thread_create(t1_stack, sizeof(t1_stack), THREAD_PRIORITY_MAIN + 1, THREAD_CREATE_WOUT_YIELD | THREAD_CREATE_STACKTEST, thread_1_2_3, (void *)t1_name, t1_name); @@ -92,8 +104,11 @@ int main(void) thread_1_2_3, (void *)t3_name, t3_name); puts("THREADS CREATED\n"); + printf("Context switch every %" PRIu32 " µs\n", timeout); + timer.callback = timer_cb; - ztimer_set(ZTIMER_USEC, &timer, OFFSET); + timer.arg = &timeout; + ztimer_set(ZTIMER_USEC, &timer, timeout); return 0; }