Diferencia entre revisiones de «Wire»
(→Comentarios) |
(→Velocidad de reloj) |
||
Línea 58: | Línea 58: | ||
Se puede varias la veocidad de reloj asi | Se puede varias la veocidad de reloj asi | ||
− | {| class="wikitable" | + | {| class="wikitable col1cen col2cen col3cen" |
|- | |- | ||
! TWBR !! Pre-escaler !! Frecuencia !! Comentario | ! TWBR !! Pre-escaler !! Frecuencia !! Comentario |
Revisión del 05:30 5 may 2019
Contenido
Descripción
La librería Wire.h estandar de Arduino le permite comunicarse con dispositivos por bus I2C (Inter-Integrated Circuit o 2 alambres). Usa dos lineas: SDA (datos) y SCL (reloj). Une los GND.
El bus I2C es extremadamente útil para conectar múltiples dispositivos, ya que todos pueden compartir los mismos dos cables (más la tierra claro). Esto se debe a que los dispositivos son "direccionables". Cada dispositivo debe tener una dirección única en el rango de 8 a 119. La dirección 0 está reservada como una dirección de "transmisión", las direcciones 1 a 7 están reservadas para otros fines y las direcciones 120 a 127 están reservadas para uso futuro.
Debido a esto puede tener una pantalla LCD en la dirección 10, otro arduino en la direccion 11 y un sensor en la direccion 12.
Nota: A partir de Arduino 1.0, la biblioteca hereda de la clase Stream, lo que la hace compatible con otras bibliotecas de lectura/escritura. Debido a esto, send() y receive() han sido reemplazados por read() y write().
Placas aplicables
Arduino | Pin SDA | Pin SCL |
---|---|---|
UNO, Ethernet | A4 | A5 |
MEGA | 20 | 21 |
Leonardo | 2 | 3 |
Debido | 20 | 21 |
Sintaxis
#include <Wire.h> Wire.begin();
Métodos
Métodos | Descripción |
---|---|
Wire.begin(num) | Inicie la libreria Wire y únase al bus I2C como maestro o esclavo. Esto normalmente debería llamarse solo una vez. En caso de varios esclavos es importante numerarlos (0~255). |
Wire.beginTrasnmission(num) | Comience una transmisión al dispositivo esclavo num. Posteriormente, coloque los bytes en cola para la transmisión con la función Wire.write() y transmítalos llamando a Wire.endTransmission(). |
Wire.read() | Lee un byte de dato desde el buffer que envío un maestro. |
Wire.write() | Pone en el buffer del maestro un byte de datos para ser transmitido a un esclavo. |
Wire.onReceive(evento) | Interrupcion para recibir datos. |
Wire.endTransmission() | Termina comunicación y libera pines. Devuelve 0 si la transmision tubo exito. |
Proceso
- La transmisión empieza con la "condición de inicio", esto es cuando SDA esta en LOW y SCL esta en HIGH.
- La direccion consta de 7 bits esta desplazada al MSB. De modo que el LSB tendra 0 = escritura o 1 = lectura (maestro a esclaveo).
- Luego, el maestro espera a que el esclavo tire de la línea SDA a LOW, lo cual es un ACK (confirmación) de que existe un esclavo en esa dirección y está listo para recibir datos. Si no hay ningún esclavo conectado y encendido o si no tiene la dirección requerida, ignorará la preticion, dejando la línea SDA HIGH. Esto cuenta como un NAK (reconocimiento negativo).
- Luego se transmite el byte de datos, el bit más significativo (MSB) primero.
- Después de los 8 bits de datos, el maestro verifica que el esclavo confirma tirando de la línea SDA LOW. Así, se reconoce cada byte.
- La transmisión finaliza con la "Condición de parada", esto es cuando SDA esta en HIGH y SCL esta en HIGH.
Velocidad de reloj
Se puede varias la veocidad de reloj asi
TWBR | Pre-escaler | Frecuencia | Comentario |
---|---|---|---|
12 | 1 | 400 kHz | Maxima soportada |
32 | 1 | 200 kHz | |
72 | 1 | 100 kHz | Default |
152 | 1 | 50 kHz | |
78 | 4 | 25 kHz | |
158 | 4 | 12.5 kHz |
Para configurar el pre-escaler en 4 (12.5 kHz):
Wire.begin(); TWBR = 158; TWBR |- bit (TWPS0);
Comentarios
La libreria estándar de I2C para Arduino es Wire. Si bien esta libreria es suficiente la mayor parte del tiempo cuando desea comunicarse con dispositivos, hay situaciones en las que no es aplicable:
- Los pines de I2C (SDA/SCL) ya están en uso para otros fines,
- el código se ejecutará en un procesador ATtiny con 1 MHz en pines arbitrarios,
- tiene poca memoria (flash y RAM), o
- no desea usar las resistencias de activación habilitadas implícitamente porque sus dispositivos se ejecutan con 3 voltios.
Se adapto la libreria I2C de Peter Fleury que está escrito en ensamblador AVR, de peso extremadamente ligero (< 500 bytes de flash) y muy rápido. Incluso con un ATtiny de 1 MHz, uno todavía puede operar el bus con 33 kHz, lo que implica que puede controlar dispositivos esclavos que usan el protocolo SMBus (tiempo de espera si la frecuencia del bus es inferior a 10 kHz).
Si desea que se ejecute una solución en una MCU ARM (Due, Zero, Teensy 3.x), desea usar los pines en el puerto H o superior en un ATmega256, o si desea usar muchos buses I2C diferentes, esta biblioteca no es la solución adecuada para usted. En estos casos recomiendo SlowSoftI2CMaster, otra libreria I2C escrita en C++.
Nota: Recuerde que Arduino UNO R3 tiene I2C dedicado adicional en los pines debajo del USB.
Advertencias
- Las direcciones I2C son de 7 bits para identificar a los esclavos ya que el 8vo bit determina si se está escribiendo o leyendo.
- Con 7 bits se puede obteniendo direcciones entre 0~127. Sin embargo, las direcciones de 0~7 y 120~127 están reservados.
- En caso de tener mas de un esclavo se deben numerar (8~119) con Wire.begin(num_esclavo) y para trasnmitir el maestro debe indentificar con Wire.begin Transmission(num_esclavo). 0 = todos los esclavos.
- Si tienes mas de un esclavo o usa cables largos, hay que poner un 4k7 como pull-up en cada linea SDA/SCL.
- No olvide conectar también todos los GND.
- El buffer es de 32 bytes.
Nota: Recuerde que se transmite un byte a la vez, por lo que para transmitir un int (2 byte) o un long (4 bytes) se debera hechar mano a las funciones lowByte(), highByte(), bitshift left (<<), bitshift right (<>>), asi como los operadores bit a bit compuestos como: |=, &=, >>=, <<=; tanto en el emisor como el receptor.
Ejemplo 1
#include <Wire.h>
byte valor = 0 ;
void setup() {
Wire.begin(); //unirse al bus i2c (dirección opcional para el maestro)
TWBR = 12; //Aumenta velocidad de reloj 4 veces
}
void loop() {
Wire.beginTransmission(44); //transmitir al dispositivo N° 44 (0x2c)
//la dirección del dispositivo se especifica en la hoja de datos
Wire.write(byte (0x00)); //envía un byte de instrucción
Wire.write(valor); //envía el valor del potenciómetro Byte
if (Wire.endTransmission() == 0){ //deja de transmitir, 0 es exito
val ++; //incrementa el valor
if (valor == 64){ //si se alcanza la posición 64 (max)
valor = 0 ; //comienza desde el valor más bajo
}
}else{
//Error
}
delay(500) ;
}
Ejemplo 2
Conectar dos Arduinos y que el maestro envie una secuencia y el esclavo la ejecute. En este caso usaremos 3 LED en los pines 11, 12 y 13 del esclavo. En el maestro ponemos este código.
#include <Wire.h>
const byte pin[] = {11, 12, 13};
byte estado = 0;
void setup() {
Wire.begin(); //Iniciamos maestro
}
void loop() {
for (byte i=0; i<3; i++){
Wire.beginTransmission(1); //Nos queremos comunicar con el esclavo 1
Wire.write(pin[i]); //Enviamos primer byte, será el pin a encender
Wire.write(estado); //Enviamos segundo byte, sera el estado 0/1
Wire.endTransmission(); //Paramos la transmisión
delay(1000);
}
if (estado == 0){
estado = 1;
}else{
estado = 0;
}
}
Para el esclavo tendremos:
#include <Wire.h>
void setup() {
Serial.begin(9600);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
Wire.begin(1); //Unimos este dispositivo al bus I2C con dirección 1
Wire.onReceive(recive); //Evento a ejecutar al detectar recepcion
}
void loop() {
delay(100);
}
//Función que se ejecuta cada Wire.endTransmission() del maestro para recibir lo que manda con Wire.write().
void recive() {
byte pin = 0;
byte estado = 0;
if (Wire.available() == 2){
pin = Wire.read(); //Leemos primer byte que sera pin
Serial.print("LED: ");
Serial.println(pin);
estado = Wire.read(); //Leemos segundo byte que sera estado
Serial.print("Estado: ");
Serial.println(estado);
}
digitalWrite(pin, estado); //Prende o apagar LED
}
Ejemplo 3
En este ejemplo ilustramos como un maestro puede solicitar datos de un esclavo.
Código maestro:
#include <Wire.h>
const byte esclavo = 42;
enum {
CMD_ID = 1,
CMD_READ_A0 = 2,
CMD_READ_D13 = 3
};
void setup (){
Serial.begin(9600);
Wire.begin();
sendCommand(CMD_ID, 1);
if (Wire.available()) {
Serial.print("Id del esclavo: ");
Serial.println(Wire.read(), DEC);
}else{
Serial.println ("No responde el esclavo");
}
}
void loop(){
int val;
sendCommand(CMD_READ_A0, 2);
val = Wire.read(); //Recuperamos MSB
val <<= 8; //Desplazamos 8 bits a la izquierda
val |= Wire.read(); //Recuperamos LSB
Serial.print("Valor sensor en A0 del esclavo: ");
Serial.println(val);
sendCommand(CMD_READ_D13, 1);
val = Wire.read();
Serial.print("Estado del pin D13 del esclavo: ");
Serial.println(val);
delay(500);
}
void sendCommand (const byte cmd, const byte largo){
Wire.beginTransmission(esclavo);
Wire.write(cmd);
Wire.endTransmission();
Wire.requestFrom(esclavo, largo); //No continua hasta que tenga datos en el buffer
}
Código esclavo:
#include <Wire.h>
const byte esclavo = 42;
char comando = 0;
enum {
CMD_ID = 1,
CMD_READ_A0 = 2,
CMD_READ_D13 = 3
};
void setup(){
pinMode(13, INPUT_PULLUP); //Pulsador
pinMode(A0, INPUT); //Sensor analogico
Wire.begin(esclavo); //Numero del esclavo
Wire.onReceive(leer); //Cuando llega mensaje
Wire.onRequest(pedir); //Cuando maestro solicita mensaje
}
void loop(){
//Nada
}
void sensor(const byte pin){
int val = analogRead(pin);
byte buf[2];
buf[0] = highByte(val); //Capturamos MSB byte
buf[1] = lowByte(val); //Capturamos LSB byte
Wire.write(buf, 2); //Envia los 2 byte del sensor en A0
}
void leer(){
comando = Wire.read();
}
void pedir(){
switch (comando){
case CMD_ID:
Wire.write(42); //Envia numero del esclavo
break;
case CMD_READ_A0:
sensor(A0); //Envia dato sensor en A0
break;
case CMD_READ_D13:
Wire.write(digitalRead(13)); //Envia estado de pin 13
break;
}
}
Ejemplo 4
En este ejemplo queremos saber que esclavos estan activos.
Código maestro:
#include <Wire.h>
void setup(){
Serial.begin(115200);
Serial.println("Escaneando bus I2C...");
byte n = 0;
Wire.begin();
for (byte i=8; i<128; i++){
Wire.beginTransmission(i);
if (Wire.endTransmission() == 0){
Serial.print("Encontre esclavo: ");
Serial.println(i);
n++;
delay(1);
}
}
Serial.println("Fin de busqueda.");
Serial.print("Encontre ");
Serial.print(n);
Serial.println(" esclavo(s).");
}
void loop(){
//Nada
}
Vea también
Referencias externas
- Wire
- I2C - Nick Gammon
- Bus I2C - Enrique Crespo
- Soft I2C Master - otra libreria I2C
- Conectar 2 Arduino con I2C - Luis Del Valle