Entire Forum This board This topic Members Entire Site
 Pages: [1]
 Author Topic: Frequency Measurement in AVR  (Read 5240 times) 0 Members and 1 Guest are viewing this topic.
khan_yousaf
Junior Member

Muted
Offline

Posts: 41

Thank You
-Given: 33

 « on: October 09, 2009, 05:50:04 17:50 »

I tried to measure frequency (range 2-200kHz) using atmega32 but ICP module of AVR seems to be a bit glitchy....
I mean after every 2 or 3 seconds i am getting a wrong value....
can somebody provide me with source code for solid performance?
 Logged
khan_yousaf
Junior Member

Muted
Offline

Posts: 41

Thank You
-Given: 33

 « Reply #1 on: October 13, 2009, 11:38:01 11:38 »

Perhaps another method might be to use a F-V converter but that will further add hardware to my design....
Here is my piece of code which i find to create the problems...
Code:
ISR(SIG_INPUT_CAPTURE1)
{
cli();
sbi(TIFR,ICF1);
temp_TCNT3=TCNT3;
COUNT_PULSE_EDGES++;
if(temp_TCNT3>60000)
cbi(TCCR1B,ICES1);
if(POS_EDGE_ID==FIRST_EDGE) //count positive edge
{
temp_ICP1=ICR1;
count_TmrOverflow=0;
POS_EDGE_ID=SECOND_EDGE;
// FREQ=0;
}
else if(POS_EDGE_ID==SECOND_EDGE)
{
temp_ICP2=ICR1;
if(temp_ICP2>temp_ICP1)
Delta=(temp_ICP2-temp_ICP1);
else if(temp_ICP1>temp_ICP2)
Delta=(temp_ICP1-temp_ICP2);
if(count_TmrOverflow==0)
T_Period=(125*(Delta)); // since the clock speed is 8 MHz , so 1/8 MHz = .125 usec. The result is in nano seconds
else if(count_TmrOverflow==1)
T_Period=(((65536-temp_ICP1)+temp_ICP2)*125);// since its a 16-bit timer so we need to subtract the reading from 65535
else if(count_TmrOverflow>1)
{
count_TmrOverflow-=1;
T_Period=(count_TmrOverflow * 4096000)+(125*((65536-temp_ICP1)+temp_ICP2));
}
//   T_Period_Ms=T_Period;//(T_Period/10000); //In fact we wanted to convert the nanosecond reading back into milli second reading
FREQ_OLD=FREQ_NEW;
FREQ=0;
FREQ=(1000000000/T_Period); // but the compiler was unable to show result like  0.166 so we reduced the division
POS_EDGE_ID=FIRST_EDGE; // factor by 10 to get 1.66 and compensated it in freq calculations by adding this factor of 10.
FREQ_NEW=FREQ;
if((FREQ_NEW>=FREQ_OLD-500)&&(FREQ_NEW<FREQ_OLD+500))
COUNT_SAME_FREQ++;
if (COUNT_SAME_FREQ==5)
{
POS_EDGE_ID=THIRD_EDGE;
// cbi(TCCR1B,ICES1);
COUNT_SAME_FREQ=0;
}
COUNT_PULSE_EDGES+=2;
}
sei();
}
 Logged
Ichan
Hero Member

Offline

Posts: 837

Thank You
-Given: 312

 « Reply #2 on: October 13, 2009, 06:17:04 18:17 »

Hi,

I don't read your code carefully, but my comment is too much things handled in the interrupt service - it can be a trouble.

The global idea on measuring frequency:
- make a global variable let's say PulseCount
- set a timer with for ex. 100ms
- set an "pin change" (i forgot the name on ATMega) interrupt service for freq. input pin, every time it's fired increment the value of PulseCount by 1.
- every time the timer interrupt fired, you got number of pulse per 100 ms on PulseCount var, which mean freq of PulseCount x 10 in Herzt. Save the PulseCount var to another var to be processed somewhere else, then reset it to zero. You might also set a flag to other process that the freq. value is available.
- This way interrupt service will be very thin.

-ichan

PS: Another simple method is to use a timer in "counter mode", but uh.. i need to read the datasheet for this.
 Logged

There is Gray, not only Black or White.
sam_des
Active Member

Offline

Posts: 224

Thank You
-Given: 49

 « Reply #3 on: October 13, 2009, 08:34:34 20:34 »

Hello khan_yousaf,
I totally agree wth Ichan. You are trying to do too much within interrupt routine.

At 200kHz, you got only 5uSec between 2 successive captures, which is already stretching AVR to the limit even @ 20MHz clock. Look at the lisitng generated by compiler, see that just epilogue/prologue code of ISR takes nearly 5uSec and you are doing 32-bit mul/div operations within ISR.

Best way is as Ichan suggested, just to count the interrupts over fixed period & then calculate frequency.

I also suggest--
1) There is no need to disable interrupts within ISR as AVR does that automatically.
2) There is no need to clear the interrupt flag, as AVR does that automatically once you enter ISR.    Check datasheet for exceptions.
3) Never ever do the lengthy calculations, loops, soft-delays within ISR. ISR should be as small & fast as possible. Smaller the code, faster it'll be & easier to debug if something goes wrong. If you need to do something time-cosuming in response to interrupt, set a flag in ISR, which will be read by main-line & acted upon.
4) Avoid calling functions from ISR(including compiler's runtime library), unless you are 101% sure that they are fully reentrant. But note thar reentran functions are slower due to their stack opertations which will add to interrupt latency.

regards,
sam_des
 « Last Edit: October 13, 2009, 08:38:13 20:38 by sam_des » Logged

Never be afraid to do something new. Remember Amateurs built the Ark, Professionals built the Titanic !
wedo
Junior Member

Muted
Offline

Posts: 53

Thank You
-Given: 10

 « Reply #4 on: October 13, 2009, 09:13:13 21:13 »

I would advise you to use T1 pin for counting the incoming pulses within 1Sec.

ICP pin Usually used for cycle measurement, you can detect the rising and falling edges during a measured time.

Please check the attachment file is two examples about using ICP and T1 for frequency measurements.

Wedo,
 Logged
 Pages: [1]