Como usar I2C no Arduino: comunicação entre duas placas Arduino

Tempo de leitura: 11 minutes

Em nosso guia anterior, aprendemos sobre comunicação SPI no Arduino. Hoje vamos aprender sobre outro protocolo de comunicação serial: I2C (Inter Integrated Circuits). Comparando I2C com SPI, I2C tem apenas dois fios, enquanto o SPI usa quatro e I2C pode ter Multiple Master e Slave, enquanto o SPI pode ter apenas um master e vários slaves. Portanto, há mais de um microcontrolador em um projeto que precisa ser mestre e então o I2C é usado. A comunicação I2C é geralmente usada para se comunicar com giroscópio, acelerômetro, sensores de pressão barométrica, telas de LED, etc.

Neste guia do Arduino I2C, usaremos a comunicação I2C entre duas placas arduino e enviaremos valores (0 a 127) entre si usando um potenciômetro. Os valores serão exibidos no LCD 16×2 conectado a cada um do Arduino. Aqui, um Arduino atuará como Master e outro como Slave. Então, vamos começar com a introdução sobre a comunicação I2C.

 

O que é o protocolo de comunicação I2C?

O termo IIC significa “Inter Integrated Circuits”. É normalmente denotado como I2C ou I ao quadrado C ou mesmo como protocolo de interface de 2 fios (TWI) em alguns lugares, mas significa o mesmo. I2C é um protocolo de comunicação síncrona, o que significa que ambos os dispositivos que compartilham as informações devem compartilhar um sinal de relógio comum. Ele tem apenas dois fios para compartilhar informações, dos quais um é usado para o sinal do galo e o outro é usado para enviar e receber dados.

 

Como funciona a comunicação I2C?

A comunicação I2C foi introduzida pela primeira vez por Phillips. Como disse anteriormente, ele tem dois fios, esses dois fios serão conectados em dois dispositivos. Aqui, um dispositivo é chamado de mestre e o outro dispositivo é chamado de escravo. A comunicação deve e sempre ocorrerá entre um Mestre e um Escravo. A vantagem da comunicação I2C é que mais de um escravo pode ser conectado a um mestre.

A comunicação completa ocorre por meio desses dois fios, a saber, Serial Clock (SCL) e Serial Data (SDA).

Serial Clock (SCL): Compartilha o sinal de clock gerado pelo mestre com o escravo

Dados seriais (SDA): Envia os dados de e para o mestre e o escravo.

A qualquer momento, apenas o mestre será capaz de iniciar a comunicação. Como há mais de um escravo no barramento, o mestre deve referir-se a cada escravo usando um endereço diferente. Quando endereçado, apenas o escravo com aquele endereço particular responderá de volta com a informação enquanto os outros continuam desligando. Dessa forma, podemos usar o mesmo barramento para nos comunicarmos com vários dispositivos.

Os níveis de tensão de I2C não são predefinidos. A comunicação I2C é flexível, significa que o dispositivo que é alimentado por 5v volt, pode usar 5v para I2C e os dispositivos de 3,3v podem usar 3v para comunicação I2C. Mas e se dois dispositivos operando em tensões diferentes precisarem se comunicar usando I2C? Um barramento I2C de 5V não pode ser conectado a um dispositivo de 3,3V. Neste caso, os deslocadores de tensão são usados ​​para combinar os níveis de tensão entre dois barramentos I2C.

Existem alguns conjuntos de condições que enquadram uma transação. A inicialização da transmissão começa com uma borda descendente de SDA, que é definida como condição ‘START’ no diagrama abaixo, onde o mestre deixa SCL alto enquanto define SDA baixo.

Conforme mostrado no diagrama acima abaixo,

A borda descendente do SDA é o gatilho de hardware para a condição START. Depois disso, todos os dispositivos no mesmo barramento entram no modo de escuta.

Da mesma maneira, a borda ascendente do SDA para a transmissão, que é mostrada como condição de ‘PARADA’ no diagrama acima, onde o mestre deixa o SCL alto e também libera o SDA para ir para ALTO. Portanto, a borda ascendente do SDA interrompe a transmissão.

O bit R/W indica a direção da transmissão dos bytes seguintes, se for HIGH significa que o escravo transmitirá e se for baixo significa que o mestre transmitirá.

Cada bit é transmitido em cada ciclo de clock, portanto, leva 8 ciclos de clock para transmitir um byte. Após cada byte enviado ou recebido, o nono ciclo de clock é mantido para o ACK / NACK (confirmado / não confirmado). Este bit ACK é gerado pelo escravo ou mestre dependendo da situação. Para o bit ACK, SDA é definido como baixo pelo mestre ou escravo no 9º ciclo de clock. Portanto, é baixo considerado ACK, caso contrário, NACK.

 

Onde usar a comunicação I2C?

A comunicação I2C é usada apenas para comunicação de curta distância. É certamente confiável até certo ponto, pois tem um pulso de clock sincronizado para torná-lo inteligente. Este protocolo é usado principalmente para se comunicar com o sensor ou outros dispositivos que precisam enviar informações a um mestre. É muito útil quando um microcontrolador precisa se comunicar com muitos outros módulos escravos usando, no mínimo, fios. Se você estiver procurando por uma comunicação de longo alcance, você deve tentar o RS232 e se estiver procurando por uma comunicação mais confiável, você deve tentar o protocolo SPI.

 

I2C no Arduino

A imagem abaixo mostra os pinos I2C presentes no Arduino UNO.

I2C LinhaPino no Arduino
SDAA4
SCLA5

Antes de começarmos a programar I2C usando dois Arduino. Precisamos aprender sobre a biblioteca Wire usada no IDE Arduino.

A biblioteca <Wire.h> está incluída no programa para usar as seguintes funções para comunicação I2C.

1. Wire.begin(endereço):

Uso: esta biblioteca é usada para fazer comunicação com dispositivos I2C. Isso inicia a biblioteca de fios e junta-se ao barramento I2C como mestre ou escravo.

Endereço: O endereço do escravo de 7 bits é opcional e se o endereço não for especificado, ele se junta ao barramento como um mestre como este [Wire.begin ()].

2. Wire.read():

Uso: Esta função é usada para ler um byte que foi recebido do dispositivo mestre ou escravo, que foi transmitido de um dispositivo escravo para um dispositivo mestre após uma chamada para requestFrom () ou foi transmitido de um mestre para um escravo.

3. Wire.write():

Uso: Esta função é usada para gravar dados em um dispositivo escravo ou mestre.

Slave to Master: o Slave grava dados em um master quando Wire.RequestFrom() é usado no master.

Master to Slave: Para transmissão de um dispositivo mestre para escravo, Wire.write() é usado entre as chamadas para Wire.beginTransmission() e Wire.endTransmission().

Wire.write() pode ser escrito como:

  • Wire.write(valor)

valor: um valor a ser enviado como um único byte.

  • Wire.write(string):

string: uma string a ser enviada como uma série de bytes.

  • Wire.write(dados, comprimento):

dados: uma matriz de dados para enviar como bytes

comprimento: o número de bytes a transmitir.

4. Wire.beginTransmission(endereço):

Uso: Esta função é usada para iniciar uma transmissão para o dispositivo I2C com o endereço de escravo fornecido. Posteriormente, crie uma fila de bytes para transmissão com a função write() e transmita-os chamando a função endTransmission(). O endereço de 7 bits do dispositivo é transmitido.

5. Wire.endTransmission();

Uso: Esta função é usada para encerrar uma transmissão para um dispositivo escravo que foi iniciada por beginTransmission() e transmite os bytes que foram enfileirados por Wire.write().

6. Wire.onRequest();

Uso: Esta função é chamada quando um mestre solicita dados usando Wire.requestFrom() do dispositivo escravo. Aqui podemos incluir a função Wire.write() para enviar dados ao mestre.

7. Wire.onReceive();

Uso: Esta função é chamada quando um dispositivo escravo recebe dados de um mestre. Aqui podemos incluir Wire.read(); função para ler os dados enviados do mestre.

8. Wire.requestFrom(endereço, quantidade);

Uso: Esta função é usada no mestre para solicitar bytes de um dispositivo escravo. A função Wire.read () é usada para ler os dados enviados do dispositivo escravo.

endereço: o endereço de 7 bits do dispositivo para solicitar bytes de

quantidade: o número de bytes a solicitar

 

Componentes necessários

Arduino Uno (x2)
Módulo de LCD 16×2 (x2)
Potenciômetro 10K (x4)
ProtoBoard
Fios de conexão

 

Diagrama de circuito

 

Explicação de trabalho

