Interrupción timer 1

Hola a todos,
estoy empezando en este mundillo de los microcontroladores, y la verdad es que estoy un poco perdida. Estoy intentando aprender c'omo va el tema de las interrupciones de timer, pero no consigo que funcionen... Estoy usando el pic 16f877a y CCS, y lo que intento hacer es generar 1000 interrupciones por segundo. El c'odigo es bastante simple, pero no s'e qu'e es lo que estoy haciendo mal...:

Código:
---------------------------------------------------------------------------------------------
#include <16f877a.h>
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use delay(clock=19660800)

long Tmr1_inic;                

#int_timer1              
void OvFlw_Tmr1() {
   
   set_timer1(Tmr1_inic);         // inicializaTimer1 para el siguiente conteo  
   output_toggle(PIN_B1);       // cambia el estado del led
}


void main() {

   Tmr1_inic = 60620;                     //  Valor inicial del timer1
   
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);    // modo de trabajo
   set_timer1(Tmr1_inic);                 // Timer1 para el siguiente conteo
   
   enable_interrupts(GLOBAL);         // Activa las interrupciones globales
   enable_interrupts(INT_TIMER1);    // INterrupciones de timer1
   
   while (TRUE);                               // infinite loop
    
}

------------------------------------------------------------------------------
Agradeceria cualquier correccion o consejo.
Un saludo,
 
Última edición por un moderador:
Hola, "jaxlala", el codigo que tienes es correcto, el unico detalle es:

-no especificas el valor del cristal, por tanto el valor que cargas al timer1 puede ser incorrecto.
pero te mando este codigo correjido.

Frecuencia de Cristal=4Mhz

Código:
#include <16f877a.h>
#fuses XT, NOWDT, NOPROTECT, NOLVP
#use delay(clock=4000000)     //cristal de 4Mhz

long Tmr1_inic;

#int_timer1
void OvFlw_Tmr1() 
{
   set_timer1(Tmr1_inic); // inicializaTimer1 para el siguiente conteo
   output_toggle(PIN_B1); // cambia el estado del led
}

void main() 
{
   Tmr1_inic = 64575; // Valor inicial del timer1
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); // modo de trabajo
   set_timer1(Tmr1_inic); // Timer1 para el siguiente conteo
   enable_interrupts(GLOBAL); // Activa las interrupciones globales
   enable_interrupts(INT_TIMER1); // INterrupciones de timer1
   while (TRUE); // infinite loop
}.

-------------------
Un saludo y buena suerte....
 
Última edición por un moderador:
Hola,
Saint_, si que he especificado el valor del cristal (#use delay(clock=19660800)), con eso es suficiente, no?

Ferny, pues tengo conectado un led en B1, entonces esperaba ver alg'un cambio ahi... (igual no lo veo porque tengo demasiadas interrupciones por segundo?)
 
Ferny, pues tengo conectado un led en B1, entonces esperaba ver alg'un cambio ahi... (igual no lo veo porque tengo demasiadas interrupciones por segundo?)

Ahí es donde yo quería llegar. Si haces 1000 interrupciones por segundo, el led está dando 500 flashes de luz de 2ms de duración cada uno... eso el ojo humano no lo ve, para el ojo es como si el led estuviera encendido siempre.

Tienes que bajar la frecuencia de parpadeo hasta hacerla algo visible (5Hz por ejemplo), o bien con un oscilocopio mira la salida del pin del pic, deberías tener una señal cuadrada de 500Hz de frecuencia.

Saludos
 
para el ojo es como si el led estuviera encendido siempre.
Es que lo que me llama la atencion es que esta todo el rato apagado...

Estoy intentando calcular el valor inicial si quisiera tener una frecuencia de 5Hz pero no me aclaro del todo con el valor inicial:
- El timer va a trabajar a 19.6608MHz/4=4.9152MHz,
- y si quiero tener una frecuencia de 5Hz, entonces necesitare 4915200/5=983040 ciclos.
- El timer1 es de 16 bits, eso supone 65536 ciclos, por tanto necesitar'ia usar prescaler, pero el valor maximo es 8, lo que me daria 524288 ciclos, entonces, como puedo conseguir esa frecuencia? (igual es una pregunta basica, pero es que no veo la manera...)

Un saludo
 
Puedes dejar el timer1 y el prescaler como está. Luego usa una variable a modo de contador dentro de la interrupción. Cuentas el número de veces que salta la interrupción, y cuando llegue a X cantidad entonces cambias el estado del led y pones el contador a cero, y vuelta a empezar.
 
Hola otra vez! :)
Deber'ia hacer algo como esto? Ah'i estoy contando las veces que entra en la interrupci'on, y cuando llega a 15 hago que se encienda el led, no? Es que no consigo que funcione...


