TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: Trev en 02 de Octubre de 2009, 19:53:10

Título: Como configurar el timer 1 en modo contador
Publicado por: Trev en 02 de Octubre de 2009, 19:53:10
Hola,queria saber si alguein me puede dar una mano de como configurar el timer1 en modo contador,lo que quiero hacer es un reloj usando un cristal externo de 32.768 Hz pero no se como conectar este y no me queda claro que instruccion usar si
setup_timer_1(T1_CLK_OUT I T1_DIV_BY_1) o T1_EXTERNAL y la ecuacion del timer no me queda no la recuerdo bien
no se si era asi:
T=(65536-T1INICIAL)xToscXPREESCALER donde Tosc seria de 1/32768 Hz,estoy lejos de mi casa y me olvide toda la documentacion estoy en un ciber si alguien me ayuda se lo agradeceré.Gracias.
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: MLO__ en 03 de Octubre de 2009, 01:06:48
Hola.

El código seria mas o menos así:

Código: C#
  1. #int_TIMER1
  2. void Reloj_isr(void)
  3. {
  4.  //Aqui tu código
  5.  set_timer1(32768);
  6. }
  7.  
  8. ...
  9.  
  10. void main (void)
  11. {
  12.  ..
  13.  setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1|T1_CLK_OUT);
  14.  enable_interrupts(INT_TIMER1);
  15.  enable_interrupts(GLOBAL);
  16.  set_timer1(32768);
  17.  ...
  18. }

No te olvides de colocar los condensadores a tierra para el cristal.

Saludos
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Suky en 03 de Octubre de 2009, 01:54:05
MLO_ si configura el timer en preescaler 2 ya no necesita recargar el timer con 32768 y será mucho más preciso.   ;-) <<-- Error de cálculos  :mrgreen:

Edit: Se podría combinar el timer con el módulo CCP en modo captura y evento especial (Todo en conjunto funcionaria de la manera que trabaja el timer2 con PR2) para generar la interrupción automática sin recarga del timer logrando mayor precisión.-

Saludos!
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: MLO__ en 03 de Octubre de 2009, 11:54:00
Si?  :P No da lo mismo? Porque sería mas preciso? Porque nos ahorramos los ticks que toma cargar el timer? o hay alguna otra razón oculta por ahí? jeje

Saludos
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Suky en 03 de Octubre de 2009, 20:43:11
Lo oculto esta en los ciclos que utiliza en decidir que interrupción y atenderla. Porque por ejemplo:

1:1 -> | Habilito interrupción |Se genera interrupción | Recargo Timer | Se genera interrupción
CCP -> | Habilito interrupción |Se genera interrupción |                |Se genera interrupción

El desfasaje que se ve es porque en realidad se debería de recargar el timer justo después de ocurrida y no después de varias otras instrucciones "desconocidas" realizadas por CCS.


Saludos!
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 06 de Octubre de 2009, 06:17:43
Muchisimas gracias pero lo hago de esa forma y no funciona :? ya tengo todo listo me faltaria la presición que me daria esto aca cuelgo el código

Código: [Seleccionar]
#include <16f877a.h>
#use delay(clock=4000000)
#fuses XT,PUT,NOPROTECT,NOWDT,NOLVP
#byte portb=6
#byte portc=7
#byte portd=8
#include <flex_lcd.c>
#use standard_io(b)
//#use fast_io(b)

#define SEGUNDOS_EN_UNA_HORA 3600
#define SEGUNDOS_EN_UN_MINUTO 60


int32 Conta_Segundos=0,MomentoActual=0;
int32 Horas=0,Minutos=0,Segundos=0;
int set,FLAG_TIMER1;
byte last_b;

void Mi_Hora(void);
void Cambiar_Hora(int32 *Horas,int32 *Minutos);
void reset(void)
{
Conta_Segundos=0;
Momentoactual=0;
}

#INT_TIMER1
timer1_rsi()
{
Mi_Hora();        //Pasado el segundo calculo la hora.
lcd_gotoxy(6,1);
printf(lcd_putc,"%02ld:%02ld:%02ld    ",Horas,Minutos,Segundos);
}

// Interrupcion RB4 - RB7 /////////////////////////////////////////////////////
#INT_RB
rb_isr()
{
    byte changes;
    changes=last_b^portb;       //XOR.
    last_b=portb;
    if (bit_test(changes,5) && !bit_test(last_b,5))
    {                                                     
         lcd_gotoxy(6,2);
         lcd_putc("..              ");    //Indica que se va a incrementar las horas.
         delay_ms(200);
         lcd_gotoxy(6,2);
         lcd_putc("                ");
         delay_ms(200);
         lcd_gotoxy(6,2);
         lcd_putc("..              ");
         if(set==1)
         {
         lcd_gotoxy(1,2);
         lcd_putc("        ..      ");   // Indica que se van a incrementar los minutos
         delay_ms(250);
         lcd_gotoxy(1,2);
         lcd_putc("                ");
         delay_ms(250);
         lcd_gotoxy(1,2);
         lcd_putc("        ..      ");
         }
         set+=1;
         if (set==2)
         set=0;   
    }
    if (bit_test(changes,6) && !bit_test (last_b,6))   //INCREMENTAR.
    {
    Cambiar_hora(&Horas,&Minutos);
    }
    if (bit_test(changes,7) && !bit_test (last_b,7))   //DECREMENTAR.
    {
    Cambiar_hora(&Horas,&Minutos);       
    }
    Conta_Segundos=(Minutos*60)+(Horas*3600)+Segundos;
    bit_clear(portb,0);          //Solo modifico el nibble bajo sin tocar el alto
    bit_clear(portb,1);          //y consigo escribir el port
    bit_clear(portb,2);
    bit_clear(portb,3);
    delay_ms(10);
    set_timer1(0);   
}


// Programa Principal /////////////////////////////////////////////////////////
   
void  Mi_Hora()
      {
      ++Conta_Segundos;
      MomentoActual=Conta_Segundos;
      Horas=MomentoActual/SEGUNDOS_EN_UNA_HORA;
      MomentoActual%=SEGUNDOS_EN_UNA_HORA;               //Guarda el resto de la división entera.
      Minutos=MomentoActual/SEGUNDOS_EN_UN_MINUTO; 
      MomentoActual%=SEGUNDOS_EN_UN_MINUTO;   
      Segundos=MomentoActual;
      MomentoActual=1;
      if(Horas==24 && Minutos==0 && Segundos==0)
      {
      Horas=0;
      Minutos=0;
      Segundos=0;
      reset();
      }
      }
     
void Cambiar_hora(int32 *Horas,int32 *Minutos)
{
if(set==0)
   {
      if(portb==0b10110000)
      {
      ++*Minutos;
      if(*Minutos==60){*Minutos=0;}
      }
   }
if(set==1)
   {
      if(portb==0b10110000)
      {
      ++*Horas;
      if(*Horas==24){*Horas=0;}
      }
   }
if(set==0)
   {
      if(portb==0b01110000)
      {
      if(*Minutos==0){*Minutos=60;}
      --*Minutos;
      }
   }   
if(set==1)
   {
      if(portb==0b01110000)
      {
      if(*Horas==0){*Horas=24;}
      --*Horas;
      }
   }   
}     
       
