在本教程中,您将学习如何使用 Arduino IDE 将中断和计时器与 ESP8266 NodeMCU 一起使用。中断允许您检测 GPIO 状态的变化,而无需不断检查其当前值。使用中断时,当检测到更改时,会触发事件(调用函数)。
例如,我们将使用 PIR 运动传感器检测运动:当检测到运动时,ESP8266会启动计时器并打开 LED 预定义的秒数。当计时器完成倒计时时,LED 会自动熄灭。
要创建中断,请调用 attachInterrupt() 并将 GPIO 中断引脚、ISR(要调用的函数)和模式作为参数传递。ISR 函数必须声明 ICACHE_RAM_ATTR 属性。模式可以是 CHANGE、RISING 或 FALLING。
attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);
在继续本教程之前,您应该在Arduino IDE中安装ESP8266附加组件。按照本教程在 Arduino IDE 中安装 ESP8266(如果尚未安装)。
目录
引入ESP8266中断
中断对于在微控制器程序中自动执行操作非常有用,并且有助于解决时序问题。
使用中断,您无需经常检查当前引脚值。当检测到更改时,会触发一个事件 – 调用一个函数。此函数称为中断服务例程 (ISR)。
当发生中断时,处理器停止主程序的执行以执行任务,然后返回主程序,如下图所示。
这对于在检测到运动或按下按钮时触发操作特别有用,而无需不断检查其状态。
attachInterrupt() 函数
要在 Arduino IDE 中设置中断,请使用 attachInterrupt() 函数,该函数接受以下参数:GPIO 中断引脚、要执行的函数名称和模式:
attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);
GPIO中断引脚
第一个参数是 GPIO 中断。您应该使用 digitalPinToInterrupt(GPIO) 将实际的 GPIO 设置为中断引脚。例如,如果要使用 GPIO 14 作为中断,请使用:
digitalPinToInterrupt(14)
ESP8266支持除 GPIO16 以外的任何 GPIO 中的中断。
ISR
attachInterrupt() 函数的第二个参数是每次触发中断时都会调用的函数的名称——中断服务程序 (ISR)。
ISR 函数应尽可能简单,以便处理器快速返回主程序的执行。
最好的方法是使用全局变量向主代码发出中断已发生的信号,并在 loop() 中检查并清除该标志,然后执行代码。
ISR 需要在函数定义之前ICACHE_RAM_ATTR才能在 RAM 中运行中断代码。
中断模式
第三个参数是模式,有 3 种不同的模式:
- CHANGE:每当引脚值发生变化时触发中断——例如ample 从 HIGH 到 LOW 或 LOW 到 HIGH;
- FALLING:当引脚从 HIGH 变为 LOW 时;
- 上升:当引脚从低电平变为高电平时触发。
对于我们的示例,将使用 RISING 模式,因为当 PIR 运动传感器检测到运动时,它所连接的 GPIO 会从 LOW 变为 HIGH。
ESP8266计时器简介
在本教程中,我们将使用计时器。我们希望 LED 在检测到运动后保持亮起预定的秒数。我们将使用计时器,而不是使用 delay() 函数来阻塞您的代码并且不允许您在确定的秒数内执行任何其他操作。
delay() 与 millis()
delay() 函数接受单个 int 数作为参数。此数字表示程序必须等待的时间(以毫秒为单位),直到移动到下一行代码。
delay(time in milliseconds);
当您调用 delay(1000) 时,您的程序会在该行上停止 1 秒钟。delay() 是一个阻塞函数。阻塞功能可防止程序在完成该特定任务之前执行任何其他操作。如果需要同时执行多个任务,则不能使用 delay()。对于大多数项目,您应该避免使用undefined,而是使用计时器。
使用名为 millis() 的函数,您可以返回自程序首次启动以来经过的毫秒数。
millis();
为什么这个函数有用?因为通过使用一些数学运算,您可以轻松地验证已经过去了多少时间,而不会阻塞您的代码。
使用 millis() 闪烁 LED(无undefined)
如果您不熟悉 millis() 函数,我们建议您阅读本节。如果您已经熟悉计时器,则可以跳到 PIR 运动传感器项目。
以下代码片段展示了如何使用 millis() 函数创建 blink 项目。它会将 LED 打开 1000 毫秒,然后将其关闭。
/*********
更多Arduino/ESP8266/ESP32等教程请访问: https://www.qutaojiao.com
*********/
// constants won't change. Used here to set a pin number :
const int ledPin = 26; // the number of the LED pin
// Variables will change :
int ledState = LOW; // ledState used to set the LED
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0; // will store last time LED was updated
// constants won't change :
const long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop() {
// here is where you'd put code that needs to be running all the time.
// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
代码的工作原理
让我们仔细看看这个没有 delay() 函数的眨眼程序(它使用 millis() 函数代替)。
基本上,此代码从当前时间(currentMillis)中减去先前记录的时间(previousMillis)。如果余数大于间隔(在本例中为 1000 毫秒),则程序会将 previousMillis 变量更新为当前时间,并打开或关闭 LED。
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
(...)
由于此代码段是非阻塞的,因此位于第一个 if 语句之外的任何代码都应正常工作。
您现在应该能够理解,您可以将其他任务添加到您的 loop() 函数中,并且您的代码仍将每秒闪烁一次 LED。
您可以将此代码上传到ESP8266进行测试。板载 LED 应每秒闪烁一次。
ESP8266 带 PIR 运动传感器的 NodeMCU
在本节中,你将了解如何在代码中使用中断和计时器通过 PIR 运动传感器检测运动。
所需零件
下面是完成本教程所需的部分列表:
- ESP8266
- 被动红外运动传感器 (AM312)
- 5mm LED
- 330欧姆电阻
- 面包板
- 连接线
原理图
将 PIR 运动传感器和 LED 组装到您的ESP8266。我们将 LED 连接到 GPIO 12 (D6),将 PIR 运动传感器数据引脚连接到 GPIO 14 (D5)。
重要提示:本项目中使用的 Mini AM312 PIR 运动传感器的工作电压为 3.3V。但是,如果您使用另一个 PIR 运动传感器,例如 HC-SR501,它会在 5V 下运行。您可以将其修改为在 3.3V 下工作,也可以直接使用 Vin 引脚为其供电。
下图显示了 AM312 PIR 运动传感器引脚排列。
使用方法
按照原理图所示对电路进行接线后,将提供的代码复制到Arduino IDE。
您可以按原样上传代码,也可以修改检测到运动后 LED 亮起的秒数。只需将 timeSeconds 变量更改为所需的秒数即可。
代码的工作原理
让我们看一下代码。
首先,将两个 GPIO 引脚分配给 led 和 motionSensor 变量。
const int led = 12;
const int motionSensor = 14;
然后,创建变量,允许您设置计时器以在检测到运动后关闭 LED。
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;
now 变量保存当前时间。lastTrigger 变量保存 PIR 传感器检测到运动的时间。startTimer 是一个布尔变量,用于在检测到运动时启动计时器。
setup()
在 setup() 中,首先以 115200 波特率初始化串行端口。
Serial.begin(115200);
将 PIR 运动传感器设置为INPUT_PULLUP。
pinMode(motionSensor, INPUT_PULLUP);
要将 PIR 传感器引脚设置为中断,请使用 attachInterrupt() 函数,如前所述。
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
检测运动的引脚是 GPIO 14,它将在 RISING 模式下调用函数 detectsMovement()。
LED 是 OUTPUT,其状态从 LOW 开始。
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
loop()
loop() 函数不断地一遍又一遍地运行。在每个循环中,now 变量都会使用当前时间进行更新。
now = millis();
在 loop() 中没有执行任何其他操作。但是,当检测到运动时,会调用 detectsMovement() 函数,因为我们之前在 setup() 中设置了中断。
detectsMovement() 函数在串行监视器中打印消息,打开 LED,将 startTimer 布尔变量设置为 true,并使用当前时间更新 lastTrigger 变量。
ICACHE_RAM_ATTR void detectsMovement() {
Serial.println("MOTION DETECTED!!!");
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}
完成此步骤后,代码将返回到 loop()。这一次,startTimer 变量为 true。因此,当以秒为单位定义的时间过去时(自检测到运动以来),以下 if 语句将为真。
if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
Serial.println("Motion stopped…");
digitalWrite(led, LOW);
startTimer = false;
}
“动议停止……”消息将打印在串行监视器中,LED 熄灭,并且 startTimer 变量设置为 false。
示范
将代码上传到您的ESP8266。确保您选择了正确的主板和 COM 端口。
以 115200 的波特率打开串行监视器。
将手移到 PIR 传感器前面。LED 应亮起,并在串行监视器中打印一条消息,提示“检测到运动!!”。10 秒后,LED 应熄灭。
总结
总而言之,中断可用于检测 GPIO 状态的变化并立即触发函数。您还了解到,您应该使用计时器来编写非阻塞代码。
学习学习