Comunicación i2c con arduino


Para la comunicación entre dispositivos existen diferentes posibilidades como puerto serie, spi, i2c, y muchas más, pero estas tres formas las lleva implementadas arduino. Lo más normal es utilizar el puerto serie ya que es el que todos dominamos pero por vaguería acabamos por no implementar control de errores, y puesto que el stack de i2c lo controla y está disponible, no hay razón para no usarlo.

I2c es un protocolo diseñado por phillips la cual, y se puede usar sin ningún tipo de problema legal puesto que la patente ya ha expirado. Este protocolo se ejecuta sobre tres cables, un cable base (GND), un cable con señal de reloj (SCL) y un tercero con datos (SDA). Todos ellos se conectan en paralelo.
En este protocolo está contemplado la existencia de un dipositivo master, que inicia la comunicación, y dispositovos esclavos. Es posible que exista más de un master, pero no al mismo tiempo, es necesario detectar si el canal está libre para poder hacer cambio de master. En arduino no he necesitado hacerlo ya que con un solo master se cumplen mis necesidades, pero es importante tener en cuenta que solo el master inicia comunicación, tratandose el slave como un objeto al que se instancia, pero este nunca inicia un flujo.

He realizado dos tipos de pruebas; arduino como master, y dispositivo como slave, y en otro escenario, dos arduinos, uno como master y otro como slave. Para realizar estos dos sketches, necesitamos la librería WIRE, que lleva programado todo el protocolo i2c.

En el escenario 1 he utilizado como master un arduino mega, y como slave un dispositivo acelerometro ADXL345 (Aquí teneis su datasheet) En este documento se puede ver que el dispositivo tiene asignado por defecto el numero 0x53 como identificador con el cual debemos dirigirnos a él.

En el caso del arduino mega los pines SDA y SCL están ubicados en los pines 20 y 21 de la placa, son lo que hay que cablearlos hasta los pines SDA Y SDL del acelerómetro, y alimentar con 3.3v así como conectar GND.

Para evitar cableados erroneos, y puesto que tenía una placa de prototipado, soldé lo que interesaba en ella; una radio RF, un GPS y el aceletrómetro.

Una vez cableado esto, podremos cargar este código en arduino para comprobar el acelerómetro:


Para la comunicación entre dispositivos existen diferentes posibilidades como puerto serie, spi, i2c, y muchas más, pero estas tres formas las lleva implementadas arduino. Lo más normal es utilizar el puerto serie ya que es el que todos dominamos pero por vaguería acabamos por no implementar control de errores, y puesto que el stack de i2c lo controla y está disponible, no hay razón para no usarlo.

I2c es un protocolo diseñado por phillips la cual, y se puede usar sin ningún tipo de problema legal puesto que la patente ya ha expirado. Este protocolo se ejecuta sobre tres cables, un cable base (GND), un cable con señal de reloj (SCL) y un tercero con datos (SDA). Todos ellos se conectan en paralelo.
En este protocolo está contemplado la existencia de un dipositivo master, que inicia la comunicación, y dispositovos esclavos. Es posible que exista más de un master, pero no al mismo tiempo, es necesario detectar si el canal está libre para poder hacer cambio de master. En arduino no he necesitado hacerlo ya que con un solo master se cumplen mis necesidades, pero es importante tener en cuenta que solo el master inicia comunicación, tratandose el slave como un objeto al que se instancia, pero este nunca inicia un flujo.

He realizado dos tipos de pruebas; arduino como master, y dispositivo como slave, y en otro escenario, dos arduinos, uno como master y otro como slave. Para realizar estos dos sketches, necesitamos la librería WIRE, que lleva programado todo el protocolo i2c.

En el escenario 1 he utilizado como master un arduino mega, y como slave un dispositivo acelerometro ADXL345 (Aquí teneis su datasheet) En este documento se puede ver que el dispositivo tiene asignado por defecto el numero 0x53 como identificador con el cual debemos dirigirnos a él.

En el caso del arduino mega los pines SDA y SCL están ubicados en los pines 20 y 21 de la placa, son lo que hay que cablearlos hasta los pines SDA Y SDL del acelerómetro, y alimentar con 3.3v así como conectar GND.

Para evitar cableados erroneos, y puesto que tenía una placa de prototipado, soldé lo que interesaba en ella; una radio RF, un GPS y el aceletrómetro.

Una vez cableado esto, podremos cargar este código en arduino para comprobar el acelerómetro:



#include <Wire.h>

#define DEVICE (0x53) // Device address as specified in data sheet

byte _buff[6];

char POWER_CTL = 0x2D;	//Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32;	//X-Axis Data 0
char DATAX1 = 0x33;	//X-Axis Data 1
char DATAY0 = 0x34;	//Y-Axis Data 0
char DATAY1 = 0x35;	//Y-Axis Data 1
char DATAZ0 = 0x36;	//Z-Axis Data 0
char DATAZ1 = 0x37;	//Z-Axis Data 1

float xmax = 0, ymax = 0, zmax = 0;
long start = millis();
long iteraciones = 0;

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  Serial.begin(115200); // start serial for output. Make sure you set your Serial Monitor to the same!
  Serial.print("init");
  
  //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
  writeTo(DATA_FORMAT, 0x01);
  //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
  writeTo(POWER_CTL, 0x08);
}

void loop()
{
  readAccel(); // read the x/y/z tilt
  if(start+1000 > millis()){
    iteraciones++;
  }
  
  Serial.print(millis()/1000); // only read every 0,5 seconds
}

