Introducción
Las comunicaciones I2C se han convertido en el método de facto de comunicación entre microcontroladores, microcomputadores y una variedad de circuitos integrados y sensores. Ha existido desde 1982 y fue desarrollado originalmente para su uso en receptores de televisión.
Aunque hemos utilizado muchos sensores y pantallas I2C en artículos anteriores, en realidad no hemos analizado cómo funciona I2C y cómo se puede usar para comunicarse entre microcontroladores.
Hoy corregiremos eso y aprenderemos más sobre I2C. También veremos cómo se puede usar para intercambiar información entre dos Arduinos y cómo se puede usar para permitir que un Arduino controle otro.
Este será el primero de cuatro artículos sobre I2C. En futuros artículos veremos cómo podemos construir nuestros propios dispositivos I2C, cómo interconectar un Raspberry Pi y un Arduino usando I2C y cómo hacer algunas configuraciones avanzadas de I2C, incluido el uso de varios maestros en un bus I2C.
¡Comencemos!
Comunicaciones I2C
I2C es un protocolo serie utilizado en una interfaz de 2 hilos de baja velocidad. Fue desarrollado originalmente por Phillips en 1982 para permitir que los circuitos integrados dentro de los receptores de televisión se comunicaran entre sí.
Los tiempos han cambiado, Phillips ahora es NXP e I2C se ha convertido en un estándar de comunicación que es compatible con prácticamente todos los principales fabricantes de semiconductores.
I2C es una abreviatura de «Inter-Integrated Circuit». También se llama » IIC » o «I al cuadrado C».
Usos y limitaciones
I2C se utiliza con microcontroladores como Arduino y con microcomputadoras como Raspberry Pi. Muchas pantallas y sensores interactúan con su controlador host usando I2C.
I2C tiene varias limitaciones sin embargo. No es particularmente rápido, aunque para la mayoría de sus usos previstos es lo suficientemente rápido.
I2C solo se puede usar en distancias cortas, después de todo, originalmente estaba destinado a comunicarse entre circuitos integrados en la misma placa de circuito impreso. La distancia máxima de transmisión fiable disminuye a medida que aumenta la velocidad, a la velocidad más lenta (100 Kbaud o una velocidad de reloj de 100 kHz) la distancia máxima es de aproximadamente un metro.
Velocidades I2C
El bus I2C original tenía una velocidad máxima de 100 kHz. La mayoría de las aplicaciones comunes siguen utilizando esta velocidad, ya que es bastante suficiente para transferir datos desde sensores y a pantallas simples.
I2C y tiene algunos modos de velocidad más altos. No todos los dispositivos I2C admiten estos modos:
- Modo rápido: tiene una velocidad de reloj máxima de 400 kHz.
- Modo de alta velocidad: Una frecuencia de reloj máxima de 3,4 MHz
- Modo ultra rápido: Frecuencia de reloj máxima de 5 MHz
En un bus I2C, es el maestro el que determina la velocidad del reloj.
Cómo funciona I2C
Un bus I2C tiene dos señales, junto con una conexión de alimentación y tierra.
Las dos líneas de señal son las siguientes:
- SDA – Esta es la línea de datos bidireccional.
- SCL-Esta es la señal del reloj.
Hay dos resistencias pull-up unidas a cada línea de señal, que elevan el bus hasta la tensión de alimentación cuando está inactivo.
Tenga en cuenta que el voltaje de alimentación no es estándar, puede ser de 3,3 o 5 voltios. También puede ser un voltaje más bajo para algunas implementaciones I2C de alta velocidad.
Esta diferencia en los voltajes de alimentación puede causar problemas cuando se interconectan dispositivos I2C que usan diferentes niveles lógicos. Discutiremos esto más en un artículo futuro cuando te muestre cómo interactuar con una Raspberry Pi (3.lógica de 3 voltios) con un Arduino Uno (lógica de 5 voltios).
Hay dos tipos de dispositivos que se pueden interconectar con el bus I2C: Maestros y esclavos.
El dispositivo Maestro controla el bus y suministra la señal de reloj. Solicita datos de los esclavos individualmente. Puede haber más de un dispositivo maestro en el bus, pero solo uno puede ser el maestro activo en un momento dado.
Los dispositivos maestros no tienen una dirección asignada.
Los dispositivos esclavos tienen una dirección, y esta dirección debe ser única en el bus. Utilizan un esquema de direccionamiento de 7 bits, por lo que hasta 128 esclavos pueden estar en un bus I2C. En la vida real, esta gran colección de dispositivos nunca se usa, es raro ver más de una docena de dispositivos I2C en un bus.
Se ha implementado un nuevo esquema de direccionamiento de 10 bits, que es compatible con versiones anteriores con el método de direccionamiento de 7 bits existente.
Los dispositivos I2C comerciales reciben direcciones I2C de NXP, que mantiene las especificaciones del bus. Aunque I2C ha sido de código abierto desde 2006, se cobra una tarifa por obtener una dirección esclava de NXP. No se requiere ninguna tarifa para los dispositivos maestros o para los dispositivos que no están destinados a la fabricación comercial.
A algunos dispositivos I2C se les asignan múltiples direcciones, generalmente varianzas en los bits de dirección inferiores. Estos dispositivos se pueden configurar manualmente para diferentes direcciones, lo que permite usar varios dispositivos del mismo tipo en un solo bus I2C.
Otros Derivados de I2C
Hay otros buses que se han derivado del bus I2C, y que son de muchas maneras compatibles con I2C.
- TWI-La interfaz de doble hilo es prácticamente idéntica al bus I2C. Este es en realidad el bus que utiliza Arduino, TWI se desarrolló cuando el bus I2C no era de código abierto y Atmel no quería arriesgarse a una violación del nombre comercial. La única diferencia importante entre TWI e I2C es que TWI no es compatible con una técnica avanzada llamada «estiramiento de reloj».
- SMBus es otro bus equivalente a I2C, desarrollado por Intel. Al igual que TWI, es compatible con la mayoría de las funciones de I2C.
En un artículo futuro explicaré cómo se estructuran los datos en el bus I2C. Pero ahora tenemos información básica de I2C, suficiente para empezar a experimentar.
Biblioteca de cables Arduino
Arduino tiene una biblioteca incorporada para trabajar con I2C llamada Biblioteca de cables. Hace que sea muy fácil comunicarse en el bus I2C, y puede configurar el Arduino para que se convierta en maestro o esclavo.
La biblioteca de cables tiene varias funciones útiles para trabajar con I2C.
- begin (): Inicia la biblioteca y configura el Arduino para que sea maestro o esclavo.
- requestFrom() – Esta función es utilizada por el maestro para solicitar datos de un esclavo.
- beginTransmission() – Esta función es utilizada por el maestro para enviar datos a un esclavo especificado.
- endTransmission() – Esta función es utilizada por el maestro para finalizar una transmisión iniciada con la función beginTransmission.
- write () – Utilizado tanto por el maestro como por el esclavo para enviar datos en el bus I2C.
- available () – Utilizado tanto por el maestro como por el esclavo para determinar el número de bytes en los datos que están recibiendo.
- read () – Lee un byte de datos del bus I2C.
- SetClock () – Utilizado por el maestro para establecer una frecuencia de reloj específica.
- onReceive () – Utilizado por el esclavo para especificar una función a la que se llama cuando se reciben datos del maestro.
- onRequest() – Utilizado por el esclavo para especificar una función que se llama cuando el maestro ha solicitado datos.
Usaremos algunas de estas funciones en nuestros bocetos.
Conexiones Arduino I2C
Las conexiones SDA y SCL para I2C son diferentes entre los modelos Arduino. Los experimentos que estoy a punto de mostrarles se hicieron usando dos Unos Arduino, pero pueden usar otros modelos de Arduino siempre que cambien los pines en consecuencia.
He creado un gráfico para ayudarte a resolverlo. Incluye algunas placas Arduino comunes, así como algunos de los chips discretos. Los pinouts para la lista de chips I (ATtiny y ATmega328P) son con el paquete DIP, no los de montaje en superficie.
Arduino Board or Chip | SDA | SCL |
Uno | A4 | A5 |
Mega2560 | 20 | 21 |
Nano | A4 | A5 |
Pro Mini | A4 | A5 |
Leonardo | 2 | 3 |
Due (has two I2C) | 20 + SDA1 | 20 + SCL1 |
ATTiny85 & ATTiny45 | 5 | 7 |
ATmega328P | 27 | 28 |
Algunos clones Arduino Uno tienen pines SDA y SCL separados y puede usarlos en lugar de los dos pines analógicos si lo desea. Están conectados internamente al mismo lugar.
Tenga en cuenta que el Arduino Due en realidad tiene dos puertos I2C.
Además, tenga en cuenta que hay algunos diagramas de conexión incorrectos en Internet para el Pro Mini. Utilice los dos pines analógicos, A4 y A5, como se muestra en la tabla anterior.
I2C Entre 2 Arduinos
Para nuestro primer experimento, juntaremos dos Arduinos e intercambiaremos datos entre ellos. Un Arduino será el amo, el otro será el esclavo.
Estoy usando dos Unos de Arduino, pero puedes sustituir otros Arduinos si no tienes dos Unos. Utilice el gráfico anterior para las conexiones.
Conectar 2 Arduinos
Así es como conecté mis dos Arduino Unos juntos:
Es una conexión bastante simple, esencialmente solo ata el suelo y los dos pines I2C juntos.
Una cosa a tener en cuenta es que mi diagrama no muestra el uso de resistencias pull-up, descubrí que todo parecía funcionar correctamente sin ellas. Sin embargo, es posible que desee incluirlos, especialmente si experimenta errores u operación intermitente.
Para conectar algunas resistencias pull-up, agregue un par de resistencias de 10k a las líneas SDA y SCL. Conecte el otro extremo a la salida de 5 voltios en uno de los Arduinos.
Boceto de demostración maestro
Aquí está el boceto que se utilizará en el Arduino que ha designado como maestro.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
/*
I2C Maestro Demo
i2c-master-demo.ino
Demonstrate use of I2C bus
Master sends character and gets reply from Slave
DroneBot Workshop 2019
https://dronebotworkshop.com
*/
// Include Arduino Wire library for I2C
#include <Wire.h>
// Define Slave I2C Address
#define SLAVE_ADDR 9
// Define Slave answer size
#define ANSWERSIZE 5
void setup() {
// Initialize I2C communications as Master
Wire.begin();
/ / Configurar el monitor serial
Serial.begin (9600);
Serial.println («Demostración de maestro I2C»);
}
bucle vacío () {
delay(50);
Serial.println («Escribir datos en esclavo»);
// Escribir un charatre en el cable esclavo
.Iniciar transmisión (SLAVE_ADDR);
Cable.write (0);
Wire.endTransmission ();
Serial.println(«Recibir datos»);
// Leer la respuesta del Esclavo
// Leer 5 caracteres
de Alambre.requestFrom (SLAVE_ADDR, ANSWERSIZE);
/ / Añadir caracteres a la cadena
String response=»»;
while (Wire.available ()) {
char b = Wire.read ();
response + = b;
}
/ / Imprimir en el monitor serial
Serial.println(respuesta);
}
|
Como todos I2C bocetos, empezamos mediante la inclusión de la librería Wire.
A continuación definimos algunas constantes para representar la dirección I2C del esclavo y el número de bytes de datos que esperamos recuperar de él.
En la Configuración inicializamos las comunicaciones I2C como maestro. Sabemos que es un maestro, ya que no hay ningún parámetro de dirección en la función begin. También configuramos un monitor en serie e imprimimos una línea de texto en él.
Ahora al bucle.
Comenzamos con un pequeño retardo de tiempo, principalmente para ralentizar las cosas lo suficiente para que podamos leer la pantalla en el monitor serie.
A continuación, usamos la función beginTransmission para enviar datos al esclavo. En este caso, los datos que enviamos son solo un número cero. Terminamos el envío con una llamada a la función de transmisión final.
A continuación solicitamos algunos datos del esclavo usando la función requestFrom.
Después de eso formulamos una cadena de respuesta leyendo los datos, un byte a la vez, del esclavo.
Imprimimos los detalles de lo que estamos haciendo y de los datos que recibimos en el monitor en serie. Y luego terminamos el Bucle y lo hacemos todo de nuevo.
Boceto de demostración esclavo
Ahora en el boceto utilizado por el esclavo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
/*
I2C Slave Demo
i2c-slave-demo.ino
Demonstrate use of I2C bus
Slave receives character from Master and responds
DroneBot Workshop 2019
https://dronebotworkshop.com
*/
// Include Arduino Wire library for I2C
#include <Wire.h>
// Definir Dirección I2C Esclava
#definir SLAVE_ADDR 9
// Definir tamaño de respuesta esclavo
#definir TAMAÑO de RESPUESTA 5
// Definir cadena con respuesta al maestro
Cadena respuesta = «Hola»;
void setup() {
// Inicializa las comunicaciones I2C como cable esclavo
.begin (SLAVE_ADDR);
// Función para ejecutarse cuando se solicitan datos del cable maestro
.onRequest (requestEvent);
// Función para ejecutarse cuando se reciben datos del cable maestro
.En recepción (Evento de recepción);
/ / Configurar el monitor serial
Serial.begin (9600);
Serial.println («Demostración de esclavos I2C»);
}
void receiveEvent () {
// Leer mientras se reciben los datos
while (0 < Cable.disponible ()) {
byte x = Cable.read ();
}
/ / Imprimir en el monitor serial
Serial.println («Recibir evento»);
}
void requestEvent () {
/ / Configurar la variable de bytes en el tamaño correcto
respuesta de bytes;
/ /Formatee la respuesta como array
para (byte i=0;i<ANSWERSIZE;i++) {
response = (byte)answer.charAt (i);
}
// Devuelve la respuesta al cable maestro
.write (response, sizeof (response));
// Imprimir en el monitor Serial
Serial.println(«Solicitud de eventos»);
}
void loop() {
// retardo de Tiempo en lazo
delay(50);
}
|
una Vez más, hemos de empezar por incluir la librería Wire. Al igual que con el boceto anterior, también definimos la dirección I2C para el esclavo, así como el número de bytes que planeamos enviar al maestro.
A continuación definimos la cadena que vamos a enviar de vuelta al maestro, en este caso solo la palabra «Hola». Si decide cambiar esto, asegúrese de ajustar la constante ANSWERSIZE en ambos bocetos para que sea correcta.
En la Configuración inicializamos la conexión al bus I2C con una función begin. Tome nota de la forma diferente en que hacemos esto, ya que es un esclavo que especificamos la dirección I2C que vamos a usar. Al hacer esto, la biblioteca de cables sabe que queremos operar en modo esclavo.
Ahora necesitamos definir los nombres de las funciones a las que llamaremos cuando ocurran dos eventos: una solicitud de datos recibida del maestro y los datos recibidos del maestro. También configuramos e imprimimos en el monitor serie.
Se llama a la función receiveEvent cuando recibimos datos del maestro. En esta función leemos los datos mientras los datos están disponibles y los asignamos a un byte (recuerde, los datos se recibirán de un byte a la vez).
La función requestEvent se llama cada vez que recibimos una solicitud de datos del maestro. Tenemos que enviar nuestra cadena de «Hola» de vuelta al maestro. Como necesitamos enviar los datos de un byte a la vez, dividimos los caracteres en «Hola» en elementos individuales en una matriz y luego los enviamos uno por uno.
Reportamos todo nuestro progreso en ambas funciones al monitor en serie.
El bucle de este croquis simplemente añade un retardo de tiempo, que coincide con el utilizado en el croquis maestro.
Ejecutar los bocetos de demostración
Para ejecutar estos bocetos, deberá poder ver el monitor serie en cada Arduino. Si tiene dos computadoras con el IDE Arduino instalado, eso lo hará mucho más fácil.
En Microsoft Windows es posible abrir dos instancias del IDE de Arduino. Si lo hace, puede mostrar ambos monitores serie uno al lado del otro en la misma pantalla.
Alternativamente, puede usar una computadora y encender la segunda Arduino con su propia fuente de alimentación. Tendrías que cambiar la computadora y la energía entre los dos Arduinos, de esta manera podrías monitorear ambas pantallas una por una.
Arduino Remote Usando I2C
En la siguiente demostración conectaremos un potenciómetro al Arduino maestro y un LED al esclavo. Utilizaremos el potenciómetro para controlar la velocidad de parpadeo del LED.
Esta es otra demostración simple, puede basarse en ella para crear algo más práctico.
Conexión de demostración remota
Así es como se organiza este experimento.
es esencialmente el mismo circuito como el experimento anterior, con la adición del potenciómetro en el maestro y el LED en el esclavo.
Tenga en cuenta que el LED del esclavo se ha conectado al pin 13. Como el Arduino Uno tiene un LED incorporado en el pasador 13, puede eliminar el LED y su resistencia de caída si lo desea.
Las observaciones sobre las resistencias pull-up también se aplican a esta conexión.
Boceto Maestro de demostración remota
El boceto para el lado maestro de este experimento es muy simple, de alguna manera el lado I2C es incluso más simple que el utilizado en la primera demostración. Esto se debe a que solo estamos enviando datos al esclavo y no esperamos obtener nada de vuelta.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
/*
I2C Master Control Demo
i2c-master-demo-de control.ino
Demonstrate use of I2C bus
Master sends potentimeter position data
DroneBot Workshop 2019
https://dronebotworkshop.com
*/
// Include Arduino Wire library for I2C
#include <Wire.h>
// Define Slave I2C Address
#define SLAVE_ADDR 9
// Analog pin for potentiometer
int analogPin = 0;
// Integer to hold potentiometer value
int val = 0;
void setup () {
/ / Inicializa las comunicaciones I2C como cable maestro
.begin ();
}
void loop() {
delay (50);
// Leer el valor del pote
/ / Asignar al rango de 1-255 para la velocidad de flash
val = map(analogRead (analogPin), 0, 1023, 255, 1);
/ / Escribe un charatre en el cable esclavo
.Iniciar transmisión (SLAVE_ADDR);
Cable.write (val);
Cable.Transmisión final();
}
|
Como siempre, debemos incluir la librería Wire en el principio de que el boceto. También definiremos una constante para mantener la dirección del esclavo.
Dado que estamos utilizando un potenciómetro, necesitaremos definir tanto el pin al que está conectado como una variable para mantener su valor.
Todo lo que hacemos en la configuración es inicializar la conexión I2C como maestro.
En el Bucle leemos el valor del potenciómetro y lo asignamos a un rango de 01-255. Necesitamos hacer eso ya que estamos enviando un byte de información y solo podemos contener estos valores en un solo byte.
Tenga en cuenta que invertimos la secuencia de numeración en la función de mapa Arduino, esto se hace para que el sistema se comporte de la manera que esperamos: girar el potenciómetro a la derecha aumenta la velocidad de flash. Como la» velocidad de destello » se especifica por un retardo de tiempo, un número mayor que se envía equivaldrá a una velocidad de destello más larga.
También tenga en cuenta que no enviamos el valor 0, que solo mantendría el LED en un estado. Fijamos nuestro rango para que termine en 1 en su lugar.
Ahora solo es cuestión de enviar el byte al esclavo y repetir el bucle de nuevo.
Demostración remota Recibir croquis
El lado esclavo necesita recibir datos del maestro y usarlos para parpadear el LED.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
/*
I2C Esclavo de Control Demo
i2c esclavo-demo-de control.ino
Demonstrate use of I2C bus
Receives potentimeter position data
Controls LED blink rate
DroneBot Workshop 2019
https://dronebotworkshop.com
*/
// Include Arduino Wire library for I2C
#include <Wire.h>
// Define Slave I2C Address
#define SLAVE_ADDR 9
// Define LED Pin
int LED = 13;
// Variable for received data
int rd;
// Variable for blink rate
int br;
void setup () {
pinMode (LED, SALIDA);
/ / Inicializa las comunicaciones I2C como cable esclavo
.begin (SLAVE_ADDR);
// Función para ejecutarse cuando se reciben datos del cable maestro
.onReceive (receiveEvent);
/ / Configurar Serial Monitor
Serial.begin (9600);
Serial.println(«I2C Esclavo de Demostración»);
}
void receiveEvent() {
// lee un carácter desde el I2C
rd = Alambre.read ();
/ / Valor de impresión de los datos entrantes
Serial.println (rd);
}
void loop() {
delay(50);
// Calcular parpadeo valor
br = map(rd, 1, 255, 100, 2000);
digitalWrite(LED, HIGH);
delay(br);
digitalWrite(LED, LOW);
delay(br);
}
|
comenzamos con la habitual inclusión de la librería Wire, así como la definición de la dirección del esclavo. También definimos un pasador para el LED.
Se definen un par de variables adicionales, una que contiene los datos recibidos mientras que la otra lleva el valor de retardo de tiempo para la velocidad de parpadeo.
En la Configuración configuramos el pin de E / S para el LED como salida e inicializamos el bus I2C. A medida que usamos la dirección de esclavo en la función begin, la biblioteca de cables sabe que estamos actuando como esclavos.
Solo necesitamos definir una función onReceive, a diferencia de la última demostración, no esperamos ninguna solicitud del maestro. También configuramos e imprimimos en el monitor serie, usaremos el monitor para ver los datos entrantes.
La función receiveEvent lee los datos entrantes y los asigna a la variable I. También imprime el valor en el monitor serie.
Finalmente, en el bucle usamos los datos entrantes para parpadear el LED. Una vez más, utilizamos la función de mapa para lograr esto, cambiando los valores entrantes de 1-255 a un rango más amplio. Puede experimentar con cambiar este rango para hacer que el LED parpadee más rápido o más lento si lo desea.
Las últimas declaraciones son esencialmente el Arduino Blink sketch disfrazado. Encendemos y apagamos el LED durante un período de tiempo que determinamos en el último paso.
Y luego repetimos el bucle.
Ejecutar la demostración remota
Cargue el código y alimente ambos Arduinos. Puede usar su monitor serie en el esclavo Arduino para ver los datos entrantes.
Girar el potenciómetro ahora debería variar la velocidad de parpadeo del LED en el esclavo.
Conclusión
Esto concluye nuestra primera mirada detallada a I2C. En la próxima entrega, aprenderemos más sobre la estructura de los datos que se están intercambiando. También tomaremos un sensor normal y lo convertiremos en un sensor I2C.
Feliz comunicación!
Recursos
Bocetos: Todos los bocetos de I2C utilizados en este artículo.
I2C información – Información sobre el protocolo I2C