I2C con RobotC e Hitechnic Experimenter kit

Atención, abrir en una nueva ventana. ImprimirE-mail

NXT

Me he propuesto aprender algo sobre el manejo del bus I2C, y no hay como escribir sobre ello para asentar las ideas. Corro el riesgo de haber metido la pata en alguna parte del artículo, así que si a alguien se lo parece estaría encantado de recibir sugerencias. El artículo todavía no está  completo, lo acompañaré de un proyecto práctico que estoy haciendo con mi hijo.

A continuación voy a explicar de modo esquemático ciertas características de este bus necesarias para conectar hardware y programarlo. Evitaré profundizar en aquellos aspectos no necesarios para el proyecto. Una de las  fuentes de información que he utilizado es el “LEGO MINDSTORMS NXT Hardware Developer Kit” que describe las características del interface incluido en el NXT.

He utilizado el Hitechnic Experimeter's kit para los ejemplos. Se trata de una excelente opción para practicar con montajes electrónicos y programación de dispositivos I2C. La documentación que acompaña al kit contiene ejemplos en RobotC y NXC además de en LabVIEW y NXT-G. En los dos últimos casos hay bloques de programación que simplifican la lectura y escritura en el "Protoboard" de Hitechnic..

protoboard protoboard

Qué es I2C

I2C es un bus de comunicaciones serie y facilita la comunicación entre numerosos dispositivos.

Para ello utiliza dos líneas para transmitir la información: una de las dos marca el tiempo (SCL) mientras que la otra se utiliza para intercambiar datos (SDA). También es necesaria una tercera línea, pero esta sólo es la referencia (masa GND).

Los dispositivos conectados al bus I2C han de tener una dirección única (normalmente establecida por el fabricante) y en un principio el número de dispositivos que se pueden conectar al bus no tiene límites. Cuando se establecen las comunicaciones uno de ellos ejerce como master (en un principio cualquiera de los dispositivos conectados al bus) mientras que los demás serán esclavos. El master podrá escribir o leer información del esclavo que desee.

Características del I2C en el NXT

En cuanto a las características del interface de comunicaciones del NXT hay que señalar lo siguiente:

  • El NXT dispone de 4 canales de comunicación I2C, uno en cada puerto de entrada (1..4).
  • Se podrían conectar hasta 127 dispositivos por entrada.
  • El NXT sólo puede trabajar como master en relación a las comunicaciones I2C y requiere que los dispositivos esclavos dispongan de resistencias pull-up incluidas en sus pins de comunicaciones.
  • En los cables de conexión del NXT SDA corresponde al cable azul (pin 6), SCL al amarillo (pin 5) y GND al negro (pin 2) o rojo (pin 3). En el caso de dispositivos sin alimentación propia (por ejemplo, el Hitechnic Experimenter kit) se utilizará el cable verde (pin 4).
  • Cada canal dispone de un buffer de entrada de 16 bytes y un buffer de salida de 16 bytes, así que un máximo de 16 bytes puede ser enviado o recibido.

Protocolo de comunicación del bus I2C

Dado que el NXT sólo puede trabajar como master, será él el que lea datos en otros dispositivos o el que los escriba. Para ello los dispositivos externos se caracterizan como áreas de memoria a leer o sobre las que escribir. Conociendo las direcciones de las posiciones de memoria de los dispositivos externos, es posible escribir en ellas o leer su contenido.

Para ello se dispone de un protocolo de comunicaciones con las siguientes características:

  • Cuando el NXT quiere iniciar una lectura o escritura de datos envía una secuencia de inicio para alertar a los dispositivos esclavos de que se quiere comenzar una transacción.
  • Una vez alertados transmite un mensaje cuyo primer byte contiene siete bits que componen la dirección del dispositivo que se desea seleccionar (el del esclavo), y un octavo bit que corresponde a la operación que se quiere realizar con él (1 para leer ó 0 para escribir).
  • Seguidamente hay que notificar al esclavo en qué dirección interna se desea leer o escribir.
  • Para acabar se envía la secuencia de parada.
I2C protocolo I2C protocolo

Ejemplo con RobotC