void  main(void)
      {
      lcd_init();
      set_tris_b(0xf0);
      port_b_pullups(TRUE);
      portb=0xf0;
      setup_timer_1(T1_EXTERNAL|T1_DIV_BY_2|T1_CLK_OUT);
      enable_interrupts(INT_TIMER1);
      enable_interrupts(INT_RB);
      enable_interrupts(GLOBAL);
      set_tris_c(0xff);
      set_tris_d(0x00);
      lcd_gotoxy(1,1);
      lcd_putc("Reloj Digital");
      delay_ms(1500);
      lcd_putc("\fHora 00:00:00 ");
      lcd_gotoxy(1,2);
      lcd_putc("        ..      ");
      delay_ms(250);
      lcd_gotoxy(1,2);
      lcd_putc("                ");
      delay_ms(250);
      lcd_gotoxy(1,2);
      lcd_putc("        ..      ");
      delay_ms(250);
      setup_timer_1(T1_EXTERNAL|T1_DIV_BY_2|T1_CLK_OUT);
      set_timer1(0);
         while(1)
         {
         lcd_gotoxy(6,1);
         printf(lcd_putc,"%02ld:%02ld:%02ld    ",Horas,Minutos,Segundos);
         }
       }
       
       
       
       
       
       
No se que pasara y no se si estare conectando mal el crystal asi que les muestro,Gracias amigos
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Suky en 06 de Octubre de 2009, 08:59:20
Las interrupciones trata de que sean lo mas corta posible, no envíes datos al LCD dentro de ella, setea una bandera y ejecútalos en el main o una función según como organices tu código. Con respecto a como se conecta el cristal puedes revisar el datasheet, allí está explicado  :mrgreen:

Una cosa que me olvide de comentar es que proteus no simula osciladores, hay que probarlo en la vida real  :mrgreen:


Saludos!
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 06 de Octubre de 2009, 12:13:44
Ahh que piola lo mio y yo dandole a run como un tarado jaja bue gracias = mi idea es que se deje de lado la cuenta cuando esta dentro de la interrupción int_rb asique no le afectaria lo largo de esta pero bue lo voy a intentar gracias nos vemos ;-)
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: MLO__ en 06 de Octubre de 2009, 12:33:20
Hola.

Para simular la entrada del cristal se puede conectar un generador de señales y asignar la frecuencia a la del cristal, o una fuente tipo Clock.

Saludos

Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 06 de Octubre de 2009, 13:42:36
¿Donde esta el problema? lo conecté con 2 condensadores de 27 pF porque no tengo de 33pF como indica el data y al fin cuenta pero muy lento me incrementa los segundos como cada 4 o 5 segundos reales,vi en el archivo .lst que la funcion Mi_Hora() tiene mas de 200 instrucciones lo cual a 4 MHz serian 200 µs pero a la interrupción del timer entra supuestamente cada segundo no por cada cuenta asique no se que estara pasando  :(
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 08 de Octubre de 2009, 18:19:11
Logré mejorarlo mucho colocando todo lo del while(1) dentro de la interrupción del timer porque a 4 MHz el printf demoraba 74 ms  :P asique recargando el timer con 32768+estos 74 ms o sea con un 35193 antes de salir de la misma me conforma mucho -aunque adelanta 3 seg cada 10 min- sino digan que se les ocurre,Gracias  ;-)
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Suky en 08 de Octubre de 2009, 18:25:50
La forma correcta de trabajar con interrupciones es utilizar una bandera para indicar lo ocurrido y tratarla después, y usando preescaler 1:2 no tienes que recargar el timer  :mrgreen:

Saludos!
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 08 de Octubre de 2009, 19:02:00
Para mi es lo mismo Suky porque en algún momento debo recargar el timer como corrección ya que desde que ocurre la interrupción hasta que se actualiza la hora en el display pasan 74.04 en promedio ms (esto lo cheque trazando un breakpoint en proteus) por eso hay que adelantar la cuenta antes de salir de la interrupción, en fin esto va a ser a pila asique va a ir bien porque en una semana adelanta un poquito mas de un minuto y ¿cuanto va a durar la pila?
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Suky en 08 de Octubre de 2009, 22:10:12
Cuando lo seteas en 1:2 la interrupción va a ocurrir siempre a 1 seg y no se va atrasar, no hay manera que se atrase, excepto que el reloj no sea exactamente de 32768Hz. Después cuanto dure la pila, mmm hay que calcular el consumo que tendrá el dispositivo y dependiendo de la pila se pude estimar el tiempo de duración.


