Answers:
有两种类型的“引脚更改”类型的中断。外部中断,Uno上有两个。它们分别称为0和1,但它们指的是板上的数字引脚2和3。这些可以配置为检测上升,下降,变化(上升或下降)或LOW。
除此之外,还有“引脚更改”中断,可检测20个引脚(A0至A5和D0至D13)中任意一个引脚的状态变化。这些引脚更改中断也是基于硬件的,因此它们本身将与外部中断一样快。
两种类型在寄存器级别上使用起来都不尽如人意,但是标准的IDE包括attachInterrupt(n)和detachInterrupt(n),它们简化了与外部中断的接口。您还可以使用引脚更改库来简化引脚更改中断。
但是,绕开库一分钟,我们可以确定引脚更改中断可以比外部中断更快或更快速。一方面,尽管引脚更改中断对一批引脚起作用,但您不必启用整个批次。例如,如果您想检测引脚D4的变化,就足够了:
草图示例:
ISR (PCINT2_vect)
{
// handle pin change interrupt for D0 to D7 here
if (PIND & bit (4)) // if it was high
PORTD |= bit (5); // turn on D5
else
PORTD &= ~bit (5); // turn off D5
} // end of PCINT2_vect
void setup ()
{
// pin change interrupt (example for D4)
PCMSK2 |= bit (PCINT20); // want pin 4
PCIFR |= bit (PCIF2); // clear any outstanding interrupts
PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7
pinMode (4, INPUT_PULLUP);
pinMode (5, OUTPUT);
} // end of setup
void loop ()
{
}
我的测试表明,“测试”引脚(引脚5)对中断引脚(引脚4)的变化花费了1.6 µs。
现在,如果您采用简单的(懒惰?)方法并使用attachInterrupt(),您会发现结果较慢,而不是更快。
示例代码:
void myInterrupt ()
{
if (PIND & bit (2)) // if it was high
PORTD |= bit (5); // turn on D5
else
PORTD &= ~bit (5); // turn off D5
} // end of myInterrupt
void setup ()
{
attachInterrupt (0, myInterrupt, CHANGE);
pinMode (2, INPUT_PULLUP);
pinMode (5, OUTPUT);
} // end of setup
void loop ()
{
}
更换测试引脚需要3.7 µs的时间,比上面的1.6 µs要多得多。为什么?因为编译器必须为“通用”中断处理程序生成的代码必须在进入ISR时保存每个可能的寄存器(将它们推入),然后在返回之前将其还原(弹出)。另外,还有另一个函数调用的开销。
现在,我们可以通过避免attachInterrupt()来解决此问题,而自己做:
ISR (INT0_vect)
{
if (PIND & bit (2)) // if it was high
PORTD |= bit (5); // turn on D5
else
PORTD &= ~bit (5); // turn off D5
} // end of INT0_vect
void setup ()
{
// activate external interrupt 0
EICRA &= ~(bit(ISC00) | bit (ISC01)); // clear existing flags
EICRA |= bit (ISC00); // set wanted flags (any change interrupt)
EIFR = bit (INTF0); // clear flag for interrupt 0
EIMSK |= bit (INT0); // enable it
pinMode (2, INPUT_PULLUP);
pinMode (5, OUTPUT);
} // end of setup
void loop ()
{
}
这是它们中最快的,为1.52 µs-看起来一个时钟周期节省了某个地方。
但是有一个警告,关于引脚更改中断。它们是批处理的,因此,如果要在许多引脚上产生中断,则需要在中断中测试哪一个已更改。您可以通过保存先前的引脚状态并将其与新的引脚状态进行比较来实现。这不一定特别慢,但是您需要检查的引脚越多,速度就越慢。
批次为:
如果只需要几个中断引脚,则可以通过选择使用不同批次的引脚(例如D4和D8)来避免进行任何测试。
有两种类型的中断。Arduino Playground说了些什么:
任何Arduino的核心处理器都有两种不同的中断:“外部”和“引脚更改”。ATmega168 / 328(即,在Arduino Uno / Nano / Duemilanove中)只有两个外部中断引脚INT0和INT1,它们分别映射到Arduino引脚2和3。可以将这些中断设置为在RISING或RISING上触发。信号边缘下降或处于低电平。触发器由硬件解释,并且中断非常快。Arduino Mega有更多可用的外部中断引脚。
另一方面,可以在更多引脚上启用引脚更改中断。对于基于ATmega168 / 328的Arduino,可以在Arduino的任何或所有20个信号引脚上启用它们;例如,在基于ATmega的Arduino上,它们可以在24个引脚上启用。它们在RISING或FALLING信号边沿被均等地触发,因此由中断代码来设置适当的引脚以接收中断,确定发生了什么事情(哪个引脚?...信号上升还是下降?),以及妥善处理。此外,引脚更改中断在MCU上分为3个“端口”,因此整个引脚本体只有3个中断向量(子例程)。这使得解决单个中断上的动作的工作更加复杂。
基本上,外部中断非常快,因为它们都是基于硬件的。但是,也有换针中断,但由于它们主要基于软件,因此它们似乎慢得多。
tl; dr: 20个中断引脚在一起的速度要慢得多。2个中断引脚是最快和最有效的。
编辑:我只是看了数据表,它说,为选定的任何引脚触发了引脚更改中断,但没有指示哪个引脚已更改(尽管它分为三个中断向量)。
如您所见,引脚更改中断会通过存储以前的状态并查看它是否是您担心的引脚而给ISR增加了很多开销。对于睡眠状态可能很好,但是最好使用外部中断。