Reset millis() clock

In searching, it appears that this request comes up periodically and is in general discouraged due to potential side effects.

http://arduino.cc/forum/index.php/topic,44844.0.html → This thread gives hints and warnings but no final solution.

For my applciation I desire to reset the millis() clock. After studying wiring.c and the data sheet, I have come up with the following Timer0_reset function that appears to work well in the attached demo sketch, run on an Uno board under Arduino 1.0.

// Millis demo with Timer0 reset

#include <avr/io.h>

unsigned long currentTime = 0;	// this variable will be overwritten by millis() each iteration of loop
unsigned long pastTime = 0;	// no time has passed yet
byte currentState = 0;		// the default state
unsigned int wait = 1000;	// set delay value in milliseconds

extern volatile unsigned long timer0_overflow_count;
extern volatile unsigned long timer0_millis;

void setup() {
	Serial.begin(9600);	
	pinMode(13, OUTPUT);	//the LED we're going to blink
	pastTime = millis();    
}

void loop() {

	unsigned long timePassed;
  
	// currentTime is now the current time (again)
	currentTime = millis();
  
	// this is roll-over proof, if currentTime is small, and pastTime large, the result rolls over
	// to a small positive value, the time that has passed
	timePassed = currentTime - pastTime;

	Serial.println (currentTime, DEC);
	
	if(timePassed >= wait) {		// part1 of the state engine
  
		switch(currentState) {		// part2 of the state engine, its this simple, an if and a switch
		
			case 0:			 		// the default state.. turn the led on!
				digitalWrite(13, HIGH);		// light the LED up!
				pastTime = millis();		// store a new pastTime
				currentState = 1;		// the next state that should be executed is 1
				break;				// break out of the switch, else it'll execute the case below this one too

			case 1:
				digitalWrite(13, LOW);  	// turn the LED off again
				pastTime = millis();		// store a new pastTime
				currentState = 0;		// turning the LED on again is done in state 0
				break;
		}
	}

	delay (50);						// delay a little to make serial monitor more readable

	if (currentTime > 10000) {
		Timer0_reset();					// reset Timer0 every 10000 milliseconds
		Serial.println("millis timer reset to zero...");
	}
}

void Timer0_reset () {

  	// NOTE: to set a bit, a bit mask is OR'ed with the variable
	// to clear a bit, the mask is inverted and then AND'ed with the variable

	TIMSK0 &= ~(1<<TOIE0);				// clear Timer0 overflow interrupt enable bit
	TCCR0B &= ~((1<<CS02) | (1<<CS01)| (1<<CS00));	// stop Timer0 by clearing CS02, CS01, CS00
	TIFR0 = 0;					// clear any pending Timer0 interrupts

	TCNT0 = 0;					// zero out Timer0 TCNT 8 bit register
	timer0_overflow_count = 0;			// zero out millis() overflow counter (extern from wiring.c)
	timer0_millis = 0;				// zero out millis() counter (extern from wiring.c)

	TIMSK0 |= (1<<TOIE0);				// set Timer0 overflow interrupt enable bit

	// restart Timer0 by setting prescale factor back to 64 (this is same code as found in init()
	// function from wiring.c, except sbi call has been replaced with &=/|= equivilent) 
	// this combination is for the standard 168/328/1280/2560
	// sbi(TCCR0B, CS01);
	// sbi(TCCR0B, CS00);
	TCCR0B |= ((1<<CS01) | (1<<CS00));

}

Does anyone see any potential mistakes or pitfalls I may have overlooked?

Edit: Fixed missing volatile statements, variable size per Reply #8 and #11 below

Thanks,
George

For my applciation I desire to reset the millis() clock.

Why? How many times a day do you run around your house resetting all the clocks and watches? The "need" to reset the millis() output is almost always a result of a misunderstanding about what millis() does or some (unfounded) fear that the Arduino will go up in smoke when millis() rolls over.

Your house doesn't burn down every night when the clocks roll over. Life goes on. The Arduino (with proper code) will too.

