Translate

miércoles, 3 de julio de 2013

Aplicación modo CAPTURA: sensor TSL235

En la entrada anterior describimos la funcionalidad del modo Captura del módulo CCP. En esta entrada usaremos dicha funcionalidad en una sencilla aplicación.  

Vamos a usar el sensor TSL235, que es un medidor de intensidad de luz. Al contrario que otros sensores, cuya salida es un voltaje proporcional a la irradianza recibida,  la salida de este sensor es una onda cuadrada, cuya frecuencia es proporcional a la magnitud (luz) medida. 

Usando el módulo de CAPTURA mediremos el periodo de los pulsos emitidos. A partir de dicho periodo será sencillo deducir su frecuencia y de ahí la irradianza (en W/m2) recibida.

Como en entradas anteriores, los datos recibidos se mandan al puerto serie del PC donde usamos un programa MATLAB para ver los resultados.

Archivos de código asociados a esta entrada:  

  Código para PIC      tls235.c  

  Programa MATLAB    flicker.m  



--------------------------------------------------------------------------------------------


Hardware: (TSL235R)


El TSL235R de TAOS (Texas Advanced Optoelectronic Solutions) es un pequeño conversor de luz a frecuencia.  Se alimenta entre 3 y 5V y por su pin de salida saca una onda cuadrada con una frecuencia proporcional a la luz (formalmente irradianza en W/m2) que está recibiendo el sensor.

Yo lo adquirí en https://www.sparkfun.com/products/9768 por unos $3, aunque supongo que se puede conseguir en otros muchos sitios.

Las ventajas de usar frecuencia como salida en vez de un voltaje proporcional son varias: 

  • Mucho mayor rango posible. El TSL235 da una salida desde 1Hz hasta casi 1MHz (en mi muestra el máximo está por 800 KHz). Eso son 6 ordenes de magnitud. Con un voltaje necesitaríamos un conversor AD de 20 bits (log2(10^6)) para obtener una resolución semejante.
  •  Además la medida de una frecuencia es mucho más resistente al ruido que la medida de la amplitud de un voltaje (pensad en FM vs AM).
En el datasheet  (https://www.sparkfun.com/datasheets/Sensors/Imaging/TSL235R-LF.pdf) vemos que su respuesta es casi lineal en la mayor parte de su rango usando una escala logarítmica tanto para la frecuencia como para la irradianza:


De la gráfica adjunta vemos que la irradianza recibida (en uW/cm2 para una luz de 635 nano-metros) coincide casi exactamente con la frecuencia (en kHz) de la onda cuadrada a su salida. Siendo más precisos:


                             log10(irrad) = 0.9871 log10(f) + 0.1579


                          irrad = 1.4385 f^0.9871, (f en KHz, irrad en  uW/cm2)


Si lo deseamos podemos convertir uW/cm2 a W/m2 (1W/m2 = 100 uW/cm2).


Conectando la salida a un osciloscopio se ve la esperada onda cuadrada cuya frecuencia varia en un rango muy amplio según el nivel de luz:

·    Máxima frecuencia de unos 780 kHz, expuesto directamente a la luz solar.
·    Frecuencias del orden de 100-300 kHz en pleno día pero con el sensor en la sombra.
·    Unos 50 kHz en una habitación, de día, pero con las persianas bajadas.
·    Unos 20 Hz si tapo el sensor con una vieja caja de película fotográfica.
·    Unos 5 Hz en casi total oscuridad.


La conexión al micro es inmediata. Se alimenta con los mismos 5V que el PIC (con un condensador de 0.1 uF entre alimentación y tierra). El pin de salida se conecta al correspondiente pin del micro. La elección del pin a usar dependerá del tipo de medida que vayamos a hacer. Para usarlo en modo CAPTURA (como haremos en este post) usaremos RC2 o RC1. Si por el contrario vamos a contar pulsos usaríamos RB0 y la correspondiente interrupción INT0 como hicimos con los codificadores en cuadratura. 


Software: 


Nuestro objetivo es medir el periodo de la señal de salida. A partir de ahí calculamos su frecuencia y con la fórmula anterior la irradianza recibida por el sensor en W/m2.
Cuando tratamos de estimar el periodo de una señal cuadrada tenemos dos posibilidades:

1. Podemos medir la duración de 1 periodo (o unos pocos si son demasiado cortos). De esta forma obtenemos una medida de la frecuencia instantánea, adecuada p.e. para monitorizar una iluminación que cambie rápidamente.

2. Si estamos midiendo una iluminación que cambia poco o nos interesa una media de la iluminación en un cierto periodo de tiempo podemos contar el número de periodos en un tiempo más largo. Obtendremos así la frecuencia media durante la observación.

El enfoque 2) es el que usamos en el tema de los codificadores en cuadratura. Conectaríamos la salida del sensor al pin RB0 y habilitaríamos la interrupción INT0. Con cada subida de la línea, en el código de la interrupción, se incrementaría un contador que representa el número de pulsos recibidos. Cada cierto tiempo un temporizador nos da el número de periodos recibidos en el intervalo (y de ahí sacamos la frecuencia).

