Det populära I2C-protokollet gör att två eller flera Arduino-kort kan kommunicera. Upptäck hur du ansluter och kodar dem.

Medan en enda Arduino kan utföra många uppgifter, kan vissa projekt kräva användning av mer än ett kort för att hantera olika funktioner. Så för att möjliggöra dataöverföring mellan de två mikrokontrollerna måste ett kommunikationsprotokoll som CAN, SPI, I2C eller UART ställas in.

I den här guiden kommer vi att täcka grunderna i hur I2C fungerar, hårdvaruanslutningarna och mjukvaruimplementeringen som behövs för att sätta upp två Arduino-kort som I2C-master- och slavenheter.

Vad är I2C?

Inter-Integrated Circuit (I2C) är ett allmänt använt kommunikationsprotokoll i inbyggda system och mikrokontroller för att möjliggöra dataöverföring mellan elektroniska enheter. Till skillnad från SPI (Serial Peripheral Interface) låter I2C dig ansluta mer än en masterenhet till en buss med en eller flera slavenheter. Det användes först av Philips och är också känt som kommunikationsprotokollet Two Wire Interface (TWI).

instagram viewer

Hur fungerar I2C-kommunikation?

I2C använder två dubbelriktade linjer: Serial Data (SDA) och Serial Clock (SCL) för att överföra data och synkronisera kommunikation mellan enheter. Varje enhet som är ansluten till I2C-bussen har en unik adress som identifierar den under kommunikation. I2C-protokollet tillåter flera enheter att dela samma buss, och varje enhet kan fungera som en master eller en slav.

Kommunikation initieras av masterenheten och felaktig adressering av slavenheter kan orsaka överföringsfel. Kolla in vår djupgående guide om hur UART, SPI och I2C seriell kommunikation fungerar för att ge dig lite sammanhang.

En stor fördel med I2C-kommunikation som är värd att notera är den flexibilitet som den erbjuder när det kommer till energihantering. Enheter som arbetar på olika spänningsnivåer kan fortfarande kommunicera effektivt med hjälp av spänningsskiftare. Detta innebär att enheter som arbetar på 3,3V behöver spänningsskiftare för att ansluta till en 5V I2C-buss.

Trådbiblioteket

Wire-biblioteket är ett inbyggt Arduino-bibliotek som tillhandahåller funktioner för att kommunicera över I2C. Den använder två stift – SDA och SCL – på Arduino-kortet för I2C-kommunikation.

I2C-stift på Arduino Uno:

Arduino Nano I2C-stift:

För att använda biblioteket måste du inkludera Wire.h header-fil i början av din Arduino-skiss.

#omfatta

Wire-biblioteket tillhandahåller funktioner för att initiera kommunikation med en I2C-enhet, skicka data och ta emot data. Några viktiga funktioner du bör känna till inkluderar:

  • Wire.begin(): används för att ansluta till I2C-bussen och initiera kommunikation.
  • Wire.beginTransmission(): används för att specificera slavadressen och påbörja en överföring.
  • Wire.write(): används för att skicka data till I2C-enheten.
  • Wire.endTransmission(): används för att avsluta överföringen och kontrollera efter fel.
  • Wire.requestFrom(): används för att begära data från I2C-enheten.
  • Wire.available(): används för att kontrollera om data finns att läsa från I2C-enheten.
  • Wire.read(): används för att läsa data från I2C-enheten.

Använd Wire.beginTransmission() funktion för att ställa in adressen till sensorn, som infogas som ett argument. Till exempel om sensorns adress är 0x68, skulle du använda:

Tråd.börja sändning(0x68);

Arduino I2C hårdvaruinstallation

För att ansluta två Arduino-kort med I2C behöver du följande hårdvarukomponenter:

  • Två Arduino-brädor (master och slav)
  • Bakbord
  • Bygeltrådar
  • Två 4,7kΩ pull-up motstånd

Anslut SDA och SCL stift på båda Arduino-brädorna till en brödbräda. Anslut pull-up motstånden mellan SDA och SCL stift och 5V elskena på brödbrädan. Slutligen, koppla ihop de två brödbrädorna med hjälp av bygelkablar.

Arduino Uno krets

Arduino Nano Circuit

Bildkredit: Arduino I2C dokumentation

Konfigurera Arduino-brädorna som I2C-master- och slavenheter

Använd Wire.requestFrom() funktion för att ange adressen till slavenheten som vi vill kommunicera med. Använd sedan Wire.read() funktion för att hämta data från slavenheten.

Huvudenhetskod:

#omfatta
tomhetuppstart(){
Tråd.Börja(); // gå med i i2c buss
Serie.Börja(9600); // starta seriell för utdata
}
tomhetta emot Data(){
int adress = 8;
int bytesToRead = 6;
Tråd.förfrågan från(adress, bytesToRead);
medan (Tråd.tillgängliga()) {
röding data = Tråd.läsa();
Serie.skriva ut(data);
}
dröjsmål(500);
}
tomhetslinga(){
receiveData();
}

De Wire.onReceive() funktionen används för att specificera vad som ska göras när slaven tar emot data från masterenheten. I ovanstående kod är Wire.available() funktionen kontrollerar om data finns tillgänglig, och Wire.read() funktionen läser data som skickas av masterenheten.

Slavenhetskod:

#omfatta
tomhetuppstart(){
Tråd.Börja(8); // gå med i I2C-bussen med adress 8
Tråd.vid ta emot(ta emotEvent); // ring receiveHändelse när data tas emot
}
tomhetslinga(){
dröjsmål(100);
}
tomhetta emotEvent(int byte){
Tråd.skriva("Hallå "); // svara med meddelande på 6 byte som förväntat av master
}

Skicka och ta emot data med I2C

I det här exemplet, låt oss läsa temperaturen från en DHT11-temperatursensor som är kopplad till slaven Arduino och skriva ut den på den seriella monitorn på master Arduino.

Låt oss modifiera koden vi skrev tidigare för att inkludera temperaturmätningen som vi sedan skickar till masterkortet över I2C-bussen. Masterkortet kan sedan läsa av värdet vi skickade och sedan visa det på den seriella monitorn.

Huvudenhetskod:

#omfatta
tomhetuppstart(){
Tråd.Börja();
Serie.Börja(9600);
Serie.println("Mästaren initialiserad!");
}
tomhetslinga(){
Tråd.förfrågan från(8, 1); // Begär temperaturdata från slav
om (Tråd.tillgängliga()) {
byte temperatur = Tråd.läsa(); // Läs temperaturdata från slav
Serie.skriva ut("Temperatur:");
Serie.skriva ut(temperatur);
Serie.println("°C");
}
dröjsmål(2000); // Vänta i 2 sekunder innan du begär temperatur igen
}

Slavenhetskod:

#omfatta
#omfatta

#definiera DHTPIN 4 // Pin ansluten till DHT-sensor
#definiera DHTTYPE DHT11 // DHT-sensortyp
DHT dht(DHTPIN, DHTTYPE);
byte temperatur;

tomhetuppstart(){
Tråd.Börja(8); // Slavadress är 8
Tråd.på förfrågan(requestEvent);
dht.Börja();
}

tomhetslinga(){
dröjsmål(2000); // Vänta i 2 sekunder tills DHT stabiliseras
temperatur = dht.läsTemperatur(); // Läs av temperatur från DHT-sensor
}

tomhetrequestEvent(){
Tråd.skriva(temperatur); // Skicka temperaturdata till master
}

Du kan anpassa den här koden för att passa vilka sensorer du kan ha i ditt projekt, eller till och med visa sensorvärdena på en displaymodul för att gör din egen rumstermometer och fuktmätare.

Slavadressering med I2C på Arduino

För att läsa värden från komponenter som läggs till en I2C-buss i ett sådant projekt är det viktigt att du tar med rätt slavadress vid kodning. Lyckligtvis erbjuder Arduino ett skannerbibliotek som förenklar processen att identifiera slav adresser, vilket eliminerar behovet av att sålla igenom långa sensordatablad och förvirrande online dokumentation.

Använd följande kod för att identifiera alla slavenheters adress som finns på I2C-bussen.

#omfatta // Inkludera Wire-biblioteket för I2C-kommunikation

tomhetuppstart(){
Tråd.Börja(); // Initiera I2C-kommunikationen
Serie.Börja(9600); // Initiera den seriella kommunikationen med en baudhastighet på 9600
medan (!Serie); // Vänta tills den seriella anslutningen upprättas
Serie.println("\nI2C-skanner"); // Skriv ut ett meddelande som indikerar början av I2C-skanning
}

tomhetslinga(){
byte fel, adress; // Deklarera variabler för att lagra fel och enhetsadresser
int nDevices; // Deklarera en variabel för att lagra antalet hittade enheter

Serie.println("Läser in..."); // Skriv ut ett meddelande som indikerar början av I2C-skanning

nDevices = 0; // Ställ in antalet hittade enheter till 0
för (adress = 1; adress < 127; adress++) { // Iterera över alla möjliga I2C-adresser
Tråd.börja sändning(adress); // Starta en överföring till den aktuella adressen
fel = Tråd.endTransmission(); // Avsluta överföringen och lagra eventuella fel

om (fel == 0) { // Om inga fel hittades
Serie.skriva ut("I2C-enhet hittades på adress 0x"); // Skriv ut ett meddelande som indikerar att en enhet hittades
om (adress < 16) Serie.skriva ut("0"); // Om adressen är mindre än 16, lägg till en inledande nolla för formateringsändamål
Serie.skriva ut(adress, HEX); // Skriv ut adressen i hexadecimalt format
Serie.println(" !"); // Skriv ut ett meddelande som indikerar att en enhet hittades

nDevices++; // Öka antalet enheter som hittas
}
annanom (fel == 4) { // Om ett fel hittades
Serie.skriva ut("Okänt fel på adress 0x"); // Skriv ut ett meddelande som indikerar att ett fel hittades
om (adress < 16) Serie.skriva ut("0"); // Om adressen är mindre än 16, lägg till en inledande nolla för formateringsändamål
Serie.println(adress, HEX); // Skriv ut adressen i hexadecimalt format
}
}
om (nEnheter == 0) { // Om inga enheter hittades
Serie.println("Inga I2C-enheter hittades\n"); // Skriv ut ett meddelande som indikerar att inga enheter hittades
}
annan { // Om enheter hittades
Serie.println("klar\n"); // Skriv ut ett meddelande som indikerar slutet på I2C-skanningen
}
dröjsmål(5000); // Fördröj i 5 sekunder innan nästa skanning påbörjas
}

Utöka ditt projekt idag

Att ansluta två Arduino-kort med I2C-kommunikationsprotokollet erbjuder ett flexibelt och effektivt sätt att utföra komplexa uppgifter som inte kan hanteras av ett enda kort. Med hjälp av Wire-biblioteket görs kommunikationen mellan de två korten med I2C enkel, vilket gör att du kan lägga till fler komponenter till ditt projekt.