Salta la barra di navigazione [1] - Vai alla barra di navigazione [3] - Scrivimi una mail [4]

Il convertitore Analogico Digitale

PIC18 in C - Sommario - Novità - Tutorial - Progetti - Risorse - Non solo elettronica

I LED sulla scheda demo

In questa pagina è descritto come usare il Convertitore Analogico Digitale (ADC) interno al PIC18 per misurare una tensione compresa tra massa e alimentazione. Se non sapete cosa è un ADC potete andare a questa pagina: il convertitore analogico digitale.

Il convertitore AD del PIC18 è caratterizzato da una risoluzione nominale di 10 bit, anche se le caratteristiche reali a volte garantiscono prestazioni paragonabili solo a quelle di un più modesto 8 bit.

L'unico ingresso dell'ADC può essere collegato ad uno dei tanti pin ANx con funzioni analogiche presenti nel PIC18. Questo permette di misurare, una alla volta, più tensioni diverse e viene spesso indicato come A-MUX (Analog Multiplexer).

Il circuito di riferimento

Il circuito utilizzato negli esempi può essere realizzato su breadboard oppure è possibile utilizzare uno starter-kit come quello mostrato nella fotografia di apertura. Lo schema mostrato è relativo ad un PIC18F14K50, ma è facilmente adattabile ad altri PIC18 cambiando la numerazione dei pin e/o alcune righe del codice e/o alcuni nomi di registri.

Il PIC18 come ADC

L'ingresso analogico utilizzato è AN11 / RB5 Ad esso è applicata una tensione variabile tra massa e tensione di alimentazione, ottenuta con il potenziometro RV1.

Il resto dell'hardware è costituito da otto LED collegati a RC0-RC7, usati per visualizzare il risultato della conversione (Osservazione 5).

Conversione a 10 bit

Tutte le varie fasi della conversione sono effettuate sequenzialmente dal software (polling). Qui il codice C completo, in varie versioni, specifiche per diversi PIC18: si consiglia di esaminarli insieme al datasheet del componente effettivamente utilizzato.

La prima operazione da effettuare è quella di indicare quale è il pin (quali sono i pin) che verrà utilizzato (verranno utilizzati) come ingresso analogico, disattivando i buffer digitali corrispondenti, sia in ingresso che in uscita; per esempio, in riferimento allo schema disegnato, è utilizzato solo RB5 e quindi il codice è il seguente (Osservazioni 1 e 2):

TRISBbits.RB5 = 1;    // Disable digital output buffer for AN11/RB5 pin
ANSELHbits.ANS11 = 1; // Disable digital input buffer for AN11 pin

Occorre quindi configurare l'ADC, attraverso tre registri: ADCON0, ADCON1, e ADCON2.

Il registro ADCON2 imposta i seguenti tre parametri:

Il codice conseguente potrebbe quindi essere:

ADCON2bits.ADFM = 1;     // Right justified
ADCON2bits.ADCS = 0b101; // A/D Conversion Clock
ADCON2bits.ACQT = 0b001; // A/D Acquisition time set to 2 TAD

Il secondo registro ADCON1 serve per impostare le tensioni di riferimento. Nell'esempio è utilizzata la tensione di alimentazione e la massa:

ADCON1=0b00000000; // VDD and VSS as voltage reference

Infine il registro ADCON0 permette di selezionare il canale dell'A-MUX da collegare all'ingresso dell'ADC.

ADCON0bits.CHS = 0b1011; // Analog Channel Select AN11

Infine occorre alimentare l'ADC che, dopo il reset, è spento:

ADCON0bits.ADON = 1;     // Power-up ADC

Tutte queste operazioni di configurazioni vengono spesso fatte una sola volta, all'inizio del programma. Ovviamente è possibile una loro modifica durante l'esecuzione del programma, per esempio per leggere la tensione da un altro canale, oppure per spegnere l'ADC quando non serve e risparmiare quindi energia.

La conversione vera e propria nell'esempio avviene dentro un ciclo infinito:

La prima operazione è avviare la conversione.

ADCON0bits.GO = 1; // Start conversion

Occorre quindi attendere il termine della conversione, operazione relativamente lunga, attraverso il test dell'apposito flag:

while (ADCON0bits.nDONE); // Wait end of conversion - Note: .nDone == .GO

A questo punto occorre leggere il risultato: vengono letti i due bit più significativi, spostati a sinistra di otto posizioni (<<) ed infine sommati agli otto bit meno significativi.

voltage10bits = ADRESH << 8;              // Read voltage - 2 MSB
voltage10bits = voltage10bits + ADRESL;   // Read voltage - 8 LSB (or: voltage10bits =  voltage10bits | ADRESL;)
                                                                  (or: voltage10bits |= ADRESL;)

La conversione è terminata!.

Potrebbe essere utile visualizzare il numero letto dall'ADC sugli 8 LED (vedere anche l'esercizio 4):

PORTC = voltage10bits >> 2;

