用 volatile 共享数据
本课属于 Coddy Arduino 学习旅程的“无需 delay 的计时”单元。第 14 课,共 18 课。
ISR 应该只做一件快速的事,所以它通常会设置一个变量,供草图的其余部分读取。两者都会使用的变量必须声明为 volatile:
volatile bool started = false;
void onPress() {
started = true;
}为了节省时间,编译器(将你的 C++ 转换成芯片指令的程序)可能会在处理器中保存一份变量副本,而不是读取内存。它不知道中断可以更改这个变量,因此草图可能会永远使用旧副本。volatile 会让程序每次都重新读取变量。
这个草图在 setup() 中使用 while 循环等待:只要条件为真,while (condition) { } 就会重复执行花括号中的内容,这里的条件是 !started。和“输入”单元中一样,! 表示非:当 started 为 false 时,循环会重复执行。没有 volatile,即使按下按钮后,它也可能永远等待。
挑战
简单这段程序应该等待按钮被按下,然后让 LED 闪烁。目前它会一直等待。请修复它:
- 在
onPress()中,将started设为true - 将
started声明为volatile
按“运行”,再按下按钮,然后按“检查”。试一次不使用 volatile,看看开发板会如何卡住。
亲手试试
const int ledPin = 13;
const int buttonPin = 2;
bool started = false; // 由中断设置,由程序读取
unsigned long previousMillis = 0;
int ledState = LOW;
void onPress() {
// 在这里将 started 设为 true
}
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(buttonPin), onPress, FALLING);
// 在这里等待,直到按钮被按下
while (!started) {
}
}
void loop() {
// 每隔 250 ms 闪烁一次
unsigned long now = millis();
if (now - previousMillis >= 250) {
previousMillis = now;
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(ledPin, ledState);
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
无需 delay 的计时中的所有课
自己练习: Arduino 在线编辑器