Esistono due tipi di interrupt di tipo "cambio pin". Gli interrupt esterni, di cui ce ne sono due su Uno. Sono chiamati 0 e 1, tuttavia si riferiscono ai pin digitali 2 e 3 sulla scheda. Questi possono essere configurati per rilevare salita, discesa, modifica (salita o discesa) o BASSO.
Inoltre, vi sono interruzioni "cambio pin", che rilevano una modifica dello stato del pin in uno qualsiasi dei 20 pin (da A0 a A5 e da D0 a D13). Questi interruttori di cambio pin sono anch'essi basati su hardware quindi, di per sé, saranno veloci quanto gli interrupt esterni.
Entrambi i tipi sono leggermente complicati da usare a livello di registro, ma l'IDE standard include attachInterrupt (n) e detachInterrupt (n) che semplifica l'interfaccia agli interrupt esterni. È inoltre possibile utilizzare la Libreria di modifica pin per semplificare gli interrupt di modifica pin.
Tuttavia, allontanandoci dalla libreria per un minuto, possiamo stabilire che gli interrupt di cambio pin possono essere più veloci o più veloci degli interrupt esterni. Per prima cosa, sebbene l'interruzione della sostituzione dei pin funzioni su lotti di pin, non è necessario abilitare l'intero batch. Ad esempio, se si desidera rilevare le modifiche sul pin D4, questo sarà sufficiente:
Esempio di schizzo:
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 ()
{
}
Il mio test indica che ci sono voluti 1,6 µs per il pin "test" (pin 5) per reagire a una modifica sul pin di interruzione (pin 4).
Ora se prendi l'approccio semplice (pigro?) E usi attachInterrupt () scoprirai che i risultati sono più lenti, non più veloci.
Codice di esempio:
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 ()
{
}
Questo richiede 3,7 µs per cambiare il pin di test, molto più degli 1,6 µs sopra. Perché? Poiché il codice che il compilatore deve generare per il gestore di interrupt "generico" deve salvare ogni registro concepibile (spingerli) all'entrata dell'ISR e quindi ripristinarli (pop) prima di tornare. Inoltre c'è il sovraccarico di un'altra chiamata di funzione.
Ora possiamo aggirare il problema evitando attachInterrupt () e facendolo da soli:
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 ()
{
}
Questo è il più veloce di tutti a 1,52 µs - sembra che un ciclo di clock sia stato salvato da qualche parte.
C'è un avvertimento però, per gli interruzioni di cambio pin. Sono in batch, quindi se si desidera avere interruzioni su molti pin, è necessario verificare all'interno dell'interrupt quale è stato modificato. Si potrebbe fare che salvando lo stato perno precedente, e confrontandolo con il nuovo stato perno. Questo non è necessariamente particolarmente lento, ma più pin è necessario controllare, più lento sarebbe.
I lotti sono:
- Da A0 a A5
- Da D0 a D7
- Da D8 a D13
Se vuoi solo un paio di pin di interruzione in più, puoi evitare qualsiasi test semplicemente scegliendo di usare pin di lotti diversi (es. D4 e D8).
Maggiori dettagli su http://www.gammon.com.au/interrupts