Istnieją dwa rodzaje przerwań typu „zmiana pinów”. Zewnętrzne przerwania, których są dwa na Uno. Nazywa się je 0 i 1, jednak odnoszą się do cyfrowych styków 2 i 3 na płycie. Można je skonfigurować tak, aby wykrywały wzrost, spadek, zmianę (wzrost lub spadek) lub niski.
Oprócz tego są przerywane „zmiany pinów”, które wykrywają zmianę stanu pinów w dowolnym z 20 pinów (od A0 do A5 i od D0 do D13). Przerwania zmiany pinów są również oparte na sprzęcie, więc same w sobie będą tak szybkie, jak przerwania zewnętrzne.
Oba typy są nieco kłopotliwe w użyciu na poziomie rejestru, ale standardowe IDE obejmuje attachInterrupt (n) i detachInterrupt (n), co upraszcza interfejs do zewnętrznych przerwań. Możesz także użyć biblioteki zmiany pinów, aby uprościć przerwania zmiany pinów.
Jednak omijając bibliotekę przez minutę, możemy ustalić, że przerwania zmiany pinów mogą być tak szybkie lub szybsze niż przerwania zewnętrzne. Po pierwsze, chociaż zmiana pinów przerywa pracę na partiach pinów, nie musisz włączać całej partii. Na przykład, jeśli chcesz wykryć zmiany na pinie D4, wystarczy:
Przykładowy szkic:
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 ()
{
}
Moje testy wskazują, że zajęło 1,6 µs, aby pin „testowy” (pin 5) zareagował na zmianę na szpilce przerywającej (pin 4).
Teraz, jeśli zastosujesz proste (leniwe?) Podejście i użyjesz attachInterrupt (), zobaczysz, że wyniki są wolniejsze, a nie szybsze.
Przykładowy kod:
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 ()
{
}
Wymaga to 3,7 µs, aby zmienić pin testowy, o wiele więcej niż 1,6 µs powyżej. Dlaczego? Ponieważ kod, który kompilator musi wygenerować dla „ogólnej” procedury obsługi przerwań, musi zapisać każdy możliwy rejestr (wypchnąć je) przy wejściu do ISR, a następnie przywrócić je (pop) przed powrotem. Dodatkowo narzut związany jest z innym wywołaniem funkcji.
Teraz możemy obejść ten problem, unikając metody attachInterrupt () i robiąc to sami:
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 ()
{
}
To najszybszy ze wszystkich przy 1,52 µs - wygląda na to, że gdzieś zapisano jeden cykl zegara.
Jest jednak jedno zastrzeżenie, dotyczące przerwań związanych ze zmianą pinów. Są one podzielone na partie, więc jeśli chcesz mieć przerwania na wielu pinach, musisz przetestować przerwanie, które zmieniło się. Możesz to zrobić, zapisując poprzedni status PIN i porównując go z nowym statusem PIN. Niekoniecznie jest to szczególnie wolne, ale im więcej pinów musisz sprawdzić, tym wolniej będzie.
Partie to:
- A0 do A5
- D0 do D7
- D8 do D13
Jeśli chcesz tylko kilka pinów przerywających, możesz uniknąć testowania, po prostu wybierając piny z różnych partii (np. D4 i D8).
Więcej informacji na stronie http://www.gammon.com.au/interrupts