Oggi ho avuto un’idea, ho pensato che un timer era proprio sprecato per far solo lampeggiare un LED. Perché non usarlo per ciò per cui è stato progettato? E’ un timer, è stato realizzato per misurare il tempo. Può sollevare anche degli interrupt. E allora perché non combinare queste due cose? Realizzare un timer software pilotato da interrupt, una porzione di codice che incrementa un contatore ogni secondo ma senza interferire con il programma principale. Infatti, un interrupt può interrompere il codice dell’utente e forzare la CPU ad eseguire un’altra porzione di codice. Quando questa è finita, la CPU può riprendere il lavoro interrotto, da qui il nome di “interrupt”.
Gli interrupt sono una parte fondamentale di qualsiasi sistema: per servire un interrupt la CPU sospende il codice che stava eseguendo, salva il puntatore dell’istruzione successiva all’ultima che stava eseguendo, e poi salta ad eseguire del codice memorizzato in una particolare area della memoria. Quando questo codice è terminato, riprende l’esecuzione del codice interrotto recuperando il puntatore che aveva salvato in precedenza. Gli interrupt sono usati per interrompere il codice principale quando si verifica un evento speciale, un evento così importanto che la CPU non può ignorarlo: pensate ad un utente che preme dei tasti su una tastiera. E’ fondamentale che il sistema salvi ogni tasto premuto, anche se la CPU sta facendo altro. Immaginate questo scenario: l’utente preme un tasto, la periferica che gestisce la tastiera solleva un interrupt, informando la CPU che è accaduto qualcosa che richiede la sua attenzione. La CPU sosende il compito attuale, riceve il codice del tasto, lo memorizza in un buffer temporaneo, e poi riprende il compito che stava eseguendo. Tutto in una maniera così del tutto trasparente all’utente finale che questi pensa che la CPU gli/le stia dedicando la massima attenzione. Quando la CPU eseguirà la porzione del codice sistema che legge l’input utente, andrà a recuperare i tasti nel buffer, dando all’utente l’impressione che fosse il compito che stava svolgendo in quel momento.
Il circuito per gestire un interrupt del CTC è quasi fatto, dobbiamo solo fare dei cambiamenti per permettere alla CPU di lavorare correttamente:
- collegare i pin /INT del CTC e della CPU
- mettere un resistore di pull-up da 10K tra i +5V ed il pin IEI
- fare un collegamento fra il pin IEO del CTC ed il pin IEI del PIO: questo servirà in futuro, quando avremo anche altre periferiche che solleveranno interrupt.
La to software dobbiamo solo fare dei piccoli cambiamenti: per prima dobbiamo attivare l’interrupt del timer 2 del CTC così che ogni volta che il contatore interno raggiunge lo zero non solo manderà un segnale sul suo pin di output ma solleverà anche un interrupt. Secondariamente, dobbiamo impostare il vettore della corrispondente routine di interrupt: questo si fa preimpostando i bit più significativi del vettore all’interno dei registri del CTC. I bit meno significativi saranno poi aggiunti dal CTC stesso e punteranno al timer che ha sollevato l’interrupt. In questo modo avremo il CTC che spedisce il vettore dell’interrupt alla CPU che è composto da una parte che identifica la periferica (in questo caso il CTC) e da un’altra parte che identifica il timer che ha sollevato l’interrupt. Successivamente dobbiamo impostare la CPU sulla modalità 2 di gestione degli interrupt. Questa modalità è molto potente: quando un interrupt viene sollevato, la CPU riceve il vettore di interrupt che punta alla routine di interrupt da eseguire dalla periferica che ha sollevato l’interrupt direttamente sul bus dati. La CPU deve solo prendere questo indirizzo (in un processo completamente automatico) e poi saltare alla locazione di memoria puntata dal vettore.
La nostra routine di interrupt è molto semplice: incrementa solo il valore di una cella di RAM e poi lo manda al PIO per “visualizzare” il suo valore attraverso 8 LED connessi alla sua porta parallela.
Ecco il codice in azione:
Il codice e lo schema sono disponibili sul mio repository GitHub, come sempre.