TODOPIC

FORO TÉCNICO => Foro Técnico => Mensaje iniciado por: Picuino en 03 de Enero de 2012, 13:22:29

Título: Precisión del oscilador de cuarzo
Publicado por: Picuino en 03 de Enero de 2012, 13:22:29
Hola a todos, aprovecho para desearles feliz año.

Estoy programando un frecuencímetro con un PIC18F2550 y me he encontrado con que el oscilador de cuarzo tiene una exactitud bastante mala.

Comparando el oscilador del pic con un reloj digital, el error es de 2.4 segundos en 3000 segundos (error de 750 ppm) y este error me parece exagerado.

El programa para contar segundos se basa en el timer0:

Código: [Seleccionar]
unsigned int sys_clk, sys_second;

/*******************************************************************
      INTERRUPT SERVICE ROUTINE
 *******************************************************************/
void isr_main(void);

#pragma code low_vector=0x18
void isr_low(void) {
   _asm GOTO isr_main  _endasm
}

#pragma interrupt isr_main
void isr_main(void) {
   if (INTCONbits.TMR0IF==1)  {   // TIMER0 overflow interrupt
        TMR0L -= 125;
        INTCONbits.TMR0IF = 0;
        sys_clk++;
        if (sys_clk == 375) {     // 1 SECOND EVERY 375*125*128 = 6000000 CYCLES
           sys_clk = 0;
           sys_second++;
        }
    }
}


/*******************************************************************
      MAIN PROGRAM
 *******************************************************************/
void main(void) {
   unsigned int old_second;

   //
   // CONFIGURE INTERRUPTS
   //
   INTCON = 0;          // All interrupts disabled
   INTCONbits.TMR0IE = 1;  // Enable TMR0 interrupt
   INTCONbits.PEIE = 1;    // Global Interrupt Enable. Enable all unmasked interrupts
   INTCONbits.GIE = 1;     // Global Interrupt Enable. Enable all unmasked interrupts
 
   //
   // CONFIGURE TIMER 0
   //
   T0CON =  (char)
           (0<<7)   // TMR0ON: Timer0 On/Off Control
         + (1<<6)   // T08BIT: 1 = Timer0 is configured as an 8-bit timer/counter
         + (0<<5)   // T0CS:   1 = Transition on T0CKI pin
         + (0<<4)   // T0SE:   1 = Increment on high-to-low transition on T0CKI pin
         + (0<<3)   // PSA:    1 = Timer0 prescaler is NOT assigned.
         + (0b110); // T0PS:   Prescale value 2^(T0PS+1)
               // 111 = 1:256 Prescale value
               // 110 = 1:128 Prescale value
               // 101 = 1:64 Prescale value
               // 100 = 1:32 Prescale value
               // 011 = 1:16 Prescale value
               // 010 = 1:8 Prescale value
               // 001 = 1:4 Prescale value
               // 000 = 1:2 Prescale value

   TMR0H = 0;
   TMR0L = -1;
   T0CONbits.TMR0ON = 1;

   //
   // MAIN ROUTINE
   //
   rs232_init();
   sys_clk = 0;
   sys_second = 0;
   
   while(1) {
      if (sys_second != old_second) {
          fprintf(_H_USART, "Second = %d\r\n", sys_second);
          old_second = sys_second;
      }
   };
}

Si el error fuese mayor, pensaría que es un error del programa; pero creo que es un problema del oscilador.

¿Es normal este error o debo pensar que hay un problema en el cristal?

Saludos.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: JBQ en 03 de Enero de 2012, 13:39:45
El datasheet del uc dice al respecto que no es recomendable usar el oscilador interno si se ha de trabajar con tiempos crìticos, incluso recomendia un cristal externo para el uso del USART. Pero incluso en estos casos, se le puede dar solución por medio el firmware.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: Picuino en 03 de Enero de 2012, 13:45:36
Estoy utilizando un cristal de cuarzo de 20MHz.

Esta frecuencia la paso a traves del PLL del PIC18F2550 y el divisor posterior para conseguir una frecuencia de trabajo de 24Mhz

¿Da problemas de precisión utilizar el PLL?

Saludos!
Título: Re: Precisión del oscilador de cuarzo
Publicado por: JBQ en 03 de Enero de 2012, 13:51:18
Aya, mmmm.... tecnicamente no deberìa darte ese tipos de problemas; debe haber un desfase... si, pero este no debe de ser tanto, y generalmente es cada 1 ò 10 horas, dependiendo de tu firmware. Parece que hay un bug ahi en tu firmware, como veo que estas programando en C, derepente una instrucciòn està haciendo una latencia demasiado larga. Como estas trabajando con el interrup hab, asegurate de actualizar de la forma apropiada el TMR0... saludos.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: Picuino en 03 de Enero de 2012, 15:36:26
Eso había pensado, que podía ser un error de software.

