Laboratorul 6: I2C (Inter-Integrated Circuit)

Acest laborator acoperă noțiunea de I2C. Pentru aprofundarea acestui topic, consultați Datasheet ATmega328P și Inter-Integrated Circuit.

1. I2C (Inter-Integrated Circuit)

Protocolul I2C (sau IIC - Inter-Integrated Circuit) este un protocol de comunicație serială sincron, multi-master - multi-slave, dezvoltat de către Phillips în anul 1982. O magistrală I2C este formată din următoarele semnale:

  • SDA - linia de date
  • SCL - semnalul de clock

Semnalul de ceas este generat de către master iar linia de date este controlată atât de master cât și de către slave. La un moment dat un singur dispozitiv de pe magistrală poate controla linia de date. Din acestă cauză protocolul I2C este half-duplex.

I2C mai este cunoscut și cu numele de TWI (Two-Wire Interface)

2. Modul de funcționare

Față de SPI unde master-ul activa, prin intermediul semnalului de Slave Select, dispozitivul cu care dorea să comunice, I2C nu necesită un asemenea semnal adițional. Protocolul I2C introduce noțiunea de Slave Address. Adresa unui dispozitiv de tip slave este un număr pe 7 biți (cel mai comun), pe 8 biți sau 10 biți. Comunicația dintre un master și un slave se face prin mesaje și este tot timpul inițiată de către master. Aceste mesaje pot fi sparte în două tipuri de cadre:

  • un cadru de adresă
  • unul sau mai multe cadre de date

Aceste cadre sunt interschimbate numai după ce master-ul a trimis condiția de start. Sfârșitul unui mesaj este identificat prin condiția de stop.

1. Condiția de start

Înainte ca master-ul să trimită pe linia de date adresa slave-ului cu care dorește să comunice, acesta trebuie sa genereze o condiție de start. Condiția de start determină toate dispozitivele slave să ”asculte” linia de date pentru că va urma o adresă. Pentru a genera această condiție, master-ul lasă linia SCL in HIGH și pune linia SDA pe LOW.

2. Cadrul de adresă

După ce masterul a generat condiția de start, acesta trimite pe linia de date (SDA) adresa dispozitivului slave cu care dorește să comunice. Adresa este (de cele mai multe ori) un număr pe 7 biți (biții A6-A0). Bitul 0 menționează dacă master-ul inițiază o operație de Citire (bitul 0 este 1) sau o operație de Scriere (bitul 0 este 0).

Slave-ul care își recunoaște adresa trimite un ACK master-ului prin punerea liniei SDA pe LOW în al nouălea ciclu de ceas. Starea default a liniilor SDA/SCL este HIGH datorită rezistențelor de pull-up. Master-ul/Slave-ul doar ”trag” liniile pe LOW.

Master-ul identifică dacă a primit ACK (SDA pus pe LOW) sau NACK (SDA a rămas HIGH pe durata celui de-al nouălea ciclu de ceas).

3. Cadrele de date

