What is the most accurate timing method for function execution?

Hi,

I am facing a dilemma where i need to run a specific function, let's say, every 200 ms, but i want the loop to be as accurate as possible. So, from what i aware of, my choices are limited to either using a timer interrupt, like Timer2.h library, otherwise use millis().

I want to find the number of encoder pulses at a specific time interval. I have attached the motor encoder pin to my Arduino UNO's external interrupt pin. But i want to read its encoder pulse value as accurately as possible at a regular time interval.

My questions:

  1. Which one is the most accurate way: timer interrupt or millis or others?
  2. Is there a big difference between the two?
  3. And why is it the most accurate? I would appreciate some insight under the hood.
  4. Is it possible to analyse the loop execution time through some code?

What frequency are you reading--that is, how many pulses do you expect in your 200ms?

I would recommend the freqCount library. FreqCount Library, for Measuring Frequencies in the 1 kHz to 5 MHz Range

It uses the pulse signal as an external clock source for Timer1 and gates the counting interval with Timer2.

It follows the frequency counting procedure shown by Nick Gammon here in the first example. https://www.gammon.com.au/timers

there is also micros() that is more specifac than millis(). you dont hear of it much because usually millis is usually sufficient.

when you are coding make sure the main readings or actions are done first. any calculations that might cause delay should be done during the wait time for the next cycle.

DryRun:
I am facing a dilemma where i need to run a specific function, let's say, every 200 ms,

200ms? or 200.0ms? or 200.000ms? The answer depends on how accurately you need to have the 200.

If you just need it to the millisecond then you can probably catch it with millis and avoid the overhead of a timer interrupt.

If you need it to the microsecond then you have no choice but to use a 16 bit timer1 interrupt.

DryRun:

  1. Which one is the most accurate way: timer interrupt or millis or others?

The timer interrupt is more accurate in as far as firing as close as possible to the target time. But how fast are your encoders really pinging? If you have a couple ms lag maybe you're off by 1 count.

DryRun:
2. Is there a big difference between the two?

Overhead. ISR will mess with timing on other functions. You'll have to use critical sections to access the data. The code is more involved. Rule of thumb with interrupts is use them when you have to and avoid them when you can.

DryRun:
3. And why is it the most accurate? I would appreciate some insight under the hood.

You can link the timer up to the system clock at 1:1 so you get 62.5ns resolution. The interrupt fires immediately (as long as an interrupt with higher priority isn't already running). So it can usually catch things the quickest, to within a microsecond or two at worst with good code.

With millis you have to actually wait for the code to come back around to that statement. If you write good code you can keep that time to a few hundred microseconds, but it's still a tiny amount of lag. For the purpose of reading encoders that aren't being spun by geared up jet engines or something, you can probably afford that.

DryRun:
4. Is it possible to analyse the loop execution time through some code?

Not without affecting it a little. And sometimes the effects aren't completely intuitive. Sometimes adding your timing code will cause the compiler to completely rethink it's optimization and you get completely different code. But most of the time just adding a loop counter to count a few thousand loops and using micros before and after you can get a pretty good average.

The best way I've come up with is a simple pin toggle on an output pin by writing to the input register (look it up "Arduino Port Manipulation") at the top or bottom of loop(). Then I can watch that pin with my scope or even another Arduino and get a pretty good idea that way. That almost never affects the timing of the rest of the code since it is a single register write right at the beginning or end of the function.