为什么在此禁用中断?

时间:2012-11-28 20:52:33

标签: c embedded rtos

static void RadioReleaseSPI(void) {
    __disable_interrupt();
    spiTxRxByteCount &= ~0x0100;
    __enable_interrupt();
}

我知道多个任务可能会尝试使用SPI资源。 spiTxRxByteCount是一个全局变量,用于跟踪SPI当前是否正被另一个任务使用。当任务需要SPI时,它可以检查spiTxRxByteCount的状态以查看是否正在使用SPI。当使用SPI完成任务时,它会调用此函数并清除该位,以指示SPI现在是空闲的。但为什么要先禁用中断然后再重新启用呢?只是偏执狂?

2 个答案:

答案 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