GUIDA ARDUINO 9: INGRESSI ANALOGICI

Nella Nona Parte della Guida verrà Illustrato come Configurare l’ADC interno al Microcontrollore e come Leggere gli Ingressi Analogici…

 

 

 

 



 

INTRO

Molti microcontrollori dispongono di un modulo interno chiamato ADC ovvero Analog Digital Converter in grado di convertire in un codice digitale un segnale analogico. Va quindi a discretizzare nel dominio del tempo e della tensione, ovvero avrò un codice digitale (a 8 o 12 bit) che rappresenta il valore in tensione quantizzato e questo codice si ripeterà ogni istante temporale. Il segnale analogico può assumere tutti i valori di tensione per ogni istante temporale, mentre il segnale convertito può assumere solo alcuni valori (ad esempio se il convertitore è a 8 bit ci sono al massimo 256 valori).

Arduino Uno e Arduino Mega dispongono di ADC e ogni ADC ha un certo numero di canali, quindi è in grado di convertire più di un segnale analogico. In particolare Arduino UNO dispone di 6 ingressi analogici mentre Arduino MEGA ne ha 16 di ingressi.

Lo schema interno tipico dell’ADC è il seguente:

In ingresso vi è un multiplexer che va a selezionare quale ingresso andare a leggere, l’uscita del multiplexer giunge all’ADC che converte l’uscita e la rende disponibile sul bus dati. Ovviamente ci sono una serie di registri che servono a selezionare le modalità di funzionamento dell’ADC.

L’ADC è molto importante nei microcontrollori perché permette, senza l’uso di chi o moduli esterni, di leggere segnali da sensori oppure di misurare tensioni per eseguire parti di codice o no. L’IDE di Arduino dispone della libraria per leggere gli ingressi analogici, però oltre a questa libreria si andranno ad analizzare i registri per configurare al meglio il modulo ADC interno ad Arduino Uno e Arduino Mega.

 



 

LA LIBRERIA

Si usa una semplice stringa per leggere il dato dall’ADC, in particolare si ha:

int Sensore = A0;                             //Inizializzo il sensore a A0
unsigned int ValoreADC1, ValoreADC2;

void setup() {
  pinMode (13, OUTPUT);                       //pin 13 uscita
}

void loop() {
  ValoreADC1 = analogRead(Sensore);           //Leggo ingresso 0
  ValoreADC2 = analogRead(A1);                //Leggo ingresso 1
  if(ValoreADC2 > 512) digitalWrite(13, HIGH);//Se il valore è maggiore di 512 accendi il LED
  else digitalWrite(13, LOW);                 //Altrimenti rimane spento
}

Bisogna inizializzare le variabili che contengono il valore o i valori convertiti, nel ciclo di setup non serve nulla mentre nel ciclo di loop si va a leggere prima A0 ovvero il pin 0 dell’ADC e poi A1 ovvero il pin 1 dell’ADC. Inoltre se il valore del secondo ingresso dell’ADC è maggiore di 512 si accende un LED.

Il tempo necessario alla conversione dipende dalla frequenza, considerando che Arduino Uno e Arduono Mega lavorano a 16MHz e che servono 25 cicli di clock per una conversione servono tra i 16uS e i 130uS per avere il valore convertito. L’ADC di entrambi i sistemi è a 10bit, quindi si ha un valore tra 0 e 1023. La frequenza massima che si può raggiungere è 15kSPS (kilo sample per second) quindi secondo il teorema del campionamento al massimo posso convertire un segnale con frequenza di 7kHz.

Essendo a 10bit ed essendoci 1024 diversi valori si hanno 1024 quanti di tensione. Se alimento Arduino a 5V ed uso un riferimento per l’ADC sempre a 5V si hanno quanti di tensione del valore di 4.88mV, quindi se la tensione in ingresso è minore di 4.88mV si avrà in uscita “00000000” mentre ad esempio con ingresso a 5mV si avrà il codice “00000001”e così via.

L’errore massimo dell’ADC è più o meno 2LSB ovvero due volte il quanto in più o in meno. Ad esempio se il convertitore ritorna “00000111” vuol dire che si ha in ingresso 4.88mV moltiplicato 7 perché il codice è 7, quindi 34.16mV ma per via dell’errore il valore reale è compreso tra 24.4mV e 44.22mV quindi per valori molto piccoli l’errore è molto grande.

 

