Ciclos de reloj y tiempo para delays 16f84

Hola amigos . uds saben como se puede hacer prender una secuencia determinada 24 veces por segundo?? seria con un pic 16f628. la idea es hacer que entren 24 escenas en un segundo para 8 leds.

Saludos

Divides 1Seg/24 y el valor que obtengas de tiempo lo generas con ayuda de un Timer. Ya sea por interrupción o testeo cada que transcurra un desborde del TMR actualizas los datos de los leds para generar el efecto de las escenas
 
Muchas gracias por la explicacion Daniel , Me podrias dar un ejemplo sencillo de como configurar el tiempo en el Timer? solo para orientarme un poco.

Saludos y muchas gracias por la ayuda.
 
La ecuación para el tiempo, considerando que usas el TMR1 (es de 16 bits y dará mayor flexibilidad que el TMR0):

TMR1=2^16-(Fosc*T)/(4*Pree)

Fosc=Frecuencia del cristal que uses
T=1/24
Pree: Divisor usado en el preescaler, ya sea 2,4,8 o 16

el resultado que te de ese cálculo será el que le tendrás que cargar al TMR1. El algoritmo sería así:

-Configurar T1CON para fuente de reloj interna, preescaler deseado
-Poner a 0 bandera de interrupción del TMR1 (TMR1IF) presente en el registro PIR1
-Cargar resultado del cálculo en los registros TMR1H y TMR1L
-Activar reloj del TMR1 por medio del bit TMR1ON presente en el registro T1CON
-Esperar a que se ponga en uno la bandera de interrupción del TMR1

La ejecución de el procedimiento anterior tomará 1/24 seg, lo que necesitas temporizar entre cada escena
 
Este ejemplo que adjunto en PICC, sirve para hacer un retardo de 1 segundo.
Puede ser usado para hacer un RTC (Reloj en Tiempo Real)
Se basa en usar un cristal de 32768 Hz con el oscilador externo T1OSI, T1OSO de algunos PIC.
Este ejemplo puede ser usado como base para lograr retardos de tiempo largos (Horas)
Incluso para hacer un reloj con displays en modo multiplex, ya que la temporización no afecta el programa.

Notas:
Programa para un 16F628A (Aplicable a otros PIC con los cambios básicos al programa)

Saludos.

Hola D@rkbytes:

He descargado tu programa y en la simulacion me andubo perfectamente, pero me surgio un problema, y es que durante la interrupcion quize agregar una suma(de una variable seg)y la cuestion es que pruebo el programa y no va para atras ni para adelante, y eso que trate de usar condicioness sencillas para seguir mi avance, aca dejo el codigo fallido :cry:

Código:
/*******************************************************************************
* Compilador: CCS PCM C Compiler, Versión 4.140
* Programa: Timer_1Seg.C
* Versión: 1.0
* Autor: D@rkbytes
* Compañia: Digitek
* Notas: 
* Timer de un segundo con TMR1 (16 Bits)
* Se usará el oscilador interno a 4 MHz.
* El externo en RB6 y RB7, con un cristal de 32768 Hz.
*******************************************************************************/
#include <16f887.h>

int seg=0;

#use     delay(internal=4mhz)
/* Estableciendo lo anterior en este PIC...
La palabra de configuración (Fuses) qué dará el compilador, está bien así.
INTRC/IO,PWRTE=ON,MCLRE=ON,BOREN=ON */
#byte    PORTA = getenv("SFR:PORTA")   // Referencia del registro PORTA
#bit     RA1 = PORTA.1                 // Definir RA1
#bit     RA2 = PORTA.2                 // Definir RA2

// Controlador de la interrupción por desborde del TMR1  
#int_timer1 
void tmr1_int(void){
   clear_interrupt(int_timer1);     // Limpiar flag por desborde del TMR1
   set_timer1(0x8000);             // Recargar el TMR1 con 32768
// RA2 = ~RA2;                    // Toggle en RA2 (Otra forma de hacer toggle)
   seg++;                    // Esto se hará cada 1 segundo.
                                    
} 
//--------------------------MUESTREO-------------------------------------------


