ESP8266-Arduino编程实例-中断

中断

本文将演示如何使用 ESP8266 NodeMCU 的中断和定时器。 中断检测 GPIO 状态的变化且无需不断检查其当前值。通过使用中断,当检测到更改时,会触发事件(调用函数)。

在实例中,使用 PIR 运动传感器检测运动:当检测到运动时,ESP8266 会启动一个定时器并打开 LED 预定义的秒数。 当计时器完成倒计时,LED 会自动关闭。

1、ESP8266中断介绍

中断对于在微控制器程序中使事情自动发生很有用,并且可以帮助解决时序问题。

使用中断,您无需不断检查当前引脚值。 当检测到变化时,会触发一个事件——调用一个函数。 该函数称为中断服务程序(ISR)。

当中断发生时,处理器停止执行主程序执行任务,进入中断服务程序,中断服务程序执行完成后再返回到主程序,如下图所示。

在这里插入图片描述

ESP8266 支持任何 GPIO 中的中断,但 GPIO16 除外。

要创建中断,需要调用 attachInterrupt() 并将 GPIO 中断引脚、ISR(要调用的函数)和模式作为参数传递。 ISR 函数必须声明 ICACHE_RAM_ATTR 属性。 模式可以是 CHANGERISINGFALLING

1
2
attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);

函数的第一个参数是 GPIO 中断。需要应该使用 digitalPinToInterrupt(GPIO)函数 将实际的 GPIO 设置为中断引脚。 例如,将 GPIO 14 作为中断:

1
2
digitalPinToInterrupt(14)

函数的第二个参数是中断服务程序ISR(Interrupt Service Routine)。ISR 功能应该尽可能简单,以便处理器快速返回主程序的执行。

最好的方法是使用全局变量向主代码发出中断已经发生的信号,并在 loop() 中检查并清除该标志,然后执行代码。

ISR 需要在函数定义之前有 IRAM_ATTR 才能在 RAM 中运行中断代码。

函数的第三个参数是中断触发模式。ESP8266支持以下三种中断触发模式:

  • CHANGE:每当引脚改变值时触发中断——例如从高到低或从低到高;
  • FALLING:当引脚从 HIGH 变为 LOW 时;
  • RISING:当引脚从 LOW 变为 HIGH 时触发。

在本次演示实例中,将使用 RISING 模式,因为当 PIR 运动传感器检测到运动时,它所连接的 GPIO 会从 LOW 变为 HIGH。

2、硬件准备

  • ESP8266 NodeMCU开发板一块
  • PIR运动传感器一个
  • LED一个
  • 220欧姆电阻一个
  • 杜邦线若干
  • 数据线一条

3、软件准备

  • Arduino IDE或VSCode + PlatformIO

在前面的文章中,对如何搭建ESP8266开发环境做了详细的介绍,请参考:

ESP8266 NodeMCU的引脚介绍在前面的文章中做了详细的介绍,请参考:

4、硬件电路接线图

在这里插入图片描述

引脚D5(GPIO14)与PIR的信号引脚连接;引脚D6(GPIO12)与LED引脚连接。

5、代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <Arduino.h>

#define timeSeconds 10

// 设置GPIO
const int led = 12;
const int motionSensor = 14;

// 定时器:辅助变量
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;

// 中断服务:检查是否检测到运动,将 LED 设置为高电平并启动计时器
IRAM_ATTR void detectsMovement() {
Serial.println("MOTION DETECTED!!!");
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}

void setup() {
// 设置串口
Serial.begin(115200);

// 将PIR引脚设置为上拉输入
pinMode(motionSensor, INPUT_PULLUP);
// 将PIR引脚设置为中断,分配中断功能并设置RISING模式
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

// 设置LED引脚为输出
pinMode(led, OUTPUT);
// 关闭LED
digitalWrite(led, LOW);
}

void loop() {
// 获取当前时间
now = millis();
// 在 达到 timeSeconds 变量中定义的秒数后关闭 LED
if(startTimer && (now - lastTrigger > (timeSeconds\*1000))) {
Serial.println("Motion stopped...");
digitalWrite(led, LOW);
startTimer = false;
}
}

6、代码解析

首先,定义引脚

1
2
3
const int led = 12;
const int motionSensor = 14;

接下来,创建计数辅助变量

1
2
3
4
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;

变量now储存当前时间;变量lastTrigger保存PIR传感器最后触发时间;变量startTimer标志当前计时器时运是否开始,当PIR触发时,设置true,计时完成后设置为false。

接下来,定义中断服务函数

1
2
3
4
5
6
7
ICACHE_RAM_ATTR void detectsMovement() {
Serial.println("MOTION DETECTED!!!");
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}

接下来,在setup函数中设置串口

1
2
Serial.begin(115200);

设置PIR引脚和LED引脚

1
2
pinMode(motionSensor, INPUT_PULLUP);

设置GPIO中断服务

1
2
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

设置LED

1
2
3
4
5
// 设置LED引脚为输出
pinMode(led, OUTPUT);
// 关闭LED
digitalWrite(led, LOW);

最后,在loop函数中,进行计数

1
2
3
4
5
6
if(startTimer && (now - lastTrigger > (timeSeconds\*1000))) {
Serial.println("Motion stopped…");
digitalWrite(led, LOW);
startTimer = false;
}

文章来源: https://iotsmart.blog.csdn.net/article/details/126006866