TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: RedPic en 22 de Octubre de 2006, 15:20:28

Título: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: RedPic en 22 de Octubre de 2006, 15:20:28
Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo y la Frecuencia

A.- Conceptos involucrados:

Si lo creéis oportuno podéis antes darle un repaso a la misma sección de Conceptos de la parte 1ª de esta Técnica en C : Tiempo en Alto (http://www.todopic.com.ar/foros/index.php?topic=14428.0). Los coceptos son los mismos y aquí solo vamos a hacer algunas consideraciones sobre los mismos.

Decíamos allí que el Tiempo T que transcurre ente dos flancos sucesivos de subida (o de bajada) es lo que conocemos como Periodo del Pulso (Period), o sea: lo que dura un pulso completo.

Cada T segundos (ó milisegundos, ó microsegundos) tenemos un nuevo pulso completo.

Como vemos, cada pulso tiene un Tiempo en Alto (High Time) y un Tiempo en estado Bajo (Low Time). La suma de los dos es el Periodo T del pulso.

(http://picmania.garcia-cuervo.net/images/serie_tecnicas_en_c_midiendo_un_pulso_2_complete_1.gif)

Como vemos en la imagen superior un pulso completo comienza con un flanco de subida en T1, sigue con uno de bajada en T2 y concluye en el nuevo flanco de subida T3.

Entre T1 y T2 permanece el pulso en Alto, Wh, mientras que entre T2 y T3 lo hace en bajo, Wl.

El tiempo transcurrido entre T1 y T3 es el Periodo del Pulso. Si este periodo lo expresamos en Segundos entonces su inverso, 1 / Periodo, es la Frecuencia del tren de pulsos.

Estos dos valores son los quie vamos a calcular con nuestro PIC: el Periodo T y la frecuencia F.

B.- Técnica a Aplicar:

Para medir el periodo T del pulso vamos a utilizar los dos mismos recursos que en la 1ª parte de este artículo, y de los que disponen la gran mayoria de los PIC's, tanto los de la serie 18F como los 16F:

El TIMER1 y la Interrupción Externa por RB0.

Para ver los detalles de estos recursos dadle también un vistazo a la primera parte de esta Técnica en C 1ª Parte. Tiempo en High (http://www.todopic.com.ar/foros/index.php?topic=14428.0).

(Quito este trozo para no sobrepasar los límites de un Post que nos impone el Foro  :P)


Aunque para calcular el Periodo del pulso y/o su Frecuencia no es necesario tomar el Tiempo T2 intermedio del Flanco de Bajada, ya que solo nos son necesarios T1 y T3, los dos flancos de Subida sucesivos, vamos a calcularlos todos ya que así tendremos también cuánto tiempo permanece tanto en alto como en bajo y tendremos de esta forma el tanto por ciento de la primera fase sobre la segunda del pulso, o sea: El Duty Cicle.

* Lo que vamos a realizar es:

1.- Configuraremos la Int_Ext por RB0 para detectar inicialmente el Flanco de Subida.
2.- Al llegarnos este Flanco de Subida guardaremos el valor en ese momento de TIMER1 y ...
3.- Cambiaremos la configuración de la Int_Ext por RB0 para detectar el siguiente Flanco de Bajada.
4.- Cuando nos llegue el Flanco de Bajada guardaremos de nuevo el valor de TIMER1 y ...
5.- Volveremos a configurar la Int_Ext por RB0 para detectar de nuevo un Flanco de Subida. Y ...
6.- Al llegarnos este nuevo Flanco de Subida guardaremos también el valor en ese momento de TIMER1 y ...
7.- Con estos tres valores de TIMER1 tendremos, expresados en Tick's de TIMER1, restando el tercero del primero, el tiempo que ha durado el Pulso. Multiplicando dicho número de Tick's de TIMER1 por el tiempo que dura cada Tick (dependiente del cristal que usemos) tendremos el Perido T que estamos buscando. Con 1 / T obtendríamos la frecuencia del Pulso.

C.- Implementación en C:

Para implementar nuestro Código en C vamos a centrarnos en los puntos que hemos descrito en la sección anterior.

Para configurar inicialmente el flanco de subida a detectar utilizaremos igual quen la primera parte:

Código: C
  1. ext_int_edge(0,L_TO_H);                  // Configuro captura de 1er flanco de subida
  2.    flagToggleFlanco = 0;                    // inicializo el Flag para cambiar de flanco
  3.    enable_interrupts(int_ext);              // Habilito las interrupciones necesarias
  4.    enable_interrupts(global);

Hemos añadido la variable int1 flagToggleFlanco debido a que una vez establecido el flanco a detectar no tenemos oportunidad de saber qué flanco es el que estamos esperando, así si flagToggleFlanco tiene un valor de 0 es que estamos esperando el de Subida y si por el contrario flagToggleFlanco tiene un valor de 1 entonces es el de bajada el que esperamos.

Con esta configuración inicial podemos ya escribir nuestra rutina ISR:

Código: C
  1. #int_ext
  2. void handle_ext_int(){
  3.  
  4.    ++numFlancoQueLlega;                     // Cuento flanco que nos llega
  5.    if(flagToggleFlanco==0){                 // He recibido Flanco de Subida
  6.       if(numFlancoQueLlega==1){
  7.          set_timer1(0);                     // Reinicio TMR1
  8.          t1=get_timer1();                   // Guardo en t1 el valor de TMR1 al primer Flanco de Subida
  9.       }
  10.       if(numFlancoQueLlega==3){
  11.          t3=get_timer1();                   // Guardo en t1 el valor de TMR1 al primer Flanco de Subida
  12.          if(flagHayDatos==0){               // Si los datos anteriores han sido procesados ...
  13.             flagHayDatos=1;                 // Indico que ya hay nuevos datos de flancos para calcular
  14.          }
  15.       }
  16.       ext_int_edge(0,H_TO_L);               // Configuro para capturar siguiente flanco de Bajada
  17.       flagToggleFlanco=1;                   // Indico que el siguiente flanco será de Bajada
  18.    } else {                                 // He recibido Flanco de Bajada
  19.       t2=get_timer1();                      // Guardo en t2 el valor de TMR1 al Flanco de Bajada
  20.       ext_int_edge(0,L_TO_H);               // Configuro para capturar siguiente flanco de subida
  21.       flagToggleFlanco=0;                   // Indico que el siguiente flanco será de Subida
  22.    }
  23.    FLASH;                                   // Reproduzco la entrada mediante un LEd en E0;
  24.    if(numFlancoQueLlega==3){
  25.       numFlancoQueLlega=0;
  26.    }
  27. }

Fijaos que en la rutina ISR anterior no realizamos ningún cálculo. Aplicamos aqui el principio fundamental de que "dentro de una rutina de interrupción haz lo debas y abandónala lo antes que puedas". Asi que si tenemos valores válidos de T1, T2 y T3 entonces solo hacemos que flagHayDatos valga 1 y ya trataremos estos valores dentro de main().

Con numFlancoQueLlega sabremos qué flanco de Subida es T1 y cúal es T3 ya que solo cuando obtengamos T3 daremos por concluida nuestra captura.

Código: C
  1. if(flagHayDatos==1){                  // Detecto que ya hay datos de flancos ...
  2.          if((t3>t2)&&(t2>t1)){              // Compruebo que estoy en la misma vuelta de TMR1
  3.             tth = t2 - t1;                  // Calculo en Tick's de TMR1 el tiempo en Alto
  4.             ttl = t3 - t2;                  // Calculo en Tick's de TMR1 el tiempo en Bajo
  5.             tt  = tth + ttl;                // Calculo en Tick's de TMR1 el Periodo del Pulso
  6.             sth = uSxTick * tth;            // Calculo en uS el tiempo en Alto.
  7.             stl = uSxTick * ttl;            // Calculo en uS el tiempo en Bajo.
  8.             st  = uSxTick * tt;             // Calculo en uS el tiempo el Periodo.
  9.             f   = 1 / (st / 1000000);       // Calculo la Frecuencia
  10.             flagHayTransmitir=1;            // Indico que tengo nuevo valor para transmitir
  11.          }
  12.          flagHayDatos=0;                    // Indico que ya han sido procesados los datos.
  13.       }


Cuando flagHayDatos es 1 podemos proceder a calcular los Ticks transcurridos entre un flanco y otro.

Fijaos que preguntamos si T3 es mayor que T2 y que éste es a su vez mayor que T1. Esto es debido a una limitación que nos impone el no tener en cuenta vueltas completas de TIMER1 para realizar nuestros cálculos: T3, T2 y T1 deben estar dentro de una misma vuelta de TIMER2, o sea que el pulso debe ser menor que 13,1072 milisegundos. Notad que cuando detectamos el primer  flanco volvemos a poner TIMER1 a cero para recomenzar siempre cerca del inicio de la cuenta de TIMER1 y evitar asi su desborde.

Para computar tiempos de pulso mayores 13,1072 milisegundos solo habría que computar tambien cuantas veces se desborada completo TIMER1 y realizar los cálculos aritméticos correspondientes.

Al igual que hacíamos en la Rutina ISR en que no calculábamos sino que solo indicábamos que podíamos calcular, aquí una vez realizados los calculos de Tick´s y su equivalencia en segundos no los transmitimos, sólo indicamos que hay datos para transmitir mediante poner a 1 flagHayTransmitir.

Para transmitir el último valor computado vamos a implementar una recepción vía serie RS232 que al recibir el comando 'T' nos envía el último valor correcto registrado:

Código: C
  1. if(Command!=0x00){                    // Si he recibido un comando vía Serie ...
  2.          if(Command=='T'){                  // Si el comando es 'T' (Transmite) ...
  3.             if(flagHayTransmitir==1){       // Si hay algo pendiente de transmitir ...
  4.                printf("Ticks ....... H  %4Lu + L  %4Lu =  %4Lu\r\n",tth,ttl,tt);
  5.                printf("uSegundos ... H %3.1f + L %3.1f = %3.1f F = %4.2f Hz\r\n\n",sth,stl,st,f);
  6.                flagHayTransmitir=0;         // Indico que ya he transmitido lo pendiente.
  7.             }
  8.          }
  9.          Command=0x00;                      // Indico que ya he procesado el comando.
  10.       }

Con todo esto el programa completo para nuestro Detector de Periodo y Frecuencia de un Pulso queda así:

Código: C
  1. ////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Midiendo_un_pulso_2_Complete.c
  4. //
  5. // SERIE: "Técnicas en C" para el Foro TODOPIC
  6. //
  7. // (c) 10.2006 by RedPic
  8. //
  9. // Propósito: Medir los tiempos en alto y bajo que permanece un pulso : Periodo y Frecuencia
  10. //
  11. // Condiciones de Test: Inyección por RB0 de una señal de 2 Khz (0.5 ms de periodo)
  12. //
  13. // Técnica Empleada: Detectar mediante la Interrupción Externa por RB0
  14. //                   un flanco de subida de un pulso, guardar el estado
  15. //                   de TMR1,  detectar  a  continuación el  siguiente
  16. //                   flanco de bajada, guardar el nuevo estado de TMR1,
  17. //                   detectar a continuación un nuevo flanco de subida,
  18. //                   guardar nuevamente el estado de TMR1 y realizar la
  19. //                   correspondiente substracción de ellos para obtener
  20. //                   el tiempo que permanece en alto y bajo y transmitir
  21. //                   los resultados mediante el puerto RS232 a petición.
  22. //
  23. ////////////////////////////////////////////////////////////////////////////////////
  24.  
  25.  
  26. #include <18f4550.h>
  27. #fuses HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN
  28. #use delay(clock=20000000)
  29.  
  30. #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
  31.  
  32.  
  33. ////////////////////////////////////////////////////////////////////////////////////
  34. //
  35. // Defines y Constantes
  36. //
  37. ////////////////////////////////////////////////////////////////////////////////////
  38.  
  39. #define LED PIN_E0                          // Defino el Pin del Led
  40. #define FLASH Output_Toggle(LED)            // Defino la funcion Flash de monitor
  41.  
  42. float const uSxTick = 0.2;                  // Microsegundos por Tick de TMR1 a 20 Mhz
  43.  
  44.  
  45. ////////////////////////////////////////////////////////////////////////////////////
  46. //
  47. // Variables en RAM
  48. //
  49. ////////////////////////////////////////////////////////////////////////////////////
  50.  
  51. char  cRec=0x00;                            // Último caracter recibido via serie
  52. char  Command=0x00;                         // Comando a procesar
  53. int8  numFlancoQueLlega=0;                  // Número de Flanco que llega
  54. int1  flagToggleFlanco=0;                   // Flag para cambiar de flanco
  55. int16 t1=0x00,t2=0x00,t3=0x00;              // Variables para guardar estados de ...
  56. int16 tth=0x00,ttl=0x00,tt=0x00;            // Timers y pulsos.
  57. float sth=0.0,stl=0.0,st=0.0,f=0.00;        // Para hacer las restas oportunas en uS
  58. int1  flagHayDatos=0;                       // Flag para indicar que ya hay datos de ..
  59.                                             // dos flancos (de subida y bajada)
  60. int1  flagHayTransmitir=0;                  // Flag para indicar que hay datos para ...
  61.                                             // Transmitir al PC.
  62.  
  63. ////////////////////////////////////////////////////////////////////////////////////
  64. //
  65. // Interrupción por Recepción Serie RS232
  66. //
  67. ////////////////////////////////////////////////////////////////////////////////////
  68.  
  69.  
  70. #int_rda
  71. void handle_rda_int(){
  72.  
  73.    if(kbhit()){                             // Si hay algo pdte de recibir ...
  74.       cRec=getc();                          // lo recibo sobre cRec ...
  75.       if(cRec!=0x00){                       // Si es distinto de \0 ...
  76.          Command=ToUpper(cRec);             // cargo cRec sobre Command para procesarlo
  77.       }                                     // pasándolo a Mayúsculas para no confundir.
  78.    }
  79. }
  80.  
  81. ////////////////////////////////////////////////////////////////////////////////////
  82. //
  83. // Interrupción por Externa por Cambio de Flanco en RB0
  84. //
  85. ////////////////////////////////////////////////////////////////////////////////////
  86.  
  87.  
  88. #int_ext
  89. void handle_ext_int(){
  90.  
  91.    ++numFlancoQueLlega;                     // Cuento flanco que nos llega
  92.  
  93.    if(flagToggleFlanco==0){                 // He recibido Flanco de Subida
  94.  
  95.       if(numFlancoQueLlega==1){
  96.          set_timer1(0);                     // Reinicio TMR1
  97.          t1=get_timer1();                   // Guardo en t1 el valor de TMR1 al primer Flanco de Subida
  98.       }
  99.       if(numFlancoQueLlega==3){
  100.          t3=get_timer1();                   // Guardo en t1 el valor de TMR1 al primer Flanco de Subida
  101.          if(flagHayDatos==0){               // Si los datos anteriores han sido procesados ...
  102.             flagHayDatos=1;                 // Indico que ya hay nuevos datos de flancos para calcular
  103.          }
  104.       }
  105.       ext_int_edge(0,H_TO_L);               // Configuro para capturar siguiente flanco de Bajada
  106.       flagToggleFlanco=1;                   // Indico que el siguiente flanco será de Bajada
  107.  
  108.    } else {                                 // He recibido Flanco de Bajada
  109.  
  110.       t2=get_timer1();                      // Guardo en t2 el valor de TMR1 al Flanco de Bajada
  111.       ext_int_edge(0,L_TO_H);               // Configuro para capturar siguiente flanco de subida
  112.       flagToggleFlanco=0;                   // Indico que el siguiente flanco será de Subida
  113.    }
  114.  
  115.    FLASH;                                   // Reproduzco la entrada mediante un LEd en E0;
  116.  
  117.    if(numFlancoQueLlega==3){
  118.       numFlancoQueLlega=0;
  119.    }
  120.  
  121. }
  122. void main() {
  123.  
  124.  
  125.     ////////////////////////////////////////// INICIALIZACIONES GENERALES
  126.  
  127.    delay_ms(333);                           // Espero a que todo se estabilice e ...
  128.    disable_interrupts(global);              // Inicializo el Micro y ...
  129.    disable_interrupts(int_timer1);          // deshabilitando todo lo no necesario ...
  130.    disable_interrupts(int_rda);
  131.    disable_interrupts(int_ext);
  132.    disable_interrupts(int_ext1);
  133.    disable_interrupts(int_ext2);
  134.    setup_adc_ports(NO_ANALOGS);
  135.    setup_adc(ADC_OFF);
  136.    setup_spi(FALSE);
  137.    setup_psp(PSP_DISABLED);
  138.    setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
  139.    setup_timer_0(RTCC_OFF);
  140.    setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  141.    setup_timer_2(T2_DISABLED,0,1);
  142.    setup_timer_3(T3_DISABLED);
  143.    setup_comparator(NC_NC_NC_NC);
  144.    setup_vref(FALSE);
  145.    port_b_pullups(FALSE);
  146.    delay_ms(333);
  147.  
  148.    /////////////////////////////////////////// INICIALIZACIONES PERTINENTES A LA APLICACION
  149.  
  150.    set_tris_c(0b10000000);                  // Habilito como entrada RC7 para canal RS232
  151.  
  152.    ext_int_edge(0,L_TO_H);                  // Configuro captura de 1er flanco de subida
  153.    flagToggleFlanco = 0;                    // inicializo el Flag para cambiar de flanco
  154.  
  155.    enable_interrupts(int_rda);              // Habilito las interrupciones necesarias
  156.    enable_interrupts(int_ext);
  157.    enable_interrupts(global);
  158.  
  159.    printf("\r\nMidiendo un pulso : Periodo\r\n");
  160.    printf("By Redpic para Foro TODOPIC\r\n\n");
  161.  
  162.    do {
  163.  
  164.       if(flagHayDatos==1){                  // Detecto que ya hay datos de flancos ...
  165.          if((t3>t2)&&(t2>t1)){              // Compruebo que estoy en la misma vuelta de TMR1
  166.             tth = t2 - t1;                  // Calculo en Tick's de TMR1 el tiempo en Alto
  167.             ttl = t3 - t2;                  // Calculo en Tick's de TMR1 el tiempo en Bajo
  168.             tt  = tth + ttl;                // Calculo en Tick's de TMR1 el Periodo del Pulso
  169.             sth = uSxTick * tth;            // Calculo en uS el tiempo.
  170.             stl = uSxTick * ttl;            // Calculo en uS el tiempo.
  171.             st  = uSxTick * tt;             // Calculo en uS el tiempo.
  172.             f   = 1 / (st / 1000000);       // Calculo la Frecuencia
  173.            
  174.             flagHayTransmitir=1;            // Indico que tengo nuevo valor para transmitir
  175.          }
  176.          flagHayDatos=0;                    // Indico que ya han sido procesados los datos.
  177.       }
  178.       if(Command!=0x00){                    // Si he recibido un comando vía Serie ...
  179.          if(Command=='T'){                  // Si el comando es 'T' (Transmite) ...
  180.             if(flagHayTransmitir==1){       // Si hay algo pendiente de transmitir ...
  181.  
  182.                printf("Ticks ....... H  %4Lu + L  %4Lu =  %4Lu\r\n",tth,ttl,tt);
  183.                printf("uSegundos ... H %3.1f + L %3.1f = %3.1f F = %4.2f Hz\r\n\n",sth,stl,st,f);
  184.  
  185.                flagHayTransmitir=0;         // Indico que ya he transmitido lo pendiente.
  186.             }
  187.          }
  188.          Command=0x00;                      // Indico que ya he procesado el comando.
  189.       }
  190.  
  191.  
  192.    } while (TRUE);
  193. }

D.- Ejemplo funcionando:

(http://picmania.garcia-cuervo.net/images/serie_tecnicas_en_c_midiendo_un_pulso_2_complete_dump.gif)

Y esto es todo por hoy, amigos.  :mrgreen:
Mañana, más.

Título: Re: Serie Técnicas en C : Midiendo un pulso. 2ª Parte. El pulso completo. El Periodo
Publicado por: Azicuetano en 22 de Octubre de 2006, 19:34:31
Muy buen artículo Diego.

La de horas que vas a ahorrar a la gente que lea esto!!! jeje



Un saludo desde Alicante
Título: Re: Serie Técnicas en C : Midiendo un pulso. 2ª Parte. El pulso completo. El Periodo
Publicado por: Nocturno en 23 de Octubre de 2006, 01:22:07
(Quito este trozo para no sobrepasar los límites de un Post que nos impone el Foro  :P)
Jajaja, ¡se te ha quedado pequeño el foro, Diego!

Enhorabuena por esta nueva serie divulgativa que acabas de comenzar.
Título: Re: Serie Técnicas en C : Midiendo un pulso. 2ª Parte. El pulso completo. El Periodo
Publicado por: RedPic en 23 de Octubre de 2006, 02:36:02
Jua, jua, jua  :D  :D  :D

Después no me creéis cuando hablo de "mis novelas" ...  :mrgreen:

Y es que me pongo, me pongo ... y no se cuando parar ... ja ja ja  :D  :D  :D

Título: Re: Serie Técnicas en C : Midiendo un pulso. 2ª Parte. El pulso completo. El Periodo
Publicado por: M.C_Barquin en 23 de Octubre de 2006, 10:51:51
Como siempre enhorabuena D.Diego.Magnífico artículo con el que todos aprendemos un poquito más.Saludos.
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: pikman en 30 de Octubre de 2006, 00:46:07
Estimado RED, tengo que decirte que te admiro por tu capacidad de enseñar..., vaya eres todo un profesor, pues es muy interesante lo que has planteado, excelente tu trabajo, graficos, bueno para sacarse el sombrero, te queria comentar que yo hice algo parecido pero usanso el CCP, que tambien genera una interrupcion en cada cambio y captura el registro del TMR1, no se si es exactamente lo mismo aca va el codigo entonces, bueno Red, gracias por tu aporte al foro, saludos

//**********************************************************************
// viento2.c
//
// Calcula la velocidad de rotacion contando los pulsos recividos
//
//**********************************************************************

#include <16F877.h>

#fuses HS, NOWDT, NOLVP
#device *=16 ADC=10

#define COMENZAR  0
#define CONTANDO  1
#define TERMINO   2
#define VECES 2

#use delay (clock=10000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#use standard_io (A)
#use standard_io (B)


// Variables Globales

char cEstado;
int32 int32Cont;
float fRpm;

//************************************************************************
// RUTINAS DE INTERRUPCION

#int_ccp1
void CCP1__isr(void) {

    if (cEstado == CONTANDO) {       // Segundo Pulso
        int32Cont += CCP_1;           // Toma el valor de captura
        cEstado = TERMINO;           // Evito que vuelva a procesarse durante esta interrupcion
//        printf("          int32Cont %lu     CCP1: %lu\r",int32Cont,CCP_1);
    }
    if (cEstado == COMENZAR)  {      // Primer Pulso
        set_timer1(0);               // Restaura el timer1 en este pulso
        cEstado = CONTANDO;
        int32Cont = 0;
  //      printf("\r  Timer1 a CERO\r");
    //    printf("I int32Cont: %lu\r",int32Cont);
    }
} // Fin CCP1__isr()


//************************************************************************
// CALCULO DE LA VELOCIDAD DEL VIENTO
//
void velocidad_viento(void){

    char cCnt;
    int32 int32Suma;
    long tiempo;

    cEstado = TERMINO;
    while (TRUE) {
      int32Suma = 0;
      int32Cont = 0;
      for (cCnt = 0; cCnt < VECES; cCnt++) {       // Acumula VECES lecturas
        cEstado = COMENZAR;                        // Permite comenzar a la interrupcion
        while (cEstado != TERMINO) {               // Espera hasta que se activa la interrupcion
          tiempo =  get_timer1();
          if (tiempo > 65500) {                    // Controla si el contador excedio el tiempo. Valor Original = 60000
            set_timer1(0);                         // Restaura el timer1 para seguir sumando el tiempo de este pulso
            int32Cont += tiempo;                   // Acumula para velocidades de viento bajas
//printf("B int32Cont: %lu\r",int32Cont);
          }                                        // Fin if
        }                                          // Fin if
        int32Suma += int32Cont;                    // Acumula los tiempos
      }                                            // fin for

//      printf("int32Suma %lu    \r",int32Suma);
      int32Suma /= VECES;                          // Saca el promedio de las VECES que se tomo la medicion
//      printf("int32Suma %lu    \r",int32Suma);
      fRpm = 1 / ( float ) int32Suma / 8 * 2.5;    // Periodo en uS
      fRpm *= 1000000;                             // Periodo en segundos
      printf("\rFRECUENCIA %05.1f Hz\r\r",fRpm);
     // fRpm *= 60;                                 // Periodo en minutos
     //printf("               %f     \r", fRpm);
    }

}// Fin velocidad_viento()





//*****************************************************************
// MAIN
//
void main (void){

    setup_timer_1 (T1_INTERNAL | T1_DIV_BY_8 );
    setup_ccp1 (CCP_CAPTURE_FE);                    // Captura cada pulso = 0 (0v)

    delay_ms (200);

    enable_interrupts (INT_CCP1);
    enable_interrupts (GLOBAL);
    while(1){
      velocidad_viento();
    }                                                //Fin While

} // Fin Main

//*****************************************************************************

Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 19 de Agosto de 2007, 18:10:24
Hola RedPic.

La causa de mi mensaje esque leyendo los grandes articulos q haces, que de paso dejame decirte q son muy benos, exactamente el "Midiendo un pulso - 4ª Parte - El pulso completo - El Periodo y la Frecuencia" me tope con un problema.

Entiendo como haces todo, aunq aveces tengo dificultad por entender esto de los TIMER0, 1,... mas que nada en la configuracion de estos. Bien, el problema q tengo esq yo uso el PIC18F2550 y para compativilizarlo con esa Tecnica en C q utilizas para medir un pulso completo solo cambie el #include y quite en el main esto: setup_psp(PSP_DISABLED) porque el F2550 no lo reconoce.

Lo q sucede esq funciona y todo pero cuando genero unos pulsos (porq tengo un generador, pero ahi no hay problema) grandes, el pic por rs232 muestra otro valor diferent a este (no es igual al q muestra el osciloscopio) y ensi lo q quisiera saber es: ¿La configuracion q haces al empezar el main (las desabilitaciones y eso) esta bien si pongo el 18F2550? y sino puedes porfavor decirme como esq quedaria para este pic? Ojala no sea en los fusibles tampoco.

Disculpa mi ignorancia.


Gracias, salu2


PD: No quise pretendr ser egoista, losiento  :P
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 20 de Agosto de 2007, 23:22:37
Antes que nada disculpen el doble post.

Lo q sucede esq viendolo y viendolo me di cuenta q mostraba esos valores por rs232 disitintos al del osciloscopio porq el pulso era muy grande y el Timer1 llegaba a su desbordamiento (13,...). Bien, entonces cambie: setup_timer_1(T1_INTERNAL | T1_DIV_BY_1) por:  setup_timer_1(T1_INTERNAL | T1_DIV_BY_2) (ó 4, 6, 8) pero me lee valores diferentes porq creo q demora mas o nose q es lo q pasara.

Entonces, tu dijiste q no meterias nada de un conteo por desbordamiento del timer1 pero parece q es lo necesito, amenos q tengas otra forma para hacerlo, por ahora es la unica forma q veo poder hacerlo, contar las veces q se desbordo. Lo q quisiera, y porfavor, esq me ilustraras como seria este conteo por desbordamiento yaq con los Timer's no me meto mucho por ahora porq no los entiendo bien y nose como hacer un conteo de este. Espero no sea mucho pedir un ejemplo pero es lo q quiero, disculpa.

Gracias por tu tiempo.


salu2 8) 8) 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 21 de Agosto de 2007, 03:06:41
Ahora estoy en el trabajo, cuando llegue a casa te contesto a todo, peor para empezar muéstranos tu código y trabajamos sobre él.
 
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 21 de Agosto de 2007, 16:08:18
Bien gracias.

Lo q llevo avanzado en mi codigo no es mas q el cambio del #include y una directiva en el main q es solo para el 18F450.

Bueno igual aqui lo pongo:
Código: [Seleccionar]
#include <18f2550.h>
#fuses HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN
#use delay(clock=20000000)

#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)


////////////////////////////////////////////////////////////////////////////////////
// Defines y Constantes
////////////////////////////////////////////////////////////////////////////////////
#define LED PIN_B5               //E0 Defino el Pin del Led
#define FLASH Output_Toggle(LED) // Defino la funcion Flash de monitor

float const uSxTick = 0.2;       // Microsegundos por Tick de TMR1 a 20 Mhz


////////////////////////////////////////////////////////////////////////////////////
// Variables en RAM//
////////////////////////////////////////////////////////////////////////////////////
char cRec = 0x00;                      // Último carácter recibido vía serie
char Command = 0x00;                   // Comando a procesar
int8 numFlancoQueLlega = 0;            // Número de Flanco que llega
int1 flagToggleFlanco = 0;             // Flag para cambiar de flanco
int16 t1 = 0x00, t2 = 0x00, t3 = 0x00;       // Variables para guardar estados de ...
int16 tth = 0x00, ttl = 0x00, tt = 0x00;     // Timers y pulsos.
float sth = 0.0, stl = 0.0, st = 0.0, f = 0.00; // Para hacer las restas oportunas en uS
int1 flagHayDatos = 0;                 // Flag para indicar que ya hay datos de ..
                                     // dos flancos (de subida y bajada)
int1 flagHayTransmitir = 0;            // Flag para indicar que hay datos para ...
                                     // Transmitir al PC.


////////////////////////////////////////////////////////////////////////////////////
// Interrupción por Recepción Serie RS232
////////////////////////////////////////////////////////////////////////////////////
#int_rda
void handle_rda_int()
{
  if (kbhit())    // Si hay algo pdte de recibir ...
  {             
    cRec = getc();             // lo recibo sobre cRec ...
    if (cRec != 0x00)       // Si es distinto de \0 ...
    {
      Command = ToUpper(cRec); // cargo cRec sobre Command para procesarlo
    }                        // pasándolo a Mayúsculas para no confundir.
  }
}

////////////////////////////////////////////////////////////////////////////////////
// Interrupción por Externa por Cambio de Flanco en RB0
////////////////////////////////////////////////////////////////////////////////////
#int_ext
void handle_ext_int()
{
  ++numFlancoQueLlega;     // Cuento flanco que nos llega
 
  if (flagToggleFlanco == 0)  // He recibido Flanco de Subida
  {
    if (numFlancoQueLlega == 1)
    {
      set_timer1(0);       // Reinicio TMR1
      t1=get_timer1();     // Guardo en t1 el valor de TMR1 al primer Flanco de Subida
    }
    if(numFlancoQueLlega == 3)
    {
      t3 = get_timer1();     // Guardo en t1 el valor de TMR1 al primer Flanco de Subida     
      if (flagHayDatos == 0)  // Si los datos anteriores han sido procesados ...
      {
        flagHayDatos = 1;    // Indico que ya hay nuevos datos de flancos para calcular
      }
    }
    ext_int_edge(0, H_TO_L); // Configuro para capturar siguiente flanco de Bajada
    flagToggleFlanco = 1;     // Indico que el siguiente flanco será de Bajada
  }
  else                     // He recibido Flanco de Bajada
  {                 
    t2=get_timer1();        // Guardo en t2 el valor de TMR1 al Flanco de Bajada
    ext_int_edge(0, L_TO_H); // Configuro para capturar siguiente flanco de subida
    flagToggleFlanco = 0;     // Indico que el siguiente flanco será de Subida
  }
 
  FLASH;                    // Reproduzco la entrada mediante un LEd en E0;
 
  if (numFlancoQueLlega == 3) //Reinicio el Flag q llega para poder leer otro pulso.
  {
    numFlancoQueLlega=0;
  }
}

////////////////////////////////////////////////////////////////////////////////////
// Inicio Programa.
////////////////////////////////////////////////////////////////////////////////////
void main()
{
  //INICIALIZACIONES GENERALES//
  delay_ms(333); // Espero a que todo se estabilice e ...
  disable_interrupts(global); // Inicializo el Micro y ...
  disable_interrupts(int_timer1); // deshabilitando todo lo no necesario ...
  disable_interrupts(int_rda);
  disable_interrupts(int_ext);
  disable_interrupts(int_ext1);
  disable_interrupts(int_ext2);
  setup_adc_ports(NO_ANALOGS);
  setup_adc(ADC_OFF);
  setup_spi(FALSE);
  //setup_psp(PSP_DISABLED);  //Para 18F4550.
  setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
  setup_timer_0(RTCC_OFF);
  setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  setup_timer_2(T2_DISABLED,0,1);
  setup_timer_3(T3_DISABLED);
  setup_comparator(NC_NC_NC_NC);
  setup_vref(FALSE);
  port_b_pullups(FALSE);
  delay_ms(333);

  //INICIALIZACIONES PERTINENTES A LA APLICACION//
  set_tris_c(0b10000000);      // Habilito como entrada RC7 para canal RS232

  ext_int_edge(0,L_TO_H);      // Configuro captura de 1er flanco de subida
  flagToggleFlanco = 0;        // inicializo el Flag para cambiar de flanco

  enable_interrupts(int_rda);  // Habilito las interrupciones necesarias
  enable_interrupts(int_ext);
  enable_interrupts(global);

  printf("\r\nMidiendo un pulso : Periodo\r\n");
  printf("By Redpic para Foro TODOPIC\r\n\n");

  do
  {
    if (flagHayDatos == 1) // Detecto que ya hay datos de flancos ...
    {   
      if ((t3 > t2) && (t2 > t1))   // Compruebo que estoy en la misma vuelta de TMR1
      {     
        tth = t2 - t1;          // Calculo en Tick's de TMR1 el tiempo en Alto
        ttl = t3 - t2;          // Calculo en Tick's de TMR1 el tiempo en Bajo
        tt = tth + ttl;         // Calculo en Tick's de TMR1 el Periodo del Pulso
        sth = uSxTick * tth;    // Calculo en uS el tiempo.
        stl = uSxTick * ttl;    // Calculo en uS el tiempo.
        st = uSxTick * tt;      // Calculo en uS el tiempo.
        f = 1 / (st / 1000000); // Calculo la Frecuencia
        flagHayTransmitir = 1;    // Indico que tengo nuevo valor para transmitir
      }
      flagHayDatos = 0;           // Indico que ya han sido procesados los datos.
    }
   
    if (Command != 0x00)   // Si he recibido un comando vía Serie ...
    {         
      if (Command == 'T')  // Si el comando es 'T' (Transmite) ...
      {         
        if (flagHayTransmitir == 1) // Si hay algo pendiente de transmitir ...
        {
          printf("Ticks ....... H %4Lu + L %4Lu = %4Lu\r\n", tth, ttl, tt);
          printf("uSegundos ... H %3.1f + L %3.1f = %3.1f F = %4.2f Hz\r\n\n", sth, stl, st, f);
          flagHayTransmitir = 0;  // Indico que ya he transmitido lo pendiente.
        }
      }
      Command = 0x00;             // Indico que ya he procesado el comando.
    }
  } while (TRUE);
}

Gracias otra ves.

Hasta luego  8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 21 de Agosto de 2007, 16:25:50
Buenoooo ... primero y muy importante, fundamental diría yo, es que hay definida una constante que te está haciendo que los cálculos te salga erróneos:


float const uSxTick = 0.2; // Microsegundos por Tick de TMR1 a 20 Mhz

Esto viene de 1 (segundo) / 20.000.000 (frecuencia de reloj)* 4 (ciclos de CPU para cada incremento de Timer1)

Como puedes ver esta constante son los microsegundos que tarda en avanzar una posición el Timer1 con un Clock de 20 Mhz .... siempre y cuando el pre-escaler (divisor) del mismo sea 1. Si el divisor es 2 entonces su valor es distinto ya que tardará el doble en incrementar el Timer 1

float const uSxTick = 0.4; // Microsegundos por Tick de TMR1 a 20 Mhz con setup_timer_1(T1_INTERNAL | T1_DIV_BY_2);

Así st = uSxTick * tt; te saldrá correcto.

Cada vez que cambies el divisor del Timer1 multiplica uSxTick por el mismo valor y asi obtendrás el valor correcto.

Para un siguiente post en este mismo hilo voy a preparar esta misma técnica pero contando vueltas completas de Timer1 para no tener que cambiar de preescaler cuando el periodo del pulso a medir sea mayor que el tiempo que tarda el Timer1 en dar una vuelta completa. No es complicado pero me gusta hacer las cosas bien y explicarlas claramente así que lo dejo para el próximo fin de semana que tengo mas tiempo.  :mrgreen:







Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 21 de Agosto de 2007, 20:06:56
Vaya! si! funciona!! wow!  :P Gracias!!

Ahora el desbordamiento del Timer1 esta en los 26,...

Pero dime una cosa, ¿¿¿en q afecta el poner T1_DIV_BY_1 q poner T1_DIV_BY_2, 4, 8... ???? Segun lo dividido es lo q se va a demorar en avanzar una poscicion el Timer1? No entiendo muy bien eso en lo q afecta. Podrias explicarme un poco mas a detalle porfavor ? 

Nuevamnt disculpa por mi ignorancia pero esq ¡quiero aprender!  :-/ :-/ jeje


salu2 y gracias. 8) 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: psykhon en 22 de Agosto de 2007, 00:12:46
ESCRIBA UN LIBRO MAESTRO!!!!!!!!!!!!!!!
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: jeremylf en 25 de Agosto de 2007, 04:42:22
Para un siguiente post en este mismo hilo voy a preparar esta misma técnica pero contando vueltas completas de Timer1 para no tener que cambiar de preescaler cuando el periodo del pulso a medir sea mayor que el tiempo que tarda el Timer1 en dar una vuelta completa. No es complicado pero me gusta hacer las cosas bien y explicarlas claramente así que lo dejo para el próximo fin de semana que tengo mas tiempo.  :mrgreen:
Lo espero ancioso!! yeah!  :-/
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 13 de Octubre de 2007, 15:58:29
Hola RedPic! queria saber si puedo hacer todo esto igual pero por la interrupcion por cambio de flanco en RB1, yaq el PIC18f2550 tiene ahi un INT1 y en el RB2 un INT2 ¿?¿?¿?¿?

Probe cambiando lo ovio...
Código: [Seleccionar]
#include <18f2550.h>
#fuses HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)


////////////////////////////////////////////////////////////////////////////////////
// Defines y Constantes
////////////////////////////////////////////////////////////////////////////////////
#define LED PIN_B5               //E0 Defino el Pin del Led
#define FLASH Output_Toggle(LED) // Defino la funcion Flash de monitor

float const uSxTick = 0.4;//0.64;//1.6;//0.4;       // Microsegundos por Tick de TMR1 a 20 Mhz


////////////////////////////////////////////////////////////////////////////////////
// Variables en RAM//
////////////////////////////////////////////////////////////////////////////////////
char cRec = 0x00;                      // Último carácter recibido vía serie
char Command = 0x00;                   // Comando a procesar
int8 numFlancoQueLlega = 0;            // Número de Flanco que llega
int1 flagToggleFlanco = 0;             // Flag para cambiar de flanco
int16 t1 = 0x00, t2 = 0x00, t3 = 0x00;       // Variables para guardar estados de ...
int16 tth = 0x00, ttl = 0x00, tt = 0x00;     // Timers y pulsos.
float sth = 0.0, stl = 0.0, st = 0.0, f = 0.00; // Para hacer las restas oportunas en uS
int1 flagHayDatos = 0;                 // Flag para indicar que ya hay datos de ..
                                     // dos flancos (de subida y bajada)
int1 flagHayTransmitir = 0;            // Flag para indicar que hay datos para ...
                                     // Transmitir al PC.


////////////////////////////////////////////////////////////////////////////////////
// Interrupción por Recepción Serie RS232
////////////////////////////////////////////////////////////////////////////////////
#int_rda
void handle_rda_int()
{
  if (kbhit())    // Si hay algo pdte de recibir ...
  {             
    cRec = getc();             // lo recibo sobre cRec ...
    if (cRec != 0x00)       // Si es distinto de \0 ...
    {
      Command = ToUpper(cRec); // cargo cRec sobre Command para procesarlo
    }                        // pasándolo a Mayúsculas para no confundir.
  }
}

////////////////////////////////////////////////////////////////////////////////////
// Interrupción por Externa por Cambio de Flanco en RB0
////////////////////////////////////////////////////////////////////////////////////
#int_ext1
void handle_ext_int()
{

   output_low(pin_b5);
   ++numFlancoQueLlega;     // Cuento flanco que nos llega
 
   if (flagToggleFlanco == 0)  // He recibido Flanco de Subida
   {
      if (numFlancoQueLlega == 1)
      {
         set_timer1(0);       // Reinicio TMR1
         t1=get_timer1();     // Guardo en t1 el valor de TMR1 al primer Flanco de Subida
      }
      if(numFlancoQueLlega == 3)
      {
         t3 = get_timer1();     // Guardo en t1 el valor de TMR1 al primer Flanco de Subida     
         if (flagHayDatos == 0)  // Si los datos anteriores han sido procesados ...
         {
         flagHayDatos = 1;    // Indico que ya hay nuevos datos de flancos para calcular
         }
      }
      ext_int_edge(1, H_TO_L); // Configuro para capturar siguiente flanco de Bajada
      flagToggleFlanco = 1;     // Indico que el siguiente flanco será de Bajada
   }
   else                     // He recibido Flanco de Bajada
   {                 
      t2=get_timer1();        // Guardo en t2 el valor de TMR1 al Flanco de Bajada
      ext_int_edge(1, L_TO_H); // Configuro para capturar siguiente flanco de subida
      flagToggleFlanco = 0;     // Indico que el siguiente flanco será de Subida
   }
 
   //FLASH;                    // Reproduzco la entrada mediante un LEd en E0;
 
   if (numFlancoQueLlega == 3) //Reinicio el Flag q llega para poder leer otro pulso.
   {
      numFlancoQueLlega=0;
   }
}

////////////////////////////////////////////////////////////////////////////////////
// Inicio Programa.
////////////////////////////////////////////////////////////////////////////////////
void main()
{
   //INICIALIZACIONES GENERALES//
   delay_ms(333); // Espero a que todo se estabilice e ...
   disable_interrupts(global); // Inicializo el Micro y ...
   disable_interrupts(int_timer1); // deshabilitando todo lo no necesario ...
   disable_interrupts(int_rda);
   disable_interrupts(int_ext);
   disable_interrupts(int_ext1);
   disable_interrupts(int_ext2);
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_spi(FALSE);
   //setup_psp(PSP_DISABLED);  //Para 18F4550.
   setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
   setup_timer_0(RTCC_OFF);
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_2);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   port_b_pullups(FALSE);
   delay_ms(333);

   //INICIALIZACIONES PERTINENTES A LA APLICACION//
   set_tris_c(0b10000000);      // Habilito como entrada RC7 para canal RS232
 
   ext_int_edge(1,L_TO_H);      // Configuro captura de 1er flanco de subida
   flagToggleFlanco = 0;        // inicializo el Flag para cambiar de flanco

   enable_interrupts(int_rda);  // Habilito las interrupciones necesarias
   enable_interrupts(int_ext1);
   enable_interrupts(global);

   output_high(PIN_B5);
   printf("\r\nMidiendo un pulso : Periodo\r\n");
   printf("By Redpic para Foro TODOPIC\r\n\n");

   do
   {     
    } while (TRUE);
}
Algo falta en los fusibles? no se puede?..... q esta mal ?  :?


