Den populære I2C-protokol gør det muligt for to eller flere Arduino-kort at kommunikere. Find ud af, hvordan du forbinder og koder dem.

Mens en enkelt Arduino kan udføre mange opgaver, kan nogle projekter kræve brug af mere end et bord til at håndtere forskellige funktionaliteter. Så for at muliggøre dataoverførsel mellem de to mikrocontrollere, skal der opsættes en kommunikationsprotokol som CAN, SPI, I2C eller UART.

I denne guide vil vi dække det grundlæggende i, hvordan I2C fungerer, hardwareforbindelserne og den nødvendige softwareimplementering for at opsætte to Arduino-kort som I2C-master- og slave-enheder.

Hvad er I2C?

Inter-Integrated Circuit (I2C) er en udbredt kommunikationsprotokol i indlejrede systemer og mikrocontrollere for at muliggøre dataoverførsel mellem elektroniske enheder. I modsætning til SPI (Serial Peripheral Interface) giver I2C dig mulighed for at tilslutte mere end én masterenhed til en bus med enkelt eller flere slaveenheder. Den blev først brugt af Philips og er også kendt som Two Wire Interface (TWI) kommunikationsprotokol.

Hvordan fungerer I2C-kommunikation?

I2C bruger to tovejslinjer: Serial Data (SDA) og Serial Clock (SCL) til at overføre data og synkronisere kommunikation mellem enheder. Hver enhed, der er tilsluttet I2C-bussen, har en unik adresse, der identificerer den under kommunikation. I2C-protokollen tillader flere enheder at dele den samme bus, og hver enhed kan fungere som en master eller en slave.

Kommunikation initieres af masterenheden, og forkert adressering af slaveenheder kan forårsage fejl i overførslen. Tjek vores dybdegående guide vedr hvordan UART, SPI og I2C seriel kommunikation fungerer for at give dig en kontekst.

En stor fordel ved I2C-kommunikation, der er værd at bemærke, er den fleksibilitet, den tilbyder, når det kommer til strømstyring. Enheder, der fungerer ved forskellige spændingsniveauer, kan stadig kommunikere effektivt ved hjælp af spændingsskiftere. Dette betyder, at enheder, der kører ved 3,3V, har brug for spændingsskiftere for at forbinde til en 5V I2C-bus.

Trådbiblioteket

Wire-biblioteket er et indbygget Arduino-bibliotek, der giver funktioner til at kommunikere over I2C. Den bruger to ben - SDA og SCL - på Arduino-kortet til I2C-kommunikation.

I2C-stifter på Arduino Uno:

Arduino Nano I2C pins:

For at bruge biblioteket skal du inkludere Wire.h header-fil i begyndelsen af ​​din Arduino-skitse.

#omfatte

Wire-biblioteket giver funktioner til at starte kommunikation med en I2C-enhed, sende data og modtage data. Nogle vigtige funktioner du bør kende omfatter:

  • Wire.begin(): bruges til at tilslutte I2C-bussen og starte kommunikation.
  • Wire.beginTransmission(): bruges til at angive slaveadressen og starte en transmission.
  • Wire.write(): bruges til at sende data til I2C-enheden.
  • Wire.endTransmission(): bruges til at afslutte transmissionen og kontrollere for fejl.
  • Wire.requestFrom(): bruges til at anmode om data fra I2C-enheden.
  • Wire.available(): bruges til at kontrollere, om data er tilgængelige til at læse fra I2C-enheden.
  • Wire.read(): bruges til at læse data fra I2C-enheden.

Brug Wire.beginTransmission() funktion til at indstille adressen på sensoren, som indsættes som et argument. For eksempel hvis sensorens adresse er 0x68, ville du bruge:

Tråd.begynde Transmission(0x68);

Arduino I2C hardware opsætning