ADC ARDUINO UNO

Per il controllo dell’ADC di Arduino Uno bisogna configurare i seguenti registri:

Analizziamo ora i registri. Il primo registro sotto analisi è ADMUX che serve a selezionare il riferimento di tensione e l’ingresso. I bit del registro ADMUX hanno le seguenti funzioni:

  • REFS1:REFS0 se 00 il riferimento è il pin AREF, 01 fa si che il riferimento sia il pin AVCC del microcontrollore mentre 10 serve per selezionare il riferimento interno a 1.1V.
  • ADLAR serve per segnalare se avere LEFT(1) o RIGHT(0) adjustament ovvero inserire il valore convertito con 8 MSB nel registro ADCH oppure rispettivamente gli 8 LSB nel registro ADCL.
  • MUX3:MUX0 servono a selezionare l’ingresso e il codice indica l’ingresso, ovvero 0000 indica l’ingresso 0, 0001 l’ingresso 1, 0010 l’ingresso 2 e così via fino a 0111 che indica l’ingresso 7.

Il registro ADCSRA ha le seguenti funzioni:

  • ADEN: Abilita l’ADC se settato a 1 logico.
  • ADSC: se settato a 1 avvia la conversione e rimane a 1 finché la conversione non finisce.
  • ADATE: Abilita l’auto trigger se settato a 1, altrimenti ad ogni conversione ne inizia un’altra.
  • ADIF: è il FLAG nel caso siano abilitati gli interrupt. È a livello alto quando finisce la conversione.
  • ADIE: Se settato a livello alto abilita l’interrupt dell’ADC.
  • ADPS2:ADSP0 selezionano il prescaler del clock dell’ADC, quindi selezionano la velocità di conversione. La frequenza deve essere tra 50kHz e 200kHz. Se 000 il prescaler è 1, quindi si ha lo stesso clock, 001 viene diviso il clock per 2, 010 per 4 e così via fino a 111 ovvero prescaler 128. Considerando che la frequenza di Arduino e 16MHz, per avere una frequenza tra 200kHz e 50Khz serve un prescaler di 128 ovvero 111.

I dati da ADTS2 a ADTS1 servono per selezionare il trigger. Il valore convertito è inserito nei registri ADCL e ADCH secondo l’indicazione di Left o Right adjustment. Un codice di esempio è il seguente:

unsigned int ValoreADC;                       //Inizializza la variabile che contiene la conversione
byte ADMUXset;                                //Valore che contiene l'inizializzazione del MUX

void setup() {    
            
  ADMUXset = B01000000;                       //riferimento Vcc, Right ADJ 
  //ADMUXset = B01010000;                     //riferimento Vcc, LEFT ADJ 
  ADCSRA = 0B10000111;                        //ADC enable, Prescaler 128, interrupt non abilitato
  pinMode (13, OUTPUT);                       //pin 13 uscita
}

unsigned int Conversione(byte pin){           //Funzione lettura ADC, riceve il numero del canale
  byte convL;                                 //Inizializza due variabile per contenere i valori
  byte convH;                                 //della conversione
  ADMUX = ADMUXset;                           //Setta ADMUX
  if(pin <= 7) ADMUX += pin;                   //Setta l'ingresso, al massimo ci sono 8 ingressi analogici
  bitSet(ADCSRA, ADSC);                       //Avvia la conversione
  while (bit_is_set(ADCSRA, ADSC));           //Aspetta che la conversione finisca
  convL = ADCL;                               //Leggi registro basso   
  convH = ADCH;                               //Leggi registro alto
  return (convH << 8) | convL;                //Concatena i due registri

  //return convH;                             //Se uso Left adj leggo solo il registro alto
}

void loop() {                          
  ValoreADC = Conversione(1);                 //Leggi il canale 1
  //Se LEFT ADJ ValoreADC è 8 bit ed equivale a conversione/4
  if(ValoreADC > 512) digitalWrite(13, HIGH); //Se il valore è maggiore di 512 accendi il LED
  else digitalWrite(13, LOW);                 //Altrimenti rimane spento
}

