Font Size

Olá, tudo bem? Hoje iniciaremos o nosso estudo sobre os protocolos de comunicação. Dessa forma, hoje vamos estar conhecendo o protocolo I2C com arduino. Isso porque a sua utilização é muito grande em projetos devido a sua facilidade tanto de programação quanto de conexões !

Sendo assim, hoje vamos nos aprofundar um pouco mais e conhecer o funcionamento do protocolo I2C usando o arduino e saber como ele trabalha para poder utilizá-lo em nossos projetos. Vamos nessa então?

Funcionamento do Protocolo I2C com Arduino

Criado pela empresa Phillips por volta dos anos 80, este protocolo foi criado com o propósito de padronizar (em outras palavras, facilitar) a comunicação entre diferentes dispositivos.

Devido a isso, ele necessita apenas de dois pinos (SDA – para dados, SCL – nosso clock) para poder conectar até 112 dispositivos para barramentos de 7 bits. É surreal, mas é possível de acontecer. Contudo, ele foi feito para distâncias curtas, e com uma velocidade média de operação (por volta de 400KHz).

Em virtude dessa sua facilidade de utilização, diversos componentes e módulos foram desenvolvidos voltados para este tipo de interface. Você pode encontrar Display LCD, RTCs entre outros. Tudo isso na loja Autocore, então para encontrá-los, só clicar no botão abaixo!

Funcionamento na vida real

Vamos conhecer o protocolo I2C com Arduino e entender como é na vida real. Bom, basicamente o protocolo I2C funciona com a ideia de hierarquia, onde temos o Mestre e seus Escravos. Assim, nosso arduino normalmente é o nosso mestre e todos os dispositivos e sensores conectados nele, os escravos. Como na imagem abaixo:

Protocolo-I2C
Esquema de funcionamento do Protocolo I²C

Entretanto, uma dúvida que surge é: como a comunicação entre mestre – escravo acontece? Para responder tal pergunta, é necessário conhecer os três tipos de comunicação de dados que existe, são els: Simplex, Half Duplex, Full Duplex. Que raios é isso? Relaxa e observe a imagem abaixo:

Protocolo-I2C
Modos de comunicação entre dispositivos

Basicamente, cada nome define como a comunicação irá ocorrer. Então, no primeiro temos apenas o envio de dados e pronto. No segundo, temos o envio e recebimento dos dois lados, porém, é preciso esperar um enviar para o outro responder. No último, ambos podem receber e enviar dados ao mesmo tempo.

Dessa forma, o protocolo I2C trabalha no modo Half-Duplex. Porém, visando não ocorrer esse conflito de dados, o dispositivo mestre do circuito é quem vai realizar a coordenação de toda a comunicação. Uma vez que ele, tem a capacidade de enviar e solicitar informações aos escravos do barramento.

Conhecendo sobre os endereços do I2C no Arduino

Pois bem, já conhecemos o modo de operação de nosso barramento, mas como saber quantos escravos eu tenho no barramento e como enviar dados para cada um individualmente? É nesse momento que entra os endereços!

O endereço tem como função informar onde tal objeto ou lugar se encontra. Um exemplo disso é o nosso endereço de casa. Tranquilo. Mas quando trabalhamos com o protocolo I2C, esse endereço é dado no formato hexadecimal, por exemplo #03F. 

Mas, assim como o nosso endereço de casa, cada escravo do barramento, tem o seu próprio e único endereço. No entanto, tem muitos casos onde o endereço de dois módulos totalmente diferentes, são iguais. E ai? 

Como resultado disso, precisamos recorrer a uma pesquisa prévia a respeito do sensor para saber se tem como fazer essa mudança de endereço soldando certos pinos dele. Observe a imagem abaixo:

Protocolo-I2C
Módulo I²C para Display LCD

Na imagem acima, temos um módulo I2C para Display LCD. Perceba que temos nele as conexões A0, A1, A2. Caso este módulo tenha o endereço de algum outro sensor do barramento, basta soldar e conectar os dois pinos do A0, ou A1 por exemplo. 

Entretanto, vale ressaltar que não são todos módulos I2C que você conseguirá fazer essa alteração física, por isso é necessário a pesquisa prévia, beleza?

Envio de dados no protocolo I2C

Depois de já conhecido a forma como o mestre reconhece e envia os dados para os seus escravo no barramento, vamos entender como o dado é enviado e recebido dentro do circuito. Para isso, observe a imagem abaixo:

Protocolo-I2C
Configuração de envio e recebimento dos dados no barramento I²C

A transmissão inicia-se com um bit de start (S), enviado pelo mestre. Em seguida, ele envia o endereço físico do escravo (aquele hexadecimal que citei) e no bit LSB, o mestre informa o que ele quer fazer: Uma leitura (HIGH) ou uma escrita (LOW).

Após o escravo ser identificado pelo o endereço enviado no barramento, ele envia um bit de reconhecimento (ACK) deixando o seu pino de SDA LOW. Ou seja ele diz “estou aqui mestre, a seu dispor”.

