Usando PIC18F4520 para medir el período lento / Frecuencia

0

He estado tratando de encontrar una solución para medir una frecuencia relativamente lenta con el PIC18F4520. El rango de frecuencia que estoy intentando medir está en el rango de .5 a 20Hz. Necesito tener una resolución de 1 decimal.

El código que he estado intentando usar no está dando resultados. Cuando uso la variable 'período' entero sin signo, obtengo todos los ceros. Si intento usar la variable 'resultado' flotante, recibo datos erróneos. Soy relativamente nuevo en el uso de PCC pero debido a la frecuencia lenta, sentí que esta sería la mejor solución.

¿Cómo puedo medir una frecuencia relativamente lenta de 0.5 a 20Hz con una resolución de 0.1 Hz con los temporizadores PIC?

//PIC18F4520
//Using CX8 ver 1.40

#include<stdio.h>
#include”lcd.h”
#pragma config OSC = XT // XTAL = 4MHZ External
#pragma config WDT = OFF

void InitGPIO()
 {
    DelayMS(10); // POWER UP DELAY
    TRISCbits.TRISC2 = 1; // CCP1 : CONFIGURE AS INPUT PIN
    ADCON1 = 0x0F ;
    TRISD = 0x00;
    TRISE = 0x00;
 }
 void CCPInit()
 {

 T3CON = 0X81;
 CCP1IE = 0;
 CCP1IF = 0;
 T1CON = 0X81;
 CCP1CON = 0x05;

 }
 void main()
 {
 unsigned char string[10];
 unsigned int start,end,period;
 float result,Frequency ;
 InitGPIO();
 InitLCD();

CCPInit();
 LCDString((char *)”Frequency Period”);
while(1)
 {
 while(!(CCP1IF)); // Wait First Rising Edge
 CCP1IF = 0; // Clear Flag Not Next Round
 start = CCPR1; // Save Value of First Rising Edge

while(!(CCP1IF)); // Wait Second Rising Edge
 CCP1CON = 0x00 ; // Disable CCP1 Capture Module
 end = CCPR1; // Save Value of Second Rising Edge

 period = end-start; // Subtract end – Start
 period = (float)period;
//Also attempted 
    //results = end-start;
    //results = (float)results;
    //Frequency = (1/results );     
 Frequency = (1/period );

LCDGotoxy(1,0);
 sprintf(string,”%.3gKHz”,Frequency);
 LCDString(string);

sprintf(string,”%.3gms”,period);
 LCDGotoxy(1,10);
 LCDString(string);
 }
 while(1);
 }
    
pregunta Cbrun17

3 respuestas

0

aquí hay un breve ejemplo:

//global variables
volatile unsigned char _rfreq_counting = 0; //'0' frequency counter is counting. '1' frequency counter is not counting = available
volatile uint32_t _freq;                    //total frequency
volatile uint16_t _freq_msw;                            //to display the msw

//isr
void interrupt isr(void) {
    //INT interrupt
    if (INTF) {
        INTF = 0;                           //reset the flag
        if (_rfreq_counting == 0) {         //counter isn't current counting -> start counting
            TMR1ON = 1;                     //start the counter
            _freq = 0;                      //TMR1H = TMR1L = 0;        //reset the counters
            _rfreq_counting = 1;            //counting in progress
        } else {                            //timer is already counting -> needs to stop it
            TMR1ON = 0;                     //stop the timer
            _freq |= (TMR1H << 8) | TMR1L;  //store freq
            _freq_msw = _freq >> 16;
            TMR1H = TMR1L = 0;              //reset all counters
            _rfreq_counting = 0;            //counting is done
        }
        IO_FLP(LED_PORT, LED);              //flip led - debug only
    }

    //timer interrupt
    if (TMR1IF) {                           //counter overflow
        TMR1IF = 0;                         //reset the flag
        _freq += 0x10000ul;                 //increment _freq - 16bit timer
    }   

}

