I want to use the overflow, compare match and capture functionality of a general purpose timer on a ST2M32F103REY Cortex M3 at the same time. CC1 is configured as compare match and CC3 is configured as capture. The IRQ handler looks as follows:
void TIM3_IRQHandler(void) {
if(TIM3->SR & TIM_SR_UIF){
TIM3->SR &= ~TIM_SR_UIF;
// do something on overflow
}
if(TIM3->SR & TIM_SR_CC1IF) {
TIM3->SR &= ~TIM_SR_CC1IF;
// do something on compare match
}
if(TIM3->SR & TIM_SR_CC3IF) {
TIM3->SR &= ~TIM_SR_CC3IF;
// do something on capture
}
}
In principle, it works good, but it sometimes seems to skip a part. My theory is that this is because the operations of resetting the IRQ flags, e.g. TIM3->SR &= ~TIM_SR_UIF
, is not atomic*, so it might happen that for example a TIM_SR_CC1IF occurring between load and store is overwritten.
* The disassembly of the instruction is as follows
8012e02: 8a13 ldrh r3, [r2, #16]
8012e06: f023 0301 bic.w r3, r3, #1
8012e0a: 041b lsls r3, r3, #16
8012e0c: 0c1b lsrs r3, r3, #16
8012e0e: 8213 strh r3, [r2, #16]
By the way: There is similar question but that one is about protecting access by multiple processes or cores and not about protecting simultaneous access by software and hardware.
Section 15.4.5 of the reference manual (CD00171190) states that all bits in TIMx->SR
work in rc_w0 mode (or are reserved).
According to the programming manual (PM0056):
read/clear (rc_w0): Software can read as well as clear this bit by writing 0. Writing ‘1’ has no effect on the bit value.
This means that you can simplify your code to entirely avoid the read-modify-write cycle and instead just use TIM3->SR = ~TIM_SR_UIF
instead.
Many application notes use a read-modify-write to clear interrupts, such as examples by Keil, but this is unnecessary and potentially dangerous, as you have experienced. In the ST application note DM00236305 (section 1.3.2), only a write operation is used.
Note, however, that when working with the NVIC, the register used for resetting is rc_w1.