我正在编写嵌入式固件,有时很难决定何时需要易失性。
当我有一个等待某个布尔标志被中断更改的函数时,很明显该标志需要是可变的,因为否则该函数将永远等待,因为编译器没有意识到该值可以通过打断。
但是当我有一个只在第一行检查一个标志的简短函数时,我希望该标志不需要是易失的,因为每次我进入函数时都会读取它的值?所以当一个中断在我第一次调用函数和第二次之间修改它的值时,我会得到新的值。还是不保证每次我进入函数时所有缓存寄存器都被清除?
你仍然需要标记你的变量volatile
:因为优化器可以自由地内联你的函数,尤其是短函数,所以在没有标记的情况下在循环中调用你的函数volatile
来访问硬件修改的内存会使你有在之后不读取内存的危险初始迭代。
...因为每次我进入函数时都会读取它的值?
不,对此没有任何保证。“缺少易失性错误”的问题在于编译器的优化器不知道某个变量可以从外部源更改,从而改变了代码的整体含义。
所以如果你有这个:
static int x=0;
int func (void)
{
if(x == 0)
{
return 1;
}
else
{
return 0;
}
}
interrupt void isr (void)
{
x = SOMETHING;
}
然后编译器会思考:“嗯,x 永远不会在任何地方修改,因为“isr”永远不会从程序中调用。所以 x 始终为 0,我将代码优化为:
int func (void)
{
return 1;
}
然后,也许,它将内联整个函数。发生与否无关紧要,因为代码的含义已经在前面的优化步骤中被破坏了。
任何与中断(或线程、DMA、硬件寄存器或回调函数)共享的变量都必须始终声明为易失性。
对硬件寄存器的任何访问都最好标记为易失性。编译器不知道它将通过硬件的中断或 DMA 进行更改,并且编译器可以并且将假设它不会,因此它可以并且将缓存某些值。
本质上,如果它是硬件映射的或可以通过中断(从硬件)更改,则将其标记为易失性。
除了将变量标记为 volatile 以强制加载(正如@dasblinkenlight 建议的那样),您还应该采取措施确保以原子方式读取(和写入)变量。在某些特定大小对象的平台上(例如最近的 x86 处理器上的 32 位值),这会自动发生。通常,您可能需要在变量周围放置一个同步锁,例如互斥锁或信号量。当异步代码是线程时,这相对容易做到。我不确定当涉及真正的中断时该怎么做,因为某些同步技术可能是不可能的。您的平台文档应该在这里提供一些见解。
应该声明所有共享内存volatile
。
您的特定编译器不会优化特定示例中的读取可能是正确的,也可能不是正确的,但在这种情况下,volatile
关键字会增加零开销(即,在指定显式读取将在任何情况下发生读取时没有开销)案例),那么为什么要冒未定义或不可移植的行为呢?