Translate

miércoles, 3 de julio de 2013

Modo de Captura en el módulo CCP

En una entrada anterior (PWM) vimos una de las funcionalidades del módulo CCP. En esta entrada describiremos otra de sus funcionalidades, el modo CAPTURE.

Como su nombre indica, este modo se usa para capturar eventos. Los eventos serán bajadas o subidas de los pines correspondientes a CCP1 y CCP2 que son, respectivamente, RC2 y RC1 (este último puede cambiarse por RB3 en la configuración inicial).  Para la captura se precisa que un timer este corriendo. Este timer será nuestro "reloj": capturar un evento será grabar la "hora" (contador del timer) en que se ha producido. Los timers usados con el módulo de CAPTURA pueden ser tanto el TMR1 como el TMR3.

Podríamos conseguir similares resultados usando las interrupciones INTx que vimos en los codificadores en cuadratura. Cada vez que entre la interrupción podríamos guardar el valor de un timer que este corriendo. La diferencia es que con este enfoque, para cuando "miremos la hora" ya habrá pasado un tiempo (mientras el micro termina lo que esté haciendo, pasa el control a la interrupción, etc.) que puede ser del orden de microsegundos.

Por el contrario, si se usa el método de captura, en cuanto se produce el evento se copia el valor del timer usado a los registros CCPRxH:CCPRxL. Por supuesto, será nuestra responsabilidad guardar dicho valor en otra variable antes de que se repita el evento y se machaque con otro valor.

En la entrada explicaremos la configuración del modo de CAPTURA y los registros asociados. En la siguiente entrada usaremos el modo de CAPTURA para leer los datos de un sensor de luz (TSL235) cuya salida en una onda cuadrada de frecuencia variable. 

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


Modo de Captura del módulo CCP:

Como siempre, la mejor explicación la encontraréis en los datasheets de Microchip. Aquí presentaré un rápido resumen.

El modo de Captura es una de las tres funciones posibles que puede desempeñar cada módulo CCP (Capture-Compare-PWM) del PIC. Normalmente hay 2 módulos CCP1 y CCP2 con pines asociados RC2 y RC1 (notad la inversión en la asignación de pines). Es posible dedicar cada módulo CCPx a una función distinta. Uno podría estar en modo CAPTURA (usando TMR1) y el otro en modo PWM (usando TMR2). Incluso usando ambos en modo CAPTURA podríamos usar una base de tiempos distinta en cada módulo.

La siguiente tabla del datasheet muestra las posibilidades de combinación:


Cada uno de los módulos CCP tiene asociados varios registros (cambiar la x en lo sucesivo por 1 o 2):

CCPxCON: con este registro definiremos el tipo de operación (Capture / Compare / PWM) del módulo. El valor para seleccionar modo de CAPTURA es: 

0b 000001xx
                 
Los dos últimos bits configuran el tipo de evento a capturar:

·  00: capturar cada caída de la línea.
·  01: capturar cada subida de la línea.
·  10: capturar cada 4ª subida de línea.
·  11: capturar cada 16º subida de línea.

CCPRxH,CCPRxL: dos registros de 8 bits. En el modo captura guardan el valor de un timer (TMR0 o TMR3) corriendo en modo 16 bits en el momento de producirse el evento.

