Autor Tema: Tomando pulsos del Timer1:velocimetro  (Leído 10733 veces)

0 Usuarios y 2 Visitantes están viendo este tema.

Desconectado Trev_11

  • PIC10
  • *
  • Mensajes: 44
Tomando pulsos del Timer1:velocimetro
« en: 10 de Octubre de 2007, 15:01:29 »
Hola :) ,intento tomar cada 400ms ,prefijados por interrupcion del timer0,la cantidad de pulsos recogidos por el timer1 en modo externo,mi idea seria ke con cada pulso se tenga una revolución,parece ke funciona bien,busco un numero redondo al temporizar el timer0 en 2ms,pero sobre el display se observa una oscilacion sobre la cantidad de pulsos correcta(si tiene ke leer 50 pulsos durante esos 400ms muestra sobre el LCD 50 un momento y 51 al sig.y de nuevo a 50)alguien puede ayudarme a corregir este error?.
Dejo el archivo proteus.
Saludos
Código: [Seleccionar]
#include <16f877.h>
#use delay(clock=4000000)
#fuses XT,PUT,BROWNOUT,NOPROTECT,NOLVP,NOWDT
#include <lcd.c>
#byte portb=6

double rev_s,rev_m;
double pulsos;
int aux;

#int_rtcc             
void muestreo()
{
if(aux==200)
{
aux=0;
pulsos=get_timer1();
set_timer1(0);
rev_s=pulsos/0.4;
rev_m=rev_s*60;
}
aux+=1;
set_timer0(130);
}


main()
      {
      set_tris_c(0x01);
      set_tris_d(0x00);
      lcd_init();
      setup_timer_0(rtcc_div_16|rtcc_internal);
      enable_interrupts(global);
      enable_interrupts(int_rtcc);
      setup_timer_1(t1_external_sync);
      set_timer1(0);
      set_timer0(130);
      lcd_putc("Rev/s=");
      lcd_gotoxy(1,2);
      lcd_putc("Rpm=");
      while(1)
         {
         lcd_gotoxy(8,1);
         printf(lcd_putc,"%.0f",rev_s);
         lcd_gotoxy(6,2);
         printf(lcd_putc,"%.0f",rev_m);
         delay_ms(3000);
         }
      }   
     

Desconectado DarkVect

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 302
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #1 en: 12 de Octubre de 2007, 11:39:11 »
Hay varias cosas... por partes:

Lo primero es que dentro de la interrupción del timer haces dos operaciones con decimales cuando por norma general hay que salir de la interrupción lo antes posible.

Yo lo cambiaría por un flag y la operación la haría en el main.

Por otro lado el retardo ese de 3 segundos en el main realizado con un delay_ms() particularmente no me gusta nada, ya que puede dar problemas. La solución es utilizar un timer para generar los retardos que te interesen y mediante un flag realizar las esperas.

Finalmente y si mis cálculos no han sido erróneos (estoy espeso esta mañana) la configuración de tu timer es:

T = (1 / 4MHz) · 4 · 16 · (256 - 130) = 0,002016

Si haces el muestreo cada 200 interrupciones tienes 0,4032 que NO son 400ms sino algo más por lo que ese error se puede ir sumando hasta hacer que te entre una interrupción más.

Desconectado Trev_11

  • PIC10
  • *
  • Mensajes: 44
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #2 en: 12 de Octubre de 2007, 19:37:57 »
Hola Darkvect,gracias por responderme,mira es verdad,saque las 2 cuentas fuera,ajuste el timer a 131 para ke de exactamente 20ms y sigo con el mismo problema sigue oscilando con un pulso de mas,no se ke se puede hacer pero igual te doy las gracias  :)

Desconectado MGLSOFT

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 7912
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #3 en: 12 de Octubre de 2007, 21:08:51 »
Una forma tonta que yo utilizo para establecer la configuracion del timer (cualquiera) es utilizar el Wizard que trae el CCS, abro un proyecto nuevo y le cargo el pic y el cristal que realmente voy a utilizar.
Luego activo la interrupcion del timer en cuestion, y por ultimo en la solapa de configuarcion de ese timer, elijo los valores hasta que me da el tiempo exacto que busco entre interrupciones, anda muy bien y me ahorra trabajo de los calculos, que son tediosos... :-/ :-/
Todos los dias aprendo algo nuevo, el ultimo día de mi vida aprenderé a morir....
Mi Abuelo.