//----------------------------------o------------------------------------
void main(void){ 
   set_tris_a(0b11111001);          // RA1 y RA2 como salidas
   setup_timer_1(t1_external_sync|t1_div_by_1|t1_clk_out);
   enable_interrupts(int_timer1);   // Habilitar interrupción del Timer 1 
   enable_interrupts(global);       // Habilitar interrupciones globales 
   set_timer1(0x8000);              // Cargar el TMR1 con 32768
   
   while(true){                     // Hacer un bucle infinito del programa.
   
      
         if(seg<10){
               output_high(PIN_A0);
               output_high(PIN_A1);
               output_high(PIN_A2);
         }
         if(seg>10){
               output_high(PIN_A3);
               output_high(PIN_A4);
               output_high(PIN_A5);
         }
   
   }
}
 
Es que debiste hacer unos cambios.
TRISA está configurado para que RA1 y RA2 sean salidas, pero tú quieres usar desde RA0 hasta RA5 como salidas.

Entonces el programa quedaría así:
PHP:
#include <16f887.h>
#use     delay(internal=4mhz)

int8 seg=0;

// Controlador de la interrupción por desborde del TMR1  
#int_timer1 
void tmr1_int(void){
   clear_interrupt(int_timer1);     // Limpiar flag por desborde del TMR1
   set_timer1(0x8000);              // Recargar el TMR1 con 32768
                                    // Esto se hará cada 1 segundo.
   seg ++;
} 

void main(void)
{
   setup_timer_1(t1_external_sync|t1_div_by_1);
   enable_interrupts(int_timer1);   // Habilitar interrupción del Timer 1 
   enable_interrupts(global);       // Habilitar interrupciones globales 
   set_timer1(0x8000);              // Cargar el TMR1 con 32768
   
   while(true)
   {
      if(seg<10)
      {
         output_high(PIN_A0);
         output_high(PIN_A1);
         output_high(PIN_A2);
      }
      
      if(seg>10)
      {
         output_high(PIN_A3);
         output_high(PIN_A4);
         output_high(PIN_A5);
      }
   }
}
Ten en cuenta que la variable "seg" siempre seguirá en incremento hasta que se desborde.
int en PIC C Compiler es de 255 bits. (1 Byte)

Con un "else" en vez de: if(seg>10) se logra el mismo resultado.
 
Última edición:
Hola D@rkbytes, con tu permiso yo también he modificado tu programa, gracias por compartirlo, muy levemente para intentar algo simple pero tengo alguna pega.
Necesito realizar temporizaciones distintas dependiendo del estado de RA0 y deben ser lo más precisas posible, sobre todo las más largas, para no ganar o perder mucho tiempo al menos durante las 24H que se usará el circuito.
Las temporizaciones necesarias son las siguientes:
27 segundos led apagado y 3 segundos led activo y vuelta a empezar.
60 segundos led apagado y activo el led 1,1 segundos y vuelta a empezar.
El caso es que los tiempos no me cuadran mucho, sobre todo el de 60/1,1 y me he dado cuenta que el reinicio de la variable seg despues de hacer el delay no sirve para lo que yo busco, el timer1 sigue contando independientemente de los delay que ponga por lo que tendría que añadirle ese 0,1 segundos de más en este segundo caso. Creo que la manera de hacer esto puede ser más sencilla de lo que yo pienso, parar y reiniciar el timer1??, pero me he quedado en blanco y pienso que la voy a liar...
El estado de RA0 solo sería necesario comprobarlo una vez, en el arranque del programa, y dependiendo de ello funcionar 24H y ya. Veo innecesario comprobarlo cada segundo, como lo hago? el tener la interupción por medio hace que me pierda en el programa... jejeje:

Código:
/*******************************************************************************
* Compilador: CCS PCM C Compiler, Versión 4.140
* Programa: Timer_1Seg.C
* Versión: 1.0
* Autor: D@rkbytes
* Compañia: Digitek
* Notas: 
* Timer de un segundo con TMR1 (16 Bits)
* Se usará el oscilador interno a 4 MHz.
* El externo en RB6 y RB7, con un cristal de 32768 Hz.
*******************************************************************************/
#include <16f628a.h>
int seg=0;
#use     delay(internal=4mhz)
#byte    PORTA = getenv("SFR:PORTA")   // Referencia del registro PORTA
#byte    PORTB = getenv("SFR:PORTB")   // Referencia del registro PORTB
#bit     RB0 = PORTB.0                 // Definir RB0
#bit     RB1 = PORTB.1                 // Definir RB1
#bit     RA0 = PORTA.0                 // Definir RA0
// Controlador de la interrupción por desborde del TMR1  
#int_timer1 
void tmr1_int(void)
{
   clear_interrupt(int_timer1);     // Limpiar flag por desborde del TMR1
   set_timer1(0x8000);             // Recargar el TMR1 con 32768
   RB1 = ~RB1;                      // Toggle en RB1 (Otra forma de hacer toggle)
   seg++;                               // Esto se hará cada 1 segundo.
} 
 
void main(void)
{
   set_tris_a(0x1F);     // Puerto A como entradas.
   set_tris_b(0x00);     // Puerto B como salidas.
   setup_timer_1(t1_external_sync|t1_div_by_1|t1_clk_out);
   enable_interrupts(int_timer1);   // Habilitar interrupción del Timer 1 
   enable_interrupts(global);       // Habilitar interrupciones globales 
   set_timer1(0x8000);              // Cargar el TMR1 con 32768
   
   while(true)
   {                     // Hacer un bucle infinito del programa.
     If (input (pin_A0)==0)
       {
        if(seg<27)
                RB0 = 0;
         else
            {
              RB0 = 1;
              delay_ms (3000);
              RB0 = 0;
              seg=0;
            } 
       } 
     else
        {
        if(seg<60)
             RB0 = 0;  
         else
            {
              RB0 = 1;
              delay_ms (1100);
              RB0 = 0;
              seg=0;
            } 
         
       }
   }
}
 
Última edición:
El estado de RA0 solo sería necesario comprobarlo una vez en el arranque del programa y dependiendo de ello, funcionar 24H y ya.
Veo innecesario comprobarlo cada segundo. ¿Cómo lo hago?
Saludos.
Puedes usar una bandera que le indique al programa si RA0 se encontraba en 1 o en 0 al inicio.

Algo así:
PHP:
void main (void)
{
   int1 flag_init = input(PIN_A0);
   
   
   while (true)
   {
      if(flag_init)
      {
         // Código
      }
      else
      {
         // Código
      }
   }
}
 
Gracias por la respuesta, el problema que más me trae de cabeza es la temporización de 1,1 segundos y luego hacer los 60 segundos con la mayor precisión posible. Creo que la opción más sencilla sin usar delay sería poner un timer que desborde cada 100ms, contar 600 interrupciones, encender, contar 11, apagar y a correr.
Salu2
 
Hola otra vez, he seguido con los cambios y la simulación en Proteus funciona al pelo, lo compilo ok pero lo grabo y no funciona. He usado tu programa pero con un cristal de 4 Mhz para hacer una temporización de 100mSeg. El circuito esta bien echo ya que con otro programa anterior funciona bien, la única diferencia es la conexión del cristal.
En mi anterior programa el circuito tenía un cristal de 4 mhz conectado en las patillas 15 y 16, OSC1 y OSC2 para usar un cristal externo, pero en este usamos el oscilador interno y un cristal de 4Mhz, en mi caso, conectado en las patillas 12 y 13, T1CKI y T1OSI.
A la hora de grabar el PIC, uso ICprog 1.05C, entiendo que solo debo seleccionar en oscilador XT y el bit de configuración PWRT.
Adjunto programa para ver si alguien puede decirme que hago mal
Código:
/*******************************************************************************
* Compilador: CCS PCM C Compiler, Versión 4.140
* Programa: Timer_100mSeg.C
* Versión: 1.0	
* Autor: FJM@N
* Timer para 60/1,1_27/3 segundos con TMR1 (16 Bits)
* Oscilador interno a 4 MHz.
* Oscilador externo en RB6 y RB7, con un cristal de 4 MHz.
*******************************************************************************/
#include <16F628A.h>
#FUSES NOWDT
#FUSES PUT                      //Power Up Timer
#FUSES XT                       //Crystal osc <= 4mhz for PCM/PCH , 3mhz to 10 mhz for PCD
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOPROTECT                //Code not protected from reading
#use delay(clock=4000000)
#byte    PORTA = getenv("SFR:PORTA")   // Referencia del registro PORTA
#byte    PORTB = getenv("SFR:PORTB")   // Referencia del registro PORTB
#bit     RA0 = PORTA.0                 // Definir RA0
#bit     RB0 = PORTB.0                 // Definir RB0
int16 COUNT;

#int_TIMER1
void  tmr1_int(void) 
{
   clear_interrupt(int_timer1);     // Limpiar flag por desborde del TMR1
   set_timer1(0x3CC2);              // Recargar el TMR1 con 15554
   COUNT++;                         // Aumentamos la cuenta de las interrupciones
}

void main(void)
{
   set_tris_a(0b11111111);          // RA0 entrada
   set_tris_b(0b11111110);          // RB0 salida
   setup_timer_1(T1_EXTERNAL|T1_DIV_BY_8|T1_CLK_OUT|T1_EXTERNAL_SYNC);      //131 ms overflow
   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);
   set_timer1(0x3CC2);              // Recargar el TMR1 con 15554
   While (COUNT>=1)
   If (RA0==0)
         {
            if (COUNT<12) //Cada interrupcion dura 0,1 segundos//Contamos 1,1 Segundos
                 RB0 = 1;
	       else
		  RB0 = 0;
            if (COUNT==612) //Contamos 60 + 1,1 Segundos
            {
            RB0 = 1;
            COUNT=1;
            }
         }
         Else
            {
            if (COUNT<31) //Cada interrupcion dura 0,1 segundos//Contamos 3 Segundos
                RB0 = 1;
	       else
	        RB0 = 0;		  
            if (COUNT==301) //Contamos 27 + 3 Segundos
            {
            RB0 = 1;
            COUNT=1;
            }  
            }

 }
:
 
En mi anterior programa el circuito tenía un cristal de 4 mhz conectado en las patillas 15 y 16, OSC1 y OSC2 para usar un cristal externo, pero en este usamos el oscilador interno y un cristal de 4Mhz, en mi caso, conectado en las patillas 12 y 13, T1CKI y T1OSI.
No creo que usando un cristal de 4 MHz para la sincronización externa del Timer 1, llegue a funcionar.
El oscilador del Timer 1 está específicamente diseñado para trabajar con un cristal de 32,768 KHz.
Timer 1 Oscillator.jpg
He usado tu programa pero con un cristal de 4 Mhz para hacer una temporización de 100mSeg.
El circuito está bien hecho ya que con otro programa anterior funciona bien, la única diferencia es la conexión del cristal.
En dado caso que el cristal de 4 MHz llegara a oscilar para el Timer 1, el valor para que desborde cada 100 ms, no es correcto.
Debería ser de: 53036 (0xCF2C) y en este caso con el prescaler a 1:8
Entiendo que sólo debo seleccionar en oscilador XT y el bit de configuración PWRT.
Seleccionar el fuse XT (XT_OSC) es para que el oscilador principal funcione con un cristal. (Obviamente externo y hasta 4 MHz.)
El fuse PUT (PWRTE) es opcional, pero es recomendable usarlo porque temporizará cerca de 72 ms después de conectada la alimentación del PIC, manteniéndolo en estado de reset.
Luego de este tiempo el PIC empezará a ejecutar el programa.
Es útil para esperar a que la tensión de alimentación se estabilice.

Puedes usar un cristal de 4 MHz, y configurar el Timer 1 para que use el interno a 4 MHz.
Y si usas un cristal de 32,768 KHz para el Timer1, el valor para que desborde cada 100 ms. es de: 65434 (0xFF9A) y ésto si se usa el prescaler 1:8
 
Nuevamente gracias por responder. Como ya imaginaba el problema es el cristal y la configuración de este que me he liado pero bien. Acabo de leer que ese oscilador es igual al oscilador interno en modo LP, o sea, solo hasta 200.000Khz.

Los calculos estan mal por que di por hecho que eran los mismos que con el timer1 en modo temporizador y me salía preescaler de 2 y precarga de 15536 para el overflow. Al simularlo ya ví que iba demasiado rápido, cambié el preescaler a 8 y ya contaba bién, se pasaba en unos 36 useg, por eso luego le puse 15554. Con ese valor salía exacto o +/- 4useg después de unas tres horas.
El fuse PUT lo puse por que hasta que se inicializaba todo notaba que era un poco aleatoria la primera interrupción y el resto mucho más exacta. Me aseguré de ello contando realmente el resto de interrupciones después de esta primera interrupción y es bastante preciso ahora.
Prefiero usar el cristal de 4 Mhz como comentas ya que anteriormente lo usaba y el circuito ya está incluso montado de hace años, cambiar algo sería engorroso y el problema era... es... la inexactitud del propio código a base de delays.... voy a ello...
Gracias por todo.

Salu2
 
Última edición:
Atrás
Arriba