Spingiamo al massimo l’ADC del PIC

Convertitore Analogico Digitale dei microcontrollori PIC

Stressare i componenti elettronici fino al massimo delle loro possibilità è sempre una attività molto interessante. Questa volta a fare da cavia è il modulo ADC del PIC. Si verificheranno sul campo le limitazioni fisiche, in termini di velocità e tensione, sino alle estreme possibilità del microcontrollore.

La funzione del convertitore ADC è quella di acquisire in ingresso una tensione analogica (compresa, per esempio, tra 0V e 5V) e trasformarla in una grandezza binaria equivalente. Questa funzione è adesso presente all'interno dei microcontrollori di fascia media e alta, e toglie al progettista tantissimo lavoro di sviluppo e di costruzione. In altre parole, i registri dell'ADC possono contenere, ad un certo istante, un valore corrispondente ad una combinazione binaria, formata da 10 Bit (da 0 a 1023), proporzionale al valore analogico in ingresso sulle porte "AN".

Nel presente articolo non sarà spiegata l'argomentazione di base sugli ADC, per le quali si rimanda ad altre trattazioni, ma verranno effettuate sperimentazioni per spingere al massimo la conversione da analogico a digitale e verificare i limiti dei microcontrollori, oltre i quali la funzionalità viene a degradare o venir del tutto meno.

Il sistema di sviluppo utilizzato è la scheda EasyPIC, prodotta dalla mikroElektronika, ma l'utente è libero di scegliere qualsiasi altra soluzione.

Il linguaggio di programmazione usato è il mikroBasic della mikroElektronika. Ovviamente si può optare, con le dovute modifiche, per altri linguaggi preferiti.

Il microcontrollore adottato per le prove è il PIC 16F877/A della Microchip. Si tratta di un potente modello di MCU, dotato di tante porte è capace di funzionare a velocità di clock elevate. Esso dispone di ben 8 porte analogiche, numerate da AN0 a AN7. Le stesse corrispondono ai seguenti pin della MCU:

  • AN0: pin 2;
  • AN1: pin 3;
  • AN2: pin 4;
  • AN3: pin 5;
  • AN4: pin 7;
  • AN5: pin 8;
  • AN6: pin 9;
  • AN7: pin 10.

Il registro ADCON1

Utilizzando gli ingressi analogici del PIC 16F877 non si può fare a meno di studiare, per poi utilizzare, il registro ADCON1. Esso stabilisce le modalità di funzionamento delle porte in questione e quali, tra esse, devono essere analogiche o digitali. In pratica esso effettua la configurazione delle porte analogiche, al fine di allineare il risultato (destra o sinistra, bit 7) e di selezionare (da bit0 a bit3) la porta o le porte da utilizzare. Può anche essere impostata una tensione di riferimento, secondo la seguente tabella:

Utilizzando un linguaggio ad alto livello, come il mikroBasic, il programmatore evita alcune impostazioni che, altrimenti, dovrebbero essere previste in linguaggio Assembler.

Schema elettrico di base

Si appronti, adesso, un prototipo generale di base, con cui si possono provare le funzionalità delle porte analogiche del PIC. Lo schema è composto dalle seguenti componentistiche principali:

  • Un microcontrollore PIC 16F877, dotato di 8 porte AN;
  • Un quarzo da 20 Mhz;
  • Un display LCD, utile per visualizzare i risultati;
  • Un set di 8 tensioni diverse, per testare le porte analogiche del micro.

Le tensioni delle 8 batterie utilizzate dispongono dei seguenti valori di voltaggio:

  • BAT2: 5 Volt;
  • BAT3: 4,5 Volt;
  • BAT4: 4 Volt;
  • BAT5: 3,5 Volt;
  • BAT6: 3 Volt;
  • BAT7: 2 Volt;
  • BAT8: 1 Volt;
  • BAT2: 0,5 Volt.

Primo firmware: test ciclico delle 8 tensioni

Il primo programma ha il compito di testare e visualizzare sul display, ciclicamente, la tensione presente sulle 8 batterie collegate ad altrettanti ingressi analogici del PIC. Il listato è molto semplice: dopo le inizializzazioni di rito, si prevede un loop infinito, nel quale vengono acquisite le tensioni analogiche sulle relative porte ed esse vengono visualizzate sul display, in formato digitale.