Saludos!
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 08 de Octubre de 2009, 22:31:39
A ver Suky corrigeme el código según dices porque yo pongo como dices T1_DIV_BY_2 e interrumpe cada 4 segundos sin cargar el timer como dices!  :( mira aca esta el código actualizado:

Código: [Seleccionar]
#include <16f877a.h>
#use delay(clock=4000000)
#fuses XT,PUT,NOPROTECT,NOWDT,NOLVP
#byte portb=6
#byte portc=7
#byte portd=8
#include <flex_lcd.c>
#use standard_io(b,c)
//#use fast_io(b)

#define SEGUNDOS_EN_UNA_HORA 3600
#define SEGUNDOS_EN_UN_MINUTO 60


int32 Conta_Segundos=0,MomentoActual=0;
int32 Horas=0,Minutos=0,Segundos=0;
int set,flag_timer1;
byte last_b;

void Mi_Hora(void);
void Cambiar_Hora(int32 *Horas,int32 *Minutos);
void reset(void)
{
Conta_Segundos=0;
Momentoactual=0;
}

#INT_TIMER1
timer1_rsi()
{
set_timer1(32768);
++Conta_Segundos;
Mi_Hora();
lcd_gotoxy(6,1);
printf(lcd_putc,"%02ld:%02ld:%02ld    ",Horas,Minutos,Segundos);
}

// Interrupcion RB4 - RB7 /////////////////////////////////////////////////////
#INT_RB
rb_isr()
{
    byte changes;
    changes=last_b^portb;       //XOR.
    last_b=portb;
    if (bit_test(changes,5) && !bit_test(last_b,5))
    {                                                     
         lcd_gotoxy(6,2);
         lcd_putc("..              ");    //Indica que se va a incrementar las horas.
         delay_ms(200);
         lcd_gotoxy(6,2);
         lcd_putc("                ");
         delay_ms(200);
         lcd_gotoxy(6,2);
         lcd_putc("..              ");
         if(set==1)
         {
         lcd_gotoxy(1,2);
         lcd_putc("        ..      ");   // Indica que se van a incrementar los minutos
         delay_ms(250);
         lcd_gotoxy(1,2);
         lcd_putc("                ");
         delay_ms(250);
         lcd_gotoxy(1,2);
         lcd_putc("        ..      ");
         }
         set+=1;
         if (set==2)
         set=0;   
    }
    if (bit_test(changes,6) && !bit_test (last_b,6))   //INCREMENTAR.
    {
    Cambiar_hora(&Horas,&Minutos);
    }
    if (bit_test(changes,7) && !bit_test (last_b,7))   //DECREMENTAR.
    {
    Cambiar_hora(&Horas,&Minutos);       
    }
    Conta_Segundos=(Minutos*60)+(Horas*3600)+Segundos;
    bit_clear(portb,0);          //Solo modifico el nibble bajo sin tocar el alto
    bit_clear(portb,1);          //y consigo escribir el port
    bit_clear(portb,2);
    bit_clear(portb,3);
    lcd_gotoxy(6,1);
    printf(lcd_putc,"%02ld:%02ld:%02ld    ",Horas,Minutos,Segundos);
    delay_ms(10);
    set_timer1(32768);   
}


// Programa Principal /////////////////////////////////////////////////////////
   
void  Mi_Hora()
      {
      MomentoActual=Conta_Segundos;
      Horas=MomentoActual/SEGUNDOS_EN_UNA_HORA;
      MomentoActual%=SEGUNDOS_EN_UNA_HORA;               //Guarda el resto de la división entera.
      Minutos=MomentoActual/SEGUNDOS_EN_UN_MINUTO; 
      MomentoActual%=SEGUNDOS_EN_UN_MINUTO;   
      Segundos=MomentoActual;
      if(Horas==24 && Minutos==0 && Segundos==0)
      {
      Horas=0;
      Minutos=0;
      Segundos=0;
      reset();
      }
      }
     
void Cambiar_hora(int32 *Horas,int32 *Minutos)
{
if(set==0)
   {
      if(portb==0b10110000)
      {
      ++*Minutos;
      if(*Minutos==60){*Minutos=0;}
      }
   }
if(set==1)
   {
      if(portb==0b10110000)
      {
      ++*Horas;
      if(*Horas==24){*Horas=0;}
      }
   }
if(set==0)
   {
      if(portb==0b01110000)
      {
      if(*Minutos==0){*Minutos=60;}
      --*Minutos;
      }
   }   
if(set==1)
   {
      if(portb==0b01110000)
      {
      if(*Horas==0){*Horas=24;}
      --*Horas;
      }
   }   
}     
       
void  main(void)
      {
      lcd_init();
      set_tris_b(0xf0);
      port_b_pullups(TRUE);
      portb=0xf0;
      setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1|T1_CLK_OUT);//el preescaler q va es 2.
      enable_interrupts(INT_TIMER1);
      enable_interrupts(INT_RB);
      enable_interrupts(GLOBAL);
      set_tris_c(0b00000011);
      set_tris_d(0x00);
      lcd_gotoxy(1,1);
      lcd_putc("Reloj Digital");
      delay_ms(1500);
      lcd_putc("\fHora 00:00:00 ");
      lcd_gotoxy(1,2);
      lcd_putc("        ..      ");
      delay_ms(250);
      lcd_gotoxy(1,2);
      lcd_putc("                ");
      delay_ms(250);
      lcd_gotoxy(1,2);
      lcd_putc("        ..      ");
      delay_ms(250);
      set_timer1(32768);
         while(1)
         {
         
         }
       }
       
       
       
       
       
       
