Volatile:
Volatile是一个被称为变量限定符的关键字,它通常被用在变量的数据类型之前,用来修改编译器和后续程序处理变量的方式。
声明volatile变量是对编译器的指令。编译器是把你的C/ c++代码翻译成机器码的软件,机器码是Arduino中Atmega芯片的真正指令。
具体来说,它指示编译器从RAM而不是从存储寄存器加载变量,存储寄存器是存储和操作程序变量的临时内存位置。在某些条件下,存储在寄存器中的变量的值可能是不准确的。
当变量的值可能被超出其所在代码段的控制范围的东西(例如并发执行的线程)改变时,就应该将其声明为volatile。在Arduino中,唯一可能发生这种情况的地方是与中断相关的代码部分,称为中断服务例程。
int or long volatiles:
如果volatile变量大于一个字节(例如16位int或32位long),那么微控制器(MCU根据自己的Arduino具体型号确定,这里使用Arduino Mega 2560,它是8位的MCU)不能一步读取它,因为它是8位微控制器。这意味着当你的主代码部分(例如循环)读取变量的前8位时,中断可能已经改变了后8位。这将为变量产生随机值。
解决方案:
当变量被读取时,中断需要被禁用,这样它们就不会在被读取时打乱位。有几种方法可以做到这一点:
LANGUAGE noInterrupts
use the ATOMIC_BLOCK macro.
原子操作是单MCU操作-最小的可能单元
Example Code:
volatile修饰符确保对状态变量的更改在loop()中立即可见。如果没有volatile修饰符,状态变量可以在进入函数时加载到寄存器中,并且在函数结束之前不会再更新。
// Flashes the LED for 1 s if the input has changed
// in the previous second.
volatile byte changed = 0;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(2), toggle, CHANGE);
}
void loop() {
if (changed == 1) {
// toggle() has been called from interrupts!
// Reset changed to 0
changed = 0;
// Blink LED for 200 ms
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
}
}
void toggle() {
changed = 1;
}
要访问一个比微控制器的8位数据总线大的变量,使用ATOMIC_BLOCK宏。宏确保在原子操作中读取变量,也就是说,在读取变量时,不能修改变量的内容。
#include <util/atomic.h> // this library includes the ATOMIC_BLOCK macro.
volatile int input_from_interrupt;
// Somewhere in the code, e.g. inside loop()
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// code with interrupts blocked (consecutive atomic operations will not get interrupted)
int result = input_from_interrupt;
}