A questo punto è doveroso comprendere tale aspetto. Una tensione analogica presente sulla porta viene convertita, dal PIC, in un valore digitale a 10 bit (es: 1001011011). Tale valore può avere una estensione molto ampia, da 0000000000 (equivalente al valore decimale 0) a 1111111111 (equivalente al valore decimale 1023). Questo vuol dire che una qualsiasi tensione compresa tra 0 Volt e 5 Volt sarà considerata nel programma come un valore intero compreso tra 0 e 1023. La risoluzione di 10 bit conferisce una minima misurazione minuziosa permettendo di effettuare rilevamenti di variazione di tensione pari a 0,0048828125 Volt.

Segue il listato, scritto in mikroBasic ma adattabile, con opportune modifiche, ad altri tipi di "dialetto".

rem --- Cristallo di 20 Mhz---

program adc01

dim LCD_RS as sbit at RB4_bit

    LCD_EN as sbit at RB5_bit

    LCD_D4 as sbit at RB0_bit

    LCD_D5 as sbit at RB1_bit

    LCD_D6 as sbit at RB2_bit

    LCD_D7 as sbit at RB3_bit

    LCD_RS_Direction as sbit at TRISB4_bit

    LCD_EN_Direction as sbit at TRISB5_bit

    LCD_D4_Direction as sbit at TRISB0_bit

    LCD_D5_Direction as sbit at TRISB1_bit

    LCD_D6_Direction as sbit at TRISB2_bit

    LCD_D7_Direction as sbit at TRISB3_bit

main:

     dim adc_value as word

     dim txt as string[5]

     dim k as byte

     rem --------------------------

     Lcd_Init()                     ' Initialize Lcd

     Lcd_Cmd(_LCD_CLEAR)            ' Clear display

     Lcd_Cmd(_LCD_CURSOR_OFF)

adc1:

     ADCON1  = %10000000

     ADC_Init()  ' Initialize ADC module

     while true

           for k=0 to 7

               adc_value = ADC_Get_Sample(k)

               WordToStr(adc_value, txt)

               Lcd_Cmd(_LCD_CLEAR)

               Lcd_Out(1, 1, "Canale:")

               Lcd_Chr(1, 9, 48+k)

               Lcd_Out(2, 1, "Binario:")

               Lcd_Out(2, 10, txt)

               delay_ms(1000)

           next k

     wend

end.

Eseguendo il programma, il display mostrerà i seguenti risultati, dopo le operazioni di conversione A/D:

  • Canale: 0 Binario: 1023
  • Canale: 1 Binario: 921
  • Canale: 2 Binario: 818
  • Canale: 3 Binario: 716
  • Canale: 4 Binario: 614
  • Canale: 5 Binario: 409
  • Canale: 6 Binario: 205
  • Canale: 7 Binario: 102

Da questi risultati si capisce come il valore analogico in entrata, compreso tra 0 Volt e 5 Volt (decimale), sia stato trasformato e codificato in un altro valore parallelo e binario, compreso tra 0 e 1023 (intero). Per la conversione da un'unità di misura e viceversa è sufficiente effettuare una semplice proporzione.

Se il programma fornisce i risultati corretti (per chi ha realizzato praticamente il prototipo) si può procedere ai successivi esperimenti.

Secondo firmware: velocità massima di acquisizione

Le funzioni e le procedure ad alto livello di mikroBasic contengono, nel loro intimo (Assembler) tutte le direttive per intraprendere un corretto campionamento del segnale ed una buona misurazione del livello analogico. Anche se tali funzioni occupano, nel listato Basic, una sola riga di programma, "dietro le quinte" è nascosta una notevole quantità di codice ed una vasta mole di programmazione.

Questo vuol dire che le misure A/D sono eseguite rispettando anche i periodi di "start" e di "stop" che questo tipo di operazioni impongono.

Il prossimo esperimento ha lo scopo di "contare" quante misurazioni analogiche avvengono in un certo lasso di tempo, per meglio valutare le prestazioni dell'A/D converter. Si ricorda che l'utilizzo di un linguaggio ad alto livello, quale il Basic, porta inevitabilmente ad un decadimento delle performaces del micro. Sarebbe meglio utilizzare linguaggi di programmazione a basso livello, quali l'Assembler o, quanto meno, il C.