Código:
#include <16f877a.h>
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use delay(clock=19660800)

long Tmr1_inic;                   // 16-bit integer; Timer1's initial value
int i;

//--------------------------------------------------------------------------

#int_timer1                       // Timer1's interruptions

void OvFlw_Tmr1() {

   set_timer1(Tmr1_inic);         // Timer1's value for the next count  
   
   i++;  
   
   if (i==15) {
       output_toggle(PIN_B1); 
       i=0;
   }
}

//--------------------------------------------------------------------------

void main() {

   Tmr1_inic = 65536;                      // Timer1's initial value to have 1ms delay
   
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);    // Timer mode 
   set_timer1(Tmr1_inic);                  // Timer1 for the next count
   
   enable_interrupts(GLOBAL);          // Enable global interruptions
   enable_interrupts(INT_TIMER1);    // Timer1 interruptions 
   
   while (TRUE);                    // infinite loop
    
}
 
Si, es algo asi, pero con el preescaler seria mas optimizado, asi te ahorras instrucciones. Como es que tu cristal es de 19660800Hz?? Si pones uno de 20Mhz te sale mas sencillas las cuentas para calcular todo. Te pongo unos calculos para que veas como se calculan con uno de 20mhz, para hacer obtener 10Hz.

20/4=5Mhz
1/5Mhz=0,0000002, osea cada instruccion dura 200ns
(El prescaler se haria aqui, osea 5Mhz/1 para 1:1, 5Mhz/2 1:2....)

200ns x 65535= 13,11ms en este caso con el prescaler 1:1 y usando los 65535 ese es el maximo que puedes obtener. (unos 76Hz). Como vemos no nos vale.

con el prescaler 1:2 nos sale 38Hz
con el 1:4 19Hz
con el 1:8 9Hz. Como vemos con este nos podria funcionar.
Ahora bien:

10Hz=100ms
Con el prescaler 1:8 tendriamos que cada ciclo seria 1,6us.
100ms/1,6us = 62500.

Pues ya sabemos que nuestro set_timer1 lo tenemos que poner que haga 62500 ciclos, seria:
65535-62500=3035

Quedaria asi:

Prescaler: 1:8
settimer: 3035

Para frecuencias menores a 9hz tendrias que usar el sistema anterior por ejemplo, osea contar 10Hz y que cada 2 interrupciones cambie el pin, para obtener 5hz.

Espero haberte resolvido las dudas del prescaler.

Puede que tu programa funcione, pero ser error de conexion, fuses,.. puedes poner por ejemplo un pin a 1 (el que desees) y conectar un led (con su resistencia) para ver si se enciende, si no se enciende es que el pic no ha arrancado.

Tambien segun veo, tu programa no configura el TRISB, quizas esta como input, y no como output (salida).

Y otro error que veo, es que utilizas un long tmr1_inic;, deberias usar un int, ya que el long son 32bits y el int 16bits, el settimer solo aceptaria los 16bits, realmente no le influye a tu programa, pero es un error que deberias intentar evitar porque te puede crear muchos dolores de cabeza.
 
Algo así, pero tienes un fallo:

Tmr1_inic = 65536;

Eso obviamente está mal calculado.

Por otro lado, ¿qué es ese reloj de 19660800? ¿Eso está bien?