Assim, o mestre inicia o envio de dados que é feito em duas partes. Primeiro é enviado 4 bits e espera-se o bit de confirmação do escravo para enviar os outros 3 bits de dados. Por fim, se o escravo receber tudo corretamente, ele permanece em LOW e o mestre manda a condição de STOP, caso contrário o pino de SDA muda para HIGH, alegando que não reconheceu o dado enviado.

Essa condição de START e STOP é gerada para que o barramento saiba se está tendo uma comunicação de dados naquele momento. Assim, para isso ocorrer, é preciso que os pinos de SDA e SCL do mestre sigam os estados da imagem abaixo:

Protocolo-I2C
Gráfico de tempo das condições de START e STOP

Pinagem de I2C dos Arduinos

Então, agora que já aprendemos sobre o funcionamento do barramento I2C e como é feita a comunicação dentro dele, chegou a hora de aprender a como conectar o arduino (nosso mestre) aos outros escravos do circuito.

Para isso, temos que saber quais pinos do nosso arduino são SDA e SCL. Assim, é muito importante prestar atenção nesse ponto, pois esses pinos mudam para cada versão de arduino. Combinado?

Por exemplo, para encontrar as pinagem de seu arduino, basta fazer uma pesquisa rápida pelo termo “diagram pinout + modelo do Arduino”. Porém, na tabela abaixo você pode encontrar as pinagens para cada modelo de Arduino:

Modelo SDA SCL
Arduino UNO A4 A5
Arduino MEGA 44 43
Arduino DUE 20 21
Arduino YÚN 19 18
Arduino MICRO 19 18
Arduino NANO 27 28
Arduino PRO MINI A4 A5

Pontos importantes sobre o protocolo I2C

Alguns detalhes importantes que precisamos saber e lembrar na hora de trabalhar com protocolo I2C são:

  • Pode haver atrasos: Isso porque como já vimos, o nosso barramento é do tipo Half Duplex. Dessa forma, quando o mestre enviar os dados (de comprimento de 1 byte) pode acontecer do escravo não conseguir trabalhar os dados recebidos, assim, ele pode forçar o mestre a entrar num estado de espera.
  • Ligação Direta: Além disso, um outro ponto importante é com relação a sua ligação, diferente do protocolo UART (do nosso módulo Bluetooth) onde invertemos os pinos TX e RX. Aqui, não podemos fazer isso pois se trata de barramento, assim, o pino de SDA do mestre, precisa estar no pino SDA do escravo, o meu vale para o pino SCL. Beleza?

Componentes do Projeto

Agora, chegou a hora de montar o nosso circuito de hoje para testar todo o conhecimento adquirido. Para isso, separa os seguintes componentes:

Montagem do Circuito

Depois de separar todos os componentes da lista, realize a ligação de todos eles de acordo com a imagem do esquemático de ligação  abaixo:

Protocolo-I2C
Esquema de Ligação no Arduino

Código do Projeto

Dispositivo Metre

<code>/* ==================================================================================================
Projeto: Treinando a comunicação I2C no Mestre
Autor: Danilo Nogueira
Data: 22/08/2019
Autor: Danilo Nogueira
// ==================================================================================================*/
// --- Declaração Bibliotecas ---
#include &lt;Wire.h&gt;    // Incluindo a biblioteca para realizar a comunicação I2C
bool LED_estado;     // Variável que aramazena o estado no LED que o mestre irá enviar
 
// ==================================================================================================
// --- Definicando o void setup() ---
void setup() {
  Wire.begin();      // Inicia a comunicação I2C
}
// ==================================================================================================
// --- Definicando o void loop() ---
void loop() {
  Wire.beginTransmission(0x08);   // Estamos iniciando a comunicação com o escravo no endereço 0x08
  
  Wire.write(LED_estado);         // Estamos enviando para o escravo o estado do LED que queremos
  Wire.endTransmission();         // Encerrando a comunicação com o escravo
  
  LED_estado = !LED_estado;       // Alteramos o estado do LED para o próximo envio de dados.
 
  delay(800);
}
// ================================================== FIM ===========================================</code>

Dispositivo Escravo

<code>/* ==================================================================================================
Projeto: Treinando a comunicação I2C no Escravo
Autor: Danilo Nogueira
Data: 22/08/2019
Autor: Danilo Nogueira
// ==================================================================================================*/
// --- Declaração Bibliotecas ---
#include &lt;Wire.h&gt;
bool LED_estado;     // Variável que aramazena o estado no LED que o mestre irá enviar
bool estado;
int  pino_LED = 4;
// ==================================================================================================
// --- Definindo void setup() ---
void setup() {
  Wire.begin(0x08);                 // Iniciando a comunicação com o mestre através do endereço 0x08
  Wire.onReceive(Dado_Recebido);    // Quando o escravo receber algo no barramento, ele chama a função 'Dado_Recebido' 
  pinMode(pino_LED,OUTPUT);         // Colocando o LED como saida 
}
// ==================================================================================================
// --- Definindo void setup() ---
void loop() {
    delay(100);
}
 
