"volatile" 关键字对于嵌入式系统有什么意义?

What is the significance of the "volatile" key word with respect to Embedded Systems?

最近一直在自学嵌入式系统编程。我观察到在声明变量时关键字 volatile 限定符的使用率相当高?

在嵌入式系统编程中声明变量时volatile有什么意义?

基本上应该在什么时候使用关键字。我确实阅读了有关编译器优化和关键字使用的内容。还有一些与内存映射寄存器有关的东西。

例如,我读了这个 Whosebug post 但我不明白它如何应用于嵌入式环境。更具体地说,我不明白什么时候应该使用关键字。我确实阅读了有关编译器优化和关键字使用的内容。还有一些跟内存映射寄存器相关的东西,但是不知道什么时候用

让我们看一个例子。当您查看 PIC 微控制器的 C 头文件时,您会看到许多元素被声明为 volatile:

extern volatile unsigned char           PORTB               @ 0x006;

如您所见,volatile 关键字禁用编译器优化。假设您编写了一个执行以下操作的程序:

PORTB = 0x00;            // set all of port B low
while (PORTB == 0x00);   // wait for any pin to get high
// do something else

当编译器优化此代码时,它会将第二行识别为无限循环:条件为真且在其主体内永远不会为假。因此, 无限循环之后的所有内容都不需要编译,因为它永远不会 运行。因此,编译器可能会决定不在生成的汇编代码中包含该部分代码。

然而,这个PORTB实际上链接到一个物理端口。它是一个 硬件 端口,其值可能会被外部电路改变。这意味着虽然循环看起来是无限的,但实际上并非如此。编译器不可能知道这一点。

这就是 volatile 的用武之地。当 PORTB 声明为 volatile 时,编译器不会根据 PORTB 的推理进行任何优化。它将假设其值随时可能因外部因素而改变。

volatile关键字主要是用来告诉编译器变量的值随时可能改变。它还告诉编译器不要对变量应用优化。我不是这方面的专家,但下面是我过去提到的很好的参考。

volatile is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time-without any action being taken by the code the compiler finds nearby. The implications of this are quite serious. However, before we examine them, let's take a look at the syntax.

参考:

让我换个角度来说,它与 const 关键字正好相反。 当编译器遇到任何变量的 const 限定符时,它会检查是否有任何函数或语句在初始化后被修改。因此标记错误。

volatile正好相反,这个变量可以被任何函数改变。因此编译器不应用优化。

由于中断的使用,您主要在嵌入式系统编程中看到这种情况,一些编程逻辑结构似乎是多余的。

在嵌入式系统世界中,volatile 关键字的关键方面之一是它表示可能随时更改的变量(例如 external/hardware 数据输入 - 例如ADC),因此编译器不得优化使用。

但具体来说,当与控制寄存器一起使用时,它表示读取访问实际上可能会更改数据!

作为一般经验法则,我建议在以下所有内容中使用 volatile 限定符:

  • 所有硬件寄存器访问(读写)
  • 所有可在多线程中访问的变量(尤其是中断处理程序)

注意:访问 volatile 不一定是原子的,因此您必须了解您的硬件和代码结构。

虽然关于优化的陈述是正确的,但对我来说似乎有点不清楚。这是真正发生的事情。

如果您不使用 volatile 关键字,C 可能会将该变量优化到它当时未使用的寄存器中。这将使汇编指令更少,代码执行速度更快。

例如,考虑以下...

extern int my_port;    // my_port is defined in a different module somewhere
                       // presumably a memory mapped hardware port
while (my_port > 0) {so stuff}

编译器可能会决定在实际的 while 语句之前只将 my_port 读入寄存器一次,然后每次测试 my_port 它都会查看寄存器而不是内存位置。

但是,如果 my_port 是一个硬件端口,端口可能会改变,但寄存器不会改变,while 条件也不会改变。

循环变量(寄存器)将"out of phase"与实际变量(my_port)。

因此需要关键字volatile。

Volatile告诉C,"Don't optimize this variable into a reg, but read it each and every time you need it."

生成的指令更多,代码有点慢,但始终准确。