Pages: [1] 2   Go Down
Author Topic: microsSince() - dealing with the roll over  (Read 1658 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 29
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Not the euromillions roll over smiley

My project needs to control 2 stepper motors and an optoisolator simultaneously, so I need to use the micros() timer to work out how long it's been since the last time I got to a place in my loop.

According to the documentation, approximately every 70 minutes, the micros timer rolls over, so this would cause a problem if it happened in a middle of the run, so I need a microsDifference(now, then) function. When the 'now' variable is less than 'then' we have obviously poped the overflow. So my question:

whats the magic max number? is it a 32 bit unsigned? 0xffffffff??

I think I want this:

if(now < then) {
  return 0xffffffff - then + now;
} else {
  return now - then;
}


I would try it out, but I'd rather understand it before I had to wait several multiples of 70 minutes smiley

Thanks
Nigel
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 480
Posts: 18736
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You just have to subtract and then it all works automagically.

Code:
if (micros () - then > some_interval)
  {
  // do something
  }
Logged


Austin, TX
Offline Offline
Faraday Member
**
Karma: 71
Posts: 6146
Baldengineer
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Subtract AND cast to unsigned.

http://www.cmiyc.com/blog/2012/07/16/arduino-how-do-you-reset-millis/
« Last Edit: January 15, 2013, 01:39:26 am by James C4S » Logged

Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

Global Moderator
Online Online
Brattain Member
*****
Karma: 480
Posts: 18736
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I don't know about the cast. In the examples on that page the subtraction is in brackets, so you already have an unsigned number. So why bother casting it?
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 29
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


I dont think this will work.

if micros() > waitUntil then trigger and waitUntil += interval

If my waitUntil rolls over because of unsigned magicalness ahead of the midros counter, then micros will always be > waitUntil, until the micros() counter rolls over itself.

So, if micros is returning 0xffffffff - 90, and I trigger each 100, and my new waitUntil is 10, then each loop for the next 90us will trigger - this sounds bad.

I think Nicks solution is what I'm after... i think, my brian doesn't want to understand it fully, but I think... smiley

Thanks guys, I'll give it a go and watch for any jitters at about 70 minutes... I'll clear the mantle piece of the good stuff, just in case smiley
Logged

Austin, TX
Offline Offline
Faraday Member
**
Karma: 71
Posts: 6146
Baldengineer
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Watch what happens when you run this code with and without the cast to signed char:
Code:
unsigned char counter = 0;
unsigned char waitUntil = 0;
#define interval 47;
void setup() {
  Serial.begin(9600);
  while (!Serial); // Leonardo Only
  waitUntil += interval;
}
 
void loop() {
  for (int x=0; x<1000; x++) { // run through uchar a few times
    counter++;  // simulate millis()
    Serial.println(counter);
    if ((char)(counter - waitUntil) >=0) {  // check for rollover
      Serial.println("Trigger Event!");
      waitUntil += interval;
    }
  }
  while(1);  // stop the serial monitor's output
}

Logged

Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

Global Moderator
Online Online
Brattain Member
*****
Karma: 480
Posts: 18736
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes but the recommended test is:

Code:
now - start_time >= interval

You have:

Code:
now - future_time >= 0

Adding to now to get a future time will roll over the wrong way.
Logged


0
Online Online
Shannon Member
****
Karma: 206
Posts: 12090
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes but the recommended test is:

Code:
now - start_time >= interval

You have:

Code:
now - future_time >= 0

Adding to now to get a future time will roll over the wrong way.

The first one will work with unsigned longs happily up to 2^32-1 us ahead (about 72mins), the second _must_ be cast to signed to do the compare and only works to 2^31 us ahead (about 36mins):
Code:
  long  signed_diff = micros () - future_time ;
  if (signed_diff >= 0L)
    ...
Logged

[ I won't respond to messages, use the forum please ]

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 98
Posts: 4803
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The best way is to use 2 stored values, start-time and interval, with "now", millis or micros.

However you can set a future-time and subtract that from "now". When "now" passes future-time the subtraction will result in a large value. Thing is that your longest interval becomes less than half as much.

Sketch for playing with unsigned's:

Code:
unsigned long a, b, c;

void setup() {
  Serial.begin( 9600 );
  a = 0xffffff00UL;
  b = 0x10UL;
  Serial.println( "\n unsigned math\n" );
  Serial.print( "a = ");
  Serial.print( a, DEC );
  Serial.print( " = 0x");
  Serial.print( a, HEX );
  Serial.print( " = 0b");
  Serial.println( a, BIN );
  Serial.print( "b = ");
  Serial.print( b, DEC );
  Serial.print( " = 0x");
  Serial.print( b, HEX );
  Serial.print( " = 0b");
  Serial.println( b, BIN );
  if ( b >= a ) Serial.println( "b >= a" );
  else          Serial.println( "a > b" );
  c = a - b;
  Serial.print( "a - b = ");
  Serial.print( c, DEC );
  Serial.print( " = 0x");
  Serial.print( c, HEX );
  Serial.print( " = 0b");
  Serial.println( c, BIN );
  c = b - a;
  Serial.print( "b - a = ");
  Serial.print( c, DEC );
  Serial.print( " = 0x");
  Serial.print( c, HEX );
  Serial.print( " = 0b");
  Serial.println( c, BIN );
  c = b - (b + 1);
  Serial.print( "b - (b + 1) = ");
  Serial.print( c, DEC );
  Serial.print( " = 0x");
  Serial.print( c, HEX );
  Serial.print( " = 0b");
  Serial.println( c, BIN );
}

void loop() {};
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Austin, TX
Offline Offline
Faraday Member
**
Karma: 71
Posts: 6146
Baldengineer
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes but the recommended test is:
Code:
now - start_time >= interval
Okay, but even this check only works when is cast to a signed value:

Code:
unsigned char counter = 0;
unsigned char start_time = 0;
#define interval 47

void setup() {
  Serial.begin(9600);
  for (int x=0; x<1000; x++) { // run through uchar a few times
    counter++;  // simulate millis() or micros()
    Serial.print(counter); Serial.print(","); Serial.println(start_time);
    if ((char)(counter - start_time) >= interval) {  // check for rollover
      Serial.println("Trigger Event!");
      start_time=counter;
    }
  }
}
 
void loop() {
}

If you remove the cast to signed char, the if-statement won't be true once counter rolls over.
Logged

Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 98
Posts: 4803
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Try running that sketch I just posted.
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

0
Online Online
Shannon Member
****
Karma: 206
Posts: 12090
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes but the recommended test is:
Code:
now - start_time >= interval
Okay, but even this check only works when is cast to a signed value:
No, not true, it works best as unsigned as I've already said.
Quote

If you remove the cast to signed char, the if-statement won't be true once counter rolls over.
If you remove the cast you are using 16 bit arithmetic and comparisons, not 8 bit - replace the (char) cast by (unsigned char) and you'll be able to allow the interval to range up to 255.  Your code only works with interval ranging up to 127.  Try it and see.

Read my previous post!
Logged

[ I won't respond to messages, use the forum please ]

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 98
Posts: 4803
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Signed math is like a ruler. Unsigned math is like a clock. It is 3:00, how long ago was 11:00?
 
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Austin, TX
Offline Offline
Faraday Member
**
Karma: 71
Posts: 6146
Baldengineer
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I apologize for basically hijacking this thread, but I am finding that this code DOES NOT work after rollover occurs:
Code:
    if ((counter - start_time) >= interval)

I resisted what you guys were saying at first because the example code I have provided demonstrates this fact.  Once the variable "counter" rolls over, the check always fails.  Now that I have tested some more, I understand what Mark and others are saying about the maximum value that can be used if the operation is cast to signed.  I agree, in the case of a char it limits you to 127.

What I still don't understand is why in this code, the "recommended check" fails after roll over.  UNLESS you specifically cast it to unsigned.
Code:
unsigned char counter = 0;
unsigned char start_time = 0;
#define interval 147

void setup() {
  Serial.begin(9600);
  for (int x=0; x<1000; x++) { // run through uchar a few times
    counter++;  // simulate millis() or micros()
    Serial.print(counter); Serial.print(","); Serial.println(start_time);
    if ((counter - start_time) >= interval) {  // check for rollover  // WITHOUT A CAST TO (unsigned char) this will NOT WORK
      Serial.println("Trigger Event!");
      start_time=counter;
    }
  }
}
 
void loop() {
}

Change line 10 to:
Code:
    if ((unsigned char)(counter - start_time) >= interval) {
and the check works after rollover.  Why is the explicit cast to unsigned char necessary?
Logged

Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

0
Offline Offline
Sr. Member
****
Karma: 4
Posts: 323
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

...
and the check works after rollover.  Why is the explicit cast to unsigned char necessary?

Because you've changed the domain of the example.  Dealing with millis(), etc. we are dealing with unsigned longs.  You've changed the parameters, and entered into the world of "what the compiler chooses to do to optimize this case," which breaks the rules - because the compiler has not done what you expected.

The following works perfectly fine, as has been known for some time:

Code:
unsigned long counter = 0;
unsigned long start_time = 0;
#define interval 10000000

void setup() {
  Serial.begin(9600);
  for (int x=0; x<1000; x++) { // run through uchar a few times
    counter += 47721860;  // simulate millis() or micros()
    Serial.print(counter); Serial.print(","); Serial.println(start_time);
    if (counter - start_time >= interval) {  // rollover works just fine
      Serial.println("Trigger Event!");
      start_time=counter;
    }
  }
}
  
void loop() {
}

Note the results:

Code:
4104079960,4056358100
Trigger Event!
4151801820,4104079960
Trigger Event!
4199523680,4151801820
Trigger Event!
4247245540,4199523680
Trigger Event!
104,4247245540
Trigger Event!
47721964,104
Trigger Event!
95443824,47721964
Trigger Event!
143165684,95443824

You're doing a different case than "how to check when millis' unsigned long rolls over" and have changed the case to "how to check when a small data type rolls over" - they are not the same, and the compiler is treating the smaller case differently, hence why it works when you explicitly cast the results.

!c

EDIT: Adding this will be fairly illuminating as to what's happening in your temporary variable handling:

Code:
    Serial.println(counter - start_time);

Results:

Code:
223,248
-25
224,248
-24
225,248
-23
226,248
-22
227,248
-21
228,248
-20
« Last Edit: August 19, 2012, 04:32:37 pm by drone » Logged

Pages: [1] 2   Go Up
Jump to: