¿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.

viernes, 17 de junio de 2011

Sensor SCP 1000 y su conexión con Arduino por SPI

El sensor SCP1000 es un sensor de presión barométrica preciso y de alta calidad. En condiciones ideales, es capaz de medir presiones de una capa de aire de 9 centímetros y está pensado para mediciones tales como en altímetros por ejemplo, con un gran rango de temperaturas de funcionamiento que van desde -20 a 70 grados centígrados. La calibración y compensación se realiza internamente, por lo que siempre dispondremos de una medición precisa. El sensor se controla mediante un bus SPI y su consumo puede ser de apenas 4 micro Amperios. El sensor se puede ver en la siguiente figura:




Conexiones realizadas



Como se ha comentado en el epígrafe anterior, este sensor se va a conectar mediante el protocolo SPI. Las conexiones que se han realizado entre dicho sensor y nuestro sistema de desarrollo han sido las siguientes:
  • Pines de tierra de ambos conectados
  • Alimentación del sensor al pin de 3,3 de Arduino
  • Pines MISO y MOSI del protocolo SPI a los pines 50 y 51 de Arduino respectivamente
  • La señal de reloj, SCK, se ha conectado pin 52 de Arduino
  • Los pines de indicación de dato listo (DRDY) y de selección del chip (CSB) se han conectado a los pines 30 y 31 de Arduino respectivamente




Código implementado



El código está estructurado de la siguiente forma:
  • En el apartado de setupo(), se configuran los registros del sensor
  • En el bucle principal se configura el sensor para que lea en alta resolución, es decir, devolverá 19 bits para la presión y 16 bits para la temperatura. Para obtener la temperatura en grados centígrados, bastará con dividir los 16 bits entre 20
  • A continuación se lee la temperatura en 2 bytes
  • Finalmente, se lee la presión en dos partes: primero se leen los 3 bits más altos, y seguidamente los 16 bits más bajos. Estas dos partes se combinan en un long entero desplazando los bits más altos y realizando una OR bit a bit para combinarlos con los 16 más bajos. La humedad en pascales es el resultado de 19 bits dividido entre 4.

El código completo se muestra a continuación. Se han dejado los comentarios para simplificar la comprensión del mismo:



// Incluimos la librería SPI
#include <SPI.h>

//Direcciones de los registros de la memoria del sensor
const int PRESSURE = 0x1F;      //3 bits mas significativos de la presion
const int PRESSURE_LSB = 0x20;  //16 bits menos significativos de la presion
const int TEMPERATURE = 0x21;   //16 bits de la lectura de temperatura
const byte READ = 0b11111100;     // Instruccion de lectura del SCP1000
const byte WRITE = 0b00000010;   // Instruccion de escritura del SCP1000

// Pines usados para la conexion con el sensor
// El resto ya estan definidos en la libreria SPI
const int dataReadyPin = 30;
const int chipSelectPin = 31;

void setup() {
  Serial.begin(9600);

  // Inicializamos la libreria SPI
  SPI.begin();

  // Inicializamos pines dataReady y chipSelect
  pinMode(dataReadyPin, INPUT);
  pinMode(chipSelectPin, OUTPUT);

  //Configuramos el SCP1000 en config. de bajo ruido
  writeRegister(0x02, 0x2D);
  writeRegister(0x01, 0x03);
  writeRegister(0x03, 0x02);
  // Y le damos tiempo al sensor para que se prepare
  delay(100);
}

void loop() {
  //Seleccionamos el modo de alta resolucion
  writeRegister(0x03, 0x0A);

  // No hacemos nada hasta que el pin dataReady este alto
  if (digitalRead(dataReadyPin) == HIGH) {
    //Leemos la temperatura
    int tempData = readRegister(0x21, 2);

    // Convertimos la temp. a Celcius y la mostramos
    float realTemp = (float)tempData / 20.0;
    Serial.print("Temp[C]=");
    Serial.print(realTemp);

    // Leemos los 3 bits mas altos de la presion
    byte  pressure_data_high = readRegister(0x1F, 1);
    pressure_data_high &= 0b00000111; // Solo hacen falta los bits del 2 al 0

    //Se leen los 16 bits mas bajos de la presion
    unsigned int pressure_data_low = readRegister(0x20, 2);
    // Se combinan las dos partes en un solo numero de 19 bits:
    long pressure = ((pressure_data_high << 16) | pressure_data_low)/4;

    // Mostramos la presion:
    Serial.println("\tPressure [Pa]=" + String(pressure));
  }
}

