Lo scopo di questo esercizio, è di capire come gestire gli Interrupts.

In questo esempio utilizzerò  tre Interrupts disponibili per il chip ATMEGA328, per valutarne il comportamento.

Per richiamare gli Interrupts, utilizzo alcuni pulsanti, collegati ai Pins del microcontrollore di Arduino, precisamente:

  • Per INT0, la porta PD2 di Arduino che corrisponde al Pin 4 del microcontrollore ATMEGA328 .
  • Per INT1, la porta PD3 di Arduino che corrisponde al pin 5 del microcontrollore ATMEGA328.
  • Per IOC, utilizzo i Pins 2,3,6 e 11 della porta D, che corrispondono rispettivamente a PD0,PD1,PD4 e PD6.

Ogni qualvolta che premo un pulsante, il programma principale è interrotto per eseguire la Macro associata all’Interrupt, completato il ciclo della Macro il programma riprende dal punto in cui  è stato interrotto.

Le Macro associate a ciascun Interrupt, hanno lo scopo di verificare se quando il pulsante è premuto, il programma principale si ferma per eseguire il sottoprogramma associato all’Interrupt.

  • INT0, l’interruzione avviene al passaggio dal livello logico zero al livello logico 1, “Rising Edge of INT”.
  • Il pulsante collegato al PD2, richiama la Macro Fast, che fa lampeggiare velocemente il led B2.
  • INT1, l’interruzione avviene al passaggio dal livello logico zero al livello logico 1, “Rising Edge of INT”.
  • Il pulsante collegato al PD3, richiama la Macro Fast2, che fa lampeggiare velocemente il led B6.
  • IOC, l’interruzione avviene al passaggio sia dal livello logico zero al livello logico 1, sia dal passaggio da 1 a zero,  “trigger on both edge”.
  • Il pulsante collegato al PD0, richiama la Macro Fast (0), che fa lampeggiare velocemente tutti i leds.

Una curiosità

Utilizzando interrupts IOC su porte diverse, il funzionamento è corretto.

Se collego altri pulsanti alla medesima porta, utilizzando lo “Interrupts IOC”, anziché richiamare la Macro associata a ciascun pulsante, il sistema esegue solo la Macro associata all’ultimo pulsante della stessa porta.

Non so se questo comportamento è contemplato nelle specifiche di fabbrica, oppure è un errore di sistema; anche con i PIC, ottengo il medesimo errore.

In pratica, l’utilizzo dello “interrupt IOC”, funziona solo, se collego un solo interrupt su porte diverse, la mia ricerca riguardo all’utilizzo di questo tipo di Interrupts multipli sulla medesima porta, fa riferimento al supporto della gestione del GPIO del microcontrollore.

arduino interrupts

INTERRUPTS

Timer Register

  • TCCRx – Timer/Counter Control Register: configura il pre-scaler.
  • TCNTx – Timer/Counter Register – immagazzina il valore del Timer.
  • OCRx – Output Compare Register.
  • ICRx – Input Capture Register (solo per 16 bit)
  • TIMSKx – Timer Counter Interrups Mask Register: abilita e disabilita i Timer Interrupts.
  • TIFRx Timer/Counter Interrupt Flag Register: indica il tempo di attesa del timer Interrupt.

Arduino, contiene al suo interno un orologio che può essere utilizzato per misurare gli eventi correlati con il tempo.

Questi Timer o contatori, possono essere pianificati in modo da ottenere la base per il tempo necessario per lo svolgimento del programma, che necessita un’esecuzione precisa, come la realizzazione di un orologio o un conta tempo.

Questi Timer, possono essere configurati mediante un pre-scaler, per ottenere una precisa frequenza di riferimento.

Il chip ATMEGA328, ha 3 contatori al suo interno che dipendono dalla frequenza di clock di Arduino, generalmente 16 MHz.

  • TRM0 - Timer0: Contatore a 8 bit di risoluzione, con 256 valori , 28
  • TRM1  - Timer 2: Contatore a 16 bit di risoluzione con 65.536 valori, 216
  • TRM2  - Timer1: Contatore a 8 bit di risoluzione con 256 valori , 28

