Help managing the fallout from timer0 mode and prescaler change on ATMega2560 without modifying wiring.c
The millis() roll over

By making Timer 0 faster, your millis() will roll over much more
often than normal: every 37.1 hours instead of every 49.7 days. Thus,
when you “scale” millis(), you should be careful to do it in a
rollover-safe way. From the code you show, it appears that you are doing
exactly the right thing. Well done! Don't change anything. Don't be
tempted to scale the value returned by millis().

The overflow in delayMicroseconds()

As this function just burns cycles in a busy loop, it is not affected by
your configuration of Timer 0. You can just use it as is, with no
scaling. Beware however that it's argument should always be smaller than
16384 (on a 16 MHz board). Otherwise it will overflow when it is
multiplied by 4 in order to get a loop count.

For example, when you call delayMicroseconds(48188);, the function
computes the number of loop iterations to make as

48188 × 4 − 5 = 61675 (modulo 216)

Then, it burns about 15&nbsp;ms of CPU time (0.25&nbsp;µs per
iteration), which is close to the 20&nbsp;ms you see. For the
difference, see below.

The cycles burned by the Timer 0 ISR

This ISR, which keeps the millis() counter updated, is supposed to run
every 1024&nbsp;µs. Each time it runs it burns a few cycles, but it's
usually not a big deal. With your new timer configuration, it is now
running far more often: every 31.875&nbsp;µs.

From the numbers you give in the question, I would say the ISR is
eating about 20% of your CPU power: when you call
delayMicroseconds(3614);, that function burns 3614&nbsp;µs of CPU
time, yet you measure a delay of about 4500&nbsp;µs. The difference is
taken by the Timer 0 ISR.

Use another timer for millis()

If you can spare a timer, you could both restore the normal behavior of
millis() and avoid wasting so much CPU in the Timer 0 ISR:


configure Timer 0 to not trigger interrupts
configure your spare timer just like the Arduino core would normally
configure Timer 0
write an overflow ISR for your spare timer that just calls the Timer 0
overflow ISR.


This way your spare timer is basically replacing Timer 0. Example using
Timer 2:

// This Timer 2 overflow ISR simply forwards the call to the Timer 0
// overflow ISR from Arduino core.
ISR(TIMER0_OVF_vect);
ISR(TIMER2_OVF_vect, ISR_NAKED) {
    TIMER0_OVF_vect();
    asm volatile("reti");

    // Alternatively, just:
    // asm volatile("jmp __vector_16");
}

void setup()
{
    // Set timer0 to phase correct PWM
    TCCR0A = (TCCR0A &amp; 0b11111100) | 0x01;

    // Set prescaler for timer0 to 1
    TCCR0B = (TCCR0B &amp; 0b11111000) | 0x01;

    // Disable Timer 0 interrupts.
    TIMSK0 = 0;

    // Configure Timer 2 to replace Timer 0.
    TCCR2A = _BV(WGM20)   // fast PWM, TOP = 0xff
           | _BV(WGM21);  // ditto
    TCCR2B = _BV(CS22);   // clock at F_CPU / 64
    TIMSK2 = _BV(TOIE2);  // enable overflow interrupt

    pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
    // Blink the LED at 1 Hz.
    uint32_t now = millis();
    static uint32_t last_toggle;
    if (now - last_toggle &gt;= 500) {
        last_toggle += 500;
        digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    }
}


I wrote a “naked” ISR to avoid having plenty of CPU registers saved and
restored twice (once by each ISR in the call chain), but then I had to
put the return instruction explicitly in assembly (ret and reti
would be equivalent in this instance). The version with jmp is
slightly more efficient, but you have to know the “real” name of the
ISR, which is MCU-dependent (__vector_16 on an Uno).

Notice that with this technique micros() will not work, as it reads
TCNT0 directly. And delay() will not work either because it relies on
micros().