Offline
Newbie
Karma: 0
Posts: 24
|
 |
« on: August 17, 2012, 01:57:46 am » |
Not the euromillions roll over  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  Thanks Nigel
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #1 on: August 17, 2012, 02:01:21 am » |
You just have to subtract and then it all works automagically. if (micros () - then > some_interval) { // do something }
|
|
|
|
|
Logged
|
|
|
|
|
Austin, TX
Offline
Faraday Member
Karma: 41
Posts: 5171
CMiYC
|
 |
« Reply #2 on: August 17, 2012, 02:37:24 am » |
|
|
|
|
« Last Edit: January 15, 2013, 01:39:26 am by James C4S »
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #3 on: August 17, 2012, 02:44:04 am » |
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
Newbie
Karma: 0
Posts: 24
|
 |
« Reply #4 on: August 17, 2012, 05:00:05 am » |
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...  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 
|
|
|
|
|
Logged
|
|
|
|
|
Austin, TX
Offline
Faraday Member
Karma: 41
Posts: 5171
CMiYC
|
 |
« Reply #5 on: August 17, 2012, 07:36:01 am » |
Watch what happens when you run this code with and without the cast to signed char: 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
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #6 on: August 17, 2012, 04:06:53 pm » |
Yes but the recommended test is: now - start_time >= interval You have: now - future_time >= 0 Adding to now to get a future time will roll over the wrong way.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Tesla Member
Karma: 71
Posts: 6624
Arduino rocks
|
 |
« Reply #7 on: August 17, 2012, 04:32:22 pm » |
Yes but the recommended test is: now - start_time >= interval You have: 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): long signed_diff = micros () - future_time ; if (signed_diff >= 0L) ...
|
|
|
|
|
Logged
|
|
|
|
|
Pittsburgh, PA, USA
Offline
Faraday Member
Karma: 30
Posts: 2924
I only know some basic electricity....
|
 |
« Reply #8 on: August 17, 2012, 04:39:01 pm » |
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: 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
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
Austin, TX
Offline
Faraday Member
Karma: 41
Posts: 5171
CMiYC
|
 |
« Reply #9 on: August 18, 2012, 06:04:22 pm » |
Yes but the recommended test is: now - start_time >= interval Okay, but even this check only works when is cast to a signed value: 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
|
|
|
|
|
Pittsburgh, PA, USA
Offline
Faraday Member
Karma: 30
Posts: 2924
I only know some basic electricity....
|
 |
« Reply #10 on: August 18, 2012, 08:23:09 pm » |
Try running that sketch I just posted.
|
|
|
|
|
Logged
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
0
Offline
Tesla Member
Karma: 71
Posts: 6624
Arduino rocks
|
 |
« Reply #11 on: August 19, 2012, 06:27:03 am » |
Yes but the recommended test is: 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. 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
|
|
|
|
|
Pittsburgh, PA, USA
Offline
Faraday Member
Karma: 30
Posts: 2924
I only know some basic electricity....
|
 |
« Reply #12 on: August 19, 2012, 11:27:30 am » |
Signed math is like a ruler. Unsigned math is like a clock. It is 3:00, how long ago was 11:00?
|
|
|
|
|
Logged
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
Austin, TX
Offline
Faraday Member
Karma: 41
Posts: 5171
CMiYC
|
 |
« Reply #13 on: August 19, 2012, 02:33:43 pm » |
I apologize for basically hijacking this thread, but I am finding that this code DOES NOT work after rollover occurs: 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. 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: if ((unsigned char)(counter - start_time) >= interval) {
and the check works after rollover. Why is the explicit cast to unsigned char necessary?
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Sr. Member
Karma: 3
Posts: 318
Arduino rocks
|
 |
« Reply #14 on: August 19, 2012, 04:24:50 pm » |
... 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: 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: 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: Serial.println(counter - start_time);
Results: 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
|
|
|
|
|
|