¿Qué es este blog?

La idea de este blog nace para compartir los avances que se vayan realizando a lo largo de un estudio sobre cómo interconectar los distintos sensores que se pueden encontrar en el mercado, o fabricar de forma casera, con la plataforma Mindstorms de LEGO. Para ello se hará uso ARDUINO, un entorno de desarrollo abierto basado en microcontrolador.

lunes, 10 de enero de 2011

Manejo del protocolo I2C usando ROBOTC


           ROBOTC es un lenguaje de programación para robótica basado en el popular lenguaje C. Fue desarrollado en la unidad de robótica de la Universidad de Carnegie Mellon [21] y la documentación y el entorno de desarrollo se pueden obtener tanto desde la página oficial de lego como desde la página del proyecto de ROBOTC
            Se trata de software libre, desarrollado por la comunidad, compatible con distintas plataformas y que suele venir acompañado del ensamblador NBC. Soporta bluetooh y puerto serie, por lo que también nos permitirá acceder al protocolo I2C de manera sencilla. Para cargarse en el robot hace uso del firmware estándar, y se generan programas ligeros y de rápida ejecución.
En cuanto a la forma de programar, aunque textual este lenguaje tiene una característica similar a la de “arrastrar y soltar” que se encuentra en los métodos gráficos de programación para el RCX, sólo que en este caso lo que se “suelta” no es una caja, sino un trozo de código. En la figura que tenemos a continuación, se ve el espacio de trabajo del programa: la parte izquierda de la pantalla contiene el diccionario de ROBOTC con todas las posibilidades del lenguaje. Además, este diccionario se puede organizar tanto para usuarios noveles, ocultando las funciones más complejas, como para usuarios más avanzados.


Entorno de trabajo de ROBOTC

            ROBOTC tiene un depurador en tiempo real bastante potente que consigue reducir el tiempo que se debe emplear para depurar un programa. Además, la gran cantidad de manuales y de vídeos explicativos proporcionadas por el propio desarrollador, hacen de este lenguaje una excelente alternativa para programar el NXT.

Descripción del funcionamiento
            A continuación se va a explicar cómo acceder al protocolo I2C mediante ROBOTC. Veamos un ejemplo de comunicación paso a paso:
-       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:
o   #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[]:
o   byte cmdbuff[4];
o   byte respbuff[2];
-       Veamos qué contendrá cada elemento de la matriz:
o   cmdbuff[0]: número de bytes  que conforman el comando I2C a enviar
o   cmdbuff[1]: la dirección del dispositivo I2C esclavo en el que se desea escribir o leer
o   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.
o   cmdbuff[3]: cuando la operación es de escritura contiene los datos a enviar
o   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:
o   sendI2CMsg(nPort, sendMsg, nReplySize), donde los parámetros son los siguientes:
  • 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.
o   cmdbuff[0] = 1;  //se va a enviar únicamente un byte
o   cmdbuff[1] = 0x02;   // la  dirección del dispositivo, en este caso “2”
o   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, 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. Imaginemos que deseamos configurar los  puertos digitales del sensor a conectar 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)
 4
2
 (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 datoscmdbuff[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. En cualquier operación de escritura se realiza del modo que se acaba 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].

-       Algunas entradas analógicas de ciertos sensores 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 que 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). Para  convertir los dos bytes en un solo valor la operación será la  siguiente (no es código):
o   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:

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.