//initialize the freq meter
void rfreq_init(void) {
    _rfreq_counting = 0;                    //reset status flag -> not counting

    IO_IN(FREQ_DDR, FREQ_IN);               //FREQ as input pin

    //initialize INT interrupt
    INTEDG = 0;                             //'0'=falling edge, '1'=rising edge
    INTF = 0;                               //clera the flag
    INTE = 1;                               //enable interrupt

    _freq = 0;                              //reset the counter-32bit

    //initialize timer1
    //TMR1ON = 0;                               //stop the timer
    T1CON = 0;                              //count on Fosc/4, counter stop'd, prescaler = 1:1
    TMR1H = TMR1L = 0;                      //reset the counter
    TMR1IF = 0;                             //clear the flag
    TMR1IE = 1;                             //enable interrupt
    //timer not yet started

    PEIE = 1;                               //enable periopheral interrupts
}


int main(void) {

    mcu_init();                             //initialize the mcu
    IO_OUT(LED_DDR, LED);                   //led as output
    rfreq_init();                           //reset the freq counter
    ei();                                   //enable global interrupts
    while (1) {
    }
}

rfreq_init () configura dos cosas: 1. la INT en el flanco descendente (el código también funciona en el flanco ascendente); 2. timer1 para contar FOSC / 4.

el ISR hace dos cosas: 1. Si el tmr1 se desborda, avanza _freq en 0x10000ul - timer1 es un temporizador de 16 bits. 2. si el INTF está configurado - > debemos iniciar el temporizador o detenerlo.

un pin (LED en RA0) se voltea para fines de depuración.

_freq contiene el valor del conteo. _freq_msw es puro para simulación ya que el simulador que estoy usando no muestra el tiempo correcto de 32 bits. _freq_counting toma el valor de 1 (el contador está contando, por lo que el valor de _freq no es confiable) o 0 (el contador no está contando, de modo que el valor de _freq es confiable - > algunas excepciones, ver más abajo).

dado que _freq es un tipo de 32 bits, debe leerlo dos veces para mantener la atomicidad.

La resolución de este enfoque depende de la frecuencia de cristal utilizada. con un cristal de 4Mhz, obtienes una resolución de 1Mhz / 2 ^ 32 = ...., mucho más de lo que normalmente usarás.

Nota: yo era perezoso al usar un tipo de 32 bits. para los mismos resultados, funciona un tipo de 16 bits, con una programación ligeramente diferente. o mejor resolución si opta por un _freq + TMR1H de 32 bits | TMR1L.

aquí,seaplicauntrendepulsosde5HzalpinRA2/INT.laMCUseejecutaenunosciladorde4Mhz,porloquedebemosesperarqueelrecuento_freqseade4Mhz/4*0.2=200K.

detendremoslaejecuciónjustodespuésdeque_freqsehayacalculadoenelISR:

ve _freq_msw = 3, y _freq = 3393 - recuerde que el simulador no puede mostrar el tipo de 32 bits correcto, por lo que 3393 es el más bajo de 16 bits. como tal, el _freq real = 3 * 64K + 3393 = 200,001. Fuera del valor esperado por 1.

La belleza de este enfoque es que se ejecuta completamente en segundo plano (a través del ISR), por lo que su bucle principal puede hacer lo que quiera.

Trasladarlo a tu mcu debería ser fácil.

espero que ayude.

    
respondido por el dannyf
2

Tu código probablemente está haciendo exactamente lo que significa estar haciendo. Intenta esto para confirmar:

  1. Eliminar esta línea: period = (float)period; . período no es un flotador, así que eso no va a funcionar.
  2. Cambie esta línea: Frequency = (1/period ); a esto, Frequency = (1.0/period ); para realizar la división de punto flotante (de lo contrario, la frecuencia siempre será cero).
  3. Cambie esta línea: sprintf(string,”%.3gms”,period); a este sprintf(string,”%u,%u”,start, end); . No puede utilizar el formato g especificado para un int sin firmar. El compilador seguramente te habrá advertido sobre eso.

Ahora, ¿qué obtienes en la pantalla LCD para el inicio y el final? Si ambos son iguales, entonces el código funciona bien y el período debería ser cero.

Para saber por qué, verifique la señal en el pin de entrada del CCP1 (puede estar cambiando más rápido de lo que espera) y verifique la velocidad de su temporizador (puede que no se incremente tan rápido como cree).

    
respondido por el Heath Raftery
1

Utilice el tren de pulsos de entrada para controlar el conteo de frecuencias internas.

El opuesto de un contador de frecuencia típico.

    
respondido por el dannyf

Lea otras preguntas en las etiquetas