salu2 y gracias 8) 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 13 de Octubre de 2007, 16:20:37
Pues creo que lo fundamental los tienes en Midiendo un pulso. 3ª Parte. Tiempo en Alto con INTRB (http://www.todopic.com.ar/foros/index.php?topic=14528.0), ampliando el tiempo en alto al pulso completo. La técnica es la misma.
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 13 de Octubre de 2007, 20:07:35
Pero no entiendo. Eso es para los pines desde RB4 hasta RB7 :S Dime, se puede hacer la interrupcion externa por RB1 ?? Porque segun el datasheet en Rb0, Rb1 y Rb2 tienen los tres INT0, 1, 2 respectivamente.  :? :?


saluos 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 14 de Octubre de 2007, 05:51:35
Ahhhh ....  :D

Con un 18F las INT_EXT, INT_EXT1 y INT_EXT2 funcionan exactamente igual y solo hay que cambiar las habilitaciones de las mismas ...

Voy a revisar tu fuente y te comento ...

15 minutos después ...

Revisado el fuente y no veo nada que esté mal. Has cambiado todos los sitios donde se configura la INT_EXT para usar ahora la INT_EXT1 y no logro ver porqué no funciona.  :(


Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 14 de Octubre de 2007, 06:55:04
Uy menos mal, gracias por darme esperanza a saber q se puede hacer lo mismo ahi porque ya mande a hacer el PCB  :lol:

Entonces que estara mal??  :? en RB0 si funciona perfectamente, no faltara alguna configuracion previa en el main? o en los fusibles quiza?  :(


Gracias RedPic! 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 14 de Octubre de 2007, 08:08:42
¿Como están los TRIS del PORTB? Prueba a declarar ese pin de entrada por si acaso, y de camino los demás del PORTB según los estés usando.

¿Necesitas las PULL-UPS internas? Habilítalas o deshabilítalas según tu fuente de señal de de pulsos negativos, tira a masa la señal para indicarte que tienes un pulso, o te de pulsos positivos, sube a 5V para indicarte eso mismo.

El asunto puede estar en los TRIS.

Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 14 de Octubre de 2007, 08:26:05
Veamos. Lo de la forma de indicarme la fuente de señal q tengo un pulso no estoy seguro como lo haga pero si de que en RB0 funciona perfecto, y si estuve pensando en los tris pero entonces fallaria tb en Rb0... igual probare (ahorita no tengo un quemador) en poner un tris para todo el puerto B.

Disculpa, pero nose a q te refieres con PULL-UPS internas  :?
esto: port_b_pullups(FALSE); ?? Pues la verdad no entiendo del todo para q sirve eso, haber si de paso me explicas  ello :P


Gracias. salu2 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 19 de Octubre de 2007, 01:23:18
Disculpen el doble post.

Buenas RedPic. Tenias razon el problema estaba en la falta de configuracion en el tris_b, pero no lo usaba porque para el INT0 no lo usabas y supuse q para la INT1 seria igual. Entonces la pregunta ahora seria: ¿Porque al usar INT0 no es necesario configurar el pin a entrada mientras que en INT1 si?

Gracias hombre!!!!!! :D!!

salu2 8) 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 19 de Octubre de 2007, 05:01:49
Esos son los misterios de los PIC's ... pero seguro que investigando el Datasheet llegamos a un por qué exacto.
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 19 de Octubre de 2007, 22:57:42
Estare en ello lo mas que pueda.

Se agradece.


salu2 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 05 de Enero de 2008, 02:25:38
(me equivoq era aqui xP)

Citar
Hola RePic.

Queria saber si es necesario la variable T1, yaque su valor es tomado del timer 1 justo despues de poner este a 0. Si no lo fuera pues entonces el valor, en ticks de timer1, del ancho del pulso en alto no serai mas que t2 y por ende no habria necesidad del: t2 - t1. Eso si esq no me equivoco y esq t1 puede llegar a tomar un valor diferente a 0, quisiera q me aclares esa duda.

Gracias.
salu2 8)

salu2, gracias.  :-)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 06 de Enero de 2008, 22:51:50
Porq lo haces asi ? x)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 06 de Enero de 2008, 23:16:34
Bueno, vamos a ver si soy capaz de explicarme. Tienes razón en que T1 debe valer 0 en esta versión del programa y que es una solemne tontería guardar ese valor si acabamos de hacerlo igual a cero.

