From 84557566187c0c396b845f0f9e0fb8d9eb44e2cb Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 30 Oct 2020 13:06:12 +0100 Subject: [PATCH] cpu/sam3/periph/timer: fix trigger of cleared timer --- cpu/sam3/include/periph_cpu.h | 9 +++++++-- cpu/sam3/periph/timer.c | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cpu/sam3/include/periph_cpu.h b/cpu/sam3/include/periph_cpu.h index da7d987605..7f28e5ef56 100644 --- a/cpu/sam3/include/periph_cpu.h +++ b/cpu/sam3/include/periph_cpu.h @@ -66,9 +66,14 @@ typedef uint32_t gpio_t; #define TIMER_MAX_VAL (0xffffffff) /** - * @brief We use 3 channels for each defined timer + * @brief We use one channel for each defined timer + * + * While the peripheral provides three channels, the current interrupt + * flag handling leads to a race condition where calling timer_clear() on one + * channel can disable a pending flag for other channels. + * Until resolved, limit the peripheral to only one channel. */ -#define TIMER_CHANNEL_NUMOF (3) +#define TIMER_CHANNEL_NUMOF (1) /** * @brief The RTT width is fixed to 32-bit diff --git a/cpu/sam3/periph/timer.c b/cpu/sam3/periph/timer.c index b0ba1ceeed..95a9129d78 100644 --- a/cpu/sam3/periph/timer.c +++ b/cpu/sam3/periph/timer.c @@ -128,6 +128,14 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value) return -1; } (&dev(tim)->TC_CHANNEL[0].TC_RA)[channel] = value; + + /* read TC status register to clear any possibly pending + * ISR flag (that has not been served yet). + * timer_clear() disables the interrupt, but does not clear the flags. + * if we don't clear them here, re-enabling the interrupt below + * can trigger for the previously disabled timer. */ + (void)dev(tim)->TC_CHANNEL[0].TC_SR; + dev(tim)->TC_CHANNEL[0].TC_IER = (TC_IER_CPAS << channel); return 0;