En el caso del modo captura, también tendremos que configurar ciertos bits especiales del registro del timer TMR3 (T3CON) que seleccionan que timer se asocia al módulo de captura.  Es posible configurar ambos módulos en modo captura y aún así usar timers distintos (TMR1 o TMR3) en cada uno de ellos (al contrario que lo que vimos en modo PWM, donde los dos módulos compartían el mismo timer (TMR2).

  • T3CON.T3CCP2 : bit 6 de T3CON
  • T3CON.T3CCP1 : bit 3 de T3CON
Notad que ambos bits no están consecutivos en T3CON. Valores posibles para estos bits:

1x --> TMR3 para ambos módulos
01 --> TMR3 usado en CCP2, TMR1 usado en CCP1
00 --> TMR1 usado en ambos módulos.

Además de los registros anteriores el modo CAPTURE tiene definida una interrupción, que salta (si esta habilitada) cuando se produzca un evento.  Esta interrupción se usa a menudo porque lo que se suele querer medir es la separación entre dos eventos (periodo, ancho de un pulso, etc.). Si no hacemos nada, al suceder el segundo evento, el PIC sobre-escribirá el tiempo del primero. La interrupción nos permite guardar el primer tiempo antes de ser sobrescrito.

Si los eventos se suceden muy rápido (p.e. del orden de 1 usec) podría ser que la interrupción no llegue a tiempo de guardar el 1er valor. En ese caso podemos programar un prescaler (modos 10 o 11 en CCPCON) para definir el evento como 1 de cada 4 o 1 de cada 16. De esta forma también mejoraríamos la calidad de la medida al promediar varios periodos.


Inicialización módulo CCP y timer asociado

Veamos un ejemplo de los pasos a realizar para usar el modo CAPTURE. Por ejemplo, para usar CCP1 con TMR3 como timer asociado, debemos:
  1. Configurar el timer a usar (TMR3) en modo 16 bits, con el prescaler escogido, definiendo así la base de tiempos a usar.
  2. Arrancar el timer (TMR3) a usar. 
  3. Poner los bits T3CCP2 y T3CCP1 de T3CON a 1 para seleccionar el uso de TMR3 como timer asociado a ambos módulos CCP.
  4. Declarar el pin correspondiente (en este caso RC2, asociado a CCP1) como entrada.
  5. Habilitar el módulo CCP1 en modo CAPTURE con la definición de evento que se desee (1x subida, 1x caída, x4, x16)
  6. Si vamos a usar la interrupción de CCP1, habilitarla y declararla de alta prioridad (aconsejable pues no queremos "saltarnos" un evento).
Veamos el código para llevar a cabo los pasos anteriores.

El timer se pondrá en marcha en modo 16 bits y se dejará en modo "free-running" sin interferencia alguna (no tocaremos el contador del TMR3). En el proyecto estoy usando un cristal de 8 MHz por lo que el ciclo de instrucción es de ½ microsegundo. Por comodidad usare un PRESCALER 1:2 en el timer TMR3, de forma que cada incremento de su contador representará 1 microsegundo. Sería fácil configurar T3CON por nuestra cuenta, pero en este ejemplo usaremos las funciones de C18:

// Starts TMR3 using OSC as source, 16bit mode,
// with 1:2 prescaler (1 usec @ 8 MHz) OpenTimer3(TIMER_INT_OFF&T3_16BIT_RW&T3_SOURCE_INT&T3_PS_1_2&T3_SOURCE_CCP);
// TMR3 as source for both CCP1/CCP2
 T3CONbits.T3CCP2=1; T3CONbits.T3CCP1=1;  

Aquí creo que hay un bug en C18. Según la documentación hay una máscara (T3_SOURCE_CCP) que si se añadiese al argumento de  OpenTimer3, pondría los valores adecuados en los bits T3CCP1 y T3CCP2 de forma que TMR3 fuese el timer a usar por ambos módulos. Sin embargo, cuando la uso no se ponen los bits a su valor correcto (1). Es por esto por lo que necesito ponerlos  a 1 "manualmente" en la siguiente línea.

Para que el módulo CCP1 funcione correctamente y detecte los  eventos es necesario que su pin asociado (RC2) esté declarado como una entrada: 

TRISCbits.TRISC2=1; // RC2 as input 

Ahora vamos a configurar el módulo CCP1 en modo CAPTURA. Por ejemplo, si deseamos configurar CCP1 en modo 4X (un evento es la llegada de 4º pulsos o más específicamente, la llegada de la 4º subida) basta hacer:

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

Para hacer el programa más legible podríamos usar la correspondiente rutina de C18 (en este caso tendríamos que incluir fichero capture.h):

// CCP1CON=0b00000111 Modo CAPTURE. Event = x16 rising edges
 OpenCapture1(CAPTURE_INT_OFF,C1_EVERY_4_RISE_EDGE);

Finalmente habilitaríamos la interrupción del CCP (alta prioridad):

 enable_priority_levels; 
 enable_CCP1_int; set_CCP1_high;
 enable_high_ints; enable_low_ints;



Ese sería el código de inicialización en el programa principal. Como hemos habilitado la interrupción CCP1 debemos escribir una ISR que maneje dicha interrupción.  Obviamente lo que hagamos en esa interrupción dependerá de la aplicación en la que estemos pensando. Sin embargo, en muchas ocasiones, lo que si querremos hacer es guardar el tiempo (registros CCPR1H:CCPR1L) del evento que ha provocado la interrupción y calcular la separación con el evento anterior. Veamos como escribir una sencilla ISR para conseguir esos objetivos:


uint16 t0=0;
uint16 dt=0;

// High priority interruption
#pragma interrupt high_ISR
void high_ISR (void)
{
 uint16 t;
 if (CCP1_flag)
  {
   t=CCPR1H; t<<=8; t+=CCPR1L;    // Read CCPR1 (time of event that just happened)
   dt = (t-t0);  // Interval between events 
   t0 = t;  // Keep latest time in t0
   CCP1_flag=0;
  }
}

// Code @ 0x0008 -> Jump to ISR for high priority ints
#pragma code high_vector = 0x0008
  void high_interrupt (void){_asm goto high_ISR _endasm}
#pragma code


El código es muy sencillo. Hay dos variables externas t0 y dt de tipo uint16. La ISR lee el momento del último evento (de CCPR1H:CCPR1L) y lo salva en t. Calcula el intervalo con el último evento (t-t0) y lo guarda en dt. Después actualiza t0 (último evento) con el valor de t.  

De esta forma en t0 guarda el momento del último evento y en dt tenemos siempre disponible el valor más reciente  de la separación de eventos. 

La limitación de este enfoque es que si la separación entre eventos supera los 65536 "clocks" del timer TMR3 va a haber un rollover y a la resta (t-t0) le faltará añadirle 65536, 65336x2, etc.

Una solución sería habilitar la interrupción del TMR3 y llevar la cuenta del número de rebosamientos que tienen lugar entre un evento y el siguiente. En ese caso el intervalo entre eventos sería:

                       # rebosamientos x 65536  + dt

En este caso no lo hemos implementado, por lo que la separación entre eventos no debería exceder los 65536 microsegundos. 


 Bueno, pues eso es todo lo que necesitabamos saber para usar el modo CAPTURA.  En la siguiente entrada vamos a ver como usarlo en una aplicación real, para medir el periodo de un sensor que convierte luz recibida en una frecuencia de salida.


6 comentarios:

  1. Muchas gracias amigo Antonio, por usar tu tiempo y esfuerzo para enseñarnos.

    ResponderEliminar
  2. MUCHAS GRACIAS, ME HA SIDO DE GRAN AYUDA. OJALÁ PUEDAS HACER LOS TUTORIALES EN MIKROC Y ENSEÑAR A USAR COMUNICACIÓN USB. GRACIAS

    ResponderEliminar
  3. Muy buena explicacioń, gracias mil

    ResponderEliminar
  4. Excelente muy buena explicación, saludos y gracias.

    ResponderEliminar
  5. Excelente muy buena explicación, saludos y gracias.

    ResponderEliminar
  6. Exelente Tutorial tiene buenos detalles

    ResponderEliminar