Basi · 20 min di lettura

Cos'è un ADC? Convertitore analogico-digitale con Arduino

Cos'è un ADC e come funziona? Analogico e digitale, risoluzione a 10 e 12 bit, analogReference, formula in volt, ADC2 dell'ESP32 e riduzione del rumore.

Un ADC (convertitore analogico-digitale) è il circuito che trasforma la tensione presente su un pin, che può variare in modo continuo, in un numero che il microcontrollore può elaborare. Ogni volta che esegui analogRead() su Arduino, questo convertitore lavora dietro le quinte: l'ADC a 10 bit di Arduino Uno trasforma una tensione tra 0 e 5 V in un numero da 0 a 1023, mentre gli ADC a 12 bit di ESP32 e Raspberry Pi Pico trasformano circa 0–3,3 V in un numero da 0 a 4095. L'ADC è la chiave per leggere qualsiasi componente con uscita analogica: potenziometri, LDR, sensori di umidità del terreno, joystick e sensori di temperatura.

In questa guida imparerai la differenza tra segnali analogici e digitali, come un ADC converte una tensione in un numero passo dopo passo e come risoluzione e tensione di riferimento influenzano le tue misure. Con numeri reali vedremo la formula per convertire una lettura in volt, come cambiare l'intervallo di misura con analogReference, la frequenza di campionamento e i modi pratici per ridurre il rumore. Parleremo anche dell'ADC dell'ESP32, che non è lineare agli estremi, del conflitto tra ADC2 e Wi-Fi e dell'impostazione di attenuazione, dell'uso dell'ADC del Raspberry Pi Pico con MicroPython e, per finire, faremo una breve introduzione al DAC, che fa esattamente il contrario.

Lettura di un potenziometro con l'ADC
Il potenziometro è il modo più semplice per provare un ADC: la tensione sul pin centrale varia tra 0 V e la tensione di alimentazione mentre giri la manopola.

Qual è la differenza tra segnale analogico e digitale?

Un segnale analogico è una grandezza che può assumere infiniti valori all'interno di un intervallo e che varia in modo continuo nel tempo. Quando la tensione sul pin centrale di un potenziometro passa da 0 V a 5 V, attraversa tutti i valori intermedi, come 1,2 V, 1,21 V o 1,215 V; anche l'onda sonora di un microfono, la tensione di un termistore che cambia con la temperatura o la tensione di una batteria che cala lentamente sono analogiche. Un segnale digitale, invece, conosce solo certi livelli. Nel mondo della logica di Arduino sono due: LOW (0) e HIGH (1). Su Arduino Uno un ingresso digitale considera LOW tutto ciò che sta sotto circa 1,5 V e HIGH tutto ciò che supera 3,0 V; la zona intermedia non è definita. Per questo digitalRead() non distingue 3,7 V da 4,2 V su una batteria e in entrambi i casi restituisce HIGH. Il punto di forza del segnale digitale è l'immunità ai disturbi: 4,6 V e 5,0 V significano lo stesso HIGH. Per misurare, memorizzare o usare in un calcolo un valore analogico devi prima convertirlo in un numero, cioè in un valore digitale, ed è proprio questo il compito dell'ADC.

Cos'è un ADC e come funziona?

Il lavoro di un ADC si riassume in due passaggi. Il primo è il campionamento: nel momento della misura il convertitore copia la tensione d'ingresso su un piccolo condensatore (circuito di sample-and-hold) e la mantiene costante fino al termine della conversione; nell'ATmega328P questo condensatore è di circa 14 pF. Il secondo è la quantizzazione: la tensione mantenuta viene arrotondata al gradino più vicino di una scala che divide la tensione di riferimento in gradini uguali, e il numero di quel gradino è il risultato. Gli ADC di Arduino Uno, ESP32 e Pico sono tutti ad approssimazioni successive (SAR, Successive Approximation Register). Un ADC SAR funziona come un gioco per indovinare un numero: prima prova metà della tensione di riferimento, mette a 1 il bit più significativo se l'ingresso è maggiore e lo lascia a 0 se è minore; poi prova metà dell'intervallo rimasto e ripete per ogni bit. Così un ADC a 10 bit trova il gradino giusto tra 1024 possibilità con soli 10 confronti. Per semplicità, la tabella mostra come un ADC a 4 bit con riferimento a 5 V misura un ingresso di 3,2 V:

PassoBit provatoTensione di confrontoL'ingresso (3,2 V) è maggiore?Bit del risultato
1Bit 3 (vale 8)8 / 16 × 5 = 2,500 VSì → 11000
2Bit 2 (vale 4)12 / 16 × 5 = 3,750 VNo → 01000
3Bit 1 (vale 2)10 / 16 × 5 = 3,125 VSì → 11010
4Bit 0 (vale 1)11 / 16 × 5 = 3,4375 VNo → 01010
RisultatoTutti i bit provati1010 = 10 → 10 × 5 / 16 = 3,125 VErrore di 0,075 V (un gradino = 0,3125 V)1010

La differenza di 0,075 V nel risultato è l'errore di quantizzazione, e nessun ADC può evitarlo: se la tensione reale cade tra due gradini, l'ADC può indicarne solo uno. L'errore resta sotto un gradino (LSB), quindi più il gradino è piccolo, più la misura è fine. Poiché il condensatore di sample-and-hold deve caricarsi a ogni misura, conta anche la resistenza della sorgente che pilota l'ADC: il datasheet dell'ATmega328P raccomanda un'impedenza d'uscita di circa 10 kΩ o meno; un partitore di tensione con resistenze più alte non riesce a caricare il condensatore in tempo e le letture derivano. Oltre al tipo SAR dei microcontrollori esistono altri tipi di ADC. Gli ADC sigma-delta sono lenti ma molto precisi: l'HX711 usato nelle bilance offre 24 bit, il modulo ADS1115 collegato via I2C 16 bit. Gli ADC flash usano un comparatore separato per ogni gradino; sono velocissimi e si trovano negli oscilloscopi.