Para comunicarse con un dispositivo I2C desde RobotC hay que empezar configurando el puerto a utilizar como sensorI2CCustom9V. En este caso utiliza el puerto S1 y le da el nombre PROTO_PORT:

#pragma config(Sensor, S1, PROTO_PORT, sensorI2CCustom9V)

Hay que definir las variables que almacenarán los datos de salida y los de entrada. Estas variables serán matrices de bytes, cmdbuff[] y respbuff[]:

  • byte cmdbuff[4];
  • byte respbuff[2];

Veamos qué contendrá cada elemento de la matriz:

  • cmdbuff[0]: número de bytes  que conforman el comando I2C a enviar
  • cmdbuff[1]: la dirección del dispositivo I2C esclavo en el que se desea escribir o leer
  • cmdbuff[2]: la dirección de la  posición de memoria en la que ha de escribirse o que hay que leer. Esta información se encuentra en la  información técnica del dispositivo.
  • cmdbuff[3]: cuando la operación es de escritura contiene los datos a enviar
  • respbuff[2]:  es la  matriz utilizada para almacenar las lecturas que se realizan en algún dispositivo I2C. Está compuesta de dos bytes que contendrán las lecturas.

Tal y como he señalado antes, para iniciar las comunicaciones hay que enviar una secuencia de inicio para poner en escucha el dispositivo esclavo deseado. Para enviar mensajes RobotC dispone del siguiente procedimiento:

sendI2CMsg(nPort, sendMsg, nReplySize);

  • nPort         nombre del puerto I2C utilizado, en el ejemplo PROTO_PORT

  • sendMsg    el mensaje a enviar, en nuestro caso cmdbuff[0]

  • nReplySize tamaño en bytes de la  respuesta. Si se trata sólo de escritura su valor será 0.

La secuencia de inicio será un mensaje que contiene la dirección del dispositivo esclavo, el procedimiento sendI2CMsg se encarga  de dar el formato necesario así que no tendremos mas que dar a los dos primeros elementos de la matriz cmdbuff[i] sus valores y enviarla.

... 

cmdbuff[0] = 1;  //se va a enviar únicamente un byte

cmdbuff[1] = 0x02;   // la  dirección del dispositivo, en este caso “2”

sendI2CMsg(PROTO_PORT, cmdbuff[0], 0);    //Envío de la secuencia de inicio

... 

Una vez iniciada la comunicación ya se pueden enviar o recibir mensajes. De todos modos, volviendo al ejemplo del Hitechnic Experimenter kit todavía queda  por hacer algo si vamos a utilizar las  entradas/salidas digitales. Hay que decirle cuáles vamos a utilizar como entradas y cuáles como salidas. Para ello el dispositivo dispone de una posición de memoria (posición 0x4E) en la que se registra dicha configuración, en ella deberemos de escribir los valores deseados.

Veamos un ejemplo. Imaginemos que deseamos configurar los  puertos digitales del Hitechnic Experimenter kit de la siguiente manera:

 Puerto B5 B4 B3 B2 B1 B0
 Modo Salida Entrada Entrada Salida Salida Entrada
 Valor binario
 1 0 0 1 1 0
 Valor Decimal 32 (16) (8) 42 (1)

Si el puerto está configurado como Salida (escritura) el valor asignado será 1 y si es como Entrada (lectura) el valor será 0. Tenemos una cadena de 6 bits (100110), en un orden determinado, que habremos de escribir en la posición 0x4E. Este valor lo habremos de convertir en su valor decimal (38 = 32 + 4 + 2) o hexadecimal (0x26), para no liarse lo más cómodo para la conversión es la calculadora de Windows u otra similar. El código será el siguiente:

... 

cmdbuff[0] = 3; // Número de bytes del comando I2C

cmdbuff[1] = 0x02;  // dirección I2C del esclavo

cmdbuff[2] = 0x4E;  // posición de memoria en la que han de escribirse los datos

cmdbuff[3] = 0x26;  //datos a escribir: 100110 = 0x26

sendI2CMsg(PROTO_PORT, cmdbuff[0], 0);  // Envío de la orden al esclavo

... 

Ahora ya es el momento de aprovechar los recursos que nos ofrece el dispositivo I2C que tengamos conectado. Cualquier operación de escritura se realiza del modo que acabo de explicar, lo único que variará es la posición de memoria en la  que han de escribirse los datos y el valor de los datos a escribir.