Creo que la frecuencia a la que lo estás haciendo funcionar aún es muy rápida para verla con el ojo. Hagamos unos cálculos. Voy a suponer que el reloj es de 20MHz, próximo a ese que tú tienes, es decir el ciclo de reloj sería de 50ns.

- La frecuencia de instrucción del PIC es 4 veces más lenta que el reloj, es decir un ciclo de instrucción dura 4 ciclos de reloj: 50ns x 4 = 200ns
- Vamos a hacer que el timer 1 desborde cada 1ms. Sabiendo que el valor máximo son 65535 y que para contar 1ms hacen falta 5000 ciclos de instrucción (5000 x 200ns = 1ms), el valor para pre-cargar el timer es de 65535 - 5000 = 60535.
- Por tanto: Tmr1_inic = 60535;
- Ahora, para conseguir por ejemplo una frecuencia de parpadeo de 2Hz (visible sin problemas), tenemos que cambiar el estado de la salida cada 250ms. Por tanto, el valor para comparar la i será 249 (ten en cuenta que el 0 también cuenta, por tanto son 250 veces las que cuenta en total).

Podría ser algo así. Te marco en rojo otros cambios que te propongo, por ejemplo declarar la variable long como unsigned, y declarar el PIN_B1 como salida:

#include <16f877a.h>
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use delay(clock=20000000)

unsigned long Tmr1_inic; // 16-bit integer; Timer1's initial value
int i;

//--------------------------------------------------------------------------

#int_timer1 // Timer1's interruptions

void OvFlw_Tmr1() {

set_timer1(Tmr1_inic); // Timer1's value for the next count

i++;

if (i==249) {
output_toggle(PIN_B1);
i=0;
}
}

//--------------------------------------------------------------------------

void main() {

Tmr1_inic = 60535; // Timer1's initial value to have 1ms delay

set_tris_b(0b11111101); // PIN_B1 como salida, resto entradas

setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); // Timer mode
set_timer1(Tmr1_inic); // Timer1 for the next count

enable_interrupts(GLOBAL); // Enable global interruptions
enable_interrupts(INT_TIMER1); // Timer1 interruptions

while (TRUE); // infinite loop

}
 
Última edición:
yo tambien vi lo del cristal a esa frecuencia y me resulto raro, sin embargo lo escribi en google y se ve que existe tal cristal, o bien es que varias personas han copiado un codigo incorrecto.

Sobre lo del long me confundi, segun he visto en CCS long es de 16bits, no se porque cada compilador le ponen distintos tamaños..
 
Os cuento, ahora si que me ha quedado claro como se hacen los calculos del valor inicial y del prescaler!

He vuelto a hacer los calculos para mi reloj (porque si que es de 19.6608 MHz), he hecho los cambios que me habeis propuesto, pero no he conseguido que funcione.
En principio las conexiones estan bien hechas, porque otros programas mas simples si que funcionan (no tengo problema para encender un led, el tema de las resistencias no lo tengo que hacer porque estoy usando los E-blocks de Matrix Multimedia http://www.matrixmultimedia.com/eblocks.php entonces ya vienen incluidas en el board de los leds (y la plaquita del PIC incluye un reloj de 19.6608MHz)), asi que ya no se que puedo estar haciendo mal o como modificar mi programa para conseguir ver parpadear el led...

Por otra parte, me queda la duda de la configuracion del TRISB, es necesario definir los pines como salidas? pensaba que al hacer output_toggle(PIN_B1), ese pin ya se configuraba directamente como salida.

Agradezco mucho vuestros comentarios, y la rapidez con la que contestais siempre, asi da gusto :)
 
creo que en ccs se llamaba set_tris_x (siendo x el puerto, en tu caso b), prueba a hacerlo el micro por defecto las pone como entradas 0xFF, creo que en el ccs tambien habia una configuracion del proyecto donde podias configurar los pines como entrada o salida, y su estado inicial.
 
hola compañeros realicé un ejercicio con timer 1 y este pic (16f873) y funciona correctamente pero al presionar el boton de MCLR del pic, no hace el reset. como no estoy tan familiarizado con lenguaje c, no se cual seria el problema. agradeceria su ayuda
 

