中断用于处理在程序正常执行期间通过外部事件或者响应软件指令触发时发生的事件。比如,在一段呼吸灯的代码中,正常运行时的结果为LED从暗到亮,再从亮到暗持续地运行。我们可以通过一个中断来控制呼吸灯的运行和停止。使用中断功能,我们就不需要不停地监控一个引脚的状态,当中断被触发,内部控制器停止执行主程序,并调用中断服务例程 (ISR) 。
中断服务例程 (ISR)是特殊类型的函数,它有一些特殊的特点和规则:
1、将部分内部 SRAM0 区域分配给IRAM,可以显著减少运行的迟延。
2、必须将中断服务例程 (ISR)放入 IRAM。
3、中断服务例程 (ISR)不能有任何参数,也不返回任何值。
4、因为中断服务例程 (ISR)会中断主程序的运行,运行时间过长会对主程序形成阻塞,所以中断服务例程 (ISR)尽可能短和快。
在IRAM中放置代码,可以使用 IRAM_ATTR 宏在源代码中指定该函数放置到IRAM中,如:
void IRAM_ATTR ISR_function()
{
// ...
}
我们在使用中断功能时,会使用一个名为attachInterrupt()
的函数个设置引脚中断。
函数:attachInterrupt
作用:用于将中断附加到定义的引脚。
格式:attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode);
参数:
pin -定义监控的GPIO 引脚号。
handler -设置中断服务例程 (ISR)
mode -设置中断模式
LOW -低电平触发
CHANGE -管脚电平变化,触发
HIGH -高电平触发
RISING -管脚从低电平变化高电平触发,即上升沿触发
FALLING -下降沿触发,电平从高变低
我们需要定义一个引脚来用于中断服务,这里选择4号引脚:
int interrupt_pin = 4; //引脚号
下面需要要定义一个计数器来记录中断的次数,默认设为0:
int interrupt_counter = 0; //中断次数计数器
定义一个开关标记来记录中断时的开关状态:
bool press_down; //中断标记开关
定义一个ISR函数,放置到IRAM:
void IRAM_ATTR ISR_function(){
interrupt_counter++; //中断次数计数器累加
press_down = true; //中断标记开关设为开
}
我们需要使用上面所介绍的attachInterrupt函数来把中断服务附加到指定的引脚:
attachInterrupt(interrupt_pin, ISR_function, FALLING); //中断附加到引脚
以下为硬件的连接和完整的代码:
int interrupt_pin = 4; //引脚号
int interrupt_counter = 0; //中断次数计数器
bool press_down; //中断标记开关
void IRAM_ATTR ISR_function(){
interrupt_counter++; //中断次数计数器累加
press_down = true; //中断标记开关设为开
}
void setup() {
Serial.begin(115200);
pinMode(interrupt_pin,INPUT_PULLUP); //引脚初始化为输入上拉模式
attachInterrupt(interrupt_pin, ISR_function, FALLING); //中断附加到引脚
}
void loop() {
if(press_down){ //如果中断标记开关为开状态
Serial.print("发生中断事件:");
Serial.println(interrupt_counter); //串口输出计数器的值
press_down = false; //中断标记开关设为关
}
}
在该例程中,我们设置监控4号引脚来触发中断服务。
我们把4号引脚设置为输入上拉模式,当开机时,4号引脚为高电平状态。
4号引脚通过一个开关与GND引脚连接,当开关按下时,4号引脚由高电平变为低电平状态。
这时,触发中断,主程序中断,进入ISR_function函数,函数功能包括给计数器累加中断次数和中断标记开关设置为开。
中断标记为开时满足if(press_down)条件,进入分支,串口此时输出计数器的值,并设置中断标记开关为关闭状态。
运行该代码,打开串口监视器,按下开关,可以观察到计数器的变化:
同时,我们也会发现,有时即使只按一次开关,也会出现计数据多次增加。这是同于硬件生产工艺的原因造成的,我们在直观上感觉只按了一次开关,但在十分微小的时间里,开关的接触弹片可能进行了多次的接触。释放按钮时也会发生同样的事情。在生产中,工艺上是无法达到理想的完美状态的。以致产生了这种名为"开关抖动"的现象。我们可以通过硬件或软件的方式来消除这种抖动。
我们在上面的例程中加入添加代码,以软件的方式来消除开关抖动。实现每按一次开只允许动行一次ISR程序:
int interrupt_pin = 4; //引脚号
int interrupt_counter = 0; //中断次数计数器
bool press_down = false ; //中断标记开关
void IRAM_ATTR ISR_function(){
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
if(interruptTime - lastInterruptTime >200){
press_down = true; //中断标记开关设为开
interrupt_counter++;
}
lastInterruptTime = interruptTime;
}
void setup() {
Serial.begin(115200);
pinMode(interrupt_pin,INPUT_PULLUP); //引脚初始化为输入上拉模式
attachInterrupt(interrupt_pin, ISR_function, FALLING); //中断附加到引脚
}
void loop() {
if(press_down){ //如果中断标记开关为开状态
Serial.print("发生中断事件:");
Serial.println(interrupt_counter); //串口输出计数器的值
press_down = false; //中断标记开关设为关
}
}