¿Alguien conoce un programa ya probado para 'afinar' o medir la frecuencia del oscilador de cuarzo?

Tengo un polímetro con frecuencímetro, pero si le conecto al cristal deja de oscilar.

Saludos!
Título: Re: Precisión del oscilador de cuarzo
Publicado por: MerLiNz en 03 de Enero de 2012, 15:56:53
el TMR0 pierde 2 ciclos cada escritura, luego algunos ciclos mas para el if(INTCON....

Lo normal es que los cristales tengan unos 100ppm de error como mucho y el error del PLL de tu pic es de 0.25%, o bien tu cristal es malo (alguno de mala fabricacion china) o algo hay de software que no te cuadra.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: Picuino en 03 de Enero de 2012, 19:44:15
Gracias,
Mirando el manual he visto que el prescaler se pone a cero cada vez que se escribe en el registro TMR0L. Esto hace perder bastantes pulsos (el prescaler está en 1/128) y el contador se retrasa.
El problema es que no se como solucionarlo.

Si dejo correr libre al TMR0 para que divida por 256, salen números decimales a la hora de contar segundos:

    Fosc = 24000000Hz = 6000000 pulsos/segundo

    F timer0 = 6000000/256 = 23437.5 ciclos/segundo

Saludos!
Título: Re: Precisión del oscilador de cuarzo
Publicado por: MerLiNz en 03 de Enero de 2012, 20:28:31
yo diria que no se pone a 0 eh? Me suena haber usado el timer0 en otras ocasiones para bajas frecuencias sin ese problema.

Bueno, el tema esta en que puedes usar otro timer, como estas usando variables para calcular los segundos te seria tan facil como un timer de 16bits

le cargas el valor de 28036 al timer. Y ahora cada 50ms tendras una interrupcion, 50ms*20=1segundo, para que te quede exacto deberias calcular el tiempo que tarda en volver a cargar el registro, asi le restas un par de Tcy y tendras un tiempo bastante exacto.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: Picuino en 04 de Enero de 2012, 06:08:46
He conseguido un adelanto de 2.1 segundos en 8 horas y media (70ppm)  :-/

El programa funciona así:
   Micro funcionando a 20Mhz (directamente del cuarzo)
   Prescaler al mínimo (1/2)
   No pongo a cero el Timer0 (como comentas) así que tengo 9765.625 interrupciones por segundo
   Cada 9766 interrupciones atraso el contador:  TMR0L += 96;     // 96 = 256 * 0.375

De esta forma, el programa puede perder uno o dos pulsos de un total de 5 millones cada segundo (error de 0.4 ppm)

(Utilizo el timer0 en modo 8 bit porque quiero que este mismo programa sirva también para otro micro más pequeño con menos timers)

Muchas gracias y saludos!
Título: Re: Precisión del oscilador de cuarzo
Publicado por: tapi8 en 04 de Enero de 2012, 10:19:01
Citar
Prescaler al mínimo (1/2)

Creo que puedes usarlo 1/1 sino tienes habilitado el WDT, le asignas el preescaler al WDT y ya esta.

Citar
(Utilizo el timer0 en modo 8 bit porque quiero que este mismo programa sirva también para otro micro más pequeño con menos timers)

El TMR1 es el mas preciso, toda la serie 16f lo lleva excepto el 16f84 (no se si hay alguna excepcion muy especial) y la serie 12f los modernos creo que lo traen todos. La 10f si que no lo se.

Título: Re: Precisión del oscilador de cuarzo
Publicado por: Picuino en 04 de Enero de 2012, 20:35:09
Tienes razón, se puede desabilitar el prescaler y es más preciso.

El timer1 lo quiero utilizar para contar pulsos de entrada (la temporización con el timer0 servirá después para hacer un frecuencímetro)
El timer0 quiero utilizarle en modo 8bit para que el programa valga para un 16F628 o un 16F88.


Por ahora programo un reloj para poder compararlo con otro reloj digital y medir el error del cuarzo. Luego compenso el error por software.

Al final he conseguido un error muy bueno (menos de 10ppm):

Código: [Seleccionar]
/****************************************************************************
   SECOND COUNTER
 ****************************************************************************/
#include <p18cxxx.h>
#include <stdio.h>
#include <string.h>

/****************************************************************************
      GLOBAL VARIABLES AND DEFINITIONS
 ****************************************************************************/
#define FOSC            20000000
#define BAUD 57600
#define FOSC_ERROR_PPM  70                 
#define FOSC_REAL       (FOSC + FOSC_ERROR_PPM*(FOSC/1000000))
#define TMR0_COUNT      (FOSC_REAL/(4*256)+1)            // Number of carrys per second
#define TMR0_OFFSET     (TMR0_COUNT*256-(FOSC_REAL/4))   // TMR0L preset every second

unsigned int sys_clk, seconds, old_seconds;


/****************************************************************************
      INTERRUPT SERVICE ROUTINE
 ****************************************************************************/
// Rutinas de Interrupcion.-

#pragma interrupt isr_main
void isr_main(void) {
   if (INTCONbits.TMR0IF==1)  {        // if TIMER0 overflow interrupt
      INTCONbits.TMR0IF = 0;           // Clear Timer0 interrupt flag
      sys_clk--;
      if (sys_clk == 0) {
         TMR0L += TMR0_OFFSET + 3;
         sys_clk = TMR0_COUNT;
         seconds++;           
      }
   }
}

#pragma code high_vector=0x08
void isr_high(void) {
   _asm GOTO isr_main  _endasm
}


/****************************************************************************
      MAIN PROGRAM
 ****************************************************************************/
#pragma code

/*
   MAIN ROUTINE
*/
void main(void) {
   // Configure Timer0
   sys_clk = 1;
   seconds = 0;
   T0CON = 0b11001000;     // Timer0 ON, 8bit count, No prescaler
   INTCON = 0b11100000;    // Global Interrupt, Peripheral Interrupt, TMR0 interrupt


   // Configure UART
   BAUDCONbits.BRG16 = 0;        // BRG16: 16-Bit Baud Rate Register Enable bit
   SPBRGH = 0;
   SPBRG = (FOSC/(16*BAUD))-1;   // Configure UART speed
   TXSTA = 0b00100110;
   RCSTA = 0b10010000;
   TRISCbits.TRISC6 = 0;         // Enable TX output
   PIE1bits.TXIE = 0;            // Disable RS232 interrupts
 
   // Main loop
   old_seconds = -1;
   while(1) {
      if (seconds != old_seconds) {
         old_seconds = seconds;
         fprintf(_H_USART, "Seconds=%u\r\n", seconds);
      }
      if (seconds == 60000)
         seconds = 0;
   }
}

Saludos!
Título: Re: Precisión del oscilador de cuarzo
Publicado por: SavageChicken en 05 de Enero de 2012, 14:10:11
Para realmente poder medir el error del cristal deberías trabajar en assembler. De esa forma sabrás exáctamente cuantas instrucciones realiza el PIC en cada bucle, y de esa manera tener un valor exacto del tiempo de oscilación del cristal. Todos los programas compiladores agregan instrucciones entre bucle y bucle, y generalmente esas instrucciones no tenidas en cuenta estan incrementando el error.

Salud.-  8)
Título: Re: Precisión del oscilador de cuarzo
Publicado por: JBQ en 05 de Enero de 2012, 18:55:55
El disassembly listing puede ayudar en estos casos.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: SavageChicken en 06 de Enero de 2012, 13:46:33
El disassembly listing puede ayudar en estos casos.

