Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Frequency counting using PIC16F876A

Status
Not open for further replies.

vsmGuy

New Member
I am trying to make a frequency counter using a PIC16F876A clocked by a 20Mhz XTAL.

The target is to design a system that has a resolution of 1LSB upto 1MHz. It is not necessary though that the PIC achieve this in isolation.

For example, if the PIC can count accurately till 100Khz, I would connect it to a 75HC4059 so that I can divide the original signal before connecting it to the PIC CCP1

Here is the core logic that I have managed so far:

Code:
#define START_OF_READING  0
#define MIDDLE_OF_READING    1
#define END_OF_READING   2

#define SAMPLE_COUNT 2

// being explicit about the types ( for quick readability )

unsigned int8 stateOfReading;

unsigned int8 timer1Overflow;

unsigned int16 uiCount, uiCountBefore, uiCountAfter, uiCountStart;

//int1 state = 0;
//#define printf

// Fires when timer1 overflows
#int_TIMER1
void  TIMER1_isr(void) 
{
   ++timer1Overflow;

   //output_bit( PIN_C0, (state = !state));
}

#int_CCP1
void  CCP1_isr(void) 
{
   /*
   output_bit( PIN_C0, state );
   state = !state;
   //*/
   uiCountStart = get_timer1();

   if ( stateOfReading == START_OF_READING )   // first edge has arrived
   {
      stateOfReading = MIDDLE_OF_READING;
      timer1Overflow = 0;
      set_timer1 ( 0 );     // restart timer on this edge
   }
   else if ( stateOfReading == MIDDLE_OF_READING ) // second edge has arrived
   {
      uiCountBefore = get_timer1();
      uiCount = CCP_1; // get capture value. *Theoretically* should be same as writing iCount = get_timer1(); ( Practically though, it never is even close ? Does the timer1 value get copied into CCP1 AFTER the ISR is serviced )
      uiCountAfter = get_timer1();

      stateOfReading = END_OF_READING;   // prevent further processing during this interrupt
   }
}


The driver:

Code:
void main()
{
   setup_adc_ports(NO_ANALOGS);

   setup_spi(SPI_SS_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   setup_timer_1( T1_INTERNAL | T1_DIV_BY_4 );
   //setup_timer_1( T1_INTERNAL );

   setup_ccp1( CCP_CAPTURE_RE | CCP_CAPTURE_DIV_4 ); // Configure CCP1 to capture every 4th rising edge\
   //setup_ccp1( CCP_CAPTURE_RE );

   enable_interrupts(INT_TIMER1);
   enable_interrupts(INT_CCP1);
   //enable_interrupts(GLOBAL);
   disable_interrupts( GLOBAL );
   
   stateOfReading = END_OF_READING; // We don't want to set this to either START_OF_READING or MIDDLE_OF_READING - as that would let the ISR code execute

   unsigned int8 dx = 0;

   while( TRUE )
   {
      unsigned int32 totalTicks = 0;

      for ( dx = 0; dx < SAMPLE_COUNT; ++dx )
      {
         stateOfReading = START_OF_READING; // allow ISR to execute
         enable_interrupts( GLOBAL );

         while ( stateOfReading != END_OF_READING );

         disable_interrupts( GLOBAL );
         
         printf ( "[%d] timer1Overflow = %d, uiCount = %Lu/%Lu/%Lu/%Lu\r\n", dx, timer1Overflow, uiCountStart, uiCountBefore, uiCount, uiCountAfter  );

         totalTicks += ( 0x10000 * timer1Overflow ); /* Every overflow -> 65536 ticks */
         totalTicks += uiCountStart;
      }

      printf ( "RAW totalTicks = %Ld\r\n", totalTicks );

      totalTicks /= SAMPLE_COUNT;
   }
}

Once I connect this to a 100Hz signal, this is what I expect:

100Hz period = 10^4uS

As we have divided timer1 by 4, its resolution is 0.8uS and thus would give 10^4/0.8 = 12500 ticks

The CCP1 ISR being configured to fire after every 4th edge, the timer1 should have 4 times the above value = 4*12500 = 50000 ticks

50k ticks is well below 65536 ticks of timer1 and hence, at this sample freq., timer 1 should never overflow.

This is the output I get once I run my code:

Code:
[0] timer1Overflow = 3, uiCount = 3387/3392/3377/3396
[1] timer1Overflow = 3, uiCount = 3387/3392/3377/3396
RAW totalTicks = 399990

As we can see, timer1 overflowed 3 times ( = 3 * 65536 ticks ) and even after that counted up to 3387
This means, the sampling freq. triggered 3 * 65536 + 3387 = 199995 ticks

At 0.8uS for timer1, 199995 ticks = 6.25Hz

This is 16 times less than the actual frequency!

What is going wrong?
 

Attachments

  • FreqCounter.hex
    5.2 KB · Views: 128
If you check MicroChip, there's a VERY, VERY old design in the application notes (20 years old?) for an auto-ranging 50Mhz frequency counter using a PIC clocked at only 4MHz. If you check the PICList there's a slightly more 'modern' (only in the teens) version that uses a 16F84 (or similar) and an LCD display. No need to use any external counters.
 
If you check MicroChip, there's a VERY, VERY old design in the application notes (20 years old?) for an auto-ranging 50Mhz frequency counter using a PIC clocked at only 4MHz. If you check the PICList there's a slightly more 'modern' (only in the teens) version that uses a 16F84 (or similar) and an LCD display. No need to use any external counters.

I believe that there is a max 40nS limit to timer0 pin which limits the freq. that can be measured by the PIC to 25Mhz?

Do we have any details about the technique of measuring freq. in excess of internal core freq. using the timer0 "self clocking" technique ( input signal is gated and an extra pin is used to clock the prescale register ) on this forum ?

I wanted to read a good writeup on this technique so I could try it out.

I did read the Appnote you are talking about - however, it uses very tightly coded delays that would tie up the PIC completely ( all the PIC would be able to do is measure a freq. while I intend to add more features )
 
I believe that there is a max 40nS limit to timer0 pin which limits the freq. that can be measured by the PIC to 25Mhz?
It's 20-nsecs (50-MHz) "with prescaler" in the specs'...

I did read the Appnote you are talking about - however, it uses very tightly coded delays that would tie up the PIC completely ( all the PIC would be able to do is measure a freq. while I intend to add more features )
You could capture overflows and gate the T0CKI input via interrupts. Many of us use a 1-msec or 10-msec 'heartbeat' interrupt already for RTC and switch management functions. Just task that for additional functionality.

Regards, Mike
 
Last edited:
You could capture overflows and gate the T0CKI input via interrupts. Many of us use a 1-msec or 10-msec 'heartbeat' interrupt already for RTC and switch management functions. Just task that for additional functionality.
Regards, Mike

I am finding it hard to "get" this thing.

Is these some nice documentation/C code for this?
 
Last edited:
If I'm reading your posts correctly it seems you want to derive Frequency from measuring the period of a signal (leading edge to leading edge). That's quite a bit different from a frequency counter so I apologize if we've accidentally taken you off track...
 
:-D

I am thinking of using TIMER1 in asynch mode - any working samples in C to read/start from?
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top