For at forbinde to Arduino-kort ved hjælp af I2C, skal du bruge følgende hardwarekomponenter:

  • To Arduino boards (master og slave)
  • Brødbræt
  • Jumper ledninger
  • To 4,7kΩ pull-up modstande

Tilslut SDA og SCL stifter af begge Arduino-plader til et brødbræt. Tilslut pull-up modstandene mellem SDA og SCL stifter og 5V strømskinne på brødbrættet. Slut til sidst de to brødbrætter sammen ved hjælp af jumperledninger.

Arduino Uno kredsløb

Arduino Nano Circuit

Billedkredit: Arduino I2C dokumentation

Opsætning af Arduino Boards som I2C Master og Slave Devices

Brug Wire.requestFrom() funktion til at angive adressen på den slaveenhed, som vi ønsker at kommunikere med. Brug derefter Wire.read() funktion til at hente data fra slaveenheden.

Hovedenhedskode:

#omfatte
ugyldigOpsætning(){
Tråd.begynde(); // join i2c bus
Seriel.begynde(9600); // start serie for output
}
ugyldigmodtage Data(){
int adresse = 8;
int bytesToRead = 6;
Tråd.anmodning Fra(adresse, bytesToRead);
mens (Tråd.ledig()) {
char data = Tråd.Læs();
Seriel.Print(data);
}
forsinke(500);
}
ugyldigsløjfe(){
modtageData();
}

Det Wire.onReceive() funktion bruges til at specificere, hvad der skal gøres, når slaven modtager data fra masterenheden. I ovenstående kode er Wire.available() funktion kontrollerer, om data er tilgængelige, og Wire.read() funktionen læser de data, der sendes af masterenheden.

Slave enhedskode:

#omfatte
ugyldigOpsætning(){
Tråd.begynde(8); // slutte sig til I2C-bussen med adresse 8
Tråd.ved Modtag(modtag Begivenhed); // ring til modtageBegivenhed, når data modtages
}
ugyldigsløjfe(){
forsinke(100);
}
ugyldigmodtage Begivenhed(int bytes){
Tråd.skrive("Hej "); // svar med besked på 6 bytes som forventet af master
}

Afsendelse og modtagelse af data ved hjælp af I2C

Lad os i dette eksempel læse temperaturen fra en DHT11 temperatursensor, der er forbundet med slaven Arduino, og udskrive den på den serielle monitor på master Arduino.

Lad os ændre koden, vi skrev tidligere, til at inkludere temperaturmålingen, som vi derefter sender til masterkortet over I2C-bussen. Hovedkortet kan derefter læse den værdi, vi sendte, og derefter vise den på den serielle monitor.

Hovedenhedskode:

#omfatte
ugyldigOpsætning(){
Tråd.begynde();
Seriel.begynde(9600);
Seriel.println("Mester initialiseret!");
}
ugyldigsløjfe(){
Tråd.anmodning Fra(8, 1); // Anmod om temperaturdata fra slave
hvis (Tråd.ledig()) {
byte temperatur = Tråd.Læs(); // Læs temperaturdata fra slave
Seriel.Print("Temperatur:");
Seriel.Print(temperatur);
Seriel.println("°C");
}
forsinke(2000); // Vent i 2 sekunder, før du anmoder om temperatur igen
}

Slave enhedskode:

#omfatte
#omfatte

#Definere DHTPIN 4 // Pin forbundet til DHT sensor
#Definere DHTTYPE DHT11 // DHT sensor type
DHT dht(DHTPIN, DHTTYPE);
byte temperatur;

ugyldigOpsætning(){
Tråd.begynde(8); // Slaveadresse er 8
Tråd.på anmodning(requestEvent);
dht.begynde();
}

ugyldigsløjfe(){
forsinke(2000); // Vent i 2 sekunder, indtil DHT stabiliserer sig
temperatur = dht.læsTemperatur(); // Aflæs temperatur fra DHT-sensor
}

ugyldigrequestEvent(){
Tråd.skrive(temperatur); // Send temperaturdata til master
}

