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:
- Universidad de Carnegie Mellon. http://www.cmu.edu/index.shtml
- Programming Solutions for the LEGO Mindstorms NXT. http://www.botmag.com/articles/10-31-07_NXT.shtml
- ROBOTC. http://www.robotc.net
- I2C con ROBOTC e Hitechnic Experimenter Kit. http://lrobotikas.net/eu/modeloak/nxt/91-i2c-con-robotc-e-hitechnic-experimenter-kit
No hay comentarios:
Publicar un comentario
Nota: solo los miembros de este blog pueden publicar comentarios.