= me parece que tengo un problema físico porque hace mas de 20 min que esta runeando esto en el proteus y la diferencia entre el reloj del simulador y lo que marca el display se mantiene constante!deben ser los capacitores o el cristal mientras q en el protoboard en el mismo tiempo se adelanta un par de segundos cuando lo comparo con un crónometro que tengo
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Suky en 08 de Octubre de 2009, 23:32:06
La idea es la siguiente:

Código: C
  1. #priority TIMER1,RB
  2. char Tecla;
  3. int1 Actualizar_hora=0, Tecla_pulsada=0;
  4.  
  5. #INT_TIMER1
  6. timer1_rsi()
  7. {
  8.    ++Conta_Segundos;
  9.    Actualizar_hora=1;
  10. }
  11.  
  12. // Interrupcion RB4 - RB7 /////////////////////////////////////////////////////
  13. #INT_RB
  14. rb_isr()
  15. {
  16.     byte changes;
  17.     changes=last_b^portb;       //XOR.
  18.     last_b=portb;
  19.     if (bit_test(changes,5) && !bit_test(last_b,5))
  20.     {                                                    
  21.        Tecla=1;
  22.     }
  23.     if (bit_test(changes,6) && !bit_test (last_b,6))   //INCREMENTAR.
  24.     {
  25.       Tecla=2;
  26.     }
  27.     if (bit_test(changes,7) && !bit_test (last_b,7))   //DECREMENTAR.
  28.     {
  29.       Tecla=3;
  30.     }
  31.  
  32. }
  33.  
  34.        
  35. void  main(void){
  36.  
  37.       lcd_init();
  38.       set_tris_b(0xf0);
  39.       port_b_pullups(TRUE);
  40.       portb=0xf0;
  41.       setup_timer_1(T1_EXTERNAL|T1_DIV_BY_2|T1_CLK_OUT);//el preescaler q va es 2.
  42.  
  43.       set_tris_c(0b00000011);
  44.       set_tris_d(0x00);
  45.       lcd_gotoxy(1,1);
  46.       lcd_putc("Reloj Digital");
  47.       delay_ms(1500);
  48.       lcd_putc("\fHora 00:00:00 ");
  49.       lcd_gotoxy(1,2);
  50.       lcd_putc("        ..      ");
  51.       delay_ms(250);
  52.       lcd_gotoxy(1,2);
  53.       lcd_putc("                ");
  54.       delay_ms(250);
  55.       lcd_gotoxy(1,2);
  56.       lcd_putc("        ..      ");
  57.       delay_ms(250);
  58.       set_timer1(0);
  59.       enable_interrupts(INT_RB);
  60.       enable_interrupts(INT_TIMER1);
  61.       enable_interrupts(GLOBAL);
  62.       while(1){
  63.          if(Actualizar_hora==1){
  64.             Actualiza_hora=0;
  65.             ...
  66.          }
  67.          if(Tecla_pulsada==1){
  68.             Tecla_pulsada=0;
  69.             switch(Tecla){
  70.                case 1:
  71.                ...
  72.             }        
  73.          }
  74.       }
  75. }