Desconectado Trev_11

  • PIC10
  • *
  • Mensajes: 44
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #4 en: 13 de Octubre de 2007, 10:24:43 »
Bueno gracias Mglsoft lo voy a probar,aunque ya no creo que tenga mas vuelta esto,si no que otra cosa se les ocurre,a mi nada!,chau  :(

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #5 en: 13 de Octubre de 2007, 12:08:20 »
En efecto,tal como te indican por arriba el usar decimales es un mal rollo, ya que a medida que la frecuencia aumenta el error es mayor. Luego tienes en el programa una división de los pulsos por 0,4 o lo que es lo mismo una multiplicación de los pulsos por 2,5, otro decimal.

He estado haciendo números y con tus valores, el TMR0 se desborda cada 2016us, con lo que tomas datos del TMR1 cada 200·2016=403,2ms, con lo que el 2,5 debiera de ser 2,48 y no 2,5.

En fin yo corrigiría todo esto para que no aparecieran decimales.

Otra cosa, ¿cual es el intervalo de frecuencias (mínimo-máximo) que deseas? Te lo digo porque la fosc=4Mhz no sirve para cualquier intervalo de frecuencias.

Pon exactamente como tienes el programa ahora.

Un saludo

Desconectado Trev_11

  • PIC10
  • *
  • Mensajes: 44
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #6 en: 13 de Octubre de 2007, 13:24:48 »
Bueno,que tal  :) gracias por responder,bueno redondie el muestreo para hacerlo cada 500ms(cosa que no era mi idea,1/2 segundo me parece mucho tiempo,pero bueno esto lo hice con prueba y error)para que con ello me diera un numero redondo sin decimales y seguimos igual(sino mirenlo en el proteus),ahh y mi idea era medir la velocidad de motores DC oscilando entre las 3000 y 10000 rpm o sea entre los 50 y 150 pulsos que es donde menos error esto comete.Saludos

Código: [Seleccionar]
#include <16f877.h>
#use delay(clock=4000000)
#fuses XT,PUT,BROWNOUT,NOPROTECT,NOLVP,NOWDT
#include <lcd.c>
#byte portb=6

double rev_s,rev_m;
double pulsos;
int aux;

#int_rtcc             
void muestreo()
{
if(aux==250)                   
         {
         pulsos=get_timer1();   //Esto se ejecuta cada 2ms*250=500ms
         set_timer1(0);
         aux=0;
         }
aux+=1;
set_timer0(131);                //T=(1/4000000)*4*16*(256-131)=0.002s=2ms
}


main()
      {
      set_tris_c(0x01);
      set_tris_d(0x00);
      lcd_init();
      setup_timer_0(rtcc_div_16|rtcc_internal);
      enable_interrupts(global);
      enable_interrupts(int_rtcc);
      setup_timer_1(t1_external_sync);
      set_timer1(0);
      set_timer0(131);
      lcd_putc("Rev/s=");
      lcd_gotoxy(1,2);
      lcd_putc("Rpm=");
      while(1)
         {
         lcd_gotoxy(8,1);
         rev_s=pulsos*2;                 //No hay decimales.
         printf(lcd_putc,"%.0f",rev_s);
         lcd_gotoxy(6,2);
         rev_m=rev_s*60;
         printf(lcd_putc,"%.0f",rev_m);
         delay_ms(1000);
         }
      }   
     

Desconectado reiniertl

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1187
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #7 en: 13 de Octubre de 2007, 17:10:22 »
Yo tu no me preocuparía demasiado por la presición, un motor girando de 3000 a 10 000 rpm, puede que tenga un momento de giro que se corresponda con 3000rpm en un instante y otro después haya dado 150 vueltas a 3010rpm, así que no te preocupes demasiado, si quieres aumentar la presición, puedes hacer dos cosas, metes más pulsos por vuelta en el timer1 y mides por menos espacio de tiempo, un conjunto elevado de muestras y luego promedias, o simplemente dejas las cosas como están y no cojas demasiada lucha.

Recuerda que un motor es un dispositivo mecánico, así que para subir o bajar la velocidad en algunos cientos de rpm, tomará tiempo si la idea es controlar la velocidad de giro, creo que tu aplicación anda bien.

Lo que te aconsejan de irte volando de las subrrutinas de interrupción, es de momento lo mejor que puedes hacer, por ejemplo puedes tomar las muestras cada poco tiempo y acumularlas en una variable de promedio, y actualizar la LCD, cada 1s, notarás que es más estable el proceso.

Saludos
Reinier

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #8 en: 14 de Octubre de 2007, 11:44:43 »
Bueno creo que ya está, me piqué con él y hasta que no lo he sacado no he parado. Además esto lo tenía ya hecho por algún sitio, se lo solucioné a un Picmaniaco hace mucho tiempo, pero no he podido encontrarlo, así que me ha tocado hacerlo de nuevo y esta vez de manera diferente saltandome un par de reglas:

- En una interrupción haz lo que necesites y lárgate rápidamente :D

- En las temporizaciones no uses decimales hijo mío :D

Y es que las reglas estan para eso ... para romperlas.

Bueno ahí va el programa que me ha tenido en vilo desde las 5 de la mañana:

Código: [Seleccionar]
#include <16f876.h>

#fuses XT, NOPROTECT, NOWDT, NOPUT, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000)
#include <lcd.c>
#include <math.h>

#define radios 4                                                 //Encoder de 4 radios
           
float n;
int16 pulsos;
int8 interrupcion;
int16 velocidad_s,velocidad_m;

#int_RTCC
RTCC_isr()
{
   pulsos=get_timer1();                      //Se entra en la interrupción cada 10ms   
   interrupcion++;
   if(interrupcion==100)                      //10ms · 100 = 1s
   {
      n=pulsos/radios;     
      velocidad_s=floor(n+(n-floor(n)));      //Redondea n, por si recibe algún pulso
                                             //de más ó de menos
      velocidad_m=velocidad_s*60;
     
      lcd_putc('\f');                        //Borra display
      lcd_gotoxy(1,1);             
      printf(lcd_putc,"%lu rev/s %uradios",velocidad_s,radios);      //rev/s
      lcd_gotoxy(1,2);       
      printf(lcd_putc,"%lu r.p.m.",velocidad_m);                     //r.p.m.
      interrupcion=0;
      set_timer1(0);
      //set_rtcc(178);                     //Inicializa el TMR0
      set_rtcc(217);                        //Inicializa el TMR0     
   }
   else
   {
      //set_rtcc(178);                     //Inicializa el TMR0
      set_rtcc(217);                        //Inicializa el TMR0     
      set_timer1(pulsos);                   //Recupera los pulsos
   }


main()
{
   set_tris_c(0x01);                        //Entrada de pulsos por RC0
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_RTCC);
   
   //setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_128);
   //set_rtcc(178);                //10ms -> 10000us -> 10000/128=78,125 -> 256-78=178
   
   setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_256);
   set_rtcc(217);                //10ms -> 10000us -> 10000/256=39,0625 -> 256-39=217   

   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);
   set_timer1(0);
   interrupcion=0;   
   lcd_init();

   while(1);
}



Revisarlo, deshuesarlo y manosearlo todo lo que querais a tuti plen.

Ya me direis algo ... chao, chao

Un saludo
« Última modificación: 15 de Octubre de 2007, 15:55:18 por pocher »

Desconectado Trev_11

  • PIC10
  • *
  • Mensajes: 44
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #9 en: 14 de Octubre de 2007, 12:20:29 »
Hola :lol: ,bueno lo del promedio me parece interesante por lo menos ahora no oscila tanto,aunque da lecturas algo erroneas.
Código: [Seleccionar]
#include <16f877.h>
#use delay(clock=4000000)
#fuses XT,PUT,BROWNOUT,NOPROTECT,NOLVP,NOWDT
#include <lcd.c>
#byte portb=6

double rev_s,rev_m;
double pulsos;
int aux,flag;

#int_rtcc             
void muestreo()
{
if(aux==250)
         {
         flag+=1;
            if(flag==4)
            {
            pulsos+=get_timer1();
            pulsos/=4;
            rev_s=pulsos*2;
            rev_m=rev_s*60;
            set_timer1(0);
            flag=0;
            }
         aux=0;
         }
aux+=1;
set_timer0(131);                //T=(1/4000000)*4*16*(256-131)=0.002s=2ms
}


main()
      {
      set_tris_c(0x01);
      set_tris_d(0x00);
      lcd_init();
      setup_timer_0(rtcc_div_16|rtcc_internal);
      enable_interrupts(global);
      enable_interrupts(int_rtcc);
      setup_timer_1(t1_external_sync);
      set_timer1(0);
      set_timer0(131);
      lcd_putc("Rev/s=");
      lcd_gotoxy(1,2);
      lcd_putc("Rpm=");
      while(1)
         {
         delay_ms(1000);
         lcd_gotoxy(8,1);
         printf(lcd_putc,"%.0f",rev_s);
         lcd_gotoxy(6,2);
         printf(lcd_putc,"%.0f",rev_m);
         }
      }   

Desconectado Trev_11

  • PIC10
  • *
  • Mensajes: 44
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #10 en: 14 de Octubre de 2007, 12:27:43 »
Bueno bacan tu laburo gracias Pocher  :) a mi me cuesta bastante seguir esas reglas y pienso que bueno si el C nos permite realizar operaciones con decimales en ventaja del asembler creo que hay que usarlas pero si ustedes lo dicen...y teniendo en cuenta sus experiencias para mi que soy novato vale,pues de antemano gracias y me pongo a echarle un ojo a tu programa!  :-/ .Salu2 a todos.

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #11 en: 14 de Octubre de 2007, 12:47:02 »
Era broma, sigue las reglas, sobre todo si empiezas. Lo que ocurre que a veces te emperras en una cosa y te preguntas ¿y esto porqué no tiene que funcionar así? y es lo que me ha sucedido en este caso.

