Go Down

Topic: Resetting Millis() to zero, reset clock (Read 28809 times) previous topic - next topic

nickgammon


Overflows are registered in timer0_overflow_count, making it possible to calculate further how long the arduino has really been running beyond the unsigned long limit ...


This is not correct. That variable counts the number of times Timer0 overflows (which itself happens every 1.024 mS). It is used in one place only and that is to return the correct value from micros(). It is not a "millis() overflow count" as you seem to think.

Quote

I personally prefer resetting timer0_millis back to 0 manually in a periodically fashion to prevent any weird stuff from happening every 50 days.


This is not a useful thing to do, any more than resetting your kitchen clock each evening to stop it "rolling over" at midnight. For one thing, it isn't necessary. For another, while you are resetting the clock it is no longer counting the time.

If you have any reasonably complex sketch where you need to count multiple intervals then there will be no obvious time in which to reset the timer.

Quote

It is better coding practice to read timer0_overflow_count before and after long-running operations ...


No, that achieves nothing, as I said above.

The others are quite right. Something like this always works:

Code: [Select]
if (millis () - startTime >= interval)
  {
  // do something
  }


(Given that startTime is unsigned long).


Example is given in OP. When dealing with large intervals, uncertainty of roll-over increases, making the possibility of negative return on comparisons (smaller number minus larger number) greater.


How can a negative return be possible on comparing unsigned numbers? That is the crux. It isn't possible, and thus you get the correct answer. What other answer could you get?
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon


Whenever you want to reset millis():
Code: [Select]
timer0_millis = 0;


I just want to also point out, in case you ignore my earlier advice, that this is flawed because you haven't "protected" that line from interrupts.

http://www.gammon.com.au/interrupts

Scroll down to "critical sections". If you must do the above, you have to turn interrupts off:

Code: [Select]

noInterrupts ()
timer0_millis = 0;
interrupts ();


Without doing that, at the moment you attempt to set the timer to zero, an interrupt may occur which means it is possibly only half set to zero.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Grumpy_Mike


Apologies - maybe it was taught as "modulo arithmetic" in English-speaking institutions in the 1970s.
That's my recollection anyway.

Yes you are right, that is what I was taught and what I did teach.

JChristensen


Apologies - maybe it was taught as "modulo arithmetic" in English-speaking institutions in the 1970s.
That's my recollection anyway.


None needed. Probably just another of those differences between English and Usanian ;)

nickgammon

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics


Jantje

Do not PM me a question unless you are prepared to pay for consultancy.
Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -

redtails

#22
Aug 28, 2013, 01:47 pm Last Edit: Aug 28, 2013, 02:07 pm by redtails Reason: 1

The others are quite right. Something like this always works:

Code: [Select]
if (millis () - startTime >= interval)
 {
 // do something
 }


(Given that startTime is unsigned long).


Example is given in OP. When dealing with large intervals, uncertainty of roll-over increases, making the possibility of negative return on comparisons (smaller number minus larger number) greater.


How can a negative return be possible on comparing unsigned numbers? That is the crux. It isn't possible, and thus you get the correct answer. What other answer could you get?


Aw sweet, it all makes sense now. I executed the following code

Code: [Select]
unsigned long var;
unsigned long varminus;

void setup(void)
{
 // initialize serial:
 Serial.begin(9600);
 var = 100;
 varminus = var-200;
 
 Serial.println("The answer to 100 minus 200 on unsigned-long vars is: ");
 Serial.println(varminus);
}

void loop(void)
{
}


And the output is:

Code: [Select]
The answer to 100 minus 200 on unsigned-long vars is:
4294967196


The odometer analogy is the perfect one for a commoner to get around obscure binary/hex-thinking.

In any case, many thanks for forum members that stuck around, I've edited OP to accommodate future reference

Paul__B



Apologies - maybe it was taught as "modulo arithmetic" in English-speaking institutions in the 1970s.
That's my recollection anyway.


None needed. Probably just another of those differences between English and Usanian ;)


While a staunch defender of the "pure" English tongue, on cogitation, I do believe the term "modular arithmetic" is in fact quite correct and indeed, more accurately descriptive than "modulo arithmetic".

The modulus is a parameter of a numeric value determined by a specific operation and the act of performing that operation is described as "modulo" or a modulo function.  So the arithmetic of performing further operations - notably comparisons - on such results, the moduli as a plural, can appropriately be described as "modular" arithmetic where "modular" derives from the "moduli" in the same way we refer to "alveolar" where the (lung) alveoli are concerned, and a few other Latin terms - though I cannot quote the original Latin rules behind that (I never formally studied Latin).

There could be claimed to be the occasional word ending in o - such as "medico" whose apparent derived adjective ("medical") ends in "al" but I suspect this is spurious and results from the colloquial addition of "o" to a word (such as "nymph") whose derived adjective did end in "al".

My point - for what it is worth :smiley-sleep: - is that "modulo" is the name of a process the result of which is the modulus and the arithmetic relating to these is concerned more with what you can do with the moduli, than how you obtain them.

nickgammon


In any case, many thanks for forum members that stuck around, I've edited OP to accommodate future reference


Thank you.

Quote

Take a look at Gamon's explanation of the millis() issue, and what is often done wrong that causes errors.


It's "Gammon" (with two "m"s.).

Gamon was a guy in Orgrimmar in World of Warcraft, who was constantly being killed.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

niko23

Hi,
I have solved this problem in this way:
Code: [Select]

unsigned long check_millis = 0;
long interval = 1000;
unsigned long previousMillis = 0;

void setup(){
}
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis >= check_millis) check_millis = currentMillis;
  else {
    check_millis = 0;
    previousMillis = 0;
  }

  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;   
    //do somethings
  }
}

NavyVet1959

The solution that I used to avoid the rollover issue was to make my own replacement for millis() that returns a "unsigned long long int".  It calls millis() to get the current time and remembers the last value that it got from millis(), so if the current time is less than the previous time, it adds 0x0100000000UL to the previous time and then subtracts the current time (this getting the elapsed time) and then adds the elapsed time to the "unsigned long long int" variable that it uses to keep track of the current time.  There is still a possibility of a rollover, but it is pushed so far into the future that I don't consider it an issue.  The rollover would occur around the 584.9 million *year* mark.  The only issue with this method is that if successive calls to this routine do not happen at least once every millis() rollover interval, it could miss a rollover and the time it keeps track of might not be accurate.  Since I use this routine for scheduling automatic actions in a state machine instead of using delay() calls, the routine is called every time the loop() routine is called.  As such, I don't foresee there will be a case where the code is not called for a long enough period of time that the millis() rollover would be an issue.

Robin2

The solution that I used to avoid the rollover issue was to make my own replacement for millis() that returns a "unsigned long long int". 
That sounds awfully complicated when the rollover problem can be avoided simply by using subtraction as in
Code: [Select]
if (millis() - previousTime >= desiredInterval) {

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Coding Badly

The solution that I used to avoid the rollover issue was to make my own replacement for millis() that returns a "unsigned long long int".
A truly terrible idea for the AVR processor.


NavyVet1959

That sounds awfully complicated when the rollover problem can be avoided simply by using subtraction as in
Code: [Select]
if (millis() - previousTime >= desiredInterval) {

...R
I suspect that it's more complicated describing it than actually doing it. :)

My system consists of basically a scheduler and events that can occur.  There might be an event for turning on a particular pin in the future and another one for turning it off at some point later.  The event handler might generate future events in the handling of the current event that it is processing.  It is possible that it might be necessary to schedule an event that will happen after multiple millis() rollovers.  Each of the events in the event queue have a 64-bit time value associated with them to tell the event handler when it needs to process that event.

Something along the line of this:

Code: [Select]

typedef unsigned long long int TIME64;

TIME64  GetTime( void )
{
    static TIME64  curTime64 = 0;
    TIME64         curMillis;
    static TIME64  prevMillis = 0;

    curMillis = millis();
    if (curMillis < prevMillis) {
        /* curMillis < prevMillis, adjusting... */
        curTime64 += 0x0100000000UL + curMillis - prevMillis;
    } else {
        curTime64 += curMillis - prevMillis;
    }
    prevMillis = curMillis;

    return (curTime64);
}

Go Up