//Lecturas desde o escrituras hacia los registros del SCP1000
unsigned int readRegister(byte thisRegister, int bytesToRead ) {
  byte inByte = 0;           // Byte entrante del SPI
  unsigned int result = 0;   // Resultado a devolver
  Serial.print(thisRegister, BIN);
  Serial.print("\t");
  // SCP1000 espera el nombre del registro en los 6 bits mas altos
  // del byte. Asi que desplazamos los bits hacia la izq. 2 posiciones
  thisRegister = thisRegister << 2;
  // Combinamos la direccion y la instruccion en un byte
  byte dataToSend = thisRegister & READ;
  Serial.println(thisRegister, BIN);
  // Bajamos el ChipSelct para seleccionar el dispositivo
  digitalWrite(chipSelectPin, LOW);
  // Mandamos al dispositivo el registro que queremos leer:
  SPI.transfer(dataToSend);
  // Mandamos un valor de 0 para leer el primer byte devuelto
  result = SPI.transfer(0x00);
  // Reducimos en uno el numero de bytes restantes por leer
  bytesToRead--;
  // Si quedan bytes por leer
  if (bytesToRead > 0) {
    // Desplazamos el primer byte hacia la izquierda, y obtenemos el 2º byte:
    result = result << 8;
    inByte = SPI.transfer(0x00);
    // Combinamos el byte que acabamos de obtener con el anterior:
    result = result | inByte;
    // Nos queda un byte menos por leer:
    bytesToRead--;
  }
  // Levantamos el chip-select
  digitalWrite(chipSelectPin, HIGH);
  // Devolvemos el resultado
  return(result);
}

//Mandamos ahora una escritura el dispositivo

void writeRegister(byte thisRegister, byte thisValue) {

  // SCP1000 espera la direccion del registo en los 6 bits mas altos
  // del byte. Asi que desplazamos en 2 posiciones hacia la izquierda
  thisRegister = thisRegister << 2;
  // Combinamos el registro de direccion y la instruccion en un solo byte:
  byte dataToSend = thisRegister | WRITE;

  // Bajamos el chip-select para seleccionar el sensor:
  digitalWrite(chipSelectPin, LOW);

  SPI.transfer(dataToSend); //Mandamos la localizacion del registro
  SPI.transfer(thisValue);  //Mandamos el valor a almacenar en el registro

  // Levantamos el chip-select
  digitalWrite(chipSelectPin, HIGH); }

Aquí os dejo una captura de la salida de datos, en la que se ve la presión en grados, la temperatura en pascales y el contenido de los registros leídos en bianrio (simplemente los he dejado como traza, para ver que todo se hace correctamente):


---------------------------------
Referencias:

- Sensor SCP1000: http://www.bricogeek.com/shop/150-sensor-de-presion-barometrica-scp1000.html

domingo, 12 de junio de 2011

Conexión del GPS Venus con conector SMA con Arduino por puerto serie


Otro de los dispositivos cuya integración se ha probado con Arduino ha sido un receptor GPS de la marca Venus, que se puede apreciar en la siguiente imagen:

Figura Receptor GPS Venus con conector SMA
Se trata de un receptor de gran sensibilidad, que permite la conexión de una antena externa mediante un conector SMA. Se puede configurar para lograr una tasa de refresco de hasta 10 Hz., y permite obtener por el puerto serie cadenas del tipo NMEA-0183, que se explicarán en otro post del blog. La tasa por defecto a la que este dispositivo devuelve datos es de 9600 baudios, pero puede configurarse con una velocidad de hasta 115200 baudios.
El corazón de este GPS es el chip Venus634FLPx, que lleva incorporado un LNC, un reloj en tiempo real y un regulador de tensión. Las características más destacadas son las siguientes:
  • 51 canales de adquisición y 14 de tracking
  • Lleva un chipset SkyTrack
  • La frecuencia máxima de actualización es de 10 Hz. aunque viene configurada por defecto a 1 H.
  • Lleva LNA integrado
  • Se debe alimentar con una tensión de entre 2,7 y 3,3 V.
  • Su consumo es de unos 28 mA una vez conectado con los satélites.
  • Su sensibilidad es de -161 dBm
  • El margen de error al posicionarse es de menos de 2,5 metros
  • Si se arranca en frío puede tardar unos 29 segundos en localizarse, mientras que si se arranca en caliente este tiempo se reduce incluso hasta un segundo.
  • Soporta antenas tanto activas como pasivas, aunque en el caso de las primeras deberán alimentarse de forma externa.
  • Su tamaño es de 3,8x1,8 cm, con lo que es fácilmente integrable.


