1. 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.
    Dismiss Notice

Arduino Timer 1 counting in excess of 16 bits

Discussion in 'Microcontrollers' started by dr pepper, Sep 29, 2017.

  1. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    I want to measure a frequency 1Mc.
    I can get it to work but I get a reading 1,065535 Mc, 65.5365Kc too high.
    After a lot of reading I found timer 1's overflow trigger doesnt occur at 0 its at FFFF, hence my reading.
    Is there any way I can get the overflow to interrupt on 0, I tried setting Ocr1 to 0 but that doesnt work.
     
  2. be80be

    be80be Well-Known Member

    Joined:
    Aug 23, 2008
    Messages:
    4,869
    Likes:
    144
    Location:
    morristown,tn
    Why not just subtract FFFF
    1,065535 Mc - 65.5365Kc
     
  3. jpanhalt

    jpanhalt Well-Known Member Most Helpful Member

    Joined:
    Jun 21, 2006
    Messages:
    6,076
    Likes:
    524
    Location:
    Cleveland, OH, USA
    It appears the Atmega has a function similar to the PIC capture/compare on Timer1:
    On a PIC, you can preset the comparator register(s) and get an interrupt/interrupt flag at any count.

    Can you describe in a little more detail how the measurement is done and its math? Are there multipliers/dividers and/or pre- or post-scalers involved?

    John
     
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,310
    Likes:
    914
    Location:
    Rochdale UK

    Preload with 1... Job done..
     
  6. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    I cut the code down to this, its all messed up, subtracting and preloading just makes things worse.
    I think the 'duino's operating system is messing things up, I've seen other frequency counters disable timer0, maybe thats it.
    I really need to use interrupts for other things, I think I might time share, count freq for a bit then do something else.
    Seems I found another 'duino not so nice, they are ok at first but you soon outgrow them, I think I'll go back to pics.

    For some reason the forums code post function wont work so I'll cut & paste.

    ISR(TIMER1_CAPT_vect) // PULSE DETECTED! (interrupt automatically triggered, not called by main program)
    {
    TCNT1 = 0; // restart timer for next revolution
    masterOscCount += ICR1; // save No. of counts between pulses
    measureReadyFlag = true;
    }

    ISR(TIMER1_OVF_vect) // counter overflow/timeout
    {
    masterOscCount += 65536;
    }

    void loop ()
    {
    if (measureReadyFlag)
    {
    measureReadyFlag = false;
    //TCCR1B = B01000000; // Timer 1 no prescaler, capture on rising edge, no noise filter, ext clock rising
    measure();
    //TCCR1B = B01000111; // Timer 1 no prescaler, capture on rising edge, no noise filter, ext clock rising
    }

    }
    void measure ()
    {
    Serial.println (masterOscCount / 1.048576);
    masterOscCount = 0;
    }
     
  7. be80be

    be80be Well-Known Member

    Joined:
    Aug 23, 2008
    Messages:
    4,869
    Likes:
    144
    Location:
    morristown,tn
    If it's atmega 328 you have
    Code (text):

    TOP Signalize that TCNT1 has reached maximum value.
    BOTTOM Signalize that TCNT1 has reached minimum value (zero).
     
    So you can test both ways has it hit top or bottom
    One more thing you can use arduino without the cores which means you can use pure avr code.
    which takes way less space.
     
  8. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    Yes I found top & bottom, had a quick look to see if you can access them, top looks as though its in OCR1, not sure where they hide bottom.
    I didnt know you could turn the core off, I'll look into that, for some stuff I'd be better without it, but I'm guessing all the niceties would disappear with it like serial & print.

    I think I'm going to try a bodge when I get home (just finished work), it seems that the counter goes from correct reading to 65535 out and doesnt vary, I've never seen it change just by a few hz and I've been using a ocxo, so its either correct or miles out, frequency readings are not going to change by that much so I'll check each reading against the last and if its changed by a large amount dump the current reading, then if the core is causing a major error once in a while (it seems to be Ok for a minute then I'll get a few 65535 out readings, then it'll be Ok again for a while so it does look as though its beating frequency wise with some operation in the core) it will be ignored.
     
    Last edited: Oct 1, 2017
  9. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    I dumped the idea altogther, I compiled paul stoffregan's Freqcounter library and tested that out, it works, then I tried it and my other code multitasking, it works fine.
    The thing now though is the Freqcount library uses the 'duino's internal crystal for timing, I want to use an external 1pps clock pulse, so I'll have to dive into the .cpp file and see if I can make it do that, as well as posting on the Pjrc forum to see if anyone has any input of course.

    Edit: just reading something I found out if your dealing with int's or long variables interrupts should be disabled as if the code is interrupted during a word or long word transfer then one or more of the bytes gets corrupted - interesting.
     
    Last edited: Oct 1, 2017
  10. be80be

    be80be Well-Known Member

    Joined:
    Aug 23, 2008
    Messages:
    4,869
    Likes:
    144
    Location:
    morristown,tn
    You can still us the stuff you want the arduino ide uses every thing kind of like turning off mikro c libraries
     
  11. Ian Rogers

    Ian Rogers Super Moderator Most Helpful Member

    Joined:
    Mar 28, 2011
    Messages:
    9,310
    Likes:
    914
    Location:
    Rochdale UK
    Bad compiler!! If the context is saved correctly the carry should never be missed.. However!! If you read the PIC timer doc, it mentions the bother with reading the timer in the wrong way!! You really need to stop the timer then read..

    If timing is critical.. Use preload as I mentioned
     
  12. be80be

    be80be Well-Known Member

    Joined:
    Aug 23, 2008
    Messages:
    4,869
    Likes:
    144
    Location:
    morristown,tn
  13. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    My head hurts.
    I tried pre loading with 1 and subtracting 1 and a few other ideas, but the result just changes by one.
    The issue comes & goes, it'll read 1,000,000 for a 1mhz input, and then every 20 results or so it'll go 1,065,535 sometimes just the once, sometimes a few times, then it'll go Ok again, so subtracting 65,535 obviously wont work.
    I think the core is robbing processing time and the timer interrupts are not being serviced when requested and the count gets too high, a bit odd though its always 65,535 too high, I've tried a 1mc signal and a 3mc signal, both come up with the same 65,535 over read.
    I find it odd the error is the same, plus if you divide 1mc by 16 bits it isnt close to an integer so the 16 bit timer will not be rolling over at the measurement interrupt anyway.
    My sig gen square wave o/p goes to 240kc this counts fine every time.
    It can be done as the freqcount lib works ok, it undereads on 3mc, and if I then measure 1mc the underead is 1/3 less so its a crystal accuracy error not a count error, the lib for this at first looks easy but after some messing you soon get bogged down.
    Reading the 16 bit timer understandably could go wrong if there was an interrupt inbetween hi & lo byte reads, I've tried disabling interrupts during counting, but the issue is exactly the same.
    So not sure what to do right now, maybe a hardware solution.
     
    Last edited: Oct 2, 2017
  14. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    It would seem google is my freind.
    This is a frequency counter using 1PPS from a gps system, I'm not using gps, but I do have 1PPS.
    If it does the job it saves me a lot of headache.
    Interesting to see they dont use timer 1 in capture mode, an interrupt pin is used as the gate instead.
    https://github.com/JChristensen/gpsFreq
     
  15. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    Unbelievable, I compiled the above example program, and its the same, not exactly but near enough, the program doesnt over read by 65,535 on occasion, it under reads by that amount.
    I definately should have stuck with pic's.

    Edit: A bit of lateral thinking, I want measure the accuracy of 1mc and 3mc oscillators so I can plot a graph, maybe also generate a control voltage to control them in a software FLL loop.
    Its not necessary to implement a 32 or 48 bit counter that counts an absolute frequency value, the osc will only be off by a max of 200hz, so I can let the 16 bit timer1 overflow and ignore it, if the oscillator is running at the correct freq the 16 bit contents will the same every time, with a 1 second gate counting 1mc with 16 bits will be 16,960 decimal, so if I subtract this from timer1 every 1 second capture I'll get deviation from the desired freq, there will be a couple other desired freq's too but I can take care of that in the code.
    Dont know why I didnt think o that sooner.
     
    Last edited: Oct 3, 2017
  16. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    Found this on a website, a method of counting frequency using timer1, it seems you need to take care if the counter rolls over:

    case GO:
    prev_t1_count = t1_count; // prev. counter snapshot
    t1_count = ICR1; // new counter snapshot

    // compensate for counter wraparound
    if (t1_count < prev_t1_count) {
    t1_delta = 65536 - (prev_t1_count - t1_count);
    }
    else {
    t1_delta = t1_count - prev_t1_count;
    }

    You were on the right lines Burt, the bit I failed to get was the check for a wraparound before subtracting 65536.
     
  17. dr pepper

    dr pepper Well-Known Member Most Helpful Member

    Joined:
    Oct 6, 2008
    Messages:
    4,763
    Likes:
    259
    Location:
    North west UK
    This worked:

    ISR(TIMER1_CAPT_vect) // PULSE DETECTED! (interrupt automatically triggered, not called by main program)
    {
    //TCNT1 = 0; // restart timer for next revolution
    oscCount = ICR1; // save No. of counts between pulses
    measureReadyFlag = true;
    }

    ISR(TIMER1_OVF_vect) // counter overflow/timeout
    {
    overFlow++;
    oscCount -= 65535;
    }

    void loop ()
    {
    if (measureReadyFlag)
    {
    measureReadyFlag = false;
    measure();
    }

    }
    void measure ()
    {
    Serial.println (((oscCount - oscCountLast) + (overFlow * 65536)) / 1.048576);
    overFlow = 0;
    oscCountLast = oscCount;

    Icr1 is the capture register, when the capture pin goes active an interrupt is generated and this register contains the snapshot value of timer1 at that time, so it doesnt matter about interrupt latency.
    Pretty darn simple once you sussed it, the only change is a -65535 when the 16 bit timer rolls over, all this does is rolls the timer back to where it was +1, kinda like a combination of what you were saying, subtracting and pre-loading.
    The reason for the / 1.048576 correction is my gate time isnt 1 second, its 1mc / 2 to the power 20, ie I just used 2 4040 counter ic's instead of 3 'hc390's to / by 1,000,000 to get 1hz.
     
    Last edited: Oct 3, 2017

Share This Page