Note the following two key lines that bring access to the two Timer0 state variables in the core file wiring.c

extern unsigned long timer0_overflow_count; extern unsigned long timer0_millis;

these plus TCNT0 are the counters that all need to be reset.

There is a last state variable timer0_fract does not appear to need to be reset.

Thanks, George

these plus TCNT0 are the counters that all need to be reset.

And, if the clock ticks while you are mucking around with these non-volatile, non-atomic values? Your timer is now hosed.

You still haven't explained WHY.

Just out of interest, why do you feel you need to reset the millis() counter?

Maybe my stopwatch class is usefull - http://www.arduino.cc/playground/Code/StopWatchClass -

... tempus fugit ...

PaulS: And, if the clock ticks while you are mucking around with these non-volatile, non-atomic values? Your timer is now hosed.

You still haven't explained WHY.

Thank You.

On why - among other reasons I wish to gain a better understanding on the Arduino core structure and functional interdependencies and share findings.

The extern variables cited are volatile as defined in wiring.c. TCNT0 is an 8 bit register with single instruction access, the function Timer0_reset() in Post 1 disables Timer0 interrupts and stops the timer while zeroing the state variables and registers. A search of the core files for TCNT0 and the two variables reveals that access for these is restricted to the wiring.c source file.

Can you please elaborate further on what specific elements of the Timer are hosed by the function?

Best Regards, George

robtillaart: Maybe my stopwatch class is usefull - http://www.arduino.cc/playground/Code/StopWatchClass -

... tempus fugit ...

Thank you for the suggestion. I will study the library for application.

George

The extern variables cited are volatile as defined in wiring.c. TCNT0 is an 8 bit register with single instruction access, the function Timer0_reset() in Post 1 disables Timer0 interrupts and stops the timer while zeroing the state variables and registers. A search of the core files for TCNT0 and the two variables reveals that access for these is restricted to the wiring.c source file.

The variables I am referring to are timer0_overflow_count and timer0_millis, both of which are unsigned long, according to your sketch. Neither, according to your sketch, is volatile.

PaulS: The variables I am referring to are timer0_overflow_count and timer0_millis, both of which are unsigned long, according to your sketch. Neither, according to your sketch, is volatile.

Nice catch!

@MGeo: This is a great example of why resetting millis is a bad idea. Get a seemingly insignificant detail wrong and your program goes haywire.

Thank you all for the comments, and the catch on volatile.

I was mistaken in thinking if it was declared volatile in the originating unit, I only need to declare as extern within the sketch. Found a thread on the subject here http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=517706 I have modified the code in Post #1 to add the missing volatile statements.

If I accept the risks, is there a reason the Post 1 reset function will not work (with the added volatile for the two variables)?

  1. millis() returns unsigned long, so your variables (currentTime, etc.) should be also.

  2. Adding volatile is not enough, those variables also need to be accessed atomically. But why add inhibited code when it's not necessary?

  3. I still don't understand why you find it desirable to reset the millis() value. Understanding how it works is one thing. The source code is there, so have at it. Are you under the impression that when the millis() value rolls over that there is some chance of a problem occurring?

[quote author=Jack Christensen link=topic=103633.msg777756#msg777756 date=1335741104] I still don't understand why you find it desirable to reset the millis() value. Understanding how it works is one thing. The source code is there, so have at it. Are you under the impression that when the millis() value rolls over that there is some chance of a problem occurring? [/quote]

Thanks for the reply Jack. I've made some mistakes here, and learned much from the responses to this post. You all have made me further consider atomic access (found http://www.gnu.org/software/libc/manual/html_node/Atomic-Data-Access.html), variable scope and volatility, as well as other pitfalls.

No I am not worried about roll over, but rather wish to understand the underpinnings to help with better design.

Thanks to all for the responses.

George

About this insistence that the millisecond timer be reset: expect the Arduino to react about as violently as I would react if you grabbed my wrist and tried setting the time on my watch.

All you need to do is declare

unsigned long myZeroTime = millis();

and then when you want the elapsed time, just ask for

(millis() - myZeroTime)

Simple as that. no need for a Stopwatch class or anything fancy.

While I would highly discourage messing with the core code's timers and variables, in this case, whether you use volatile or not will not really matter. All volatile does is ensure that the value in memory is really read or really written to memory each time you reference it. The code that references the volatile variables is in a function and the compiler will not "remember" or defer the storage of the contents of those variables to memory beyond the function call so the contents of memory will get updated just fine even without the use of volatile.

However, what is more important relates to atomicity. Not only is it important to ensure that the 16 bit variables on 8 bit systems like the AVR get updated atomically (which is not what volatile does), but in this case the function itself needs to be fully atomic because it is mucking around with low level hardware that affects interrupts and messing variables that are updated inside the ISR routines all while interrupts are still running. So you have two choices you can either wrap the function with atomicity control or add it to the function itself. For example instead of just calling Timer0_reset(); you could wrap it like this:

ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
    Timer0_reset();
}

Or use the ATOMIC_BLOCK() around all the code inside of Timer0_reset();

See this for more about ATOMIC_BLOCK: http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html

The main reason to avoid mucking around with stuff at this level is that it makes your code highly unportable. For example, what if you later decide you want to run your code on an Maple or chipKit "arduino" board? Both of those support the Arduino s/w environment yet by doing AVR specific low level code like this you are locked into the AVR and more specifically certain AVR chips. As you have seen from others, there are other more portable ways to implement the equivalent of a "reset" of the timer.

--- bill

Thanks for the thorough description Bill.

If the variables in question have been confirmed to be ISR access only by the Timer0 overflow ISR and we have disabled the interrupt, is it necessary to provide explicit atomic access protection or is 'standard' access sufficient from the function in question?

Best Regards, George

As written, @MGeo's code essentially accesses the variables atomically. The timer is stopped (and the interrupt disabled) during the access so there is no possibility of a conflict with the interrupt service routine.

However, @MGeo restarts the timer without ensuring the new values have been first written to SRAM. That's why volatile is necessary.

If the variables in question have been confirmed to be ISR access only by the Timer0 overflow ISR and we have disabled the interrupt, is it necessary to provide explicit atomic access protection

No.

or is 'standard' access sufficient from the function in question?

Yes.

Edit: spelling.

Thank you, that adds much clarity to the subject.

odometer:
About this insistence that the millisecond timer be reset: expect the Arduino to react about as violently as I would react if you grabbed my wrist and tried setting the time on my watch.

That’s a bit much, particularly considering as programmer it is in the end my watch to set.


In case others come across this thread with the same question:

After all the input here and my testing and studying of wiring.c, I’m realizing you can in fact reset millis, mostly. The fractional counter variable timer0_fract within wiring.c is used for the microseconds calculation and is declared as follows (in bold italic):

volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;

This can not be reached from a sketch. So you can clear most but not all of the counter state variables from a sketch. The only way I can see around this is to modify wiring.c. If that is the case, one would be better off placing a custom millis_reset() in the modified wiring.c. All a bit much for the casual user.

George

MGeo: For my applciation I desire to reset the millis() clock.

For my application I desire to reset the year to 1984.

I am using mills() command to operate a clock. I have calibrated the software timer to make the millis() function accurate on an UNO. (about 1-2 seconds a day) I initally set the clock with an NTP server on power-up and touch-up the time every 24 hours. This is going to operate a sprinkler system at my home. The documentation states the mills() will reset about every 50 days. I am assuming at or about 50 days the value will go to Zero and start over. This may have some unpredictable result on my system. To be proactive, I was thinking about a day counter that when it gets to 40 days and 40 nights :astonished:, to cause a pin on the arduino to toggle and fire a 555 to make a reset pulse. All my essential data is stored in EEPROM in the 328 and the clock will resync with a NTP server on power-up / reboot. My question is, does anyone see a fault in my logic. Its kind of hard to test something if it only happens every 50 days or so.