Dacă master-ul a primit ACK (dacă există un slave pe magistrală cu adresa respectivă), el poate continua cu transmiterea datelor (operație de scriere), sau cu recepția datelor (operație de citire). Numărul de cadre de date este arbitrar, pot fi interschimbate oricâte. Fiecare cadru trimis/recepționat este ACK'd sau NACK'd. În funcție de operație (citire sau scriere), ACK-ul/NACK-ul este trimis fie de master fie de slave.

  • Daca master-ul a inițiat o operație de scriere, fiecare cadru trimis este confirmat (ACK'd) de către slave.
  • Daca master-ul a inițiat o operatie de citire, fiecare cadru recepționat este confirmat de (ACK'd) de master. Când master-ul dorește să oprească tranzacția după ce un anumit număr de cadre a fost recepționat, în loc să trimită ACK trimite NACK. Astfel slave-ul se va opri din transmitere.

4. Condiția de stop

După ce toate cadrele de date au fost interschimbate, master-ul generează condiția de stop. Aceasta este realizată prin eliberarea liniei SDA (trecere din LOW în HIGH) după eliberarea liniei SCL (trecere din LOW în HIGH).

Magistrala este aceeași pentru toți senzorii iar fiecare slave este referit printr-o adresa. Astfel, dacă avem mai mulți slave conectați pe magistrala, masterul alege cu care dintre ei dorește sa initieze comunicatia, precizand adresa specificata în datasheetul senzorului. Cel mai adesea, se folosesc adrese reprezentate pe 7 biti, iar cel mai nesemnificativ bit din octetul adresei ne arata daca urmeaza o operatie de scriere sau citire. După identificarea dispozitivului cu care se comunică, se poate începe transmiterea datelor.

3. Registre configurare I2C

Arduino poate funcționa atât în modul I2C Master cât și I2C Slave.

TWI Bit Rate Register

  • TWBR selectează divizorul de frecvență care generează frecvența ceasului SCL în modurile master.

TWI Control Register

  • TWCR este folosit pentru a activa interfața I2C, inițiază accesul la master prin aplicarea condiției START, generează confirmarea receptorului, generează condiția de oprire și control al acesteia în timpul scrierii în registrul de date TWDR. De asemenea, indică coliziuni la scriere.
  • TWINT : TWI Interrupt Flag
    • 1 - Clear, setat din software, in rest este setat de hardware.
  • TWEA: TWI Enable Acknowledge Bit
    • 1 - ACK este trimis
    • 0 - NACK, deconectare virtuală a dispozitivului de pe interfață
  • TWEN: TWI Enable Bit
    • 1 - Permite comunicarea
    • 0 - Încheierea transmisiunii indiferent de stare

TWI Status Register

  • TWSR descrie statusul si prescalerul interfeței.

TWI Data Register

  • TWDR conține în modul de transmisie următorul octet de transmis iar în modul de recepție ultimul octet primit.

4. Exemplu I2C Master/Slave

În acest laborator vom utiliza biblioteca Arduino pentru a ușura lucrul cu dispozitivele I2C. API-ul acesteia poate fi văzut în headerul ”Wire.h” din scheletul de laborator. Un exemplu de interconectare este de asemenea disponibil mai jos:

Master Writer

#include <Wire.h>
 
void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
}
 
byte x = 0;
 
void loop()
{
  Wire.beginTransmission(4); // transmit to device #4
  Wire.write("x is ");        // sends five bytes
  Wire.write(x);              // sends one byte  
  Wire.endTransmission();    // stop transmitting
 
  x++;
  delay(500);
}

Slave Reader

#include <Wire.h>
 
void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}
 
void loop()
{
  delay(100);
}
 
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // loop through all but the last
  {
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  int x = Wire.read();    // receive byte as an integer
  Serial.println(x);         // print the integer
}

Master Reader

#include <Wire.h>
 
void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}
 
void loop() {
  Wire.requestFrom(8, 6);    // request 6 bytes from slave device #8
 
  while (Wire.available()) { // slave may send less than requested
    char c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
  }
 
  delay(500);
}

Slave Writer

#include <Wire.h>
 
void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
}
 
void loop() {
  delay(100);
}
 
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Wire.write("hello "); // respond with message of 6 bytes
  // as expected by master
}

5. Exerciții

Task 0 Folosiți codul Arduino pentru a implementa un Master Reader și un Slave Writer. Master-ul va avea atașat un led, iar Slave-ul un buton. Master-ul trebuie să interogheze Slave-ul despre starea butonului și să aprindă/stingă led-ul în funcție de starea butonului (apăsat/neapăsat). Cele două board-uri vor comunica pe I2C. Pentru realizarea comunicării, Master-ul trebuie să facă request de citire a 3 caractere, iar Slave-ul să răspundă cu starea butonului: “ON!” sau “OFF”.

Task 1 Folosiți codul Arduino pentru a implementa un Master Writer și un Slave Reader. Master-ul va avea atașate 2 butoane, iar Slave-ul un led. Master-ul trebuie să trimită Slave-ului una din cele 2 comenzi: “ON!” sau “OFF”. Cele două board-uri vor comunica pe I2C. Pentru realizarea comunicării, la apăsarea primului buton, Master-ul trebuie să trimită comanda “ON!”, iar la apăsarea butonului 2, comanda “OFF”. În funcție de comanda recepționată, Slave-ul va aprinde/stinge led-ul.

6. Resurse

pinout Arduino UNO

7. Linkuri Utile

pm/lab/lab6-2021.txt · Last modified: 2021/04/22 13:43 by dumitru.brigalda
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0