PIC 16f877a frequency counter

Thread Starter

dmta

Joined Mar 24, 2013
32
Hi all,

I am trying to make a frequency counter and wrote some code. I wanted to use a frequency averaging part to make things more accurate. But when I run the code with the averaging part it acts funny (at frequencies close to 10kHz).

Also please give me some pointers to the variable declaration (eg int, float etc) as I am pretty sure me using "int" is wrong. Furthermore the way I am doing the calculations, is there a better/faster way of doing them.

I'm using a PIC16F877A with 4MHz crystal.

Regards
Rich (BB code):
Rich (BB code):
sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;

int kk  = 0;
int fr  = 0;
int cnt = 0;
int rit = 0;
int acc = 0;
int i   = 0;
int fra[10];

char CN[10];

void interrupt() {

     if(INTCON.TMR0IF == 1){
          fr = (cnt + kk*256)*(1000000)/(256*256); //4MHz crystal, 256 Timer0 increment rate
         /*if(acc<10){         ///
               fra[acc] = fr; ///Counted frequencies are put into an array
               acc++;         ///
          }
          else if(acc >= 10){
               acc = 0;
               for(i=0;i<10;++i){       ///
                   rit = rit + fra;  ///Average frequency is calculated
               }                        ///
          rit = rit/10;                 ///
          }*/
     cnt = 0;
     kk  = 0;
     INTCON.TMR0IF=0;
     }

    if(INTCON.INTF == 1){
         cnt++;
         if(cnt>=256){
              cnt = 0;
              kk++;
         }
    INTCON.INTF = 0;
    }
}

void main() {

     Lcd_Init();
     Lcd_Cmd(_LCD_CLEAR);
     Lcd_Cmd(_LCD_CURSOR_OFF);
     
     PWM1_Init(1000);
     PWM1_Set_Duty(100);
     PWM1_Start();

     INTCON.GIE        = 1; //Enable Global Interrupt
     INTCON.INTE       = 1; //Enable RB0/INT external Interrupt
     INTCON.TMR0IE     = 1; //Enable TMR0 Overflow Interrupt
     
     OPTION_REG.T0CS   = 0; //Timer0 increments with Internal instruction cycle clock
     OPTION_REG.PSA    = 0; //Prescaler is assigned to the Timer0 module
     OPTION_REG.PS2    = 1; ////
     OPTION_REG.PS1    = 1; ////Timer0 increments every 256 instruction cycles
     OPTION_REG.PS0    = 1; ////
     OPTION_REG.INTEDG = 1; //Interrupt on rising edge
     
     while(1){
     
           IntToStr(fr,CN);
           Lcd_Out(1,1,CN); //Display the average frequency
     
     }

}
 

ErnieM

Joined Apr 24, 2011
8,377
There's much room for improvement here, but to start write out how you are making your measurement. With no schematic and just some code I can't tell why you hit the interrupt routine (IRS) and what you are calculating. I would have to guess your expected frequency range, accuracy... everything.

Inside the ISR you want to do a minimum, meaning don't do calculations there. Save off the value, reset for the next measurement, set a DONE flag, and exit. Your main loop checks the DONE flag, calculates, writes to display, resets DONE, and loops.

Using INT over FLOAT makes for faster code. If you can't fit into an int use a long int. And since frequency is always positive use an unsigned int or long (which is also faster too)

When computing don't compute anything you can per-compute, meaning don't divide by (256*256) divide by (14336). Dig thru that entire calculation and pull out anything that can be computed NOW and not every time you loop.

Have to leave for work... I'm sure others will dog pile on your rabbit now.
 
Top