static void RadioReleaseSPI(void) {
__disable_interrupt();
spiTxRxByteCount &= ~0x0100;
__enable_interrupt();
}
我知道多个任务可能会尝试使用SPI资源。 spiTxRxByteCount
是一个全局变量,用于跟踪SPI当前是否正被另一个任务使用。当任务需要SPI时,它可以检查spiTxRxByteCount
的状态以查看是否正在使用SPI。当使用SPI完成任务时,它会调用此函数并清除该位,以指示SPI现在是空闲的。但为什么要先禁用中断然后再重新启用呢?只是偏执狂?
答案 0 :(得分:15)
& =将执行读 - 修改 - 写操作 - 它不是原子的。你不希望中断在中间改变,导致写入覆盖的值不正确。
答案 1 :(得分:9)
您需要禁用中断以确保原子访问。您不希望任何其他进程在您阅读时访问并可能修改该变量。
来自Introduction to Embedded Computing:
原子访问需求
想象一下这个场景:前台程序,在8位uC上运行, 需要检查一个16位变量,称之为X.所以它加载高 字节然后加载低字节(或其他方式,顺序 没关系),然后检查16位值。现在想象一下 用相关的ISR中断,修改该16位变量。 进一步想象变量的值恰好是0x1234 at 程序执行中的给定时间。这是非常糟糕的事情 可能发生的事情:
- 前景加载高字节(0x12)
- 发生ISR,将X修改为0xABCD
- 前景加载低字节(0xCD)
- 前台程序看到16位值0x12CD。
问题在于,我们的数据是一个不可分割的数据 变量X,实际上是在访问过程中修改过的, 因为访问变量的CPU指令是可分的。 因此我们对变量X的加载已被破坏。你可以看到 变量读取的顺序无关紧要。如果订单是 在我们的示例中反转,该变量将被错误地读取 为0xAB34而不是0x12CD。无论哪种方式,读取的值都不是 旧的有效值(0x1234)也不是新的有效值(0xABCD)。
编写ISR引用的数据并不是更好。这次假设了 为了ISR的利益,前台程序已经编写了 先前的值为0x1234,然后需要写入一个新值0xABCD。在 这种情况下,VBT如下:
- 前景存储新的高字节(0xAB)
- 发生ISR,将X读为0xAB34
- 前景存储新的低字节(0xCD)
代码(这次是ISR)再次看不到前一个 有效值为0x1234,也不是新的有效值0xABCD,而是 无效值0xAB34。
虽然spiTxRxByteCount &= ~0x0100;
可能看起来像C中的单个指令,但它实际上是CPU的几条指令。在GCC中编译,汇编列表如下所示:
57:atomic.c **** spiTxRxByteCount &= ~0x0100;
68 .loc 1 57 0
69 004d A1000000 movl _spiTxRxByteCount, %eax
69 00
70 0052 80E4FE andb $254, %ah
71 0055 A3000000 movl %eax, _spiTxRxByteCount
71 00
如果在这些指令之间插入中断并修改数据,则第一个ISR可能会读取错误的值。因此,您需要在操作之前禁用中断,并声明变量volatile
。