Adjuntos

  • Archivos jdrv.rar
    17.2 KB · Visitas: 21
El pin de reset del PIC16F873, tan sólo es "RESET"
Y si estás simulando el programa, no va a hacer bien esa función.
Debes probar el proyecto físicamente y ahí si notarás cuando se produzca el reset.

---------- Actualizado ----------

Ese programa está muy mal y no cumple para nada con lo que se pretende en el esquema de simulación.
Si se supone que quieres hacer un cronómetro, el Timer 1 no tiene porqué estar configurado con un desborde cada 250 ms.

Lo del problema con el reset, podría ser porque no está conectado el pulsador y tampoco tiene polarización positiva el pin de reset.

Como quiera que sea, ese programa no tiene nada que ver con un cronómetro y mucho menos con displays de 7 segmentos.
Pero estás de suerte, porque hace poco tiempo realicé un programa para un cronómetro.

Lo adjunto para que lo analices y veas como funciona. También está basado en el Timer 1 y usa displays de 7 segmentos.
Muestra horas, minutos, segundos y milisegundos. O sea, un total de 8 displays multiplexados. Cronómetro multiplex con Timer 1.jpg

Aclaración: Este proyecto no ha sido probado físicamente.

Espero sea de utilidad. Suerte.
 

Adjuntos

  • 16F873A Cronómetro Displays 7 segmentos con Timer1.rar
    52.3 KB · Visitas: 85
Última edición:
Gracias por tu ayuda D@rkbytes. esto era un ejercicio personal para ver el comportamiento y saber si podia realizar un proyecto como el que subiste por mis propios metodos y con mi poca experiencia en micro controladores.

gracias y saludos
 
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
 

Adjuntos

  • Prueba Pulsos PIC.rar
    114.2 KB · Visitas: 15
Si lo que quieres es medir frecuencia, al mostrar el resultado cada 629.1456 mS, no estarás mostrando un valor correcto.
Mejor configura el Timer 1 para que desborde cada 100 mS y cuentas hasta 10 para lograr 1 segundo.

Con ese método que estás usando no lograrás medir frecuencias muy altas.
Dependerá también del tipo de variable que uses para almacenar el conteo.

Existen mejores métodos para realizar un frecuencímetro con microcontrolador.

Edit:
Otro detalle y causa del problema...
La variable "pulsos" debes limpiarla después de mostrar el valor, no cuando el conteo llegue a 3.
Y quita ese retardo de 100 mS que está después de mostrar los datos.
Te recomiendo que le des formato a la variable para no estar limpiando la pantalla cada vez que muestres los datos.
 
Última edición:
Gracias D@rkbytes, justo despues de publicar mi duda revise con detalle y encontre el error. Voy aplicar lo que mencionas de 1seg, una consulta a que te refieres con darle formato a la variable?
 
Darle formato a una variable es definir de qué forma será mostrado su valor.
Es decir, tú debes saber cuantos dígitos deben ser mostrados con respecto al valor máximo del resultado.
Entonces, si sabes que serán 3 dígitos porque tu valor máximo será 255, deberás indicar que siempre se muestren tres dígitos.

Ejemplo de formato a 3 dígitos (Variable de 8 bits):
printf (lcd_putc, "Resultado: %03u", valor);

Dando ese formato a la variable, si tenemos como valor un 7, se mostrará: Resultado: 007
Como verás, el formato %03u ya está colocando dos ceros a la izquierda y eso mantiene siempre tres dígitos.
Con eso evitas estar limpiando la pantalla para que no se sobreescriba el resultado, algo que es muy molesto porque se producen parpadeos.

En lenguajes de programación para aplicaciones de PC, el formato puede ser definido por el programador.
Pero en este caso para microcontroladores, únicamente se dispone de formatos predefinidos por el compilador.
Dichos tipos de formato los puedes encontrar en la ayuda del entorno de programación.

En PIC C Compiler puedes encontrar esa información escribiendo en la búsqueda de la ayuda (Index), la instrucción printf.
 
Última edición:
Atrás
Arriba