Mañana si tengo un rato, intentaré hacerlo todo desde el main y sin interrupciones ¿Quién dá más?

Un saludete

Desconectado Trev_11

  • PIC10
  • *
  • Mensajes: 44
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #12 en: 14 de Octubre de 2007, 12:56:42 »
Bacan!! funciona de pelos como diria Bart hehe  :-) salvo cuando pongo radios=1 pero bueno,ahora si que puedo seguir adelante!!,Pocher ni idea de esa flor de funcion jaja,ya me parecia necesario que existiera una función asi,pero bueno no tengo un buen manual de C,todo lo que aprendi lo hice chusmeando por aqui  :-/.Ahora que tengo la velocidad instantanea quiero poder ingresarle por un pad la velocidad de referencia y contrastarla permanentemente con aquella y poder realizar la corrección pertinente al duty del PWM,algo asi como un burdo control a lazo cerrado ya vere que me sale.Salu2!!

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #13 en: 14 de Octubre de 2007, 15:11:34 »
Cuantos más radios dibujes menor error. Lo normal es poner 4.

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: Tomando pulsos del Timer1:velocimetro
« Respuesta #14 en: 15 de Octubre de 2007, 13:48:03 »
Hola, ¡eh voila!

Os dejo el programa, en el cual no se emplean decimales para la temporización del TMR0, ya que lo hago girar una vuelta completa, sin precargarlo y al no haber precarga no se pierde tiempo en la instrucción de precarga (no hay set_timer0()).

Tampoco entra en la interrupción con lo que tampoco se pierde tiempo.

Para esta técnica es necesario usar un cristal de 3,2768MHz ó otro cualquiera de forma que no aparezcan decimales en el cálculo de la temporización y que aunque el cálculo de la temporización sea exacto no haya que precargar al TMR0 (el de 4MHz no sirve), es decir que la precarga sea de 0.

Como alguién habrá deducido esta es la técnica para programar un reloj con un PIC que funcione bien, sin retrasos ni adelantos.

Este es el programa:

Código: [Seleccionar]
#include <16f876.h>

#fuses XT, NOPROTECT, NOWDT, NOPUT, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use fast_io(B) 
#use delay(clock=3276800)       //Para conseguir 1s sin precargar (retrasos) al TMR0
#include <lcd.c>
#include <math.h>

#bit T0IF=0x0B.2

#define radios 4                                             //Encoder de 4 radios

main()
{
   float n;
   int16 pulsos;
   int8 cuenta_interrupciones;
   int16 velocidad_s,velocidad_m;
   
   lcd_init();
   lcd_putc('\f');   
   
   set_tris_c(0x01);                              //Entrada de pulsos por RC0
   
   setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_128);
   // El TMR0 se desbordará cada: Temp=(256-0)·(4·128)/3,2768=40ms (40ms · 25 = 1s)
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);
   
   enable_interrupts(GLOBAL);
   enable_interrupts(INT_RTCC);
   
   cuenta_interrupciones=0;
   set_timer1(0);             
   set_timer0(0);
   T0IF=0;
   
   while(1)
   {
      if(T0IF)
      {
         T0IF=0;
         cuenta_interrupciones++;
         if(cuenta_interrupciones==25)          //  40ms · 25 = 1s
         {   
            pulsos=get_timer1();           
           
            n=pulsos/radios;
           
            velocidad_s=floor(n+(n-floor(n)));   //Redondea n, por si recibe algún pulso
                                                //de más ó de menos
            velocidad_m=velocidad_s*60;
     
            lcd_putc('\f');                        //Borra display
            lcd_gotoxy(1,1);             
            printf(lcd_putc,"%lu rev/s %uradios",velocidad_s,radios);      //rev/s
            lcd_gotoxy(1,2);       
            printf(lcd_putc,"%lu r.p.m.",velocidad_m);                     //r.p.m.           
   
            cuenta_interrupciones=0;
            set_timer1(0);             
            set_timer0(0);   
         }
      }
   }
}
 

Como esta pregunta de como contar pulsos se ha hecho muchas veces a lo largo de estos años, dejaré unos días los programas para que los reviseis, posteriormente los subiré al subforo de Ejemplos para simular (sin quitarlos de aquí) donde añadiré la correspondiente simulación.

Yo aprendí a programar a base de picarme y resolver dudas a los demás, lástima que muchos de esos programas no me los copié. Podía haber hecho una colección y publicarla por aquí por si servía a alguien.

Un saludo
« Última modificación: 15 de Octubre de 2007, 15:49:53 por pocher »