Spunti: livello di carica di una batteria con un microcontrollore PIC e i moduli ADC e FVR

Quella di cui sto per parlare è una soluzione molto semplice e che implemento nel momento in cui realizzo un dispositivo a batteria: un semplice partitore resistivo per scalare la tensione della batteria da monitorare con un ingresso A/D di un microcontrollore.

A dire il vero, il partitore di tensione non è sempre necessario: lo è soltanto nel momento in cui la tensione da leggere supera quella di alimentazione del microcontrollore o quella del riferimento di tensione. Se stiamo utilizzando un microcontrollore alimentato a 3.3V e dobbiamo leggere la tensione fornita da due batterie AA, pari a 3V, allora la si può leggere direttamente senza partitore, ma nel momento in cui la tensione è superiore, o rischia di esserlo, il partitore di tensione diventa necessario. Ci sono anche delle considerazioni da fare nel momento in cui la batteria da monitorare è la stessa che alimenta anche il microcontrollore.

Farò un semplice esempio con un microcontrollore PIC che ho sottomano ultimamente, un PIC16F1825, e una batteria LiPo. Dato che userò il Code Configurator, l’esempio è facilmente adattabile ad ogni PIC.

Considerazioni

La batteria che userò come riferimento è una LiPo in formato 1865o che ultimamente ho trovato come giusto compromesso tra costi, disponibilità, facilità di trovare moduli economici per lo step-up e la carica e quantità di corrente. 

C’è anche da dire che comunque reputo le LiPo un po’ pericolose, per cui la mia attenzione si sta spostando verso le LiFePO4 che risultano essere più sicure, ma per le quali non c’è ancora tutta la disponibilità di batterie ed accessori a prezzi economici come per le LiPo

Le celle LiPo hanno una tensione massima di 4.2V a piena carica e non vanno scaricate sotto i 3.2 ÷ 3.0V, pena danneggiamento permanente. Alimentando il PIC a 3.3V non posso fornire all’ingresso A/D la tensione della batteria in maniera diretta: probabilmente il PIC la tollera nel momento in cui sto usando un modello che funziona anche a 5V, ma è chiaro che al di sopra della tensione di alimentazione verrà sempre letto il valore massimo (1023 nel caso di un A/D a 10bit), per cui da 3.3V a salire (fino a 4.2V) il modulo A/D segnalerà sempre un valore di 1023 lasciandoci credere che la batteria è carica.

Se siete a digiuno di come funziona il modulo A/D di un microcontrollore PIC vi invito prima a leggere i vecchi articoli:

Il modulo FVR

Con una tensione da monitorare più elevata di quella con cui alimentiamo il microcontrollore si rende quindi necessario un partitore di tensione per non superare i 3.3V di alimentazione (o la tensione del riferimento).

In alcuni casi molte persone preferiscono, invece, un convertitore tensione/frequenza: si tratta di circuiti integrati che misurano una tensione in ingresso e forniscono un’uscita ad onda quadra con frequenza direttamente proporzionale alla tensione misurata.

Il modulo ADC dei microcontrollori PIC ha la possibilità di avere un riferimento di tensione diverso dalla Vdd di alimentazione, in particolare su tutti i nuovi modelli è possibile utilizzare il modulo FVR (Fixed Voltage Reference). Tale modulo produce una tensione cosante, stabile, pari a 1024mV ed è possibile impostare un guadagno pari a 1x, 2x o 4x che consente di avere una tensione di riferimento, fisssa, di 1024mV, 2048mV o 4096mV.

Se il PIC viene alimentato a 3.3V, il guadagno di 4x, che porta ad avere un riferimento di 4.096V, non è disponibile.

In aggiunta, il modulo FVR può fornire la propria tensione anche ad altri moduli (come il DAC) oltre che al modulo ADC. Nel mio caso voglio utilizzare il modulo ADC con la tensione di riferimento fornita dall’FVR anzichè da Vdd.

Perchè usare l’FVR? L’utilizzo di base del modulo ADC prevede che la tensione di riferimento sia la stessa di alimentazione: la CPU deve avere un riferimento basso (in genere GND) e uno alto (Vdd o altro) per poter affermare con certezza che una tensione misurata è di X volts. Nel caso in cui la tensione di alimentazione abbia delle oscillazioni, anche il risultato delle conversioni subirà le stesse oscillazioni: se normalmente alimentiamo il PIC a 3.3V e questi misura una tensione di 3.3V, restituirà il valore di 1023 (stiamo prendendo in considerazione un modulo ADC a 10bit), ma se ad un certo punto la batteria con cui stiamo alimentando il PIC si scarica e fornisce, ad esempio, 2.8V, a questo punto il riferimento alto è diventato 2.8V per cui il valore di 1023 mi verrà restituito non più a 3.3V ma a partire da 2.8V falsando tutte le misurazioni.

