Go Down

Topic: Best Practice with millis()? (Read 1 time) previous topic - next topic

JasonK

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: [Select]
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: [Select]
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?

PaulS

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.


cantore

#3
Dec 23, 2011, 03:57 pm Last Edit: Dec 23, 2011, 04:00 pm by cantore Reason: 1
but look at this code:
Code: [Select]

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

CrossRoads

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.




Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Coding Badly


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

Quote
[font=Courier New]  if(currentMillis - previousMillis >= interval) {[/font]



WizenedEE


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: [Select]
// 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;
    }
}

Coding Badly

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.


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: [Select]

        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: [Select]

    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?


cantore


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

PaulS

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).

cantore

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)

AWOL

The only time (forgive the pun) you'll have a problem is if the interval is more than half the rollover time.
"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.

cantore


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 )

Go Up