Ogni impulso di clock incrementa questi contatori.

In Arduino, i contatori sono configurati alla frequenza di 1 KHz, tramite registri speciali, e sono normalmente abilitati.

TRM0, ha un registro a 8 bit, può contenere 256 impulsi prima di generare il segnale di overflow; è utilizzato con la funzione Ritardo (Delay) per la gestione millisecondi e microsecondi, pertanto, se si utilizza TRM0 come base dei tempi, questa è influenzata dalla esecuzione del programma.

  • TRM1, ha un registro a 16 bit, può contenere 65.536 impulsi, al superamento di questo valore genera l’impulso di overflow.
  • TRM2, ha un registro di 8 bit come TRM0.

Dopo aver generato il segnale di overflow, il conteggio nei registri riparte da zero.

Con la frequenza di 16.000.000 MHz, la velocità massima d’incremento del contatore del Timer è di 1/16.000.000 al secondo, che corrisponde a circa 63 nanosecondi, in questo modo, il contatore impiegherà 10/16.000.000 = 0,0000006 secondi per raggiungere il valore 9 (i Timers sono indicizzati a 0) e 100/16.000.000 = 0,00000063 secondi per raggiungere il valore 99.

In alcune situazioni, questa velocità di riempimento dei contatori, è troppo elevata, ed è necessario rallentarla.

Il “Timer Match Register”, è il comparatore che genera l’overflow, quando il contatore del timer è pieno:

con TRM0, 256/16.000.000 secondi  (circa 16 microsecondi) per riempire gli 8 bit del contatore associato a TRM0 .

con TRM1, 65.536/16.000.000 secondi (circa 4 microsecondi) per riempire il contatore associato a TRM1.

Questi valori non sono utili se si vuole interrompere il programma 1 volta al secondo.

È possibile controllare questa velocità, utilizzando il “prescaler”, che determina la velocità secondo la formula:

(velocità del timer(Hz)) = (frequenza di clock di Arduino (16MHz))/ prescaler

Pertanto:

  • con il prescaler a 1, il contatore incrementerà a 16MHz.
  • con il prescaler a 8, il contatore incrementerà a 2 MHz.
  • Con il prescaler a 64, il contatore incrementerà a 250 KHz.
  • Con il prescaler a 1024, il contatore incrementerà a 15.625 Hz.

È possibile calcolare la frequenza di interrupt con la seguente equazione:

Frequenza di interrupt(HZ)= Clock di Arduino (16MHz) / (prescaler* (Compare Match Register + 1)

Più 1, perché è indicizzato a zero.

Ricavando Compare Match Register dall’equazione:

Compare Match Register = [16MHz/(prescaler * frequenza desiderata)] -1

Quindi, se si vuole ottenere un segnale di interrupt di 1 Hz, da utilizzare per il progetto del nostro orologio,

Utilizzeremo il TRM1 (registro a 16bit) che può contenere 65.536 conteggi.

65.536 =  [16000000/(prescaler * 1)]-1

65.536 = 16000000 /1024*1]-1 = 15.625

Ciò significa che il programma principale sarà interrotto 15.625 volte il secondo.

Configurazione e selezione della frequenza del Timer

È possibile selezionare frequenza  diverse dell’oscillatore, per ciascun timer.

Calcolare la frequenza necessaria per avere  due chiamte di Interrupt, ogni secondo, utilizzando TRM1.

  • La frequenza di lavoro di Arduino è di 16 Mhz.
  • Il valore massimo del contatore del timer per TRM1 è di 65.536.

Dividere la frequenza di 16.000.000 utilizzando il prescaler (16.000.000/256 =62.500).

Dividere il risultato per la frequenza che si vuole ottenere (62.500/2 Hz = 31.250).

Verificare se il valore ottenuto può essere contenuto nel registro del contatore; 31.250 è minore di 65.536 che è il valore massimo che TRM1 può contenere.

Pertanto, 31.250, sarà la frequenza necessaria per richiamare la Macro di Interrupt, 2 volte al secondo.

 

Riccardo Monti