Aqui, para demonstrar a comunicação I2C no Arduino, usamos Two Arduino UNO com dois monitores LCD 16X2 conectados um ao outro e usamos dois potenciômetros em ambos arduino para determinar os valores de envio (0 a 127) de mestre para escravo e escravo para mestre variando o potenciômetro.

Pegamos o valor analógico de entrada no pino A0 do arduino de (0 a 5 V) usando o potenciômetro e os convertemos em valor analógico para digital (0 a 1023). Em seguida, esses valores ADC são posteriormente convertidos em (0 a 127), pois podemos enviar apenas dados de 7 bits por meio da comunicação I2C. A comunicação I2C ocorre por meio de dois fios nos pinos A4 e A5 de ambos os arduino.

Os valores no LCD do Slave Arduino serão alterados variando o POT no lado mestre e vice-versa.

Programação I2C em Arduino

Este tutorial tem dois programas, um para o Arduino mestre e outro para o Arduino escravo. Programas completos para ambos os lados são fornecidos no final deste projeto.

 

Explicação de programação do Master Arduino

1. Em primeiro lugar, precisamos incluir a biblioteca Wire para usar as funções de comunicação I2C e a biblioteca LCD para usar as funções LCD. Defina também os pinos de LCD para LCD 16×2. Saiba mais sobre a interface do LCD com o Arduino aqui.

#include<Wire.h>    
// Incluir a biblioteca: 
#include <LiquidCrystal_I2C.h> 
// Cria o objeto lcd da classe LiquidCrystal_I2C: 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

2. No void setup()

  • Iniciamos a comunicação serial na taxa Baud 9600.
Serial.begin(9600);
  • Em seguida, iniciamos a comunicação I2C no pino (A4, A5)
Wire.begin(); // Começa a comunicação I2C no pino (A4, A5)
  • Em seguida, inicializamos o módulo de exibição LCD no modo 16X2 e exibimos a mensagem de boas-vindas e apagamos após cinco segundos.
lcd.begin(16,2);            // Inicia o display LCD
lcd.setCursor(0,0);         // Define o cursor na primeira linha do display
lcd.print("Cap Sistema");   // Imprime Cap Sistema no LCD
lcd.setCursor(0,1);         // Define o Cursor na segunda linha do Display
lcd.print("I2C 2 ARDUINO"); // Imprime I2C ARDUINO em LCD
delay(5000);                // Atrase por 5 segundos
lcd.clear();                // Limpa o display LCD

3. Em void loop()

  • Primeiro precisamos obter dados do Slave, então usamos requestFrom () com o endereço do escravo 8 e solicitamos um byte
Wire.requestFrom(8,1);
  • O valor recebido é lido usando Wire.read()
byte MasterReceive = Wire.read();
  • Em seguida, precisamos ler o valor analógico do arduino mestre POT ligado ao pino A0
int potvalue = analogRead(A0);
  • Convertemos esse valor em termos de um byte de 0 a 127.
byte MasterSend = map(potvalue,0,1023,0,127);
  • Em seguida, precisamos enviar esses valores convertidos, então começamos a transmissão com o arduino escravo com 8 endereços// solicitar 1 byte do arduino escravo (8)
Wire.beginTransmission(8);                          
Wire.write(MasterSend);                       
 Wire.endTransmission();
  • Em seguida, exibimos esses valores recebidos do arduino escravo com um atraso de 500 microssegundos e continuamente recebemos e exibimos esses valores.
lcd.setCursor(0,0);                            // Defina o Cursor na linha um do LCD
lcd.print(">>  Master  <<");                   // Imprime >> Master << no LCD
lcd.setCursor(0,1);                            // Define o Cursor na linha dois do LCD
lcd.print("SlaveVal:");                        // Imprime SlaveVal: no LCD
lcd.print(MasterReceive);                      // Imprime MasterReceive em LCD recebido de Slave
Serial.println("Mestre recebido de escravo");  // Imprime no monitor serial
Serial.println(MasterReceive);
delay(500);                                    
lcd.clear();

 

Explicação de programação do Slave Arduino

1. Da mesma forma que o mestre, em primeiro lugar, precisamos incluir a biblioteca Wire para usar as funções de comunicação I2C e a biblioteca LCD para usar as funções LCD. Defina também os pinos de LCD para LCD 16×2

