iot

Nuovamente un tutorial per far lampeggiare un led; questo però credo sia interessante sotto molti punti di vista.

Schermata 2018 01 28 alle 16.32.15 

Tutte le volte che si cerca un modo rapido per far lampeggiare un led si casca nel classico esempio dove viene illustrato come cambiare il livello logico di una porta temporizzando l'evento tramite le funzioni delay(). Va bene se si vuole verificare il funzionamento del micro. Non va bene se si vuole far lampeggiare il led durante lo svolgimento di un'altro programma.

Si passa poi all'utilizzo dei timer che permette di veder lampeggiare il led anche durante l'esecuzione di un altro programma (si veda l'esempio Lampeggio LED). Può tornare utile per accertarsi che il micro stia lavorando: debug del nostro programma. E' però uno spreco utilizzare il timer esclusivamente per pilotare un led (anche disponendo di più di un timer).

In questo esempio voglio disporre di un sistema di temporizzazione che mi permetta di sincronizzare il lampeggio di un led e qualsivoglia altra temporizzazione del mio programma. Quello che voglio fare è qualcosa di simile alla funzione millis() presente nella libreria di Arduino, ovvero una funzione che quando interrogata mi restituisce il numero di millisecondi trascorsi a partire da un momento prefissato.

Questo è lo schema del circuito di test utilizzato; ho montato 7 led, più uno di monitor, proprio per evidenziare con quanta facilità è possibile temporizzare simultaneamente e in modo affidabile tante funzioni diverse con un solo timer. 

sch ex led irq

Lanciamo MPLABX e creiamo un nuovo progetto vuoto poi lanciamo il tool MCC. E' necessario impostare:

  1. La sorgente e la frequenza di clock.
  2. I pin usati per pilotare i LED.
  3. Il timer0 e l'IRQ.

Avviando MCC la prima maschera che compare è proprio l'impostazione del clock di sistema:

clock

Successivamente cliccando sui lucchetti delle porte utilizzate si impostano le uscite digitali:

pinout

Occorre poi dare un nome utile all'identificazione dei LED e, visto che i LED sono collegati in modo da accendersi quando la porta va a livello basso, selezionare le uscite a livello alto dopo lo start-up. Nel riquadro in alto a sinistra "Project Resources" cliccare sulla voce "Pin Module" e impostare la maschera che segue come in figura:

pin nameNon resta che avviare il Timer0. Individuare la finestra sulla sinistra denominata "Device Resources" e scorrere fino a trovare la voce "Timer->TMR0"; cliccarci sopra due volte e impostare la maschera "Easy Setup" come in figura:

timer0

Abbiamo impostato il Timer0 con un periodo di 1ms e abbiamo abilitato l'Interrupt. Premiamo il bottone "Generate" e chiudiamo MCC. Il micro è pronto a operare, dobbiamo solo dirgli come utilizzare il timer.

Per prima cosa creiamo una variabile globale che possa contenere il conteggio dei millisecondi. Se avessimo potuto disporre di una variabile a 64 o 128 bit non ci saremmo preoccupati a quello che succede quando il conteggio supera la capienza della variabile, ma, disponendo al massimo di una variabile a 32 bit dobbiamo tenere in considerazione che dopo 49 giorni la variabile riparte da zero. Potremmo accettare la cosa (come fa Arduino avvertendo che dopo "approssimativamente" 50 giorni la funzione millis() non restituirà più quello che dovrebbe),

Arduino

oppure possiamo prendere atto di questo limite e fare in modo che non danneggi la funzione che definiremo a breve. Adottando questo secondo modo di operare non è necessario avere una variabile a 32 bit, possiamo utilizzarne una a 16 bit tenendo conto che si azzererà ogni minuto.

globalsHo voluto mettere la variabile in un file apposito ma è sufficente che sia globale. Il valore  millisec verrà incrementato di una unità ad ogni scadere del millisecondo, ecco come:

tmr0 irq

Il tool MCC ha creato per noi tutte le procedure per la gestione dell'IRQ del timer0, in particolare nel file tmr0.c troviamo la funzione TMR0_DefaultInterruptHandler() che viene chiamata allo scadere di ogni millisecondo. Nel corpo della funzione scriviamo l'incremento della variabile sopra definita.

Come possiamo utilizzare una variabile che conta i millisecondi e si azzera ad ogni minuto? Con due funzioni: start_msTimeout e checkTimeoutExpired; prima però abbiamo bisogno di una struttura che ci consenta di incapsulare i valori dei conteggi di ogni funzione chiamante.

struct

Ripensiamo all'obiettivo che ci siamo posti: utilizzare un unico timer per realizzare temporizzazioni differenti e, nel nostro caso, accendere LED a frequenze diverse. Per fare questo utilizzeremo per tutti i casi due sole funzioni alle quali passeremo di volta in volta i dati relativi al conteggio dello specifico LED. Questo è il motivo per cui è stata creata la struttura msecdata e per cui il suo indirizzo viene passato alle funzioni.

Vediamo il dettaglio delle funzioni:

procedure

start_msTimeout serve a memorizzare il numero di millisecondi di ritardo voluti e il valore della variabile millisec all'inizio dello specifico conteggio.

Con la funzione checkTimeoutExpired verifichiamo se il timeout impostato è scaduto confrontando l'attuale valore di millisecondi con il valore di partenza, nel caso restituiamo true. E' da notare che come prima cosa verifichiamo che il valore corrente della variabile millisec sia inferiore al valore di partenza del conteggio, se così è la variabile millisec è andata in overflow e si è azzerata; dobbiamo tenere conto di questo evento aggiungendo al valore del conteggio i millisecondi che erano stati conteggiati prima dell'overflow. Questo è lo scopo della variabile gap.

Vediamo come utilizzare le due funzioni sopra definite.

main 1

Prima di tutto è necessario ricordarsi di abilitare gli interrupt (MCC li inserisce nel codice ma li lascia commentati) poi creiamo una variabile di tipo msecdata per ogni timeout che vogliamo attivare, passiamo poi il puntatore della struttura creata alla funzione start_msTimeout aggiungendo anche il numero di millisecondi dopo i quali il timeout scade.

Successivamente nel loop principale del programma verifichiamo con checkTimeoutExpired se il timer è scaduto nel caso cambiamo stato al LED.

main 2

Provando il programma sarà istruttivo vedere come i timeout lavorino indipendentemente l'uno dall'altro pur appoggiandosi ad un unico timer.

Come di consueto il progetto è scaricabile da github.  download project