Pero esto es un añadido al programa original que no llevaba el set_timer1(0);

Quiero decir que realmente no le hace falta ya que si T3>T2>T1 sean cuales sean los valores de T3, T2 y T1 hacen que tengamos el pulso completamente definido con los tres flancos: inicio, cambio y final. Pero ... y es un pero muy importante ... siempre y cuando el pulso completo empiece y termine en la misma vuelta del Timer1.

Como sabes un Timer empieza a contar en 0x00 y acaba en 0xFF, si es de 8 bits, y vuelve a empezar por 0x00 sin solución de continuidad. Entonces puede darse perfectamente el caso de que el primer flanco nos llegue con el estado del Timer1 muy avanzado con lo que la variable T1 podría tener un valor muy cercano a 0xFF, y con ello se produciría el caso de que los restantes flancos nos llegasen tras el desborde del Timer1, su paso de 0xFF a 0x00, con lo que T2 y T3 serían entonces menores que T1.

Esto haría que nuestro cálculo de Pulso en Bajo =  T2 - T1, Pulso en Alto = T3 - T2 y Pulso completo T3 - T1 no sería válido sin contar con las vueltas completas o desbordes del Timer1.

Como esta serie de artículos eran para primerizos en esto de los PIC's no me pareció oportuno complicar los cálculos poniendo una variable que indicase el número de veces que ocurría este tránsito en pleno pulso del desborde del Timer. Porque la verdad es que la cosa se complica un poco y la rutina deja de ser tan extremadamente clara como quería conseguir.

Así que opté por la solución mas fácil y que era poner a cero el Timer1 al llegar el primer flanco. Con esto conseguía que si el pulso era mas corto que el tiempo que tarda el Timer1 en dar una vuelta completa la formula de T3-T2-T1 sería siempre verdadera y no habría que complicar nada. Asi que puse el Set_Timer1(0) tras la primera entrada en la interrupción y esperé a que T2 y T3 fuesen siempre menores a 0xFF pero mayores a T1 (que yo había puesto a cero).

Esa es la explicación de tan extraña secuencia de instrucciones.  :mrgreen:

Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 07 de Enero de 2008, 02:01:35
Haya. Entonces mas q nada es para hacer una comparacion correcta a la hora de tener un nuevo dato. Si, estaba pensando q fuera asi porq siempre valia 0. En el caso de las interrupciones pues si q se complica algo y estoy investigando ello  :D; pero e logrado algo mas "compacto" por asi decirlo que, es lo mismo, pero ya ves q si e aprendido algo gracias a ti  :mrgreen:

Código: [Seleccionar]
#INCLUDE <12F683.h>

#FUSES HS
#FUSES INTRC
#FUSES NOWDT
#FUSES PUT
#FUSES MCLR
#FUSES NOPROTECT
#FUSES NOCPD

#USE DELAY(clock = 8 000 000)

#USE RS232(baud = 9600, xmit = PIN_A0, FORCE_SW)

float const us_por_tick = 0.5;   //0.2;   //Cada 0.5 microsegundos se producira un tick de timer1, dada por la formula: 4 / (FOSC(Mhz)).
int1 hay_pulso_contado = 0;
int8 numero_de_flanco_alto = 0;
int16 periodo_alto_en_ticks_1, periodo_alto_en_ticks_2;
float frecuencia_hz, periodo_en_us;
                   
#INT_EXT
void SQW_OUT()
{
   numero_de_flanco_alto++;     
 
   if (numero_de_flanco_alto == 1)
   {
      set_timer1(0);
      periodo_alto_en_ticks_1 = get_timer1();
   }
   
   if (numero_de_flanco_alto == 2)
   {
      periodo_alto_en_ticks_2 = get_timer1();     
      numero_de_flanco_alto = 0;
      set_timer1(0);
     
      if (hay_pulso_contado == 0)
      {
         hay_pulso_contado = 1;
      }
   }
}


void main()
{
   setup_oscillator(OSC_8MHZ);
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); 
   delay_ms(300);
         
   ext_int_edge(L_TO_H);
   
   enable_interrupts(INT_EXT);
   enable_interrupts(GLOBAL);
   
   while (TRUE)
   {
      if (hay_pulso_contado == 1)
      {
         if (periodo_alto_en_ticks_1 < periodo_alto_en_ticks_2)
         {
            periodo_en_us = us_por_tick * periodo_alto_en_ticks_2;      // Calculo en uS el periodo del pulso.
            frecuencia_hz = 1 / (periodo_en_us / 1000000);             // Calculo la Frecuencia
           
            printf("uSegundos: %3.1f  F = %4.2f Hz\r\n\n", periodo_en_us, frecuencia_hz);
         }
         
         hay_pulso_contado = 0;
      }
   }
}
Solo hago el conteo con los pulsos en alto y aunq aqui tb t1 (con otro nombre) vale 0 solo lo hago, como tu,  el tema del timer1 desbordado.


Gracias! Ahora voy entendiendo mas est tema d interrupciones, pulsos y eso  :P
salu2 8)

PD: Estaba pensando q para hacer una lectura de los pulsos sin tener q estar estos dentro de una vuelta del TMR1, podria cojerse, esperando el flanco de bajada (despues de esperar el d subida),  el tmr1 y ahi mismo ponerlo 0 y asi con el ultimo flanco para q cada tiempo de los tiempos en alto y en bajo sean independientes en cada timer1 por asi decirlo. No estoy seguro pero estare investigando sobre ello o bien recurrir a las tenebrosas interrupciones de estas  :-)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 07 de Enero de 2008, 11:43:47
Si, buena idea (la de poner a 0 el timer tras recoger cada flanco). Pero el desarrollo definitivo, el que haría independiente del valor del timer en cada momento sería algo así:

#INT_EXT Recoge los tres estados consecutivos del Timer sin ponerlo a cero nunca, dejándolo correr alegremente, sólo conmutando el tipo de flanco a recoger cada recepción. Cada tres conmutaciones tenemos los valores T1, T2 y T3 y podemos poner en alto el flag de Hay_Dato. Entre T1 y T2 pueden haber N desbordes del Timer, y entre T2 y T3 puede haber M desbordes del Timer. Para poder realizar el cálculo correcto del periodo debemos saber los valores de N y M.

