中断的定义
程序运行过程中时常需要监控一些事件的发生,如对某一传感器的检测结果做出反应。使用轮询的方式进行检测时效率较低,等待时间较长,而使用中断方式进行检测时则可以达到实时检测的效果。
当中断被触发时,控制器会暂停当前正在运行的主程序,而跳去运行中断程序,当中断程序运行完后,会再回到之前主程序暂停的位置,继续运行主程序。如此便可以达到实时响应处理事件的效果。
外部中断是由外部设备发起请求的中断。要想使用外部中断,就需要了解中断引脚的位置,根据外部设备选择中断模式,以及编写一个中断被触发后需要执行的中断函数。
中断引脚
ARDUINO中的外部中断通常是由Pin口电平改变触发的。每种型号的ARDUINO板都有数个PIn口可以用来注册中断
常见开发板外部引脚图:常见开发板引脚图
中断模式
为了设置中断模式,还需要了解设备触发外部中断的输入 信号类型。中断模式也就是中断触发的方式。在大多数ARDUINO上支持以下几个中断触发方式
LOW 低电平触发
CHANGE 电平变化触发
RISING 上升沿触发,即高电平变低电平
FALLING 下降沿触发,即低电平变高电平
中断函数
除了设置中断模式外,还需要编写一个响应中断的处理程序——中断函数,当中断被触发后,便可以让Arduino运行该中断函数。中断函数就是当中断被触发后要去执行的函数,该函数不能带有任何参数,且返回类型为空
这些准备工作完成后,还需要在setup()中使用attachInterrrupt()函数对中断引脚进行初始化配置,以开启arduino的外部中断功能,其用法如下:
attachInterrupt(interrupt, function,mode)
功能:对中断引脚进行初始化配置
参数:
interrupt,中断编号,注意,这里的中断编号并不是引脚编号
function,中断函数名,当中断被触发后即会运行此函数所代表的中断函数。
mode,中断模式
detachInterrupt(interrupt)
功能:禁用外部中断
参数:
interrrupt,需要禁用的中断编号
实验代码
#define LED A2
#define KEY 2
volatile byte state;
volatile unsigned long last_time;
void setup() {
Serial.begin(115200);
// put your setup code here, to run once:
pinMode(LED, OUTPUT);
pinMode(KEY, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(KEY), keyInterrupt, RISING);
state = LOW;
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(LED, state);
}
void keyInterrupt() {
if ((millis() - last_time) > 500) {
Serial.println("检测到中断");
if (state == LOW) {
state = HIGH;
} else {
state = LOW;
}
last_time = millis();
}
}
尝试自己模拟运行下吧:https://wokwi.com/projects/347197223040189011
可能会遇到的问题
当实际操作的时候你会发现你的中断函数被触发很多次,这个当然不是我们想要看到的。这是由于我们按键是机械结构所造成的抖动,而需求就是消除这个抖动。
if ((millis() - last_time) > 500) {
Serial.println("检测到中断");
if (state == LOW) {
state = HIGH;
} else {
state = LOW;
}
last_time = millis();
}
此方法是加入时间函数利用时间差进行处理抖动效果。
当然还有另外的一种方案笔者没有尝试过,利用在按钮两端并联一颗电容利用它的特性过滤按钮的抖动,感兴趣的可以自己测试。
定时器库的使用
引入库: TimerOne
代码中使用:#include "TimerOne.h"
#include "TimerOne.h"
#define LED 7
volatile byte state = LOW;
void setup() {
Serial.begin(115200);
// put your setup code here, to run once:
pinMode(LED, OUTPUT);
digitalWrite(LED, state);
Timer1.initialize( 500000 ); // 初始化, interval 以 micro sec 为单位
Timer1.attachInterrupt( timer_task ); // attach the service routine here
}
void loop() {
digitalWrite(LED, state);
}
void timer_task() {
Serial.println("timer_task 执行了");
state = ~state;
}