Autor Tema: timer1  (Leído 4465 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado cvargcal

  • PIC16
  • ***
  • Mensajes: 166
timer1
« en: 19 de Febrero de 2017, 17:25:24 »
Saludos, tengo un problema.... estoy calculado el timer1 así:

Código: C
  1. #include <18F45K22.h>
  2. #device ADC=10
  3. #Device PASS_STRINGS=IN_RAM  
  4. #ZERO_RAM                                                    // Variables en memoria
  5. #define FASTER_BUT_MORE_ROM                                  // Variables en memoria
  6. //#fuses HSH                                                 // High speed Osc, high power 16MHz-25MHz
  7. #fuses HSM                                                   // High speed Osc, medium power 4MHz-16MHz
  8. #fuses  BROWNOUT, NOPBADEN, NOHFOFST                         //
  9. #fuses NOWDT                                                 // No Watch Dog Timer
  10. #fuses PUT                                                   // Power Up Timer
  11. #fuses MCLR                                                  // Master Clear pin enabled
  12. #fuses PROTECT                                               // Code protected from reads
  13. #fuses PLLEN                                                 // HW PLL enabled    16MH* 4 =64M
  14. #use delay(clock=64MHz)


Código: C
  1. #int_TIMER1                                              // Interrupción
  2. void TIMER1_isr(void){                                   // Función de la interrupción    
  3.    cont++;                                               // Contador  
  4.  
  5.    if(cont == 30){                                       // 0.032768*30=0.98304 segundos
  6.       seg++; cont=0;                                     // Bandera para un segundo
  7.       if (seg==60){                                      // 1 minuto, se reincia timer
  8.          min++;seg=0;                                    //                              
  9.       }                                                  //
  10.    }                                                     //
  11.   set_timer1 (0x00);                                     // Carga del TMR1  0.032768=4/64000000*8(65536-x),x=0 >> desbordamiento es 0.0425s
  12. }
  13.  
  14.  
  15. main(void){
  16.    setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);               //  32,768 ms overflow (0.032768 s), Setup timer: Reloj interno, preescaler= 8  
  17.    set_timer1 (0x00);                                    //  Carga del TMR1  0.032768=4/64000000*8(65536-x),x=0 >> desbordamiento es 0.0425 ss
}

se supone que cuando cuente 1 minuto en main, disparo un evento. El problema es que no siempre cuenta  los 60s.
aveces funciona bien, pero otras veces no.... si dejo el pic un largo tiempo encendido, el contador se descalibra o no sé lo que pasa. pero si uso lo mismo para un cristal de 12M (cambiando el desbordamiento) si funciona bien.

Este pic tiene un PLLx4, osea que quiero usar 16M*4 =64, pero no se, si  ahí esta el problema.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:timer1
« Respuesta #1 en: 23 de Febrero de 2017, 15:06:18 »
Supuestamente ese intergado puede tomar ambos, es decir Fosc y Fosc/4

A 64Mhz quiere decir que cada ciclo es de:

Fosc : 15.625ns
Fosc/4 : 62.5ns

Supongamos por un momento que es Fosc/4 ( el T1_INTERNAL ese )

Con preescaler en 8 queda en 500ns cada cuenta.
Si ponemos el timer a 0.. tenemos que se produce el overflow luego de 65536 cuentas o 32.768ms
En tu codigo contas 30 veces esto para saber que paso 1 minuto, pero deberias contar 1831.05688 veces eso.. o...
Podemos hacer 1831 veces de 65536 y 1 de de 3584, es decir cargar en los registros del timer 1831 veces 0 y una de 61952

Como ves estas muy lejos de tu "minuto"

Tambien existe la posibilidad, que como cada cuenta es de 500ns, podrias usar un numero entero como 40000 ( es decir cargarlo con 25536 ) de esa forma cada interrupcion es de: 20ms
Y solo contas la cantidad de interrupciones. que serian 60s / 20ms = 3000