Risoluzione dell'ADC: cosa significano 10 bit e 12 bit?

La risoluzione indica in quanti gradini l'ADC divide il suo intervallo di misura. Un ADC a N bit produce 2^N gradini: 10 bit sono 1024 gradini (0–1023), 12 bit sono 4096 (0–4095) e 16 bit 65.536. La tensione di un gradino si chiama LSB (bit meno significativo) e si calcola con LSB = Vref / 2^N. Su Arduino Uno fa 5 V / 1024 = 4,88 mV, su ESP32 e Pico 3,3 V / 4096 = 0,81 mV. Quindi Uno non vede variazioni inferiori a 4,88 mV; con un LM35, che fornisce 10 mV/°C, significa un passo di circa 0,5 °C. Ma risoluzione e precisione non sono la stessa cosa. Rumore, non linearità ed errore di riferimento di un ADC rendono inutili una parte dei bit dichiarati sulla carta. La misura di questo è il numero effettivo di bit (ENOB): l'ADC dell'RP2040 nel Raspberry Pi Pico fornisce un risultato a 12 bit, ma secondo il datasheet il numero effettivo di bit è 8,7. Mettiamo a confronto gli ADC delle tre schede:

CaratteristicaArduino Uno R3ESP32 (WROOM-32)Raspberry Pi Pico
Risoluzione dell'ADC10 bit (0–1023)12 bit (0–4095)12 bit (scalati a 0–65535 in MicroPython)
Ingressi analogici6 (A0–A5; A0–A7 sul Nano)18 canali: 8 su ADC1, 10 su ADC23 pin (GP26–GP28) + VSYS/3 (GP29) + sensore di temperatura
Intervallo di misura predefinito0–5 VCirca 0–3,1 V (attenuazione 11 dB)0–3,3 V (ADC_VREF)
Un gradino (LSB)4,88 mVCirca 0,8 mV0,81 mV
Opzioni di riferimentoDEFAULT (5 V), INTERNAL (1,1 V), EXTERNAL (AREF)Attenuazione: 0; 2,5; 6 e 11 dBRiferimento esterno sul pin ADC_VREF
Frequenza di campionamentoCirca 10.000 letture/s20 kHz – 2 MHz in modalità continua (DMA)500.000 letture/s in hardware
Nota sulla precisioneErrore assoluto ±2 LSBNon lineare agli estremi, serve calibrazioneENOB 8,7 bit, offset di circa 30 mV
Uscita analogica (DAC)Nessuna (si usa il PWM)2 canali, 8 bit (GPIO 25 e 26)Nessuna (si usa il PWM)

Formula per convertire il valore dell'ADC in volt

Il numero restituito dall'ADC da solo non significa nulla; per convertirlo in tensione usi la tensione di riferimento e il numero di gradini: Tensione = valore ADC × Vref / 2^N. Su Arduino Uno diventa V = valore × 5,0 / 1024; se analogRead() ha restituito 512, la tensione sul pin è 512 × 5 / 1024 = 2,50 V. Al contrario, trovi a quale numero corrisponde una certa tensione con valore = V × 1024 / Vref: 3,3 V vengono letti come circa 675 su Uno. In molti esempi vedrai dividere per 1023 invece che per 1024. Il datasheet dell'ATmega328P riporta la formula ADC = Vin × 1024 / Vref; il valore massimo, 1023, rappresenta un gradino sotto Vref. Dividere per 1023 fa mostrare al valore massimo esattamente 5,00 V, ma sposta tutti i valori di circa lo 0,1 %. Questa differenza è più piccola dell'errore proprio dell'ADC (±2 LSB), quindi vanno bene entrambe; l'importante è essere coerenti nel progetto. La vera grande fonte di errore è Vref stessa: il pin 5V di un Uno alimentato via USB sta spesso tra 4,7 e 5,1 V, e scrivere 5,0 nella formula causa errori fino al 5 %. Per misure precise, quindi, misura il pin 5V con un multimetro e inserisci il valore reale nel codice. Anche l'unico ingresso analogico dell'ESP8266 (A0) è a 10 bit; il chip misura 0–1 V, e il partitore integrato su schede come NodeMCU e Wemos D1 Mini estende l'intervallo a circa 0–3,2 V.

Scheda e impostazioneIntervallo lettoConversione in voltEsempio
Arduino Uno / Nano (DEFAULT, 5 V)0–1023V = valore × 5,0 / 1024768 → 3,75 V
Uno, analogReference(INTERNAL)0–1023V = valore × 1,1 / 1024512 → 0,55 V
ESP32 (11 dB)0–4095Calibrato: analogReadMilliVolts() / 1000; approssimato: valore × 3,3 / 40951650 mV → 1,65 V
Raspberry Pi Pico (read_u16)0–65535V = valore × 3,3 / 6553532768 → 1,65 V
ESP8266 NodeMCU / Wemos D1 Mini (A0)0–1023V = valore × 3,2 / 1024512 → 1,60 V

Collegare un potenziometro: Arduino, ESP32 e Pico