Conexiones realizadas

Las conexiones realizadas son similares a las que se hicieron en el caso del lector RFID, con la diferencia de que en este caso el circuito va alimentado a una tensión de 3,3 V. Por lo tanto:
  • Las tierras (pines GND) de dispositivo se conectan entre sí y con cualquiera de los pines de tierra de la placa de Arduino.
  • El pin TX se conecta con el pin RX0 de Arduino, que en el caso de Arduino Mega 2560 corresponde al pin 0.
  • El pin de alimentación de 3,3 V se conecta con el pin de 3,3 V de Arduino.

Código implementado

En el código que se incluye a continuación, simplemente se configura la velocidad a 9600 baudios y se van mostrando los datos que se vayan recibiendo a través del puerto serie:

char val = 0; //Para almacenar el valor que le llega

void setup() {
    Serial.begin(9600); // Conectamos al puerto serie
}

void loop () {

    char IDstring[13]; //Para almacenar el tag
    int  i;
  
    if (Serial.available() > 0 ) {
      //Serial.println("Datos --> ");
      val = Serial.read();    // Se busca el inicio del codigo
      Serial.print(val);
      if (val=='\r'){
        Serial.println(" ");
      }
    }
}



Arduino y la comunicación SPI


En este epígrafe se va a analizar el protocolo de comunicaciones SPI, y se va a ver de qué manera se puede manejar desde el entorno de desarrollo Arduino. En primer lugar se hará un resumen de cómo funciona dicho protocolo, y a continuación se explicarán las distintas funciones disponibles para poder utilizarlo.

El protocolo SPI
El protocolo SPI proviene de las siglas en inglés “Serial Peripheral Interface”, y es un estándar de comunicaciones usado principalmente en la transferencia de información entre circuitos integrados en circuitos electrónicos. Se trata de un bus serie de datos para la transferencia síncrona y bidireccional de información. En toda comunicación por SPI deberá haber al menos un dispositivo actuando como maestro, y uno o más actuando como esclavos. Para seleccionar a cada uno de los esclavos existe una línea, denominada “slave select” o “chip select”.
Las señales del protocolo SPI son las siguientes:
  • SCLK: Es la señal de reloj, impuesta por el dispositivo maestro.
  • MOSI: Corresponde a las siglas “Master Output – Slave Input”, es decir, el maestro enviará los datos a través de esta línea y el esclavo los recibirá.
  • MISO: Corresponde a las siglas “Master Input – Slave Output”, y es la línea por la que los esclavos enviarán datos al dispositivo maestro.
  • SS: Es la señal de “Slave Select”, es decir, la línea que el maestro activará para indicar al esclavo que se va a establecer la comunicación con él.



Habitualmente, el pin MISO del maestro se conecta con el pin MOSI del esclavo, y viceversa. Además, la señal de selección de esclavo suele ser activa a nivel bajo.
A continuación se va a describir con un poco más de detalles cómo funciona el protocolo:
  • Para iniciar la comunicación, el maestro configura el reloj usando una frecuencia menos o igual a la frecuencia máxima que soporta el esclavo. Estas frecuencias suelen estar en el rango de 1 a 70 MHz.
  • El maestro a continuación pone a nivel bajo la señal “Slave Select” del esclavo para indicarle que se va a comunicar con él. Si es necesario esperar un tiempo antes de iniciar la comunicación (por ejemplo para permitir una conversión analógico / digital), el maestro esperará al menos ese tiempo antes de proseguir con el intercambio de información.
  • Durante cada ciclo de reloj se produce una comunicación en los dos sentidos, ya que por una parte el maestro va a mandar un bit a través del pin MOSI y el esclavo lo va recibir, mientras que a la vez el esclavo va a mandar un bit a través de la línea MISO para que el maestro lo reciba.
  • Cuando ya no quedan datos que transmitir, el maestro deja de accionar la señal de reloj y normalmente vuelve a colocar a nivel alto la señal de “Slave Select” para así deseleccionar al dispositivo.
  • Cualquier dispositivo que no tenga a nivel bajo su señal de selección, ignorará los movimientos que haya en las líneas MISO y MOSI, con lo que podemos tener distintos dispositivos conectados a esas mismas líneas sin que interfieran en la comunicación. Evidentemente, la señal de “Slave Select” sí debe ser propia de cada dispositivo.
  • Además de la frecuencia de reloj, el maestro también puede configurar la polaridad y la fase de la señal de reloj con respecto a los datos. La polaridad se suele denominar usando las siglas CPOL, mientras que la fase se suele denominar como CPHA. En la figura inferior se puede ver un diagrama temporal que explica las distintas combinaciones (o modos) que se describen a continuación:

o   Si CPOL = 0, el valor base de la señal de reloj es el nivel bajo.
§  Si CPHA = 0, los datos se capturan en el flanco de subida de la señal de reloj, y se propagan en el flanco de bajada. Este modo se denomina modo 0.
§  Si CPHA = 1, los datos de capturan en el flanco de bajada de la señal de reloj, y se propagan en el flanco de subida. Este modo se denomina modo 1.
o   Si CPOL = 1, el valor base de la señal de reloj es el nivel alto.
§  Si CPHA = 0, los datos de capturan en el flanco de bajada de la señal de reloj, y se propagan en el flanco de subida. Este modo se denomina modo 2.
§  Si CPHA = 1, los datos se capturan en el flanco de subida de la señal de reloj, y se propagan en el flanco de bajada. Este modo se denomina modo 3.




Figura Diagrama temporal del protocolo SPI

Funciones disponibles en Arduino
La gestión del protocolo SPI en Arduino se realiza a través de la librería SPI. Esta interfaz se inicializa automáticamente cuando la librería SPI se incluye en un código. En el caso de Arduino Mega 2560 usando en las pruebas, los pines se inicializan de la siguiente forma:
  • Pin 50: señal MISO
  • Pin 51: señal MOSI
  • Pin 52: señal SCK
  • Pin 53: Señal SS, aunque se pueden usar otros pines para este fin. Esto puede ser útil por ejemplo en el caso de tener varios dispositivos conectados en un mismo bus.


La configuración por defecto del protocolo SPI en Arduino establece que el esclavo será él mismo, que se van a transferir en primer lugar los bytes más significativos de cada byte (MSB), que  el modo seleccionado es el 0 y se establece la frecuencia de reloj en una cuarta parte de la frecuencia del sistema. Veamos las distintas funciones disponibles para modificar estos parámetros:
  • mode (byte config). Permite modificar el registro de configuración del SPI. Si hay varios dispositivos usando SPI y cada uno necesita un modo distinto, esta función se deberá llamar antes de acceder a cada dispositivo en particular. Veamos dos ejemplos de uso:
    • Spi.mode( (1<<CPOL) | (1<<CPHA) ). Establece el modo 3
    • Spi.mode( (1<<SPR0) ). Configura el reloj SCK a 1/16 la velocidad del reloj del sistema.
  • Byte transfer(byte b). Envía y recibe un byte a través del bus SPI. Por ejemplo:
    • n = Spi.transfer( 0x2A ). Envía el byte 0x2A y devuelve lo que reciba del otro dispositivo.
  • Byte.transfer(byte b, byte delay). Esta función es idéntica a la anterior, sólo que antes de acometer la operación espera un número de microsegundos especificados en el parámetro delay. Es útil cuando hay que esperar un tiempo antes de iniciar una transferencia de datos.






--------------------------------------------
Referencias:


Arduino y la comunicación serie: librería Serial


Todas las placas de Arduino disponen de al menos un puerto serie, que permite la comunicación con un ordenador o con otros dispositivos haciendo uso de esta interfaz. Los pines que se usan para la comunicación por puerto serie vienen marcados en la placa con las siglas RX y TX, que corresponden al pin por el que se enviarán los datos, y al pin por el que se recibirán los datos respectivamente.
En el caso que nos ocupa, usaremos el puerto serie no para comunicarnos con un ordenador, sino para poder comunicarnos con determinados sensores. En este epígrafe se expondrán las funciones necesarias para establecer la comunicación e intercambiar datos, y en el capítulo cuarto se verán ejemplos de sensores conectados por el puerto serie.


