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

时间:2016-12-19 16:59:23

标签: timer arm atomic peripherals

我想在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]
  • 这看似合理吗?在执行IRQ处理程序期间,TIM3-> SR寄存器的内容是否会发生变化?
  • 是否有可能对TIM3-> SR寄存器进行原子读写?
  • 还有其他合适的解决方案吗?

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

1 个答案:

答案 0 :(得分:2)

reference manual(CD00171190)的 15.4.5 部分指出TIMx->SR中的所有位都在 rc_w0 模式下工作(或保留)

根据programming manual(PM0056):

  

读取/清除(rc_w0):软件可以通过写入0来读取和清除该位。写入“1”对位值没有影响。

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

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

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