During my experimenting I needed a seconds() independent from millis(). Here is a small hack which gives you seconds() function implemented around Timer0 OVF ISR, fully independent from millis(), and as precise as well you know the exact frequency of your crystal oscillator (ie. done via a measurement).
It does not use counting interrupts, but elaborates MCU cycles with a special algorithm instead. The new core uint32 seconds() function returns a precise n. of seconds since boot (like the millis() returns milliseconds) with a nice bonus - it works with any crystal oscillator frequency value. The seconds() does not drift in long run, so you may use it for measuring long periods.
The only thing you have to do is to provide your precise crystal frequency in Hz (or, better, the frequency the crystal oscillator actually runs on your board), ie. on my 16MHz board:
#define CRYSTAL_OSC_FREQ 15998457L
In wiring.c (I did it with 1.0.5 core in uecide):
..
volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;
// ***** PITO
#define CRYSTAL_OSC_FREQ 15998457L
volatile unsigned long timer0_seconds = 0;
volatile long timer0_ticker = CRYSTAL_OSC_FREQ;
// *****
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
// 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;
// ***** PITO
long t = timer0_ticker;
// *****
m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}
timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
// ***** PITO - here is the seconds() zero drift algorithm
t -= 16384L;
if ( t < 16384L )
{ t += CRYSTAL_OSC_FREQ;
timer0_seconds++;
}
timer0_ticker = t;
// *****
}
..
// ***** PITO
unsigned long seconds()
{
unsigned long s;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_seconds or we might get an
// inconsistent value
cli();
s = timer0_seconds;
SREG = oldSREG;
return s;
}
unsigned long ticks()
{
unsigned long t;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_overflow_count or we might get an
// inconsistent value
cli();
t = timer0_overflow_count;
SREG = oldSREG;
return t;
}
// *****
The sketch to test it:
// The Zero Drift Seconds() Test
void setup() {
Serial.begin(115200);
}
void loop() {
long ms, sec, tks;
sec = seconds();
ms = millis();
tks = ticks();
Serial.print(sec);
Serial.print(" ");
Serial.print(ms);
Serial.print(" ");
Serial.print(tks);
Serial.println();
delay(50);
}
You have to add seconds() function def into Arduino.h and into keywords.txt in a similar way as with millis().
Arduino.h:
..
unsigned long millis(void);
unsigned long seconds(void);
unsigned long ticks(void);
unsigned long micros(void);
..
\lib\keywords.txt:
..
millis KEYWORD2 Millis
ticks KEYWORD2 Ticks
seconds KEYWORD2 Seconds
micros KEYWORD2 Micros
..
EDIT: included ticks(), millis() = 1.024 * ticks()
For adventurous users only, provided as-is, no warranty of any kind