di Grazia Ancona
Concludiamo il nostro lavoro analizzando il firmware della nostra applicazione e tutte le opzioni dell’interfaccia utente. La presente puntata si occuperà anche della presentazione dei dati raccolti su un grafico elaborato da EXCEL.
VUOI GUARDARE LA PRIMA PARTE? CLICCA QUI!
Una visione generale del firmware
La volta scorsa (guarda articolo) abbiamo descritto soltanto la funzione di uno dei 4 timer/contatori interni al PIC: TMR1. Ricordiamo che questo, nella nostra applicazione, è un vero e proprio orologio in grado di dare un “tocco” ogni 15 secondi per mezzo di un’interruzione. Quest’ultima è causata dal suo “overflow” (bit TMR1F settato). Questi “tocchi” vengono poi contati e, allo scadere del 24°, viene prelevato un campione di potenza istantanea. Si avranno quindi 360 secondi (24 x 15) fra un campione e l’altro e, con questo intervallo, i dati ci saranno presentati sul grafico. Il tutto potrà essere più chiaro osservando la Figura 1.
Occupiamoci adesso di TMR0 (nella nostra applicazione, non sono usati TMR2 e TMR3). La funzione di questo contatore è fondamentale: esso ha il compito di memorizzare il numero di impulsi provenienti dal contatore. In altri termini, ogni fronte positivo dell’impulso incrementa il suo registro che ha un’ampiezza totale di 16 bit. Esso è a sua volta è diviso in due registri ad 8 bit: TMR1H e TMR1L.
Osservando la Figura 2 possiamo vedere come questa sezione sia connessa al resto del circuito. È interessante notare che sia TMR0 che TMR1 possono funzionare in modo del tutto asincrono dal flusso principale del programma. In altri termini, ci si dovrà preoccupare solo della loro inizializzazione e poi, al momento opportuno, leggere i registri che riguardano il timer interessato. Per TMR1 sarà il codice dell’interruzione, richiamato dall’overflow del suo contatore, ad incaricarsi del ripristino dei valori d’inizializzazione. Sono invece gli impulsi contati da TMR0 a fornirci un campione di potenza elettrica assorbita, nell’arco di 360 secondi (sei minuti). Vediamo come, considerando il caso di un contatore che abbia la taratura a 1000 impulsi/KWh. Quest’ultimo dato ci dice che, nell’ambito di un’ora (3600 secondi), prelevando una potenza di un KW costante, la portante I.R. del LED installato sul contatore si interromperà 1000 volte. Ovvero verranno contati 1000 impulsi. Sapendo ciò, è’ possibile calcolare la potenza assorbita su 360 secondi (il dato espresso è riferito alla potenza in W/h, ovvero Watt/ora). La relazione è la seguente:
W/h = (N° imp. x 3600) / 360
Come si può osservare, Il prodotto degli impulsi è diviso per 360 in quanto questi sono i secondi in cui avviene il conteggio. Se il contatore fornisce 10000 impulsi/KWh è sufficiente dividere per 10 il risultato ottenuto, ovvero:
W/h = ((N° imp. x 3600) / 360) / 10
I calcoli riportati sono ovviamente fatti per ogni campione di potenza assorbita che, nell’ambito di due campionamenti consecutivi, potrebbe non essere costante. In questo caso, il numero degli impulsi è mediato dall’algoritmo cosicché il dato ottenuto rappresenta, per l’appunto, la media dei consumi in 360 secondi (vedi Figura 3).
I campioni (dati) raccolti sono subito salvati in una matrice composta da 240 colonne e due righe. Nella riga 0 sarà caricato il byte MSB mentre, nella riga 1, troverà posto quello LSB. Ricordiamo che il campione, a fronte della sua estensione numerica, deve necessariamente essere composto da 2 byte. Questo comporterà una certa ridondanza di bit nel byte MSB, tuttavia ciò non sarà un problema per la RAM di questa CPU che è sovrabbondante per i nostri scopi. Una volta terminata la fase di campionamento, i dati saranno trasferiti nella EEPROM, se questa è presente. Da questo momento in poi l’utente potrà introdurre i dati sul foglio Excel oppure, se lo desidera, fare un nuovo campionamento. In quest’ultimo caso, ovviamente, i dati precedenti andranno perduti. È bene precisare, invece, che il riversamento dei dati su PC non provoca la distruzione degli stessi e l’operazione potrà essere ripetuta più volte. Per una migliore chiarezza di quanto esposto possiamo dare uno sguardo al digramma di flusso di Figura 4. Questi non è sicuramente un flow-chart dettagliato ma da certamente un’idea di come procede il flusso principale del programma Le opzioni a disposizione dell’utente sono inserite nel riquadro trasparente.
Osservando la Figura 4, si può notare come tutte le funzioni principali siano sottoposte a verifica all’interno del flusso stesso e, in caso d’anomalia, viene visualizzata, una condizione di allarme. Vedremo meglio, in seguito, come l’utente può capire, dall’accensione dei LED, se l’applicazione è in condizione d’errore. Le anomalie che possono essere rilevate sono:
- Tensione batteria inferiore a 4,1 VDC (batterie scariche).
- Eventuali errori sul bus I2C (solo se vi è la EEPROM installata).
- Mancanza della portante I.R. sul fotodiodo.
- Uno o più LED non funzionanti (questa condizione è rilevata dall’utente)
Le condizioni a) e b) innescano un errore “irreversibile”. In altri termini, sarà necessario rimuovere la condizione di errore (magari fornendo di batterie cariche l’applicazione) e poi ripartire ridando l’alimentazione al dispositivo. Il test della EEPROM, se questa è installata, è abbastanza scrupoloso poiché vengono scritti 4 byte, uno per ogni banco da 256 byte, e poi riletti. Ovviamente, se un solo dato letto è differente da quello scritto la condizione d’errore è sicuramente accertata. La condizione c) è poco più complessa. Infatti, la presenza della portante I.R. (ricordiamo che, anche se non transitano impulsi, il LED del contatore emette un fascio I.R.) è verificata non appena l’utente decide di avviare un campionamento. In questo caso, se manca il segnale I. R. sul fotodiodo, magari per un mancato allineamento ottico con il LED del contatore, viene attivata la condizione di allarme. Tuttavia, non appena il fascio I.R. è rilevato il campionamento parte tranquillamente, ovvero la condizione d’errore è reversibile. La portante infrarossa è però controllata anche durante la fase di campionamento. In questo caso, la mancata rilevazione della portante I.R. conduce invece ad un allarme irreversibile. Quest’ultima scelta, apparentemente più “severa”, è necessaria poiché non possiamo sapere quanti impulsi non sono stati registrati e ciò condurrebbe a inevitabili errori sui dati raccolti. Veniamo adesso alla condizione d). Al momento dell’accensione, entrambi i LED si attivano per circa un secondo. L’utente deve solo verificare se questo accade regolarmente a mezzo di semplice osservazione. L’organizzazione dei dati all’interno della memoria RAM del PIC e, se presente, nella EEPROM merita un piccolo approfondimento. Sebbene nel flow-chart di Figura 4, per ovvii motivi di semplicità, la funzione non sia riportata, è bene precisare che ogni dato rilevato ogni 360 secondi è immediatamente memorizzato in una matrice. Quest’ultima è formata da 240 righe e 2 colonne, si accetta una ridondanza di capienza dati del 50% nel caso l’utente scelga un tempo di campionamento pari a 12 ore invece di 24. Le due colonne della matrice, definite canonicamente 0 e 1, contengono rispettivamente il byte MSB e LSB di un singolo dato a 16 bit. Ovviamente la prima riga (riga 0) conterrà il primo dato campionato e così via fino alla riga 239 (o 119 nel caso di campionamento su 12 ore). Se la memoria EEPROM non è installata i dati rimarranno in questa matrice fino al loro dumping su porta seriale e anche oltre. Se però viene tolta l’alimentazione al circuito la RAM, ovviamente, perderà ogni informazione. Questa evenienza, dati i tempi di campionamento piuttosto lunghi, può essere “in agguato” nel caso le batterie non fossero proprio al meglio delle loro prestazioni. È quindi caldamente consigliato l’uso di una piccola memoria seriale, se non altro per rendere più sicuro e comodo il tutto. Se questa è presente, non appena il campionamento è terminato i dati presenti sulla matrice (RAM) verranno copiati a blocchi di 16 byte (modalità di scrittura definita PAGE WRITING) nella EEPROM. Ed a questo punto possiamo anche spegnere il tutto e rimandare l’operazione di dumping a… quando ci sarà più comodo farlo! Infatti, ormai la memoria conterrà i dati campionati e l’unico modo per cancellarli sarà effettuare un nuovo campionamento. Ovviamente, all’accensione, il lampeggio dei LED indicherà che la memoria EEPROM ha dati validi al suo interno. Va osservato che la quantità del numero dei dati in memoria, derivati dalla scelta dell’utente di campionare su 12 o 24 ore, è indicato in un’apposita locazione della stessa EEPROM. Questo fa sì che un eventuale dumping su dati raccolti in 24 ore non si interrompa a metà, ovvero dopo aver elaborato mezzo grafico, nel caso il ponticello JP2 fosse settato su 12 ore. In altri termini, nella fase di dumping con EEPROM installata, la lettura dell’apposito byte memorizzato sulla EEPROM ha priorità maggiore rispetto alla condizione di JP2. Nella Tabella 1 troveremo il dettaglio di quanto detto ed altre informazioni circa la mappatura della EEPROM.
La modalità in cui viene effettuato il “filling” della EEPROM merita, a mio parere, un approfondimento. Abbiamo accennato come i dati stessi, non appena raccolti, siano subito caricati in una matrice di dimensioni adeguate che, per una migliore organizzazione del programma, contiene il byte MSB del dato sulla prima colonna e quello LSB sulla seconda. Questo discorso non è invece applicabile alla EEPROM che dobbiamo immaginar come un unico vettore, composto di 1024 locazioni consecutive. Questo spazio di memoria è a sua volta diviso (per oscure ragioni costruttive…) in 4 banchi da 256 byte ed ogni banco è a sua volta diviso in 16 pagine da 16 byte. La Figura 5 ci aiuterà a capire questo concetto.
La suddivisione in pagine può essere sfruttata sia in lettura che in scrittura in quanto è possibile accedere, con grandi vantaggi di tempo e di istruzioni, a 16 byte consecutivi. Tutto questo senza dover fornire un nuovo indirizzo per il byte successivo (le operazioni di lettura/scrittura sono necessariamente sequenziali). In effetti, il nostro programma, effettua letture e scritture in questa modalità utilizzando, per entrambe, un buffer di dimensioni adeguate (16 byte in formato “char”). Il buffer è poi riversato nella matrice contenente i dati per poi riversarsi, a fine campionamento, nelle EEPROM (se prevista). Quindi, per leggere o scrivere nella EEPROM sarà necessario:
- Impostare il bit che indica lettura o scrittura nell’apposita parola.
- Fornire un indirizzo di banco sui 4 disponibili (2 bit).
- Fornire un indirizzo di pagina su 16 disponibili (4 bit).
- Leggere o scrivere i dati di un’intera pagina (16 byte ogni operazione).
Appurato tutto ciò, per i dettagli del protocollo I2C vi invito a consultare l’ampia letteratura in merito (vedi paragrafo “Bibliografia”). Un piccolo ausilio grafico può esserci d’aiuto per capire come si interfacciano la matrice RAM e la EEPROM seriale, esso è mostrato in Figura 6.
Come si può notare, la matrice in RAM è idealmente suddivisa in 30 pagine, composte ognuna da 16 byte, a loro volta organizzati in 8 righe e 2 colonne. Il riempimento della memoria EEPROM avviene ponendo il byte LSB del dato sugli indirizzi di pagina pari (0, 2, 4, ecc.) ed il byte MSB su quelli dispari (1, 3, 5, ecc.). In questo modo si stabilisce un parallelismo fra una pagina della matrice RAM ed una pagina della EEPROM. Quest’ultima, infatti, non può essere organizzata in righe e colonne, coma una matrice, ma appare ai dati come un unico vettore da 1024 locazioni di un byte. Essendo la capienza massima prevista pari a 480 byte (240 campionamenti da 2 byte cadauno), non tutta la EEPROM verrà riempita. Abbiamo precedentemente citato l’evenienza che, durante la fase di campionamento, possa venir meno la presenza della portante I.R. proveniente dal contatore. In questo caso l’applicazione si porta in condizione di allarme irreversibile. Il tutto è stato risolto, via firmware, come in Figura 7. In effetti Il problema nasce quando si hanno contatori a 10.000 impulsi/KWh e potenze impegnate notevoli (superiori ai 3 KWh).
In questi casi, tutt’altro che infrequenti, il segnale I.R. prodotto dal LED del contatore va a livello basso (presenza impulso) più di 9 volte in un secondo. Considerando la durata dell’impulso pari a 50 ms (dato costante indipendentemente dal consumo) si totalizza un tempo totale a livello “L” pari a 450 ms. Ne consegue che il tempo a livello “H” (assenza d’impulso) totalizzerà i restanti 550 ms. In altri termini il duty-cycle risultante sarà pari al 46% (periodo a 3,3 KWh / durata impulso). Con uno scarto appena del 4% il semplice polling della linea d’ingresso impulsi sarebbe stato problematico: sicuramente il programma avrebbe considerato le alte potenze come una mancanza di portante I.R. A questo punto abbiamo pensato di effettuare una serie di campionamenti a intervalli regolari (polling) della linea interessata, tutto questo nell’arco di 15 s., ovvero fra due interrupt per overflow di TMR1. Ogni volta che la linea viene trovata ad “H” si assegna ad una variabile un punteggio positivo. In caso di linea nello stato “L”, invece, uno negativo. Il bilancio del punteggio dei campionamenti effettuati, allo scadere dei 15 s., decide l’esito del test. Infatti, se la sommatoria algebrica dei punti è maggiore di 0 tutto va bene, un numero negativo indica invece l’assenza di portante infrarossa. In altri termini, la somma di tutti gli istanti in cui il LED I.R. del contatore emette un impulso non può comunque superare gli istanti in cui il segnale è a riposo (livello “H”). Se questo accade è evidente che il segnale infrarosso è venuto meno e si innesca una condizione d’allarme. Il codice sorgente, in linguaggio C tradotto dal potente compilatore CCS, è comunque ampiamente commentato in ogni istruzione mentre, i punti salienti, sono evidenziati con altrettanti commenti. È quindi abbastanza facile, con l’ausilio del diagramma di flusso di Figura 4, comprendere la sequenza delle istruzioni a beneficio di tutti i lettori che potrebbe voler personalizzare alcune funzioni.
L’interfaccia utente
Come interagisce l’utente con la nostra applicazione? Il titolo di questo capitolo è in effetti un po’ enfatico se si pensa che il tutto è composto da:
- Due LED di diverso colore.
- Un pulsante.
- Due DIP-FIX (ponticelli).
L’intenzione di rendere questo data-logger economico e semplice ha imposto una certa parsimonia di elementi e la cosa mi pare sia stata colta in pieno. Cominciamo dai DIP-FIX, la cui funzione è già stata accennata, parlando dell’hardware, nella prima puntata. In questa sede diremmo solamente che devono essere predisposti prima dell’accensione del dispositivo poiché, com’è logico, vengono letti solo nella fase d’inizializzazione. Si faccia soprattutto attenzione alla predisposizione di JP1: quest’ultimo informa il programma dell’esatta frequenza di ripetizione degli impulsi I.R. Un’impostazione sbagliata fornirebbe dati del tutto errati! Le funzioni dei due LED devono invece essere commentate con attenzione. Infatti, la nostra applicazione può comunicare i suoi stati solo attraverso quest’ultimi. Ho quindi riassunto in 3 tabelle commentate tutte le combinazioni possibili (dieci). Occorre comunque tener conto che alcuni stati sono segnalati con combinazioni di acceso/spento che interessano entrambi i LED.
Per una migliore comprensione di quanto esposto nelle tabelle 2, 3 e 4, soprattutto nei riguardi dei tempi di ON/OFF di accensione LED, è possibile consultare la Figura 8 A-B. In essa troveremo il riferimento alla riga della tabella ove la sequenza è spiegata in dettaglio. Esso è posto nell’immediata sinistra della sequenza d’accensione raffigurata. Nelle tabelle il riferimento che punta alla riga della Figura 8 A – B è riportato nella prima colonna (indicata con la scritta “Rif. Figura 8 x”). In sostanza, i due LED ci danno informazioni riguardo:
- Lo stato della memoria.
- Se, con dati immagazzinati, è stato fatto o meno almeno un dumping su PC.
- Un brevissimo segnale di campionamento in corso (lampeggio ogni 15 sec., ovvero il “respiro” dell’applicazione).
- Dumping dati in corso.
- Eventuali allarmi in corso.
- Test-monitor degli impulsi provenienti dal contatore (solo su richiesta dell’utente all’accensione).
Ovviamente, per improrogabili motivi di risparmio energetico, durante la lunga fase di campionamento si è preferito ricorrere ad un lampeggio brevissimo. Parimenti, l’intervallo fra due lampeggi consecutivi, è scandito dagli interrupt provenienti da TIMER1 (circa 15 secondi).
L’ultimo elemento d’interfaccia con l’utente è il pulsante P1. Esso ha 3 funzioni che sono differenziate dal tempo di pressione esercitato sul pulsante stesso. Per semplicità, ho preferito riassumere il tutto nella tabella 5. Unica nota: per tempo di pressione “prolungato” si intende circa 5 secondi.
L’analisi grafica dei dati raccolti
Supponiamo adesso di avere compiuto un intero ciclo di acquisizione, non importa se di 12 o 24 ore, e vediamo come possiamo visualizzare ed analizzare i dati raccolti. Abbiamo già accennato che saremmo ricorsi, a livello di presentazione-applicazione, al programma Excel. Esso, nelle sue versioni più recenti, può aiutare il nostro processore che non è in grado di svolgere performance grafiche sufficienti per i nostri scopi. L’interfaccia “fisica” verso il PC è costituita dalla porta seriale RS232 che, nel nostro caso, lavora in modo unidirezionale (nessun dato fluisce dal PC all’applicazione). Non è utilizzato alcun protocollo hardware ed i dati sono trasmessi semplicemente sotto forma di caratteri ASCII a 8 bit. Purtroppo Excel non permette l’acquisizione diretta dei dati da porta seriale, sia quest’ultima di tipo RS232 che USB. Questo problema è risolto con l’aiuto di una “macro”, ovvero un piccolo programma in Visual Basic. Questi, attingendo al buffer della porta seriale, permette l’acquisizione dei dati stessi sulle celle di un foglio Excel. Ovviamente il nostro foglio dati deve essere abilitato ad usare le macro (il programma potrebbe chiedere questa conferma all’utente). Gli interessati troveranno i file .xlsm già pronti per l’uso, con il piccolo programma in Visual Basic già caricato (VEDERE IN BASSO PER SCARICARE IL FILE COMPRESSO). Essi sono due: il primo è denominato Grafico 24 h.xlsm mentre il secondo è Grafico 12 h.xlsm. Ovviamente il primo sarà usato per campionamenti su 24 ore mentre il secondo si occuperà di quelli su 12 ore. L’estensione .xlsm sta a significare che si tratta di un foglio Excel provvisto, per l’appunto, di macro. All’apertura del file il programma richiederà all’utente la conferma per usare la macro presente al suo interno, dovrebbe apparire un pulsante come quello indicato su Figura 9.
Cliccando sul pulsante indicato si aprirà un menù molto semplice, anche questo mostrato in Figura 10.
Una volta attivate le macro il nostro foglio è pronto a ricevere i dati dalla porta seriale. Essi saranno ospitati nella prima colonna (colonna “A”) denominata “Watt/h”. Questa colonna, riportante i campioni di consumo raccolti, nel foglio EXCEL Grafico 24 h.xlsm, disporrà di 240 celle. Ovviamente, nel foglio Grafico 12 h.xlsm, le celle saranno soltanto 120. La colonna a sinistra (colonna “B”) riporta le ore (ascisse del grafico) che dividono idealmente la colonna dei campioni in gruppi di 10 (un campione ogni 6 minuti). In Figura 11 è possibile vedere il grafico risultante e parte delle colonne “Watt/h” e “ore”. La figura, essendo stata prodotta in fase di test, mostra il consumo nelle 24 ore diviso in 4 valori costanti (le prime sei ore corrispondono ad un consumo di 800 W/h).
Non ci si preoccupi di cancellare le celle già riempite a seguito di un dumping, un nuovo scaricamento dei dati sovrascriverà semplicemente il vecchio contenuto. Coloro che conoscono bene Excel saranno sicuramente in grado di visualizzare il grafico su un altro foglio, chiamato “Dashboard”, che attinge i dati dal primo. In questo modo, aumentando le dimensioni, potremmo studiare l’andamento del consumo con maggiore dettaglio e comodità. Buon lavoro e… occhio ai consumi!
Bibliografia
“I2C-bus specification and user manual” UM10204 Rev. 6 (NXP Semiconductors)