Tutorial · Principiante · 30 min
Come funziona il bus I2C: indirizzi e resistori di pull-up
Scopri a cosa servono i resistori di pull-up su un bus I2C condiviso, perché più schede lo compromettono e come risolvere una collisione di indirizzi.
Introduzione
Ogni pagina di sensori di questo sito ti dice di collegare SDA e SCL, ma nessuna spiega che cosa stiano facendo quei due fili. Il più delle volte non hai bisogno di saperlo: colleghi tutto, la libreria funziona e il sensore restituisce una lettura.
Poi aggiungi una seconda scheda e la prima smette di rispondere. Oppure la libreria restituisce soli zeri senza errori. Oppure due dispositivi vogliono lo stesso indirizzo e nella documentazione non c’è nulla che dica come gestirlo.
Tutti questi sintomi dipendono dagli stessi pochi fatti su I²C, che sono meno di quanti i sintomi facciano pensare.
Due fili, molti dispositivi
I²C collega ogni dispositivo a una singola coppia di fili condivisa:
- SDA — la linea dei dati.
- SCL — il clock, sempre pilotato dal controllore.
Su un Arduino Uno sono A4 e A5, più i pin duplicati accanto ad AREF, che sono gli stessi due pin e non altri. Ogni dispositivo si collega agli stessi due in parallelo. Non esiste un filo di selezione del chip né un anello: aggiungere un dispositivo significa saldarlo alla stessa coppia.
È il motivo per cui scala così bene per i robot. Un MPU-6050, un VL53L0X e un PCA9685 condividono i due fili e vengono distinti tramite l’indirizzo, non tramite il cablaggio.
Nessuno pilota la linea alta
Questo è il fatto da cui discende tutto il resto.
Un dispositivo I²C può soltanto portare una linea a livello basso. Non può pilotarla alta: l’uscita è open-drain, vale a dire un interruttore verso massa e nulla più. Se lasciata libera, la linea oscillerebbe senza una tensione definita.
Sono i resistori di pull-up a portare la linea alta. Non sono una misura di robustezza né un accessorio opzionale: sono metà del circuito e, senza di essi, il bus non ha uno stato inattivo e nulla funziona.
Open-drain offre un vantaggio reale, ed è il motivo per cui si usa in questo modo: se due dispositivi parlano contemporaneamente, uno porta la linea bassa e l’altro la rilascia, e il risultato è basso. Niente si contraddice, niente va in cortocircuito e il dispositivo che ha perso può rilevarlo. Su un bus in cui qualsiasi dispositivo può parlare, questo è importante.
L’intervallo in cui deve vivere un resistore di pull-up
Ci sono limiti da entrambi i lati, entrambi calcolabili.
Troppo basso e nessun dispositivo riesce a portare la linea verso il basso. La specifica richiede a un dispositivo di assorbire almeno 3 mA mantenendo la linea sotto 0.4 V, quindi:
R_min = (Vcc − V_OL) / I_sink = (5 − 0.4) / 0.003 = 1533 Ω
Troppo alto e il fronte di salita è troppo lento. La linea sale seguendo una curva RC rispetto alla capacità del bus e il clock non aspetterà:
t_rise ≈ 0.85 × R × C ≤ 1000 ns at 100 kHz, ≤ 300 ns at 400 kHz
Ora arriva la parte che coglie di sorpresa. Quasi ogni scheda breakout hobbistica ha già montati i propri resistori di pull-up, di solito da 4.7 kΩ. Se ne colleghi diverse allo stesso bus, quei resistori sono in parallelo:
| Schede sul bus | Pull-up effettivo | Verdetto |
|---|---|---|
| 1 | 4700 Ω | Adeguato |
| 2 | 2350 Ω | Adeguato |
| 3 | 1567 Ω | Al limite — 34 Ω sopra il minimo |
| 4 | 1175 Ω | Sotto il limite |
Il guasto ha quindi una forma precisa: un sensore funziona, due funzionano, tre funzionano nei giorni buoni e la quarta scheda blocca l’intero bus. Nulla è rotto. I dispositivi semplicemente non riescono più a portare la linea abbastanza in basso da essere letti come livello basso.
La soluzione è rimuovere i resistori di pull-up da tutte le schede tranne una: di solito sono due piccoli resistori a montaggio superficiale marcati 472 e la documentazione della scheda indica quali. Dissaldarli con il saldatore sembra sbagliato la prima volta, ma è la risposta corretta.
Scansiona il bus prima di eseguire il debug di qualsiasi altra cosa
Prima di toccare una libreria, chiedi al bus che cosa contiene. Questo sketch prova ogni indirizzo e indica quali rispondono:
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(115200);
while (!Serial) {}
Serial.println(F("Scanning I2C..."));
uint8_t found = 0;
for (uint8_t addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print(F(" device at 0x"));
Serial.println(addr, HEX);
found++;
}
}
Serial.println(found ? F("done") : F("nothing answered — check wiring and pull-ups"));
}
void loop() {}
Il risultato indica quale problema hai:
- Non risponde nulla → cablaggio, alimentazione o resistori di pull-up mancanti. Non è la libreria.
- Il dispositivo risponde a un indirizzo inatteso → un pin di selezione dell’indirizzo è impostato in modo diverso da quanto presumevi.
- Il dispositivo risponde, ma la libreria restituisce soli zeri → il bus va bene e il problema è al di sopra.
Quest’ultimo punto vale più di tutto il resto dello sketch. Separa in modo netto «il filo è sbagliato» da «il codice è sbagliato», distinzione che occupa altrimenti gran parte del tempo di debug.
Indirizzi e la confusione con 0xD0
Un indirizzo I²C è di 7 bit. Sono riservati sedici valori, ne restano 112 utilizzabili.
Ecco la trappola. Sul filo, l’indirizzo viene inviato come sette bit seguiti da un bit di lettura/scrittura, quindi sembra un valore a 8 bit. Le schede tecniche riportano spesso questa forma a 8 bit. La libreria Wire di Arduino vuole la forma a 7 bit.
La scheda tecnica di un MPU-6050 indica l’indirizzo 0xD0 per la scrittura e 0xD1 per la lettura. La libreria Wire vuole 0x68:
0xD0 = 1101 0000
0x68 = 0110 1000 the same bits, shifted right by one
Se un dispositivo non risponde all’indirizzo stampato sulla sua scheda tecnica, prova quel valore spostato verso destra di una posizione prima di concludere che la scheda sia morta. È l’errore I²C più comune in assoluto.
Quando due dispositivi vogliono lo stesso indirizzo
Gli indirizzi sono impostati dal produttore, quindi le collisioni sono normali. Tre soluzioni, in ordine di preferibilità:
1. Un pin di selezione dell’indirizzo. La soluzione più economica, quando il componente ne ha uno. Un MPU-6050 usa 0x68, oppure 0x69 con il pin AD0 portato alto, così due possono coesistere con un solo filo. Una PCA9685 ha sei ponticelli saldabili che danno 64 indirizzi: per questo puoi collegare in catena fino a 62 schede su un solo bus.
2. Mantieni gli altri in reset mentre ne riassegni uno. Alcuni componenti hanno un indirizzo fisso ma possono essere riprogrammati durante l’esecuzione. Un VL53L0X è sempre 0x29 appena uscito dalla fabbrica e questo è il metodo standard per usarne diversi: porta in basso il pin XSHUT di ogni sensore, portane uno in alto, comandagli di passare a un nuovo indirizzo e ripeti. Il nuovo indirizzo risiede nella RAM, quindi la sequenza si ripete a ogni avvio.
3. Un multiplexer. Un TCA9548A è un singolo dispositivo all’indirizzo 0x70 con otto bus commutati dietro. Seleziona un canale, parla con il dispositivo che si trova su quel canale, seleziona il successivo. È la soluzione per otto sensori identici senza pin di selezione né riassegnazione, al costo di un componente e di un bus utilizzabile un dispositivo alla volta.
Velocità, lunghezza e ciò che causa davvero i guasti
I²C è stato progettato per funzionare fra chip su una stessa scheda. Un robot lo estende lungo un telaio ed è lì che i limiti iniziano a farsi sentire.
- La capacità è il limite reale, non la lunghezza. La specifica consente 400 pF totali. Il cavo piatto aggiunge circa 50–100 pF per metro e ogni dispositivo circa 10 pF. Le tratte lunghe falliscono rendendo il fronte di salita troppo lento, come mostra il limite decrescente nella figura sopra.
- Rallenta prima di arrenderti.
Wire.setClock(50000)dimezza la frequenza standard e offre un margine reale su un bus al limite. Un robot che interroga un sensore a 100 Hz non ha bisogno di 400 kHz. - Mantieni separati SDA e SCL. Farli correre come coppia adiacente in un cavo piatto accoppia i fronti di clock ai dati. Un filo di massa fra loro aiuta.
- Clock stretching. Un dispositivo che ha bisogno di tempo può trattenere SCL basso e il controllore deve aspettare. L’hardware I²C reale lo gestisce; alcune implementazioni a bit banging non lo gestiscono e il sintomo è un dispositivo che funziona da solo ma corrompe i dati quando il bus è occupato.
- Bus bloccato. Se un dispositivo è stato reimpostato durante un trasferimento può restare con SDA basso per sempre e ogni scansione non restituisce nulla. Nove impulsi su SCL gli permettono di completare il byte che credeva di stare inviando e di rilasciare la linea: è ciò che fa la routine di ripristino di una buona libreria.
Miscelare 3.3 V e 5 V
Un ESP32 e un Raspberry Pi gestiscono I²C a 3.3 V; un Uno lo gestisce a 5 V. Poiché il bus è open-drain, la tensione di pull-up determina il livello alto per tutti coloro che sono sul bus. Se esegui il pull-up verso 5 V con un componente collegato che accetta soltanto 3.3 V, stai applicando 5 V a un pin la cui portata è 3.6 V.
Due soluzioni ti salvano quasi sempre, ed è utile sapere su quale stai contando:
- Molte schede breakout dichiarate «compatibili con 5 V» hanno un chip a 3.3 V con un regolatore e traslatori di livello già sulla scheda. Verifica prima di presumere: la sola presenza di un regolatore di tensione non significa che i pin di segnale siano protetti.
- Dove non sono presenti, un traslatore di livelli MOSFET bidirezionale è la soluzione standard. Un divisore di tensione non lo è: rompe il comportamento open-drain nella direzione che conta.
Quando va storto
| Sintomo | Di solito |
|---|---|
| Lo scanner non trova assolutamente nulla | Resistori di pull-up mancanti, SDA/SCL scambiati oppure massa comune assente |
| Funziona con tre schede e si blocca con quattro | Pull-up in parallelo sotto il limite di corrente assorbita |
| Il dispositivo è assente all’indirizzo della sua scheda tecnica | Indirizzo a 8 bit riportato; spostalo verso destra di una posizione |
| Due sensori identici, ma ne risponde uno solo | Collisione di indirizzi: pin di selezione, XSHUT o multiplexer |
| Legge correttamente su un tratto corto, ma fallisce su un cavo lungo | Capacità del bus; rallenta il clock o accorcia la tratta |
| Corruzione intermittente mentre i motori girano | Rumore dell’alimentazione — vedi alimentazione e cali di tensione |
| Lo scanner restituisce improvvisamente nulla dopo un blocco | Un dispositivo tiene SDA basso; invia nove impulsi di clock su SCL |
| Funziona da solo e corrompe i dati quando il bus è occupato | Il controllore non rispetta il clock stretching |
L’abitudine utile è eseguire lo scanner per primo, ogni volta, prima di aprire il codice di una libreria. Richiede trenta secondi e divide il problema a metà: metà di tutto il debug I²C consiste nel non sapere da quale lato del filo si trova il guasto.
Esplora il grafo
Fa parte di queste costruzioni
Progetti e percorsi di apprendimento che includono questo tutorial.
Per approfondire