#include<Wire.h>    
// Incluir a biblioteca: 
#include <LiquidCrystal_I2C.h> 
// Cria o objeto lcd da classe LiquidCrystal_I2C: 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

2. No void setup()

  • Iniciamos a comunicação serial na taxa Baud 9600.
Serial.begin(9600);
  • Em seguida, iniciamos a comunicação I2C no pino (A4, A5) com o endereço do escravo como 8. Aqui é importante especificar o endereço do escravo.
Wire.begin(8);
  • Em seguida, precisamos chamar a função quando o Slave recebe o valor do mestre e quando o mestre solicita o valor do Slave
Wire.onReceive(receiveEvent);          
Wire.onRequest(requestEvent);
  • Em seguida, inicializamos o módulo de exibição LCD no modo 16X2 e exibimos a mensagem de boas-vindas e apagamos após cinco segundos.
lcd.begin(16,2);                          // Inicializa o display LCD
lcd.setCursor(0,0);                       // Define o cursor na primeira linha do display
lcd.print("Cap Sistema");                 // Imprime Cap Sistema no LCD
lcd.setCursor(0,1);                       // Define o Cursor na segunda linha do Display
lcd.print("I2C 2 ARDUINO");               // Imprime I2C ARDUINO em LCD
delay(5000);                              // Atraso de 5 segundos
lcd.clear();                              // Limpa o display LCD

3. Em seguida, temos duas funções, uma para o evento de solicitação e outra para o evento de recebimento

Para solicitar evento

Quando o mestre solicitar o valor do escravo esta função será executada. Esta função obtém o valor de entrada do Slave POT, converte-o em termos de 7 bits e envia esse valor ao mestre.

void requestEvent()  
{
  int potvalue = analogRead(A0);     
  byte SlaveSend = map(potvalue,0,1023,0,127);   
  Wire.write(SlaveSend);        
}

Para receber evento

Quando o Mestre envia dados para o escravo com endereço de escravo (8), esta função será executada. Esta função lê o valor recebido do mestre e armazena em uma variável do tipo byte.

