vsmGuy
Advanced Member level 2
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:
The driver:
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:
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?
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?