I2C – Arduino master lendo informações do Slave.

Ola Pessoal. Vamos aprender como um Arduino atuando como master em um setup I2C consegue ler informações de um Arduino slave? Este é o terceiro post sobre I2C avançado. Para acompanhar este post, recomendo que leiam os dois anteriores:

I2C – Master controlando o slave

I2C – Master enviando informações ao slave

Pois bem, agora que leram os posts anteriores, sabem que começamos com o master controlando qual função o slave executava. Depois incluímos uma nova função no slave, e esta função era dependente de outras informações enviadas pelo master. Em nenhum momento o master requisitava informações do slave. Podemos considerar que o master mandava, mas não queria saber se o serviço tinha sido feito 😀

Agora o master tem que prestar contas, e ele começa a pedir que o slave passe o resultado do trabalho efetuado. Então vamos aprender como é que o master recebe estas informações.

Vamos ao código, e então analisar as alterações necessárias?

I2C – Código do Arduino master

#include <Wire.h>
#define SLAVE01 0x1
byte a;
byte b;

void setup() {
  Serial.begin(9600);
  Wire.begin();
}

void loop() {
  int comando=random(1,6);
  Wire.beginTransmission(SLAVE01);
  Wire.write(comando);
  if (comando == 5){
    a=random(1,256);
    Wire.write(a);
    b=random(1,256);
    Wire.write(b);
  }
  Wire.endTransmission();


  delay(5000);
    if (comando == 5){
    Wire.requestFrom(SLAVE01,2);
    int c= Wire.read() << 8 | Wire.read();
    Serial.print(a);
    Serial.print(" + ");
    Serial.print(b);
    Serial.print(" = ");
    Serial.println(c);
  }
}

Logo no loop eu atribui o valor retornado por random a duas variáveis, a e b. Eu poderia ter mantido a saída do random diretamente no Wire.write, mas como quero exibir estes valores na serial, preferi armazená-los.

Logo depois do delay(5000) vem o código que solicita informações do slave. Verificamos se o valor de comando é 5 (este é o valor que faz com que o slave execute a função soma). Caso seja, solicitamos que o slave nos passe informações utilizando a função Wire.requestFrom(). Esta função precisa de 2 parâmetros, sendo primeiro o endereço I2C do slave, e segundo o número de bytes que estamos requisitando.

Na sequência lemos estes valores atribuindo o resultado a variável c. A linha é um pouco estranha, vou resumi-la sem entrar em detalhes de operações com bit. O primeiro byte que recebemos é o byte “alto”, ou o mais a esquerda. Lemos este valor e “empurramos” os bits 8 casas à esquerda. Lembrem-se que um tipo int é formado por 16 bits, enquanto o tipo byte é formado por 8 bits. Se definimos que o primeiro byte é o mais a esquerda, então << 8 posiciona os 8 bits do tipo int na posição correta. Logo após esta operação de bit shifiting, entramos com outra operação sobre bits, o bitwise OR (“|”), utilizando o byte “baixo”, no caso o valor que lemos do próximo Wire.read().

Após toda essa lógica booleana, imprimimos na serial os valores enviados para o slave, e o valor calculado pelo slave. Podem conferir que o slave trabalhou certinho 😀

Falando nele, vamos ver o código do slave.

I2C – Código do Arduino slave

#include <Wire.h>
#define i2c_address 0x1
char comando = 1;
byte a = 0;
byte b = 0;
int c;

void setup(){
  Serial.begin(9600);
  Wire.begin(i2c_address);
  Wire.onReceive(receivEvent);
  Wire.onRequest(respondEvent);
}

void loop(){
  switch (comando){
    case 1:
    comportamento001();
    break;
    case 2:
    comportamento002();
    break;
    case 3:
    comportamento003();
    break;
    case 4:
    comportamento004();
    break;
    case 5:
    soma();
    break;
    default:
    comando=1;
    break;
  }
  delay(1000);
}

void receivEvent(int x){
  comando=Wire.read();
  if (x == 3) {
    a=Wire.read();
    b=Wire.read();
  }
}
void respondEvent(){
  uint8_t buffer[2];
  buffer[0] = c >> 8;
  buffer[1] = c & 0xff;
  Wire.write(buffer, 2); 
}

