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
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.
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?
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)?
millis() returns unsigned long, so your variables (currentTime, etc.) should be also.
Adding volatile is not enough, those variables also need to be accessed atomically. But why add inhibited code when it's not necessary?
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?
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 Atomic Data Access (The GNU C Library)), 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.
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:
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.
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?
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?
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.
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 , 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.