void receiveEvent (int howMany
{
   SlaveReceived = Wire.read();                   
}

4. No Void loop():

Exibimos o valor recebido do mestre continuamente no módulo de display LCD.

void loop(void)
{
  lcd.setCursor(0,0);                              //Defina o cursor na linha um do LCD
  lcd.print(">>  Slave  <<");                      //Imprime >> Slave << no LCD
  lcd.setCursor(0,1);                              //Define o cursor na linha dois do LCD
  lcd.print("MasterVal:");                         //Imprime MasterVal: em LCD
  lcd.print(SlaveReceived);                        //Imprime o valor SlaveReceived no LCD recebido do Master
  Serial.println("Escravo recebido do mestre:");   //Impressões no Monitor Serial
  Serial.println(SlaveReceived);
  delay(500);
  lcd.clear();
}

Girando o potenciômetro de um lado, você pode ver os valores variáveis no LCD do outro lado:

Então é assim que a comunicação I2C ocorre no Arduino, aqui usamos dois Arduinos para demonstrar não apenas o envio de dados, mas também o recebimento dos dados usando a comunicação I2C. Agora você pode conectar qualquer sensor I2C ao Arduino.

 

A codificação completa do Arduino Master e Slave é fornecida abaixo.

 

Código

Programação Master Arduino

//I2C MASTER CODE 
//Comunicação I2C entre dois Arduino

#include<Wire.h> 
// Incluir a biblioteca: 
#include <LiquidCrystal_I2C.h> 
// Cria o objeto lcd da classe LiquidCrystal_I2C: 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE)

void setup() 
{ 
  lcd.begin(16,2);                           // Inicia o display LCD 
  lcd.setCursor(0,0);                        // Define o cursor na primeira linha do display 
  lcd.print("Cap Sistema");                  // Imprime Cap Sistema no LCD 
  lcd.setCursor(0,1);                        // Define o Cursor na segunda linha do Display 
  lcd.print("I2C 2 ARDUINO");                // Imprime I2C ARDUINO em LCD 
  delay(5000);                               // Atrase por 5 segundos 
  lcd.clear();                               // Limpa o display LCD
  Serial.begin(9600);                        //Begins Serial Communication at 9600 baud rate
  Wire.begin();                              // Começa a comunicação I2C no pino (A4, A5)
}

void loop()
{
    Wire.requestFrom(8,1);                           // solicitar 1 byte do arduino escravo (8)
    byte MasterReceive = Wire.read();                // recebe um byte do arduino escravo e armazena em MasterReceive
    int potvalue = analogRead(A0);                   // Lê o valor analógico do POT (0-5V)
    byte MasterSend = map(potvalue,0,1023,0,127);    // Converte o valor digital (0 a 1023) para (0 a 127)
     
    Wire.beginTransmission(8);                       // inicia a transmissão para o arduino escravo (8)
    Wire.write(MasterSend);                          // envia um valor POT convertido de um byte para o escravo
    Wire.endTransmission();                          // pare de transmitir
    lcd.setCursor(0,0);                              // Defina o cursor na linha um do LCD
    lcd.print(">>  Master  <<");                     // Imprime >> Master << no LCD
    lcd.setCursor(0,1);                              // Define o cursor na linha dois do LCD
    lcd.print("SlaveVal:");                          // Imprime SlaveVal: no LCD
    lcd.print(MasterReceive);                        // Imprime MasterReceive em LCD recebido de Slave
    Serial.println("Mestre recebido de escravo");    // Impressões no Monitor Serial 
    Serial.println(MasterReceive);
    delay(500);                                     
    lcd.clear();
  
}

Programação Slave Arduino

//I2C SLAVE CODE
//Comunicação I2C entre dois Arduino

#include<Wire.h> 
// Incluir a biblioteca: 
#include <LiquidCrystal_I2C.h> 
// Cria o objeto lcd da classe LiquidCrystal_I2C: 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

byte SlaveReceived = 0;

void setup() 

{
  lcd.begin(16,2);                        //Inicializar display LCD
  lcd.setCursor(0,0);                     //Define o cursor na primeira linha do display 
  lcd.print("Cap Sistema");               //Imprime Cap Sistema em LCD
  lcd.setCursor(0,1);                     //Define o cursor na segunda linha do display
  lcd.print("I2C 2 ARDUINO");             //Imprime I2C ARDUINO em LCD
  delay(5000);                            //Delay for 5 seconds
  lcd.clear();                            //Limpa o display LCD
  Serial.begin(9600);                     //Começa a comunicação serial na taxa de transmissão de 9600
  Wire.begin(8);                          //Começa a comunicação I2C com Slave Address como 8 no pino (A4, A5)
  Wire.onReceive(receiveEvent);           //Chamada de função quando o Slave recebe valor do mestre
  Wire.onRequest(requestEvent);           //Chamada de função quando o Mestre solicita valor do Escravo
} 

void loop(void) 
{
  lcd.setCursor(0,0);                              //Chamada de função quando o Mestre solicita valor do Escravo...
  lcd.print(">>  Slave  <<");                      //Imprime >> Slave << no LCD
  lcd.setCursor(0,1);                              //Define o cursor na linha dois do LCD
  lcd.print("MasterVal:");                         //Imprime MasterVal: em LCD
  lcd.print(SlaveReceived);                        //Imprime o valor SlaveReceived no LCD recebido do Master
  Serial.println("Escravo recebido do mestre:");   //Impressões no Monitor Serial
  Serial.println(SlaveReceived); 
  delay(500);
  lcd.clear();
  
}

void receiveEvent (int howMany)                    // Esta função é chamada quando o Slave recebe valor do mestre
{
   SlaveReceived = Wire.read();                    // Usado para ler o valor recebido do mestre e armazenar na variável SlaveReceived
}

void requestEvent()                                // Esta função é chamada quando o Mestre deseja valor do escravo
{
  int potvalue = analogRead(A0);                   // Lê o valor analógico do POT (0-5V)
  byte SlaveSend = map(potvalue,0,1023,0,127);     // Converte o valor digital potvalue (0 a 1023) para (0 a 127)
  Wire.write(SlaveSend);                           // envia um valor POT convertido de um byte para o mestre
}

 

 

Conclusão

Neste artigo, mostrei como usar a transferencia entre 2 Arduinos Uno usando protocolo I2C. Espero que você tenha achado útil e informativo. Se sim, compartilhe com um amigo que também gosta de eletrônica e de fazer coisas!

Eu adoraria saber quais projetos você planeja construir (ou já construiu) com essa expliação. Se você tiver alguma dúvida, sugestão ou se achar que falta algo neste tutorial, por favor, deixe um comentário abaixo.