Arduino 中的中断
什么是中断?
顾名思义,中断是打断正常代码流程的例程。中断例程包含一段代码,当发生事件时,板上的微控制器应该执行这段代码。以空调为例。假设它有以下温度控制设置:当温度达到 18 摄氏度时,关闭冷却功能。现在,将有一个温度传感器不断测量温度。每当它报告温度为 18 摄氏度时,空调微控制器上运行的正常代码就会被中断,它会执行关闭冷却功能的代码,然后恢复正常代码。
中断何时触发?
触发中断的方法有很多。您可以通过按下按钮来触发中断(基本上是当引脚状态从高电平到低电平或从低电平到高电平发生变化时)。您可以使用基于定时器的中断,这些中断以固定的时间间隔触发,您可以通过 UART、SPI 或 Wire 等接收数据时触发中断,等等。可用的中断列表可以在这里找到:http://gammon.com.au/interrupts
定义中断服务例程
每当发生中断事件时执行的代码存储在一个特殊类型的函数中,称为中断服务例程。它们有以下限制:
- 它们不应接收任何参数/参数
- 它们不能返回任何内容
如果代码中有多个中断,每个中断都有自己的中断服务例程 (ISR),那么一次只能运行一个 ISR。另外,请注意以下内容(来自 Arduino 文档):
- millis() 在 ISR 内部不会递增,因为它依赖于中断来递增自身。由于两个 ISR 不能同时运行,因此 millis 不能在 ISR 内部递增
- 同样,delay() 也不适用于中断,因为它需要中断才能工作
- delayMicroseconds() 不需要中断才能工作,因此它将在中断内正常工作
- micros() 最初可以工作,但在 1-2 毫秒后行为不可预测
在 ISR 内更改变量
如果希望在 ISR 内更改变量的值,则需要将变量声明为 volatile。
因此,
int p1 = 1;
变成
volatile int p1 = 1;
示例实现
我们将考虑按钮中断。更具体地说,是在检测到某个引脚上的上升沿或下降沿时触发的中断。现在,并非板的所有引脚都可以用于中断代码。每个板都有一些特定的引脚保留用于外部中断。列表可以在这里找到:https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
从上面的链接可以看出,Arduino Uno 的引脚 2 和 3 可用于外部中断。
现在,中断应该非常快地执行。因此,我们在中断内部要做的只是设置一个标志。然后在循环中,每当标志被设置时,我们都会打印一个语句,然后将标志重新设置为 0。
最重要的主要函数是 attachInterrupt()。
语法如下:
语法
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)
这里,引脚是引脚号,ISR 是 ISR 函数的名称,并且模式可以是以下之一:
- RISING:每当在引脚上看到低电平到高电平的转换时
- FALLING:每当在引脚上看到高电平到低电平的转换时
- LOW:每当引脚处于低电压时
- CHANGE:每当引脚看到电压变化(高电平到低电平或低电平到高电平)时
在某些开发板上(Due、MKR1000 和 Zero),您也可以使用 HIGH 模式:每当引脚处于高电压时。
电路图和示例代码如下所示。
电路图
从下面的电路图可以看出,每当按下按钮开关时,引脚 2 就会连接到 GND,并且会在该引脚上观察到下降沿(高电平到低电平),因为它通常被上拉(模式 INPUT_PULLUP)。此下降沿将生成中断。
示例
const int interruptPin = 2; volatile bool isButtonPressed = false; void setup() { Serial.begin(9600); pinMode(interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), button_pressed, FALLING); } void loop() { if(isButtonPressed){ Serial.println("Button Pressed!"); isButtonPressed = false; } } void button_pressed() { isButtonPressed = true; }