Hemos configurado en el paso anterior los puertos digitales B1, B2 y B5 como salidas. Imaginemos que tenemos conectados a ellas tres LEDs rojo (B1), amarillo (B2) y verde (B5) a modo de semáforo y que deseamos encender los LEDs amarillo y verde. Para ello deberemos dar a esas salidas el valor lógico 1 (el 0 sería para apagar).

Podemos ver los valores en la tabla, los bits de las salidas no van a ser significativos así que los he dejado en  cero.

 Puerto B5 B4 B3 B2 B1 B0
 LED Verde   Amarillo Rojo  
 
 Valor binario
 1   1 0 
 Valor Decimal 32 (16) (8) 4(2) (1)

El valor a escribir será 100100 en binario, 36 (= 32 + 4) en decimal y 0x24 en hexadecimal, así que el código será el siguiente:

... 

cmdbuff[0] = 3;  // Número de bytes del comando I2C

cmdbuff[1] = 0x02;  // dirección I2C del esclavo

cmdbuff[2] = 0x4D;  // posición de memoria en la que han de escribirse los datos. 0x4D es la dirección para salidas digitales.

cmdbuff[3] = 0x24;  //datos a escribir: 100100 = 0x24

sendI2CMsg(PROTO_PORT, cmdbuff[0], 0);  // Envío de la orden al esclavo

... 

Vamos a ver cómo se realiza una lectura de datos. En realidad una lectura  de datos consta de dos operaciones: la primera es de escritura, la solicitud de datos al esclavo y la segunda de lectura de los datos recibidos. Vamos a ver primero el código de solicitud de datos:

... 

cmdbuff[0] = 2;  // Número de bytes del comando I2C

cmdbuff[1] = 0x02;  // dirección I2C del esclavo

cmdbuff[2] = 0x42;  // dirección correspondiente a la entrada a leer. En este ejemplo se trata de la entrada analógica A0

sendI2CMsg(PROTO_PORT, cmdbuff[0], 2);  // Envío del comando al esclavo

...

En este caso hay una diferencia en el procedimiento sendI2CMsg, el tercer parámetro es un 2 lo que indica que quiere leer dos bytes.

...

wait1Msec(10);  // da tiempo para que responda

readI2CReply(PROTO_PORT, respbuff[0], 2); // almacena la respuesta en la matriz respbuff[i].

... 

Las entradas analógicas del Protoboard devuelven un valor entre 0 y 1023 (un valor de 10 bits) que almacenan en grupos de dos bytes, Los 8 bits del primer byte recibido (respbuff[0]) contienen los 8 bits más significativos de la lectura mientras que los dos bits menos significativos del segundo byte (respbuff[1]) almacenan los dos bits menos significativos de la lectura del canal de entrada. Así que una vez recibidos hay que combinarlos para obtener el valor de lectura. Los valores recibidos son ubytes (byte sin signo de 0 a 255), no bytes (-128...127) lo cual genera un problema en RobotC ya que no dispone de dicho tipo de variable. Si no se tiene esto en cuenta  los valores no serán lo esperado (esto no ocurre en NXC).

No voy a entrar aquí en explicar cómo hacer la conversión, puede verse en los ejemplos que acompañan al Hitechnic Experimenter's kit. Para  convertir los dos bytes en un solo valor la operación será la  siguiente (no es código):

Lectura =  respbuff[0]*4 +  respbuff[1]    

Si lo que deseamos es hacer una lectura de las entradas digitales, el proceso es muy similar, la diferencia es que sólo se recibirá un byte que contendrá las lecturas de todas ellas, una vez recibidas habrá que relacionar cada bit con la entrada  correspondiente.

Referencias

+/-
Escribir comentario
Nombre:
Email:
 
Website:
Título:
Código UBB:
[b] [i] [u] [url] [quote] [code] [img] 
 
 
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch:
:(:shock::X:side::):P:unsure::woohoo::huh::whistle:;):s
 
Por favor introduce el código anti-spam que puedes leer en la imagen.
+/- Comentarios
Añadir nuevo Buscar

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."