Avere a disposizione un modulo FVR consente di avere una tensione fissa, stabile, indipendente dalla tensione di alimentazione (fino a certi livelli): è chiaro che se stiamo usando il modulo FVR settato a 2,048V e la tensione di alimentazione scende sotto tale valore, anche il riferimento salta.

I PIC ad 8bit, a meno che non siano in versione LF, non funzionano più a quella tensione e ad ogni modo abbiamo sempre il settaggio del BrownOut reset che ci consente di tenere il PIC in reset fino a che la tensione di alimentazione non arriva al livello desiderato.

Impostare ADC e FVR da MCC

Proseguiamo con l’esempio. Dall’MPLAB Code Configurator, includiamo il modulo FVR (doppio click sul modulo FVR in Device Resources) e impostiamolo come segue:

Spuntiamo Enable FVR e selezioniamo il guadagno del buffer 1, che imposta la tensione in maniera separata per il modulo ADC (il buffer 2 è usato per le altre periferiche e nel nostro caso lo impostiamo ad OFF).

Va quindi impostato il modulo ADC per poter utilizzare il modulo FVR. Andando ad includere il modulo ADC dalla finestra Device Resources, potremmo avere due voci:

  • ADC [PIC10 / PIC12 / PIC16 / PIC18 MCUs by Microchip Technology, Inc.]
  • ADC [Foundation Services Library by Microchip Technology, Inc.]

La seconda voce, Foundation Services, include delle librerie semplificate che non permettono il settaggio di alcuni parametri: includiamo la prima libreria.

Impostiamo il modulo ADC come segue:

In particolare vedete che:

  • Abbiamo impostato la sorgente di clock su Frc, che consente di avere il TAD sempre corretto con qualunque clock esterno
  • Il risultato è stato allineato a destra
  • Abbiamo impostato come riferimento positivo FVR anzichè Vdd

Se alcune cose di cui sto parlando non sono chiare, è perchè non avete letto gli articoli vecchi sul modulo ADC, che ripeto, sono: [1] [2] [3] [4]

Premetto che il PIC in oggetto ha il modulo ADC, altri PIC di fascia alta hanno invece il modulo ADCC (ADC con Computazione) che consente di eseguire alcune operazioni in automatico, per ulteriori informazioni vedi links 3 e 4 nella nota precedente.

Dopo fatto questo, nella griglia del pin manager compare anche il modulo ADC e possiamo impostare quali pin utilizzare con la funzione analogica:

Nel mio caso ho chiuso il lucchetto su RA4, quindi avrò solo RA4 con la funzione analogica, e dal pin module (ma anche dall’immagine del PIC), si vede che RA4 è stato nominato automaticamente come channel_AN3

Ritorniamo alla finestra delle impostazioni dell’ADC e clicchiamo sulla scheda Registers per poter modificare a mano alcuni registri:

In CHS selezioniamo AN3 per fare in modo di avere tale canale già selezionato e pronto.

Nel mio esempio, scaricabile, ho incluso anche la USART per avere l’output su seriale, tralascio questa parte dato che questo esempio l’ho già affrontato in questo articolo, premendo il pulsante Generate in alto a sinistra in Project Resources, viene generato il codice. Alla fine della generazione del codice possiamo chiudere l’MCC.

Scalare i valori letti

Abbiamo quindi fatto in modo che l’ADC ora utilizzi l’FVR a 2,048V (2048mV), per cui, essendo l’ADC a 10bit, questo mi restituirà un valore pari a 1023 nel momento in cui viene letta una tensione di 2048mV, per cui posso approssimare dicendo che: tensione in millivolt = lettura ADC * 2

Ora, però, non devo fare più in modo che la tensione che raggiunge l’ingresso analogico non sia superiore a 3.3V, tensione di alimentazione del PIC, bensì che non sia superiore a 2,048V, altrimenti leggerò sempre 1023 per tensioni superiori a questa.