Saludos!
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 09 de Octubre de 2009, 20:54:22
Suky probe tu código estan blandos lo pulsadores estan que vuelan y fíjate que debujeando en proteus actualiza la hora cada un segundo exacto pero en la proto sigue = asique concluyo o que mi crystal se corrio de frecuencia o algo pero que el programa esta bien adjunto la imagen de proteus,Saludos  ;-)



Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Suky en 09 de Octubre de 2009, 22:21:07
Te he convencido  :mrgreen: Con respecto al hardware real, mmm.. lo has conectado como indica el datasheet, lo mas cerca posible del PIC?
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 10 de Octubre de 2009, 00:40:25
Mira amigo Suky nunca use la directiva del priority pero supongo que acepté lo de las banderas porque se trata solo de if en el programa principal por eso quise meter dentro de la interrupción del timer el cálculo de la hora por ejemplo,porque no se puede controlar la carga del contador de programa;ej: en la función Mi_Hora estando en el programa principal q pasaba si se interrumpia por timer justo cuando el pc estuviese apuntando despues de la primera instrucción?:se hubiera ignorado el incremento del contador de segundos lo cual hubiera redundado en una perdida de tiempo ya que el registro MomentoActual seguiria operando con el valor previo a la interrupción,por eso a veces hago largas las interrupciones porque de alguna manera me sirve para ``controlar´´ el pc.Saludos.  ;-)

PD:No cambia lo ponga cerca o mas lejos para mi es el cristal de  4MHz que esta corrido para arriba porque adelanta y no atrasa vi la hoja de datos como me dijiste la lei con dificultad ya que odio el english y lo único que dice es q van de 33pf pero habla de LP y no se si estara hablando para este caso (yo uso 2 de 27 pf no creo q haya gran diferencia =) asique yo esperaria que atrasara y no que adelantara.
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: MLO__ en 10 de Octubre de 2009, 12:31:28
Hola.

El cristal, al que se refiere Suky, debe ser de 32k768Hz. El de 4MHz es el cristal para el oscilador principal, el otro es para el oscilador secundario para el Timer1 (T1OSI - T1OSO).

Saludos
Título: Re: Como configurar el timer 1 en modo contador
Publicado por: Trev en 10 de Octubre de 2009, 13:49:31
Da igual  :mrgreen: alguno o los dos estan corridos para arriba y ¿porque debe estar lo más cerca posible?  :shock: ,¿Coincidimos en esto?: el clock envia un flanco asc cada 1/32768 s si esto es cierto y si tengo algún error en el programa tiene que retrasar no adelantar,no se voy a ver si consigo mejores cristales y sino pasare al DS1307 y espero tener mejor suerte.