Arduino micros function

Hi all

I have a very specific question about the Arduino micros() function.
Only purpose is to understand and learn, so I do NOT have a practical issue.
But I hope someone will be able to enlighten me.

The function (which I found on a forum):

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());
}

Introduction to my question:
nearly at the end of the function, flag TOV0 is checked to see whether there is a pending overflow interrupt.
IF yes, then this indicates that the software counter ‘timer0_overflow_count’ has not been updated yet by the Interrupt handler (interrupts are temporarily blocked by the micros() function) and we need to take care of that by adding one to variable ‘m’ (pending the update of ‘timer0_overflow_count’ which will happen immediately upon re-enabling interrupts).
So, ‘m’ will correctly contain the number of Timer 0 overflows (each overflow represents 1024 micro seconds, to be added to the number of micros read directly from Timer zero).

Up to this point, all is fine.

My question:

Why does the if clause if ((TIFR0 & _BV(TOV0)) && (t & 255)) check for a non-zero value of the timer reading ? (t & 255)
As I see it (although I guess I’m missing something) this is wrong, even if ‘t’ would be zero, the fact that TOV0 is set would still mean the last update of ‘timer0_overflow_count’ is pending.

Can someone explain this to me ? I would sleep a lot better :slight_smile:

As said, it’s just about understanding it. I have no practical issue.

Many thanks

herwig9820:
The function (which I found on a forum):

Why look on a forum when you have the file (wiring.c) installed on your PC?

That piece of code also looks to be incorrect and I don’t see a version like it in the history of wiring.c, so I don’t know where it came from. Spot the difference in the version from my installation:

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());
}

herwig9820:
Why does the if clause

if ((TIFR & _BV(TOV0)) && (t < 255))

check for a non-zero less than 255 value of the timer reading ? (t < 255)

The timer is configured in FastPWM mode (check the corresponding datasheet) and so counts from BOTTOM (0) to MAX (255); the overflow flag is set when the counter reaches MAX and the overflow interrupt will also fire.

If t==255, it is only necessary to add the 255 to the micros() return total.
If t<255 and the overflow flag is set, then the value of t, let’s say 3, isn’t by itself enough to add to the total as an overflow must have already occurred, so m must be incremented as well.

Hello arduarn

To answer your question first, well, I just stumbled on this Micros function code on a forum (Examination of the Arduino micros() Function | µC eXperiment)
and I never bothered to double check. My confidence in the world is too big, I’m afraid.

Anyway, I’m grateful for your answer.

Indeed, the bitwise AND operator seemed very strange to me, and looking at the original code (wiring.c) solves the mystery almost completely.

But I believe there is still a missing piece in the explanation.

Looking at these two lines, executed in sequence (I left out the compiler directives):

 t = TCNT0L;
 if ((TIFR0 & _BV(TOV0)) && (t < 255))

Imagine this sequence of events:

  1. Timer reading is 254 (‘t’)
  2. In between the execution of the two statements, the timer reaches 255 and TOV0 is set
  3. TIFR0 is read, indicating TOV0 is set

=> the counter of timer overflows (‘t’) will be incremented, which is good
=> 254 micros will be added, which is bad.

If everything would have occured a few system clock cycles later, 0 microseconds would have been added (timer reading would have been 255).

Maybe there is some synchronizing logic in the chip that prevents this from occuring (the second statement is executed very fast after the previous one), but I didn’t find anything about that.

I would value your opinion about this.

To give you some background, I started to dig in to this because I use timer 1 in PWM, phase correct mode (I need 10 bits for my application), to generate interrupts and a 1KHz PWM waveform on the designated pin, with OCR1A controlling duty cycle. Setup:

 TCCR1A = _BV(COM1A1) | _BV(WGM11);
 TCCR1B = _BV(WGM13) | _BV(CS11);
 ICR1 = 1000L; 
 TIMSK1 = _BV(TOIE1);

So Timer 1 became my time base and I implemented a micros() function for it, which seems to work fine (and doesn’t have the alleged timer 0 issue I just mentioned).

Looking forward to read your thoughts about it,
Best regards

I get your point and to some extent you're not wrong. I think the code is a bit fragile, but it probably still works based on the timing involved. A comment in the code would have been helpful, but that's life.

Since the clock is divided by 64, there is a tick interval of 4uS.
If you take a look at the 328 datasheet:

You'll see that the counter value hits MAX first and only upon the falling scaled clock edge that triggers a wrap around to BOTTOM does the TOV flag get set. So, that's almost 4uS after the counter value was copied to t to do the if statement. Not pretty, but I think it works.

In fact the Micros() code is perfectly OK, as I now realize - and my example was wrong.

This is the correct example:

  1. Timer reading is 255 ('t')
  2. In between the execution of the two statements, the timer reaches 0 and TOV0 is set
  3. TIFR0 is read, indicating TOV0 is set

=> the counter of timer overflows ('t') will be incremented (+ 1024 micros), which is good
=> 0 additional micros will be added, which is good.

This relies on the fact that the time between the execution of the two read statements (timer and TIFR0) is definitely less than 4 microseconds.

In the example above, if TIFR0 had been read as false, counter 't' would not have been incremented (+ 0 micros) but 255 * 4 microseconds (+ 1020 micros) would have been added to the time.

So, it all perfectly fits. The code is not fragile at all (but as you mentioned, a small comment would have been helpful).

Case closed.
Thanks