Andremo quindi a realizzare un partitore di tensione, con dei valori di resistenze che ci consentano di non superare questa soglia:

La formula del partitore di tensione è la seguente:

Nel nostro esempio abbiamo che Vbat=4.2V (tensione massima fornita dalla batteria) e Vout non lo vogliamo superiore a 2.048V. Indicando tutto il rapporto R2/R1+R2 come α, abbiamo che:

Che, con i nostri valori di tensione, restituisce un valore di 0,48. Una volta scelto uno dei due valori R1 o R2, è facile trovare l’altroi mediante la formula inversa:

Mettendo come valore di R2 1200Ω, viene fuori R1=1300Ω che sono due valori commerciali a partire dalla serie E24 (5% di tolleranza). Io, ad ogni modo, non voglio tenermi troppo stretto ai 2.048V massimo in ingresso e scelgo come R1 un valore leggermente più alto: 1500Ω. Con R1=1500Ω e R2=1200Ω avrò il rapporto α pari a 0.44, per cui con una tensione di 4,2V in ingresso al partitore mi ritroverò sull’ingresso analogico una tensione pari a Vout=Vbat*α=4.2*0.44=1.848V.

Dato che sto usando il modulo FVR impostato a 2x, e il modulo ADC è a 10bit, da cui mi consegue che 2,048V mi portano ad una lettura di 1023, posso impostare una semplice proporzione per risalire al valore numerico che mi restituirà l’ADC per quel valore di tensione:

da cui x (valore letto all’ADC per 1.848V in ingresso all’ADC, pari a 4.2V in ingresso al partitore) = 923. Per un valore all’ADC da 923 a salire, quindi, potrò dare un’indicazione di batteria carica al 100%. Abbiamo detto sopra che è bene, con una batteria LiPo, non scendere sotto i 3.2 ÷ 3.0V per cella. Imposto un valore di 3.2V per stare tranquillo e, usando lo stesso sistema, mi trovo il valore che dovrò leggere all’ADC per indicare batteria scarica.

In realtà mi metto a 3.2V anche perchè in alcune mie applicazioni utilizzo un modulo DC/DC switching per ricavare una tensione più alta a partire da quella fornita dalla LiPo e tale modulo, siglato SX1308, non accetta in ingresso una tensione inferiore a 3.2V, per cui avere questa tensione minima in ingresso mi assicura che il regolatore continua a funzionare correttamente e che posso inviare la segnalazione di batteria scarica.

3.2V di tensione alla batteria, mi portano a (3.2*0.44)=1.408 all’uscita del partitore, che saranno letti dall’ADC come 703. Di conseguenza sarò sicuro di avere la batteria in buone condizioni quando il valore letto dall’ADC sul partitore è compreso tra 703 (batteria da ricaricare) a 923 (batteria completamente carica).

In un’applicazione semplice possiamo semplicemente fornire un’indicazione di batteria scarica quando leggiamo 703 e bloccare tutte le operazioni: dal momento che il microcontrollore deve segnalare batteria scarica, magari accendendo un led o segnalandolo su un display, può essere utile, per risparmiare corrente, portare il clock al minimo ammissibile (nel caso in cui sia utilizzato il clock interno) e spegnere tutte le periferiche, oppure, ancora, dare la segnalazione subito dopodichè entrare in stand-by: l’importante è non scendere troppo con la tensione in modo sia da non danneggiare la batteria, sia da avere la possibilità di avere la segnalazione.

Download

Il codice di esempio utilizza un PIC16F1825, prevede il partitore su RA4 come descritto sopra, l’alimentazione a 3.3V (dal progetto è stato già impostato che viene fornita dal PICkit4, se usate un programmatore diverso, fate la modifica) e fornisce un’uscita seriale a 9600-N-8-1 sul pin n°6 dalla quale possiamo leggere, tramite un adattatore UART/USB, il valore fornito dall’ADC.

Misurare senza IO

Nel vostro progetto non avete più IO a disposizione per misurare la tensione della batteria e segnalare quando è scarica? Non ci sono problemi: esiste un trucchetto che consente di misurare la tensione di alimentazione senza utilizzare IO. Ho illustrato qui questa tecnica.

Se questo articolo ti è piaciuto, condividilo su un social:
Se l'articolo ti è piaciuto o ti è stato utile, potresti dedicare un minuto a leggere questa pagina, dove ho elencato alcune cose che potrebbero farmi contento? Grazie :)