Pin del potenziometroArduino UnoESP32Raspberry Pi Pico
Pin esterno 15V3V33V3(OUT) (pin 36)
Pin centrale (segnale)A0GPIO 34 (ADC1_CH6)GP26 / ADC0 (pin 31)
Pin esterno 2GNDGNDAGND (pin 33) o GND
Condensatore antidisturbo (facoltativo)100 nF tra A0 e GND100 nF tra GPIO 34 e GND100 nF tra GP26 e AGND
Gli ingressi analogici di ESP32 e Raspberry Pi Pico accettano al massimo 3,3 V; collegare il pin esterno del potenziometro a 5 V o collegare direttamente un sensore con uscita a 5 V può danneggiare il pin in modo permanente. Anche su Uno non applicare mai a un ingresso più della tensione di alimentazione (5 V) né meno di 0 V; abbassa le tensioni più alte con un partitore di tensione. Un pin analogico lasciato scollegato legge valori casuali; non è un guasto, il pin è semplicemente "flottante".

Passo dopo passo: la tua prima lettura ADC con Arduino

  1. Monta il circuito: collega i pin esterni di un potenziometro da 10 kΩ a 5V e GND, e il pin centrale ad A0.
  2. Misura il pin 5V: misura con il multimetro la tensione tra 5V e GND e scrivila nella costante VREF del codice (ad es. 4.93).
  3. Carica il codice: apri lo sketch qui sotto nell'IDE Arduino, scegli scheda e porta e caricalo.
  4. Apri il Monitor seriale: impostalo a 9600 baud e osserva il valore dell'ADC che cambia tra 0 e 1023 mentre giri la manopola (dettagli nella guida al Monitor seriale).
  5. Verifica: misura con il multimetro tra A0 e GND e confronta con la tensione sullo schermo; la differenza dovrebbe restare intorno a 10–20 mV.
C++ (Arduino IDE)
1// Potansiyometreyi ADC ile okuma / Reading a potentiometer with the ADC
2// Kenar bacaklar 5V ve GND'ye, orta bacak A0'a / Outer legs to 5V and GND, wiper to A0
3const int POT_PIN = A0;
4const float VREF = 5.0;      // 5V pininde ölçtüğün gerilim / voltage you measured on the 5V pin
5
6void setup() {
7  Serial.begin(9600);
8}
9
10void loop() {
11  int raw = analogRead(POT_PIN);             // 0-1023 arası sayı / number between 0-1023
12  float volts = raw * VREF / 1024.0;         // sayıyı volta çevir / convert the number to volts
13  int percent = map(raw, 0, 1023, 0, 100);   // yüzdeye ölçekle / scale to percent
14
15  Serial.print(F("ADC: "));
16  Serial.print(raw);
17  Serial.print(F("  Gerilim / Voltage: "));
18  Serial.print(volts, 3);
19  Serial.print(F(" V  Yuzde / Percent: "));
20  Serial.println(percent);
21  delay(200);
22}

analogRead() completa una misura in circa 100 µs e restituisce un intero tra 0 e 1023. Il codice converte prima questo numero in volt con la formula e poi in percentuale con map(). Poiché map() usa l'aritmetica intera, scarta la parte decimale; se ti serve un risultato con i decimali, fai tu il calcolo con un float. Questa è una misura raziometrica: dato che potenziometro e ADC sono alimentati dagli stessi 5 V, la posizione della manopola, cioè il valore dell'ADC, non cambia anche se l'alimentazione scende a 4,9 V; cambia solo la tensione calcolata. Lo stesso vale per i sensori letti tramite un partitore, come LDR e NTC. Quando invece misuri la tensione di una batteria o un sensore con uscita fissa in mV come l'LM35, il valore reale di Vref si riflette direttamente sul risultato.

Tensione di riferimento e analogReference

La tensione di riferimento (Vref) è il punto più alto della scala dell'ADC: se la tensione d'ingresso raggiunge o supera Vref, il risultato resta bloccato sul valore massimo (1023). Ridurre Vref restringe l'intervallo di misura ma rende ogni gradino più piccolo, quindi misuri i segnali piccoli con più finezza. Su Arduino Uno scegli il riferimento con la funzione analogReference(). Con l'opzione INTERNAL, Vref diventa 1,1 V e un gradino scende a 1,07 mV; puoi leggere un LM35 (10 mV/°C) a passi di circa 0,1 °C, ma in cambio non puoi misurare nulla oltre 1,1 V, cioè oltre 110 °C. Lo stesso riferimento interno da 1,1 V può variare tra 1,0 e 1,2 V da un chip all'altro, quindi nei progetti precisi conviene calibrarlo una volta con un multimetro. Le prime letture dopo il cambio di riferimento possono essere imprecise, come segnala anche la documentazione di Arduino, quindi è buona abitudine scartarne alcune dopo il cambio. Inoltre l'impostazione vale contemporaneamente per tutti i pin analogici della scheda. Le opzioni cambiano da scheda a scheda:

OpzioneRiferimentoSchedeQuando usarla?
DEFAULT5 V (3,3 V sulle schede a 3,3 V)Uno, Nano, Mega, LeonardoSensori raziometrici rispetto all'alimentazione, come potenziometri, LDR e joystick
INTERNAL1,1 V (ATmega328P); 2,56 V sul LeonardoUno, Nano, Leonardo (non sul Mega)Sensori a bassa tensione come l'LM35
INTERNAL1V1 / INTERNAL2V561,1 V / 2,56 VSolo MegaSegnali piccoli sul Mega
EXTERNALLa tensione sul pin AREF (0–5 V)Uno, Nano, MegaUsare un tuo riferimento di precisione (ad es. 2,5 V o 4,096 V)
AR_INTERNALCirca 1,43 V (riferimento interno del chip)Uno R4 Minima e WiFiSegnali piccoli su Uno R4
Se hai collegato un riferimento esterno al pin AREF, devi chiamare analogReference(EXTERNAL) prima di usare analogRead(). Altrimenti il riferimento generato all'interno del chip va in cortocircuito con la tensione sul pin AREF e il microcontrollore può danneggiarsi. Se vuoi passare da un riferimento all'altro, collega la tensione esterna ad AREF tramite una resistenza da 5 kΩ; poiché forma un partitore con la resistenza interna del pin, di circa 32 kΩ, tieni conto che la tensione su AREF scenderà un po' (circa 2,2 V per 2,5 V). Non applicare mai ad AREF meno di 0 V o più di 5 V.

