Background
To fully understand the micros() function, you first need to understand the Timer #0 overflow interrupt handler which was covered in this post.
Recall the typical Ardiuno runs on a 16MHz oscillator. Both the millis() and micros() functions base their calculations on the Arduino Timer #0, which is running with a prescale of 64. This results in the timer ticking at 64*1/16,000,000th of a second (which is 0.000004 seconds or evey 4 µs). Its important to take note of this because the resolution of micros() is therefore 4 µs.
Also recall that since the Timer #0 counter (TCNT0) is 8-bit, it “rolls over” or “overflows” after every 256 ticks. This means an overflow occurs every 1/16,000,000(oscillator) * 64(prescale) * 256(roll over) = 0.001024 seconds, or 1.024 ms, or 1024 µs.
Expand a Macro
Now, let’s do some additional math so we can substitute a number in the place of the following macro (this macro is embedded inside an Arduino hardware file):
#define clockCyclesPerMicrosecond () ( F_CPU / 1000000L )
F_CPU is the oscillator frequency, and is defined during compilation. We already know this is 16,000,000, which makes:
clockCyclesPerMicrosecond = 16,000,000/1,000,000 = 16
micros() Simplified
I’ve removed some housekeeping steps which check for the potential rare instance of an interrupt occurring during the micros() function call and substituted the expanded macro from above. What is left is simply the meat of the function, which calculates the elapsed microseconds:
unsigned long micros() { return((timer0_overflow_count << 8) + TCNT0)*(64/16); }
Knowing all this boils the micros() calculation down to:
micros = (Timer #0 counter + (number of times timer #0 has overflowed * 256)) * 4
Actual Arduino micros() Function:
unsigned long micros() { unsigned long m; uint8_t oldSREG = SREG, t; cli(); m = timer0_overflow_count; #if defined(TCNT0) t = TCNT0; #elif defined(TCNT0L) t = TCNT0L; #else #error TIMER 0 not defined #endif #ifdef TIFR0 if ((TIFR0 & _BV(TOV0)) && (t & 255)) m++; #else if ((TIFR & _BV(TOV0)) && (t & 255)) m++; #endif SREG = oldSREG; return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond()); }
Do you wish you could read and write inline assembly code for the Arduino? Check out the book with greatly expanded coverage!
So the ‘micros()’ function itself already spoils the accuracy of the measurement? What’s the point then? (see my other post on FIXED POINT issue)
Hi. Is there any way I can modify this code to generate the number of nanoseconds since the arduino has started? Like an “nanos()” function…
Since a clock cycle will take 1/16.000.000 = 63 nanoseconds , the accuracy of your nanos function would be just 63 nanoseconds. I recommend to code your own functions with Timer0.
How can I build micros for timer1?
First consider, Timer 1 is used by the servo library. Using it would invalidate the library. Second, I haven’t looked through the datasheet to see if the functionality of Timer 0 could be duplicated with Timer 1 (for example, Timer 0 is 8-bit and Timer 1 is 16-bit). Otherwise, simply transfer the setup to the Timer 1 registers.
This is a great place to start.
how to get timer0_overflow_count because I tried this function with another name and it didn’t work
timer0_overflow_count is defined inside the wiring.c file as,
volatile unsigned long timer0_overflow_count = 0;
You should be able to access this variable by placing the following definition inside your code:
extern volatile unsigned long timer0_overflow_count;
I agree with your calculation for “This means an overflow occurs every 1/16,000,000(oscillator) * 64(prescale) * 256(roll over) = 0.001024 seconds, or 1.024 ms, or 1024 µs.”
However, on the Arduino website https://www.arduino.cc/reference/en/language/functions/time/micros/, it says “overflow (go back to zero), after approximately 70 minutes”. So I am a bit confused, pls help.
Make the distinction:
1. The internal timer #0 counter (TCNT0) is 8-bit, it “rolls over” or “overflows” after every 256 ticks. This means the counter overflows every 0.001024 seconds (or 1.024 ms, or 1024 µs).
2. The value micros() returns, which is an unsigned long, is the number of microseconds since the Arduino board began running the current program. It will overflow (go back to zero), after approximately 70 minutes.