Efectivamente, esa sería una buena forma de contar las instrucciones si utilizas un compilador.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: reiniertl en 06 de Enero de 2012, 14:08:55
TIMER0, mala elección para el frecuencímetro.

Mi sugerencia es utilizar un módulo CCP+TIMER1 y TIMER1 con oscilador externo y de precisión para el mismo. Ya con eso te evitarás una muy buena parte de los problemas.

El módulo CCP en modo captura te permitirá analizar señales de baja frecuencia y también las de alta solamente con configurar el valor de comparación para activar la interrupción cuando la captura coincida con el valor de comparación deseado. Esto es algo relativamente de lograr con un PIC si se utilizan los periféricos apropiados.

Reconozco que todo mundo tira al pobre TIMER0 este tipo de aplicaciones cuando lo correcto es utilizar el hardware que viene con el PIC para ello.

Espero que esta sugerencia te ayude a resolver tu problema.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: tapi8 en 06 de Enero de 2012, 15:49:41
Citar
TIMER0, mala elección para el frecuencímetro.

Hombre el TMR0 da problemas cuando hay que contar tiempos grandes y usar preescaler altos, pero si son tiempos que se pueden contar con preescaler 1/1 considero que es bastante eficiente.

Hacer la rutina directamente en assembler puede ser una buena solucion.

