It's also possible to add the display refresh code into an alternate millis() interrupt ISR so you don't waste time calling and returning from multiple ISRs. I do this by disabling the timer0 overflow interrupt, and instead enabling the compare interrupt with the compare register set to 249. In CTC mode this makes timer0 roll over every 250 counts, which will be a perfect 1ms if the crystal is right. The ISR is now only one line of code, and you can easily add the refresh code. Here's a Blink sketch using the revised millis() generator:
/*
This features a replacement for the ISR(TIMER0_OVF_vect)
interrupt that drives millis(). It disables the OVF
interrupt, enables the COMPA interrupt, sets OCR0A to 249,
and changes Timer0 to CTC mode. This results in an
interrupt every 250 timer clock cycles, which is exactly 1ms
for a 16MHz crystal. For 8MHz, OCR0A is set to 124. The new
ISR increments millis, but since the interrupt rate is exactly
correct, no periodic double increment of millis is needed.
Using this code probably means you can't do any analog
writes that use Timer0, which would include pins 5 and 6.
Millis() should work as normal at 16MHz or 8MHz.
The effect on micros() is unknown.
*/
extern volatile unsigned long timer0_millis; //this defined in wiring.c
byte MILLIS_INCB = (64 * 250) / (F_CPU / 1000); // ms to 250 ticks
const int cycleTime = 500; // flash LED every second
unsigned long oldMillis = millis();
unsigned long newMillis = 0;
void setup() { //Set up alternate interrupt
// at 249 on timer0
cli(); // disable interrupts while doing this
TCCR0A = 0; // set entire TCCR0A register to 0
TCCR0B = 0; // same for TCCR0B
TCNT0 = 0; // initialize timer0 count to 0
OCR0A = (250/MILLIS_INCB) - 1; // set top of counter (249 for 16MHz, 124 for 8MHz)
TIMSK0 &= ~bit(TOIE0); // disable overflow interrupt
TCCR0A |= bit(WGM01); // turn on CTC mode
TCCR0B |= (bit(CS01)+bit(CS00)); // Set CS00&CS01 bits for prescaler = 64
TIMSK0 |= bit(OCIE0A); // enable timer compare interrupt
sei(); // enable interrupts
pinMode(13,OUTPUT);
digitalWrite(13,HIGH);
}
void loop() { // flashes LED for 1/2 second
// every second
newMillis = millis();
if ((newMillis - oldMillis) == cycleTime) {
oldMillis = newMillis;
digitalWrite(13,!digitalRead(13)); // invert pin 13 state
}
}
ISR(TIMER0_COMPA_vect) { // this is the new ISR - much
// simpler than original
timer0_millis++;
}