void comportamento001(){
  int i = 0;
  for (i=0;i<1;i++){
    tone(13, 800, 50); // play the tone
    delay(100);
      }
}

void comportamento002(){
  int i = 0;
  for (i=0;i<2;i++){
    tone(13, 800, 50); // play the tone
    delay(100);
      }
}

void comportamento003(){
  int i = 0;
  for (i=0;i<3;i++){
    tone(13, 800, 50); // play the tone
    delay(100);
      }
}

void comportamento004(){
  int i = 0;
  for (i=0;i<4;i++){
    tone(13, 800, 50); // play the tone
    delay(100);
      }
}

void soma(){
  c = a + b;

Tá ficando grande o código hein?

Começamos inserindo a declaração da variável c, do tipo int. Antes ela era declarada apenas na função soma, mas como vamos utilizá-la fora da função soma, precisamos que ela tenha escopo global.

Em setup, declaramos um novo manipulador de eventos(event handler). Se lembram que eu disse que o slave usa eventos para tratar as operações de escrita e leitura? Pois temos, já tínhamos a declaração do evento .onReceive para tratar operações de escrita, e agora declaramos o evento .onRequest para tratar operações de leitura. A declaração do evento pede um argumento, que é justamente a função que irá manipular o evento, no caso demos o nome de respondEvent.

No loop não alteramos absolutamente nada.

Também não alteramos nada na função receivEvent, assim como nas funções comportamento00X

Já na função soma() fizemos algumas alterações. Eliminamos qualquer código que não seja a soma em si dos valores recebidos do master. Antes nós imprimíamos os valores recebidos do barramento I2C, e na sequência o resultado da soma. Como o master quer mostrar o trabalho(imprimir na serial), o slave apenas soma os valores. Reparem também que a variável c antes era declarada dentro da função, tornando-a local. Agora nós declaramos no início, alterando seu escopo para global

Tratando o evento .onRequest.

Por fim, criamos a função respondEvent, que efetivamente trata o evento .onRequest, respondendo ao master. Eu criei ela embaixo da função receivEvent apenas para “agrupar” funções semelhantes. Após a função soma ter somado os valores recebidos do master e atribuir o resultado a variável c, nós dividimos a variável c em dois bytes (se lembre que c é do tipo int, e Wire.write envia bytes). Aqui temos uma diferença do master na forma como enviamos mais de um byte.

No master podemos enviar mais de um byte de duas formas: enviando os bytes um a um, chamando Wire.write para cada byte(essa é a forma que estamos utilizando até agora), ou então podemos criar um array com vários bytes e chamar Wire.write uma única vez.

Já no slave podemos fazer uma única chamada a Wire.write, não importa se enviando apenas um byte, ou uma série de bytes, desde que seja em um array.

O motivo para isso é simples. Notem que no master nós chamamos Wire.write sempre após um Wire.beginTransmission, e antes do Wire.endTransmission. Pois bem, os dados são efetivamente transmitidos com o Wire.endTransmission, antes disso cada Wire.write armazena os dados em um buffer com 32 bytes de tamanho. Já no slave, a informação é enviada imediamente com um Wire.write.

Resumo

Com esse post cobrimos as duas principais operações do protocolo I2C, escrita e leitura, utilizando Arduino. Foram exemplos bem simples de uso, sem utilidade prática eu diria, porem o objetivo é que entendam a ideia.

Não sei se notaram que a chamada a Wire.requestFrom foi posicionada após o delay. Aqui temos um ponto muito importante quando trabalhamos com interrupções e o timing do sketch. Inicialmente eu coloquei ela após antes do delay, e o comportamento foi não esperado. O slave respondia sempre com atraso, ou seja, ele sempre me devolvia o resultado da operação anterior. Isso acontecia porque quando o master solicitava informações do slave, disparava o evento .onRequest. Já sabemos que os eventos são tratados por interrupção, paralisando todo o sketch, e a interrupção “interrompia” justamente a operação de soma com os valores atuais.

Bem, é isso por enquanto. Sei que esta achando simples demais, mas no próximo post vamos ver um setup multimaster, onde a compreensão do que vimos nestes 3 últimos posts será fundamental.

 

Abs a todos.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

dezoito − catorze =