Il programma prevede uno schema elettrico minimale, dal momento che ha solo la funzione di eseguire un milione di letture analogiche. Occorre semplicemente controllare il tempo nel quale il programma esegue tali letture e calcolare, di conseguenza, il tempo impegato da una sola acquisizione, mediante una semplice divisione.

Il diodo Led si illumina all'inizio del processo di lettura e si spegne al termine. Può essere utile per una misura manuale, tramite un cronometro. In alternativa, per ottenere maggiori precisioni temporali, è possibile collegare la porta RC0 ad un cronometro digitale o ad un datalogger elettronico, per meglio cronometrare il tempo impiegato. Questa ultima soluzione è stata scelta per eseguire le misure.

Qui sotto è mostrato il listato 2, anch'esso minimale ed estremamente semplice. Il suo scopo è quello di effettuare esclusivamente 1 milione di acquisizioni analogiche. Il compito dell'operatore è, invece, solo quello di "misurare" con sufficiente precisione tali operazioni, in modo da ricavare il tempo di ogni singola lettura. Come si vede dal listato, il diodo Led, collegato sulla porta RC0, si accende ad inizio processo e si spegne a letture ultimate.

rem --- Cristallo di 20 Mhz---

program adc02

main:

     dim adc_value as word

     dim k as longword

adc2:

     trisc.0=0

     portc.0=0     'Spegne LED

     ADCON1  = %10000000

     ADC_Init()  ' Initialize ADC module

     delay_ms(1000)

     portc.0=1     'Accende LED

     for k=1 to 1000000

         adc_value = ADC_Get_Sample(0)

     next k

     portc.0=0     'Spegne LED

end.

Bene, eseguendo il firmware, anche con qualsiasi tensione collegata alla porta analogica (ovviamente compresa tra 0V e 5V), vengono effettuate un milione di acquisizioni analogiche in 72 secondi. Ciascuna lettura, dunque, impiega 72uS (o 0,072 ms). In ciascun secondo sono effettuate ben 13888 rilevazioni analogiche. Teoricamente, con tale velocità, si potrebbe digitalizzare, con buoni risultati, anche un segnale di 1500 Hz.

Questo dato sarà estremamente utile in seguito, per i prossimi esperimenti e per fermare le nostre esigenze ai limiti fisici dell'ADC del PIC.

Il programma, dopo le consuete inizializzazioni e azzeramenti, attende un secondo per iniziare il suo lavoro vero e proprio. Inizialmente accende il diodo Led, posto sulla porta RC0. Dopo l'effettuazione di un milione di letture, il Led si spegne, dimostrando la fine delle operazioni. E' proprio questo diodo (o, più in generale la tensione presente sulla porta RC0) che fornisce la testimonianza dei tempi di start e di stop delle misure. Effettuando un record di tale segnale su un registratore di segnali visuale digitale, è possibile cronometrare, con molta precisione, la durata dell'intero lavoro eseguito. Da tale tempo (e dal numero di acquisizioni) si risale facilmente alla velocità delle stesse.

Riepilogando, ecco di seguito i risultati salienti di questo esperimento:

  • Acquisizioni e letture effettuate: 1.000.000;
  • Durata intera lettura: 72 secondi;
  • Tempo impiegato da ciascuna lettura: 72 uS;
  • Rilevazioni effettuate in 1 secondo: 13.888.

Terzo firmware: alziamo il tiro. Processare un'onda sinusoidale

Adesso che si conoscono i limiti imposti dall'ADC converter e, soprattutto, dal linguaggio di programmazione adottato, si può fare un salto di qualità affrontando una problematica ben più complessa: la misura statistica su un'onda periodica sinusoidale.

La tensione utile sarà prelevata da un trasformatore di tensione collegato alla rete luce, che fornisce una precisa onda sinusoidale alla frequenza di 50 Hz. Il trasformatore utilizzato possiede i seguenti requisiti:

  • Tensione primario: 230V;
  • Tensione secondario: 14V (zero-picco);

Gli altri parametri, come potenza, VA, ecc, non sono necessari in quanto, dal trasformatore, sarà prelevata una corrente alquanto esigua. Con le giuste variazioni, ognuno può utilizzare qualsiasi altra tipologia di trasformatore.

Ben lungi dal collegarlo direttamente al PIC, pena la distruzione immediata della MCU, ecco i due motivi per cui la tensione prelevata dal pesante componente ferroso non può essere subito utilizzata:

  1. La tensione zero-picco è pari a 14V e la tensione picco-picco è di 28V, limiti molto al di fuori della portata del PIC, che accetta al massimo 5V positivi rispetto a massa;
  2. La tensione del trasformatore è sinusoidale e contiene anche semionde negative, intollerabili dal micro che accetta, invece, una tensione negativa minima di VSS-0.3V, in pratica -0,3V.

Per questi motivi si deve prima approntare un piccolo circuito adattatore che adegui il range della tensione sinusoidale a livelli più accettabili e che, soprattutto, trasformi e sposti l'intera sinusoide sul quadrante positivo del dominio temporale. Entrambe le operazioni sono abbastanza semplici da effettuare.

Si esamini adesso lo schema elettrico, focalizzando l'attenzione sui due fatti appena esposti.

Tralasciando il commento sulla parte squisitamente logica del microcontrollore, lo schema è formato da due parti analogiche fondamentali:

  • Il partitore di tensione, che ha lo scopo di "abbassare" la tensione, proveniente dal trasformatore, ad un livello accettabile dal micro. Nella fattispecie, il segnale da misurare, prelevato dal nodo centrale del partitore, è abbassato grazie all'intervento delle due resistenze, R2 e R3. Stabilito un valore a piacere per R3 (non troppo basso per non soffocare la sorgente del segnale e non produrre eccessivo calore)  il valore di R2 si calcola facilmente con la formula: R2 = ((R3 * Vin) / Vout ) - R3;
  • Il fissatore di tensione (clamper) che ha lo scopo di far slittare la tensione alternata, da un valore di +Vc/-Vc ad uno compatibile con il micro, con tensione compresa tra di 2Vc e 0V. Il clamper è formato da C3, R4 e D2, di questi l'ultimo deve essere caratterizzato da una caduta di tensione la più bassa possibile. Allo scopo può essere utilmente impiegare un diodo al germanio o uno schotty (ad esempio il modello 1N5817).

Il grafico che segue mostra le tre differenti tensioni, misurate ai nodi cruciali del circuito, ossia:

  • Traccia verde: è il segnale sinusoidale misurato direttamente sul trasformatore. Come si può notare, esso è alternato e cambia la propria polarità 50 volte al secondo. E' compreso tra -14V e +14V;
  • Traccia ocra: è il segnale misurato al centro del partitore. Esso è ridotto rispetto al segnale di partenza, ma assume sempre una polarità positiva e negativa;
  • Traccia rossa: è il segnale da misurare, quello che entra direttamente nel PIC. Assume solamente una polarità positiva, tranne nei punti di "gola" che sono "leggermente" negativi. Tale ammontare dipende dalla differenza di potenziale ai capi del diodo del clamper. Questa leggera tensione negativa non viene letta, ovviamente, dal PIC. Occorre prestare attenzione a non superare il limite imposto dalla Microchip, che equivale a -0.3V, rispetto a massa, come si evince dagli "Absolute Maximum Ratings" del datasheet del PIC 16F877. Con un diodo ideale, tale problema non esiste.

Il segnale analogico da misurare è applicato alla porta AN0 del PIC, pronto per essere elaborato dal firmware che segue.

Adesso si esaminerà il firmware, adibito all'acquisizione del segnale analogico e alla relativa elaborazione statistiche. Si fa presente che il circuito accetta qualsiasi fonte di segnale, di qualunque forma d'onda. L'importante è adeguare le due resistenze del partitore per non oltrepassare il limite TTL e non superare limiti di frequenza eccessivamente elevati.

Il listato, abbastanza corposo, misura e visualizza, di un segnale periodico, i seguenti parametri:

  • Valore medio del segnale;
  • Valore Minimo;
  • Valore Massimo;
  • Valore RMS;
  • Tensione picco-picco.
rem --- Cristallo di 20 Mhz---

program adc03

