Examination of the Arduino micros() Function

us clock

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!

BookCover
[click on the image]

About Jim Eli

µC experimenter
This entry was posted in Uncategorized and tagged , , , , , , . Bookmark the permalink.

9 Responses to Examination of the Arduino micros() Function

  1. 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)

  2. Teo says:

    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…

    • Control says:

      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.

  3. Homayon says:

    How can I build micros for timer1?

    • Jim Eli says:

      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.

  4. Memo says:

    how to get timer0_overflow_count because I tried this function with another name and it didn’t work

    • Jim Eli says:

      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;

  5. bz says:

    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.

    • Jim Eli says:

      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.

Leave a comment