void readAccel() {

  
  uint8_t howManyBytesToRead = 6;
  readFrom( DATAX0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345

  // each axis reading comes in 10 bit resolution, ie 2 bytes. Least Significat Byte first!!
  // thus we are converting both bytes in to one int
  float x = (((int)_buff[1]) << 8) | _buff[0];
  if(x>xmax){xmax=x;}
  float y = (((int)_buff[3]) << 8) | _buff[2];
  if(y>ymax){ymax=y;}  
  float z = (((int)_buff[5]) << 8) | _buff[4];
  if(z>zmax){zmax=z;}  
  Serial.print("x: ");
  Serial.print( x/100 );
  Serial.print("G y: ");
  Serial.print( y/100 );
  Serial.print("G z: ");
  Serial.print( z/100 );
  Serial.print("G ");
  Serial.print("xmax: ");
  Serial.print( xmax/100 );
  Serial.print("G ymax: ");
  Serial.print( ymax/100 );
  Serial.print("G zmax: ");
  Serial.print( zmax/100 );
  Serial.println("G");  
}

void writeTo(byte address, byte val) {
  Wire.beginTransmission(DEVICE); // start transmission to device
  Wire.write(address); // send register address
  Wire.write(val); // send value to write
  Wire.endTransmission(); // end transmission
}

// Reads num bytes starting from address register on device in to _buff array
void readFrom(byte address, int num, byte _buff[]) {
  Wire.beginTransmission(DEVICE); // start transmission to device
  Wire.write(address); // sends address to read from
  Wire.endTransmission(); // end transmission

  Wire.beginTransmission(DEVICE); // start transmission to device
  Wire.requestFrom(DEVICE, num); // request 6 bytes from device

  int i = 0;
  while(Wire.available()) // device may send less than requested (abnormal)
  {
    _buff[i] = Wire.read(); // receive a byte
    i++;
  }
  Wire.endTransmission(); // end transmission
}

Más adelante explicaré como se puede manejar un arduino desde otro con i2c solicitando información o acciones.




#include <Wire.h>

#define DEVICE (0x53) // Device address as specified in data sheet

byte _buff[6];

char POWER_CTL = 0x2D;	//Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32;	//X-Axis Data 0
char DATAX1 = 0x33;	//X-Axis Data 1
char DATAY0 = 0x34;	//Y-Axis Data 0
char DATAY1 = 0x35;	//Y-Axis Data 1
char DATAZ0 = 0x36;	//Z-Axis Data 0
char DATAZ1 = 0x37;	//Z-Axis Data 1

float xmax = 0, ymax = 0, zmax = 0;
long start = millis();
long iteraciones = 0;

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  Serial.begin(115200); // start serial for output. Make sure you set your Serial Monitor to the same!
  Serial.print("init");
  
  //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
  writeTo(DATA_FORMAT, 0x01);
  //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
  writeTo(POWER_CTL, 0x08);
}

void loop()
{
  readAccel(); // read the x/y/z tilt
  if(start+1000 > millis()){
    iteraciones++;
  }
  
  Serial.print(millis()/1000); // only read every 0,5 seconds
}

void readAccel() {

  
  uint8_t howManyBytesToRead = 6;
  readFrom( DATAX0, howManyBytesToRead, _buff); //read the acceleration data from the ADXL345

  // each axis reading comes in 10 bit resolution, ie 2 bytes. Least Significat Byte first!!
  // thus we are converting both bytes in to one int
  float x = (((int)_buff[1]) << 8) | _buff[0];
  if(x>xmax){xmax=x;}
  float y = (((int)_buff[3]) << 8) | _buff[2];
  if(y>ymax){ymax=y;}  
  float z = (((int)_buff[5]) << 8) | _buff[4];
  if(z>zmax){zmax=z;}  
  Serial.print("x: ");
  Serial.print( x/100 );
  Serial.print("G y: ");
  Serial.print( y/100 );
  Serial.print("G z: ");
  Serial.print( z/100 );
  Serial.print("G ");
  Serial.print("xmax: ");
  Serial.print( xmax/100 );
  Serial.print("G ymax: ");
  Serial.print( ymax/100 );
  Serial.print("G zmax: ");
  Serial.print( zmax/100 );
  Serial.println("G");  
}

void writeTo(byte address, byte val) {
  Wire.beginTransmission(DEVICE); // start transmission to device
  Wire.write(address); // send register address
  Wire.write(val); // send value to write
  Wire.endTransmission(); // end transmission
}

// Reads num bytes starting from address register on device in to _buff array
void readFrom(byte address, int num, byte _buff[]) {
  Wire.beginTransmission(DEVICE); // start transmission to device
  Wire.write(address); // sends address to read from
  Wire.endTransmission(); // end transmission

  Wire.beginTransmission(DEVICE); // start transmission to device
  Wire.requestFrom(DEVICE, num); // request 6 bytes from device

  int i = 0;
  while(Wire.available()) // device may send less than requested (abnormal)
  {
    _buff[i] = Wire.read(); // receive a byte
    i++;
  }
  Wire.endTransmission(); // end transmission
}

Más adelante explicaré como se puede manejar un arduino desde otro con i2c solicitando información o acciones.


3 comentarios de “Comunicación i2c con arduino”

  1. Buen dia. Tengo un problema para hacer andar un lcd llamado YwRobot LCM1602 iic. Me parce que es problema de las librerias. Estoy trabajando con arduino loenardo.
    Si puedes por favor ayudame. Gracias

  2. Hola Alvaro, estoy arrancando un proyecto de un quadcopter y dentro de un IMu que compre en DealExtreme tengo el ADLX345, y he querido empezar tratando de entender i2c programando solo este sensor, cargue el programa pero en el monitor serie, al moverlo, no obtengo respuesta, solo valores estables, y no encuentro el problema, espero si podes dar una mano seria de gran utilidad, saludos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *