Ir al contenido

I2C Bus en el PLC Arduino para automatización industrial

SDA, SCL, pull-ups y la librería Wire en M-Duino y Ardbox
11 de febrero de 2020 por
I2C Bus en el PLC Arduino para automatización industrial
Boot & Work Corp. S.L, Support Team

Guía completa: Cómo elegir un controlador industrial

I2C es un bus síncrono de dos hilos: SDA lleva los datos y SCL el reloj, y un maestro habla con esclavos que tienen cada uno una dirección de 7 bits. En un PLC de Industrial Shields basado en Arduino, conectas los dispositivos a los bornes SDA y SCL del PLC (pines 20 y 21 de Arduino en el M-Duino 21+), añades resistencias pull-up y usas la librería Wire: beginTransmission(), write() y endTransmission() para escribir, requestFrom() y read() para leer. I2C está pensado para distancias cortas, dentro del armario.

Todos los controladores de Industrial Shields basados en Arduino, M-Duino y Ardbox, tienen un puerto I2C, igual que el ESP32 PLC y el Raspberry PLC. Muchos dispositivos tienen conexión I2C: convertidores analógico a digital, relojes de tiempo real, memorias EEPROM, pantallas, acelerómetros y brújulas. Esta guía usa un M-Duino 21+. El código de Wire es el mismo en los demás modelos basados en Arduino: solo cambian los bornes SDA y SCL.

¿Cómo funciona el protocolo I2C?

Philips creó I2C (Inter-Integrated Circuit) en 1982 para conectar chips dentro de sus productos. Hoy la especificación la publica NXP (UM10204). El maestro genera el reloj e inicia cada transferencia. Los esclavos nunca inician una transferencia y no hablan entre ellos.

  1. Inicio. El maestro pone SDA a nivel bajo mientras SCL está a nivel alto.
  2. Dirección y sentido. 7 bits de dirección y 1 bit de lectura o escritura.
  3. ACK. El esclavo con esa dirección pone SDA a nivel bajo durante un ciclo de reloj para decir "estoy aquí".
  4. Datos. Uno o más bytes, cada uno seguido de un ACK. Cuando el maestro lee, responde al último byte con NACK.
  5. Parada. El maestro libera SDA mientras SCL está a nivel alto. Con un inicio repetido en lugar de una parada, el maestro mantiene el bus e inicia otra transferencia.
Cronograma I2C: reloj SCL y datos SDA con inicio, dirección de 7 bits 0110001 y bit W, ACK, byte de datos 01111010, NACK y parada
Una transferencia: inicio, dirección 0110001 con el bit W, ACK, byte de datos 01111010, NACK y parada.
ModoVelocidadWire.setClock() en el AVR
Modo estándar100 kbit/s100000
Modo rápido400 kbit/s400000
Modo rápido plus1 Mbit/sNo en todos los procesadores
Modo de alta velocidad3,4 Mbit/sNo en todos los procesadores

La dirección de 7 bits da 128 combinaciones. La especificación reserva de 0x00 a 0x07 y de 0x78 a 0x7F, así que los dispositivos usan de 0x08 a 0x77. Cada dispositivo del bus necesita su propia dirección. Muchos dispositivos permiten cambiar los últimos bits con pines de dirección.

Ventajas y límites de I2C

Iconos de pulgar arriba y pulgar abajo para las ventajas y los límites de I2C
Ventajas y límites del bus I2C.
VentajasLímites
Dos hilos (más la masa) para muchos dispositivosDistancia corta: la capacidad del bus limita la longitud del cable
Cada byte se reconoce (ACK), así el maestro sabe si un dispositivo respondeSin CRC: el ACK dice que ha llegado un byte, no que su contenido sea correcto
Direccionamiento en el bus, sin una línea de selección por dispositivoSemidúplex y velocidad media
Compatible con la mayoría de microcontroladores y sensoresUn dispositivo que mantiene SDA a nivel bajo puede bloquear todo el bus

Cableado y resistencias pull-up

Conecta SDA con SDA y SCL con SCL en todos los dispositivos, y conecta las masas. SDA y SCL son líneas de drenador abierto: los dispositivos solo las llevan a nivel bajo, y una resistencia pull-up a Vcc las devuelve a nivel alto.

Bus I2C: un maestro y varios esclavos comparten las líneas SDA y SCL, con dos resistencias pull-up a Vcc
Un maestro y varios esclavos en las mismas líneas SDA y SCL, con las resistencias pull-up a Vcc.
  • Pull-ups. Wire.begin() activa las pull-up internas del microcontrolador, pero son débiles: los flancos suben despacio y el bus solo funciona a baja velocidad y con cables cortos. Para un bus fiable, añade pull-ups externas de 1 kΩ a 4,7 kΩ. Industrial Shields tiene un módulo de resistencias pull-up I2C para carril DIN.
  • Nivel lógico. Los microcontroladores del M-Duino y del Ardbox trabajan a 5 V. Un dispositivo de 3,3 V necesita un adaptador de nivel.
  • Distancia. Mantén el cableado I2C corto y dentro del armario. Para distancias mayores usa un extensor de bus I2C, o un bus de campo como RS-485 con Modbus.
  • Direcciones ocupadas. En los modelos con expansores de E/S en el bus I2C, como el Raspberry PLC, algunas direcciones ya están ocupadas. La hoja de datos del Raspberry PLC las indica.

¿Dónde están SDA y SCL en el PLC?

Cada PLC tiene los bornes SDA y SCL en su conector de comunicaciones. Consulta la guía de usuario de tu modelo. En el M-Duino 21+, SCL es el pin 21 de Arduino y SDA es el pin 20. En el Ardbox HF+, SDA es el pin 2 y SCL es el pin 3.

Tabla de pines de la zona A del M-Duino 21+ con SCL en el pin 21 de Arduino y SDA en el pin 20 marcados
Zona A del M-Duino 21+: SCL en el pin 21 de Arduino y SDA en el pin 20.

La librería Wire

El IDE de Arduino incluye la librería Wire (Wire.h), que controla el hardware I2C del microcontrolador. Con las placas de Industrial Shields instaladas, encontrarás sus ejemplos en File > Examples > Wire.

Menú File, Examples, Wire del IDE de Arduino con los ejemplos master_reader, master_writer, slave_receiver y slave_sender para la familia M-Duino
Los ejemplos de Wire en el IDE de Arduino con la familia M-Duino seleccionada.
FunciónQué hace
Wire.begin()Inicia I2C como maestro. Wire.begin(address) lo inicia como esclavo con esa dirección
Wire.setClock(100000)Fija el reloj: 100000 (estándar) o 400000 (rápido)
Wire.beginTransmission(address)Prepara una escritura al esclavo con esa dirección de 7 bits
Wire.write(data)Pone en cola uno o más bytes. Devuelve el número de bytes en cola
Wire.endTransmission(stop)Envía los bytes en cola. Con false termina con un inicio repetido y mantiene el bus. Devuelve 0 si ha funcionado, 1 datos demasiado largos, 2 NACK en la dirección, 3 NACK en los datos, 4 otro error, 5 timeout
Wire.requestFrom(address, quantity, stop)Lee un número de bytes de un esclavo. Devuelve los bytes recibidos
Wire.available() y Wire.read()Número de bytes en espera, y el byte siguiente
Wire.onReceive(handler) y Wire.onRequest(handler)En un esclavo: funciones que se llaman cuando el maestro envía datos o los pide
Wire.setWireTimeout(us, reset)Tiempo máximo de una transferencia, para que un bus bloqueado no detenga el programa

Ejemplo completo: explora el bus y lee un registro

Este sketch convierte el PLC en maestro I2C. Al arrancar explora el bus y muestra la dirección de cada dispositivo que responde. Después, cada segundo, lee dos bytes de un registro de un dispositivo: escribe el número de registro, mantiene el bus con un inicio repetido y lee la respuesta. Cambia DEVICE_ADDRESS, FIRST_REGISTER y BYTES_TO_READ con los datos de tu dispositivo.

// I2C master on an Industrial Shields Arduino based PLC (M-Duino, Ardbox)
// 1. Scans the bus and prints the address of every device that answers.
// 2. Reads N bytes from a register of one device (write pointer + repeated start).
#include <Wire.h>

const uint8_t DEVICE_ADDRESS = 0x48;  // 7-bit address of your device (see its datasheet)
const uint8_t FIRST_REGISTER = 0x00;  // register to read
const uint8_t BYTES_TO_READ  = 2;

void scanBus() {
  uint8_t found = 0;
  for (uint8_t address = 0x08; address <= 0x77; address++) {  // 0x00-0x07 and 0x78-0x7F are reserved
    Wire.beginTransmission(address);
    if (Wire.endTransmission() == 0) {  // 0 = the device answered with ACK
      Serial.print("Device found at 0x");
      Serial.println(address, HEX);
      found++;
    }
  }
  if (found == 0) Serial.println("No I2C device found: check SDA, SCL, GND and pull-ups");
}

bool readRegisters(uint8_t address, uint8_t reg, uint8_t *buffer, uint8_t length) {
  Wire.beginTransmission(address);
  Wire.write(reg);                           // register pointer
  if (Wire.endTransmission(false) != 0) {    // false = repeated start, keep the bus
    return false;                            // NACK, bus error or timeout
  }
  if (Wire.requestFrom(address, length) != length) {
    return false;
  }
  for (uint8_t i = 0; i < length; i++) {
    buffer[i] = Wire.read();
  }
  return true;
}

void setup() {
  Serial.begin(115200);
  Wire.begin();                    // this PLC is the master
  Wire.setClock(100000);           // standard mode, 100 kHz
  Wire.setWireTimeout(25000, true); // 25 ms: do not hang if a device blocks the bus
  scanBus();
}

void loop() {
  uint8_t data[BYTES_TO_READ];
  if (readRegisters(DEVICE_ADDRESS, FIRST_REGISTER, data, BYTES_TO_READ)) {
    uint16_t value = ((uint16_t)data[0] << 8) | data[1];  // most devices send the high byte first
    Serial.print("Register value: ");
    Serial.println(value);
  } else {
    Serial.println("Read failed");
    if (Wire.getWireTimeoutFlag()) {
      Wire.clearWireTimeoutFlag();
      Serial.println("I2C timeout: the bus was reset");
    }
  }
  delay(1000);
}
  • La exploración usa una escritura vacía: endTransmission() devuelve 0 cuando un dispositivo reconoce su dirección.
  • endTransmission(false) deja el bus con un inicio repetido, como esperan la mayoría de dispositivos con registros entre la escritura del registro y la lectura.
  • setWireTimeout(25000, true) detiene una transferencia después de 25 ms y reinicia el hardware I2C, así un dispositivo bloqueado no congela el programa del PLC.
  • La mayoría de dispositivos envían primero el byte alto. Comprueba el orden de los bytes y el significado del registro en la hoja de datos de tu dispositivo.

Qué hemos probado. El sketch compila sin errores para el M-Duino 21+ con las placas AVR de Industrial Shields (9.478 bytes de memoria de programa, 3,6 %). No lo hemos ejecutado con un dispositivo I2C para esta actualización.

Problemas habituales con I2C

Qué vesCausa habitualQué revisar
La exploración no encuentra ningún dispositivoCableado, alimentación o pull-upsSDA y SCL sin cruzar, masa común, dispositivo alimentado, pull-ups externas, nivel lógico
endTransmission() devuelve 2Ningún dispositivo responde en esa direcciónLa dirección de la hoja de datos es de 7 bits. Algunas hojas la dan ya desplazada con el bit R/W (8 bits)
Las lecturas fallan de vez en cuandoCable largo, pull-ups débiles o ruido eléctricoCableado más corto, pull-ups más fuertes, 100 kHz en lugar de 400 kHz
El programa se detieneUn dispositivo bloquea el busWire.setWireTimeout() y la alimentación del dispositivo
Dos dispositivos con la misma direcciónEl mismo modelo en el mismo busLos pines de dirección del dispositivo, o un multiplexor I2C

Un dispositivo I2C en un proyecto con PLC. La PLC Solution Library tiene un ejemplo completo que lee un convertidor analógico a digital ADS1015 por I2C, con entradas diferenciales y ganancia automática.

Mira el ejemplo del ADS1015Elige un controlador industrial

Preguntas frecuentes sobre I2C en un PLC basado en Arduino

¿Cuántos dispositivos puedo conectar al bus I2C?

Hasta 112 direcciones con direccionamiento de 7 bits (de 0x08 a 0x77). En la práctica, el límite es la capacidad del bus: muchos dispositivos y cables largos hacen más lentos los flancos.

¿Qué longitud puede tener el cable I2C?

I2C está pensado para conexiones en una placa o dentro de un armario. La longitud depende de la velocidad, las pull-ups y la capacidad del cable. Para conexiones entre armarios, usa un extensor de bus o un bus de campo como RS-485.

¿El PLC puede trabajar como esclavo I2C?

Sí. Inícialo con Wire.begin(address) y registra Wire.onReceive() y Wire.onRequest().

¿Qué diferencia hay entre I2C y SPI?

I2C usa dos líneas compartidas y direcciones. SPI usa cuatro líneas, una selección de chip por dispositivo, y es más rápido y dúplex completo. Los dos son para distancias cortas.

¿Qué valor de pull-up debo usar?

4,7 kΩ es un valor habitual a 100 kHz. Valores más bajos, hasta 1 kΩ aproximadamente, dan flancos más rápidos para cableados más largos o 400 kHz, siempre que todos los dispositivos puedan absorber la corriente.

Buscar en nuestro blog

I2C Bus en el PLC Arduino para automatización industrial
Boot & Work Corp. S.L, Support Team 11 de febrero de 2020
Compartir esta publicación

¿Estás buscando tu Controlador Lógico Programable ideal?

Echa un vistazo a esta comparativa de producto de varios controladores industriales basados en Arduino.

Comparamos entradas, salidas, comunicaciones y otras especificaciones con las de los equipos de otras marcas destacadas.

Comparativa de PLCs