This seems to be something that people ask how to do fairly frequently. 99 % of the time, such as when dealing with millis overflow, it really isn’t necessary. That being said, sometimes it may be appropriate and it provides an interesting insight into some of the core Arduino code.
Before trying any of the following please remember that a lot of libraries use millis() internally and changing its behaviour MAY BREAK THINGS. I think it’s extremely unlikely that you are going to permanently brick your Arduino, but you should understand what it is that you are doing.
Millis internals
So how does millis actually work? Millis uses Timer0 which is configured to “tick” every 64 clock cycles. Every time it ticks, the register containing the number of times Timer0 has ticked is incremented. Since the register is only 1 byte, after 256 ticks the register overflows. When this happens, an interrupt is triggered and the associated interrupt handling code is run.
To find out how this is implemented, you need to look in wiring.c which is found in the arduino folder “hardware/arduino/cores/arduino” or thereabouts. First off there are some #defines that are used to calculate various values depending on the clock frequency being used.
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) #define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000) #define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) #define FRACT_MAX (1000 >> 3)
To work out the values we also need the following #defines:
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
here are the relevant numbers for both 16MHz and 8 Mhz.
| #def | 16MHz | 8MHz |
| MICROSECONDS_PER_TIMER0_OVERFLOW | (64 * 256) / (16000000Hz/1000000L) = 1024 | 2048 |
| MILLIS_INC | 1024/1000 = 1 | 2048/1000 = 2 |
| FRACT_INC | 1024%1000 >> 3 = 3 | 2048%1000>>3 = 6 |
| FRACT_MAX | 1000 >> 3 = 125 | 1000 >> 3 = 125 |
For the moment I will just focus on the values for a 16MHz clock to avoid confusion. So by the calculations above we can see that it takes 1024 microseconds for Timer0 to overflow. The millis value is incremented, therefore, by 1. However this leaves 24 microseconds unaccounted for every millisecond. If this was ignored then millis would lose roughly 2000 seconds a day. This is where FRACT_INC and FRACT_MAX come in. FRACT_INC is the “extra” 24 microseconds bit-shifted to fit into one byte. FRACT_MAX, therefore, is the threshold at which you have accumulated 1 millisecond of error, bit shifted to also fit into one byte. I assume that this is done to reduce the number of instructions (i.e. using 8 bit math on an 8 bit microprocessor) in the interrupt handler since it runs so frequently.
Here is the code for the interrupt handler:
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;
//Increment millis value
m += MILLIS_INC;
//increment extra microseconds by correct amount
f += FRACT_INC;
//If we've accrued more than 1ms of error increment the millis value and deduct a millisecond from our error
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}
//write new values to memory
timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}
It’s also worth pointing out that this implementation means that millis will be incremented by 2 (or 3 using an 8MHz oscillator) when the microsecond error is factored in. This means you might get unexpected behaviour if you do things like “if(millis()==1043)” as millis() might never return that value.
Now that we understand how millis() actually works we can go about resetting it.
Resetting Millis()
I’ll say it again: Reseting/modifying the millis() count MAY break other libraries or possibly your existing code.
Moving swiftly on, the pertinent code can again be found in wiring.c:
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;
return m;
}
This is the actual millis() function. It is very simple and can be broken down into:
- Declare an unsigned long (value that will be returned)
- Make a copy of the Status Register.
- Disable interrupts.
- Copy millis value into the declared unsigned long.
- Restore the Status Register from our copy.
- Return value.
We can pretty much use that code as is, the only thing we need to change is to copy the new value to millis rather than get the millis value.
//Sets the millis value
void setMillis(unsigned long new_millis)
{
uint8_t oldSREG = SREG;
cli();
timer0_millis = new_millis;
SREG = oldSREG;
}
You can put this function in wiring.c if you don’t mind modifying the Arduino core code. This does mean, however, that you will have to maintain it when/if it gets updated. The alternative is to put the function in your own code, but this requires you to declare:
extern volatile unsigned long timer0_millis;
so that you can access the millis variable in your code.
An example sketch would be:
extern volatile unsigned long timer0_millis;
unsigned long new_value = 0;
void setup(){
//Setup stuff
}
void loop(){
//Do stuff
//--------
//Change Millis
setMillis(new_value);
}
void setMillis(unsigned long new_millis){
uint8_t oldSREG = SREG;
cli();
timer0_millis = new_millis;
SREG = oldSREG;
}
Personally, I’ve used both methods and so far I’ve not encountered any problems. It does require some careful thought as to what effect changing millis, in a particular section of code, will have. Changing it while in a timed loop, for example, is likely to cause problems. It is also worth considering if what you need to do can be achieved by other, safer, methods. In my case I am trying to synchronise clocks between multiple microcontrollers and I wanted a method of applying an offset to millis. Originally I just stored the offset in a separate variable but changed it as the aim is to actually synchronise the millis counts.
Thank you Tom fro sharing your knowledge,
I have found some inconsistency in yor explanation.
If you look at the definition, it says:
#define FRACT_MAX (1000 >> 3) -> Not deneding on the processor speed
While in the next table, it says:
FRACT_MAX (For 16MHz) 1024 >> 3 = 128 (For 8 MHz) 2048 >> 3 = 256
May I assume that the correct is the one from wiring.c?
Thank you,
Jose
Thanks for pointing that out. I actually made a mistake, FRACT_MAX is defined as (1000 >> 3) for both 8Mhz and 16Mhz. It’s just the amount the fractional counter is incremented by that is modified by the clock speed. I’ve fixed it now.
I do believe all the ideas you have offered in your post. They are very convincing and can certainly work. Still, the posts are very short for novices. May just you please prolong them a bit from next time? Thank you for the post:
ELI http://www.net-ict.be/
The method above will modify millis, but micros will be inconsistent with millis. I ended up needing to steal timer0 from the Arduino core in an application, so I wanted to fix up Arduino time when I gave it back. I managed to do this to within a few microseconds – see http://becomingmaker.com/borrowing-an-arduino-timer/ for details.
“If [the 24 us remainer] was ignored then millis would lose roughly 2 seconds a day.”
That’s 24 us per MILLIsecond; 2000+ seconds per day!
You are completely correct! Fixed, thank you.
Thanks much! Being able to reset the millis() counter means I can now not only simply my time based functionality even more, but it means I never have to deal with the dreaded millis() rollover to 0 every 49 days!
I have several applications that use a Realtime Clock Chip for reference. Once a day, I update the time from the RTC and then use millis() with offsets to take care of the daily time based events. There are several advantages to resetting the millis value to match an RTC chip. And when I say match the RTC chip, I mean a function that calculates the “Seconds Since Midnight” from the RTC value and then millis() is synchronized to that time, via offsets, so that the “new” millis() counter now reflects milliseconds since midnight.
Having millis()synchronized to the RTC, allows faster, tighter code for time related operations. As opposed to continually reading the RTC, for a specific time. Tighter code: Performing a comparison of (Millis() >= some time value) is faster and more efficient than reading the RTC, converting it’s BCD values to decimal, and then doing a similar comparison – a hundred(?), thousand(?), million(?) times a day.
And most of all, we don’t have to have code that watches for, and then reacts to the millis() counter rolling over to 0 every 49 days. All time related operations based on millis() absolutely needs to do that.
Now, being able to ACTUALLY resetting the Millis() counter once a day, week, or fortnight, in a controlled fashion means, smaller and more efficient code. And most of all, never having to deal with a rollover again 😉