对 ARM 外设寄存器的原子访问

Atomic access to ARM peripheral registers

我想在 ST2M32F103REY Cortex M3 上同时使用通用定时器的溢出、比较匹配和捕获功能。 CC1 配置为比较匹配,CC3 配置为捕获。 IRQ 处理程序如下所示:

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
  }
}

原则上还是可以的,就是有时会漏掉一部分。我的理论是,这是因为重置 IRQ 标志的操作,例如TIM3->SR &= ~TIM_SR_UIF,不是原子*,因此可能会发生例如加载和存储之间发生的 TIM_SR_CC1IF 被覆盖。

* 指令反汇编如下

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]

顺便说一下:There is similar question但是那个是关于保护多个进程或内核的访问,而不是关于保护软件和硬件的同时访问。

reference manual (CD00171190) 的 15.4.5 部分声明 TIMx->SR 中的所有位在 [=35= 中工作]模式(或保留)。

根据 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.

这意味着您可以简化代码以完全避免读取-修改-写入循环,而只需使用 TIM3->SR = ~TIM_SR_UIF

许多应用笔记使用读取-修改-写入来清除中断,例如 Keil 的示例,但正如您所经历的那样,这是不必要的并且有潜在的危险。 在ST应用笔记DM00236305(第1.3.2节)中,只使用了写操作。

但是请注意,当使用 NVIC 时,用于重置的寄存器是 rc_w1.