Du kan tilpasse denne kode, så den passer til de sensorer, du måtte have i dit projekt, eller endda vise sensorværdierne på et displaymodul for at lav dit eget rumtermometer og fugtmåler.

Slaveadressering med I2C på Arduino

For at aflæse værdier fra komponenter tilføjet til en I2C-bus i et sådant projekt, er det vigtigt, at du medtager den korrekte slaveadresse ved kodning. Heldigvis tilbyder Arduino et scannerbibliotek, der forenkler processen med at identificere slave adresser, hvilket eliminerer behovet for at gennemsøge lange sensordatablade og forvirrende online dokumentation.

Brug følgende kode til at identificere enhver slave-enheds adresse, der findes på I2C-bussen.

#omfatte // Inkluder Wire-biblioteket til I2C-kommunikation

ugyldigOpsætning(){
Tråd.begynde(); // Initialiser I2C-kommunikationen
Seriel.begynde(9600); // Initialiser den serielle kommunikation med en baudrate på 9600
mens (!Seriel); // Vent på, at den serielle forbindelse er etableret
Seriel.println("\nI2C Scanner"); // Udskriv en meddelelse, der angiver starten på I2C-scanning
}

ugyldigsløjfe(){
byte fejl, adresse; // Erklære variabler for at gemme fejl og enhedsadresser
int nEnheder; // Erklærer en variabel for at gemme antallet af fundne enheder

Seriel.println("Scanner..."); // Udskriv en meddelelse, der angiver starten på I2C-scanning

nEnheder = 0; // Indstil antallet af fundne enheder til 0
til (adresse = 1; adresse < 127; adresse++) { // Iterér over alle mulige I2C-adresser
Tråd.begynde Transmission(adresse); // Start en transmission til den aktuelle adresse
fejl = Tråd.endTransmission(); // Afslut transmissionen og gem eventuelle fejl

hvis (fejl == 0) { // Hvis ingen fejl blev fundet
Seriel.Print("I2C-enhed fundet på adresse 0x"); // Udskriv en meddelelse, der angiver, at en enhed blev fundet
hvis (adresse < 16) Seriel.Print("0"); // Hvis adressen er mindre end 16, skal du tilføje et indledende 0 til formateringsformål
Seriel.Print(adresse, HEX); // Udskriv adressen i hexadecimalt format
Seriel.println(" !"); // Udskriv en meddelelse, der angiver, at en enhed blev fundet

nDevices++; // Øg antallet af fundne enheder
}
andethvis (fejl == 4) { // Hvis der blev fundet en fejl
Seriel.Print("Ukendt fejl på adresse 0x"); // Udskriv en meddelelse, der angiver, at der er fundet en fejl
hvis (adresse < 16) Seriel.Print("0"); // Hvis adressen er mindre end 16, skal du tilføje et indledende 0 til formateringsformål
Seriel.println(adresse, HEX); // Udskriv adressen i hexadecimalt format
}
}
hvis (nEnheder == 0) { // Hvis ingen enheder blev fundet
Seriel.println("Ingen I2C-enheder fundet\n"); // Udskriv en meddelelse, der angiver, at ingen enheder blev fundet
}
andet { // Hvis der blev fundet enheder
Seriel.println("færdig\n"); // Udskriv en meddelelse, der angiver slutningen af ​​I2C-scanning
}
forsinke(5000); // Forsink i 5 sekunder, før du starter næste scanning
}

Udvid dit projekt i dag

Sammenkobling af to Arduino-kort ved hjælp af I2C-kommunikationsprotokollen giver en fleksibel og effektiv måde at udføre komplekse opgaver på, som ikke kan håndteres af et enkelt kort. Ved hjælp af Wire-biblioteket er kommunikationen mellem de to kort ved hjælp af I2C gjort let, så du kan tilføje flere komponenter til dit projekt.