After my Sanguino wakes up from its 8 sec sleep, I would like to advance the millis() value by 8000. Is this possible?
Yes and no. No, if you use the standard Arduino libraries. Yes, if you do not mind to patch them.
In hardware/cores/arduino/wiring.c
you find
volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;
and the code that advances them. You can add another function to advance them on your own demand. Take care to make the advance function internally atomic. That is start it like
uint8_t old_SREG = SREG;
cli();
and end it like
SREG = old_SREG;
cli();
Yes and no. No, if you use the standard Arduino libraries. Yes, if you do not mind to patch them.
In hardware/cores/arduino/wiring.c
you find
volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;
and the code that advances them. You can add another function to advance them on your own demand. Take care to make the advance function internally atomic. That is start it like
uint8_t old_SREG = SREG;
cli();
and end it like
SREG = old_SREG;
cli();
Thanks for the help.
While I was looking through the wiring.c file I noticed that in simple terms, the millis() function returns the timer0_millis variable.
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;
}
So for a rough test, I added the following in the global scope:
extern volatile unsigned long timer0_millis;
and the following in my sketch right after the arduino wakes up from sleeping:
timer0_millis = timer0_millis + 8000;
That seems to do the trick nicely without modifying the wiring.c file. Now, I suspect it's not internally atomic. I'm not quite sure what that even means, googling the term internally atomic didn't help much. I've noticed that the watch dog timer code that I've adapted uses the cli() command before setting the watch dog timer and the sei() command after. Should my timer0_millis line be there too? If it's not too much trouble I'd appreciate an brief explanation of what internally atomic means.
Also, feel free to point out anything wrong with what I'm doing.
Matt,
In computer terms, atomic means indivisible. In other words, an atomic operation will complete before any other operation takes place.
Here's the problem: adding 8000 to the millis counter takes several machine instruction. The CPU has to fetch the current value of millis, put 8000 in a register, add the two together, then store the new value back in millis. While all of this is happening, an interrupt can occur. If it's the timer interrupt that increments the millis counter, it could be incrementing the counter in the middle of your calculation, and you can get some very strange results. To make it even worse, it will appear to happen randomly, because sometimes your calculation will complete without being interrupted and sometimes it will get messed up by the interrupt.
The cli() turns off interrupts, so your calculation can complete unmolested.
Regards,
-Mike
Makes sense, so I take it then the sei() command turns interrupts back on. I will then put my + 8000 command between those two where the program disables the wdt after waking up.
I appreciate the explanation. Thanks Mike.
The Arduino added higher level functions to perform the disabling and enabling of interrupts, so you use the below instead of that shown above.
http://arduino.cc/en/Reference/Interrupts
Lefty
How about:
long sleptTime = 0;
unsigned long my_millis(void)
{
return millis() + sleptTime;
}
do_sleep()
{
//Magic
sleptTime += 8000; // 8 seconds spent asleep
}
No interrupt worries, no wiring modifications. Doesn't work if you were providing some library and wanted people to be able to use the standard millis() function...