Usando algunos componentes que funcionan mediante el bus I2C puede ocurrir que las cosas no funcionen a la primera… Por qué ?

Si nos remontamos al funcionamiento del I2C, éste funciona mediante la comunicación de diferentes componentes mediante dos cables SDA y SCL de comunicación bidireccional a distintos nodos.

  • SDA –> Serial Data
  • SCL –> Serial Clock

Para hacerse una idea, el modo de diferenciar que un componente se comunica con otro, es mediante la especificación de una dirección de 1 byte (8 bits), a esto lo llamamos I2C Address.

Este direccionamiento contiene una trama de 7 bits; el octavo bit se define como R/W bit para definir que el maestro envia información al esclavo o viceversa; por lo que se disponen de 127 direcciones disponibles para poder llegar a conectar 127 nodos en este bus.

slave-address-fig1

Dentro de Arduino hay habilitados dos pines para la comunicación de este bus. SDA en el A4 y SCL en el A5. Librería Wire oficial de Arduino.

arduino-uno-i2c-pinPero ocurre que algunos componentes que se compran vienen con una dirección definida que a priori desconocemos. Este es el ejemplo de comprar una pantalla LCD, y cargar los típicos programas de ejemplo que no funcionan… Esto ocurre porque no sabemos qué dirección hemos de especificar para hacer funcionar el esclavo.

Escanear I2C

Vamos a programar el siguiente sketch I2C_Scanner que nos proporcionará información de cuántos dispositivos tenemos conectados por I2C y cuáles son estas direcciones…

De esta manera podremos programar nuestra placa para especificar a quién debemos de mandar los datos.


#include <Wire.h>
 
 
void setup()
{
  Wire.begin();
 
  Serial.begin(9600);
  while (!Serial);             // Leonardo: wait for serial monitor
  Serial.println("\nI2C Scanner");
}
 
 
void loop()
{
  byte error, address;
  int nDevices;
 
  Serial.println("Scanning...");
 
  nDevices = 0;
  for(address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
 
    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");
 
      nDevices++;
    }
    else if (error==4)
    {
      Serial.print("Unknow error at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
 
  delay(5000);           // wait 5 seconds for next scan
}

En este ejemplo conectaré varios componentes. Una pantalla LCD y un MPU9250 9 Axis (giroscopio- acelerometro-brújula).

lcdmpuscanner

El primero 0x3F se refiere a la pantalla LCD y el segundo 0x68 es la dirección del MPU9250. Así que en el momento de ejecutar una librería para hacer funcionar estos componentes, deberé de especificar esas direcciones.

Para el caso de la pantalla LCD usaré la siguiente librería LyquidCrystal_I2C.


#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3f,16,2);  // set the LCD address for a 16 chars and 2 line display

void setup()
{
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  Serial.begin(9600);
}

void loop(){

}

Y de esta manera en el objeto especificaríamos la dirección del dispositivo.

Para el IMU MPU9250 no hace falta ser tan específico, ya que dentro de las librerías ya se define la dirección 68 para poner en funcionamiento este componente en concreto.

 

Otros recursos I2C

Desde las siguientes páginas se puede encontrar más información acerca del funcionamiento del I2C.