Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 5 of 5

Thread: Frequency meter, inaccurate at Teensy ++2.0

  1. #1
    Junior Member
    Join Date
    Sep 2020
    Posts
    6

    Frequency meter, inaccurate at Teensy ++2.0

    Hello Board,

    hope you can help!

    I try to build up an frequency meter but I have problems to reach the needed accuracy.
    Whats important to me:
    -Frequency range from 50Hz to 500 Hz
    -Accuracy in 0,1 Hz

    Currently I have the following results with the Frequency Generator at F0:
    Test 1:
    The generator delivers 110 Hz to the teensy, the serial monitor shows 109,89 Hz and 110,21 Hz, but nothing in between.

    Test 2:
    The generator delivers 330 Hz to the teensy, the serial monitor shows 328,74 Hz and 331,57 Hz, but nothing in between.


    Here is my current code:

    //----------------------------------------------
    boolean clipping = 0;

    //data storage variables
    byte newData = 0;
    byte prevData = 0;
    unsigned int time = 0;//keeps time and sends vales to store in timer[] occasionally
    int timer[10];//storage for timing of events
    int slope[10];//storage for slope of events
    unsigned int totalTimer;//used to calculate period
    unsigned int period;//storage for period of wave
    byte index = 0;//current storage index
    float frequency;//storage for frequency calculations
    int maxSlope = 0;//used to calculate max slope as trigger point
    int newSlope;//storage for incoming slope data

    //variables for decided whether you have a match
    byte noMatch = 0;//counts how many non-matches you've received to reset variables if it's been too long
    byte slopeTol = 10;//slope tolerance- adjust this if you need
    int timerTol = 3;//timer tolerance- adjust this if you need

    //variables for amp detection
    unsigned int ampTimer = 0;
    byte maxAmp = 0;
    byte checkMaxAmp;
    byte ampThreshold = 30;//raise if you have a very noisy signal

    void setup(){

    Serial.begin(9600);



    cli();//diable interrupts

    //set up continuous sampling of analog pin 0 at 38.5kHz

    //clear ADCSRA and ADCSRB registers
    ADCSRA = 0;
    ADCSRB = 0;

    ADMUX |= (1 << REFS0); //set reference voltage
    ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only

    ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
    ADCSRA |= (1 << ADATE); //enabble auto trigger
    ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
    ADCSRA |= (1 << ADEN); //enable ADC
    ADCSRA |= (1 << ADSC); //start ADC measurements

    sei();//enable interrupts
    }

    ISR(ADC_vect) {//when new ADC value ready

    PORTB &= B11101111;//set pin 12 low
    prevData = newData;//store previous value
    newData = ADCH;//get value from A0
    if (prevData < 127 && newData >=127){//if increasing and crossing midpoint
    newSlope = newData - prevData;//calculate slope
    if (abs(newSlope-maxSlope)<slopeTol){//if slopes are ==
    //record new data and reset time
    slope[index] = newSlope;
    timer[index] = time;
    time = 0;
    if (index == 0){//new max slope just reset
    PORTB |= B00010000;//set pin 12 high
    noMatch = 0;
    index++;//increment index
    }
    else if (abs(timer[0]-timer[index])<timerTol && abs(slope[0]-newSlope)<slopeTol){//if timer duration and slopes match
    //sum timer values
    totalTimer = 0;
    for (byte i=0;i<index;i++){
    totalTimer+=timer[i];
    }
    period = totalTimer;//set period
    //reset new zero index values to compare with
    timer[0] = timer[index];
    slope[0] = slope[index];
    index = 1;//set index to 1
    PORTB |= B00010000;//set pin 12 high
    noMatch = 0;
    }
    else{//crossing midpoint but not match
    index++;//increment index
    if (index > 9){
    reset();
    }
    }
    }
    else if (newSlope>maxSlope){//if new slope is much larger than max slope
    maxSlope = newSlope;
    time = 0;//reset clock
    noMatch = 0;
    index = 0;//reset index
    }
    else{//slope not steep enough
    noMatch++;//increment no match counter
    if (noMatch>9){
    reset();
    }
    }
    }

    if (newData == 0 || newData == 1023){//if clipping
    clipping = 1;//currently clipping
    Serial.println("clipping");
    }

    time++;//increment timer at rate of 38.5kHz

    ampTimer++;//increment amplitude timer
    if (abs(127-ADCH)>maxAmp){
    maxAmp = abs(127-ADCH);
    }
    if (ampTimer==1000){
    ampTimer = 0;
    checkMaxAmp = maxAmp;
    maxAmp = 0;
    }

    }

    void reset(){//clean out some variables
    index = 0;//reset index
    noMatch = 0;//reset match couner
    maxSlope = 0;//reset slope
    }


    void checkClipping(){//manage clipping indication
    if (clipping){//if currently clipping
    clipping = 0;
    }
    }


    void loop(){

    checkClipping();


    if (checkMaxAmp>ampThreshold){
    frequency = 38462/float(period);//calculate frequency timer rate/period

    //print results
    Serial.print(frequency);
    Serial.println(" hz");
    }

    delay(100);

    }

    //----------------------------------------------

    Hope you guys can help, thanks in advance!

    Best Regards!

  2. #2
    Senior Member
    Join Date
    Aug 2013
    Location
    Gothenburg, Sweden
    Posts
    336
    With a sampling clock of 38.5 kHz then the period values have a time granularity of 25.97 uS.
    Measuring a 110Hz signal, then gives an uncertainty of 0.3 Hz,

    deltaf = 110 - 1/(1/110 + 1/38500) = 0.313

    similarly for 330 Hz :

    deltaf = 330 - 1/(1/330 + 1/38500) = 2.804

  3. #3
    Junior Member
    Join Date
    Sep 2020
    Posts
    6
    Understand, thanks a lot!
    Is there a chance to become more accurate by software optimization or is it a hardware limitation?

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,087
    Maybe try the FreqMeasure library?

    https://www.pjrc.com/teensy/td_libs_FreqMeasure.html

    If you already have a signal generator set up, just connect it to pin 17 on Teensy++ 2.0 and open the example in Arduino with File > Examples > FreqMeasure > Serial_Output. Very easy to just run the example and see if it does any better.

  5. #5
    Junior Member
    Join Date
    Sep 2020
    Posts
    6
    Quote Originally Posted by PaulStoffregen View Post
    Maybe try the FreqMeasure library?

    https://www.pjrc.com/teensy/td_libs_FreqMeasure.html

    If you already have a signal generator set up, just connect it to pin 17 on Teensy++ 2.0 and open the example in Arduino with File > Examples > FreqMeasure > Serial_Output. Very easy to just run the example and see if it does any better.
    This libary works very accurate, I can say it`s around only +/- 0,01 Hz deviation!
    Do you see any chance to combine this libary with my program? Because normally I need a analog (ADC) input with the checkMaxAmp function to seperate the usefull signal from any other "noise" like overtones.

    I already tryed to built up an analog circuite with frequency filter compression and comerator in the end, but even this works not very well. Unfortunatly im more in analog engineering and not very deep in programming, so it's quite hard to find a solution... .

    Thank you in advance!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •