PicManía by RedRaven
 

Búsqueda personalizada

 

TÉCNICAS  EN C

 
 


Tareas Comunes Realizadas en C

 
 

Esta serie de contenidos titulada Técnicas en C pretende mostrar con ejemplos comentados la forma de implementar ciertos procesos muy usuales durante el uso de los Microcontroladores PIC de las series 16F y 18F.

 


  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 mediante INTEXT. Los conceptos 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.


 

  • 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 que 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 mayoría 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 Alto.
  • 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 exactamente es:
 

  • 1.- Configuraremos la INTEXT 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 INTEXT 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 INTEXT 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 Periodo 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 que en la primera parte:

     
 
   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_ext); // Habilito las interrupciones necesarias
   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:

     
  #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){
    numFlancoQueLlega=0;
  }
}
 
     
  • Fijaos que en la rutina ISR anterior no realizamos ningún cálculo. Aplicamos aquí el principio fundamental de que "dentro de una rutina de interrupción haz lo debas y abandónala lo antes que puedas". Así que si tenemos valores válidos de T1 y T2 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 cuál es T3 ya que solo cuando obtengamos T3 daremos por concluida nuestra captura.
     
     
 
  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 en Alto.
    stl = uSxTick * ttl;    // Calculo en uS el tiempo en Bajo.
    st = uSxTick * tt;      // Calculo en uS el tiempo del Periodo.
    f = 1 / (st / 1000000); // Calculo la Frecuencia del pulso
      flagHayTransmitir=1;  // Indico que tengo nuevo valor para transmitir
    }
    flagHayDatos=0;         // Indico que ya han sido procesados los datos.
  }
 
     
  • 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 éste es 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 en alto debe ser menor que 13,1072 milisegundos. Notad que cuando detectamos el primer flanco ponemos TIMER1 a cero para recomenzar siempre cerca del inicio de la cuenta de TIMER1 y evitar así su desborde.
  • Para computar tiempos de pulso mayores 13,1072 milisegundos solo habría que computar también cuantas veces se desborda 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:
     
    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.
  }
 
     
 
  • Con todo esto el programa completo para nuestro Detector de Periodo y Frecuencia de un Pulso queda así:
 
     
  ////////////////////////////////////////////////////////////////////////////////////
//
// midiendo_un_pulso_4_complete.c
//
// SERIE: "Técnicas en C" para el Foro TODOPIC
//
// (c) 10.2006 by RedPic
//
// Propósito: Medir los tiempos en alto y bajo que permanece un pulso
//
// Condiciones de Test: Inyección por RB0 de una señal de 2 Khz (0.5 ms de periodo)
//
// Técnica Empleada: Detectar mediante la Interrupción Externa por RB0
// un flanco de subida de un pulso, guardar el estado
// de TMR1, detectar a continuación el siguiente
// flanco de bajada, guardar el nuevo estado de TMR1,
// detectar a continuación un nuevo flanco de subida,
// guardar nuevamente el estado de TMR1 y realizar la
// correspondiente substracción de ellos para obtener
// el tiempo que permanece en alto y bajo y transmitir
// los resultados mediante el puerto RS232 a petición.
//
////////////////////////////////////////////////////////////////////////////////////


#include <18f4550.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_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){
    numFlancoQueLlega=0;
  }
}

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);
  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);
}
 
     
 


 

 

Esta página se modificó el 27/12/2008


Esta página usa la letra Ñ

Nota pública importante sobre las consultas al Webmaster de PicManía.


Sugerencias a Picmanía... (que serán leídas pero seguramente no podrán ser contestadas)

Esta página pertenece al grupo de páginas de Diego RedRaven

 

 



Nota: Esta página Web esta repleta de imágenes, textos, logotipos y demás material extraídos de los mas variados medios de los que no soy ni autor ni depositario de los correspondientes derechos de autor, uso y/o reproducción. Si Ud. es depositario de dichos derechos y desea que el material correspondiente sea eliminado de esta Web no dude en ponerse en contacto conmigo mediante e-mail y será inmediatamente retirado. Gracias.
 
Visitas
Totales : 19768 Hoy: 1 Activas: 1 Vistas: 19768

Esta página fue modificada el 07-08-2010 15:42:16

           
 DmSoft WAMP Escribir Unreal