Developing in C for the ATmega328: Marking Time and Measuring Time
Where I discuss marking time and measuring program execution time.
Introduction
The Arduino has two time routines, millis() and micros(). The former provides the ability to measure time in milliseconds for a long period of time (50 days) and the latter provides the ability to measure in microseconds (kinda) for a far shorter period of time, 70 minutes. The kinda arises from this comment from the arduino reference on micros() “On 16 MHz Arduino boards (e.g. Duemilanove and Nano), this function has a resolution of four microseconds (i.e. the value returned is always a multiple of four). On 8 MHz Arduino boards (e.g. the LilyPad), this function has a resolution of eight microseconds.”
AVR C Precise (nsec) Time Measurement - ticks()
The AVR_C approach is slightly different. It’s most fundamental time element is a tick, which is the clock period of the processor. On a 16MHz ATmega328P, this is 62.5 nanoseconds. To determine elapsed ticks, one needs to call ticks() twice, once before the event and once after, then subtract the former from the latter to derive the number of ticks which have passed. There are two things to understand:
- Dividing the number of ticks by 16 gets you 1 microsecond, which is the same as shifting to the right, 4 times. So n usecs = (ticks() » 4).
- There was a micros() routine which has since been deprecated. It returned the number of micros() elapsed, however it was a derivative of ticks(), and returned ticks() shifted to the right four times (see point 1.) When this was done there were rounding errors:
Testing System Ticks (1 tick = 62.5ns)
Delay of 1 ms
ticks() -> micros: 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 Complete
micros(): 1003 62443 1003 1003 1003 62443 1003 1003 1003 1003 Complete
AVR C (msec) Time Measurement - ticks_ro() and millis()
The routine ticks() only counts to 65,535, so it rolls over at approximately 4.096 milliseconds. To access the roll over count, there is ticks_ro(), it tracks the number of times ticks() has rolled over (or think of it as a counter of every 4.096msecs). This counter will roll over at 65,535 4.096ms or 4.47 minutes.
You may also use millis() which provides millisecond counter which can be valuable as well.
Usage
I typically use ticks() for my execution time measurements as the processor is typically doing something which takes less than 4.096ms. I’ll use millis() to measure something in the physical world, such as reading a sensor or multi-tasking. I use ticks_ro() as a check on ticks() to ensure I haven’t began to measure something which is taking longer than 4.096ms.
All three functions will require initialization using the sysclock.h header and calling:
- sys_clock_1() for ticks() and ticks()_ro
- sys_clock_2()) for millis() and it is used for button debouncing
And all three functions follow the same process:
- call timing function before the event, (now)
- call timing function after the event (elapsed)
- subtract (now) from (elapsed) to derive the number of time elements which have passed.
As an example, here is ticks() to time math calculations in the mapping entry and math entry. It is an example of how I demonstrated accuracy and speed of execution in the math entry. (all lines shown are required):
|
|
- Line 10: I am shifting to the right by 4 for a divide by 16 to arrive at microseconds.
I recommend taking a similar approach when you are attempting to evaluate either execution speed or an optimal approach. For example, in the math entry, I found that while float delivers the right answer, it can be as much as 8X slower than a 32-bit unsigned calculation. In situations where I might want to sample and calculate at a fast rate, this is critically important.
Using millis()
For completeness, I want to include an example of using millis(). In this case, I’ll use millis() to demonstrate that both millis() and delay() will result in the same amount of time. For example when I time a delay for 1000ms or 1 second, millis() will report 1000ms have elapsed. Note: With a 3-4ms overhead.
|
|
Using ticks() and ticks_ro()
Here is the example showing how to use both ticks() and ticks_ro(). Using them together provides a clock which is far more precise than micros() and allows for greater accuracy.
|
|
Comments powered by Talkyard.