Arduino micros function
programming - Explanation for micros function - Arduino Stack Exchange
timers - Using millis() and micros() inside an interrupt routine - Arduino Stack Exchange
Help using the micros() function
Videos
If the timer 0 has overflowed (TIFR0 & _BV(TOV0)) and there is at least a count of 1 in the timer count register (t & 255) then increment the local copy of the overflow count.
That overflow count is used later on to add an upper 8 bits to what is basically an 8 bit counter to turn it into a 16 bit one ((m << 8) + t).
Since at the start of this function interrupts have been disabled by the cli() function it is possible that the timer TCNT0 has overflowed yet this event has not been processed and included in the value of timer0_overflow_count that has been read into m. Therefore the line that you are asking about is checking for this condition and incrementing the local copy m if necessary before using it to calculate the number of microseconds since startup.
The other answers are very good, but I want to elaborate on how micros() works. It always reads the current hardware timer (possibly TCNT0) which is constantly being updated by the hardware (in fact, every 4 µs because of the prescaler of 64). It then adds in the Timer 0 overflow count, which is updated by a timer overflow interrupt (multiplied by 256).
Thus, even inside an ISR, you can rely on micros() updating. However if you wait too long then you miss the overflow update, and then the result returned will go down (i.e you will get 253, 254, 255, 0, 1, 2, 3 etc.)
This is micros() - slightly simplified to remove defines for other processors:
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
cli();
m = timer0_overflow_count;
t = TCNT0;
if ((TIFR0 & _BV(TOV0)) && (t < 255))
m++;
SREG = oldSREG;
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}
The code above allows for the overflow (it checks the TOV0 bit) so it can cope with the overflow while interrupts are off but only once - there is no provision for handling two overflows.
TLDR;
- Don't do delays inside an ISR
- If you must do them, you can time then with
micros()but notmillis(). AlsodelayMicroseconds()is a possibility. - Don't delay more than 500 µs or so, or you'll miss a timer overflow.
- Even short delays may cause you to you miss incoming serial data (at 115200 baud you will get a new character every 87 µs).
It is not wrong to use millis() or micros() within an interrupt routine.
It is wrong to use them incorrectly.
The main thing here is that while you are in an interrupt routine "the clock isn't ticking". millis() and micros() won't change (well, micros() will initially, but once it goes past that magic millisecond point where a millisecond tick is required it all falls apart.)
So you can certainly call on millis() or micros() to find out the current time within your ISR, but don't expect that time to change.
It is that lack of change in the time that is being warned about in the quote you provide. delay() relies on millis() changing to know how much time has passed. Since it doesn't change delay() can never finish.
So essentially millis() and micros() will tell you the time when your ISR was called no matter when in your ISR you use them.
I am using Atmel Studio to program my ATmega 328p microcontroller. I am trying to use the micros() function to get time stamps for certain events occurring in the program. I think I have to include a specific header file to use the function, but I don't know which one. Any help is appreciated
Yes, depending your Arduino's basic clock rate. For example here are the counter-timer input frequencies and periods after pre-scaling, for an ATMega2560's counter-timer 2, and a basic clock rate of 16MHz. The timer has built in "prescaler" value options which determine frequency/period, shown in this table:
TCCR2B bits 2-0 Prescaler Freq [KHz], Period [usec] after prescale
0x0 (TC stopped) -- --
0x1 1 16000. 0.0625
0x2 8 2000. 0.500
0x3 32 500. 2.000
0x4 64 250. 4.000
0x5 128 125. 8.000
0x6 256 62.5 16.000
0x7 1024 15.625 64.000
For better timing resolution, you use a value called TCNT2. There is build in counter that goes from 0 to 255 because the timer is 8 bit. When the counter reaches the value assigned by TCNT2 it triggers an interrupt. This interrupt is called TIMER2_OVF_vect.
given this information, the resulting interrupt rate would be: 16MHz / (prescaler * (255 - TCNT2))
You could get the timer to run at the full 16MHz (62.5nSec) though that's way faster than you need; 2MHz with an initial count of (255-2) would give you 1MHz interrupt rate. Divide that by 2 in your ISR:
extern uint32_t MicroSecClock = 0;
ISR(TIMER2_OVF_vect) {// this is a built in function that gets called when the timer gets to the overflow counter number
static uint_8 count; // interrupt counter
if( (++count & 0x01) == 0 ) // bump the interrupt counter
++MicroSecClock; // & count uSec every other time.
digitalWrite(53,toggle);// pin 53 is arbitrary
TCNT2 = 253; // this tells the timer when to trigger the interrupt. when the counter gets to 253 out of 255(because the timer is 8 bit) the timmer will trigger an interrupt
TIFR2 = 0x00; // clear timer overflow flag
};
The data sheet for your MCU is the basic resource; this article will give you (and gave me!) a good head-start.
If dropping down to AVR Level is acceptable (as you mentioned in your question), you can set up a timer and even get the ticks of the AVR's clock.
You need to refer to your AVR's datasheet because it differs within the different ATMegas and ATTinys. But the procedure is always the same. What you need to do is:
- decide which prescaler to use (for example set it to 1, i.e. no prescaling if you want the actual CPU clock speed), refer to the TCCR documentation
- set up a timer overflow interrupt, an interrupt handler and activate interrupts globally
- activate the timer in the Timer/Counter Control Register
TCCR
That way can get the exact ticks from the timer register, however you need to count the overflows manually. This is as precise as possible by technology.
Here is some example code for the outdated AT90S2313, but it gives you good hints what to do basically:
/* uC: AT90S2313 */
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void)
{
// set up timer 0
TCCR0 = (1<<CS01); // Prescaler 8
// enable overflow interrupt
TIMSK |= (1<<TOIE0);
// activate interrupts
sei();
while(1)
{
/* Do whatever you like */
}
}
ISR (TIMER0_OVF_vect)
{
/*
This gets called everytime there in an overflow in the timer register
*/
}