Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! 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

Advanced Member level 2
Joined
Jun 26, 2006
Messages
503
Helped
44
Reputation
88
Reaction score
15
Trophy points
1,298
Location
VddVss
Activity points
3,882
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?
 

have you simulated it or tested on real life? if sim, which simulator did you use?
 

Does it matter ? :)

Does it work correctly in the simulator you use?
 

i don't have CCS now, nor the HEX file. could you attach it, i can test it with proteus...
 

    vsmGuy

    Points: 2
    Helpful Answer Positive Rating
here you go :)

hex file with .tar extension ( rename it back to .hex )
 

mmmm are you sure it's for a 16f876A it doesn't work connecting a virtual terminal to RX/TX (RC6, RC7) and a pulse generator to RC2...
 

Hi,

Have you seen this one (mikroC) :

https://www.micro-examples.com/public/microex-navig/doc/110-p16f84-freqmeter.html

"This simple frequency meter will give a 1 Hz accuracy in the range from 1 Hz to 67 KHz approximately with a 16 MHz crystal.
The maximum frequency depends on the speed of the microcontroller : with a 20 Mhz clocked pic, the maximum frequency range should be around 83 KHz
You can also make it run with a high-speed PIC : 40 MHz or 48 MHz, which should allow more than 160 KHz."
 

    vsmGuy

    Points: 2
    Helpful Answer Positive Rating
Kurenai_ryu said:
mmmm are you sure it's for a 16f876A it doesn't work connecting a virtual terminal to RX/TX (RC6, RC7) and a pulse generator to RC2...

Are you using Proteus 7.5 SP3?
 

Kurenai_ryu said:

You need to patch pic16.dll ( among others - depending on your installation ).

Better still - use a more recent (7.7) version of Proteus.

Your version has problem with the PIC16 models - for eg - the CCP/PWM feature will not work.

You can try and write a program that uses PWM/CCP of a 16 part and try and simulate in Proteus.
 

mmmm,
ok I'll take your advise, thank you...


ok, Now i tested with an older version,
mmmm, i got the same results...

well, I made an small CC5X version of your program, and... it worked! but I had to clear the timer everytime i'm going to wait... i got you 12500 ticks...

Added after 3 minutes:

here it is,


so i think it's an CCS problem, maybe the CCP init, is not correct...
 

I think that

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

is not the right way to configure ccp1 to capture every 4th edge... must check CCS documentation...

also, you could try to set it "by hand", writing directly to CCP1CON and T1CON
 

Kurenai_ryu - could you do a quick test and see what is the lowest and highest freq. you code can sample accurately in Proteus?

Also upload the .DSN and .hex file you used for this test separately in your reply.
 

well... doing some tests.. i found a bug in both our codes!!!
where:
Code:
#int_TIMER1 
void  TIMER1_isr(void) 
{ 
   ++timer1Overflow; 
}
should be
Code:
#int_TIMER1 
void  TIMER1_isr(void) 
{ 
  if(stateOfReading == MIDDLE_OF_READING )
   ++timer1Overflow; 
}

test it and tell me the results...


I checked 10Hz and i get 125001 ticks (2 overflows and 59465)

I checked 1kHz and i get 1251 ticks (0 overflows and 1251)


i checked 10kHz and i iget 126 ticks (0 overflow, and 126)

i checked 100kHz and i iget 14 ticks (urg!) (0 overflow, and 14)

when i got some free time, i'll takeout the dividiers and prescalers, and re-test all of this...

Added after 5 minutes:

all of them with the "before" variable, it seems the most accurate
 

    vsmGuy

    Points: 2
    Helpful Answer Positive Rating
Kurenai_ryu said:
well... doing some tests.. i found a bug in both our codes!!!
where:
Code:
#int_TIMER1 
void  TIMER1_isr(void) 
{ 
   ++timer1Overflow; 
}
should be
Code:
#int_TIMER1 
void  TIMER1_isr(void) 
{ 
  if(stateOfReading == MIDDLE_OF_READING )
   ++timer1Overflow; 
}

Why do you say so?

If you look closely - you will notice that:

1. I enable interrupts only during the reading
2. I reset the timer at the start of the CCP1 ISR

all of them with the "before" variable, it seems the most accurate

It should not be so - I will come to that later :)
 

vsmGuy said:
Kurenai_ryu said:
well... doing some tests.. i found a bug in both our codes!!!
where:
Code:
#int_TIMER1 
void  TIMER1_isr(void) 
{ 
   ++timer1Overflow; 
}
should be
Code:
#int_TIMER1 
void  TIMER1_isr(void) 
{ 
  if(stateOfReading == MIDDLE_OF_READING )
   ++timer1Overflow; 
}

Why do you say so?

If you look closely - you will notice that:

1. I enable interrupts only during the reading
2. I reset the timer at the start of the CCP1 ISR
ok, it's a bug in my code, i got more timeroverflows while printing the message...

all of them with the "before" variable, it seems the most accurate

It should not be so - I will come to that later :)
yeah I think the same!
but i think it 'compensates' the fact that we clear the timer many times... the correct way is just capture 2 consecutives edges and check the difference between them, so not touching the timer (it runs free, and just take in account the overflows between both edges...) otherway is to measure the correct delay and "compensate" it after...
 

Hi,

This look much simpler to me (mikroC) :

**broken link removed**


"This is a simple capture a lenght of pulse. Capture from rising edge
to rising edge and send to terminal via RS232"
 
Last edited by a moderator:

hugo said:
Hi,

This look much simpler to me (mikroC) :

**broken link removed**


"This is a simple capture a lenght of pulse. Capture from rising edge
to rising edge and send to terminal via RS232"

It does not handle timer overflows :)
 
Last edited by a moderator:

hello!
look in here,exactly what u need:

http://lea.hamradio.si/~s53mv/counter/ctr.zip


dont't expect the simulation to work on PROTEUS!
the simulator DOES NOT switch off the inverter gate at T1CKI.it is very active
between RC1 AND RC2.the result is that indicated frequency is 3.006 times higher.
the simulator just ignores 0=T1OSCEN.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top