Frequenza di campionamento: quante letture al secondo fa un ADC?

La frequenza di campionamento è il numero di misure che l'ADC può fare in un secondo. Su Arduino Uno il clock dell'ADC viene impostato a 125 kHz dividendo i 16 MHz per 128, e una conversione dura 13 cicli di clock; per questo analogRead() impiega circa 100 µs e si possono fare al massimo circa 10.000 letture al secondo. Per grandezze che cambiano lentamente, come temperatura, luce o tensione di una batteria, è più che sufficiente; spesso bastano anche 10 letture al secondo. Con i segnali veloci entra in gioco il teorema di Nyquist: per catturare un segnale senza perderlo devi campionare almeno al doppio della sua frequenza più alta, e in pratica si preferisce da 5 a 10 volte. Per vedere il ronzio di rete a 50 Hz servono almeno 100 letture/s, per registrare la voce fino a 4 kHz almeno 8.000 letture/s. Se su Uno ti servono misure più rapide, puoi ridurre il prescaler dell'ADC: con ADCSRA = (ADCSRA & 0xF8) | 0x04; in setup() il prescaler diventa 16, il clock dell'ADC sale a 1 MHz e analogRead() scende a circa 16 µs, al prezzo di un po' di precisione. Sull'ESP32 le singole chiamate ad analogRead() consentono decine di migliaia di letture al secondo, e nella modalità continua con DMA (analogContinuous() in Arduino) si può scegliere una frequenza tra 20 kHz e 2 MHz; questa modalità funziona solo con i pin dell'ADC1. L'ADC del Raspberry Pi Pico usa un proprio clock a 48 MHz, completa ogni conversione in 96 cicli, cioè 2 µs, e arriva a 500.000 letture al secondo; un ciclo in MicroPython non riesce a stare a questo ritmo, e per la piena velocità servono l'SDK in C e il DMA.

ADC dell'ESP32: non linearità, conflitto ADC2/Wi-Fi e attenuazione

Sulla carta l'ADC a 12 bit dell'ESP32 misura con una finezza quattro volte maggiore rispetto a Uno, ma in pratica devi fare attenzione a tre cose. La prima è la non linearità: vicino a 0 V l'ADC dell'ESP32 legge 0 fino a circa 100–150 mV, e all'estremo superiore va in saturazione a 4095 prima che l'ingresso arrivi a 3,3 V; anche la curva intermedia non è una retta perfetta. La seconda è la variabilità del riferimento: per progetto il riferimento interno dell'ESP32 è di 1100 mV, ma secondo Espressif varia tra 1000 e 1200 mV da un chip all'altro, il che significa un errore di circa ±9 % sulle letture non calibrate. Per questo Espressif scrive dati di calibrazione negli eFuse dei chip in fase di produzione. La funzione analogReadMilliVolts() di Arduino usa questi dati per convertire direttamente il valore grezzo in millivolt calibrati e, nella maggior parte dei casi, è molto più precisa del calcolo analogRead() × 3,3 / 4095. La terza è l'attenuazione: il nucleo dell'ADC misura solo fino a circa 1 V, quindi le tensioni più alte vengono ridotte da un attenuatore regolabile all'ingresso. In Arduino il valore predefinito è 11 dB, che permette letture fino a circa 3,1 V, ma l'intervallo preciso che Espressif raccomanda per questa impostazione è 150–2450 mV. Con segnali piccoli, ridurre l'attenuazione migliora sia la precisione sia la risoluzione:

AttenuazioneCostante ArduinoIntervallo precisoUso
0 dBADC_0db100–950 mVSegnali piccoli, misura più fine
2,5 dBADC_2_5db100–1250 mVUscite di sensori intorno a 1 V
6 dBADC_6db150–1750 mVSegnali di livello medio
11 dB (predefinito)ADC_11db150–2450 mV (la lettura arriva a circa 3,1 V)Potenziometri e sensori 0–3,3 V
Sull'ESP32 anche il driver Wi-Fi usa l'ADC2. Una volta avviato il Wi-Fi non è possibile leggere dai pin dell'ADC2 (GPIO 0, 2, 4, 12, 13, 14, 15, 25, 26, 27); in questo caso ESP-IDF restituisce un errore di timeout (ESP_ERR_TIMEOUT), e nelle versioni attuali del core Arduino analogRead() restituisce 0. Nei progetti con Wi-Fi collega sempre i sensori analogici ai pin dell'ADC1: GPIO 32, 33, 34, 35, 36 (VP) e 39 (VN). Di questi, i pin da 34 a 39 sono solo ingressi e non hanno resistenze di pull-up interne, il che li rende ideali per le letture analogiche. I GPIO 0, 2, 12 e 15 sono inoltre pin di avvio (strapping) e non sono affatto adatti ai sensori analogici.
C++ (Arduino IDE)
1// ESP32 ADC: ham değer ve kalibre milivolt / ESP32 ADC: raw value and calibrated millivolts
2// Potansiyometre: 3V3, GND ve orta bacak GPIO 34 / Pot: 3V3, GND and wiper to GPIO 34
3const int POT_PIN = 34;      // ADC1_CH6, Wi-Fi açıkken de çalışır / ADC1_CH6, works with Wi-Fi on
4
5void setup() {
6  Serial.begin(115200);
7  analogReadResolution(12);                     // 0-4095 (varsayılan / default)
8  analogSetPinAttenuation(POT_PIN, ADC_11db);   // yaklaşık 3,1 V'a kadar / up to about 3.1 V
9}
10
11void loop() {
12  uint32_t rawSum = 0;
13  uint32_t mvSum = 0;
14  for (int i = 0; i < 16; i++) {                // 16 ölçümün ortalaması / average of 16 readings
15    rawSum += analogRead(POT_PIN);              // ham ADC değeri / raw ADC value
16    mvSum += analogReadMilliVolts(POT_PIN);     // eFuse kalibrasyonlu mV / eFuse-calibrated mV
17  }
18  float raw = rawSum / 16.0;
19  float naiveV = raw * 3.3 / 4095.0;            // basit formül / naive formula
20  float calibratedV = mvSum / 16.0 / 1000.0;    // kalibre edilmiş / calibrated
21
22  Serial.printf("Ham / Raw: %4.0f  Formul / Formula: %.3f V  Kalibre / Calibrated: %.3f V\n",
23                raw, naiveV, calibratedV);
24  delay(500);
25}