Conexiones
Como se ha comentado en la introducción de este epígrafe, se van a usar los pines TX y RX de la placa de Arduino. En las pruebas realizadas se ha usado la placa “Arduino Mega 2560”, la cual dispone de 8 pines destinados para ello, en lugar de los 2 de los que disponen el resto de placas. Estos pines vienen marcados en la placa y son las siguientes parejas (0,1), (14,15), (16,17), (18,19) y (20,21).
Para un correcto funcionamiento, se debe conectar el pin de recepción de la placa (RX) al pin de transmisión del dispositivo externo, y el pin de transmisión de la placa (TX) al pin de recepción de dicho dispositivo. Además, para comunicar el sistema de desarrollo con un dispositivo TTL es necesario conectar las tierras entre ellas.

Principales funciones
Las principales funciones usadas en la comunicación por puerto serie vienen incluidas en la librería Serial() y  son las siguientes:
  • Serial.begin(velocidad). Establece la velocidad en bits por segundo (baudios) para la transmisión de datos en serie.
  • El parámetro velocidad se puede configurar con el valor que se desee para comunicarse con un dispositivo externo (hasta un máximo de 115200 baudios), sin embargo si la comunicación por puerto serie va a ser con un ordenador, se deberá elegir una de las siguientes: 300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600 o 115200.
  • Serial.end(). Desactiva la comunicación serie, permitiendo que los pines de recepción y transmisión puedan ser usados como pines de entrada / salida digitales. Si se desee reanudar la comunicación serie, se deberá invocar nuevamente Serial.begin().
  • Serial.available(). Devuelve, ecomo entero, el número de bytes (caracteres) disponibles para ser leídos por el puerto serie. Se refiere a datos ya recibidos y disponibles en el buffer de recepción del puerto, cuya capacidad es de 128 bytes. En el caso de Arduino Mega, existen además las funciones Serial1.available(), Serial2.available() y Serial3.available().
  • Serial.read(). Lee los datos entrantes al puerto serie. Devuelve como entero el primer byte disponible recibido por el puerto serie, o -1 si no hay datos disponibles. Al igual que en el caso anterior, para la placa Arduino Mega existen también las funciones Serial1.read(), Serial2.read() y Serial3.read().
  • Serial.flush(). Vacía el buffer de la entrada de datos serie. Esto quiere decir que si se llama a la función Serial.read() o Serial.available(), estas sólo devolverán los datos recibidos después de que se haya realizado esta llamada. No requiere parámetros ni devuelve nada. Para la placa Arduino Mega existen disponibles las llamadas Serial1.flush(), Serial2.flush() y Serial3.flush() además de la principal.
  • Serial.print(valor, formato). Esta función imprime los datos al puerto serie como texto ASCII. El primer parámetro, valor, es el propio valor que se desea imprimir. Esto se podrá realizar de distintas formas:
    • Los números son impresos mediante un juego de caracteres ASCII para cada dígito.
    • Los valores de tipo flotante (float) se imprimen en forma de dígitos ASCII con 2 decimales por defecto, es decir, si ejecutamos Serial.print(1.23456) se imprimirá “1.23”.
    • Los valores de tipo “byte” se enviarán como un único carácter.
    • Los caracteres y las cadenas se enviarán tal cual.
  • Además, existe el segundo parámetro es opcional y permite especificar el formato que se desea usar. Así, este parámetro puede tomar el valor BYTE, BIN (valores binarios, en base 2), DEC (valores decimales, en base 10), HEX (valores hexadecimales, en base 16). Si lo que se está imprimiendo son números con coma flotante, este parámetro especificará el número de decimales a usar.
  • Serial.println(valor, formato). Imprime los datos al puerto serie como texto ASCII seguido de un retorno de carro(ASCII 13 o ‘\r’) y un carácter de avance de línea (ASCII 10 o ‘\n’). Por lo demás, este comando tiene la misma forma y los mismos parámetros que Serial.print() descrito anteriormente.
  •  Serial.write(). Esta función escribe datos binarios en el puerto serie. Estos se enviarán como un byte o como una serie de bytes. Si lo que se desea es envíar los caracteres que representan los números, es mejor usar la función Serial.print() en su lugar. Puede tomar distintos parámetros:
    • Serial.write(valor) enviará un solo byte.
    • Serial.write(str) enviará una cadena como una serie de bytes.
    • Serial.write(buf, len) enviará un array como una serie de bytes. El tamaño del array se indicará en el segundo parámetro.

--------------------------------------------------------------------------
ReferenciasArduino Serial library. http://arduino.cc/es/Reference/Serial