Citar
Al final he conseguido un error muy bueno (menos de 10ppm):

Aunque Picuino dice que ya tiene lo que quiere.
Título: Re: Precisión del oscilador de cuarzo
Publicado por: Picuino en 07 de Enero de 2012, 21:16:12
He conseguido que el timer0 cuente correctamente sumandole 3 cada vez que modifico TMR0L (aunque el manual dice que se pierden sólo 2 ciclos.)
La rutina final para contar tiempo es tan precisa como lo sea el cristal de cuarzo

Esta rutina de interrupción cuenta centésimas de segundo de forma exacta:

Código: [Seleccionar]
#define FOSC            20000000
#define TMR0_COUNT      (FOSC/((unsigned long)4*250*100))

static unsigned char sys_clk, cent_second;

void isr_main(void) {
   if (INTCONbits.TMR0IF==1)  {        // if TIMER0 overflow interrupt
      INTCONbits.TMR0IF = 0;           // Clear Timer0 interrupt flag
      TMR0L += 6+3;
      sys_clk--;
      if (sys_clk==0) {
         sys_clk = TMR0_COUNT;
         cent_second++;
      }
   }
}

Para ajustar la frecuencia del cristal de cuarzo de la placa PIC he añadido una capacidad extra al cuarzo. El procedimiento completo es un poco complicado y utiliza un reloj casio (10ppm o 1 segundo/dia) y un generador de señal digital/contador (http://www.elecfreaks.com/store/dds-signal-generatorsg100x-p-90.html).

Al final no he necesitado código máquina y funciona muy bien.

Saludos!
Título: Re: Precisión del oscilador de cuarzo
Publicado por: Picuino en 16 de Enero de 2012, 20:40:10
Se me olvidó poner en el ejemplo anteriór la configuración inicial del timer0:
Código: [Seleccionar]
   INTCON = 0b11100000;  // Enable interrupts
   T0CON =  0b01001000;
   sys_clk = TMR0_COUNT;
   TMR0L = 6;
   cent_second = 0;


TIMER0, mala elección para el frecuencímetro.

Mi sugerencia es utilizar un módulo CCP+TIMER1 y TIMER1 con oscilador externo y de precisión para el mismo. Ya con eso te evitarás una muy buena parte de los problemas.

El módulo CCP en modo captura te permitirá analizar señales de baja frecuencia y también las de alta solamente con configurar el valor de comparación para activar la interrupción cuando la captura coincida con el valor de comparación deseado. Esto es algo relativamente de lograr con un PIC si se utilizan los periféricos apropiados.

Reconozco que todo mundo tira al pobre TIMER0 este tipo de aplicaciones cuando lo correcto es utilizar el hardware que viene con el PIC para ello.

Espero que esta sugerencia te ayude a resolver tu problema.

Gracias por tu sugerencia. De hecho, ahora estoy intentando realizar el frecuencímetro con el TIMER2 (CCP) como contador de tiempo.
Lo que si he descubierto por ahora es que el TIMER1 cuenta pulsos fenomenal (es capaz de contar hasta 80Mhz sin prescaler en un 18F2550)


Saludos!
Título: Re: Precisión del oscilador de cuarzo
Publicado por: reiniertl en 19 de Enero de 2012, 11:18:39
Citar
Lo que si he descubierto por ahora es que el TIMER1 cuenta pulsos fenomenal

Es porque este timer con los módulos CPP han sido diseñados para eso mismo. Es muy común intentar utilizar el TIMER0 para contar y comparar porque si miras al TIMER0 tiene un esquema muy simple y pocos bits que tocar para configurarlo, pero no es la mejor recomendación. En cambio los TIMERS acoplados a módulos de captura y comparación son mucho más eficaces y eficientes.

Mi recomendación un poco insistente y a veces hasta molesta para algunos es que si quieren contar y comparar o medir frecuencia o período es que utilicen esta combinación. Es más complejo de entender el hw, pero vale la pena cuando una vez que cambias unos pocos bits se hace la magia. En este foro hemos tenido gente que se ha pasado meses tratando de hacer eso con TIMER0 y siempre salta algún resultado inesperado cuando agregan un fragmento de software en otro lado y se afecta su rutina de conteo/medición.

Veo que ya vas por el buen camino entonces y que pronto estarás haciendo maravillas.

Un saludo
Reinier