Iniziamo a inizializzare due variabili utili, la prima contiene il valore letto dell’ADC e il secondo contiene il settaggio dell’ADC. Nel setup settiamo quest’ultima variabile come se fosse il registro “ADMUX” lasciando il canale del mux a 0, poi settiamo il registro ADCSRA e il pin di uscita. Settiamo l’ADC con riferimento a VCC ovvero 5V, 8 LSB nel registro ADCL e 2 nel registro ADCH ovvero right ADJ, poi uso un prescaler di 128, non abilito gli interrupt e abilito l’ADC.

Nella funzione di conversione (che riceve il canale) si inizializzano le due variabili per  contenere i valori dei registri, poi setto il canale secondo la variabile pin, avvio la conversione e aspetto che finisca. Una volta conclusa si leggono i registri e si ricombinano per ritornare il valore a 10bit.

Nel loop vado a leggere il valore dell’ADC e anche in questo caso se maggiore di 512 accendo un LED, altrimenti rimane spento.

Alcune stringhe sono come commento, se si sostituiscono alle stringhe sovrastanti si va a realizzare una conversione che ritorna il valore di conversione diviso 4, ovvero esclude i 2 LSB della conversione e ritorna un valore a 8 bit. Questa soluzione è molto usata, considerando che l’ADC ha un errore di due LSB, quindi li elimino e poi mi semplifico la conversione da binario a decimale.

 

ADC ARDUINO MEGA

Per il controllo dell’ADC di Arduino MEGA 2560 bisogna configurare i seguenti registri:

Le funzioni dei bit nei registri sono uguali, cambiano solo i nomi di alcuni registri e vi è bit in più per il multiplexer. Il codice varia leggermente e in particolare la funzione semplificata di lettura dall’ADC è:

unsigned int Conversione(byte pin){           //Funzione lettura ADC, riceve il numero del canale
  byte convL;                                 //Inizializza due variabile per contenere i valori
  byte convH;                                 //della conversione
  ADMUX = ADMUXset;                           //Setta ADMUX
  if(pin <= 7){
    ADMUX += pin;                             //Setta l'ingresso, al massimo ci sono 8 ingressi analogici
    bitClear(ADCSRB, MUX5);}                  //metti a 0 MUX5
  if(pin >= 8){                               //Se l'ingresso è maggiore di 8
    pin -= 8;                                 //Sottrai 8  
    bitSet(ADCSRB, MUX5);                     //Setta MUX5
    ADMUX += pin;}                            //Aggiorna ADMUX
  bitSet(ADCSRA, ADSC);                       //Avvia la conversione
  while (bit_is_set(ADCSRA, ADSC));           //Aspetta che la conversione finisca
  convL = ADCL;                               //Leggi registro basso   
  convH = ADCH;                               //Leggi registro alto
  return (convH << 8) | convL;                //Concatena i due registri

  //return convH;                             //Se uso Left adj leggo solo il registro alto
}


Quindi si va a controllare se il pin è minore o uguale a 7 vado a scrivere direttamente il valore e metto a 0 “MUX5” mentre se maggiore modifico il valore di pin, setto “MUX5” e scrivo il valore del pin in ADMUX, come indicato nella tabella dei valori del multiplexer.

 

SCHEMA

Lo schema di esempio è il seguente:

Vado ad inserire un resistore variabile tra VCC a 5V e la massa e il pin centrale va all’ingresso 1 dell’ADC. Il LED è connesso con resistore di protezione al pin 13 con l’anodo verso la resistenza e il catodo a massa.
Se lo schema si realizza o si simula si nota che il LED si accende appena il resistore variabile supera il 50% questo perché al 50% ho 2.5V e se divido 2.5V per 0.0048829 (ovvero il passo di quantizzazione 4.8828=5/1024) ottengo 511, mentre al 51%, ovvero 2.55V ottengo 522 e il LED si accende.

 

DOWNLOAD

Potete scaricare la simulazione del circuito con PROTEUS e gli SKETCH di ARDUINO al seguente LINK!!!



 

[Voti Totali: 0 Media Voti: 0]
Segui la Nostra Pagina Facebook: Facebook

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *