Skip to content

Commit

Permalink
cpu/stm32/periph_timer: fix race conditions
Browse files Browse the repository at this point in the history
Allow two threads to share the same timer - provided they use distinct
sets of timer channels - without occasionally corrupting registers or
state flags.
  • Loading branch information
maribu committed Nov 24, 2022
1 parent 26d5b7c commit 93c5755
Showing 1 changed file with 10 additions and 3 deletions.
13 changes: 10 additions & 3 deletions cpu/stm32/periph/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value)
return -1;
}

unsigned irqstate = irq_disable();
set_oneshot(tim, channel);

TIM_CHAN(tim, channel) = (value & timer_config[tim].max);
Expand All @@ -136,6 +137,7 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value)
#endif

dev(tim)->DIER |= (TIM_DIER_CC1IE << channel);
irq_restore(irqstate);

return 0;
}
Expand All @@ -147,6 +149,7 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags
return -1;
}

unsigned irqstate = irq_disable();
clear_oneshot(tim, channel);

if (flags & TIM_FLAG_SET_STOPPED) {
Expand All @@ -155,14 +158,11 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags

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;
Expand All @@ -171,6 +171,7 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags
if (flags & TIM_FLAG_RESET_ON_MATCH) {
dev(tim)->ARR = value;
}
irq_restore(irqstate);

return 0;
}
Expand All @@ -182,7 +183,9 @@ int timer_clear(tim_t tim, int channel)
return -1;
}

unsigned irqstate = irq_disable();
dev(tim)->DIER &= ~(TIM_DIER_CC1IE << channel);
irq_restore(irqstate);

#ifdef MODULE_PERIPH_TIMER_PERIODIC
if (dev(tim)->ARR == TIM_CHAN(tim, channel)) {
Expand All @@ -200,12 +203,16 @@ unsigned int timer_read(tim_t tim)

void timer_start(tim_t tim)
{
unsigned irqstate = irq_disable();
dev(tim)->CR1 |= TIM_CR1_CEN;
irq_restore(irqstate);
}

void timer_stop(tim_t tim)
{
unsigned irqstate = irq_disable();
dev(tim)->CR1 &= ~(TIM_CR1_CEN);
irq_restore(irqstate);
}

static inline void irq_handler(tim_t tim)
Expand Down

0 comments on commit 93c5755

Please sign in to comment.