您需要禁用中断以确保原子访问。您不希望任何其他进程在您阅读它时访问并可能修改该变量。
从介绍到嵌入式计算:
原子访问的需求
想象一下这种情况:在 8 位 uC 上运行的前台程序需要检查一个 16 位变量,将其称为 X。因此它先加载高字节,然后再加载低字节(或者相反,顺序不没关系),然后检查 16 位值。现在想象一个带有修改该 16 位变量的关联 ISR 的中断。进一步想象,在程序执行的给定时间,变量的值恰好是 0x1234。这是可能发生的非常糟糕的事情:
- 前台加载高字节(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
。