// ==================================================================================================
// --- Definindo as funções criadas ---
void Dado_Recebido(int leitura) {
  
   estado = Wire.read();    // Lê os dados eviados
 
  if (estado == 1){
    digitalWrite(pino_LED,HIGH);
  }
  
  else{
    digitalWrite(pino_LED,LOW);
  }
}
// ================================================ FIM ==============================================</code>

Explicação do Código

Em primeiro lugar, iremos começar pela definição da nossa biblioteca, a qual tem funções que gerenciam a comunicação entre dispositivos I2C. A biblioteca que usaremos será “Wire.h”, e chamaremos ela nos dois códigos através de:

<code>#include &lt;Wire.h&gt;    // Incluindo a biblioteca para realizar a comunicação I2C</code>

Analisando Código Mestre

No código do nosso arduino mestre, temos a definição da variável booleana que guardará o estado do led e dentro do void setup(), já iniciamos a comunicação I2C através do comando Wire.begin():

<code>void setup() {
  Wire.begin();      // Inicia a comunicação I2C
}</code>

Um detalhe importante que na função Wire.begin(), precisamos colocar o endereço do canal (ou barramento) de comunicação. Contudo, o mestre do circuito não necessita colocar, porém todos os escravos sim!

Logo em seguida, temos que dizer qual escravo iremos nos conectar e enviar os dados. Para isso utilizamos o comando “Wire.beginTransmission(endereço do escravo)”, este comando serve para basicamente especificar, dentro de todo o barramento, qual o escravo o mestre quer se comunicar.

Depois de especificar o escravo, está na hora de enviar os dados. Para isso, utilizamos a função “Wire.write(dados)”. Logo depois de enviar os dados, precisamos encerrar a comunicação para que uma nova seja criada como já falamos, assim, utilizamos a função “Wire.endTransmission()”.

<code>void loop() {
  Wire.beginTransmission(0x08);   // Estamos iniciando a comunicação com o escravo no endereço 0x08
  
  Wire.write(LED_estado);         // Estamos enviando para o escravo o estado do LED que queremos
  Wire.endTransmission();         // Encerrando a comunicação com o escravo
  
  LED_estado = !LED_estado;       // Alteramos o estado do LED para o próximo envio de dados.
 
  delay(800);
}</code>

Analisando Código Escravo

Em nosso dispositivo escravo, começamos com a definição da nossa biblioteca e declaração das variáveis que iremos trabalhar. Uma para o estado do led, outra para analisar o valor enviado pelo mestre (0 e 1) e uma variável para dizer o pino do LED.

<code>bool LED_estado;     // Variável que aramazena o estado no LED que o mestre irá enviar
bool estado;
int  pino_LED = 4;</code>

Entrando no void setup(), temos alguns comandos importantes. Iniciamos com o “Wire.begin(endereço)”, e perceba que aqui já temos o endereço do Arduino.

Então, a ideia é a seguinte, cada escravo tem o número de seu “apartamento” (nosso endereço) que precisamos inicializar para dizer ao mestre que ele pode se comunicar. Assim, quando o mestre for enviar algo, ele irá colocar o endereço apenas no “Wire.beginTransmission” e não no “Wire.begin”. Beleza?

Em seguida temos o “Wire.onReceive(função)”, o qual verifica se algo foi enviado pelo barramento e pega esse dado e joga na função que estiver dentro dele. No nosso caso ele jogou o dado para a função criada chamada “Dado_Recebido”.

<code>void setup() {
  Wire.begin(0x08);                 // Iniciando a comunicação com o mestre através do endereço 0x08
  Wire.onReceive(Dado_Recebido);    // Quando o escravo receber algo no barramento, ele chama a função 'Dado_Recebido' 
  pinMode(pino_LED,OUTPUT);         // Colocando o LED como saida 
}</code>

No void loop(), temos apenas um delay() de segurança, o qual a documentação do Arduino aconselha colocar. Já no final do código, temos a criação da nossa função “Dado_Recebido”, que será chamada sempre que houver alguma informação enviada pelo mestre.

Nesta função utilizamos a sentença Wire.read() para obter o valor relativo ao estado do led (enviado pelo mestre) e armazená-lo na variável estado, de modo que, conforme o conteúdo da mesma, optamos através de uma função if() se devemos acender ou apagar o led.

<code>void Dado_Recebido(int leitura) {
  
   estado = Wire.read();    // Lê os dados eviados
 
  if (estado == 1){
    digitalWrite(pino_LED,HIGH);
  }
  
  else{
    digitalWrite(pino_LED,LOW);
  }
}</code>

Resultado do Projeto

Confira abaixo o resultado da aplicação do nosso protocolo I2C em nosso canal do Youtube!

Mas e ai? Gostou do post? Caso tenha ficado algo incompleto para você, comente abaixo 📝

Dicas? Dúvidas? 🤔 Críticas? 😱 Só comentar abaixo! 👇

Forte abraço e compartilha se gostou! 🤘

5.00 avg. rating (96% score) - 2 votes