【技术专辑】在Arduino上使用中断

使用Interrupts简化您的Arduino代码 - 对实时事件做出反应的简单方法!

 

推荐等级

 

中间

 

我们中断这个广播......

 

事实证明,所有Arduinos都内置了一个很好的(未充分利用的)机制,非常适合监控这些类型的实时事件。这种机制称为中断。中断的工作是确保处理器快速响应重要事件。当检测到某个信号时,中断(顾名思义)会中断处理器正在做的任何事情,并执行一些代码,用于对任何外部刺激进行反馈。一旦该代码完成,处理器就会回到原来的状态,好像什么也没发生!

 

令人敬畏的是,它构建了您的系统,以便快速有效地对软件中不易预测的重要事件做出反应。最重要的是,它可以让你的处理器在等待某个事件出现时做其他事情。 

 

按钮中断

 

让我们从一个简单的例子开始 - 使用中断来监控按下按钮。首先,我们将采用您之前可能已经看过的草图 - 所有Arduinos中包含的“Button”示例草图。(您可以在“示例”示意图中找到它。查看“文件>示例>数字>按钮”。)

                    const int buttonPin = 2;     // the number of the pushbutton pin

const int ledPin =  13;      // the number of the LED pin

 

// variables will change:

int buttonState = 0;         // variable for reading the pushbutton status

 

void setup() {

  // initialize the LED pin as an output:

  pinMode(ledPin, OUTPUT);

  // initialize the pushbutton pin as an input:

  pinMode(buttonPin, INPUT);

}

 

void loop() {

  // read the state of the pushbutton value:

  buttonState = digitalRead(buttonPin);

 

  // check if the pushbutton is pressed.

  // if it is, the buttonState is HIGH:

  if (buttonState == HIGH) {

    // turn LED on:

    digitalWrite(ledPin, HIGH);

  }

  else {

    // turn LED off:

    digitalWrite(ledPin, LOW);

  }

}

     

你在这里看到的并不令人震惊或惊人 - 所有程序正在做的,一遍又一遍,正在运行`loop()`,并读取`buttonPin`的值。假设你想在`loop()`中做一些其他事情 - 除了读取一个引脚之外。这就是Interrupts的用武之地。我们不是一直只看那个引脚,而是将监视该引脚的工作转移到中断,并释放`loop()`来做我们在此期间需要做的事情!新代码看起来像这样:

 

                    const int buttonPin = 2;     // the number of the pushbutton pin

const int ledPin =  13;      // the number of the LED pin

 

// variables will change:

volatile int buttonState = 0;         // variable for reading the pushbutton status

 

void setup() {

  // initialize the LED pin as an output:

  pinMode(ledPin, OUTPUT);

  // initialize the pushbutton pin as an input:

  pinMode(buttonPin, INPUT);

  // Attach an interrupt to the ISR vector

  attachInterrupt(0, pin_ISR, CHANGE);

}

 

void loop() {

  // Nothing here!

}

 

void pin_ISR() {

  buttonState = digitalRead(buttonPin);

  digitalWrite(ledPin, buttonState);

}

 

循环和中断模式

 

你会发现这里有一些变化。第一个,也是最明显的一个是`loop()`不包含任何指令!我们可以逃避这一点,因为之前由`if / else`语句完成的所有工作现在由一个新函数`pin_ISR()`处理。这种类型的函数称为_interrupt service routine_ - 它的工作是快速运行并处理中断并让处理器返回主程序(即`loop()`的内容)。编写中断服务程序时需要考虑一些重要事项,您可以在上面的代码中看到:

 

•ISR应该简短而甜蜜。你不想让主循环脱轨太久了!

 

•没有输入变量或返回值。必须对全局变量进行所有更改。 

 

你可能想知道 - 我们怎么知道中断何时运行?触发它的是什么?`setup()`例程中的第三个函数是为整个系统设置中断的。这个函数`attachInterrupt()`有三个参数:

 

1. 中断向量,确定哪个引脚可以产生中断。这不是引脚本身的数量 - 它实际上是对内存中Arduino处理器必须查看是否发生中断的位置的引用。该向量中的给定空间对应于特定的外部引脚,并非所有引脚都可以产生中断!在

Arduino Uno上,引脚2和3能够产生中断,它们分别对应于中断向量0和1。有关哪些引脚可用作中断引脚的列表,请查看`attachInterrupt()`上的Arduino文档。 

 

2. 中断服务程序的函数名称  - 它确定满足中断条件时运行的代码。

 

3. 中断模式,确定哪个引脚操作触发中断。Arduino Uno支持四种中断模式: 

 

  *`RISING`,在中断引脚的上升沿激活中断,

 

  *'FALLING`,在下降边缘激活,

 

  *`CHANGE`,响应中断引脚值的任何变化,

 

  *`LOW`,在引脚为数字低电平时触发。

 

回顾一下 - 我们设置`attachInterrupt()`设置我们监视中断向量0 /引脚2,使用`pin_ISR()`响应中断,并在我们看到任何变化时调用`pin_ISR()`针脚2上的状态。 

 

挥发性 - 不要摇晃!

 

还有一点需要指出 - 我们的ISR使用变量`buttonState`来存储引脚状态。查看`buttonState`的定义 - 而不是类型`int`,我们将其定义为`volatile int`类型。这是什么交易?`volatile`是一个应用于变量的C关键字。这意味着该变量的值不完全在程序的控制范围内。它反映了`buttonState`的值可以改变,并改变程序本身无法预测的东西 - 在这种情况下,用户输入。 

 

`volatile`关键字做的另一个有用的事情是保护我们免受任何意外的编译器优化。事实证明,编译器除了将源代码转换为机器可执行文件外,还有一些目的。他们的任务之一是从机器代码中修剪未使用的源代码变量。由于变量`buttonState`不是在`loop()`或`setup()`函数中直接使用或调用的,因此存在编译器可能将其作为未使用的变量删除的风险。显然,这是错误的 - 我们需要变量!`volatile`关键字有副作用,告诉编译器冷却其喷嘴并挂起该变量 - 这不是一个胖手指错误!

 

(旁白:从代码中修剪未使用的变量是编译器的一个特性,而不是错误。人们偶尔会在源代码中留下未使用的变量,这会占用内存。如果您正在编写C程序,这不是什么大问题。一台计算机,有几千兆字节的RAM。然而,在Arduino上,RAM是有限的,你不想浪费任何东西!即使是计算机上的C程序也会这样做,有大量的系统内存可用。为什么?同样的人们清理露营地的原因 - 最好不要留下垃圾!) 

 

结束

 

中断是一种简单的方法,可以使您的系统对时间敏感的任务更具响应性。它们还有一个额外的好处,就是可以释放主`loop()`来专注于系统中的一些主要任务。(我发现当我使用它时,这会使我的代码更有条理 - 更容易看到代码的主要代码是什么,而中断处理周期性事件。)此处显示的示例几乎是最多的使用中断的基本情况 - 您可以使用它们来读取I2C设备,发送或接收无线数据,甚至启动或停止电机。

 

还有其他方法可以实现中断; 特别是通过使用寄存器和寄存器掩码,您可以使用Arduino板的所有其他引脚。 

  • 【技术专辑】在Arduino上使用中断已关闭评论
    A+
发布日期:2019年03月04日  所属分类:参考设计