dim LCD_RS as sbit at RB4_bit

    LCD_EN as sbit at RB5_bit

    LCD_D4 as sbit at RB0_bit

    LCD_D5 as sbit at RB1_bit

    LCD_D6 as sbit at RB2_bit

    LCD_D7 as sbit at RB3_bit

    LCD_RS_Direction as sbit at TRISB4_bit

    LCD_EN_Direction as sbit at TRISB5_bit

    LCD_D4_Direction as sbit at TRISB0_bit

    LCD_D5_Direction as sbit at TRISB1_bit

    LCD_D6_Direction as sbit at TRISB2_bit

    LCD_D7_Direction as sbit at TRISB3_bit

main:

     dim adc_value as word

     dim txt as string[16]

     dim k as longword

     dim somma,colpi,media as longword

     dim minimo,massimo as word

     dim tensione as float

     dim somma1 as longword

     dim rms as float

     rem --------------------------

     Lcd_Init()                     ' Initialize Lcd

     Lcd_Cmd(_LCD_CLEAR)            ' Clear display

     Lcd_Cmd(_LCD_CURSOR_OFF)

     rem --------------------------

     somma = 0

     somma1 = 0

     colpi = 0

     minimo = 65000

     massimo = 0

adc3:

     ADCON1 = %10000000

     ADC_Init()  ' Initialize ADC module

     Lcd_Out(1, 1, "Attendere....")

     Lcd_Out(2, 1, "Misura in corso.")

     for k=1 to 10000

         adc_value = ADC_Get_Sample(0)   'Legge valore analogico

         rem --- Somma e colpi per la MEDIA----

         somma = somma + adc_value

         colpi = colpi + 1

         rem --- Calcola minimo e massimo----

         if adc_value > massimo then

            massimo = adc_value

         end if

         if adc_value < minimo then

            minimo = adc_value

         end if

         rem --- Calcola RMS----

         somma1=somma1+(adc_value*5/1023)*(adc_value*5/1023)

     next k

     rem ----- Mostra risultati --------

     while true

           rem ------Mostra media-------

           Lcd_Cmd(_LCD_CLEAR)            ' Clear display

           media = somma / colpi

           tensione = media * 5 / 1023

           FloatToStr(tensione, txt)

           Lcd_Out(1, 1, "Media (Volts):")

           Lcd_Out(2, 1, txt)

           delay_ms(2000)

           rem ------Mostra minimo-------

           Lcd_Cmd(_LCD_CLEAR)            ' Clear display

           tensione = minimo * 5 / 1023

           FloatToStr(tensione, txt)

           Lcd_Out(1, 1, "Minimo (Volts):")

           Lcd_Out(2, 1, txt)

           delay_ms(2000)

           rem ------Mostra massimo-------

           Lcd_Cmd(_LCD_CLEAR)            ' Clear display

           tensione = massimo * 5 / 1023

           FloatToStr(tensione, txt)

           Lcd_Out(1, 1, "Massimo (Volts):")

           Lcd_Out(2, 1, txt)

           delay_ms(2000)

           rem ------Mostra RMS-------

           Lcd_Cmd(_LCD_CLEAR)            ' Clear display

           rms = sqrt(somma1 / colpi)

           FloatToStr(rms, txt)

           Lcd_Out(1, 1, "RMS:")

           Lcd_Out(2, 1, txt)

           delay_ms(2000)

           rem ------Mostra picco-picco-------

           Lcd_Cmd(_LCD_CLEAR)            ' Clear display

           tensione = (massimo-minimo) * 5 / 1023

           FloatToStr(tensione, txt)

           Lcd_Out(1, 1, "Picco-Picco:")

           Lcd_Out(2, 1, txt)

           delay_ms(2000)

     wend

end.
Il progettista può, ovviamente, programmare altre opzioni aggiuntive. Adesso si vedranno le modalità con cui vengono calcolate le grandezze sopra esposte.

L'esecuzione del programma dura circa 5.3 secondi, durante i quali il PIC effettua ben 10.000 digitalizzazioni del segnale sinusoidale. Nell'ambito di questo ciclo, il micro esegue tanti calcoli, come la somma dei campioni, la determinazione del minimo e del massimo del segnale, ecc. Sono tutte operazioni che, ovviamente, determinano un drastico rallentamento delle possibilità reali dell'ADC, rispetto ad un semplice ciclo di lettura pulito.

