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;