Controllo radio per tabellone segnapunti - Software e firmware

Date la relativa complessità del codice e le elaborazioni necessarie, il software del microcontrollore ricevente è scritto in C.

L’ambiente di sviluppo utilizzato è il compilatore mikroC pro versione 4.15.0.0.

Per permettere il corretto funzionamento del programma, i fuses vanno impostati nel modo seguente:

Oscillator: XT
Fail-Safe Clock Monitor Enable: Disabled
Internal External Switch Over Mode: Disabled
Power Up Timer: Disabled
Brown Out Detect: Disabled in hardware, SBOREN disabled
Brown Out Voltage: 2.0V
Watchdog Timer: Disabled-Controlled by SWDTEN bit
Watchdog Postscaler: 1:32768
CCP2 Mux: RB3
PortB A/D Enable: PORTB configured as digital I/O on RESET
Low Power Timer1 Osc enable: Disabled
Master Clear Enable: MCLR Disabled, RE3 Enabled
Stack Overflow Reset: Enabled
Low Voltage Program: Disabled
Extended Instruction Set Enable bit: Disabled
Background Debug: Disabled
Code Protect: Disabled

Tutte le protezioni della memoria (write, read, EEPROM, table) disabilitate.

Il programma si può suddividere indicativamente in tre sezioni:

  • La routine principale;
  • La routine di interrupt a bassa priorità;
  • La routine di interrupt ad alta priorità.

La routine principale si occupa inizialmente dell’impostazione, dell’inizializzazione delle periferiche interne e della configurazione degli interrupt, dopodiché entra in un ciclo perpetuo in cui controlla il flag che indica la necessità di aggiornare il tabellone, se tale flag è attivo viene lanciato un aggiornamento e lo si azzera.

In secondo luogo il ciclo principale si occupa anche di lanciare il salvaschermo (modalità power-saving o test dei segmenti) quando è necessario.

La routine di interrupt a bassa priorità si occupa della ricezione via radio, della decodifica e dell’esecuzione dei comandi. Quando viene ricevuto un comando, ne viene controllata la validità e, in caso sia corretto, viene eseguita l’azione corrispondente e si attiva il flag di richiesta refresh.

Il refresh non è svolto nella routine di interrupt, ma nel ciclo principale attraverso l’utilizzo del flag di richiesta, per il semplice fatto che il sistema deve essere in grado di ricevere dei comandi anche mentre effettua l’aggiornamento del tabellone. Se il microcontrollore fosse impegnato in quest’operazione durante l'arrivo di un comando, il PIC lo decodificherebbe immediatamente, per poi riprendere l’aggiornamento da dove l’aveva lasciato prima di gestire l’interrupt.

Infine, la routine di interrupt ad alta priorità è dedicata alla gestione del tempo attraverso il timer interno (TIMER0). Questa routine è responsabile dello scorrimento del tempo (ogni volta che passa un secondo, il tempo viene aggiornato e viene richiesto un refresh) e di tutto quello che ha a che fare con la temporizzazione come, ad esempio, la funzione della modalità di standby automatico o le routine di lampeggio.

Per la gestione di questi due interrupt è stato necessario impostare un sistema di priorità a causa della lunga durata della routine di lettura dei dati ricevuti: approssimativamente 360 ms.

Gli interrupt da TIMER0 arrivano ogni 65.536 ms e, se mancasse un sistema a priorità, sarebbe servito solo il primo interrupt arrivato. Nel caso in cui, durante lo scorrimento del tempo, fosse ricevuto un comando, si entrerebbe nella routine di lettura dei dati per 360 ms (ignorando 5 interrupt del timer) col risultato di rallentare lo scorrere del tempo di 328 ms per ogni comando ricevuto.

Dando una priorità bassa all’interrupt relativo alla ricezione ed una priorità alta all’interrupt del timer questa situazione non rappresenta più un problema, poiché l’interrupt del timer può interrompere la lettura dei dati.

Questa decisione potrebbe sembrare inopportuna perché ogni interrupt del timer arresta temporaneamente la lettura dei dati in arrivo, rischiando di interrompere la sincronia degli istanti di decisione. In realtà la durata della routine relativa all’interrupt del timer è minima (qualche decina di microsecondi) e, in quanto tale, non è assolutamente in grado di interferire con la lettura dei dati in arrivo.

Seguono i diagrammi di flusso delle tre routine correlati dalle relative spiegazioni. Essi sono soltanto un riassunto delle principali operazioni svolte nel programma, disegnare un diagramma comprensivo di tutte le funzioni sarebbe lungo ed inutile dato che tali informazioni sono facilmente reperibili nel codice sorgente.

Il seguente diagramma di flusso schematizza lo svolgimento della routine principale:

Dopo l’inizializzazione e la configurazione delle periferiche e delle porte, si entra nel ciclo principale. Questo ciclo prevede il controllo del flag di richiesta di aggiornamento e, in caso sia attivo, esegue un aggiornamento.

In seguito viene controllato anche il flag di richiesta della modalità salvaschermo/power-saving.

Se tale flag è attivo si entra nella routine salvaschermo.

Questa routine, non descritta nei diagrammi di flusso, dura finché il flag di richiesta del salvaschermo resta attivo.

Siccome si svolge come una parte del ciclo principale può essere interrotta regolarmente dagli interrupt.

In questo modo, quando un comando viene ricevuto e convalidato, il flag di salvaschermo viene azzerato e la routine termina istantaneamente, tornando al ciclo principale del programma.

Per scelta progettuale, ogni comando ricevuto in modalità salvaschermo la interrompe ma non viene eseguito. Così è possibile interrompere il salvaschermo premendo un tasto qualsiasi senza preoccuparsi delle conseguenze.

La routine relativa all’interrupt del timer è la seguente:

(clicca sull'immagine per vederla più grande)

Si controlla se il tabellone è inattivo da almeno mezz’ora e che non stia scorrendo il tempo. In questo caso viene abilitato il flag di richiesta salvaschermo (modalità power-saving automatica).

Viene poi verificato il flag che indica se il tempo stia scorrendo o meno:

In caso negativo, la routine termina qui.

In caso positivo, si incrementa un contatore e, se tale contatore ha raggiunto il valore di 16, si considera passato un secondo.

Nel codice sorgente sono visibili alcuni aggiustamenti per fare sì che questa temporizzazione sia precisa al microsecondo, in sostanza viene modificato il valore di preload del timer ad ogni interrupt.

Essendo passato un secondo, il tempo viene decrementato e il flag di richiesta refresh viene impostato.

Se il tempo è arrivato a zero il suo scorrimento viene fermato e, nel caso sia abilitata, viene lanciata la segnalazione acustica.

La routine dell’interrupt a bassa priorità si occupa della ricezione, della decodifica e dell’attuazione dei comandi ricevuti via radio:

Innanzitutto viene verificato il primo bit ricevuto: in caso questo non sia valido, la routine termina. In caso contrario, si procede alla lettura e al salvataggio del codice di comando e del codice di controllo.Se i due codici corrispondono il comando viene eseguito, altrimenti viene ignorato.

In ogni caso viene abilitato il flag di richiesta aggiornamento prima di ritornare al ciclo principale.

Qui di seguito puoi scaricare il codice sorgente in assembler o il file esadecimale già compilato.


Codice Sorgente