Al termine delle misure, il display visualizza, con sequenza paginata e ciclica, della durata di due secondi per videata, i risultati dei calcoli:

 

E adesso qualche calcolo matematico sulle acquisizioni analogiche da parte del PIC. Il microcontrollore esegue 10.000 misurazioni analogiche in 5,3 secondi. Esso non è un dato assoluto, ma varia in funzione del numero e della tipologia delle istruzioni Basic presenti all'interno del programma. Questo vuol dire che per ogni secondo sono effettuate 1887 acquisizioni. Ogni acquisizione ha la durata di 530 uS. Relativamente all'onda sinusoidale di 50 Hz, per ogni periodo sono elaborati 38 campioni. Riepilogando:

  • 10.000 acquisizioni in 5,3 secondi;
  • 1.887 acquisizioni in ogni secondo (10.000:5,3);
  • 530 uS la durata di ogni acquisizione (5,3:10.000);
  • 38 campioni per ogni periodo della sinusoide di 50 Hz (20.000:530).

Infatti 38 campioni x 50 Hz x 5.3 sec. = circa 10.00 campioni in totale.

Si ricorda che il valore efficace (RMS) è la radice quadrata della media dei quadrati.

Per quanto riguarda la media, essa è nulla per un'onda sinusoidale alternata, su quadrante positivo e negativo. Dal momento che nella presente sperimentazione il segnale è stato spostato sul quadrante positivo, essa è stata calcolata considerando i singoli valori istantanei di tensione.

Ricorrendo anche alla approssimazione delle aree dei rettangoli sotto la curva della sinusoide, è possibile ottenere un'idea approssimativa della superficie reale. Il valore medio può essere trovato sommando tutti questi settori. Se poi si utilizza un numero infinito di piccoli rettangoli più sottili, il risultato finale è molto più preciso. L'area sotto la curva può essere trovata anche mediante vari metodi di approssimazione. Pertanto l'area sottesa dalla curva positiva dell'onda periodica è calcolata mediante integrale.

Conclusioni

Il progettista, può sicuramente migliorare quanto fin'ora esposto, adottando accorgimenti e tecniche diversificate.

Alcuni possibili miglioramenti possono riassumersi come segue:

  • E' possibile acquisire un dato, prelevando solamente i suoi 8 bit meno significativi e trattando la tensione campionata con una variabile di tipo "byte". Il compilatore ed il micro processano questa tipologia di registro molto più velocemente del tipo "word";
  • Si può tentare ad aumentare la frequenza di clock del micro, adottando un quarzo più veloce. A volte la conversione può subire peggioramenti;
  • L'ideale sarebbe utilizzare il linguaggio Assembler, molto ostico e complicato. In questo modo si "parlerebbe" direttamente alla MCU, riducendo la dimensione finale dell'eseguibile in memoria ed aumentando le prestazioni dell'intero sistema.

L'articolo costituisce uno spunto iniziale, dal quale partire, per poter realizzare prototipi di acquisizione digitale. Esso chiarisce un po' le idee sulle conversioni analogico digitali e concorre a fornire un'idea di base per il proseguo delle proprie sperimentazioni. Buon lavoro.

 

GDM

 

Quello che hai appena letto è un Articolo Premium reso disponibile affinché potessi valutare la qualità dei nostri contenuti!

 

Gli Articoli Tecnici Premium sono infatti riservati agli abbonati e vengono raccolti mensilmente nella nostra rivista digitale EOS-Book in PDF, ePub e mobi.
volantino eos-book1
Vorresti accedere a tutti gli altri Articoli Premium e fare il download degli EOS-Book? Allora valuta la possibilità di sottoscrivere un abbonamento a partire da € 2,95!
Scopri di più

7 Comments

  1. Giorgio B. Giorgio B. 26 settembre 2013
  2. Piero Boccadoro Piero Boccadoro 27 settembre 2013
  3. delfino_curioso delfino_curioso 28 settembre 2013
  4. Piero Boccadoro Piero Boccadoro 30 settembre 2013
  5. Giovanni Di Maria gio22 30 settembre 2013
  6. Stuttgart Stuttgart 8 gennaio 2014
  7. Giovanni Di Maria gio22 9 gennaio 2014

Leave a Reply