En esta entrada vamos a usar el enfoque 1), basado en medir la duración de un periodo usando el módulo de captura. Para ello conectaremos la salida del sensor al pin RC2 (asociado al módulo CCP1).


Configuración CCP1 y TMR3:

El código de configuración es prácticamente idéntico al que vimos en la entrada anterior. Programamos T3CON para asociar el timer TMR3 al módulo CCP1 en modo CAPTURA. El timer TMR3 se configura en modo 16 bits con un prescaler 1:2 (con ese prescaler @ 8 MHz, cada incremento del timer equivale a 1 usec). Se habilita el modo CAPTURA y declaramos el pin asociado RC2 como entrada. Finalmente se habilita la correspondiente interrupción:

 OpenTimer3(TIMER_INT_OFF&T3_16BIT_RW&T3_SOURCE_INT&T3_PS_1_2&T3_SOURCE_CCP);
 T3CONbits.T3CCP2=1; T3CONbits.T3CCP1=1;  // TMR3 as source for both CCP1/CCP2
 TRISCbits.TRISC2=1; // RC2 (CCP1 associated pin) is an input

 CCP1CON=0b00000110; // Modo CAPTURE. Event = x4 rising edges


Como los eventos pueden producirse muy rápido (del orden de 1 usec si hay mucha luz) hemos configurado el módulo de captura en modo 4X (capturamos cada 4º pulso)


Interrupción CCP1:

A partir de ese momento, si el sensor TSL235 está conectado al pin RC2, empezarán a producirse eventos y los correspondientes tiempos del timer TMR3 se capturarán en los registros CCPR1H:CCPR1L. Para que no se sobre-escriban debemos habilitar la correspondiente interrupción:

 enable_priority_levels;
  CCP1_flag=0; enable_CCP1_int; set_CCP1_high;
 enable_high_ints; enable_low_ints;

Hemos declarado la interrupción CCP1 como de alta prioridad. La razón es que luego añadiremos otra interrupción y queremos que CCP1 se ejecute de forma preferentemente (para no perder eventos).

Una vez que habilitamos la interrupción CCP1 hay que escribir el correspondiente código de la ISR para procesar los tiempos de los eventos antes de que se pierdan. La rutina usada es la misma que el explicado en la entrada anterior. Dos variables externas t0 y dt de tipo uint16 guardan, respectivamente, el tiempo del último evento y la separación entre los dos últimos eventos.  La variable dt corresponderá por lo tanto a 4 periodos de la señal del sensor.

Como comentamos en la anterior entrada, si no llevamos la cuenta de los rebosamientos de TMR3, nuestra medida se verá limitada a un máximo de 65536 usec. Eso equivale a un periodo de 65536/4 = 16384 microsegundos, lo que corresponde a una frecuencia de unos 60 Hz en la señal del sensor.

En el código de la interrupción hemos añadido un cambio de status en el pin RC1, cuyo objeto es ver si la interrupción entra correctamente. 

En el siguiente pantallazo (izquierda) podemos ver (canal 1 amarillo) el ritmo de la interrupción (cambios en RC1) y en el canal 2 (abajo, azul) la salida directa del sensor. Vemos que la interrupción sucede cada 4 pulsos de la señal como habíamos programado. También se aprecia (en el zoom de la derecha) que la entrada de la interrupción está retrasada unos 10 usec con respecto a la subida del 4º pulso que la causa. Esto es debido a que el PIC tarda unos pocos ciclos en guardar su estado, saltar a la rutina de la interrupción, etc. Sin embargo, esto no afecta a la medida porque aunque la ejecución de la interrupción se retrase el instante correcto del evento quedó correctamente guardado en CCPR1L:CCPR1H.




Muestreo y envío de datos:

Con el código de la interrupción nos aseguramos de que en la variable dt (uint16) tengamos siempre disponible el valor más reciente de la medida que nos interesa (en este caso, el tiempo de 4 periodos de la señal del sensor).

Se trata ahora de (cada cierto tiempo) consultar dicho valor y volcarlo por un LCD o puerto serie. Siguiendo con el modelo de entradas anteriores voy a usar el puerto serie y mandar los datos (en binario) para que un programa en MATLAB en el PC se ocupe de recibirlos, convertirlos a frecuencia (o irradianza) y graficarlos.

Para implementar esta parte vamos a usar el timer TMR0 para que nos marque el ritmo de muestreo. Programare el timer para que su interrupción salte cada un cierto intervalo (Ts msec). En la interrupción consultaré el valor de dt y lo escribiré en otra variable, levantando una cierta bandera. Este sería el código de la ISR del TMR0:

// Low priority interruption
#pragma interruptlow low_ISR
void low_ISR (void)
{
 if (TMR0_flag)   // ISR de la interrupcion de TMR3
  {
   set_TMR0(contador);
   PORTCbits.RC0=~PORTCbits.RC0;
   contador = 65535-TMR0_delay;

   data=dt; data_ready=1;

   TMR0_flag=0;
  }
}

La variable TMR0_delay va a determinar cada cuanto salta la interrupción y se muestrea el valor dt.

En el programa principal configuraré el timer TMR0 y habilitaré su interrupción (como de baja prioridad):

OpenTimer0(TIMER_INT_ON&T0_16BIT&T0_SOURCE_INT&T0_PS_1_2); // 16 bit, Prescaler 1:2
enable_TMR0_int; set_TMR0_low;

El valor de arranque para TMR0_delay es de 10000, que con un prescaler 1:2 para TMR0 y un reloj de 8 MHz corresponde a un intervalo nominal de muestreo Ts = 10000 usec = 10 msec.

Finalmente, en el programa principal (tras la configuración de timers, módulo CCP, etc. ) entraré en un bucle infinito y consultaré la bandera (data_ready). Si me indica que hay datos que mandar me pondré a ello (tendré que controlar que el tiempo para mandar los datos no exceda el intervalo de muestreo Ts):

while(1)
  {
   if (data_ready==1) // Dump msg with dt and TMR0_delay               
   {
    send_byte('A');  // Message head
    send_byte(ptr[0]); send_byte(ptr[1]);  // send bytes in data (dt)

    data = TMR0_delay;
    send_byte(ptr[0]); send_byte(ptr[1]); // send bytes in data (TMR0_delay)

    send_byte(msg++); send_byte('Z'); // Message counter and tail         
    data_ready=0;  
   }

   if (DataRdyUSART())  // Modify TMR0_delay through serial port
    {
     ch = getcUSART();
     switch(ch)
      {
       case '+':  TMR0_delay+=2; break;
       case '0':  TMR0_delay=10000; break;
       case '-':  TMR0_delay-=2; break;
      }    
    }
  }

La segunda parte del código nos permite aumentar ('+) o disminuir ('-') el intervalo de muestreo a través del puerto serie.

Como en otras entradas, el código del PIC se acompaña de un programa MATLAB que recibe los datos capturados por el PIC y se encarga de presentarlos gráficamente, etc.


Resultados:

Veamos algunos resultados del programa bajo diferentes condiciones de iluminación. En primer lugar, un vídeo mostrando la salida del sensor (visualizada desde el PC con el programa MATLAB que acompaña a esta entrada) cuando está iluminado por luz natural:



En el caso de iluminación con luz natural (o con luz artificial "continua", como una linterna) no hay ninguna influencia del periodo de muestreo (alrededor de 10 msec) en la luminosidad recibida, ya que esta no cambia con el tiempo.

Las cosas son diferentes si registramos la salida del sensor bajo condiciones de luz artificial. Debido a la corriente alterna usada en la red, la iluminación artificial oscila con el ciclo de la red. Idealmente la luz se apagará cada vez que el voltaje pase por el 0 (100 veces por segundo si la frecuencia de la red es de 50 Hz, como en España). Dependiendo del tipo de iluminación el efecto es mayor o menor. La oscilación es más fácil de observar con fluorescentes. Las lámparas incandescente, por su mayor "inercia" (no les da tiempo a enfriarse), muestran un menor efecto.

La idea es que al ser nuestra medida bastante "instantánea" (en nuestro código estamos midiendo 4 pulsos del sensor, lo que a iluminaciones "normales" se traduce a una fracción de milisegundo) vamos a poder "ver" dichas oscilaciones.

Esta es la principal diferencia entre un enfoque de medición instantánea (capturando la duración de un pulso) y contar los pulsos producidos durante un intervalo grande de tiempo. Si tomásemos la medida contando los pulsos recibidos en 20 milisegundos  obtendríamos una medida media de la luminosidad  en un periodo de la red y no podríamos apreciar estos efectos.

Hemos dicho que el periodo de la iluminación con una luz artificial serán 10 milisegundos (la mitad del periodo del voltaje de la red). Nuestro programa está muestreando cada 10 milisegundos (nominales), ¿qué deberiamos ver?

La respuesta es que deberíamos ver una iluminación constante. La iluminación cambia continuamente, pero como mi periodo de muestreo coincide con el de cambio de la señal, siempre muestreo en el mismo punto del ciclo, obteniendo el mismo valor.

Esto se ilustra en la siguiente figura (fila 4). Tenemos (azul) una señal (luminosidad) con un periodo de 10 mseg, que se muestrea (puntos rojos) cada 10 milisegundos exactamente. Lo que obtenemos es un valor constante, mayor o menor dependiendo de en que parte del ciclo acertemos.

En la práctica será difícil sincronizar perfectamente nuestro muestreo con los 100 Hz de la iluminación. En ese caso se obtienen las otras situaciones ilustradas en la figura. El punto de muestreo se va corriendo en cada muestra y observamos una oscilación en los resultados. Notad que esa oscilación no está realmente en la señal, es un efecto ("aliasing") de intentar muestrear una señal con un ritmo de muestreo incorrecto (demasiado lento).




Estas oscilaciones (rojo) son las que esperamos ver con el programa si exponemos el sensor a luz artificial y muestreamos con una frecuencia del orden de 10 mili-segundos. 

Como el programa permite modificar el retraso (TMR0_delay) entre muestras podemos cambiarlo hasta intentar obtener una salida estable. En ese momento nuestro interrupción de muestreo estará perfectamente sincronizada con la red. Esta situación de equilibrio no durará mucho porque tanto nuestro cristal como la red pueden experimentar una deriva (alejamiento de su frecuencia nominal) que hará que se pierda la sincronización y volvamos a ver una oscilación.

El siguiente vídeo ilustra este comportamiento:




Este fenómeno de aliasing o "interferencia" entre la frecuencia de una señal y la frecuencia de muestreo puede apreciarse en multitud de situaciones. Es la razón de que podamos ver los radios de una rueda "moviéndose"  en dirección contraria al desplazamiento del vehículo. También se usaba en los antiguos discos estroboscópicos para ajustar la velocidad de giro de los tocadiscos, etc.

En el siguiente video (youtube) podéis ver otro ejemplo de aliasing. El concepto es el mismo que el presentado aquí (muestrear una señal periódica con un periodo de muestreo coincidente con el de la señal para verla estacionaria), pero en este caso el resultado es mucho mas llamativo que nuestras gráficas: 



5 comentarios:

  1. por favor no podrias explicar las graficas en matlab

    gracias

    ResponderEliminar
    Respuestas
    1. ¿Te refieres a las gráficas de muestreo o a las que aparecen en los videos?

      Antonio

      Eliminar
  2. felicitacione y gracias por tod lo que compartes,.!!!!!!!!!! Siguiendo !!

    ResponderEliminar
  3. Muchas gracias por el código, yo lo modifique para CCS para que corra en un 16F628A a 4Mhz, tengo un problema con los desbordes del TIMER1 que en mi código es el que lleva la cuenta del CCP1, por lo que veo en el archivo que compartes no esta implementado, por lo tanto, no entiendo como en el video te calcula bien la frecuencia. Tendría que usar la interrupción del TIMER1 para calcular el numero de desbores pero no se con que prioridad ubicarla.

    Saludos
    Jorge

    ResponderEliminar
  4. Hola muchas gracias y sobre todo felicitaciones por un proyecto tan bien explicado .
    me gustaria implementar el proyecto sin embargo tengo una enorme duda acerca de como se conectaria el circuito a la PC para observar la señal obtenida por el TSL235?

    ResponderEliminar