static void RadioReleaseSPI(void) {
__disable_interrupt();
spiTxRxByteCount &= ~0x0100;
__enable_interrupt();
}
Run Code Online (Sandbox Code Playgroud)
我知道多个任务可能会尝试使用SPI资源.spiTxRxByteCount是一个全局变量,用于跟踪SPI当前是否正被另一个任务使用.当任务需要SPI时,它可以检查状态spiTxRxByteCount以查看是否正在使用SPI.当使用SPI完成任务时,它会调用此函数并清除该位,以指示SPI现在是空闲的.但为什么要先禁用中断然后再重新启用呢?只是偏执狂?
您需要禁用中断以确保原子访问.您不希望任何其他进程在您阅读时访问并可能修改该变量.
从简介到嵌入式计算:
对原子访问的需求
想象一下这个场景:在8位uC上运行的前台程序需要检查一个16位变量,称之为X.所以它加载高字节然后加载低字节(或者相反,顺序没有没关系,然后检查16位值.现在假设一个带有相关ISR的中断修改了这个16位变量.进一步想象在程序执行的给定时间,变量的值恰好是0x1234.以下是可能发生的非常糟糕的事情:
- 前景加载高字节(0x12)
- 发生ISR,将X修改为0xABCD
- 前景加载低字节(0xCD)
- 前台程序看到16位值0x12CD.
问题在于,在访问变量的过程中,实际上修改了一个不可分割的数据,我们的变量X,因为访问变量的CPU指令是可分的.因此我们对变量X的加载已被破坏.您可以看到变量读取的顺序无关紧要.如果在我们的示例中顺序相反,则该变量将被错误地读取为0xAB34而不是0x12CD.无论哪种方式,读取的值既不是旧的有效值(0x1234)也不是新的有效值(0xABCD).
编写ISR引用的数据并不是更好.这次假设为了ISR的利益,前台程序已经写入了先前的值0x1234,然后需要写入一个新的值0xABCD.在这种情况下,VBT如下:
- foreground存储新的高字节(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
Run Code Online (Sandbox Code Playgroud)
如果在这些指令之间插入中断并修改数据,则第一个ISR可能会读取错误的值.因此,您需要在操作之前禁用中断并声明变量volatile.
| 归档时间: |
|
| 查看次数: |
2615 次 |
| 最近记录: |