Ahora con el codigo..
Cuando entra a la interrupcion ya tenes un exceso de ciclos en tu cuenta, que se van acumulando poco a poco (debido a la latencia de interrupcion). Para minimizar esto, debes poner el valor en el timer lo antes posible. y luego si hacer todas las operaciones que quieras. En tu codigo haria que el set_timer seria lo primero a poner.

Si aun asi esa latencia representa un error grande, deberias empezar a mirar de usar el modulo CCP. En modo Compare. Que hace que se resetee por si solo el timer por hardware. Disminuyendo el error por latencia.

Desconectado cvargcal

  • PIC16
  • ***
  • Mensajes: 166
Re:timer1
« Respuesta #2 en: 23 de Febrero de 2017, 23:01:09 »
.....

Genial,  muchas gracias por tu explicación.... Tu explicas cosas que nunca enseñan en la universidad. ((:-))
Pero curiosamente puse esto:
Código: C
  1. use delay(clock=64MHz,crystal=16MHz)     //  PLLx4

Estoy reportando una trama de datos por  GPRS a  un servidor. y eso lo hago cada 1 minuto... este es el log que guardo en el servidor.
(quito la trama, porque es muy larga),  pero segun el log,  veo que  si funciona el timer2.

21/02/2017 06:41:21 p.m. ****
21/02/2017 06:42:20 p.m. ****
21/02/2017 06:43:19 p.m. ****
21/02/2017 06:44:18 p.m.  ***
21/02/2017 06:45:17 p.m. *****
21/02/2017 06:46:16 p.m. *****
21/02/2017 06:47:15 p.m. *****
21/02/2017 06:48:14 p.m. *****
21/02/2017 06:49:12 p.m. *****
21/02/2017 06:50:11 p.m. *****
21/02/2017 06:51:10 p.m. *****

Se que la teoría puede con todo, pero... ni idea que esta pasando, el caso es que ya me funciona. Jejjejej


Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:timer1
« Respuesta #3 en: 24 de Febrero de 2017, 15:38:06 »
A 64Mhz es imposible tu codigo. Tal ves se esten cargando otros valores, ademas me decis que lo haces con el Timer2 y esto va por el Timer1. El Timer2 posee otras opciones, preescaler, postscaler y si no me equivoco tiene otro postscaler para la interrupcion.

Desconectado cvargcal

  • PIC16
  • ***
  • Mensajes: 166
Re:timer1
« Respuesta #4 en: 24 de Febrero de 2017, 17:33:02 »
....
Siii, si es el timer1... jejej  :? que raro... pero funciona, ya lleva 2 semanas corriendo el pic sin apagarse... :shock:  (Me asusta la idea de que falle, porque ese circuito ya  no la tengo a la mano)
¿Cómo seria el conteo correcto de ese minuto?
« Última modificación: 24 de Febrero de 2017, 17:38:26 por cvargcal »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:timer1
« Respuesta #5 en: 25 de Febrero de 2017, 11:27:48 »
Acabo de ver que fue un error mio. Las 1800 vueltas las tenes casi si haces 30 * 60 = 1800

Volvamos a calcular de nuevo

Fosc = 64Mhz

Fosc/4 = 16Mhz

Pasando el Preescaler que es de 1:8 = 2Mhz Entrando al TMR1
Es decir cada cuenta es de 0.5us o 500ns

Hasta acá estamos igual que antes. Ahora a calcular tu problema.

Tu problema:

Las instrucciones antes del set_timer, como te dije el poner el set_timer antes implica que se cargue rapidamente el valor del TMR1 y no deba pasar por tdas las instrucciones, suponete que apenas entra a la interrupcion el timer1 queda en 0x00 ya que se produjo el overflow, luego de varias instrucciones al final queda en 0x02 y vos lo pones a 0 de vuelta, esos 2 conteos implican un error que va a ocurrir cada ves que entre a la interrupcion. Si decis que entra 30 veces por cada segundo, y son 60 segundos, quiere decir que estaria contando 30*60*2 = 3600 , esos 2 son lo que conto y que sobreescribimos de mas. Se entiende que es un error acumulativo?

Podriamos agarrar y omitir el "set_timer1", total al momento de dispararse la interrupcion es por que paso de 0xFFFF a 0x0000 el TMR1, por lo tanto ya esta en 0 y no deberiamos hacerlo. Aca solucionamos el problema anterior pero tenemos otro problema. Como ves en tus calculos tenes que:

Citar
0.032768*30=0.98304 segundos

Son 0.98 segundos y no 1 segundo exacto. Esto que ocurra en los 60 segundos te da que 60 * 0.98304 = 58.9824s , es decir todavia le falta aproximadamente 1 segundo mas. Y si por ejemplo le pones 61 vueltas en ves de 60 tendrias 59.96544s y pensarias que tendrias todo perfecto, pero ocurriria por ejemplo que a la media hora de corriendo tu codigo retrasa un segundo la toma de datos... es decir verias algo asi:

21/02/2017 06:21:20 p.m. ****
21/02/2017 06:22:20 p.m. ****
21/02/2017 06:23:10 p.m. ****
.....
.....
21/02/2017 06:51:19 p.m. *****
21/02/2017 06:52:19 p.m. *****
21/02/2017 06:53:19 p.m. *****


Una solucion seria hacer 62 vueltas en ves de 61 como dijimos antes. Solo que la de 62 cubra esa diferencia y no comienza el timer desde 0. Suponete:

Código: C
  1. #int_TIMER1                                              // Interrupción
  2. void TIMER1_isr(void){                                   // Función de la interrupción    
  3.    cont++;                                               // Contador  
  4.    if(cont == 30){                                       // 0.032768*30=0.98304 segundos
  5.       seg++; cont=0;                                     // Bandera para un segundo
  6.       if(seg = 62) {
  7.            set_timer1(63231);
  8.            seg = 0;
  9.            min++;
  10.       }
  11.    }                                                     //
  12. }

El calculo es .. Tenemos que suplir 60s - 59.96544s = 0.03456 es decir debe contar , pero antes de entrar al if de esos 62, ocurre que tenes que pasar por 30 antes, entonces 0.03456s / 30 = 1.152ms
A 0.5us cada cuenta  -> 2304 a contar, es decir cargar el timer con 63232, ese ultimo valor, podrias hacerlo con 1 mas por las instrucciones anteriores.

Ahora porque complicarse con 2 variables si podemos usar una sola?

int16 cont = 0;

Código: C
  1. #int_TIMER1                                              // Interrupción
  2. void TIMER1_isr(void){                                   // Función de la interrupción    
  3.    set_timer1(25536);
  4.    cont++;                                               // Contador  
  5.    if(cont == 3000){                                       // 0.032768*30=0.98304 segundos
  6.            cont = 0;
  7.            min++;
  8.     }
  9. }

Esta ves cuento 40.000 en cada interrupcion del timer es decir 20ms, a los 3000 interrupciones son 20ms*3000 = 60s
Como ves esto me permite con una sola variable tener todo y ser mas simple. Ademas como la interrupcion va a ser un poco mas exacta no tengo que hacer esa ultima vuelta con un timer diferente.
Problemas con esto? Si tendrias el mismo problema que con el anterior, hasta que entre a la interrupcion se perderian algunos ciclos, Y se tornaria en un error acumulable que haria que adelante 1 segundo cada 1 hora o mas tal ves,

Si queres ser mas exacto deberias usar el CCP. Lo configurarias en modo COMPARE; que se auto reinicie el TIMER1, y que ocurra la interrupcion a los 40.000, Asi al menos tendrias solo un error, ya que no se acumularia. Tendrias el error desde que se produce la interrupcion hasta que haces tus cosas en la interrupcion y fue.

Desconectado cvargcal

  • PIC16
  • ***
  • Mensajes: 166
Re:timer1
« Respuesta #6 en: 25 de Febrero de 2017, 17:04:59 »
....

Oww muchas gracias por explicación  ((:-)) , bueno, cuento 60 segundos, para ir contando minutos, y luego en determinado momento jugar con la variable minuto, lo que busco es que ese tiempo sea programable en cualquier momento desde el servidor,  tampoco necesito exactitud. Pero, voy a seguir usando tu idea.
Una pregunta mas:
Tengo entendido que con esto se carga el desbordamiento (osea cada cuanto tiempo quiero que suceda la interrupción):
Carga del TMR1  0.032768=4/64000000*8(65536-x),    entonces x=0 >> desbordamiento es 0.032768
para luego usar:
set_timer1 (0x00);   

0.032768 =  32.768ms ese dato lo obtengo de un programa que hizo redpic y esta aquí en el foro. Supongo que eso viene en el manual del pic e incluso  CCS en el wizard lo configura.

¿Entonces dime por favor de donde sacas el 63232  y 25536? :mrgreen:
« Última modificación: 25 de Febrero de 2017, 17:09:44 por cvargcal »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:timer1
« Respuesta #7 en: 25 de Febrero de 2017, 18:51:39 »
Como el Timer es ascendente (va sumando 1 en 1 ) y la interrupcion ocurre cuando pasa de 0xFFFF a 0x0000. Entonces tenes que cargar el complemento del numero, o que es lo mismo tenes que cargar lo que le falta para llegar a 65536...

Entonces si yo necesito CONTAR 2304, , debo cargarle el valor 65536-2304 = 63232

Lo mismo ocurre con el otro valor, cuando necesito contaro 40.000 quiere decir que debo cargar en el timer 65536 - 40000 = 25536

Se podria poner de otra forma la formula, para que veas que vos tambien la usas.

Código: [Seleccionar]
Cant.DeValores - LoQueNecesitoContar = LoQueTengoQueCargar
Ej ( 65536 - 2304 = 63232)

Podemos pasar terminos e intentar saber cuanto voy a contar... es decir:

Código: [Seleccionar]
CantDeValores - LoQueTengoQueCargar = LoQueNecesitoContar
Esta ultima formula la usaste vos en la que me pasaste. En la ecuacion que me pasaste:

4/64000000*8(65536-x)

Si descomponemos esa formula queda:

Código: [Seleccionar]
(4 / Fosc)  * Preescaler * LoQueNecesitoContar
o que es igual a:

Código: [Seleccionar]
(4 / Fosc)  * Preescaler * (CantDeValores - LoQueTengoQueCargar)

x = LoQueTengoQueCargar

En un ejemplo numerico:
(4/64000000) * 8 * (65536-25536)
(4/64000000) * 8 * 40000

(4/64000000) = Tosc = Periodo de la señal de entrada ANTES del preescaler
(4/64000000) * 8 = Periodo de la señal a la entrada del Timer1, o basicamente cada cuanto TIEMPO el timer suma 1.

Si tengo cada cuanto suma 1, y quiero saber cuanto tiempo pasaria hasta contar 40.000, lo multiplico y me daria los 0.5us, en este caso. Pero como observas debo cargar 25.536 en este caso para poder contar 40.000

Desconectado cvargcal

  • PIC16
  • ***
  • Mensajes: 166
Re:timer1
« Respuesta #8 en: 25 de Febrero de 2017, 19:22:03 »
....

:-/ :-/ muchas gracias, excelente explicación, ahora si tengo todo claro :)

Desconectado jorge luis

  • PIC10
  • *
  • Mensajes: 13
Re:timer1
« Respuesta #9 en: 01 de Diciembre de 2017, 01:38:10 »
Hola, necesito su ayuda, estoy intentando calcular la frecuencia de una señal capturando los pulsos de cada flanco de bajada por interrupción externa del RB0 sumandolo y por cada 3 interrupciones del Timer1 muestro la cantidad de pulsos, sin embargo parece que no logra entrar a las interrupciones. Cuando quito la interrupcion del Timer1 y dejo solo la interrupcion externa por RB0 la cuenta se da de forma correcta, pero al agregar las lineas de configuracion del Timer1 sigue con el problema. Adjunto el codigo en ccs 5.070 y proteus 8.6


 

anything