Se siamo interessati a conoscere la tensione in volt, come numero "con virgola", è possibile realizzare la conversone a partire dal valore di fondo scala (VREF, pari nell'esempio a Vcc) e del numero di quanti (1024 = 210). Tale valore può essere letto tramite il debugger, fermando l'esecuzione del programma.

voltage = (double) VREF / 1024 * voltage10bits;

Conversione a 8 bit

Il secondo esempio è simile al precedente, ma si limita ad elaborare solo 8 bit, risparmiando un poco di tempo e soprattutto spazio in memoria. Osservazione 4

Il codice è simile al precedente. Le uniche differenze sono relative all'allineamento (più comodo se il risultato è già allineato a sinistra) e alla lettura di un solo byte nel risultato

ADCON2bits.ADFM = 0;   // Left justified

voltage8bits = ADRESH; // Read voltage (8 bit only!)

Da notare che la conversione viene effettuata dall'hardware comunque a 10 bit.

Nel codice sono presenti tre funzioni diverse e alternative per visualizzare il risultato. Sono oggetto del primo esercizio e quindi, intenzionalmente, sono prive di commento.

Le prestazioni

Il tempo di conversione teorico dell'ADC può essere calcolato come prodotto di due grandezze

A questo occorre aggiungere il tempo di acquisizione, programmabile, che deve essere di alcuni µs, in relazione all'impedenza di uscita della sorgente.

In teoria si potrebbe quindi arrivare a 100 kHz; si tratta però di un calcolo assolutamente teorico in quanto non è presente alcun meccanismo di buffering dei risultati; esistono inoltre numerosi vincoli sulla scelta dei parametri.

La velocità di conversione è stata misurata sfruttando RB6, settato subito dopo l'inizio della conversione e resettato appena terminata.

Il codice ed il diagramma temporale mostrato sono basati sull'esempio del PIC18F14K50 nella versione "a 10 bit" con CPU funzionante a 16 MHz e ADC funzionante a 1 MHz. Le modifiche rispetto al codice mostrato nell'esempio:

ADCON0bits.GO = 1;        // Start conversion
LATBbits.LB6 = 1          // Set RB6
while (ADCON0bits.nDONE); // Wait end of conversion
LATBbits.LB6 = 0;         // Reset RB6

I dati raccolti possono essere rappresentati dal seguente diagramma temporale dell'andamento di RB6:

Velocità dell'ADC del PIC18F14

Da esso emerge che il tempo di conversione, è di poco più di 14 µs. Ciò è conforme alle aspettative: 2 µs è il tempo di acquisizione hardware,13 µs il tempo di conversione vero e proprio. Ovviamente tali tempi dipendono dalla frequenza del clock usato per l'ADC (1 MHz in questo esempio), ma non da quella del processore.

Si noti che il tempo necessario per trasformare il numero intero letto dal convertitore in un numero con virgola è significativo: quasi 200 µs. Questo tempo dipende, a differenza del precedente, dalla frequenza del clock usato per il processore (16 MHz in questo esempio).

Non è stata fatta una misura del SNR, della linearità o altre misure sulle sue caratteristiche "analogiche" (approfondimento).

Il codice

Esercizi

  1. Esaminare le funzioni displayX presenti nell'esempio di conversione a 8 bit
  2. Modificare il codice per leggere in sequenza più di tensioni presenti su pin diversi e memorizzarle in una array
  3. Scrivere le due funzioni configureADC() e readADC(int pin). rispettivamente per configurare il convertitore e per leggere la tensione da un determinato pin
  4. Collegare 10 LED e visualizzare tutti i 10 bit forniti dall'ADC

Approfondimento

Osservazioni

  1. In realtà non è strettamente necessario disattivare il buffer di ingresso digitale per il corretto funzionamento del convertitore: lasciarlo attivo ha come effetto "solo" un aumento significativo della corrente di alimentazione, soprattutto se la tensione di ingresso è intermedia tra massa e alimentazione.
  2. Se si lascia attivo il buffer di uscita, la tensione passata al convertitore è il valore digitale generato dal PIC18 stesso. Potrebbe essere un modo per monitorare malfunzionamenti dovuti per esempio ad un eccessivo assorbimento di corrente da parte di circuiti esterni
  3. La tabella completa che lega la frequenza di clock del processore con quella dell'ADC è disponibile sui fogli tecnici. E' possibile anche l'uso di un clock  dedicato anche se questa opzione viene sconsigliata in casi di polling
  4. Questa scelta, apparentemente bizzarra, è motivata anche dal fatto che le non esaltanti caratteristiche analogiche di questo convertitore rendono ben poco rilevante il valore dei due bit meno significativi.
  5. Il modo migliore per conoscere il risultato della conversione durante il debug del codice è però l'inserimento di un opportuno breakpoint o l'esecuzione passo-passo.
  6. Sui fogli tecnici la discussione sulla scelta del tempo di acquisizione, anche in funzione dell'impedenza di uscita della sorgente utilizzata

PIC18 in C

Appendici:

Qualche utile informazione integrativa: C per sistemi embedded

Licenza Creative Commons Attribuzione 4.0 Internazionale


EN - Pagina principale - Sommario - Accessibilità - Note legali e privacy policy - Posta elettronica

XHTML 1.0 Strict - CSS 3