Pages: [1] 2   Go Down
Author Topic: Reset millis() clock  (Read 3719 times)
0 Members and 2 Guests are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 0
Posts: 64
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.  

Code:
// 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
« Last Edit: April 29, 2012, 07:36:36 pm by MGeo » Logged

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

Quote
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.
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 64
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

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

Quote
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.
Logged

Gosport, UK
Offline Offline
Faraday Member
**
Karma: 21
Posts: 3113
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

 ... tempus fugit ...
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Jr. Member
**
Karma: 0
Posts: 64
View Profile
 Bigger Bigger  Smaller Smaller  Reset 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.

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
« Last Edit: April 29, 2012, 10:57:19 am by MGeo » Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 64
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

Logged

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

Quote
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.
Logged

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

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

Offline Offline
Jr. Member
**
Karma: 0
Posts: 64
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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)?
« Last Edit: April 29, 2012, 04:53:54 pm by MGeo » Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 92
Posts: 3952
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Offline Offline
Jr. Member
**
Karma: 0
Posts: 64
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 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
« Last Edit: April 29, 2012, 06:57:10 pm by MGeo » Logged

Offline Offline
Full Member
***
Karma: 2
Posts: 197
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Code:
unsigned long myZeroTime = millis();

and then when you want the elapsed time, just ask for
Code:
(millis() - myZeroTime)

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

Dallas, TX USA
Online Online
Faraday Member
**
Karma: 63
Posts: 2641
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
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
Logged

Pages: [1] 2   Go Up
Jump to: