Time returned by micros() goes backwards sometimes

Hello. I have been prototyping a product using an Arduino Diecimila board and have run into a problem. I am hoping that you great guys on this forum could help me track it down, please. :slight_smile:

Sometimes the time given my micros() is less than a time given by micros() previously. This happens quite often under the right circumstances and is not caused by the micros number overflowing as that should only happen about once every 70 minutes. Here is some code that exhibits the problem. I invite you to load it into your own arduino and see what happens. :slight_smile:

Note that it uses interrupts and outputs its own signal on one of the pins to trigger the interrupt. I started with a program that listened to an interrupt caused by outside circuitry but switched to this so you all could see the problem without replicating the circuitry.


//Created by Andy D on 10-15-09
//
const int basebandPin=2; //digital pin. Must be either 2 or 3, because these are the only two interrupt pins.

unsigned long usSinceStart = 0; // overflows @ 70 minutes since reboot.
unsigned long prevUsSinceStart = 0;

unsigned long countTimeGoesForward = 0;
unsigned long countTimeGoesBackward = 0;

void setup ()
{
//open serial port
Serial.begin(9600);

//Set the baseband pin to input mode:
pinMode(basebandPin, INPUT);
pinMode(basebandPin, OUTPUT);

//Set up our interrupt:
//The "interrupt number" is 0 if we use digital pin 2 and 1 if we use digital pin 3.
attachInterrupt(0,basebandChangeInterrupt,CHANGE);

}

//Execuded on 0-1 or 1-0 transistions of basebandPin:
void basebandChangeInterrupt()
{
prevUsSinceStart = usSinceStart;

//Get our time Now.
usSinceStart=micros(); //This will go overflow after 70 minutes, and we should allow for that somehow.

//if time seems to go backgwards:
if ( usSinceStart < prevUsSinceStart)
{
countTimeGoesBackward=countTimeGoesBackward+1;
Serial.print("micros() is less than before! Time shouldn't go backwards. ");
Serial.print(prevUsSinceStart);
Serial.print("-> ");
Serial.print(usSinceStart);
Serial.print(". Diff is: -");
Serial.print(prevUsSinceStart-usSinceStart);
Serial.print(". Happens ");
Serial.print(countTimeGoesBackward);
Serial.print(" of ");
Serial.print(countTimeGoesForward+countTimeGoesBackward);
Serial.print(" times");
Serial.println(".");

Serial.print("micros() in binary is now: ");
Serial.println(usSinceStart,BIN);
}
else
{
countTimeGoesForward=countTimeGoesForward+1;
}
}

void loop ()
{

//Use this loop to cause an interrupt:
digitalWrite(basebandPin,HIGH);
digitalWrite(basebandPin,LOW);
}


If you run the code you'll get a stream of text like this:
"
micros() is less than before! Time shouldn't go backwards. 1074160-> 1073156. Diff is: -1004. Happens 42 of 63751 times.
micros() in binary is now: 100000110000000000100

micros() is less than before! Time shouldn't go backwards. 1082352-> 1081348. Diff is: -1004. Happens 43 of 64205 times.
micros() in binary is now: 100001000000000000100

micros() is less than before! Time shouldn't go backwards. 1129460-> 1128452. Diff is: -1008. Happens 44 of 67030 times.
micros() in binary is now: 100010011100000000100

micros() is less than before! Time shouldn't go backwards. 17015796-> 17014788. Diff is: -1008. Happens 885 of 993586 times.
micros() in binary is now: 1000000111010000000000100

micros() is less than before! Time shouldn't go backwards. 17044468-> 17043460. Diff is: -1008. Happens 886 of 995280 times.
micros() in binary is now: 1000001000001000000000100

micros() is less than before! Time shouldn't go backwards. 17057780-> 17056772. Diff is: -1008. Happens 887 of 996038 times.
micros() in binary is now: 1000001000100010000000100

micros() is less than before! Time shouldn't go backwards. 17059828-> 17058820. Diff is: -1008. Happens 888 of 996110 times.
micros() in binary is now: 1000001000100110000000100
"

You'll notice that time as returned by micros() is going backwards sometimes. That's a problem.

In an effort to diagnose the problem I learned that when micros goes backwards it is always about 1024 less than it should be at that point (I can't tell exactly because the time a loop takes varies). That suggests that the 11th least significant bit is a 0 when it should be a 1 or most of the 10 least significant bits are 0's when they should be 1's. Also the issue seems to have something to do with the code being in the interrupt because if I copy it to the loop() function micros() never goes backwards. Unfortunately I need to use the code in an interrupt for my purposes.

Looking closer I told my program to print the micros() time in binary. That showed something very interesting (see the above output from the program). Whenever it happens the 10 least significant bits are always "0000000100". Furthermore it seems to happen every 889/1003428 time the interrupt is called which is .9 times out of 1024.

If you're reading this, can you please try this code on your arduino and let me know if it has the same problem? Maybe there is an arduino that uses a microprocessor that does't exhibit this problem, that I can switch to. Also, any insight you can provide would be of great help! Thank you so much!

Sincerely,
Andy D

Well I'm not good enough with software yet to troubleshoot your code. However I can see that you are kind of misusing the interrupt concept. A ISR routine should be a short as possible, just reading something and setting a global flag and then let the main loop read the change in the flag to do stuff. It's really not recommended to even call other functions while inside a ISR. Recall that once you enter a ISR all other interrupts are disabled until your ISR completes and returns to the main loop. I suspect your problem is due to the length of your ISR or the functions you are calling inside it, as all the processors timers will be frozen while inside the ISR.

That's probably not specific enough help to fix your problem, but that's what I have learned so far playing with interrupt routines for and encoder application.

Make sense?

PS: came across this in the Arduino reference on attachInterrupt(interrupt, function, mode):

"You should declare as volatile any variables that you modify within the attached function. " Why don't you try that for the four long variables you have defined and use.

Lefty

Hello Retrolefty,

Thanks for your help! I appreciate it!

About the interrupt length: My original code didn't have the if statement at all. Therefore, the only thing happening in the interrupt was storing the previous time (prevUsSinceStart=usSinceStart) and getting the current time (usSinceStart=micros()). That seems like it aught to be short enough. :slight_smile: That code exhibited the same problem. I just added the if statement to make the symptoms more clear. I am not sure why it would be a problem, though, to run a large program from an interrupt, as long as the program is not dependent on other interrupts getting a chance to run, or on the loop{} code running ever. I believe the worst case is that an event might be missed because the previous event was still processing. If one knows the events are spaced far enough apart, it seems like it shouldn't be a problem.

Previous iterations of the code had the volatile flag too, and those had the problem. As far as I understand, the volatile flag is only needed if a variable can be changed by multiple "sections" of the code, for example, the "loop{}", and the interrupt. Since these variables aren't used in the loop{} function, I removed the volatile keyword. I'm afraid it didn't seem to make a difference either-way for this issue.

I'm glad you brought these things up. It's good to examine all the possibilities.

Sincerely,
Andy D

A bug related to reading micros() from within an ISR was fixed in version 0017. Are you perhaps using an older version of the IDE?

If so, can you post a code example that demonstrates the problem but doesn't include much code in the ISR?

Sure:

It's the exact same thing, but with the "did time go backwards" check in the loop{} function, activated by a flag in the interrupt. I also made the variables volatile.

I am using arduino-0017.

Could you please try the code on your arduinos and check to see if it is diecimila/microcontroller dependent?

~Andy


//Created by Andy Doucette on 11-31-09
//
const int basebandPin=2; //digital pin. Must be either 2 or 3, because these are the only two interrupt pins.

volatile unsigned long usSinceStart = 0; // overflows @ 70 minutes since reboot.
volatile unsigned long prevUsSinceStart = 0;

volatile unsigned long countTimeGoesForward = 0;
volatile unsigned long countTimeGoesBackward = 0;

volatile boolean checkTimesNow=false;

void setup ()
{
//open serial port
Serial.begin(9600);

//Set the baseband pin to input mode:
pinMode(basebandPin, INPUT);
pinMode(basebandPin, OUTPUT);

//Set up our interrupt:
//The "interrupt number" is 0 if we use digital pin 2 and 1 if we use digital pin 3.
attachInterrupt(0,basebandChangeInterrupt,CHANGE);

}

//Execuded on 0-1 or 1-0 transistions of basebandPin:
void basebandChangeInterrupt()
{
prevUsSinceStart = usSinceStart;

//Get our time Now.
usSinceStart=micros(); //This will go overflow after 70 minutes, and we should allow for that somehow.

//Do some processing in the loop{} regarding the times we just got.
checkTimesNow=true;
}

void loop ()
{

//Use this loop to cause an interrupt:
digitalWrite(basebandPin,HIGH);
digitalWrite(basebandPin,LOW);

//If we just ran an interrupt, do some processing on it:
if ( checkTimesNow)
{
//if time seems to go backgwards:
if ( usSinceStart < prevUsSinceStart)
{
countTimeGoesBackward=countTimeGoesBackward+1;
Serial.print("micros() is less than before! Time shouldn't go backwards. ");
Serial.print(prevUsSinceStart);
Serial.print("-> ");
Serial.print(usSinceStart);
Serial.print(". Diff is: -");
Serial.print(prevUsSinceStart-usSinceStart);
Serial.print(". Happens ");
Serial.print(countTimeGoesBackward);
Serial.print(" of ");
Serial.print(countTimeGoesForward+countTimeGoesBackward);
Serial.print(" times");
Serial.println(".");

Serial.print("micros() in binary is now: ");
Serial.println(usSinceStart,BIN);
}
else
{
countTimeGoesForward=countTimeGoesForward+1;
}
}
//reset our flag:
checkTimesNow=false;
}

The fix is apparently tagged for version 018. If you want to apply the patch yourself, you can find the proposed change here:

http://code.google.com/p/arduino/issues/detail?id=55

That patch fixed the problem. Bugs are sneaky little things. Thanks so much!

~Andy