Contar pulsos con CCP en modo captura

Realizar 500 veces la lectura del conversor ADC, no se me hace buena idea, aparte de que se puede producir el desborde de la variable contenedora.

Si quieres que la lectura se muestre más estable, usa el Timer 0 porque ya se está usando el Timer1.
Usa el desborde del Timer para incrementar una variable, y cuando llegue a x número, realizas la lectura del ADC.
 
Código:
#include <16f883.h>
#device ADC=10
#use     delay(internal = 8MHz)
#define  LCD_DATA_PORT getenv("SFR:PORTC")
#include <lcd.c>
#define led PIN_B5
int1 flagHayDatos=0; 
float const ticks_us = 4.0;          // microsegundos por Tick del Timer 1 @ 8 MHz. (Osc. Interno)
//float const ticks_us = 8.0;          // microsegundos por Tick del Timer 1 @ 8 MHz. (Osc. Cristal)
 int8 flancos;
int8 flag_flanco;
int16 periodo1,periodo2,periodo3;
int16 tiempo_alto,tiempo_bajo,tiempo_total;
float us_alto,us_bajo,us_total;
float frecuencia;
#INT_EXT
void sdi_externa_RB0 (void)
{
   flancos++;                          // Incrementar la variable "flancos"
   
   if(!flag_flanco)                    // Si el flanco del pulso es bajo...
   {
      if(flancos == 1)                 // Si el conteo de flancos es 1...
      {
         set_timer1(0);                // Limpiar el Timer 1
         periodo1 = get_timer1();      // "periodo1" tendrá el tiempo del pulso en alto.
      }
      if(flancos == 3)                 // Si el conteo de flancos es 3...
         periodo3 = get_timer1();      // "periodo3" tendrá el tiempo del pulso en alto.
         
         EXT_INT_EDGE(H_TO_L);         // Establecer la interrupción por flanco de bajada.
         flag_flanco = 1;              // Indicar que el próximo flanco será de bajada.
   }
   else                                // Caso contrario. (Pulso en estado alto)...
   {
      periodo2 = get_timer1();         // "periodo2" tendrá el valor del pulso en bajo.
      EXT_INT_EDGE(L_TO_H);            // Establecer la interrupción por flanco de subida.
      flag_flanco = 0;                 // Indicar que el próximo flanco será de subida.
       if(flagHayDatos==0){       // Si los datos anteriores han sido procesados ...
      flagHayDatos=1;          // Indico que ya hay nuevos datos de flancos para calcular
    }
   }
    if(flancos > 2)flancos = 0;         // Si la variable "flancos" llega a 3, ponerla a 0.
}
 void main()
{
long int var1=0, var2=0, var3=0;
 float valor, valor1,valor2;
   float duty;
   int8 porcentaje;
   
 setup_adc_ports(sAN1|sAN3);
 setup_adc(ADC_CLOCK_INTERNAL );
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
   enable_interrupts(int_ext);
   ext_int_edge(0,L_TO_H);
   enable_interrupts(global);
   
   lcd_init();
   
   #ZERO_RAM   // Limpiar RAM. (Variables en 0)
   
   while (true)
   {
   
 
  for(var1=0;var1<50;var1++)
  {
    valor=valor+ get_timer1();
    delay_us(62);  
  }
   periodo1=valor/50;
   
    for(var2=0;var2<50;var2++)
  {
    valor1=valor1+ get_timer1();
    delay_us(62);  
  }
   periodo2=valor/50;
    for(var3=0;var3<50;var3++)
  {
    valor2=valor2+ get_timer1();
    delay_us(62);  
  }
   periodo3=valor/50;
   
    if(flagHayDatos==1){
    output_HIGH(led);
      if((periodo3 > periodo2) && (periodo2 > periodo1))
      {
         tiempo_alto = periodo2 - periodo1;                 // Obtener el periodo del pulso en estado alto.
         tiempo_bajo = periodo3 - periodo2;                 // Obtener el periodo del pulso en estado bajo.
         tiempo_total = tiempo_alto + tiempo_bajo;          // Obtener el periodo de la frecuencia.
         us_alto = ticks_us * tiempo_alto;                  // Obtener el periodo en microsegundos del pulso en alto.
         us_bajo = ticks_us * tiempo_bajo;                  // Obtener el periodo en microsegundos del pulso en bajo.
         us_total = ticks_us * tiempo_total;                // Obtener los microsegundos en total de la frecuencia.
         frecuencia = 1 / (us_total / 1000000);             // Obtener la frecuencia.
         duty = ((float) tiempo_bajo / (float)(tiempo_bajo + tiempo_alto));
         porcentaje = (duty * 100) + 0.5; // Físicamente el + 0.5 puede no ser necesario. (Ajusta desviación pero consume ROM)
      } 
       flagHayDatos=0; 
    }
      lcd_gotoxy(1,1);
      printf(lcd_putc,"CPS:%03.0fHz",frecuencia);
      lcd_gotoxy(1,2);
      printf(lcd_putc,"DUTY:%02u%c",porcentaje,0x25);
       flagHayDatos=0; 
       
      delay_ms(100); // Retardo para disminuir parpadeos en la pantalla. (No afecta interrupción externa.)
      if(flagHayDatos==0){
      frecuencia = 0;
      porcentaje=0;
      output_low(led);
      }
      
    }
}
El adc me funciona bien, las variaciones las tengo en las muestras de frecuencia y duty es aquí que estoy haciendo un lio bárbaro con el promedio
 
Última edición:
Sinceramente no encuentro la complicación a algo tan sencillo como obtener un promedio.
Ya te mencioné sobre otra forma de obtener lecturas más estables.


 
Adjunto el código con lo expuesto, usando el Timer 0.
No sé si se consiga buena estabilidad físicamente de ésta forma, pero te puede dar ideas.
 

Adjuntos

  • Main.rar
    1.4 KB · Visitas: 13
Estuve haciendo pruebas físicamente con el programa que adjunto, y fueron satisfactorias.
La frecuencia se mantiene estable y el ciclo activo cambia conforme éste varíe.

Te vuelvo a recomendar que uses un osciloscopio y un frecuencímetro, porque tal vez las señales que estás tratando de leer, no son estables.
Así, por más que quieras estabilizar eso por software, no lo vas a conseguir.
 

Adjuntos

  • 16F887 Obtener porcentaje del ciclo activo II.rar
    47.8 KB · Visitas: 12
Si estuve trabajando bastante con esto y funciona muy bien, la señal entrante es muy baja y ruidosa! se mantiene estable y funciona, hay un solo problema que no se porque lo hace, estas midiendo ejemplo 146hz 58% duty y de golpe te tira 3457hz y queda colgado ahí puse capacitores por todos lados y lo hace pocas veces pero lo hace es ruido tendría que ver la placa de hacerla con mas masa, saludos
 
Lo que hice fue cambiar las variables para que cuando hay algún pico así no se pase, con la variable int no se pasa de 225, los parámetros no se como hacerlo, funciona muy bien lo único son esos picos que mete la bobina.

Y estoy tratando de colocar un mensaje de alerta cuando los amper llegan a 2,5 tendría que borrar la pantalla y mostrar "alerta" y cuando baje volver como estaba
If(adc > 200) {
Printf (lcd_putc"alerta" ;
}
Else if (Adc>=200)
 
Última edición:
Usa un bucle de retención hasta que el valor de los amperes se estabilice.
Por ejemplo:
while (amps > x)
{
lcd_putc("\f !Alerta! ");
lcd_gotoxy(1,2);
printf(lcd_putc,"%f Amps.",amps);
delay_ms(x);
}
 
Si lo arme asi, cuando supera los 2,5 amper da la alerta, ahora cuando bajan los amper no vuelve a su posición original porque?

set_adc_channel(3);
delay_us(20);
amper=0;
for(var2=0;var2<300;var2++)
{
amper=amper+ read_adc();
delay_us(62);
}
valor2 = amper / 300;
valor2 = valor2 * 19 / 1023;


lcd_gotoxy(11,2);
printf(lcd_putc,"%4.1f",valor2);

while (valor2 > 2.5)
{
lcd_putc("\f !Alerta! ");
lcd_gotoxy(1,2);
printf(lcd_putc,"%f amper."valor2);
delay_ms(100);
 
porque el valor de la variable "valor2", no se actualiza dentro del bucle. entra y nunca sale...

falta algo como esto dentro del bucle:

amper=amper+ read_adc();
delay_us(62);
}
valor2 = amper / 300;
valor2 = valor2 * 19 / 1023;

o cambiar el algoritmo.
 
Última edición:
while (valor2 >= 2.5){if (valor2 <2.4){break;}

lcd_putc("\f !Alerta! ");
lcd_gotoxy(1,2);
printf(lcd_putc,"valor superado!");
delay_ms(100);
valor2=0;


ahí casi estoy me falta limpiar bien la pantalla
 
en ese algoritmo no tiene mucho sentido el while, si utilizas solo la condicional es suficiente o sea
if(valor2 >=2.5){
lcd_putc("\f !Alerta! ");
lcd_gotoxy(1,2);
printf(lcd_putc,"valor superado!");
delay_ms(100);}

 
Última edición:
while (valor2 >= 2.5){if (valor2 <2.4){break;}

lcd_putc("\f !Alerta! ");
lcd_gotoxy(1,2);
printf(lcd_putc,"valor superado!");
delay_ms(100);
valor2=0;


ahí casi estoy me falta limpiar bien la pantalla

Creo, que como dijo el compañero D@rkbytes seria mas sencillo que crearas la rutina de lectura y conversión y lo llamaras desde tu bucle de alerta.
Te paso un ejemplo de voltimetro con alerta cuando llega a los 1 Ampers.:
 

Adjuntos

  • Voltimetro.rar
    108.3 KB · Visitas: 14
Me sirvió muchas gracias!

Ahora este ejemplo de voltimetro la medición de los amper la toma en proporción de la tension o es conveniente poner una resistencia shunt?
Y por otro lado se ejecuta más rápido los programas usando funciones, que escribiendo el código a lo largo como lo hacía yo.
 
Atrás
Arriba