Esto lo conseguimos con ..

#INT_TIMER Esta interrupción se produce cada vez que el Timer pasa de 0xFF a 0x00. Así que en ella ponemos un simple contador de desbordes. ++NumDesbordes.

Entonces nuestro programa debe recoger tanto los valores del Timer como el estado de NumDesbordes en cada recogida de T1, T2 y T3 respectivamente en dT1, dT2, y dT3. Con estas parejas de valores podemos calcular perfectamente el tiempo transcurrido ente cada uno de ellos y el siguiente, independiente de si el pulso es corto o muy largo.

Ésta es la técnica que implemento en mi Analizador lógico de 3 Canales (http://www.todopic.com.ar/foros/index.php?topic=8109.0). Esta idea la explico un poco mas en profundidad en este (http://www.todopic.com.ar/foros/index.php?topic=8109.msg63609#msg63609) post y los siguientes.  :mrgreen:




Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 07 de Enero de 2008, 20:10:31
Si! asi estaba haciendo algo tb, algo tan simple como eso n la interupcion del timer1. Gracias por dejarmelo en claro.

Porcierto, es igual usar un cristal externo q uno interno a la hora de querer contar pulsos y todo esto ?? Porq necesito algo muy pero muy preciso yaq estoy intentando leer el SQW del DS1307 para revisar si el cristal (32,768 KHZ) esta bien o no pormedio d la INTEXT del`PIC12f683. Cambiando cristales como q baja un poco  el conteo  de la frecuencia (ocea el tiempo), o quizas sea por q al poner una frecuncia menor de 20MHZ (yaq ese pic internamente como maximo solo me da 8MHZ) este contando mas lento...

Esq quiero una prescicion bien precisa  :D y creo q aqui entra mucho el tema del oscilador verda?  :? Que puedo hacer? ajustar el uSxTick a un valor diferent al q es para q sea igual al q lee en 20 mhz ? modificar mi condigo ISR para mejorar precicion? ...


Gracias, salu2 8)

Revisando ese links q dejast  :mrgreen:...
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 07 de Enero de 2008, 21:10:53
jeremy, la precisión la vas a obtener cuando tu programa pueda acumular cuantos mas ticks de Timer que te sea posible entre dos flancos sucesivos.

Si cada tick de Timer avanza cada 0.2 uS será esa la precisión máxima que podrás alcanzar.

Y tantas veces esa precisión como ocupe, o tarde, tu programa en "recoger" los valores de dos flancos sucesivos será el ancho mínimo de pulso que podrás detectar.

Por ello cuanto más rápido corra tu PIC menor será el pulso que pueda detectar y mayor será la precisión con que lo haga. Yo hasta ahora para alcanzar la máxima precisión he utilizado los 18F de la serie USB para poder ponerlos a 48 Mhz, usando la PLL interna con un cristal externo de 20 Mhz.  :mrgreen:

P.D.: uSxTick es el tiempo que tarda en incrementarse una posición el Timer. Por ello depende directamente del cristal u oscilador que utilices. Es imprescindible colocarle a uSxTick  el valor correcto (1/ FOSC * 4) o si nó no valdrá de nada todo el resto del programa.

Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 07 de Enero de 2008, 22:31:22
Estoy usando el pic 12F683 y ese no trae la opcion de PLL y pues solo puedo usar su oscilador interno de 8Mhz para leer un pulso de 1Mhz (el SQW/OUT del DS1307), y queria saber si con esta oscilacion puedo leer exactamente eso ?? Esq intento verificar por esa salida del RTC si el cristal con q lo e montado esta en buenas condiciones (leer exactamente la frecuencia del SQW) yaq comunmente me vienen fallados esos crsitales de 32.768Khz y como los voy a utilizar para un proyecto serio pues necesito verificar esto con buena precicion.

Decirt q solo quiero esto para leer ese SQW/OUT a 1Mhz, por eso quiza no necesite de los 48Mhz en el pic pero si q quiero q sea bien preciso para q si hay una mala lectura entender q es el cristal de 32.768Khz el q esta fallando y no el cristal del pic o el mismo pic. Claro q para esto necesitare del desbordamiento del TMR1 o TMR0.

Gracias, salu2.

PD: Q valor en uSxTick pusist usando 48Mhz en el pic? porq haciendo los calculos correspondientes en la calculadora de la PC me sale un numero infinito con e-4 o algo asi no recuerdo  :-)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 08 de Enero de 2008, 01:17:27
Disulpen el doble post.
Mira, e avanzado con mi programa y me a qedado asi, con interrupciones en tmr1 y todo  :mrgreen::
Código: [Seleccionar]
#INCLUDE <12F683.h>

#FUSES HS
#FUSES INTRC
#FUSES NOWDT
#FUSES PUT
#FUSES MCLR
#FUSES NOPROTECT
#FUSES NOCPD

#USE DELAY(clock = 8 000 000)

#USE RS232(baud = 9600, xmit = PIN_A0, FORCE_SW)

float const us_por_tick = 0.5;   //Cada 0.5 microsegundos se producira un tick de timer1, dada por la formula: 4 / (FOSC(Mhz)).
int1 hay_pulso_contado = 0;
int8 numero_de_flanco_alto = 0;
int16 veces_desbordado, periodo_alto_en_ticks;
float frecuencia_hz, periodo_en_us;

#INT_TIMER1
void desbordamiento_tmr1()
{
   if (hay_pulso_contado == 0)
   {
      veces_desbordado++;     
   }
}
                   
#INT_EXT
void SQW_OUT()
{
   if (hay_pulso_contado == 0)
   {
      numero_de_flanco_alto++;     // Cuento flanco que nos llega
 
      if (numero_de_flanco_alto == 1)
      {
         veces_desbordado = 0;
         set_timer1(0);
      }
   
      if (numero_de_flanco_alto == 2)
      {
         periodo_alto_en_ticks = get_timer1();     
         numero_de_flanco_alto = 0;
         set_timer1(0);
     
         hay_pulso_contado = 1;
      }
   }
}


void main()
{
   setup_oscillator(OSC_8MHZ);
   
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
       
   delay_ms(300);
   
   printf("\fProbador de DS1307 y Cristal 32.768Khz");
         
   delay_ms(300);
     
   ext_int_edge(L_TO_H);
   
   enable_interrupts(INT_TIMER1);
   enable_interrupts(INT_EXT);
   enable_interrupts(GLOBAL);
   
   while (TRUE)
   {
      if (hay_pulso_contado == 1)
      {
         periodo_en_us = us_por_tick * ((65536 * veces_desbordado) + periodo_alto_en_ticks);
         
         frecuencia_hz = 1 / (periodo_en_us / 1000000); // Calculo la Frecuencia
           
         printf("\r\n\nuSegundos: %3.1f  F: %4.2f Hz  IntT1: %Lu", periodo_en_us, frecuencia_hz, veces_desbordado);
         
         hay_pulso_contado = 0;
      }
   }
}

Con resultados algo satisfactorios yaq estoy en protoboar aun.

(http://img218.imageshack.us/img218/9714/dibujomt6.jpg)

Necesito de tu aprobacion!  :D

PD: Aun tngo las dudas sobre si el cristal q estoy usando ahora esta bien del todo  :? Porq como ves, necesito de una buena precicion para contar el SQW/OUT en ese pic.

Gracias por todo.
salu2 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 11 de Enero de 2008, 19:39:20
Amigo Jeremy, vas bien.

Con tus uSxTick a 0.5 uS una vuelta de Timer1 (16 bits) es de 65.536 * 0.5 us son 32.768 uS por vuelta al que podemos llamar uSxDesb.

Luego puedes calcular cada tiempo como Ticks x uSxTick + Desbordes * uSxDesb.

Así una pareja de datos de un par de flancos sucesivos del estilo:

Primer Flanco    en Ticks 60.000 Desborde 21
Segundo Flanco en Ticks  1.000 Desborde 23

Estarían separados por ((65.535 - 60.000) + 1.000 Ticks * uSxTick) + (1 * uSxDesb)

La explicación:

Para llegar (en tiempos) desde el Primer Flanco hasta el Segundo Flanco el Timer1 tiene primero que llegar desde los 60.000 (en el desborde 21) hasta 65.535, pasar de nuevo a 0 (y provocar el desborde 22), recorrer todos los 65.535 ticks de ese desborde, volver de nuevo a 0 (y provocar el desborde 23) y alcanzar los 1000 ticks (del desborde 23)

Así tenemos (en orden de aparición) 5.535 + 65.535 + 1000 Ticks.

Si llamamos TicksPf y DesbordesPf a los del primer flanco y TicksSf y DesbordesSf a los del segundo la fórmula para calcular cuantos Ticks y microsegundos hay entre ellos incluyendo los sucesivos desbordes sería:

Código: C#
  1. if(DesbordesPf == DesbordesSf ){ // están en el mismo desborde
  2.    TicksDiff = TicksSf - TicksPf;
  3. } else { // Están en distintos desbordes
  4.    TicksDiff = (65535 - TicksPf ) + TicksSf + ((DesbordesSf - DesbordesPf - 1) * 65535;
  5. }
  6. TiempoSep = TicksDiff * uSxTick;

 :mrgreen: :mrgreen: :mrgreen:
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 12 de Enero de 2008, 02:51:34
Jojo! algo complicado de entender  :lol:, pero lo estare analizando. Muchas gracias. Mi formula para hallar el tiempo (us) cn todo y desbordes es correcta?

Por no molestar mas solo quisiera saber q valor pones en uSxTick cuando usas 48Mhz (cn PLL y eso claro) ?? Segun la formula (4/FOSC) saldria 0.08333... Yo lo tengo solo como 0.0833, esto puede hacer una mala prescicion? es necesario poner mas 3 como la division lo indica ?  :?

Ahora si me callo!  :-)

Gracias.
salu2 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 12 de Enero de 2008, 07:35:20
(1/FOSC) * 4 -> 1/48000000*4 -> 0,083333333333333333333333333333333 periódico. Así que ponle la cantidad de treses que desees, cuantos mas le pongas mas cerca estarás de la verdad que siempre te sera inasequible.  :mrgreen:
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: jeremylf en 12 de Enero de 2008, 20:33:55
Si q me bajaste la moral  :D Creo q me limitare a usar el de 20Mhz o uno mayor q tenga una division exacta y q pueda soportar el pic.

Ya os comentare  :P

Gracias.
Salu2 8)
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: fastyx2 en 14 de Enero de 2008, 21:58:49
Tengo una duda : que ocurre cuando tenes que hacer una comparacion entre los valores de frecuencia , y esa comparacion en el main en tiempo es mas grande que lo que tarda el dato en estar listo para ser comparado?

desde ya gracias
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: RedPic en 15 de Enero de 2008, 04:20:00
Ufff Imagino que hacer un muestreo y saltarme ciclos ...
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: fastyx2 en 15 de Enero de 2008, 08:01:45
esto te lo pregunto porque estoy haciendo una controladora que tiene que manejar un motor de continua que regula las rpm modificando el acelerador de la boma de inyeccion de un grupo electrogeno.

el tema es que en el main necesito comparar 3 situaciones definidas : cuando esta en regimen normal ( que se apague el puente H ) , cuando esta por encima ( que el puente H baje las rpm , y que las suba si esta por debajo.

esto implica que , al ser el puente H con reles , yo deba agregar un tiempo muerto en el caso que cambie la direccion de regulacion , de manera que el tiempo de la comparacion es mucho mayor que el tren de datos disponibles creados a partir de las interrupciones externas.

lo unico que se me ocurre es des_habilitar las interrupciones durante la comparacion en el main , y al finalizar volver a habilitarlas , ya que no tiene mucho sentido tener pulsos en la " sala de espera".
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Per
Publicado por: julietgolf en 10 de Marzo de 2008, 19:22:34
excelente, como siempre.... E X C E L E N T E ! ! !   :-/
Muchas gracias RedPic! Me hasido de gran utilidad...
Título: Re: Serie Técnicas en C : Midiendo un pulso. 4ª Parte. El pulso completo. El Periodo
Publicado por: andreaymaria en 14 de Abril de 2010, 06:05:42
Hola RedPic...me interesa mucho este método para medir la frecuencia de mi señal generada pero tengo unas dudas..

Yo utilizo C18 con mplab...es compatible este código con C18??O tendría que cambiar muchas cosas?
Podría utilizarlo con un cristal de 4MHz??
La señal cuadrada que tengo generada debe ir a RB0 obligatoriamente para poder usar el sistema de interrupciones o puedo usar otro puerto??

Espero su respuesta...muchisimas gracias!