Il codice mostra due risultati affiancati: il valore grezzo convertito con la formula semplice e il valore calibrato da analogReadMilliVolts(). Girando il potenziometro verso gli estremi noterai che la differenza cresce, e in fondo, dove la formula indica 0,000 V, il multimetro misura ancora circa 0,1 V. analogSetPinAttenuation() cambia l'attenuazione di un solo pin, mentre analogSetAttenuation() la cambia per tutti. L'ESP32 è decisamente più rumoroso di Uno, soprattutto mentre il Wi-Fi trasmette; la stessa Espressif consiglia di aggiungere un condensatore da 100 nF sull'ingresso e di fare la media di più campioni, ed è proprio ciò che fa la media di 16 letture nel codice. I chip più recenti come ESP32-S3 ed ESP32-C3 usano una calibrazione più evoluta ad adattamento di curva, ma numeri dei pin e regole dell'ADC2 cambiano da modello a modello, quindi controlla sempre la piedinatura della tua scheda (strumento Pinout). Spieghiamo come costruire un partitore per misurare tensioni sopra i 3,3 V nella guida al partitore di tensione.

ADC sul Raspberry Pi Pico (MicroPython)

Il Raspberry Pi Pico ha tre ingressi analogici utilizzabili dall'esterno: GP26 (ADC0), GP27 (ADC1) e GP28 (ADC2). GP29 (ADC3) misura sulla scheda un terzo della tensione di alimentazione VSYS, e l'ADC4 è collegato al sensore di temperatura interno del chip. La funzione read_u16() di MicroPython scala il risultato a 12 bit sull'intervallo 0–65535, così lo stesso codice funziona su schede diverse, ma la risoluzione reale resta di 4096 gradini. L'RP2040 non ha una tensione di riferimento separata: usa come riferimento la propria alimentazione dell'ADC. Sul Pico questa alimentazione (ADC_VREF, pin 35) è ricavata dall'uscita del regolatore switching a 3,3 V (SMPS), filtrata con una resistenza di circa 200 Ω. Il datasheet della scheda di Raspberry Pi ne elenca chiaramente tre conseguenze: la precisione dei 3,3 V è limitata, l'alimentazione è un po' rumorosa e la corrente assorbita dall'ADC crea un offset di circa 30 mV. Le soluzioni sono nello stesso documento: portare GP23 a HIGH per mettere il regolatore in modalità PWM, con meno ripple; collegare ad AGND un canale ADC inutilizzato e sottrarne la lettura come offset; e, per una precisione molto migliore, collegare un riferimento da 3,0 V come l'LM4040 tra ADC_VREF e AGND (l'intervallo di misura diventa allora 0–3,0 V). È noto inoltre che l'ADC dell'RP2040 presenta piccoli salti (errori DNL) ai valori 512, 1536, 2560 e 3584; fare la media di più letture ne riduce l'effetto. Sul Pico W, GP23 è riservato al chip wireless e la modalità del regolatore si controlla tramite WL_GPIO1, quindi su un Pico W elimina le righe di GP23 dal codice seguente.

MicroPython
1from machine import ADC, Pin
2import time
3
4pot = ADC(26)                 # GP26 = ADC0 (pin 31)
5chip_temp = ADC(4)            # dahili sıcaklık sensörü / internal temperature sensor
6VREF = 3.3                    # ADC_VREF; multimetreyle ölçüp düzelt / measure and correct with a multimeter
7
8# Yalnız Pico ve Pico 2 (W değil): SMPS PWM moduna, daha az gürültü / Plain Pico and Pico 2 only (not W): SMPS to PWM mode, less noise
9smps_mode = Pin(23, Pin.OUT)
10smps_mode.value(1)
11
12
13def read_average(adc, samples=16):
14    total = 0
15    for _ in range(samples):
16        total += adc.read_u16()           # 0-65535 (12 bit ölçeklenmiş / 12-bit scaled)
17    return total / samples
18
19
20while True:
21    raw = read_average(pot)
22    volts = raw * VREF / 65535            # sayıyı volta çevir / convert the number to volts
23    t_volts = read_average(chip_temp) * VREF / 65535
24    temp_c = 27 - (t_volts - 0.706) / 0.001721    # RP2040 veri sayfası formülü / RP2040 datasheet formula
25    print("ADC: {:5d}  {:.3f} V  Yonga / Chip: {:.1f} C".format(int(raw), volts, temp_c))
26    time.sleep(0.5)

Come ridurre il rumore dell'ADC

Anche misurando una tensione stabile è normale che il valore dell'ADC oscilli di uno o due gradini; contribuiscono la quantizzazione, il ripple dell'alimentazione, i disturbi captati dai cavi lunghi e i motori o le trasmissioni Wi-Fi vicine. Su Uno un'oscillazione di ±1–2 gradini è considerata normale; molto di più di solito indica un problema nel circuito. Puoi combattere il rumore sia nell'hardware sia nel software:

  • Aggiungi un condensatore: un condensatore ceramico da 100 nF tra il pin analogico e GND filtra i disturbi veloci e fornisce carica pronta al condensatore di campionamento con sorgenti ad alta resistenza.
  • Usa cavi corti: se il cavo del sensore è lungo, attorciglia insieme il filo del segnale e quello di GND; fai passare i cavi di motori e relè lontano dalle linee analogiche.
  • Cura la massa: la corrente di ritorno di carichi ad alto assorbimento come motori, relè e strisce LED non deve passare dal filo GND del sensore; unisci le masse in un unico punto (massa a stella).
  • Usa un riferimento pulito: l'alimentazione USB è rumorosa; nelle misure di tensione assoluta il riferimento interno o un integrato di riferimento esterno danno risultati più stabili.
  • Scarta la prima lettura dopo un cambio di canale: il multiplexer all'ingresso dell'ADC può portarsi dietro la carica del canale precedente; quando leggi più pin di seguito, sacrifica una lettura su ogni pin.
  • Fai la media: la media di N letture riduce il rumore casuale di circa √N volte; 16 letture lo portano a un quarto.
  • Sovracampionamento (oversampling): se sommi 4^n letture e sposti il risultato di n bit a destra, guadagni n bit di risoluzione in più; spostare di 2 bit la somma di 16 letture porta i 10 bit di Uno a 12. Perché funzioni, il segnale deve avere almeno un gradino di rumore naturale.
  • Filtri mediano ed esponenziale: il filtro mediano elimina i picchi improvvisi causati dai motori, mentre la media mobile esponenziale (EMA) dà una curva morbida usando pochissima memoria.
C++ (Arduino IDE)
1// Ham, ortalama ve EMA filtreli ADC değerleri / Raw, averaged and EMA-filtered ADC values
2// Serial Plotter ile izle / Watch with the Serial Plotter
3const int SENSOR_PIN = A0;
4const float ALPHA = 0.1;     // EMA katsayısı (0-1) / EMA coefficient (0-1)
5float ema = 0;
6
7void setup() {
8  Serial.begin(115200);
9  analogRead(SENSOR_PIN);              // ilk okumayı at / discard the first reading
10  ema = analogRead(SENSOR_PIN);        // filtreyi başlat / seed the filter
11}
12
13void loop() {
14  int raw = analogRead(SENSOR_PIN);    // ham değer / raw value
15
16  long sum = 0;
17  for (int i = 0; i < 16; i++) {       // 16 ölçüm topla / sum 16 readings
18    sum += analogRead(SENSOR_PIN);
19  }
20  float average = sum / 16.0;          // ortalama (0-1023) / average (0-1023)
21
22  ema += ALPHA * (raw - ema);          // üstel hareketli ortalama / exponential moving average
23
24  Serial.print(F("ham:"));
25  Serial.print(raw);
26  Serial.print(F(" ortalama:"));
27  Serial.print(average);
28  Serial.print(F(" ema:"));
29  Serial.println(ema);
30  delay(20);
31}

Caricando il codice e aprendo il Plotter seriale dell'IDE Arduino vedrai tre linee: il valore grezzo trema, la media di 16 letture è nettamente più calma e l'EMA è la curva più morbida, ma reagisce ai cambiamenti con un po' di ritardo. Il coefficiente ALPHA si sceglie tra 0 e 1: 0,1 significa che ogni nuova lettura pesa per il 10 % sul risultato, quindi contano all'incirca le ultime 10 letture; aumentandolo la risposta diventa più rapida ma il rumore cresce. Il filtro giusto dipende dal segnale: per una temperatura che varia lentamente va bene un'EMA forte, per un joystick che deve rispondere in fretta una media breve. Confrontiamo media mobile, filtro mediano e isteresi con esempi reali di sensori nella guida al filtraggio dei dati dei sensori.

Breve introduzione al DAC: dal digitale all'analogico

Un DAC (convertitore digitale-analogico) fa il contrario di un ADC: riceve un numero e produce una tensione proporzionale. Se scrivi 0 in un DAC a 8 bit ottieni 0 V, con 255 un valore vicino alla tensione di alimentazione e con 128 circa la metà. I DAC più semplici sono realizzati con una scala di resistenze chiamata R-2R. Chiariamo qui un equivoco frequente: analogWrite() su Arduino Uno non è un DAC, ma genera un segnale PWM; il pin in realtà commuta molto rapidamente tra 0 V e 5 V. Per la luminosità di un LED o la velocità di un motore basta, ma se ti serve davvero una tensione stabile devi aggiungere un filtro passa-basso RC all'uscita PWM; puoi calcolare la costante di tempo del filtro con lo strumento per la costante di tempo RC. Quanto alle schede con un vero DAC: l'ESP32 ha due DAC a 8 bit su GPIO 25 e GPIO 26, che si usano con dacWrite(pin, valore), mentre ESP32-S3 ed ESP32-C3 non hanno DAC. Arduino Uno R4 ha un DAC a 12 bit sul pin A0. Per Uno R3, Nano e Pico la via più semplice è il modulo MCP4725 a 12 bit collegato via I2C. Con un DAC puoi generare suoni, produrre onde sinusoidali o triangolari e persino collegare l'uscita del DAC a un ingresso dell'ADC per testare l'ADC con tensioni note:

C++ (Arduino IDE)
1// ESP32 DAC ile sinüs dalgası / Sine wave with the ESP32 DAC
2// Çıkış GPIO 25'te, osiloskopla izle / Output on GPIO 25, watch it with an oscilloscope
3const int DAC_PIN = 25;      // DAC1 = GPIO 25 (DAC2 = GPIO 26)
4const int STEPS = 64;        // bir periyottaki nokta sayısı / points per period
5uint8_t sineTable[STEPS];
6
7void setup() {
8  for (int i = 0; i < STEPS; i++) {
9    sineTable[i] = 128 + 127 * sin(2 * PI * i / STEPS);  // 1-255 arası tablo / table between 1-255
10  }
11}
12
13void loop() {
14  for (int i = 0; i < STEPS; i++) {
15    dacWrite(DAC_PIN, sineTable[i]);   // 0 → 0 V, 255 → yaklaşık 3,3 V / 0 → 0 V, 255 → about 3.3 V
16    delayMicroseconds(150);            // yaklaşık 100 Hz / roughly 100 Hz
17  }
18}

Idee di progetto con l'ADC

  • Voltmetro digitale: misura da 0 a 25 V con un partitore di tensione e mostra il risultato su un LCD; i passaggi sono nella guida costruire un voltmetro con Arduino.
  • Luce notturna automatica: leggi la tensione di una LDR e accendi i LED quando fa buio.
  • Vaso intelligente: usa l'uscita analogica di un sensore di umidità del terreno per far partire una pompa quando la terra si asciuga.
  • Robot comandato con joystick: i due assi di un joystick sono due ingressi analogici separati; trasforma i loro valori in velocità e direzione dei motori.
  • Misuratore di corrente: leggi la tensione proporzionale alla corrente di un ACS712 per sapere quanta corrente assorbe un apparecchio.
  • Indicatore di livello sonoro: campiona velocemente l'uscita analogica di un sensore sonoro KY-038 e accendi una barra di LED in base al valore di picco.
  • Indicatore della batteria: misura la tensione in un progetto a batteria e mostra la carica residua in percentuale su un display.

Errori comuni e soluzioni

SintomoCausa probabileSoluzione
Il valore cambia a caso senza niente collegatoIl pin è flottanteCollega il sensore; non leggere i pin inutilizzati o portali a GND
Il valore resta fermo a 1023 (4095 sull'ESP32)L'ingresso supera il riferimento oppure è selezionato INTERNALRegola riferimento o attenuazione, abbassa le tensioni alte con un partitore
Sull'ESP32 analogRead() restituisce 0 quando il Wi-Fi è attivoIl sensore è su un pin dell'ADC2Usa i pin dell'ADC1, GPIO 32–39
L'ESP32 mostra 0 con tensioni basse e 4095 con quelle alteL'ADC non è lineare agli estremiUsa analogReadMilliVolts() e tieni il segnale tra 150 e 2450 mV
La tensione calcolata differisce del 3–5 % dal multimetroVref non è 5,00 V (alimentazione USB)Misura il pin 5V e scrivilo in VREF, oppure aggiungi un fattore di calibrazione
Leggendo due pin di seguito, i valori si influenzanoMultiplexer e resistenza della sorgente altaScarta la prima lettura dopo il cambio, tieni la sorgente sotto 10 kΩ, aggiungi 100 nF
I valori saltano quando gira un motore o scatta un relèDisturbi sulla linea GND comune, l'alimentazione calaDai ai carichi un ritorno GND separato, usa condensatori e un filtro mediano
Le letture sono sbagliate dopo aver applicato tensione ad AREFAREF usato senza chiamare analogReference(EXTERNAL)Seleziona prima EXTERNAL o collega AREF tramite una resistenza da 5 kΩ
Dopo aver scelto INTERNAL il potenziometro arriva a 1023 a metà corsaIl riferimento da 1,1 V vale per tutti i pin analogiciTorna a DEFAULT prima di leggere sensori raziometrici e scarta alcune letture
Sul Pico la lettura è sopra 0 anche con l'ingresso a GNDOffset dell'ADC e rumore su ADC_VREFCollega un canale ad AGND, misura l'offset e sottrailo; porta GP23 a HIGH
Il modo più rapido per verificare un circuito con ADC è misurare una tensione nota. Prima collega l'ingresso analogico a GND e controlla di vedere un valore vicino a 0. Poi collega una pila stilo AA di cui hai misurato la tensione con il multimetro, con il negativo a GND e il positivo al pin analogico: per circa 1,5 V dovresti vedere circa 307 su Uno, circa 1500 mV con analogReadMilliVolts() sull'ESP32 e circa 29.800 con read_u16() sul Pico. Se il risultato torna, codice e collegamenti sono corretti; altrimenti il problema è molto probabilmente nella tensione di riferimento o nella massa comune.

L'ADC è il ponte tra il mondo fisico e il tuo codice: quando conosci la sua risoluzione, la tensione di riferimento e la frequenza di campionamento, sai anche cosa rappresenta ogni numero sullo schermo e quanto puoi fidarti. Su Uno misura bene il riferimento, sull'ESP32 usa i pin dell'ADC1 e analogReadMilliVolts(), sul Pico tieni conto di offset e rumore, poi rendi stabile il risultato con medie e filtri. Nell'app BlueGrays trovi progetti basati sulle letture analogiche, come la misura dell'intensità luminosa con una LDR, un voltmetro digitale con LCD, la misura della temperatura con un NTC e il controllo di un servo e della luminosità di un LED con un potenziometro, con schemi e codice passo dopo passo.

Domande frequenti

Cos'è un ADC e a cosa serve?

Un ADC (convertitore analogico-digitale) è il circuito che converte la tensione di un pin in un numero che il microcontrollore può elaborare. Serve a leggere componenti con uscita analogica come potenziometri, LDR e sensori di temperatura e umidità. Su Arduino è l'istruzione analogRead() ad attivarlo.

Qual è la differenza tra segnale analogico e digitale?

Un segnale analogico può assumere qualsiasi valore all'interno di un intervallo e varia in modo continuo, mentre un segnale digitale ha solo determinati livelli, LOW e HIGH su Arduino. digitalRead() ti dice solo se un pin è a 0 o a 1, analogRead() invece ti dà con un numero quanta tensione c'è.

Perché analogRead di Arduino restituisce valori da 0 a 1023?

L'ADC di Arduino Uno è a 10 bit e produce 2^10 = 1024 gradini diversi, numerati da 0 a 1023. Il valore 0 rappresenta 0 V, mentre 1023 rappresenta un gradino sotto la tensione di riferimento (di solito 5 V). Gli ADC a 12 bit di ESP32 e Pico restituiscono valori da 0 a 4095.

Come si converte il valore dell'ADC in tensione?

Usa la formula tensione = valore ADC × tensione di riferimento / numero di gradini. Su Arduino Uno è V = valore × 5,0 / 1024; sull'ESP32 analogReadMilliVolts() restituisce direttamente millivolt calibrati. Sul Pico, per il valore di read_u16() di MicroPython si usa V = valore × 3,3 / 65535.

I pin ADC2 dell'ESP32 funzionano con il Wi-Fi?

No. Sull'ESP32 anche il driver Wi-Fi usa l'unità ADC2, quindi con il Wi-Fi attivo non si può leggere dai pin dell'ADC2 (GPIO 0, 2, 4, 12–15, 25–27). Nei progetti con Wi-Fi collega i sensori analogici ai pin dell'ADC1 (GPIO 32–39).

Cos'è un DAC e in cosa differisce da un ADC?

Un DAC (convertitore digitale-analogico) fa il contrario di un ADC: converte un numero in una tensione. L'ADC serve a misurare la tensione che arriva da un sensore, il DAC a generare una tensione, come un suono o un segnale. Arduino Uno non ha un DAC e analogWrite() genera PWM; l'ESP32 ha due DAC a 8 bit su GPIO 25 e 26, e Uno R4 un DAC a 12 bit su A0.

Vedi tutti i progetti
Misurare l'intensità luminosa con LDR
#11Principiante

Misurare l'intensità luminosa con LDR

Vuoi vedere quanto è luminosa la tua stanza sotto forma di numero? L'LDR, un piccolo componente sensibile alla luce, misura la luce ambiente e l'Arduino scrive il valore sullo schermo del computer ogn

4.6(81)
139
Voltmetro digitale con display LCD
#56Intermedio
Premium

Voltmetro digitale con display LCD

Scopri quanta tensione dà una pila o un alimentatore con uno strumento costruito da te. Un partitore fatto di due resistenze abbassa la tensione alta a un livello che la scheda può leggere, e l'Arduin

4.5(27)
36
Lettura dell'intensità luminosa con il modulo LDR ESP32
#113Principiante

Lettura dell'intensità luminosa con il modulo LDR ESP32

Monitoraggio in tempo reale sul Serial Monitor del valore grezzo dell'ADC del modulo LDR e della percentuale relativa di luce calcolata dai valori di riferimento al buio e alla luce. Questa percentuale non è una misurazione in lux. Spiega il principio di calibrazione del modulo. È consigliabile eseguirlo prima del Progetto 10, poiché serve a determinare il valore di soglia.

4.6(36)
55
Luminosità di un LED con Potenziometro su ESP32 (Analogico → PWM)
#115Intermedio
Premium

Luminosità di un LED con Potenziometro su ESP32 (Analogico → PWM)

In questo progetto controlliamo in modo analogico la luminosità di un LED tramite un potenziometro. Il pin ADC dell'ESP32 legge la tensione tra 0 e 3,3 V dal potenziometro; il valore viene convertito in un segnale PWM (Pulse Width Modulation) e inviato al LED. Ruotando il potenziometro, il LED si accende e si spegne gradualmente. Con Arduino Core 3.0+ si usa direttamente analogWrite(); su ESP32-S2/S3 la risoluzione dell'ADC è fissata a 12 bit.

4.5(19)
32
Misura di temperatura con NTC e display Lcd 2x16
#44Principiante
Premium

Misura di temperatura con NTC e display Lcd 2x16

Misura la temperatura dell'ambiente con un sensore che sembra una piccola perlina. Questo componente, chiamato NTC, fa passare la corrente sempre più facilmente man mano che si scalda; l'Arduino legge

4.5(33)
44
Funzionamento del servomotore con potenziometro
#36Principiante
Premium

Funzionamento del servomotore con potenziometro

Mentre giri la manopola del potenziometro, il braccio del servomotore ruota insieme a lei: un'estremità della manopola corrisponde a zero gradi, l'altra a mezzo giro. L'Arduino legge di continuo la po

4.6(37)
52
Controllo LED con joystick
#59Intermedio
Premium

Controllo LED con joystick

Spingi fino in fondo la levetta del joystick in una direzione e si accende il LED di quella direzione: ci sono quattro colori diversi per destra, sinistra, su e giù. Quando lasci la levetta torna al c

4.7(50)
67
Lampada con miscelatore di colori ESP32 (3 potenziometri + LED RGB + OLED)
#138Avanzato
Premium

Lampada con miscelatore di colori ESP32 (3 potenziometri + LED RGB + OLED)

Una lampada mista colori intelligente che, ruotando i 3 potenziometri con l'ESP32, permette di miscelare il colore desiderato! Man mano che si ruotano i potenziometri rosso, verde e blu, il LED RGB a catodo comune cambia colore istantaneamente. Sul display OLED SSD1306 vengono visualizzati in tempo reale i valori compresi tra 0 e 255 per ogni colore, le barre di riempimento visive e il codice colore HEX (#FF8040, ad esempio). Un mini studio di colore professionale: insegna la teoria dei colori R

4.7(6)
10