对 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]
- 这合理吗? TIM3->SR寄存器的内容能否在IRQ handler执行过程中改变?
- 是否可以对 TIM3->SR 寄存器进行原子读写?
- 还有其他合适的解决方案吗?
顺便说一下: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.
我想在 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]
- 这合理吗? TIM3->SR寄存器的内容能否在IRQ handler执行过程中改变?
- 是否可以对 TIM3->SR 寄存器进行原子读写?
- 还有其他合适的解决方案吗?
顺便说一下: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.