I2C con RobotC e Hitechnic Experimenter kit
Última actualización el Lunes, 31 de Agosto de 2009 09:35 Escrito por Koldo Lunes, 24 de Agosto de 2009 17:51
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..
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.
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) | 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 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
- Bus I2C
- I2C en la wikipedia
- Artículo Comunicación - Bus I2C Descripción y funcionamiento por Eduardo J. Carletti
Comentarios |
|
|
Powered by !JoomlaComment 3.26
|





