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 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.
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).
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;
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.
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 quasi 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:
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).
Data di creazione di questa pagina: settembre 2014
Ultima modifica: 20 maggio 2016
PIC18 in C - Versione 0.991 - luglio 2019
Copyright 2014-2019, Vincenzo Villa (https://www.vincenzov.net)
PIC18 in C di Vincenzo Villa è distribuito con Licenza Creative Commons Attribuzione 4.0 Internazionale