Tutorial → PIC18 in C →
Timer1 e ECCP →
Timer1 e ECCP con le PLIB

Le PLIB sono dal luglio 2015 sconsigliate per nuovi progetti

Questa pagina affronta l'uso dei moduli Timer1 e ECCP (Enhanced Capture,
Compare, and PWM) in modalità Compare Mode, utilizzando le PLIB
Il modulo ECCP in modalità "compare"
Utilizzando il modulo ECCP insieme a Timer1 è possibile generare in modo
totalmente hardware frequenze programmabili. Questo da una parte semplifica il lavoro
del PIC18, dall'altra permette di generare intervalli temporali
che dipendono esclusivamente dalla precisione del clock. Si osserva che
nella descrizione seguente non sono necessarie le capacità enhanced,
quindi la descrizione dovrebbe essere corretta anche per i PIC18 più vecchi.
Due sono i blocchi fondamentali:
- Il blocco di confronto che attiva un segnale quando il contenuto del
registro CCPR1 coincide con il conteggio raggiunto da Timer1 (o Timer3)
- La logica di uscita, che sceglie l'azione da intraprendere: azzerare
Timer1 oppure Timer3, avviare il convertitore AD, generare un interrupt,
modificare lo stato del pin CCP1
In sintesi il funzionamento di Timer1 + ECCP è simile a quello di un
contatore asincrono il cui modulo dipenderà dal contenuto del registro
CCPR1; quindi il conteggio di Timer1 inizia da zero e termina ad un
valore programmabile inferiore a 65536. Se fa notare che il termine asincrono è stato
usato impropriamente in quanto la struttura interna è in realtà
interamente sincrona.

Il codice di esempio configura ECCP per azzerare
Timer1 ed attivare un interrupt allo scopo di far lampeggiare un LED un paio
di volte al secondo. Sono utilizzate le PLIB, ma leggete la
richiesta del terzo esercizio.
Il circuito è semplicemente un LED ed una resistenza connessi a PORTC0:

Leggiamo il codice. La funzione main():
- Imposta le porte e i registri degli interrupt, come già visto più volte
- Imposta Timer1 per funzionare a 16 bit, con clock interno e senza
prescaler. Si noti che non è attivo l'interrupt: sarà infatti il modulo
ECCP ad incaricarsi di questo aspetto. Si noti inoltre che il conteggio
non viene inizializzato, cioè partirà da zero.
OpenTimer1(TIMER_INT_OFF & T1_16BIT_RW &
T1_SOURCE_INT & T1_PS_1_1);
- Il modulo ECCP viene inizializzato per generare un interrupt quando
il confronto avrà successo e per generare uno Special Event
Trigger. Viene inoltre inizializzato al valore di 49 999 ( f
= 250 kHz /
(49 999 + 1)) produce un segnale di durata 200 ms)
OpenCompare1(COM_INT_ON & COM_TRIG_SEVNT,
TIMER1_VALUE);
- Associa il modulo ECCP al Timer1
T3CONbits.T3CCP1 = 0; // Timer1 is the clock source
for compare
- Infine impostare la priorità dell'interrupt ad alta:
IPR1bits.CCP1IP = 1; // CCP1 Interrupt Priority set
to high
- A questo punto l'attività termina
L'attività della ISR è particolarmente semplice:
- Verifica se l'interrupt proviene da ECCP
if (PIR1bits.CCP1IF == 1) // Interrupt from ECCP ?
- Cambia lo stato dell'uscita PORTC0
- Azzera il flag che ha generato l'interruzione
PIR1bits.CCP1IF = 0; // Clear interrupt status
Si noti che, nel caso particolare, il LED poteva essere collegato
direttamente al pin CCP1 (pin 13), liberando completamente il processore da
questa incombenza (quarto esercizio).
Il modulo ECCP è molto più flessibile di quanto mostrato: si legga
quanto proposto nell'approfondimento.
Un altro esempio, associato all'uso dell'ADC
lo trovate in questa pagina.
Disporre di due sorgenti di interrupt indipendenti come Timer0 e Timer1
ci permette di visualizzare come opera un sistema in cui sono presenti più
attività "contemporanee".
Il codice, presente a fine pagina, esegue tre attività
diverse e indipendenti, ma con il vincolo che il processore è uno solo:
- Il main(), dopo la configurazione delle
periferiche, non fa altro che cambiare continuamente lo stato di
RED, cioè del pin PORTC0
- La funzione my_isr_low(), è attivata da Timer0
ogni 6.1 ms circa (conteggio da 64000 a 65536 di un clock con frequenza
250 kHz). La sua attività è cambiare per 50
volte lo stato di BLUE, cioè il pin PORTC1, per far ciò impiega un tempo di circa 1,2
ms (tempo misurato, non calcolato)
- La funzione my_isr_high(), è attivato da Timer1
ogni 4 ms (conteggio da 0 a 1000 di un clock con frequenza 205 kHz). La
sua attività è cambiare per 10
volte lo stato di GREEN, cioè del pin PORTC2. per far ciò impiega un
tempo di circa 0,2
ms (tempo misurato, non calcolato)
La descrizione dettagliata del codice è la stessa già presentata
per Timer0 e Timer1.
Osserviamo quello che succede sul grafico reali delle tre uscite,
misurato dopo le fasi iniziali del programma:
- Al tempo zero di riferimento è attivo main()
e quindi RED
cambia continuamente stato
- Dopo circa 0,8 ms dal tempo zero, RED interrompe il suo funzionamento
ed il controllo passa a my_isr_high(), alias
GREEN, per poco più di 0,2 ms
- Quindi riprende il lavoro di RED, dallo stato rimasto nell'istante di
sospensione (nell'esempio l'uscita PORTC0 è rimasta casualmente alta)
- Dopo poco più di 2 ms dal tempo zero,
my_isr_low(), alias BLUE, interrompe il funzionamento di RED, per
poco più di 1 ms.
- Al termine, RED riprende il suo lavoro (questa volta l'uscita PORTC0
è rimasta casualmente bassa)
- Dopo 4,8 ms dal tempo zero il controllo torna nuovamente a GREEN, che lo
lascia poco dopo
- Dopo 8,5 ms dall'inizio, il controllo torna a BLUE
- Dopo un ulteriore breve intervallo il controllo passa a GREEN, che
ha priorità maggiore e quindi interrompe l'attività di BLUE
- Dopo 0,2 ms il controllo torna a BLU, visto che non ha ancora
finito il sui lavoro.
- Infine il processore tornerà a RED, passaggio non mostrato nel
grafico

Aspetti da osservare:
- Ogni cambio del processo in esecuzione richiede un certo tempo per
la commutazione di contesto (context switch).
Abbiamo già visto che tale tempo è
nelle condizioni date,
dell'ordine dei 150 µs
- Un interrupt ad alta priorità può interrompere un interrupt a bassa
priorità (ma non viceversa)
- In presenza di interruzioni, la durata precisa di un'attività non
può essere calcolato con certezza
- Scrivere il codice che utilizza Timer1 per far lampeggiare un LED
utilizzando PLIB, come già fatto con Timer0
Data di creazione di questa pagina: luglio 2014
Ultima modifica: 31 luglio 2015