Pages: [1] 2   Go Down
Author Topic: Best Practice with millis()?  (Read 802 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 1
Posts: 48
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have seen code examples using millis 2 different ways and was wondering if one of these is better then the other.

Method 1:
Code:
unsigned long previousMillis = 0;     
unsigned long interval = 1000;     

void setup() {
  // setup code here     
}

void loop()
{
  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;   
    // do something
   
  }
}

Method 2:
Code:
unsigned long previousMillis = 0;     
unsigned long interval = 1000;     

void setup() {
  // setup code here     
}

void loop()
{
  unsigned long currentMillis = millis();
 
  if(currentMillis > nextMillis) {
    nextMillis = currentMillis + interval;   
    // do something
   
  }
}

Method 2 only does the addition/subtraction one time so it would save some cycles every time through the loop, however it looks like it will have issues when the unsigned long rolls over (approximately 50 days).

does Method 1 get around the bound condition when the millis rolls over or is there any other reason to do the code that way instead of method 2?
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 547
Posts: 45982
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
does Method 1 get around the bound condition when the millis rolls over
Yes, it does, and that is why it is the preferred solution. In terms of time required, the subtraction operation is very fast. Don't worry about "saving clock cycles" here.
Logged

Offline Offline
Newbie
*
Karma: 1
Posts: 48
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

thank you.
Logged

italy
Offline Offline
Full Member
***
Karma: 2
Posts: 215
Muuuuu
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

but look at this code:
Code:
void setup()
{
  Serial.begin(9600);
}


void loop()
{
  Serial.println( millis()-4294967295 );
}



where 4294967295 is the maximum unsigned long value. The result because of overflow (i suppose) is only the millis() value. So currentMillis - previousMillis can make problems when previousMillis is a big value
« Last Edit: December 23, 2011, 10:00:50 am by cantore » Logged

Global Moderator
Boston area, metrowest
Offline Offline
Brattain Member
*****
Karma: 435
Posts: 23611
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

How is the upper bits dropping off a problem?

How much is 0x00000100 - 0xFFFFFFE0? which would be a rollover condition?
= 0x00000120, the bits above the long word go away, and result in the answer you want.


 

Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 176
Posts: 12285
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


While Method 1 is preferred it is not correct...

Quote
 if(currentMillis - previousMillis >= interval) {

Logged

Offline Offline
Edison Member
*
Karma: 17
Posts: 1041
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
does Method 1 get around the bound condition when the millis rolls over
Yes, it does, and that is why it is the preferred solution. In terms of time required, the subtraction operation is very fast. Don't worry about "saving clock cycles" here.

But so does method 2. Try out this sketch. It does something once per second and uses addition rather than subtraction. I think it's also simpler for beginners to understand, since there's the analogy of "You look at the clock, figure out when it should be done by, then when it's that time, you do it."

Code:
// This variable stores the current time returned by millis()
extern volatile unsigned long timer0_millis;

void setup() {
    Serial.begin(9600);

    cli(); //disable interrupts

    // Max unsigned long: 4,294,967,295
    // Set the current time to 5 seconds before wrap around
    //   so we don't have to wait 50 days to test this
    timer0_millis = 4294962295UL;

    sei(); //enable interrupts again
}


// Print out the current time every second

void loop() {
    static unsigned long curTime;
    static unsigned long timeToFire;

    curTime = millis();

    if (curTime >= timeToFire) {
        Serial.print("Current time: "); Serial.println(millis());
        timeToFire = curTime + 1000;
    }
}
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 176
Posts: 12285
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

But so does method 2. Try out this sketch. It does something once per second and uses addition rather than subtraction.

Your analysis is incomplete.  Method 2 has a fatal flaw.  Method 1 is the correct choice.
Logged

Seattle, WA
Offline Offline
God Member
*****
Karma: 7
Posts: 673
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

But so does method 2. Try out this sketch. It does something once per second and uses addition rather than subtraction.
Your analysis is incomplete.  Method 2 has a fatal flaw.  Method 1 is the correct choice.

And the flaw is this...

Let's say curTime and timeToFire are UINT32_MAX - 900.  Then we run this:

Code:
        timeToFire = curTime + 1000;


What is timeToFire now?  100.  What is curTime?  Still UINT32_MAX - 900.  Is curTime > timeToFire?  You bet.  So the following will fire constantly for 900ms.

Code:
    if (curTime >= timeToFire) {
        Serial.print("Current time: "); Serial.println(millis());
        timeToFire = curTime + 1000;
    }

And now you have a bug.  A subtle bug, a rare bug, a bug that only lasts for 900ms.  But still a bug, and why do it when there is a perfectly good method 1 at your disposal?
Logged


Offline Offline
Edison Member
*
Karma: 17
Posts: 1041
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

...

Oh, I see. Thanks.
Logged

italy
Offline Offline
Full Member
***
Karma: 2
Posts: 215
Muuuuu
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

How is the upper bits dropping off a problem?

How much is 0x00000100 - 0xFFFFFFE0? which would be a rollover condition?
= 0x00000120, the bits above the long word go away, and result in the answer you want.


In that case no problem because the bit above are lost but how if I substract A and B with B bigger than A like this:
A-B= 0x00000100 - 0x0000FFE0  (dec 256-65504 )
I obtain FFFF0120 that in decimal is -65248 only if I use SIGNED number
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 547
Posts: 45982
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
but how if I substract A and B with B bigger than A like this
Does your Arduino's clock run backwards? The specific discussion going on deals with subtracting an earlier time from a later time to get the interval between the two times. The B value (the earlier time) can not possibly be larger than the A value (the later time).
Logged

italy
Offline Offline
Full Member
***
Karma: 2
Posts: 215
Muuuuu
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

No, unfortunatly it can, and this is the problem I am thinking about. This happen after millis has reached the maximum unsigned long value. It restarts from 0 and so we can have situations like thisTime=210510 oldTime=4294967200. The problem is about number rappresentation. If you have 2-4 the result is -2 but if the maximum value you can store is (for example) 5 the time elapsed would be 3. In this case the maximum value is 4294967295(the maximum unsigned long value)
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 238
Posts: 24298
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The only time (forgive the pun) you'll have a problem is if the interval is more than half the rollover time.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

italy
Offline Offline
Full Member
***
Karma: 2
Posts: 215
Muuuuu
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The only time (forgive the pun) you'll have a problem is if the interval is more than half the rollover time.

Exact! For example having as max number 255 (8bit)

d(12-240) --> b(0000 1100-1111 0000) = b0001 1100 d28 OK

d(12-112) --> b(0000 1100-0111 0000) = b1001 1100 d-100 WRONG (result would be 255-112+12=155 )
Logged

Pages: [1] 2   Go Up
Jump to: